openapi-updated #816
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Regenerate | |
| on: | |
| repository_dispatch: | |
| types: [openapi-updated] | |
| workflow_dispatch: | |
| jobs: | |
| generate: | |
| name: Regenerate from OpenAPI | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: '8.2' | |
| tools: composer | |
| - name: Setup Java (for openapi-generator) | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Install OpenAPI Generator | |
| run: npm install -g @openapitools/openapi-generator-cli | |
| - name: Fetch latest OpenAPI spec | |
| run: | | |
| curl -f --retry 5 --retry-delay 2 --retry-all-errors --max-time 60 -o openapi.yaml https://zernio.com/openapi.yaml | |
| echo "Fetched OpenAPI spec" | |
| - name: Generate SDK | |
| run: | | |
| openapi-generator-cli generate \ | |
| -i openapi.yaml \ | |
| -g php \ | |
| -o . \ | |
| --skip-validate-spec \ | |
| --additional-properties=packageName=Zernio,invokerPackage=Zernio,apiPackage=Api,modelPackage=Model \ | |
| --git-user-id=zernio-dev \ | |
| --git-repo-id=zernio-php | |
| - name: Add Late namespace backwards-compat shim | |
| run: | | |
| # Register a lazy spl_autoloader that aliases any referenced | |
| # Late\Foo to Zernio\Foo via class_alias() on first access. This | |
| # keeps pre-rebrand users working without having to duplicate | |
| # every class into a second namespace. | |
| cat > lib/LateAliases.php <<'PHP' | |
| <?php | |
| /** | |
| * Legacy "Late\" namespace support. | |
| * | |
| * The SDK was originally published under the Late\ namespace and | |
| * later rebranded to Zernio\. This autoloader maps any Late\Foo | |
| * reference to Zernio\Foo at resolution time via class_alias(). | |
| * | |
| * Loaded automatically via composer.json "autoload.files". | |
| */ | |
| spl_autoload_register(function (string $class): void { | |
| if (strncmp($class, 'Late\\', 5) !== 0) { | |
| return; | |
| } | |
| $zernioClass = 'Zernio\\' . substr($class, 5); | |
| if (class_exists($zernioClass, true) | |
| || interface_exists($zernioClass, true) | |
| || trait_exists($zernioClass, true)) { | |
| class_alias($zernioClass, $class); | |
| } | |
| }, true, true); | |
| PHP | |
| - name: Register Late shim in composer.json | |
| run: | | |
| # Add lib/LateAliases.php to composer autoload "files" so it's | |
| # loaded automatically for any project that requires the SDK. | |
| jq '.autoload.files = ((.autoload.files // []) + ["lib/LateAliases.php"] | unique)' \ | |
| composer.json > tmp.json && mv tmp.json composer.json | |
| - name: Patch README install instructions | |
| run: | | |
| python3 -c " | |
| import re, textwrap | |
| with open('README.md', 'r') as f: | |
| content = f.read() | |
| install_section = textwrap.dedent('''## Installation | |
| ### Requirements | |
| PHP 8.1 and later. | |
| ### Composer | |
| \`\`\`bash | |
| composer require zernio-dev/zernio-php | |
| \`\`\` | |
| Use the \`Zernio\\\\\` namespace. The legacy \`Late\\\\\` namespace is auto-aliased on load for backwards compatibility.''') | |
| content = re.sub(r'## Installation & Usage.*?(?=\n## )', install_section + '\n\n', content, flags=re.DOTALL) | |
| with open('README.md', 'w') as f: | |
| f.write(content) | |
| " | |
| - name: Install dependencies | |
| run: composer install --no-interaction | |
| - name: Run tests | |
| run: ./vendor/bin/phpunit | |
| - name: Check for changes | |
| id: changes | |
| run: | | |
| git add -A | |
| if git diff --staged --quiet; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Get next version | |
| if: steps.changes.outputs.has_changes == 'true' | |
| id: version | |
| run: | | |
| # Highest existing tag, NOT `git describe`: a no-commit run once tagged | |
| # v0.0.298 onto the commit already tagged v0.0.297, and describe kept | |
| # resolving the tie to v0.0.297 — so every later run recomputed | |
| # "next = v0.0.298", found the tag taken, and silently reused the old | |
| # release (Packagist was stuck from 2026-05-27 to 2026-07-17). | |
| LATEST_TAG=$(git tag -l 'v*' | sort -V | tail -n1) | |
| LATEST_TAG=${LATEST_TAG:-v0.0.0} | |
| NEW_VERSION=$(echo $LATEST_TAG | awk -F. '{printf "v%d.%d.%d", $1, $2, $3+1}' | sed 's/vv/v/') | |
| COMPOSER_VERSION=$(echo $NEW_VERSION | sed 's/v//') | |
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "composer_version=$COMPOSER_VERSION" >> $GITHUB_OUTPUT | |
| - name: Update composer.json version | |
| if: steps.changes.outputs.has_changes == 'true' | |
| run: | | |
| jq '.version = "${{ steps.version.outputs.composer_version }}"' composer.json > tmp.json && mv tmp.json composer.json || true | |
| - name: Commit and push | |
| if: steps.changes.outputs.has_changes == 'true' | |
| id: commit | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add -A | |
| # The "Check for changes" gate above can report has_changes=true off a | |
| # transient diff (e.g. the generator strips composer.json's `version` | |
| # field, which the version-bump step then restores to the same value). | |
| # Once that churn is neutralized there may be nothing left to commit; | |
| # `git commit` would exit non-zero and, under `bash -e`, fail the job. | |
| # Bail out cleanly instead so an idempotent regen is a success, not red. | |
| if git diff --staged --quiet; then | |
| echo "No net changes after version bump; nothing to commit." | |
| echo "committed=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "committed=true" >> $GITHUB_OUTPUT | |
| git commit -m "chore: regenerate from OpenAPI spec | |
| - Auto-generated SDK updates | |
| - Version: ${{ steps.version.outputs.new_version }}" | |
| # Retry with rebase to survive concurrent dispatches that land | |
| # back-to-back pushes to main. | |
| for attempt in 1 2 3; do | |
| if git push; then break; fi | |
| echo "Push attempt $attempt failed; rebasing and retrying..." | |
| git pull --rebase origin main | |
| sleep $((attempt * 5)) | |
| done | |
| - name: Create GitHub Release | |
| # Gate on an actual commit: tagging a no-commit run is how the | |
| # duplicate v0.0.297/v0.0.298 tag pair happened. | |
| if: steps.changes.outputs.has_changes == 'true' && steps.commit.outputs.committed == 'true' | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ steps.version.outputs.new_version }} | |
| name: ${{ steps.version.outputs.new_version }} | |
| generate_release_notes: true | |
| body: | | |
| ## Auto-generated SDK Update | |
| ```bash | |
| composer require zernio-dev/zernio-php | |
| ``` | |
| Use the `Zernio\` namespace. The legacy `Late\` namespace remains | |
| available for backwards compatibility via runtime class aliasing. | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |