From d8b658fdffa1b23a1abe66f6ba9f24986dcb2bc1 Mon Sep 17 00:00:00 2001 From: temycodes Date: Mon, 22 Jun 2026 13:29:15 +0100 Subject: [PATCH] ci: export OpenAPI spec as CI artifact (#40) --- Backend/package-lock.json | 82 +++++++++++++++++++-- Backend/package.json | 17 +++-- Backend/src/export-spec.ts | 35 +++++++++ infrastructure/ci/docs-generation.yml | 39 ++++++++++ infrastructure/scripts/generate-api-docs.sh | 24 +++--- 5 files changed, 169 insertions(+), 28 deletions(-) create mode 100644 Backend/src/export-spec.ts diff --git a/Backend/package-lock.json b/Backend/package-lock.json index cd1c519..0bbcee9 100644 --- a/Backend/package-lock.json +++ b/Backend/package-lock.json @@ -22,6 +22,7 @@ "cookie-parser": "^1.4.7", "csrf": "^3.1.0", "ethers": "^6.15.0", + "js-yaml": "^5.0.0", "pg": "^8.13.3", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", @@ -38,6 +39,7 @@ "@types/express": "^5.0.0", "@types/geojson": "^7946.0.16", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.10.7", "@types/supertest": "^6.0.2", "eslint": "^9.18.0", @@ -887,6 +889,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/js": { "version": "9.39.4", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", @@ -3008,7 +3033,7 @@ "version": "1.15.41", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.41.tgz", "integrity": "sha512-03nQq/082QRJJiOvp3FGbgxTGyyxMxohPTjhk/W9bD2J0tk4ukITI7goOhOO2WbaHn/lsPmo/zf8+DIXhwpgYQ==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -3052,6 +3077,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3068,6 +3094,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3084,6 +3111,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -3100,6 +3128,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3116,6 +3145,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3132,6 +3162,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3148,6 +3179,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3164,6 +3196,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3180,6 +3213,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3196,6 +3230,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3212,6 +3247,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3228,6 +3264,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3241,14 +3278,14 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/types": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz", "integrity": "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -3514,6 +3551,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -5794,6 +5838,29 @@ } } }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -8595,10 +8662,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.0.0.tgz", + "integrity": "sha512-GSvaPUbk1U+FMZ7rJzF+F8e5YVtu7KnD40et/5rBXXRBv2jCO9L3qCewvIDDdudC0QycTFlf6EAA+h3kxBsuUw==", "funding": [ { "type": "github", @@ -8614,7 +8680,7 @@ "argparse": "^2.0.1" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "js-yaml": "bin/js-yaml.mjs" } }, "node_modules/jsesc": { diff --git a/Backend/package.json b/Backend/package.json index 044ff98..936fa48 100644 --- a/Backend/package.json +++ b/Backend/package.json @@ -20,7 +20,8 @@ "test:e2e": "jest --config ./test/jest-e2e.json", "migration:run": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:run", "migration:revert": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:revert", - "migration:generate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:generate" + "migration:generate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d src/database/data-source.ts migration:generate", + "export-spec": "ts-node -r tsconfig-paths/register src/export-spec.ts" }, "dependencies": { "@nestjs/common": "^11.0.1", @@ -28,18 +29,19 @@ "@nestjs/core": "^11.0.1", "@nestjs/mapped-types": "^2.1.0", "@nestjs/platform-express": "^11.0.1", + "@nestjs/swagger": "^11.2.6", + "@nestjs/throttler": "^6.4.0", "@nestjs/typeorm": "^11.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", + "cookie-parser": "^1.4.7", + "csrf": "^3.1.0", "ethers": "^6.15.0", + "js-yaml": "^5.0.0", "pg": "^8.13.3", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "typeorm": "^0.3.28", - "@nestjs/throttler": "^6.4.0", - "@nestjs/swagger": "^11.2.6", - "cookie-parser": "^1.4.7", - "csrf": "^3.1.0" + "typeorm": "^0.3.28" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -52,6 +54,7 @@ "@types/express": "^5.0.0", "@types/geojson": "^7946.0.16", "@types/jest": "^29.5.14", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.10.7", "@types/supertest": "^6.0.2", "eslint": "^9.18.0", @@ -86,4 +89,4 @@ "coverageDirectory": "../coverage", "testEnvironment": "node" } -} \ No newline at end of file +} diff --git a/Backend/src/export-spec.ts b/Backend/src/export-spec.ts new file mode 100644 index 0000000..93fccfb --- /dev/null +++ b/Backend/src/export-spec.ts @@ -0,0 +1,35 @@ +import { NestFactory } from '@nestjs/core'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { writeFileSync, mkdirSync } from 'fs'; +import { join } from 'path'; +import { dump } from 'js-yaml'; +import { AppModule } from './app.module'; + +const docsDir = join(__dirname, '..', '..', 'infrastructure', 'docs'); + +(async () => { + try { + const app = await NestFactory.create(AppModule, { logger: false }); + await app.init(); + + const swaggerConfig = new DocumentBuilder() + .setTitle('Gist API') + .setDescription('Anonymous hyperlocal messaging on Stellar') + .setVersion('0.1.0') + .build(); + + const document = SwaggerModule.createDocument(app, swaggerConfig); + + mkdirSync(docsDir, { recursive: true }); + writeFileSync( + join(docsDir, 'openapi.json'), + JSON.stringify(document, null, 2), + ); + writeFileSync(join(docsDir, 'openapi.yaml'), dump(document)); + + await app.close(); + } catch (error) { + console.error(error); + process.exit(1); + } +})(); diff --git a/infrastructure/ci/docs-generation.yml b/infrastructure/ci/docs-generation.yml index 7347e85..b44a40e 100644 --- a/infrastructure/ci/docs-generation.yml +++ b/infrastructure/ci/docs-generation.yml @@ -19,17 +19,56 @@ jobs: generate-api-docs: name: API Docs runs-on: ubuntu-latest + services: + postgres: + image: postgres:15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: vertexchain_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: Backend/package-lock.json + + - name: Install Backend dependencies + run: cd Backend && npm ci + - name: Generate OpenAPI and static docs + env: + DATABASE_HOST: localhost + DATABASE_PORT: 5432 + DATABASE_USER: postgres + DATABASE_PASSWORD: postgres + DATABASE_NAME: vertexchain_test run: | version="${{ github.event.release.tag_name }}" if [ -z "$version" ]; then version="$(date +%Y.%m.%d)"; fi bash infrastructure/scripts/generate-api-docs.sh "$version" + - name: Upload OpenAPI spec artifact + uses: actions/upload-artifact@v4 + with: + name: openapi-spec + path: | + infrastructure/docs/openapi.json + infrastructure/docs/openapi.yaml + if-no-files-found: error + - name: Commit API docs run: | git config user.name "github-actions[bot]" diff --git a/infrastructure/scripts/generate-api-docs.sh b/infrastructure/scripts/generate-api-docs.sh index cbf1288..21b3272 100755 --- a/infrastructure/scripts/generate-api-docs.sh +++ b/infrastructure/scripts/generate-api-docs.sh @@ -17,18 +17,16 @@ mkdir -p "$(dirname "${spec_output}")" "${site_output}" "$(dirname "${version_fi version="${1:-$(date +%Y.%m.%d)}" echo "${version}" > "${version_file}" -cat > "${spec_output}" < "${site_output}/index.html" < "${site_output}/index.html" < VertexChain API Docs @@ -43,6 +41,6 @@ cat > "${site_output}/index.html" < -EOF +HTMLEOF echo "Generated API docs for version ${version}"