diff --git a/.github/workflows/schema-publish.yaml b/.github/workflows/schema-publish.yaml new file mode 100644 index 0000000000..7e82191b0f --- /dev/null +++ b/.github/workflows/schema-publish.yaml @@ -0,0 +1,61 @@ +name: schema-publish + +# author: @ralfhandl +# issue: https://github.com/OAI/OpenAPI-Specification/issues/3715 + +# +# This workflow updates the respec 'pretty' rendered versions of the spec +# on the gh-pages branch when the corresponding markdown files change. +# + +# run this on push to main +on: + push: + branches: + - main + workflow_dispatch: {} + +jobs: + publish: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 # checkout main branch + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 # setup Node.js + with: + node-version: '20.x' + + - name: Install dependencies + run: npm ci + + - uses: actions/checkout@v4 # checkout gh-pages branch + with: + ref: gh-pages + path: deploy + + - name: run main script + run: scripts/schema-publish.sh + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: publish-schema-iteration + base: gh-pages + delete-branch: true + path: deploy + labels: Housekeeping,Schema + team-reviewers: OAI/tsc #TODO: check if this works, or if it needs a special access token + title: Publish OpenAPI Metaschema Iterations + commit-message: New OpenAPI metaschema iterations + signoff: true + body: | + This pull request is automatically triggered by GitHub action `schema-publish`. + + The `schemas/**/*.yaml` files have changed and JSON files are automatically generated. + + diff --git a/scripts/schema-convert.js b/scripts/schema-convert.js new file mode 100644 index 0000000000..ec8c6919ea --- /dev/null +++ b/scripts/schema-convert.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +'use strict'; + +const fs = require('fs'); +const yaml = require('yaml'); + +function convert(filename,date) { + try { + const s = fs.readFileSync(filename,'utf8'); + const obj = yaml.parse(s, {prettyErrors: true}); + // replace last segment of id, $id, and $ref value with date + for (const p of ["id","$id","$ref"]) { + if (obj[p]) { + obj[p] = obj[p].replace(/\/[^\/]+$/,'/'+date); + } + } + console.log(JSON.stringify(obj,null,2)); + } + catch (ex) { + console.warn(' ',ex.message); + process.exitCode = 1; + } +} + +if (process.argv.length<4) { + console.warn('Usage: convert-schema.js file.yaml YYYY-MM-DD'); +} +else { + convert(process.argv[2], process.argv[3]); +} + diff --git a/scripts/schema-publish.sh b/scripts/schema-publish.sh new file mode 100755 index 0000000000..3990927cc8 --- /dev/null +++ b/scripts/schema-publish.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Author: @ralfhandl + +# run this script from the root of the repo. It is designed to be run by a GitHub workflow. + +for filename in schemas/v3*/schema.yaml ; do + vVersion=$(basename $(dirname "$filename")) + version=${vVersion:1} + lastCommitDate=$(git log -1 --format="%ad" --date=short "$filename") + + echo "$filename $lastCommitDate" + mkdir -p deploy/oas/$version/schema + node scripts/schema-convert.js "$filename" $lastCommitDate > deploy/oas/$version/schema/$lastCommitDate + mv deploy/oas/$version/schema/*.md deploy/oas/$version/schema/$lastCommitDate.md + + filenameBase=$(dirname "$filename")/schema-base.yaml + if [ -f "$filenameBase" ]; then + echo "$filenameBase $lastCommitDate" + mkdir -p deploy/oas/$version/schema-base + node scripts/schema-convert.js "$filenameBase" $lastCommitDate > deploy/oas/$version/schema-base/$lastCommitDate + mv deploy/oas/$version/schema-base/*.md deploy/oas/$version/schema-base/$lastCommitDate.md + fi +done diff --git a/tests/md2html/md2html.test.js b/tests/md2html/md2html.test.js index 3309611d55..590ec94ce3 100644 --- a/tests/md2html/md2html.test.js +++ b/tests/md2html/md2html.test.js @@ -2,10 +2,9 @@ import { readdirSync, readFileSync } from "node:fs"; import { exec } from "node:child_process"; import { resolve } from "node:path"; import { describe, test, expect } from "vitest"; -import assert from "node:assert"; const folder = "./tests/md2html/fixtures/"; -describe("v3.0 - Validate examples", async () => { +describe("Markdown to HTML", async () => { readdirSync(folder, { withFileTypes: true }) .filter((entry) => entry.isFile() && /\.md$/.test(entry.name)) .forEach((entry) => { diff --git a/tests/schema-convert/fixtures/schema.json b/tests/schema-convert/fixtures/schema.json new file mode 100644 index 0000000000..0d42e54685 --- /dev/null +++ b/tests/schema-convert/fixtures/schema.json @@ -0,0 +1,8 @@ +{ + "id": "https://spec.openapis.org/oas/3.0/schema/8765-43-21", + "$id": "https://spec.openapis.org/oas/3.0/schema/8765-43-21", + "$ref": "path/to/somewhere/8765-43-21", + "other": { + "foo": "bar" + } +} diff --git a/tests/schema-convert/fixtures/schema.yaml b/tests/schema-convert/fixtures/schema.yaml new file mode 100644 index 0000000000..79efd300d9 --- /dev/null +++ b/tests/schema-convert/fixtures/schema.yaml @@ -0,0 +1,5 @@ +id: https://spec.openapis.org/oas/3.0/schema/2024-10-17 +$id: https://spec.openapis.org/oas/3.0/schema/some-iteration +$ref: path/to/somewhere/replace-last-segment +other: + foo: bar diff --git a/tests/schema-convert/schema-convert.test.js b/tests/schema-convert/schema-convert.test.js new file mode 100644 index 0000000000..8fd47ace83 --- /dev/null +++ b/tests/schema-convert/schema-convert.test.js @@ -0,0 +1,58 @@ +import { readdirSync, readFileSync } from "node:fs"; +import { exec } from "node:child_process"; +import { resolve } from "node:path"; +import { describe, test, expect } from "vitest"; +import assert from "node:assert"; + +const folder = "./tests/schema-convert/fixtures/"; +describe("Convert Schemas", async () => { + test("YAML with id, $id, $ref", async () => { + const expected = readFileSync(folder + "schema.json", "utf8"); + const output = await convert( + [ + "schema.yaml", + "8765-43-21", + ], + folder, + ); + expect(output.stdout).to.equal(expected); + }); + + test("non-existing input", async () => { + const output = await convert( + [ + "does-not-exist", + "YYYYMMDD", + ], + folder, + ); + expect(output.stderr).to.equal(" ENOENT: no such file or directory, open 'does-not-exist'\n"); + }); + + test("invalid parameters", async () => { + const output = await convert( + [ + "schema.yaml", + ], + folder, + ); + expect(output.stderr).to.equal("Usage: convert-schema.js file.yaml YYYY-MM-DD\n"); + }); +}); + +function convert(args, cwd) { + return new Promise((res) => { + exec( + `node ${resolve("./scripts/schema-convert.js")} ${args.join(" ")}`, + { cwd }, + (error, stdout, stderr) => { + res({ + code: error?.code || 0, + error, + stdout, + stderr, + }); + }, + ); + }); +}