Release #9
Workflow file for this run
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: Release | |
| # Triggered when a GitHub Release is published. | |
| # Flow: | |
| # 1. scan-app — npm audit (HIGH+) + govulncheck on go-web-proxy | |
| # 2. build-pwa — npm run build, upload dist/ as a workflow artifact | |
| # 3. build-image — buildx + Trivy (HIGH/CRITICAL) + push to GHCR | |
| # 4. deploy-pages — sync dist/ into the `dist` branch (preserving committed | |
| # vendor JS files); Pages auto-redeploys from that branch | |
| # | |
| # Any HIGH/CRITICAL finding in scan-app or build-image blocks the release — | |
| # image is not pushed, Pages is not updated. | |
| # | |
| # Pages source must be set to: Settings → Pages → Source: Deploy from a branch | |
| # → Branch: dist → Folder: / (root). The `dist` branch is expected to already | |
| # exist and contain the vendor files (solclient.js, jszip.min.js) plus an | |
| # empty .nojekyll file so Pages skips Jekyll processing. | |
| on: | |
| release: | |
| types: [published] | |
| permissions: | |
| contents: read | |
| env: | |
| IMAGE_NAME: ghcr.io/${{ github.repository }} | |
| NODE_VERSION: '22' | |
| GO_VERSION: '1.26' | |
| # Opt into Node.js 24 for all JavaScript actions ahead of the June 2026 | |
| # default flip. Drops the "Node.js 20 actions are deprecated" warnings. | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' | |
| concurrency: | |
| group: release-${{ github.event.release.tag_name }} | |
| cancel-in-progress: false | |
| jobs: | |
| scan-app: | |
| name: Scan source (npm audit + govulncheck) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - run: npm ci --no-audit --no-fund | |
| - name: npm audit (fail on HIGH/CRITICAL) | |
| run: npm run scan:npm | |
| # govulncheck-action installs Go itself via go-version-input — no separate | |
| # setup-go step. go-web-proxy has stdlib-only deps, so there is no go.sum | |
| # to cache and no benefit from setup-go's caching layer. | |
| - name: govulncheck (fail on any finding) | |
| uses: golang/govulncheck-action@v1 | |
| with: | |
| go-version-input: ${{ env.GO_VERSION }} | |
| work-dir: go-web-proxy | |
| build-pwa: | |
| name: Build PWA bundle | |
| needs: scan-app | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - run: npm ci --no-audit --no-fund | |
| # Produces dist/index.html (prod), dist/mock.html (demo), dist/min.html (minimal). | |
| - run: npm run build | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: pwa-bundle | |
| path: dist | |
| retention-days: 7 | |
| if-no-files-found: error | |
| build-image: | |
| name: Build, scan, and push image | |
| needs: scan-app | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # OCI image references must be all-lowercase. `${{ github.repository }}` | |
| # preserves the org's original casing (e.g. SolaceLabs), which GHCR's push | |
| # endpoint accepts but the strict OCI reference parser (used by Trivy and | |
| # other tools) rejects. Lowercase IMAGE_NAME once, here, so every | |
| # downstream step sees a valid reference. | |
| - name: Lowercase image name | |
| run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> "$GITHUB_ENV" | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Compute image tags | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.IMAGE_NAME }} | |
| # Release tag format vX.Y.Z (e.g. v3.3.0) → produces tags 3.3.0, 3.3, 3, latest | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=raw,value=latest | |
| # Load the image into the local daemon so Trivy can scan it before we push. | |
| - name: Build image (load into local daemon) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| load: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Trivy scan (HIGH/CRITICAL, fail on findings) | |
| uses: aquasecurity/trivy-action@v0.36.0 | |
| with: | |
| image-ref: ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} | |
| severity: HIGH,CRITICAL | |
| exit-code: '1' | |
| ignore-unfixed: true | |
| vuln-type: os,library | |
| - name: Push image (all computed tags) | |
| run: | | |
| echo "${{ steps.meta.outputs.tags }}" | while IFS= read -r tag; do | |
| [ -z "$tag" ] && continue | |
| docker push "$tag" | |
| done | |
| deploy-pages: | |
| name: Deploy PWA to `dist` branch | |
| needs: [build-pwa, build-image] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| # Check out the dist branch itself — vendor files (solclient.js, jszip.min.js) | |
| # and Pages-control files (.nojekyll, CNAME) are expected to already be | |
| # committed here. We overlay the freshly-built bundle on top. | |
| - name: Check out dist branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: dist | |
| fetch-depth: 0 | |
| - name: Download PWA bundle | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: pwa-bundle | |
| path: _bundle | |
| # Only index.html is published. mock.html and min.html stay as workflow | |
| # artifacts (downloadable from the run page) but are not pushed to dist. | |
| # Anything else already present on the dist branch (vendor files, | |
| # .nojekyll, CNAME, etc.) is left untouched. | |
| - name: Copy index.html into dist branch | |
| run: | | |
| cp _bundle/index.html ./index.html | |
| rm -rf _bundle | |
| - name: Commit and push to dist branch | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add -A | |
| if git diff --staged --quiet; then | |
| echo "No changes to deploy." | |
| exit 0 | |
| fi | |
| git commit -m "Deploy ${{ github.event.release.tag_name }}" | |
| git push origin dist |