Update versions #108
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: Update versions | |
| # Refresh the SDK versions shown on the product family pages from the canonical feed | |
| # https://products.groupdocs.com/versions.json. Upstream regenerates that file twice a day | |
| # (06:00 + 18:00 UTC via its own GitHub Action), so we run +10 minutes later. | |
| # | |
| # scripts/update_versions.py trims the feed to just the fields we render (version + release-notes URL) | |
| # and sorts keys, so data/versions.json only changes when a version actually moves (not on the | |
| # upstream's generatedAt/downloads churn). | |
| # | |
| # Single-branch model: the data file is committed once, to `main`, and staging is redeployed. | |
| # Production is NOT redeployed by default — see AUTO_PROMOTE_VERSIONS below. | |
| # | |
| # Note this job commits with GITHUB_TOKEN, whose pushes deliberately do not trigger other | |
| # workflows. That is why it calls the deploy jobs itself, and why a deploy marker in its commit | |
| # message would be inert — do not add one. | |
| on: | |
| schedule: | |
| - cron: '10 6,18 * * *' # 06:10 + 18:10 UTC (+10 min after upstream publishes versions.json) | |
| workflow_dispatch: | |
| inputs: | |
| force: | |
| description: Redeploy even if versions did not change | |
| type: boolean | |
| default: false | |
| environment: | |
| description: When forcing, which environment(s) to redeploy | |
| type: choice | |
| options: [both, production, staging] | |
| default: staging | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: update-versions | |
| cancel-in-progress: false | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| do_staging: ${{ steps.sync.outputs.do_staging }} | |
| do_prod: ${{ steps.sync.outputs.do_prod }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| - name: Fetch + transform versions.json | |
| run: python3 scripts/update_versions.py --out /tmp/versions.json | |
| - name: Commit data/versions.json on main | |
| id: sync | |
| env: | |
| FORCE: ${{ github.event.inputs.force || 'false' }} | |
| FORCE_ENV: ${{ github.event.inputs.environment || 'staging' }} | |
| # Repo variable, default off. When true, a version change also publishes to production. | |
| # Leave it off unless you want the site continuously deployed: `main` is now the single | |
| # branch, so a production run here publishes EVERY product's current content, not just | |
| # the version strings. | |
| AUTO_PROMOTE: ${{ vars.AUTO_PROMOTE_VERSIONS || 'false' }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "groupdocs-version-bot" | |
| git config user.email "groupdocs-version-bot@users.noreply.github.com" | |
| git fetch origin main | |
| git checkout -B main origin/main | |
| cp /tmp/versions.json data/versions.json | |
| changed=false | |
| if git diff --quiet -- data/versions.json; then | |
| echo "no version changes" | |
| else | |
| git add data/versions.json | |
| git commit -m "Update SDK versions from products.groupdocs.com/versions.json" | |
| # `main` is now the only branch, so it also absorbs every product sync. Retry on a | |
| # racing push rather than failing (or, worse, silently losing) the update. | |
| pushed=false | |
| for i in 1 2 3 4 5; do | |
| if git push origin main; then pushed=true; break; fi | |
| sleep $(( i * 5 )) | |
| git fetch origin main | |
| git rebase origin/main | |
| done | |
| if [ "$pushed" != true ]; then | |
| echo "::error::could not push data/versions.json after 5 attempts" | |
| exit 1 | |
| fi | |
| changed=true | |
| echo "versions updated" | |
| fi | |
| do_staging=$changed | |
| do_prod=false | |
| if [ "$AUTO_PROMOTE" = "true" ] && [ "$changed" = "true" ]; then | |
| do_prod=true | |
| fi | |
| # Manual dispatch with force: redeploy the selected environment(s) even with no change. | |
| if [ "$FORCE" = "true" ]; then | |
| case "$FORCE_ENV" in | |
| production) do_prod=true ;; | |
| staging) do_staging=true ;; | |
| *) do_prod=true; do_staging=true ;; | |
| esac | |
| fi | |
| { | |
| echo "do_staging=$do_staging" | |
| echo "do_prod=$do_prod" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "changed=$changed do_staging=$do_staging do_prod=$do_prod" | |
| # Rebuild + redeploy the product family pages with the new versions. Only the 15 product builds | |
| # render versions (home/aggregates don't), so we skip them. Both environments build `main` — the | |
| # single deploy branch. | |
| deploy_staging: | |
| needs: sync | |
| if: needs.sync.outputs.do_staging == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| product_family: | |
| - annotation | |
| - assembly | |
| - classification | |
| - comparison | |
| - conversion | |
| - editor | |
| - markdown | |
| - merger | |
| - metadata | |
| - parser | |
| - redaction | |
| - search | |
| - signature | |
| - viewer | |
| - watermark | |
| uses: ./.github/workflows/deploy_product.yml | |
| with: | |
| product_family: ${{ matrix.product_family }} | |
| environment: staging | |
| ref: main | |
| secrets: inherit | |
| deploy_production: | |
| needs: [sync, deploy_staging] | |
| # `always()` so an explicit production force still runs when deploy_staging was skipped. | |
| if: ${{ always() && needs.sync.outputs.do_prod == 'true' | |
| && needs.deploy_staging.result != 'failure' && needs.deploy_staging.result != 'cancelled' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| product_family: | |
| - annotation | |
| - assembly | |
| - classification | |
| - comparison | |
| - conversion | |
| - editor | |
| - markdown | |
| - merger | |
| - metadata | |
| - parser | |
| - redaction | |
| - search | |
| - signature | |
| - viewer | |
| - watermark | |
| uses: ./.github/workflows/deploy_product.yml | |
| with: | |
| product_family: ${{ matrix.product_family }} | |
| environment: production | |
| ref: main | |
| secrets: inherit |