From e84fab434b8d4e4c67f12c41e87376e27475f883 Mon Sep 17 00:00:00 2001 From: Benson Shen Date: Fri, 6 Mar 2026 12:03:19 -0500 Subject: [PATCH] feat: add Express API changelog generation Parameterize api-diff.js to accept custom input/output paths via CLI args. Update generate-release workflow to diff both Platform and Express specs and combine release descriptions under section headers. Ticket: DX-3144 Co-Authored-By: Claude Opus 4.6 --- .github/CODEOWNERS | 1 + .github/workflows/generate-release.yml | 65 ++++++++++++++++++++++---- scripts/api-diff.js | 12 +++-- tests/generate-release.test.js | 27 +++++++++-- 4 files changed, 87 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e2501cf..335a296 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,3 @@ * @BitGo/developer-experience api.yaml @BitGo/developer-experience @BitGo/technical-writers +express-api.yaml @BitGo/developer-experience @BitGo/technical-writers diff --git a/.github/workflows/generate-release.yml b/.github/workflows/generate-release.yml index dd1c76c..840b28d 100644 --- a/.github/workflows/generate-release.yml +++ b/.github/workflows/generate-release.yml @@ -6,6 +6,7 @@ on: - master paths: - 'api.yaml' + - 'express-api.yaml' jobs: generate-release: @@ -24,17 +25,61 @@ jobs: with: node-version-file: .nvmrc - - name: Get API specs and generate JSON files + - name: Get Platform API specs and generate JSON files run: | PREVIOUS_MERGE=$(git rev-list --merges master | head -n 2 | tail -n 1) git show $PREVIOUS_MERGE:api.yaml > previous.yaml || echo "v0.0.0" > previous.yaml - yq -o=json previous.yaml > previous.json - - yq -o=json api.yaml > current.json + yq -o=json previous.yaml > previous-platform.json + + yq -o=json api.yaml > current-platform.json rm previous.yaml - - name: Run API diff - run: node scripts/api-diff.js + - name: Get Express API specs and generate JSON files + run: | + PREVIOUS_MERGE=$(git rev-list --merges master | head -n 2 | tail -n 1) + git show $PREVIOUS_MERGE:express-api.yaml > previous-express.yaml || echo "v0.0.0" > previous-express.yaml + yq -o=json previous-express.yaml > previous-express.json + + if [ -f express-api.yaml ]; then + yq -o=json express-api.yaml > current-express.json + else + echo "{}" > current-express.json + fi + rm -f previous-express.yaml + + - name: Run Platform API diff + run: node scripts/api-diff.js previous-platform.json current-platform.json platform-release.md + + - name: Run Express API diff + run: node scripts/api-diff.js previous-express.json current-express.json express-release.md + + - name: Combine release descriptions + run: | + PLATFORM_HAS_CONTENT=false + EXPRESS_HAS_CONTENT=false + + if [ -s platform-release.md ]; then + PLATFORM_HAS_CONTENT=true + fi + if [ -s express-release.md ]; then + EXPRESS_HAS_CONTENT=true + fi + + # Clear output + > release-description.md + + if [ "$PLATFORM_HAS_CONTENT" = true ] && [ "$EXPRESS_HAS_CONTENT" = true ]; then + # Both have content — nest under headers + echo "## Platform API" >> release-description.md + sed 's/^## /### /' platform-release.md >> release-description.md + echo "" >> release-description.md + echo "## Express API" >> release-description.md + sed 's/^## /### /' express-release.md >> release-description.md + elif [ "$PLATFORM_HAS_CONTENT" = true ]; then + cat platform-release.md >> release-description.md + elif [ "$EXPRESS_HAS_CONTENT" = true ]; then + cat express-release.md >> release-description.md + fi - name: Determine version id: version @@ -43,11 +88,11 @@ jobs: YEAR=$(date +%Y) MONTH=$(date +%m) DAY=$(date +%d) - + # Get the latest tag for current year.month.day CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v$YEAR.$MONTH.$DAY.0") echo "Current version: $CURRENT_VERSION" - + # Extract version number if [[ $CURRENT_VERSION == v$YEAR.$MONTH.$DAY.* ]]; then # If we already have a tag for today, increment its number @@ -57,7 +102,7 @@ jobs: # If this is the first tag for today, start at .1 NEW_VERSION="v$YEAR.$MONTH.$DAY.1" fi - + echo "New version: $NEW_VERSION" echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT @@ -76,7 +121,7 @@ jobs: echo "Release summary is empty, skipping release creation" exit 0 fi - + # Create GitHub Release gh release create ${{ steps.version.outputs.new_version }} \ --title "${{ steps.version.outputs.new_version }}" \ diff --git a/scripts/api-diff.js b/scripts/api-diff.js index 02e554f..728d3fb 100644 --- a/scripts/api-diff.js +++ b/scripts/api-diff.js @@ -1,8 +1,12 @@ const fs = require('fs'); -// Read the JSON files -const previousSpec = JSON.parse(fs.readFileSync('previous.json', 'utf8')); -const currentSpec = JSON.parse(fs.readFileSync('current.json', 'utf8')); +// Read the JSON files (paths configurable via CLI args) +const previousPath = process.argv[2] || 'previous.json'; +const currentPath = process.argv[3] || 'current.json'; +const outputPath = process.argv[4] || 'release-description.md'; + +const previousSpec = JSON.parse(fs.readFileSync(previousPath, 'utf8')); +const currentSpec = JSON.parse(fs.readFileSync(currentPath, 'utf8')); // Initialize change tracking const changes = { @@ -491,4 +495,4 @@ detectRenamedEndpoints(); const releaseDescription = generateReleaseNotes(); // Write release notes to markdown file -fs.writeFileSync('release-description.md', releaseDescription); +fs.writeFileSync(outputPath, releaseDescription); diff --git a/tests/generate-release.test.js b/tests/generate-release.test.js index 5bf5c11..04abee2 100644 --- a/tests/generate-release.test.js +++ b/tests/generate-release.test.js @@ -11,9 +11,11 @@ describe('Generate Release Description', async () => { beforeEach(() => { // Clean up any existing output files - if (fs.existsSync('release-description.md')) { - fs.unlinkSync('release-description.md'); - } + ['release-description.md', 'custom-output.md', 'previous.json', 'current.json'].forEach(file => { + if (fs.existsSync(file)) { + fs.unlinkSync(file); + } + }); }); scenarios.forEach(scenario => { @@ -43,9 +45,26 @@ describe('Generate Release Description', async () => { }); }); + it('supports custom file paths via CLI args', () => { + const scenarioDir = path.join(FIXTURES_DIR, 'new-route-and-method'); + + // Run the script with custom input/output paths + execSync( + `node scripts/api-diff.js ${path.join(scenarioDir, 'previous.json')} ${path.join(scenarioDir, 'current.json')} custom-output.md` + ); + + const actual = fs.readFileSync('custom-output.md', 'utf8').trim(); + const expected = fs.readFileSync( + path.join(scenarioDir, 'expected.md'), + 'utf8' + ).trim(); + + assert.strictEqual(actual, expected); + }); + afterEach(() => { // Clean up test files - ['previous.json', 'current.json', 'release-description.md'].forEach(file => { + ['previous.json', 'current.json', 'release-description.md', 'custom-output.md'].forEach(file => { if (fs.existsSync(file)) { fs.unlinkSync(file); }