diff --git a/.github/clean.rb b/.github/clean.rb index 189d4b5..e7e65cf 100644 --- a/.github/clean.rb +++ b/.github/clean.rb @@ -1,26 +1,19 @@ require "fileutils" -ALLOW_LIST = [ - ".git", - ".github", - ".gitignore", - ".npmignore", - ".openapi-generator-ignore", - "CHANGELOG.md", - "LICENSE", - "MIGRATION.md", - "README.md", - "node_modules", - "openapi", - "openapitools.json", - "tmp" -].freeze +# Version-targeted deletion: Deletes specified version directory only +# All workflows must provide version directory parameter -::Dir.each_child(::Dir.pwd) do |source| - next if ALLOW_LIST.include?(source) +target_dir = ARGV[0] - # Preserve test-output directories for multi-version POC testing - next if source.start_with?("test-output-") +if target_dir.nil? || target_dir.empty? + raise "Error: Version directory parameter required. Usage: ruby clean.rb " +end - ::FileUtils.rm_rf("#{::Dir.pwd}/#{source}") +# Delete only the specified directory +target_path = "#{::Dir.pwd}/#{target_dir}" +if ::File.exist?(target_path) + ::FileUtils.rm_rf(target_path) + puts "Deleted: #{target_path}" +else + puts "Directory not found (will be created during generation): #{target_path}" end diff --git a/.github/version.rb b/.github/version.rb index d061278..87d9f3a 100644 --- a/.github/version.rb +++ b/.github/version.rb @@ -1,22 +1,22 @@ require "yaml" -# Support both single config and multi-version config approaches -# For multi-version POC, we can specify which config file to use -config_file = ARGV[1] || "openapi/config.yml" +# Default to config-v20111101 file if not provided for backwards compatibility +# This is because automated openapi repository dispatch currently only generates v20111101 +config_file = ARGV[1] || "openapi/config-v20111101.yml" config = ::YAML.load(::File.read(config_file)) major, minor, patch = config["npmVersion"].split(".") +# Note: "skip" logic is handled by workflow (version.rb not called when skip selected) +# Only minor and patch bumps are supported (major version locked to API version) case ARGV[0] -when "major" - major = major.succ - minor = 0 - patch = 0 when "minor" minor = minor.succ patch = 0 when "patch" patch = patch.succ +else + raise "Invalid version bump type: #{ARGV[0]}. Supported: 'minor' or 'patch'" end config["npmVersion"] = "#{major}.#{minor}.#{patch}" diff --git a/.github/workflows/generate-multi-version.yml b/.github/workflows/generate-multi-version.yml deleted file mode 100644 index f5b7491..0000000 --- a/.github/workflows/generate-multi-version.yml +++ /dev/null @@ -1,149 +0,0 @@ -name: Generate Multi-Version SDK - -on: - workflow_dispatch: - inputs: - api_version: - description: 'API Version to generate' - required: true - default: 'v20111101' - type: choice - options: - - 'v20111101' - - 'v20250224' - - 'latest' - version_level: - description: "Bump version" - required: true - default: "patch" - type: choice - options: - - major - - minor - - patch - -jobs: - Generate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "20" - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1 - - # Determine configuration based on API version - - name: Set version configuration - id: config - run: | - API_VERSION="${{ github.event.inputs.api_version }}" - - echo "config_file=./openapi/config-$API_VERSION.yml" >> $GITHUB_OUTPUT - - if [ "$API_VERSION" = "latest" ]; then - echo "spec_url=https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/v20111101.yml" >> $GITHUB_OUTPUT - echo "output_dir=." >> $GITHUB_OUTPUT - else - echo "spec_url=https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/$API_VERSION.yml" >> $GITHUB_OUTPUT - echo "output_dir=./$API_VERSION" >> $GITHUB_OUTPUT - fi - - echo "branch_suffix=-$API_VERSION" >> $GITHUB_OUTPUT - - - name: Bump version - id: bump_version - run: echo "version=$(ruby .github/version.rb ${{ github.event.inputs.version_level }} ${{ steps.config.outputs.config_file }})" >> $GITHUB_OUTPUT - - # Note: Do NOT clean repo for multi-version - we want to preserve existing version directories - # The clean.rb script already protects version directories (v20111101, v20250224) - - - name: Install openapi-generator-cli - run: | - npm install @openapitools/openapi-generator-cli -g - - # Validate config file exists - - name: Validate config file - run: | - if [ ! -f "${{ steps.config.outputs.config_file }}" ]; then - echo "❌ Config file ${{ steps.config.outputs.config_file }} not found" - echo "Available config files:" - ls -la ./openapi/config*.yml || echo "No config files found" - exit 1 - fi - echo "βœ… Using config file: ${{ steps.config.outputs.config_file }}" - - - name: Generate SDK - run: | - echo "πŸ”§ Generating SDK for version: ${{ github.event.inputs.api_version }}" - echo "πŸ“„ Config: ${{ steps.config.outputs.config_file }}" - echo "🌐 Spec: ${{ steps.config.outputs.spec_url }}" - echo "πŸ“ Output: ${{ steps.config.outputs.output_dir }}" - - openapi-generator-cli generate \ - -i ${{ steps.config.outputs.spec_url }} \ - -g typescript-axios \ - -c ${{ steps.config.outputs.config_file }} \ - -t ./openapi/templates \ - -o ${{ steps.config.outputs.output_dir }} - - # Test TypeScript compilation - - name: Test TypeScript compilation - run: | - cd ${{ steps.config.outputs.output_dir }} - npm install - npm run build - echo "βœ… TypeScript compilation successful" - - - name: Get package info - id: package_info - run: | - PACKAGE_NAME=$(cat ${{ steps.config.outputs.output_dir }}/package.json | grep '"name"' | cut -d'"' -f4) - echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT - - - name: Create branch - run: git checkout -b "openapi-generator-${{ steps.bump_version.outputs.version }}${{ steps.config.outputs.branch_suffix }}" - - - name: Create commit - run: | - git config user.name "devexperience" - git config user.email "devexperience@mx.com" - git add . - git commit -m "Generated version ${{ steps.bump_version.outputs.version }} (${{ github.event.inputs.api_version }}) - - This pull request was automatically generated by a GitHub Action to generate version ${{ steps.bump_version.outputs.version }} of this library using API version ${{ github.event.inputs.api_version }}." - git push -u origin "openapi-generator-${{ steps.bump_version.outputs.version }}${{ steps.config.outputs.branch_suffix }}" - - - name: Create PR - run: | - gh pr create \ - --title "Generated version ${{ steps.bump_version.outputs.version }} (${{ github.event.inputs.api_version }})" \ - --body "This pull request was automatically generated using API version **${{ github.event.inputs.api_version }}**. - - ## Generation Details - - πŸ“„ **Config**: \`${{ steps.config.outputs.config_file }}\` - - 🌐 **Spec**: \`${{ steps.config.outputs.spec_url }}\` - - πŸ”’ **Version**: ${{ steps.bump_version.outputs.version }} - - πŸ“¦ **Package name**: ${{ steps.package_info.outputs.package_name }} - - βœ… **TypeScript compilation**: Passed - - ## Testing Notes - This is a multi-version SDK generation for testing purposes. The generated package uses a version-specific name to avoid conflicts with the production package. - - Please review the generated changes before merging." - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Slack notification - uses: ravsamhq/notify-slack-action@v2 - if: always() - with: - status: ${{ job.status }} - token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{repo}: {workflow} workflow" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}>" - footer: "<{workflow_url}|View Workflow>" - notify_when: "failure" - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 1c4f2bc..b0448a1 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -3,69 +3,158 @@ name: Generate on: workflow_dispatch: inputs: - version_level: - description: "Bump version" + api_version: + description: "API version to generate" required: true - default: "patch" type: choice options: - - major - - minor - - patch + - v20111101 + - v20250224 + version_bump: + description: "Version bump type" + required: true + default: "skip" + type: choice + options: + - skip + - minor + - patch jobs: + Validate: + runs-on: ubuntu-latest + outputs: + config_file: ${{ steps.validate.outputs.config_file }} + spec_url: ${{ steps.validate.outputs.spec_url }} + steps: + - uses: actions/checkout@v3 + - name: Validate configuration + id: validate + run: | + API_VERSION="${{ github.event.inputs.api_version }}" + CONFIG_FILE="openapi/config-${API_VERSION}.yml" + # Versioned spec URLs (manual workflow uses 'master' branch) + # Note: Manual workflow doesn't need commit SHA since developer controls timing + # CDN cache race condition only affects automated repository_dispatch triggers + # v20111101 -> https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/v20111101.yml + # v20250224 -> https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/v20250224.yml + SPEC_URL="https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/${API_VERSION}.yml" + + # Check config file exists + if [ ! -f "$CONFIG_FILE" ]; then + echo "❌ Config file not found: $CONFIG_FILE" + exit 1 + fi + + # Validate semantic versioning (major must match API version) + CURRENT_VERSION=$(grep 'npmVersion' "$CONFIG_FILE" | cut -d':' -f2 | tr -d ' ') + MAJOR_VERSION=$(echo "$CURRENT_VERSION" | cut -d'.' -f1) + + if [ "$API_VERSION" = "v20111101" ] && [ "$MAJOR_VERSION" != "2" ]; then + echo "❌ Semantic versioning error: v20111101 must have major version 2, found $MAJOR_VERSION" + exit 1 + fi + + if [ "$API_VERSION" = "v20250224" ] && [ "$MAJOR_VERSION" != "3" ]; then + echo "❌ Semantic versioning error: v20250224 must have major version 3, found $MAJOR_VERSION" + exit 1 + fi + + echo "βœ… Validation passed" + echo "config_file=$CONFIG_FILE" >> $GITHUB_OUTPUT + echo "spec_url=$SPEC_URL" >> $GITHUB_OUTPUT + Generate: runs-on: ubuntu-latest + needs: Validate steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1 - - name: Bump version - id: bump_version - run: echo "::set-output name=version::$(ruby .github/version.rb ${{ github.event.inputs.version_level }})" - - name: Clean repo - run: ruby .github/clean.rb - - name: Install openapi-generator-cli and Generate SDK - run: | - npm install @openapitools/openapi-generator-cli -g - - run: | - openapi-generator-cli generate \ - -i https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/mx_platform_api.yml \ - -g typescript-axios \ - -c ./openapi/config.yml \ - -t ./openapi/templates \ - -o ./latest - - name: Copy documentation to latest - run: | - cp LICENSE ./latest/LICENSE - cp CHANGELOG.md ./latest/CHANGELOG.md - cp MIGRATION.md ./latest/MIGRATION.md - - name: Create branch - run: git checkout -b "openapi-generator-${{ steps.bump_version.outputs.version }}" - - name: Create commit - run: | - git config user.name "devexperience" - git config user.email "devexperience@mx.com" - git add . - git commit -m "Generated version ${{ steps.bump_version.outputs.version }} + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1 + - name: Bump version + id: bump_version + run: | + if [ "${{ github.event.inputs.version_bump }}" != "skip" ]; then + NEW_VERSION=$(ruby .github/version.rb ${{ github.event.inputs.version_bump }} ${{ needs.Validate.outputs.config_file }}) + else + NEW_VERSION=$(jq -r '.version' ./${{ github.event.inputs.api_version }}/package.json 2>/dev/null || grep 'npmVersion' ${{ needs.Validate.outputs.config_file }} | cut -d':' -f2 | tr -d ' ') + fi + echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "πŸ“¦ Version: $NEW_VERSION" + - name: Clean repo + run: ruby .github/clean.rb ${{ github.event.inputs.api_version }} + - name: Copy generator ignore rules + run: cp .openapi-generator-ignore ./${{ github.event.inputs.api_version }}/ + - name: Install openapi-generator-cli and Generate SDK + run: npm install @openapitools/openapi-generator-cli -g + - name: Generate SDK + run: | + openapi-generator-cli generate \ + -i ${{ needs.Validate.outputs.spec_url }} \ + -g typescript-axios \ + -c ${{ needs.Validate.outputs.config_file }} \ + -t ./openapi/templates \ + -o ./${{ github.event.inputs.api_version }} + - name: Update CHANGELOG + run: | + VERSION=$(jq -r '.version' ./${{ github.event.inputs.api_version }}/package.json) + DATE=$(date +%Y-%m-%d) + + # Find the line number of the first version entry (first line starting with ##) + FIRST_ENTRY_LINE=$(grep -n "^##" CHANGELOG.md | head -1 | cut -d: -f1) + + # Extract header (everything before first entry) + head -n $((FIRST_ENTRY_LINE - 1)) CHANGELOG.md > /tmp/new_changelog.txt + + # Add new entry + cat >> /tmp/new_changelog.txt << EOF + + ## [$VERSION] - $DATE (${{ github.event.inputs.api_version }} API) + Updated ${{ github.event.inputs.api_version }} API specification. + [See full API changelog](https://docs.mx.com/resources/changelog/platform) + EOF + + # Add rest of file (from first entry onwards) + tail -n +$FIRST_ENTRY_LINE CHANGELOG.md >> /tmp/new_changelog.txt + + # Replace original + mv /tmp/new_changelog.txt CHANGELOG.md + - name: Copy documentation + run: | + cp LICENSE ./${{ github.event.inputs.api_version }}/LICENSE + cp CHANGELOG.md ./${{ github.event.inputs.api_version }}/CHANGELOG.md + cp MIGRATION.md ./${{ github.event.inputs.api_version }}/MIGRATION.md + - name: Create branch + run: git checkout -b "sdk/generate-api-${{ github.event.inputs.api_version }}-${{ steps.bump_version.outputs.version }}" + - name: Create commit + run: | + git config user.name "devexperience" + git config user.email "devexperience@mx.com" + git add . + git commit -m "Generated API ${{ github.event.inputs.api_version }} SDK for version ${{ steps.bump_version.outputs.version }} - This pull request was automatically generated by a GitHub Action to generate version ${{ steps.bump_version.outputs.version }} of this library." - git push -u origin "openapi-generator-${{ steps.bump_version.outputs.version }}" - - name: Create PR - run: gh pr create -f - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Slack notification - uses: ravsamhq/notify-slack-action@v2 - if: always() - with: - status: ${{ job.status }} - token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{repo}: {workflow} workflow" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}>" - footer: "<{workflow_url}|View Workflow>" - notify_when: "failure" - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + This pull request was automatically generated by a GitHub Action. + + API Version: ${{ github.event.inputs.api_version }} + SDK Version: ${{ steps.bump_version.outputs.version }} + Version Bump: ${{ github.event.inputs.version_bump }} + ${{ github.event.inputs.version_bump == 'skip' && '⚠️ NO VERSION BUMP - This is a review-only generation for validation before release.' || '' }}" + git push -u origin "sdk/generate-api-${{ github.event.inputs.api_version }}-${{ steps.bump_version.outputs.version }}" + - name: Create PR + run: gh pr create -f + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Slack notification + uses: ravsamhq/notify-slack-action@v2 + if: always() + with: + status: ${{ job.status }} + token: ${{ secrets.GITHUB_TOKEN }} + notification_title: "{repo}: {workflow} workflow" + message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}>" + footer: "<{workflow_url}|View Workflow>" + notify_when: "failure" + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/generate_publish_release.yml b/.github/workflows/generate_publish_release.yml index 10832e6..4211d57 100644 --- a/.github/workflows/generate_publish_release.yml +++ b/.github/workflows/generate_publish_release.yml @@ -4,9 +4,53 @@ on: repository_dispatch: types: [generate_publish_release] +env: + # Default to v20111101 only for backwards compatibility + # When openapi repo sends api_versions, use that instead + VERSIONS_TO_GENERATE: ${{ github.event.client_payload.api_versions || 'v20111101' }} + jobs: + Setup: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Set up matrix + id: set-matrix + run: | + VERSIONS="${{ env.VERSIONS_TO_GENERATE }}" + echo "Versions to generate: $VERSIONS" + + # Build matrix JSON + MATRIX_JSON='{"include":[' + FIRST=true + + for VERSION in $(echo $VERSIONS | tr ',' ' '); do + if [ "$FIRST" = false ]; then + MATRIX_JSON+=',' + fi + FIRST=false + + # Map version to config file and major version + if [ "$VERSION" = "v20111101" ]; then + CONFIG="openapi/config-v20111101.yml" + elif [ "$VERSION" = "v20250224" ]; then + CONFIG="openapi/config-v20250224.yml" + fi + + MATRIX_JSON+="{\"api_version\":\"$VERSION\",\"config_file\":\"$CONFIG\"}" + done + + MATRIX_JSON+=']}' + echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT + echo "Matrix: $MATRIX_JSON" + Generate: runs-on: ubuntu-latest + needs: Setup + strategy: + matrix: ${{ fromJson(needs.Setup.outputs.matrix) }} + fail-fast: false steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -17,24 +61,114 @@ jobs: ruby-version: 3.1 - name: Bump version id: bump_version - run: echo "::set-output name=version::$(ruby .github/version.rb ${{ github.event.client_payload.version }})" + run: | + NEW_VERSION=$(ruby .github/version.rb ${{ github.event.client_payload.version || 'patch' }} ${{ matrix.config_file }}) + echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT - name: Clean repo - run: ruby .github/clean.rb + run: ruby .github/clean.rb ${{ matrix.api_version }} + - name: Copy generator ignore rules + run: cp .openapi-generator-ignore ./${{ matrix.api_version }}/ - name: Install openapi-generator-cli + run: npm install @openapitools/openapi-generator-cli -g + - name: Generate SDK run: | - npm install @openapitools/openapi-generator-cli -g - - run: | + # Versioned spec URLs with commit SHA to avoid GitHub CDN cache race condition + # Problem: GitHub's raw.githubusercontent.com CDN caches files for 5 minutes + # If openapi repo commits and immediately triggers this workflow, CDN may serve stale spec + # Solution: Use commit SHA in URL to bypass cache and guarantee correct spec version + # Falls back to 'master' if openapi doesn't send commit_sha openapi-generator-cli generate \ - -i https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/mx_platform_api.yml \ + -i https://raw.githubusercontent.com/mxenabled/openapi/${{ github.event.client_payload.commit_sha || 'master' }}/openapi/${{ matrix.api_version }}.yml \ -g typescript-axios \ - -c ./openapi/config.yml \ + -c ${{ matrix.config_file }} \ -t ./openapi/templates \ - -o ./latest - - name: Copy documentation to latest + -o ./${{ matrix.api_version }} + - name: Copy documentation + run: | + cp LICENSE ./${{ matrix.api_version }}/LICENSE + cp CHANGELOG.md ./${{ matrix.api_version }}/CHANGELOG.md + cp MIGRATION.md ./${{ matrix.api_version }}/MIGRATION.md + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: generated-${{ matrix.api_version }} + path: ./${{ matrix.api_version }} + + Commit-and-Push: + runs-on: ubuntu-latest + needs: [Setup, Generate] + steps: + - uses: actions/checkout@v3 + - name: Download all artifacts + uses: actions/download-artifact@v3 + with: + path: ./generated + - name: Move generated files and track versions + id: track_versions + run: | + GENERATED_VERSIONS="" + + for dir in ./generated/generated-*; do + VERSION=$(basename "$dir" | sed 's/generated-//') + mv "$dir" "./$VERSION" + GENERATED_VERSIONS="$GENERATED_VERSIONS $VERSION" + done + + echo "generated_versions=$GENERATED_VERSIONS" >> $GITHUB_OUTPUT + - name: Update CHANGELOG run: | - cp LICENSE ./latest/LICENSE - cp CHANGELOG.md ./latest/CHANGELOG.md - cp MIGRATION.md ./latest/MIGRATION.md + GENERATED_VERSIONS="${{ steps.track_versions.outputs.generated_versions }}" + DATE=$(date +%Y-%m-%d) + + # Only update if something was generated + if [ -z "$GENERATED_VERSIONS" ]; then + exit 0 + fi + + # Initialize version variables as empty + V20111101_VERSION="" + V20250224_VERSION="" + + # Read versions only for versions that were actually generated + for VERSION in $GENERATED_VERSIONS; do + if [ "$VERSION" = "v20111101" ]; then + V20111101_VERSION=$(jq -r '.version' ./v20111101/package.json 2>/dev/null) + elif [ "$VERSION" = "v20250224" ]; then + V20250224_VERSION=$(jq -r '.version' ./v20250224/package.json 2>/dev/null) + fi + done + + # Find the line number of the first version entry (first line starting with ##) + FIRST_ENTRY_LINE=$(grep -n "^##" CHANGELOG.md | head -1 | cut -d: -f1) + + # Extract header (everything before first entry) + head -n $((FIRST_ENTRY_LINE - 1)) CHANGELOG.md > /tmp/new_changelog.txt + + # Build and add changelog entries ONLY for versions that were actually generated + # v20250224 first (newer API version), then v20111101 + if [ ! -z "$V20250224_VERSION" ]; then + cat >> /tmp/new_changelog.txt << EOF + + ## [$V20250224_VERSION] - $DATE (v20250224 API) + Updated v20250224 API specification. + [See full API changelog](https://docs.mx.com/resources/changelog/platform) + EOF + fi + + if [ ! -z "$V20111101_VERSION" ]; then + cat >> /tmp/new_changelog.txt << EOF + + ## [$V20111101_VERSION] - $DATE (v20111101 API) + Updated v20111101 API specification. + [See full API changelog](https://docs.mx.com/resources/changelog/platform) + EOF + fi + + # Add rest of file (from first entry onwards) + tail -n +$FIRST_ENTRY_LINE CHANGELOG.md >> /tmp/new_changelog.txt + + # Replace original + mv /tmp/new_changelog.txt CHANGELOG.md - name: Checkout master run: git checkout master - name: Create commit @@ -42,30 +176,30 @@ jobs: git config user.name "devexperience" git config user.email "devexperience@mx.com" git add . - git commit -m "Generated version ${{ steps.bump_version.outputs.version }} + git commit -m "Generated SDK versions: ${{ env.VERSIONS_TO_GENERATE }} - This commit was automatically created by a GitHub Action to generate version ${{ steps.bump_version.outputs.version }} of this library." + This commit was automatically created by a GitHub Action." - name: Push to master run: git push origin master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Generate access token - id: generate_token - uses: tibdex/github-app-token@v1 - with: - app_id: ${{ secrets.PAPI_SDK_APP_ID }} - installation_id: ${{ secrets.PAPI_SDK_INSTALLATION_ID }} - private_key: ${{ secrets.PAPI_SDK_PRIVATE_KEY }} + + Publish-and-Release: + runs-on: ubuntu-latest + needs: [Setup, Commit-and-Push] + strategy: + matrix: ${{ fromJson(needs.Setup.outputs.matrix) }} + steps: - name: Publish - uses: peter-evans/repository-dispatch@v2 + uses: ./.github/workflows/publish.yml with: - token: ${{ steps.generate_token.outputs.token }} - event-type: publish_sdk + version_directory: ${{ matrix.api_version }} + secrets: inherit - name: Release - uses: peter-evans/repository-dispatch@v2 + uses: ./.github/workflows/release.yml with: - token: ${{ steps.generate_token.outputs.token }} - event-type: release_sdk + version_directory: ${{ matrix.api_version }} + secrets: inherit - name: Slack notification uses: ravsamhq/notify-slack-action@v2 if: always() @@ -73,7 +207,7 @@ jobs: status: ${{ job.status }} token: ${{ secrets.GITHUB_TOKEN }} notification_title: "{repo}: {workflow} workflow" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}>" + message_format: "{emoji} Generated and published ${{ matrix.api_version }}" footer: "<{workflow_url}|View Workflow>" notify_when: "failure" env: diff --git a/.github/workflows/on-push-master.yml b/.github/workflows/on-push-master.yml index d064f38..4c8a300 100644 --- a/.github/workflows/on-push-master.yml +++ b/.github/workflows/on-push-master.yml @@ -4,14 +4,58 @@ on: push: branches: [master] paths: - # Latest version SDK files - - 'latest/**' + - 'v20111101/**' + - 'v20250224/**' jobs: - Publish: + # Check for skip-publish flag in commit message. This allows skipping publish/release for specific scenarios, + # such as testing generated code, migrating files to new paths, etc. + check-skip-publish: + runs-on: ubuntu-latest + outputs: + skip_publish: ${{ steps.check.outputs.skip_publish }} + steps: + - name: Check for [skip-publish] flag in commit message + id: check + run: | + COMMIT_MSG="${{ github.event.head_commit.message }}" + if [[ "$COMMIT_MSG" == *"[skip-publish]"* ]]; then + echo "skip_publish=true" >> $GITHUB_OUTPUT + echo "🚫 [skip-publish] flag detected - skipping all publish/release jobs" + else + echo "skip_publish=false" >> $GITHUB_OUTPUT + echo "βœ… No skip flag - proceeding with publish/release" + fi + + # Matrix-based publish and release: runs one job per version, + # conditionally based on path changes + # Each matrix iteration only executes if both conditions are true: + # 1. [skip-publish] flag is NOT present in commit message + # 2. Files in this version's directory were modified in the commit + publish: + needs: check-skip-publish + strategy: + matrix: + version: + - api_version: v20111101 + - api_version: v20250224 + fail-fast: false + if: needs.check-skip-publish.outputs.skip_publish == 'false' && contains(github.event.head_commit.modified, matrix.version.api_version) uses: ./.github/workflows/publish.yml + with: + version_directory: ${{ matrix.version.api_version }} secrets: inherit - - Release: + + release: + needs: [check-skip-publish, publish] + strategy: + matrix: + version: + - api_version: v20111101 + - api_version: v20250224 + fail-fast: false + if: needs.check-skip-publish.outputs.skip_publish == 'false' && contains(github.event.head_commit.modified, matrix.version.api_version) uses: ./.github/workflows/release.yml + with: + version_directory: ${{ matrix.version.api_version }} secrets: inherit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 26e32f4..10a0ca4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,10 +1,18 @@ name: Publish on: - repository_dispatch: - types: [publish_sdk] workflow_dispatch: + inputs: + version_directory: + description: 'Version directory to publish from' + required: true + type: string workflow_call: + inputs: + version_directory: + description: 'Version directory to publish from' + required: true + type: string jobs: Publish: @@ -16,10 +24,10 @@ jobs: node-version: "20.x" registry-url: "https://registry.npmjs.org" - name: Install and Publish - working-directory: ./latest + working-directory: ./${{ inputs.version_directory }} run: | npm install - npm publish --tag next + npm publish env: NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} - name: Slack notification diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35ae98a..a4a1f9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,18 @@ name: Release on: - repository_dispatch: - types: [release_sdk] + workflow_dispatch: + inputs: + version_directory: + description: 'Version directory to read version from' + required: true + type: string workflow_call: + inputs: + version_directory: + description: 'Version directory to read version from' + required: true + type: string jobs: Release: @@ -17,7 +26,7 @@ jobs: ruby-version: 3.1 - name: Read version id: read_version - run: echo "::set-output name=version::$(jq -r '.version' ./latest/package.json)" + run: echo "version=$(jq -r '.version' ./${{ inputs.version_directory }}/package.json)" >> $GITHUB_OUTPUT - name: Create tag and release run: | gh release create "v${{ steps.read_version.outputs.version }}" diff --git a/.github/workflows/test-multi-version.yml b/.github/workflows/test-multi-version.yml deleted file mode 100644 index e80fd13..0000000 --- a/.github/workflows/test-multi-version.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: Test Multi-Version SDK - -on: - workflow_dispatch: - inputs: - api_version: - description: 'API Version to generate' - required: true - default: 'v20111101' - type: choice - options: - - 'v20111101' - - 'v20250224' - - 'latest' - test_level: - description: 'Testing level' - required: true - default: 'generate_only' - type: choice - options: - - 'generate_only' # No commits, no publishing - - 'github_packages' # Publish to GitHub Packages (safe staging) - -jobs: - Test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "20" - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1 - - # Determine configuration based on API version - - name: Set version configuration - id: config - run: | - API_VERSION="${{ github.event.inputs.api_version }}" - - echo "config_file=./openapi/config-$API_VERSION.yml" >> $GITHUB_OUTPUT - echo "output_dir=./test-output-$API_VERSION" >> $GITHUB_OUTPUT - - if [ "$API_VERSION" = "latest" ]; then - echo "spec_url=https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/v20111101.yml" >> $GITHUB_OUTPUT - else - echo "spec_url=https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/$API_VERSION.yml" >> $GITHUB_OUTPUT - fi - - # Create output directory for testing versions - - name: Prepare test output directory - run: mkdir -p ${{ steps.config.outputs.output_dir }} - - # Install OpenAPI Generator - - name: Install openapi-generator-cli - run: | - npm install @openapitools/openapi-generator-cli -g - - # Validate config file exists - - name: Validate config file - run: | - if [ ! -f "${{ steps.config.outputs.config_file }}" ]; then - echo "❌ Config file ${{ steps.config.outputs.config_file }} not found" - echo "Available config files:" - ls -la ./openapi/config*.yml || echo "No config files found" - exit 1 - fi - echo "βœ… Using config file: ${{ steps.config.outputs.config_file }}" - - # Generate SDK - - name: Generate SDK - run: | - echo "πŸ”§ Generating SDK for version: ${{ github.event.inputs.api_version }}" - echo "πŸ“„ Config: ${{ steps.config.outputs.config_file }}" - echo "🌐 Spec: ${{ steps.config.outputs.spec_url }}" - echo "πŸ“ Output: ${{ steps.config.outputs.output_dir }}" - - openapi-generator-cli generate \ - -i ${{ steps.config.outputs.spec_url }} \ - -g typescript-axios \ - -c ${{ steps.config.outputs.config_file }} \ - -t ./openapi/templates \ - -o ${{ steps.config.outputs.output_dir }} - - # Test TypeScript compilation - - name: Test TypeScript compilation - run: | - cd ${{ steps.config.outputs.output_dir }} - npm install - npm run build - echo "βœ… TypeScript compilation successful" - - # Archive generated code as artifact for inspection - - name: Archive generated code - uses: actions/upload-artifact@v3 - with: - name: generated-sdk-${{ github.event.inputs.api_version }} - path: ${{ steps.config.outputs.output_dir }} - retention-days: 7 - - # Validation summary - - name: Validation summary - run: | - echo "πŸŽ‰ Generation successful for ${{ github.event.inputs.api_version }}!" - - # Show package.json name for verification - PACKAGE_NAME=$(cat ${{ steps.config.outputs.output_dir }}/package.json | grep '"name"' | cut -d'"' -f4) - PACKAGE_VERSION=$(cat ${{ steps.config.outputs.output_dir }}/package.json | grep '"version"' | cut -d'"' -f4) - echo "πŸ“¦ Generated package: $PACKAGE_NAME@$PACKAGE_VERSION" - - # Count API files - API_COUNT=$(find ${{ steps.config.outputs.output_dir }} -name "*api.ts" | wc -l) - echo "πŸ” Generated API files: $API_COUNT" - - # Show file structure - echo "" - echo "πŸ“‚ Generated file structure:" - ls -lh ${{ steps.config.outputs.output_dir }} - - # Publish to GitHub Packages (Safe Testing) - - name: Publish to GitHub Packages - if: ${{ github.event.inputs.test_level == 'github_packages' }} - run: | - cd ${{ steps.config.outputs.output_dir }} - - # Configure for GitHub Packages - npm config set registry https://npm.pkg.github.com - npm config set //npm.pkg.github.com/:_authToken ${{ secrets.GITHUB_TOKEN }} - - # Publish the test package - npm publish - - PACKAGE_NAME=$(cat package.json | grep '"name"' | cut -d'"' -f4) - PACKAGE_VERSION=$(cat package.json | grep '"version"' | cut -d'"' -f4) - - echo "βœ… Published test package to GitHub Packages" - echo "πŸ“¦ Package: $PACKAGE_NAME@$PACKAGE_VERSION" - echo "πŸ”— View at: https://github.com/mxenabled/mx-platform-node/packages" - - - name: Slack notification - if: always() - uses: ravsamhq/notify-slack-action@v2 - with: - status: ${{ job.status }} - token: ${{ secrets.GITHUB_TOKEN }} - notification_title: "{repo}: {workflow} workflow" - message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}>" - footer: "<{workflow_url}|View Workflow>" - notify_when: "failure" - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/MIGRATION.md b/MIGRATION.md index c77937e..5ac2994 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,37 @@ # Migration Guide +## Upgrading from v20111101 (v2.x) to v20250224 (v3.x) + +The v20250224 API is now available, and v3.0.0 of this SDK provides support as an independent major version. + +### Installation + +The two API versions are published as separate major versions of the same npm package: + +**For v20111101 API:** +```bash +npm install mx-platform-node@^2 +``` + +**For v20250224 API:** +```bash +npm install mx-platform-node@^3 +``` + +### Migration Path + +1. **Review API Changes**: Consult the [MX Platform API Migration Guide](https://docs.mx.com/api-reference/platform-api/overview/migration) for breaking changes and new features +2. **Update Package**: Update your `package.json` to use `mx-platform-node@^3` +3. **Update Imports**: Both APIs have similar structure, but review type definitions for any breaking changes +4. **Run Tests**: Validate your code works with the new SDK version +5. **Deploy**: Update production once validated + +### Benefits of TypeScript + +Since this is a TypeScript SDK, the compiler will help catch most compatibility issues at compile time when you update to v3.x. + +--- + ## Upgrading to v2.0.0 from v1.10.0 or earlier ### Breaking Change: API Class Restructure diff --git a/README.md b/README.md index 777ca83..891801b 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,55 @@ *This project is currently in **Beta**. Please open up an issue [here](https://github.com/mxenabled/mx-platform-node/issues) to report issues using the MX Platform Node.js library.* -# MX Platform Node.js +# MX Platform Node.js SDK -The [MX Platform API](https://www.mx.com/products/platform-api) is a powerful, fully-featured API designed to make aggregating and enhancing financial data easy and reliable. It can seamlessly connect your app or website to tens of thousands of financial institutions. +This repository contains the Node.js SDK for the [MX Platform API](https://www.mx.com/products/platform-api). The SDK supports multiple API versions, published as independent major versions of the same npm package. -## Documentation +## Which API Version Do You Need? -Examples for the API endpoints can be found [here.](https://docs.mx.com/api) - -## Requirements - -The generated Node module can be used in the following environments: - -Environment -* Node.js -* Webpack -* Browserify - -Language level -* ES5 - you must have a Promises/A+ library installed -* ES6 - -Module system -* CommonJS -* ES6 module system +| API Version | npm Package | Documentation | +|---|---|---| +| **v20111101** | `mx-platform-node@^2` | [v20111101 SDK README](./v20111101/README.md) | +| **v20250224** | `mx-platform-node@^3` | [v20250224 SDK README](./v20250224/README.md) | ## Installation -To build and compile the TypeScript sources to JavaScript use: +```bash +# For v20111101 API +npm install mx-platform-node@^2 -```shell -npm install mx-platform-node +# For v20250224 API +npm install mx-platform-node@^3 ``` -## Getting Started - -In order to make requests, you will need to [sign up](https://dashboard.mx.com/sign_up) for the MX Platform API and get a `Client ID` and `API Key`. - -Please follow the [installation](#installation) procedure and then run the following code to create your first User: +## API Migration -```javascript -import { Configuration, UsersApi } from 'mx-platform-node'; +If you're upgrading from v20111101 to v20250224, see the [MX Platform API Migration Guide](https://docs.mx.com/api-reference/platform-api/overview/migration). -const configuration = new Configuration({ - // Configure with your Client ID/API Key from https://dashboard.mx.com - username: 'Your Client ID', - password: 'Your API Key', +## Repository Structure - // Configure environment. https://int-api.mx.com for development, https://api.mx.com for production - basePath: 'https://int-api.mx.com', +This repository uses [OpenAPI Generator](https://openapi-generator.tech) to automatically generate TypeScript SDKs from OpenAPI specifications. - baseOptions: { - headers: { - Accept: 'application/vnd.mx.api.v1+json' - } - } -}); - -const usersApi = new UsersApi(configuration); - -const requestBody = { - user: { - metadata: 'Creating a user!' - } -}; - -const response = await usersApi.createUser(requestBody); - -console.log(response.data); +``` +v20111101/ # Generated SDK for v20111101 API +v20250224/ # Generated SDK for v20250224 API +openapi/ # SDK generation configuration and templates +.github/workflows/ # Automation for generation, publishing, and releasing +docs/ # Repository documentation and contribution guidelines ``` -## Upgrading from v1.x? - -> **⚠️ Breaking Changes in v2.0.0:** If you're upgrading from v1.10.0 or earlier, the API structure has changed significantly. See the [Migration Guide](MIGRATION.md) for detailed instructions on updating your code. +## For Contributors -## Development +For detailed information about: +- How the SDK generation process works +- How to contribute to this repository +- Publishing and release workflows +- Architecture and design decisions -This project was generated by the [OpenAPI Generator](https://openapi-generator.tech). To generate this library, verify you have the latest version of the `openapi-generator-cli` found [here.](https://github.com/OpenAPITools/openapi-generator#17---npm) +Please see the [docs/](./docs/) directory. -Running the following command in this repo's directory will generate this library using the [MX Platform API OpenAPI spec](https://github.com/mxenabled/openapi/blob/master/openapi/mx_platform_api.yml) with our [configuration and templates.](https://github.com/mxenabled/mx-platform-ruby/tree/master/openapi) -```shell -openapi-generator-cli generate \ --i https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/mx_platform_api.yml \ --g typescript-axios \ --c ./openapi/config.yml \ --t ./openapi/templates -``` +## Support -## Contributing +- **SDK Issues**: [Open an issue](https://github.com/mxenabled/mx-platform-node/issues) +- **API Documentation**: [MX Platform API Docs](https://docs.mx.com) +- **API Changelog**: [MX Platform Changelog](https://docs.mx.com/resources/changelog/platform) -Please [open an issue](https://github.com/mxenabled/mx-platform-node/issues) or [submit a pull request.](https://github.com/mxenabled/mx-platform-node/pulls) diff --git a/docs/Adding-a-New-API-Version.md b/docs/Adding-a-New-API-Version.md new file mode 100644 index 0000000..e61e7dc --- /dev/null +++ b/docs/Adding-a-New-API-Version.md @@ -0,0 +1,302 @@ +# Adding a New API Version to mx-platform-node + +**Document Purpose**: Step-by-step guide for adding support for a new API version (e.g., `v20300101`) to the mx-platform-node repository. + +**Last Updated**: January 27, 2026 +**Time to Complete**: 30-45 minutes +**Prerequisites**: Familiarity with the multi-version architecture (see [Multi-Version-SDK-Flow.md](Multi-Version-SDK-Flow.md)) + +--- + +## Overview + +When the OpenAPI repository releases a new API version, adding it to mx-platform-node requires four main steps: +1. Create a configuration file for the new API version +2. Update workflow files to include the new version in the matrix +3. Coordinate with the OpenAPI repository on payload format +4. Verify the setup works correctly + +The process is designed to be self-contained and non-breakingβ€”existing versions continue to work regardless of whether you've added new ones. + +--- + +## Step 1: Create Configuration File + +Create a new configuration file for your API version: `openapi/config-v20300101.yml` + +```yaml +--- +generatorName: typescript-axios +npmName: mx-platform-node +npmVersion: 4.0.0 # New major version for new API +apiVersion: v20300101 +supportsES6: true +.openapi-generator-ignore: true +``` + +**Critical: Semantic Versioning Rule** + +Major version must be unique and increment sequentially: +- v20111101 API β†’ npm version 2.x.x +- v20250224 API β†’ npm version 3.x.x +- v20300101 API β†’ npm version 4.x.x (NEW) + +This ensures moving between major versions always indicates an API change. + +**File Locations and Naming** + +- **Config file**: `openapi/config-v.yml` +- **Generated directory**: `v/` (created automatically on first generation) +- **API version format**: Must match spec files in openapi repository (e.g., `v20300101.yml`) + +### Verify the Config File + +Test that your config file is valid YAML and contains all required fields: +```bash +ruby -e "require 'yaml'; puts YAML.load(File.read('openapi/config-v20300101.yml'))" +``` + +Should output valid parsed YAML without errors. + +--- + +## Step 2: Update Workflow Files + +### 2.1 Update on-push-master.yml + +Add the new version to the matrix strategy: + +**File**: `.github/workflows/on-push-master.yml` + +**Find this section**: +```yaml +strategy: + matrix: + version: + - api_version: v20111101 + npm_version: 2 + - api_version: v20250224 + npm_version: 3 +``` + +**Add new entry**: +```yaml +strategy: + matrix: + version: + - api_version: v20111101 + npm_version: 2 + - api_version: v20250224 + npm_version: 3 + - api_version: v20300101 # NEW + npm_version: 4 +``` + +### 2.2 Update Path Triggers + +In the same file, add the new path trigger: + +**Find this section**: +```yaml +on: + push: + branches: [master] + paths: + - 'v20111101/**' + - 'v20250224/**' +``` + +**Add new path**: +```yaml +on: + push: + branches: [master] + paths: + - 'v20111101/**' + - 'v20250224/**' + - 'v20300101/**' # NEW +``` + +This ensures that when changes to `v20300101/` are pushed to master, the publish and release workflows automatically trigger. + +### 2.3 Verify Workflow Syntax + +Check that your YAML is valid: +```bash +ruby -e "require 'yaml'; puts YAML.load(File.read('.github/workflows/on-push-master.yml'))" +``` + +--- + +## Step 3: Coordinate with OpenAPI Repository + +The OpenAPI repository must be updated to send the new API version in the `repository_dispatch` event payload. + +### What Needs to Change in openapi repo + +When openapi repository wants to trigger generation for the new version, it should send: + +```json +{ + "api_versions": "v20111101,v20250224,v20300101" +} +``` + +**Example curl command** (what openapi repo would use): +```bash +curl -X POST \ + https://api.github.com/repos/mxenabled/mx-platform-node/dispatches \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -d '{ + "event_type": "generate_sdk", + "client_payload": { + "api_versions": "v20111101,v20250224,v20300101" + } + }' +``` + +### Backward Compatibility + +If the OpenAPI repository doesn't send the new version in the payload: +- `generate_publish_release.yml` defaults to `v20111101` only +- Existing versions continue to work unchanged +- New version won't generate until explicitly included in the payload + +This is intentionalβ€”allows phased rollout without breaking existing workflows. + +### Transition Plan + +**Phase 1**: New config exists but openapi repo doesn't send the version +- System works with v20111101 and v20250224 only +- New version `v20300101/` directory doesn't get created +- No errors or issues + +**Phase 2**: OpenAPI repo updated to send new version +- Next `generate_publish_release.yml` run includes all three versions +- `v20300101/` directory created automatically +- All three versions published to npm in parallel + +--- + +## Step 4: Verify the Setup + +### 4.1 Manual Generation Test + +Test that your new version can be generated manually before waiting for upstream changes. + +Run the `generate.yml` workflow manually: +1. Go to GitHub Actions β†’ `generate.yml` +2. Click "Run workflow" +3. **api_version**: Select `v20300101` (should appear in dropdown) +4. **version_bump**: Select `skip` (for testing, no version bump) +5. Click "Run workflow" + +**Expected Results**: +- Workflow completes successfully +- A new PR is created with branch name: `openapi-generator-v20300101-4.0.0` +- PR contains generated SDK files in new `v20300101/` directory + +### 4.2 Verify Generated Structure + +Once PR is created (before merging), verify the generated files: + +```bash +# Check directory was created +ls -la v20300101/ + +# Verify package.json has correct version +cat v20300101/package.json | grep -A 2 '"version"' + +# Should show: +# "version": "4.0.0", +# "apiVersion": "v20300101", +``` + +### 4.3 Check npm Package Metadata + +The generated `package.json` should have: + +```json +{ + "name": "mx-platform-node", + "version": "4.0.0", + "description": "MX Platform Node.js SDK (v20300101 API)", + "apiVersion": "v20300101" +} +``` + +This ensures npm registry will show the correct API version in the package description. + +### 4.4 Verify on-push-master.yml Would Trigger + +Check that your path trigger configuration is correct: + +```bash +# This confirms the path syntax is valid +git status --porcelain | grep "v20300101/" +``` + +After merging the PR, pushing to master with changes in `v20300101/` should automatically trigger `on-push-master.yml`. + +--- + +## Checklist + +Use this checklist to verify you've completed all steps: + +- [ ] Created `openapi/config-v20300101.yml` with correct syntax +- [ ] Major version in config is unique and sequential (4.0.0 for v20300101) +- [ ] Updated `.github/workflows/on-push-master.yml` matrix with new version +- [ ] Updated `.github/workflows/on-push-master.yml` paths with `v20300101/**` +- [ ] Verified workflow YAML syntax is valid +- [ ] Coordinated with OpenAPI repository on payload changes +- [ ] Ran `generate.yml` manual test with new version +- [ ] Verified generated `package.json` has correct version and apiVersion +- [ ] Verified PR would be created with correct branch name format +- [ ] Merged test PR to master (or closed it if testing only) +- [ ] Confirmed no errors in existing version workflows + +--- + +## Troubleshooting + +### Config file not found during generation +**Cause**: Filename doesn't match API version +**Solution**: Verify config file is named exactly `openapi/config-v20300101.yml` + +### New version doesn't appear in generate.yml dropdown +**Cause**: Config file syntax error or not recognized +**Solution**: Verify YAML syntax with `ruby -e "require 'yaml'; puts YAML.load(File.read('openapi/config-v20300101.yml'))"` + +### Generated version is 2.x.x or 3.x.x instead of 4.0.0 +**Cause**: Wrong major version in config file +**Solution**: Update `npmVersion: 4.0.0` in config file to use unique major version + +### on-push-master.yml doesn't trigger after merge +**Cause**: Path trigger syntax incorrect +**Solution**: Verify path is exactly `v20300101/**` with forward slashes + +### Existing versions break after adding new version +**Cause**: Matrix syntax error or bad YAML +**Solution**: Verify on-push-master.yml YAML is valid; test existing workflows still work + +--- + +## Next Steps + +Once verified: + +1. **Commit changes**: Push config and workflow updates to a feature branch +2. **Create PR**: Get code review of workflow changes +3. **Merge PR**: Once approved, merge to master +4. **Wait for OpenAPI updates**: New version won't generate until OpenAPI repo sends it in payload +5. **Monitor first generation**: Watch the automatic `generate_publish_release.yml` run when OpenAPI repo triggers it + +--- + +## Reference + +For more details on how the workflows use these configurations, see: +- [Multi-Version-SDK-Flow.md](Multi-Version-SDK-Flow.md) - Architecture overview +- [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md) - Detailed implementation diff --git a/docs/Multi-Version-SDK-Flow.md b/docs/Multi-Version-SDK-Flow.md new file mode 100644 index 0000000..5b9e319 --- /dev/null +++ b/docs/Multi-Version-SDK-Flow.md @@ -0,0 +1,241 @@ +# Multi-Version SDK Flow - Quick Reference + +**Document Purpose**: Quick-reference guide to the multi-version SDK generation, publishing, and release system. This is your entry point to understanding how the system works. + +**Last Updated**: January 27, 2026 +**Read Time**: 5-10 minutes +**Audience**: Anyone joining the team or needing a system overview + +--- + +## What Is This? + +The mx-platform-node repository publishes multiple API versions of the same npm package: +- `mx-platform-node@2.x.x` β†’ API v20111101 +- `mx-platform-node@3.x.x` β†’ API v20250224 + +Each version is independently generated, tested, published to npm, and released on GitHub. The system is **automatic** (triggered by OpenAPI spec changes) and **manual** (triggered by developer workflows). + +### Key Design Principles +1. **Separate Directories**: Each API version in its own directory (`v20111101/`, `v20250224/`) +2. **Reusable Workflows**: `workflow_call` passes version info to publish/release jobs +3. **One Config Per Version**: `config-v20111101.yml`, `config-v20250224.yml`, etc. +4. **Matrix Parallelization**: All versions generate/publish simultaneously +5. **Safety First**: Skip-publish flags and path-based triggers prevent accidents + +--- + +## Three Ways Things Happen + +### πŸ€– Flow 1: Automatic (Upstream Triggers) +OpenAPI spec changes β†’ `generate_publish_release.yml` runs β†’ SDK generated, published, released + +**When**: OpenAPI repository sends `repository_dispatch` with API versions +**Who**: Automated, no human intervention +**Result**: All specified versions generated in parallel, committed, published, released in single workflow + +**Key Details**: See [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md#flow-1-automatic-multi-version-generation-repository-dispatch) + +### πŸ‘¨β€πŸ’» Flow 2: Manual (Developer Triggers) +Developer runs `generate.yml` β†’ SDK generated β†’ PR created β†’ Developer merges β†’ Auto-publish triggers + +**When**: Developer clicks "Run workflow" on `generate.yml` +**Who**: Developer (controls version selection and bump strategy) +**Inputs**: +- `api_version`: Choose `v20111101` or `v20250224` +- `version_bump`: Choose `skip`, `minor`, or `patch` + +**Result**: SDK generated in feature branch, PR created for review, auto-publishes on merge + +**Key Details**: See [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md#flow-2-manual-multi-version-generation-workflow-dispatch) + +### πŸ”„ Flow 3: Auto-Publish (Master Push) +Changes pushed to `v20111101/**` or `v20250224/**` β†’ `on-push-master.yml` runs β†’ Publishes and releases + +**When**: Any commit to master with version directory changes +**Who**: Triggered automatically, can be skipped with `[skip-publish]` flag +**Safety**: Only affected version(s) published (no cross-version interference) + +**Key Details**: See [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md#flow-3-auto-publish-trigger-with-path-based-matrix-execution-on-push-masteryml) + +--- + +## Visual Flows + +### Automatic Flow + +```mermaid +sequenceDiagram + participant OpenAPI as OpenAPI
Repository + participant GH as GitHub
Actions + participant Gen as generate_publish
_release.yml + participant npm as npm
Registry + participant GHRel as GitHub
Releases + + OpenAPI->>GH: Changes to v20111101.yml
and v20250224.yml
(repository_dispatch) + GH->>+Gen: Trigger workflow + Gen->>Gen: Matrix: Generate both versions
in parallel + Gen->>Gen: Commit to master
Update CHANGELOG.md + deactivate Gen + + rect rgba(0, 255, 0, .1) + note right of Gen: Parallel Publishing + par publish v20111101 + npm->>npm: npm publish v2.0.1 + and publish v20250224 + npm->>npm: npm publish v3.0.1 + and release v20111101 + GHRel->>GHRel: Create tag v2.0.1 + and release v20250224 + GHRel->>GHRel: Create tag v3.0.1 + end + end +``` + +### Manual Flow + +```mermaid +sequenceDiagram + participant Dev as Developer + participant GH as GitHub
Actions + participant Gen as generate.yml + participant Review as Code
Review + participant Auto as on-push-
master.yml + participant npm as npm + participant GHRel as GitHub + + Dev->>GH: Run generate.yml
(select API + bump) + GH->>+Gen: Trigger workflow + Gen->>Gen: Generate SDK
Create PR + deactivate Gen + + Review->>Review: Review & Approve + Review->>Review: Merge PR to master + + GH->>+Auto: on-push-master.yml + Auto->>npm: Publish selected version + Auto->>GHRel: Create release + deactivate Auto +``` + +### Auto-Publish Flow + +```mermaid +sequenceDiagram + participant Push as Git Push + participant Auto as on-push-
master.yml + participant pub as publish.yml + participant rel as release.yml + participant npm as npm + participant GHRel as GitHub + + Push->>+Auto: Push to master
(v20111101/** changed) + Auto->>pub: Matrix: Publish v20111101 + pub->>npm: npm publish + Auto->>rel: Matrix: Release v20111101 + rel->>GHRel: Create tag + deactivate Auto +``` + +--- + +## Common Tasks + +### I Want to Add a New API Version +β†’ See [Adding-a-New-API-Version.md](Adding-a-New-API-Version.md) + +**Quick summary**: Create config file β†’ Update workflow matrix β†’ Coordinate with OpenAPI repo + +### Something Broke +β†’ See [Troubleshooting-Guide.md](Troubleshooting-Guide.md) + +**Quick summary**: Check error message β†’ Find section β†’ Follow solution + +### I Need Deep Technical Details +β†’ See [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md) + +**Covers**: Step-by-step implementation, configuration files, scripts, environment variables + +--- + +## Key Files Reference + +| File | Purpose | Used By | +|------|---------|---------| +| `.github/workflows/generate_publish_release.yml` | Automatic generation from upstream API changes | OpenAPI repo | +| `.github/workflows/generate.yml` | Manual generation with version selection | Developer | +| `.github/workflows/on-push-master.yml` | Auto-publish trigger with path-based matrix | Any master push | +| `.github/workflows/publish.yml` | Publishes SDK to npm | publish_release & on-push-master | +| `.github/workflows/release.yml` | Creates GitHub release | publish_release & on-push-master | +| `.github/version.rb` | Bumps version in config files | Workflows | +| `.github/clean.rb` | Removes old generated files | Workflows | +| `openapi/config-v20111101.yml` | Config for v20111101 generation | generate_publish_release & generate | +| `openapi/config-v20250224.yml` | Config for v20250224 generation | generate_publish_release & generate | +| `openapi/templates/package.mustache` | npm package.json template | OpenAPI Generator | +| `openapi/templates/README.mustache` | README.md template | OpenAPI Generator | + +--- + +## Semantic Versioning + +**Major version = API version** (no exceptions) + +| npm Version | API Version | What It Means | +|------------|------------|--------------| +| 2.x.x | v20111101 | First stable API | +| 3.x.x | v20250224 | Second API version | +| 4.x.x | (future) | Next API version | + +Consumers know exactly which API they have by checking the major version number. + +--- + +## Backward Compatibility + +If OpenAPI repo doesn't send new version in payload, the system doesn't break: +- Existing versions continue to work unchanged +- New version doesn't generate until explicitly requested +- No errors or warnings +- Phased rollout friendly + +--- + +## Safety Features + +| Feature | What It Does | When It Helps | +|---------|-------------|--------------| +| **Path-based triggers** | Only publish if `v20111101/**` or `v20250224/**` changed | Prevents false publishes from doc-only changes | +| **[skip-publish] flag** | Skip publish/release for this commit | During directory migrations or refactors | +| **Matrix conditionals** | Each version publishes only if its path changed | Prevents unintended version bumps | +| **Version validation** | Major version must match API version | Prevents semantic versioning violations | +| **Config file validation** | Workflow fails if config doesn't exist | Catches typos early | + +--- + +## Environment Variables & Secrets + +| Secret | Used For | +|--------|----------| +| `NPM_AUTH_TOKEN` | Publishing to npm registry | +| `GITHUB_TOKEN` | Creating releases (auto-provided) | +| `SLACK_WEBHOOK_URL` | Failure notifications | + +--- + +## Next Steps + +1. **Understand the architecture**: Read this document +2. **Need to add a version?**: Go to [Adding-a-New-API-Version.md](Adding-a-New-API-Version.md) +3. **Need to fix something?**: Go to [Troubleshooting-Guide.md](Troubleshooting-Guide.md) +4. **Need implementation details?**: Go to [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md) + +--- + +## Document Overview + +| Document | Purpose | Read Time | +|----------|---------|-----------| +| **Multi-Version-SDK-Flow.md** | Overview & entry point (you are here) | 5-10 min | +| **Adding-a-New-API-Version.md** | Step-by-step guide for new versions | 10-15 min | +| **Troubleshooting-Guide.md** | Common issues & solutions | 5-10 min | +| **Workflow-and-Configuration-Reference.md** | Deep technical details | 20-30 min | diff --git a/docs/SDK-Generation-Publishing-Flow.md b/docs/SDK-Generation-Publishing-Flow.md new file mode 100644 index 0000000..a932625 --- /dev/null +++ b/docs/SDK-Generation-Publishing-Flow.md @@ -0,0 +1,419 @@ +# SDK Generation, Publishing, and Release Flow (LEGACY - Single-Version) + +> ⚠️ **ARCHIVED DOCUMENTATION** - This document describes the **single-version SDK system** that was in use before multi-version support was added. +> +> **For current documentation**, see: +> - [Multi-Version-SDK-Flow.md](Multi-Version-SDK-Flow.md) - Current system overview +> - [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md) - Current technical details +> +> This document is kept for **historical reference** and may be useful for repositories that have not yet migrated to multi-version support. + +**Document Purpose**: This document explains how the Node.js SDK was automatically generated, published to npm, and released in the single-version system. It covers both automatic triggers (from the OpenAPI repository) and manual generation flows (for development and testing). + +**Last Updated**: January 20, 2026 (ARCHIVED) +**Repository**: mx-platform-node +**Author**: DevExperience Team +**Status**: Legacy - Superseded by multi-version flows + +--- + +## Overview + +The mx-platform-node repository has a fully automated pipeline for generating TypeScript SDKs from OpenAPI specifications, publishing them to npm, and creating releases on GitHub. The process is triggered in two ways: + +1. **Automatic Flow** - When OpenAPI specifications change in the upstream `openapi` repository +2. **Manual Flow** - When developers manually trigger generation for development, testing, or version bumps + +Both flows use the same underlying generation logic but differ in how they handle commits, publishing, and release creation. + +--- + +## Flow 1: Automatic Generation (Repository Dispatch) + +### Trigger +OpenAPI specifications in the upstream `openapi` repository change β†’ Repository sends `repository_dispatch` event to `mx-platform-node` β†’ `generate_publish_release.yml` workflow is triggered + +### Current Implementation + +**Workflow**: `.github/workflows/generate_publish_release.yml` + +This is the **production flow** that automatically generates, publishes, and releases SDKs when upstream APIs change. + +#### Step 1: Generate SDK from OpenAPI Spec +- **Input**: OpenAPI specification from `https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/mx_platform_api.yml` +- **Output Directory**: `./latest/` +- **Configuration**: Uses `./openapi/config.yml` to control generation settings + - Package name: `mx-platform-node` + - **Package version source**: `npmVersion` field in `./openapi/config.yml` (the source of truth) + - **Version bump**: `client_payload.version` from repository dispatch (e.g., "patch", "minor") tells `version.rb` which component to increment + - Flow: `client_payload.version` (bump instruction) β†’ `version.rb` reads config β†’ updates `npmVersion` in config β†’ `package.mustache` uses updated `npmVersion` to create `package.json` +- **Templates**: Uses `./openapi/templates/` (package.mustache, README.mustache, etc.) +- **Process**: + 1. Clean repository using `clean.rb` (removes old generated files) + 2. Bump version: `version.rb` reads current `npmVersion` from config, increments based on `client_payload.version`, writes updated version back to config + 3. Run OpenAPI Generator to create TypeScript-Axios SDK (uses updated config with new `npmVersion`) + 4. Copy documentation files (LICENSE, CHANGELOG.md, MIGRATION.md) to generated directory + +#### Step 2: Commit Changes to Master +- **Git Config**: Uses `devexperience` bot account +- **Commit Message**: `"Generated version X.Y.Z - This commit was automatically created by a GitHub Action..."` +- **Target Branch**: Directly commits to `master` (no PR created) + +#### Step 3: Publish to npm +- **Trigger**: After successful commit, dispatches `publish_sdk` repository_dispatch event +- **Workflow**: `.github/workflows/publish.yml` (triggered via repository_dispatch) +- **Process**: + 1. Navigate to `./latest/` directory + 2. Install dependencies: `npm install` + 3. Publish to npm registry with `--tag next` + 4. Uses NPM_AUTH_TOKEN secret for authentication + +#### Step 4: Create GitHub Release +- **Trigger**: After successful publish, dispatches `release_sdk` repository_dispatch event +- **Workflow**: `.github/workflows/release.yml` (triggered via repository_dispatch) +- **Process**: + 1. Read version from `./latest/package.json` + 2. Create GitHub release tagged with version (e.g., `v2.0.5`) + 3. Slack notification on failure + +--- + +## Flow 2: Manual Generation (Workflow Dispatch) + +### Trigger +Developer manually clicks "Run workflow" on `generate.yml` in GitHub Actions UI + +### User Inputs +The workflow accepts two parameters: +- **version_level**: Which version component to bump (major, minor, or patch) +- Default: patch + +### Current Implementation + +**Workflow**: `.github/workflows/generate.yml` + +This flow is used for: +- Development and testing +- Creating PRs with proposed SDK updates +- Manual version bumping before merging + +#### Step 1: Version Bumping +- **Script**: `ruby .github/version.rb ` +- **Input File**: `./openapi/config.yml` +- **Process**: + 1. Read current version from config file + 2. Increment major/minor/patch based on input + 3. Write updated version back to config file + 4. Output new version for next steps +- **Example**: + - Current: 2.0.5 β†’ Run with "minor" β†’ New: 2.1.0 + +#### Step 2: Clean Repository +- **Script**: `ruby .github/clean.rb` +- **Process**: + 1. Delete all generated files from previous generation + 2. Protect certain directories from deletion (`.git`, `.github`, `openapi`, `LICENSE`, etc.) + 3. Keeps source configuration and workflow files intact + +#### Step 3: Generate SDK +- **Input**: OpenAPI specification from `https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/mx_platform_api.yml` +- **Output Directory**: `./latest/` +- **Configuration**: Uses `./openapi/config.yml` +- **Process**: + 1. Install OpenAPI Generator CLI globally + 2. Run generator with TypeScript-Axios language + 3. Copy documentation files to output directory + +#### Step 4: Create Feature Branch +- **Branch Name**: `openapi-generator-X.Y.Z` (version from step 1) +- **Process**: + 1. Create new branch from current checkout + 2. Stage all changes with `git add .` + 3. Commit with version number in message + +#### Step 5: Create Pull Request +- **Command**: `gh pr create -f` (uses commit message for PR title/body) +- **Destination**: Targets default branch (master) +- **Review**: PR is created and awaits manual review + merge + +#### Step 6: Trigger Publishing (After Merge) +- **Trigger**: When PR is merged to `master`, `on-push-master.yml` workflow activates +- **Condition**: Triggered only if files matching `latest/**` were modified +- **Workflows Called**: + 1. `./.github/workflows/publish.yml` + 2. `./.github/workflows/release.yml` +- **Result**: Same publishing and releasing as automatic flow + +--- + + + +## Supporting Scripts + +### version.rb +**Purpose**: Increment version numbers in configuration files + +**Usage**: `ruby .github/version.rb [config_file]` + +**Important Nuance - npmVersion as Source of Truth**: + +The `npmVersion` field in the config file is the **authoritative source of truth** for the package version. Here's how the version flows through the system: + +1. **Config File** (Source of Truth) + - `openapi/config.yml` contains `npmVersion: 2.0.0` + - This is the persistent, stored version number + - Lives in Git and is checked in with each update + +2. **version.rb Script** (Updates Source of Truth) + - Reads the current `npmVersion` from the config file + - Receives bump instruction from caller: "patch", "minor", or "major" + - Calculates new version: 2.0.0 β†’ 2.0.1 (patch), 2.1.0 (minor), or 3.0.0 (major) + - **Writes updated npmVersion back to config file** (persists to Git) + - Outputs new version to stdout (for workflow logging) + +3. **package.mustache Template** (Uses Source of Truth) + - Contains placeholder: `"version": "{{npmVersion}}"` + - OpenAPI Generator replaces `{{npmVersion}}` with value from config file + - Generates `package.json` with the correct version number + +4. **Result** + - The generated `package.json` always has the correct version + - Version comes entirely from the config file + - No hardcoding in workflows or templates + +**Important Distinction**: +- `client_payload.version` from repository_dispatch is a **bump instruction** (e.g., "patch") +- `npmVersion` in config file is the **actual version number** (e.g., "2.0.0") +- These are different things! The bump instruction is used to calculate the new version number. + +**Behavior**: +- Reads YAML config file +- Parses current version (major.minor.patch) +- Increments requested component +- Resets lower components to 0 (e.g., 2.1.5 β†’ 3.0.0 when major bumped) +- Writes updated config file +- Outputs new version to stdout + +**Config File Parameter**: Optional, defaults to `./openapi/config.yml` + +**Examples**: +```bash +ruby .github/version.rb patch # Bumps config.yml patch version (2.0.0 β†’ 2.0.1) +ruby .github/version.rb minor openapi/config-v20111101.yml # Bumps config-v20111101 minor version (2.0.0 β†’ 2.1.0) +ruby .github/version.rb major openapi/config-v20250224.yml # Bumps config-v20250224 major version (3.0.0 β†’ 4.0.0) +``` + +### clean.rb +**Purpose**: Remove generated SDK files before regeneration + +**Behavior**: +- Walks through repository root directory +- Deletes all files/directories except those in ALLOW_LIST +- Protected directories: `.git`, `.github`, `.openapi-generator-ignore`, `openapi`, `LICENSE`, `README.md`, `CHANGELOG.md`, `MIGRATION.md` +- Prevents accidental deletion of configuration and workflow files + +**Part 5 Note**: When multi-version support is activated, protected list will include: `v20111101`, `v20250224`, and remove `latest` + +**Part 5 Note**: When multi-version support is activated, protected list will include: `v20111101`, `v20250224`, and remove `latest` + +--- + +## Configuration Files + +### openapi/config.yml + +```yaml +--- +generatorName: typescript-axios +npmName: mx-platform-node +npmVersion: 2.0.0 +supportsES6: true +.openapi-generator-ignore: true +``` + +**Used by**: +- `generate.yml` (manual generation) +- `generate_publish_release.yml` (automatic generation) +- `version.rb` script + +### openapi/templates/ +**Purpose**: Customized templates for package generation + +**Files**: +- `package.mustache`: Controls package.json generation + - **Key Feature**: Includes `"files"` field to explicitly control what gets published to npm + - Controls package name, version, scripts, dependencies +- `README.mustache`: Controls README.md generation + +--- + +## Path-Based Triggers + +### on-push-master.yml +**Purpose**: Automatically trigger publish and release workflows when SDK code changes are pushed to master + +```yaml +on: + push: + branches: [master] + paths: + - 'latest/**' # Only trigger on changes in the latest/ directory +``` + +**This prevents**: +- Enhancement PRs (docs only) from triggering publish +- README updates from triggering releases +- Workflow file changes from triggering publish + +--- + +## Workflow Sequences + +The following sequence diagrams show the timeline and interactions for each flow, making it clear when workflows trigger and what happens at each stage. + +### Flow 1: Automatic Generation (Repository Dispatch) + +```mermaid +sequenceDiagram + participant OpenAPI as OpenAPI
Repository + participant GH as GitHub
Actions + participant Gen as generate_publish
_release.yml + participant npm as npm
Registry + participant GHRel as GitHub
Releases + + OpenAPI->>GH: mx_platform_api.yml changes
(repository_dispatch event) + GH->>+Gen: Trigger workflow + Gen->>Gen: 1. Clean repo
(clean.rb) + Gen->>Gen: 2. Bump version
(version.rb) + Gen->>Gen: 3. Generate SDK
(OpenAPI Generator) + Gen->>Gen: 4. Copy docs
(LICENSE, CHANGELOG, MIGRATION) + Gen->>Gen: 5. Commit to master
(devexperience bot) + Gen->>npm: Dispatch publish_sdk event + Gen->>GHRel: Dispatch release_sdk event + deactivate Gen + + rect rgba(0, 255, 0, .1) + note right of npm: Parallel Publishing & Release + npm->>npm: publish.yml:
npm publish --tag next
from ./latest/ + GHRel->>GHRel: release.yml:
gh release create
with version tag + end + + npm-->>GH: βœ… Published + GHRel-->>GH: βœ… Released +``` + +### Flow 2: Manual Generation (Workflow Dispatch) + +```mermaid +sequenceDiagram + participant Dev as Developer + participant GH as GitHub
Actions + participant Gen as generate.yml + participant Review as Code
Review + participant Trigger as on-push-
master.yml + participant npm as npm
Registry + participant GHRel as GitHub
Releases + + Dev->>GH: Run generate.yml
(version_level: major/minor/patch) + GH->>+Gen: Trigger workflow + Gen->>Gen: 1. Bump version
(version.rb) + Gen->>Gen: 2. Clean repo
(clean.rb) + Gen->>Gen: 3. Generate SDK
(OpenAPI Generator) + Gen->>Gen: 4. Copy docs + Gen->>Gen: 5. Create feature branch
(openapi-generator-X.Y.Z) + Gen->>Gen: 6. Create Pull Request + deactivate Gen + + Review->>Review: πŸ‘€ Code Review + Review->>Review: Approve & Merge
to master + + GH->>+Trigger: on-push-master.yml
(path: latest/**) + Trigger->>Trigger: βœ… Path matched + Trigger->>npm: workflow_call publish.yml + Trigger->>GHRel: workflow_call release.yml + deactivate Trigger + + rect rgba(0, 255, 0, .1) + note right of npm: Parallel Publishing & Release + npm->>npm: publish.yml:
npm publish --tag next
from ./latest/ + GHRel->>GHRel: release.yml:
gh release create
with version tag + end + + npm-->>GH: βœ… Published + GHRel-->>GH: βœ… Released +``` + +--- + +--- + +## Environment Variables & Secrets Used + +### Required Secrets (`.github/secrets`) + +| Secret | Used In | Purpose | +|--------|---------|---------| +| `NPM_AUTH_TOKEN` | publish.yml | Authenticate to npm registry for publishing | +| `GITHUB_TOKEN` | All workflows | GitHub API access (auto-provided, but sometimes explicitly referenced) | +| `SLACK_WEBHOOK_URL` | All workflows | Send failure notifications to Slack | +| `PAPI_SDK_APP_ID` | generate_publish_release.yml | GitHub App ID for custom token generation | +| `PAPI_SDK_INSTALLATION_ID` | generate_publish_release.yml | GitHub App installation ID | +| `PAPI_SDK_PRIVATE_KEY` | generate_publish_release.yml | GitHub App private key | + +### Environment Setup + +- **Node**: v20.x +- **Ruby**: 3.1 +- **OpenAPI Generator**: Latest version (installed via npm during workflow) + +--- + +## Current Implementation Status + +### βœ… Fully Implemented +- Automatic generation from OpenAPI spec changes (Flow 1) +- Manual generation for PRs with version bumping (Flow 2) +- Path-based triggers for publish/release +- npm publishing with semantic versioning +- GitHub releases +- Slack notifications +- v2.0.0 stable release confirmed + +### Key Architectural Decisions + +1. **Direct Commit vs PR**: + - Automatic flow (Flow 1) commits directly to master + - Manual flow (Flow 2) creates PR for review + +2. **Tag Strategy**: + - `--tag next`: Used during auto-publish (for staging validation) + - Can be promoted to `latest` tag after validation + +3. **Path-Based Triggers**: Only changes to SDK code (`latest/**`) trigger publish/release, not docs or workflows + +4. **Atomic Operations**: Publishing and releasing happen in sequence within same workflow, preventing version mismatches + +--- + +## Troubleshooting + +### NPM Publish Fails +- Check `NPM_AUTH_TOKEN` secret is valid +- Verify token has publish permissions for `mx-platform-node` package +- Check version isn't already published + +### Release Creation Fails +- Verify `GITHUB_TOKEN` has release creation permissions +- Check if tag already exists (gh release fails if tag exists) + +### Generation Produces No Changes +- Verify OpenAPI spec URL is accessible +- Check openapi-generator-cli installed correctly +- Verify config file exists and is valid YAML + +### publish.yml or release.yml Don't Trigger After Generate +- Check path filter in `on-push-master.yml` matches changed files +- Verify files were actually committed (`git log` to confirm) +- Check branch is `master` and not another branch + +--- diff --git a/docs/Troubleshooting-Guide.md b/docs/Troubleshooting-Guide.md new file mode 100644 index 0000000..030fe37 --- /dev/null +++ b/docs/Troubleshooting-Guide.md @@ -0,0 +1,384 @@ +# Troubleshooting Guide + +**Document Purpose**: Quick reference for diagnosing and fixing issues in the multi-version SDK generation, publishing, and release workflows. + +**Last Updated**: January 27, 2026 +**Audience**: Developers debugging workflow failures + +--- + +## Quick Diagnosis + +### No SDK Generated +Check in this order: +1. Is the OpenAPI spec file accessible? +2. Does the config file exist for that API version? +3. Are there syntax errors in the config file? + +### SDK Generated But Not Published +Check in this order: +1. Did the commit reach master successfully? +2. Is `NPM_AUTH_TOKEN` secret valid? +3. Did the path filter (`v20111101/**`, etc.) match the changes? + +### SDK Generated and Published But Release Not Created +Check in this order: +1. Did publish step complete successfully? +2. Does the release tag already exist? +3. Does `GITHUB_TOKEN` have release creation permissions? + +--- + +## Common Issues and Solutions + +### Generate Workflow: Config File Not Found + +**Error Message**: +``` +Error: Config file not found: openapi/config-v20111101.yml +``` + +**Causes**: +- Config file doesn't exist for selected API version +- Filename typo or wrong path +- API version not yet configured + +**Solutions**: +1. Verify file exists: `ls -la openapi/config-v*.yml` +2. Check filename matches API version exactly +3. For new API versions, create config file first (see [Adding-a-New-API-Version.md](Adding-a-New-API-Version.md)) + +--- + +### Generate Workflow: Semantic Versioning Validation Fails + +**Error Message**: +``` +Error: Invalid npm version for API v20250224 +Expected: 3.x.x +Got: 2.0.0 +``` + +**Cause**: Major version in config doesn't match API version + +**Semantic Versioning Rule**: +- v20111101 API must use `npmVersion: 2.x.x` +- v20250224 API must use `npmVersion: 3.x.x` +- New APIs must use next sequential major version + +**Solution**: Update config file with correct major version +```yaml +--- +npmName: mx-platform-node +npmVersion: 3.0.0 # Must start with 3 for v20250224 +apiVersion: v20250224 +``` + +--- + +### Generate Workflow: Config File Syntax Error + +**Error Message**: +``` +YAML syntax error in openapi/config-v20111101.yml +``` + +**Common Issues**: +- Incorrect indentation (YAML is whitespace-sensitive) +- Missing quotes around values with special characters +- Trailing spaces after values +- Invalid field names + +**How to Test**: +```bash +ruby -e "require 'yaml'; puts YAML.load(File.read('openapi/config-v20111101.yml'))" +``` + +If this errors, your YAML syntax is wrong. + +**Solution**: Fix YAML syntax and re-test +```yaml +--- +generatorName: typescript-axios # Must start with --- +npmName: mx-platform-node +npmVersion: 2.0.0 +apiVersion: v20111101 +supportsES6: true +.openapi-generator-ignore: true +``` + +--- + +### Publish Fails: NPM Auth Token Invalid + +**Error Message**: +``` +npm ERR! 401 Unauthorized +npm ERR! need auth 401 Unauthorized - PUT https://registry.npmjs.org/mx-platform-node +``` + +**Causes**: +- `NPM_AUTH_TOKEN` secret expired or revoked +- Token doesn't have publish permissions for `mx-platform-node` +- Token was deleted from GitHub repository secrets +- Wrong npm registry configured + +**Solutions**: +1. Verify token exists: Go to GitHub repo β†’ Settings β†’ Secrets and variables β†’ Actions β†’ Look for `NPM_AUTH_TOKEN` +2. If missing or expired, generate new token: + - Log into npm.js as maintainer + - Create new "Publish" scope token (not Read-only) + - Copy full token value + - Update secret in GitHub repository +3. Verify token has permissions for `mx-platform-node` package +4. Wait 5 minutes after updating secret before retrying + +--- + +### Publish Fails: Version Already Published + +**Error Message**: +``` +npm ERR! 403 Forbidden - PUT https://registry.npmjs.org/mx-platform-node/2.0.5 +You cannot publish over the previously published version 2.0.5 +``` + +**Cause**: Version already exists on npm registry + +**Why This Happens**: +- SDK was already published with this version number +- Version bump didn't increment (check `version.rb` output) +- Wrong version directory being published + +**Solutions**: +1. If intentional, increment version and regenerate: + ```bash + ruby .github/version.rb patch openapi/config-v20111101.yml + ``` +2. If accidental duplicate: + - Check `openapi/config-v20111101.yml` has next sequential version + - Run generate workflow again to create PR with new version + - Merge PR to trigger publish with correct version + +--- + +### Release Not Created After Publish + +**Error Message**: +``` +gh release create: error: failed to create release +fatal: A release with this tag already exists +``` + +**Causes**: +- Release tag already exists (e.g., `v2.0.1`) +- `GITHUB_TOKEN` lacks release creation permissions +- Typo in tag name + +**Solutions**: +1. Check if release already exists: + ```bash + git tag -l | grep v2.0.1 + ``` +2. If release exists but workflow failed, delete it: + - Go to GitHub repo β†’ Releases β†’ Find the release + - Click "Delete" on the release + - Re-run the release workflow +3. Verify `GITHUB_TOKEN` has release creation permissions (usually auto-provided by GitHub) +4. Check that version in `package.json` matches tag format (`v2.0.1`, not `2.0.1`) + +--- + +### Both Versions Publish When Only One Should + +**Scenario**: Merged a PR for v20111101 only, but both v20111101 and v20250224 published + +**Causes**: +- Changes were made to both `v20111101/` and `v20250224/` directories +- Unintended changes to both directories were committed +- Path filter in `on-push-master.yml` has wrong syntax + +**Solutions**: +1. Review what changed in the merged PR: + ```bash + git log --oneline -n 5 | head -1 + git show --name-status | grep -E "v20111101|v20250224" + ``` +2. If only one version should have changed: + - Revert the commit + - Fix the unintended changes + - Create a new PR with only the intended changes +3. If both versions should have changed: + - This is correct behavior (both path filters matched) + - Both versions published as expected + +--- + +### Generation Produces Stale Spec Files + +**Symptom**: Generated SDK doesn't include changes that were in the OpenAPI spec + +**Cause**: GitHub's raw.githubusercontent.com CDN cached the old file for 5 minutes + +**Why This Happens**: +- OpenAPI repo commits spec change at 2:00 PM +- Repository dispatch sent immediately at 2:01 PM +- Workflow runs at 2:02 PM but CDN still has 2:00 PM version +- SDK generated from stale spec + +**Solution**: Already implemented in `generate_publish_release.yml` +- Uses commit SHA in spec URL: `raw.githubusercontent.com/mxenabled/openapi//openapi/v20111101.yml` +- Commit SHA bypasses CDN and guarantees exact spec version +- Nothing to doβ€”this is automatic + +**Manual Generation Note**: `generate.yml` uses `master` branch reference (not commit SHA) because developer controls timing and doesn't have CDN race condition concern. + +--- + +### Workflow Not Triggering on Push + +**Symptom**: Merged a PR with changes to `v20111101/` directory, but `on-push-master.yml` didn't run + +**Causes**: +- Path filter in `on-push-master.yml` has syntax error or wrong path +- Changes were not actually in the version directory +- Commit was made to wrong branch +- Workflow file has syntax error + +**Solutions**: +1. Verify path filter syntax is correct: + ```yaml + on: + push: + branches: [master] + paths: + - 'v20111101/**' # Correct format + - 'v20250224/**' + ``` +2. Check what files were actually changed: + ```bash + git diff HEAD~1..HEAD --name-only | grep -E "v20111101|v20250224" + ``` +3. Verify commit was to `master` branch: + ```bash + git log --oneline -n 1 + git branch -r --contains HEAD + ``` +4. Check `on-push-master.yml` syntax: + ```bash + ruby -e "require 'yaml'; puts YAML.load(File.read('.github/workflows/on-push-master.yml'))" + ``` + +--- + +### Skip-Publish Flag Not Working + +**Symptom**: Added `[skip-publish]` to commit message but workflow still published + +**Causes**: +- Flag syntax wrong (case-sensitive, needs brackets) +- Flag in PR title/body instead of commit message +- Commit message doesn't include the flag + +**Solution**: Commit message must include exact text `[skip-publish]` +```bash +# Correct +git commit -m "Migrate SDK structure [skip-publish]" + +# Wrong - will not work +git commit -m "Migrate SDK structure [SKIP-PUBLISH]" +git commit -m "Migrate SDK structure (skip-publish)" +git commit -m "Migrate SDK structure skip-publish" +``` + +--- + +### Version.rb Script Errors + +#### Error: "Version directory parameter required" +``` +Error: Version directory parameter required. Usage: ruby clean.rb +``` + +**Cause**: `clean.rb` called without version directory argument + +**Solution**: Always provide version directory +```bash +ruby .github/clean.rb v20111101 # Correct +ruby .github/clean.rb # Wrong - missing parameter +``` + +#### Error: "Invalid version bump type" +``` +Error: Invalid version bump type: major. Supported: 'minor' or 'patch' +``` + +**Cause**: Tried to use `major` option (not allowed for semantic versioning) + +**Solution**: Use only `minor` or `patch` +```bash +ruby .github/version.rb patch openapi/config-v20111101.yml # Correct +ruby .github/version.rb major openapi/config-v20111101.yml # Wrong - major not allowed +``` + +#### Error: "Config file not found" +``` +Error: Config file not found: openapi/config-invalid.yml +``` + +**Cause**: Config file path doesn't exist + +**Solution**: Verify file path and spelling +```bash +ls -la openapi/config-v*.yml # List valid files +ruby .github/version.rb patch openapi/config-v20111101.yml # Use correct path +``` + +--- + +### GitHub Actions Workflow Syntax Errors + +**Error Message** (in GitHub Actions UI): +``` +Invalid workflow file +``` + +**Common Causes**: +- YAML indentation error +- Invalid GitHub Actions syntax +- Missing required fields +- Circular job dependencies + +**How to Debug**: +1. Go to GitHub repo β†’ Actions β†’ Select failed workflow +2. Click on the workflow run +3. Look for error message at top (usually shows line number) +4. Check YAML syntax locally: + ```bash + ruby -e "require 'yaml'; YAML.load_file('.github/workflows/on-push-master.yml')" + ``` + +**Most Common Fix**: Indentation +- GitHub Actions workflows are YAML files +- Indentation must be consistent (usually 2 spaces) +- Use an editor with YAML validation + +--- + +## Getting Help + +If you encounter an issue not covered above: + +1. **Check workflow logs**: Go to GitHub repo β†’ Actions β†’ Failed workflow β†’ Click run β†’ Expand failed step +2. **Review error message**: Look for specific file names, line numbers, or error codes +3. **Check recent changes**: Did a recent PR change workflows or configs? +4. **Test locally**: Try running Ruby scripts manually to verify syntax +5. **Ask the team**: Reference the error message and steps to reproduce + +--- + +## Reference + +- [Multi-Version-SDK-Flow.md](Multi-Version-SDK-Flow.md) - Architecture overview +- [Workflow-and-Configuration-Reference.md](Workflow-and-Configuration-Reference.md) - Detailed implementation +- [Adding-a-New-API-Version.md](Adding-a-New-API-Version.md) - Step-by-step guide for new versions diff --git a/docs/Workflow-and-Configuration-Reference.md b/docs/Workflow-and-Configuration-Reference.md new file mode 100644 index 0000000..a2827e4 --- /dev/null +++ b/docs/Workflow-and-Configuration-Reference.md @@ -0,0 +1,581 @@ +# Workflow and Configuration Reference + +**Document Purpose**: Detailed technical reference for the multi-version SDK generation, publishing, and release workflows. Covers implementation details, configuration files, and system architecture. + +**Last Updated**: January 27, 2026 +**Audience**: Developers who need to understand or modify the implementation + +--- + +## Flow 1: Automatic Multi-Version Generation (Repository Dispatch) + +### Trigger +OpenAPI specifications change in the upstream `openapi` repository β†’ Repository sends `repository_dispatch` event with optional `api_versions` payload β†’ `generate_publish_release.yml` workflow is triggered + +### Backward Compatibility Model +- **No Payload (v20111101 only)**: If openapi repo sends `repository_dispatch` without `api_versions` field, defaults to generating v20111101 only +- **Single Version Payload** (`api_versions: "v20111101"`): Generates only the specified version +- **Multi-Version Payload** (`api_versions: "v20111101,v20250224"`): Generates both versions in parallel + +This allows phased migration: current behavior works as-is, and when the openapi repo is ready for multi-version, no code changes are needed in mx-platform-node. + +### Implementation + +**Workflow**: `.github/workflows/generate_publish_release.yml` + +#### Step 1: Setup - Determine Versions to Generate + +```yaml +env: + VERSIONS_TO_GENERATE: ${{ github.event.client_payload.api_versions || 'v20111101' }} +``` + +- If `api_versions` in payload: Use specified versions (e.g., `v20111101,v20250224`) +- If no payload: Default to `v20111101` (backward-compatible single-version behavior) + +#### Step 2: Generate SDKs (Matrix Execution) + +**Matrix Strategy**: Each API version runs as independent matrix job in parallel + +```yaml +strategy: + matrix: + api_version: [v20111101, v20250224] +``` + +**For Each Version** (e.g., v20111101): + +1. **Clean Version Directory** + - Command: `ruby .github/clean.rb v20111101` + - Deletes previously generated SDK files from that version directory only + - Prevents accidentally deleting unrelated version code + +2. **Setup Workflow Files** + - Copy `.openapi-generator-ignore` to version directory + - Create directory structure for generation + +3. **Bump Version** + - Script: `ruby .github/version.rb patch openapi/config-v20111101.yml` + - Reads `npmVersion` from config, increments, writes back + - Example: 2.0.0 β†’ 2.0.1 (patch) or 2.1.0 (minor) + +4. **Generate SDK from OpenAPI Spec** + - **Input Spec URL**: Version-specific with commit SHA to bypass CDN cache + ``` + https://raw.githubusercontent.com/mxenabled/openapi//openapi/v20111101.yml + ``` + - **Why Commit SHA**: GitHub's raw CDN caches files for 5 minutes. Without the commit SHA, a workflow triggered immediately after a spec change might pull a cached (stale) version instead of the new one. The commit SHA ensures we always use the exact spec that triggered the workflow. + - **Output Directory**: `v20111101/` (version-specific) + - **Configuration**: Uses version-specific config file with correct `npmVersion` and `apiVersion` + - **Templates**: Shared templates in `./openapi/templates/` use `{{apiVersion}}` and `{{npmVersion}}` placeholders + - **Process**: + 1. Install OpenAPI Generator CLI globally + 2. Run TypeScript-Axios generator with version-specific config + 3. Generates SDK code in version directory + +5. **Copy Documentation** + - Copy documentation files to version directory: + - `LICENSE` β†’ `v20111101/LICENSE` + - `MIGRATION.md` β†’ `v20111101/MIGRATION.md` + - `.openapi-generator-ignore` β†’ `v20111101/.openapi-generator-ignore` + +6. **Upload Artifacts** + - Upload generated SDK to workflow artifact storage + - Allows atomic multi-version commit after all matrix jobs complete + +#### Step 3: Post-Generation Processing (After All Matrix Jobs Complete) + +1. **Download Artifacts** + - Retrieve generated SDKs for all versions from artifact storage + +2. **Track Generated Versions** + - Record which directories were actually generated + - Used by CHANGELOG automation to add entries only for generated versions + +3. **Update CHANGELOG.md** + - Find first version entry: `grep -n "^##" CHANGELOG.md` + - Insert new entries after document header, before first existing entry + - Format: `## [X.Y.Z] - YYYY-MM-DD (API vXXXXXXXX)` + - Only add entries for versions that were actually generated + - Example: + ```markdown + # Changelog + + ## [2.0.1] - 2026-01-27 (v20111101 API) + Updated v20111101 API specification. + + ## [3.0.1] - 2026-01-27 (v20250224 API) + Updated v20250224 API specification. + ``` + +4. **Copy CHANGELOG to Version Directories** + - Copy root `CHANGELOG.md` to each version directory + - Each npm package includes changelog for users + +#### Step 4: Commit All Changes to Master + +- **Git Config**: Uses `devexperience` bot account +- **Commit Message**: `"Generated Node.js SDKs [v20111101=2.0.1,v20250224=3.0.1]"` +- **Files Committed**: Updated config files, generated SDK directories, updated CHANGELOG.md +- **Target Branch**: Directly commits to `master` (no PR created for automatic flow) +- **Atomic Operation**: All versions committed together in single commit + +#### Step 5: Publish to npm (Parallel Matrix Execution) + +**Architecture**: Uses `workflow_call` to invoke `publish.yml` as reusable workflow + +**Why workflow_call?** Repository dispatch events don't support input parameters. `workflow_call` allows passing `version_directory` to specify which version directory contains the SDK to publish. + +**Process**: +1. Call `publish.yml` with version-specific directory +2. Navigate to version directory (e.g., `v20111101/`) +3. Install dependencies: `npm install` +4. Publish to npm: `npm publish` (no tag for production) +5. Use `NPM_AUTH_TOKEN` secret for authentication + +**Result**: +- `mx-platform-node@2.0.1` published to npm (v20111101 API) +- `mx-platform-node@3.0.1` published to npm (v20250224 API) +- Both major versions coexist on npm registry under same package name + +#### Step 6: Create GitHub Releases (Parallel Matrix Execution) + +**Same architecture as publish**: Uses `workflow_call` to invoke `release.yml` + +**Process**: +1. Read version from version-specific `package.json` +2. Create GitHub release with version-specific tag (e.g., `v2.0.1`, `v3.0.1`) +3. Release body includes API version and links to API documentation + +**Result**: +- GitHub release `v2.0.1` created (v20111101 API) +- GitHub release `v3.0.1` created (v20250224 API) +- Both versions have separate release history + +--- + +## Flow 2: Manual Multi-Version Generation (Workflow Dispatch) + +### Trigger +Developer manually clicks "Run workflow" on `generate.yml` in GitHub Actions UI + +### User Inputs + +1. **api_version** (required): Which API version to generate + - Options: `v20111101` or `v20250224` + - Maps to correct config file automatically + +2. **version_bump** (required): Version bump strategy + - Options: `skip`, `minor`, or `patch` (no major option) + - Major version locked to API version (semantic versioning) + - `skip`: Generate without bumping version (test/review mode) + +### Implementation + +**Workflow**: `.github/workflows/generate.yml` + +#### Step 1: Validate Inputs + +- **Config File Exists**: Verify selected API version config file exists +- **Semantic Versioning Check**: Verify major version matches API version +- **Fail Fast**: If validation fails, workflow stops before any generation + +#### Step 2: Version Bumping (Conditional) + +**Only runs if `version_bump` != `skip`** + +- Script: `ruby .github/version.rb openapi/config-v20111101.yml` +- Reads current version from config file +- Increments minor or patch based on input +- Writes updated version back to config file +- Output new version for next steps + +#### Step 3: Clean Version Directory + +- Script: `ruby .github/clean.rb v20111101` +- Deletes generated files from previous generation in that directory only +- Unrelated version directories untouched + +#### Step 4: Generate SDK + +- **Input Spec URL**: Master branch reference (not commit SHA) + - `https://raw.githubusercontent.com/mxenabled/openapi/master/openapi/v20111101.yml` + - Manual workflow doesn't have CDN cache concern since developer controls timing +- **Output Directory**: Version-specific (e.g., `v20111101/`) +- **Configuration**: Version-specific config file +- **Process**: + 1. Install OpenAPI Generator CLI + 2. Run TypeScript-Axios generator with selected config + 3. Copy documentation files to version directory + +#### Step 5: Update CHANGELOG.md + +- Insert new entry after document header, before first existing entry +- Format: `## [X.Y.Z] - YYYY-MM-DD (API vXXXXXXXX)` +- Only selected version's CHANGELOG entry added + +#### Step 6: Create Feature Branch + +- **Branch Name**: `openapi-generator-v20111101-2.0.1` +- Format: `openapi-generator--` +- Makes it easy to identify which API version each PR targets + +#### Step 7: Create Pull Request + +- **Command**: `gh pr create -f` +- **Destination**: Targets `master` branch +- **Status**: Awaits code review and approval before merging +- **Benefits**: Allows time to validate SDK quality and close/retry if needed + +#### Step 8: Trigger Publishing (After PR Merge) + +**Trigger**: When PR is merged to `master`, `on-push-master.yml` automatically activates (see Flow 3) + +**Workflows Called**: +1. `publish.yml` (via workflow_call with version_directory input) +2. `release.yml` (via workflow_call with version_directory input) + +**Result**: Same publishing and releasing as automatic flow + +--- + +## Flow 3: Auto-Publish Trigger with Path-Based Matrix Execution (on-push-master.yml) + +### Trigger +Push to `master` branch with changes in version-specific directories (`v20111101/**` or `v20250224/**`) + +### Skip-Publish Safety Mechanism +Include `[skip-publish]` in commit message to prevent publish/release for this push. + +**Use Case**: When making structural changes (e.g., directory migrations), commit with `[skip-publish]` flag to prevent accidental publishes. + +### Implementation + +**Workflow**: `.github/workflows/on-push-master.yml` + +**Architectural Approach**: Matrix strategy with conditional execution per iteration eliminates code duplication while maintaining clear, independent version management. + +#### Step 1: Check Skip-Publish Flag + +**Job**: `check-skip-publish` + +```yaml +- name: Check for skip-publish flag + run: | + if [[ "${{ github.event.head_commit.message }}" == *"[skip-publish]"* ]]; then + echo "skip_publish=true" >> $GITHUB_OUTPUT + else + echo "skip_publish=false" >> $GITHUB_OUTPUT + fi +``` + +- Parses HEAD commit message +- Sets output: `skip_publish` = true/false +- Used by subsequent jobs to determine execution + +#### Step 2: Matrix-Based Publish Jobs + +**Matrix Definition**: +```yaml +strategy: + matrix: + version: + - api_version: v20111101 + npm_version: 2 + - api_version: v20250224 + npm_version: 3 +``` + +**For Each Version**: + +**Job Executes When**: +- No upstream failures (`!cancelled()`) +- No `[skip-publish]` flag in commit message +- Files in this version's directory were changed + +**Process**: +1. Call `publish.yml` with version-specific directory +2. Navigate to version directory +3. Install dependencies and publish to npm +4. Each version publishes independently with its own version number + +**Result**: Each version publishes independently, no race conditions, parallel execution when both versions changed + +#### Step 3: Matrix-Based Release Jobs + +**Same Matrix Strategy as Publish** + +**For Each Version**: + +**Job Executes When**: +- Same conditions as publish (skip-publish flag, path match) +- **Plus**: Only after its corresponding publish job succeeds + +This ensures each version's release depends only on its own publish job, preventing race conditions. + +**Process**: +1. Call `release.yml` with version-specific directory +2. Read version from that directory's `package.json` +3. Create GitHub release with version-specific tag + +**Result**: Each version released independently, ordered after its corresponding publish job + +--- + +## Supporting Scripts + +### version.rb - Multi-Version Support + +**File**: `.github/version.rb` + +**Purpose**: Increment version numbers in configuration files + +**Usage**: `ruby .github/version.rb [config_file]` + +**Supported Options**: +- `minor`: Increment minor version (e.g., 2.0.0 β†’ 2.1.0) +- `patch`: Increment patch version (e.g., 2.0.0 β†’ 2.0.1) +- No `major` option (major version locked to API version) + +**Important**: npmVersion as Source of Truth + +The `npmVersion` field in the config file is the **authoritative source of truth** for the package version: + +1. **Config File** (Source of Truth) + - Contains persistent version number + - Lives in Git, checked in with each update + - Example: `openapi/config-v20111101.yml` contains `npmVersion: 2.0.0` + +2. **version.rb Script** (Updates Source of Truth) + - Reads current `npmVersion` from config file + - Receives bump instruction: "patch" or "minor" + - Calculates new version: 2.0.0 β†’ 2.0.1 (patch) or 2.1.0 (minor) + - **Writes updated npmVersion back to config file** (persists to Git) + - Outputs new version to stdout (for workflow logging) + +3. **package.mustache Template** (Uses Source of Truth) + - Contains placeholder: `"version": "{{npmVersion}}"` + - OpenAPI Generator replaces `{{npmVersion}}` with value from config file + - Generates `package.json` with correct version number + +4. **Result** + - Generated `package.json` always has correct version + - Version comes entirely from config file + - No hardcoding in workflows or templates + +### clean.rb - Version-Targeted Deletion + +**File**: `.github/clean.rb` + +**Purpose**: Remove generated SDK files before regeneration for a specific version + +**Usage**: `ruby .github/clean.rb ` + +**Behavior**: +- Version-targeted deletion: deletes only specified version directory +- Protected files: `.git`, `.github`, `openapi`, other version directories, LICENSE, README, CHANGELOG +- Required parameter: must provide version directory name +- Error if parameter missing: raises clear error message + +--- + +## Configuration Files + +### openapi/config-v20111101.yml + +```yaml +--- +generatorName: typescript-axios +npmName: mx-platform-node +npmVersion: 2.0.0 +apiVersion: v20111101 +supportsES6: true +.openapi-generator-ignore: true +``` + +**Purpose**: Generates v20111101 API SDK as `mx-platform-node@2.x.x` + +**Key Fields**: +- `npmVersion`: Source of truth for package version (updated by `version.rb`) +- `apiVersion`: Passed to `package.mustache` for description and metadata +- `npmName`: Same across all configs (single package name with multiple major versions) +- `generatorName`: Language/framework for code generation (TypeScript-Axios) +- `supportsES6`: Target JavaScript version for transpilation +- `.openapi-generator-ignore`: Prevents overwriting certain files + +### openapi/config-v20250224.yml + +```yaml +--- +generatorName: typescript-axios +npmName: mx-platform-node +npmVersion: 3.0.0 +apiVersion: v20250224 +supportsES6: true +.openapi-generator-ignore: true +``` + +**Purpose**: Generates v20250224 API SDK as `mx-platform-node@3.x.x` + +### openapi/templates/package.mustache + +```json +{ + "name": "{{npmName}}", + "version": "{{npmVersion}}", + "description": "MX Platform Node.js SDK ({{apiVersion}} API)", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "apiVersion": "{{apiVersion}}", + "files": [ + "dist/", + "*.md" + ], + "scripts": { + "build": "tsc --declaration", + "test": "npm run build" + }, + "dependencies": { + "axios": "^1.6.2" + } +} +``` + +**Key Features**: +- `"version"`: Uses `{{npmVersion}}` from config (source of truth) +- `"description"`: Includes `{{apiVersion}}` to identify which API version (visible in npm registry) +- `"apiVersion"`: Custom field for programmatic access to API version +- `"files"`: Explicitly controls what gets published (critical for multi-version from subdirectories) + +**Consumer Discovery**: +- **In npm registry**: Description shows "MX Platform SDK (v20111101 API)" or "(v20250224 API)" +- **Programmatically**: + ```javascript + const pkg = require('mx-platform-node/package.json'); + console.log(pkg.apiVersion); // "v20111101" or "v20250224" + ``` + +### openapi/templates/README.mustache + +**Key Features**: +- Title includes `{{apiVersion}}`: `# MX Platform Node.js ({{apiVersion}} API)` +- SDK and API version clearly identified +- Version selection section explains available versions +- Links to correct API documentation per version + +--- + +## Path-Based Triggers + +### on-push-master.yml Path Configuration + +```yaml +on: + push: + branches: [master] + paths: + - 'v20111101/**' # Triggers publish/release for v20111101 + - 'v20250224/**' # Triggers publish/release for v20250224 + # Does NOT trigger on: + # - '.github/**' (workflow changes) + # - 'openapi/**' (config changes alone) + # - 'docs/**' (documentation changes) + # - 'README.md' (root documentation) +``` + +**Benefits**: +- Enhancement PRs (docs only) don't trigger publish +- Workflow file changes don't trigger publish +- Only actual SDK code changes trigger publish/release +- Each version independently triggers when its directory changes +- Prevents false publishes from non-SDK changes + +--- + +## Semantic Versioning Strategy + +The repository uses **semantic versioning with major version = API version**: + +| Version | API Version | Release Type | Notes | +|---------|------------|--------------|-------| +| 2.x.x | v20111101 | Stable | New minor/patch releases for spec updates | +| 3.x.x | v20250224 | Stable | New major version for new API version | +| 4.x.x | (future) | Future | When new API version available | + +**Key Principle**: Major version number directly tied to API version number. +- Moving between major versions (2.x β†’ 3.x) always means API change +- No confusion about which API version is in use +- Consumers can use `package.json` to determine API version + +--- + +## Environment Variables & Secrets + +### Required Secrets (`.github/secrets`) + +| Secret | Used In | Purpose | +|--------|---------|---------| +| `NPM_AUTH_TOKEN` | publish.yml | Authenticate to npm registry for publishing | +| `GITHUB_TOKEN` | All workflows | GitHub API access (auto-provided by GitHub Actions) | +| `SLACK_WEBHOOK_URL` | All workflows | Send failure notifications to Slack | + +### Environment Setup + +- **Node**: v20.x (for npm operations) +- **Ruby**: 3.1 (for version.rb and clean.rb scripts) +- **OpenAPI Generator**: Latest version (installed via npm during workflow) +- **Git**: Configured with `devexperience` bot account for automatic commits + +--- + +## Execution Timelines + +### Automatic Flow Timeline + +``` +OpenAPI Repo: Commits change to v20111101.yml and v20250224.yml + ↓ +repository_dispatch: {"api_versions": "v20111101,v20250224"} + ↓ +generate_publish_release.yml: Triggered + β”œβ”€ Matrix[v20111101]: Clean, Bump, Generate (parallel) + β”œβ”€ Matrix[v20250224]: Clean, Bump, Generate (parallel) + β”œβ”€ Download artifacts + β”œβ”€ Update CHANGELOG.md + β”œβ”€ Commit to master + β”œβ”€ publish[v20111101]: npm publish (parallel) + β”œβ”€ publish[v20250224]: npm publish (parallel) + β”œβ”€ release[v20111101]: Create tag v2.0.1 (parallel) + β”œβ”€ release[v20250224]: Create tag v3.0.1 (parallel) + ↓ +Result: Both versions published and released, CHANGELOG updated +``` + +### Manual Flow Timeline + +``` +Developer: Runs generate.yml (api_version, version_bump) + ↓ +generate.yml: Validate, Bump (if needed), Clean, Generate + β”œβ”€ Update CHANGELOG.md + β”œβ”€ Create feature branch + β”œβ”€ Create Pull Request + ↓ +Code Review: Developer reviews and merges PR + ↓ +on-push-master.yml: Triggered + β”œβ”€ check-skip-publish: false + β”œβ”€ publish[matching_version]: npm publish + β”œβ”€ release[matching_version]: Create tag + ↓ +Result: Selected version published and released +``` + +--- + +## Reference + +For quick guides and troubleshooting, see: +- [Multi-Version-SDK-Flow.md](Multi-Version-SDK-Flow.md) - Architecture overview and diagrams +- [Adding-a-New-API-Version.md](Adding-a-New-API-Version.md) - Step-by-step guide for new versions +- [Troubleshooting-Guide.md](Troubleshooting-Guide.md) - Common issues and solutions diff --git a/openapi/config-latest.yml b/openapi/config-latest.yml deleted file mode 100644 index d2b2b24..0000000 --- a/openapi/config-latest.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Latest is mapped to v20111101 for now to maintain backwards compatibility. -# In the future, this will point to the latest version of the API. ---- -generatorName: typescript-axios -npmName: mx-platform-node -npmVersion: 2.0.0 -supportsES6: true diff --git a/openapi/config-v20111101.yml b/openapi/config-v20111101.yml index 5e3a8dc..2c8fe1d 100644 --- a/openapi/config-v20111101.yml +++ b/openapi/config-v20111101.yml @@ -1,5 +1,7 @@ --- +apiVersion: v20111101 generatorName: typescript-axios -npmName: mx-platform-node-v20111101 +npmName: mx-platform-node npmVersion: 2.0.0 supportsES6: true +.openapi-generator-ignore: true diff --git a/openapi/config-v20250224.yml b/openapi/config-v20250224.yml index 5a3e7e1..a4fa35c 100644 --- a/openapi/config-v20250224.yml +++ b/openapi/config-v20250224.yml @@ -1,5 +1,7 @@ --- +apiVersion: v20250224 generatorName: typescript-axios -npmName: mx-platform-node-v20250224 +npmName: mx-platform-node npmVersion: 3.0.0 supportsES6: true +.openapi-generator-ignore: true diff --git a/openapi/config.yml b/openapi/config.yml.bak similarity index 75% rename from openapi/config.yml rename to openapi/config.yml.bak index 5efac3a..cb020e7 100644 --- a/openapi/config.yml +++ b/openapi/config.yml.bak @@ -1,3 +1,4 @@ +# Archived to support multiple versions. Reference --- npmName: mx-platform-node npmVersion: 2.0.0-rc.1 # Pre-release for testing new directory structure diff --git a/openapi/templates/README.mustache b/openapi/templates/README.mustache index 777ca83..6c1c1fb 100644 --- a/openapi/templates/README.mustache +++ b/openapi/templates/README.mustache @@ -1,6 +1,44 @@ *This project is currently in **Beta**. Please open up an issue [here](https://github.com/mxenabled/mx-platform-node/issues) to report issues using the MX Platform Node.js library.* -# MX Platform Node.js +# MX Platform Node.js ({{apiVersion}} API) + +**SDK version:** {{npmVersion}} +**API version:** {{apiVersion}} + +You are using the **{{apiVersion}}** API version of `mx-platform-node`. For other API versions, see [Available API Versions](#available-api-versions) below. + +### Checking Your Installed Version + +To verify which API version you have installed: + +**In package.json:** +```json +{ + "dependencies": { + "mx-platform-node": "^{{npmVersion}}" + } +} +``` + +**Programmatically in your code:** +```javascript +const pkg = require('mx-platform-node/package.json'); +console.log(pkg.apiVersion); // {{apiVersion}} +``` + +**Via npm:** +```shell +npm view mx-platform-node@{{npmVersion}} +``` + +## Available API Versions + +- **{{npmName}}@2.x.x** - [v20111101 API](https://docs.mx.com/api-reference/platform-api/v20111101/reference/mx-platform-api/) +- **{{npmName}}@3.x.x** - [v20250224 API](https://docs.mx.com/api-reference/platform-api/reference/mx-platform-api/) + +--- + +## Overview The [MX Platform API](https://www.mx.com/products/platform-api) is a powerful, fully-featured API designed to make aggregating and enhancing financial data easy and reliable. It can seamlessly connect your app or website to tens of thousands of financial institutions. diff --git a/openapi/templates/package.mustache b/openapi/templates/package.mustache index b1334f2..9c81cd9 100644 --- a/openapi/templates/package.mustache +++ b/openapi/templates/package.mustache @@ -1,7 +1,8 @@ { "name": "{{npmName}}", "version": "{{npmVersion}}", - "description": "A Node library for the MX Platform API.", + "description": "A Node.js SDK for the MX Platform API ({{apiVersion}})", + "apiVersion": "{{apiVersion}}", "author": "MX", "keywords": [ "mx", @@ -14,9 +15,7 @@ "typings": "./dist/index.d.ts", "files": [ "dist/", - "CHANGELOG.md", - "README.md", - "MIGRATION.md" + "*.md" ], "scripts": { "build": "tsc --outDir dist/", diff --git a/latest/.gitignore b/v20111101/.gitignore similarity index 100% rename from latest/.gitignore rename to v20111101/.gitignore diff --git a/latest/.npmignore b/v20111101/.npmignore similarity index 100% rename from latest/.npmignore rename to v20111101/.npmignore diff --git a/latest/.openapi-generator-ignore b/v20111101/.openapi-generator-ignore similarity index 100% rename from latest/.openapi-generator-ignore rename to v20111101/.openapi-generator-ignore diff --git a/latest/.openapi-generator/FILES b/v20111101/.openapi-generator/FILES similarity index 100% rename from latest/.openapi-generator/FILES rename to v20111101/.openapi-generator/FILES diff --git a/latest/.openapi-generator/VERSION b/v20111101/.openapi-generator/VERSION similarity index 100% rename from latest/.openapi-generator/VERSION rename to v20111101/.openapi-generator/VERSION diff --git a/latest/CHANGELOG.md b/v20111101/CHANGELOG.md similarity index 100% rename from latest/CHANGELOG.md rename to v20111101/CHANGELOG.md diff --git a/latest/LICENSE b/v20111101/LICENSE similarity index 100% rename from latest/LICENSE rename to v20111101/LICENSE diff --git a/latest/MIGRATION.md b/v20111101/MIGRATION.md similarity index 100% rename from latest/MIGRATION.md rename to v20111101/MIGRATION.md diff --git a/latest/README.md b/v20111101/README.md similarity index 100% rename from latest/README.md rename to v20111101/README.md diff --git a/latest/api.ts b/v20111101/api.ts similarity index 100% rename from latest/api.ts rename to v20111101/api.ts diff --git a/latest/base.ts b/v20111101/base.ts similarity index 100% rename from latest/base.ts rename to v20111101/base.ts diff --git a/latest/common.ts b/v20111101/common.ts similarity index 100% rename from latest/common.ts rename to v20111101/common.ts diff --git a/latest/configuration.ts b/v20111101/configuration.ts similarity index 100% rename from latest/configuration.ts rename to v20111101/configuration.ts diff --git a/latest/git_push.sh b/v20111101/git_push.sh similarity index 100% rename from latest/git_push.sh rename to v20111101/git_push.sh diff --git a/latest/index.ts b/v20111101/index.ts similarity index 100% rename from latest/index.ts rename to v20111101/index.ts diff --git a/latest/package.json b/v20111101/package.json similarity index 100% rename from latest/package.json rename to v20111101/package.json diff --git a/latest/tsconfig.esm.json b/v20111101/tsconfig.esm.json similarity index 100% rename from latest/tsconfig.esm.json rename to v20111101/tsconfig.esm.json diff --git a/latest/tsconfig.json b/v20111101/tsconfig.json similarity index 100% rename from latest/tsconfig.json rename to v20111101/tsconfig.json