chore: update dependency node to v24.21.0 - autoclosed #361
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: Docker Build & Publish | |
| # All events build + push to both registries (Docker Hub + GHCR): | |
| # - Pull requests: validated with a native amd64 build only (--load, no | |
| # push) — PRs aren't a published artifact. | |
| # - Push to main: tagged latest (+ short sha for traceability) | |
| # - Push to development/nightly/feature/**: tagged with the (sanitized) | |
| # branch name, e.g. development, nightly, feature-foo | |
| # - Release published: tagged with the released semver (and latest, since a | |
| # fresh release should be latest) | |
| # | |
| # Multi-arch publishing builds each platform on its own NATIVE runner | |
| # (ubuntu-latest for amd64, ubuntu-24.04-arm for arm64) rather than | |
| # cross-building arm64 under QEMU emulation on an amd64 runner. On | |
| # 2026-08-09 a QEMU-emulated `npm ci --legacy-peer-deps` reliably crashed with "qemu: uncaught | |
| # target signal 4 (Illegal instruction) - core dumped" while running one of | |
| # esbuild's postinstall steps — deterministic on every retry, not a | |
| # transient hang. GitHub's Linux arm64 hosted runners are free for public | |
| # repos and sidestep emulation entirely for the actual build; QEMU is no | |
| # longer used anywhere in this workflow. Each arch pushes its image by | |
| # digest, then a merge job combines the digests into one multi-arch | |
| # manifest per tag and pushes that to both registries. | |
| # | |
| # Every published build is scanned for image vulnerabilities (Trivy) and | |
| # supply-chain risk (Syft SBOM + Grype), uploaded to the Security tab as | |
| # SARIF. Neither blocks the build right now — this is meant for visibility | |
| # (know where and when to patch) rather than enforcement; revisit once | |
| # there's a branch strategy to gate against. Known findings with no fix yet | |
| # are suppressed via .trivyignore / .grype.yaml — see SECURITY.md for why, | |
| # and the review date each suppression expires. | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main, development, nightly, 'feature/**'] | |
| release: | |
| types: [published] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/wikid82/hestia | |
| jobs: | |
| # PR builds validate the Dockerfile builds cleanly on native amd64 — no | |
| # push, no arm64 leg (self-hosters pull from the push/release builds | |
| # below, not from a PR). | |
| build-pr: | |
| if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'github-actions[bot]' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 | |
| - name: Free disk space | |
| uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 | |
| with: | |
| android: true | |
| dotnet: true | |
| haskell: true | |
| large-packages: true | |
| docker-images: false | |
| swap-storage: true | |
| tool-cache: false | |
| - name: Build (validate only) | |
| uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 | |
| with: | |
| context: . | |
| push: false | |
| load: true | |
| tags: hestia:pr-${{ github.event.pull_request.number }} | |
| - name: Trivy vulnerability scan (table) | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| image-ref: hestia:pr-${{ github.event.pull_request.number }} | |
| format: table | |
| severity: CRITICAL,HIGH | |
| exit-code: '0' | |
| trivyignores: .trivyignore | |
| # Push/release builds: one native runner per architecture, each pushing | |
| # its single-platform image by digest. See header comment for why this | |
| # replaced a single cross-platform QEMU build. | |
| build: | |
| if: github.event_name != 'pull_request' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| runner: ubuntu-latest | |
| - platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 20 | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout (release) | |
| if: github.event_name == 'release' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| ref: ${{ github.event.release.tag_name }} | |
| - name: Checkout | |
| if: github.event_name != 'release' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 | |
| - name: Log in to GHCR | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4 | |
| with: | |
| max_attempts: 3 | |
| timeout_minutes: 2 | |
| retry_wait_seconds: 10 | |
| command: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin | |
| - name: Free disk space | |
| uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 | |
| with: | |
| android: true | |
| dotnet: true | |
| haskell: true | |
| large-packages: true | |
| docker-images: false | |
| swap-storage: true | |
| tool-cache: false | |
| - name: Build and push by digest | |
| id: build | |
| uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 | |
| with: | |
| context: . | |
| platforms: ${{ matrix.platform }} | |
| # provenance: false — otherwise buildx wraps even a single-platform | |
| # push in a 2-entry OCI index (image manifest + attestation | |
| # manifest), so the exported digest points at an index rather than | |
| # the plain image manifest. That breaks anything resolving the | |
| # digest directly (confirmed: Trivy's remote fetcher errors with | |
| # "no child with platform linux/amd64 in index ..." trying to scan | |
| # it) and serves no purpose here since digest-merge already | |
| # produces one clean multi-arch manifest in the step below. | |
| provenance: false | |
| outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true | |
| - name: Export digest | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - name: Sanitize platform for artifact name | |
| id: platform-slug | |
| run: echo "slug=$(echo '${{ matrix.platform }}' | tr '/' '-')" >> "$GITHUB_OUTPUT" | |
| - name: Upload digest | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: digests-${{ steps.platform-slug.outputs.slug }} | |
| path: /tmp/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| # Combine the per-architecture digests into one multi-arch manifest per | |
| # tag, pushed to both registries, then scan the result. | |
| merge: | |
| if: github.event_name != 'pull_request' | |
| needs: build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| permissions: | |
| contents: read | |
| packages: write | |
| security-events: write | |
| steps: | |
| - name: Checkout (release) | |
| if: github.event_name == 'release' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| ref: ${{ github.event.release.tag_name }} | |
| - name: Checkout | |
| if: github.event_name != 'release' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Download all digests | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| path: /tmp/digests | |
| pattern: digests-* | |
| merge-multiple: true | |
| - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 | |
| - name: Log in to GHCR | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4 | |
| with: | |
| max_attempts: 3 | |
| timeout_minutes: 2 | |
| retry_wait_seconds: 10 | |
| command: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin | |
| - name: Log in to Docker Hub | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4 | |
| with: | |
| max_attempts: 3 | |
| timeout_minutes: 2 | |
| retry_wait_seconds: 10 | |
| command: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin | |
| - name: Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6 | |
| with: | |
| images: | | |
| wikid82/hestia | |
| ghcr.io/wikid82/hestia | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,format=short,enable={{is_default_branch}} | |
| type=ref,event=branch | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| - name: Create manifest list and push | |
| working-directory: /tmp/digests | |
| run: | | |
| docker buildx imagetools create \ | |
| $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ | |
| $(printf "${{ env.REGISTRY_IMAGE }}@sha256:%s " *) | |
| env: | |
| DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }} | |
| - name: Determine digest for scanning | |
| id: scan | |
| run: | | |
| # Either single-platform digest is representative for scanning — | |
| # OS package and npm dependency findings don't differ by arch. | |
| echo "digest=sha256:$(ls /tmp/digests | head -n1)" >> "$GITHUB_OUTPUT" | |
| - name: Trivy vulnerability scan (table) | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| image-ref: ${{ env.REGISTRY_IMAGE }}@${{ steps.scan.outputs.digest }} | |
| format: table | |
| severity: CRITICAL,HIGH | |
| exit-code: '0' | |
| trivyignores: .trivyignore | |
| - name: Trivy vulnerability scan (SARIF) | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| continue-on-error: true | |
| with: | |
| image-ref: ${{ env.REGISTRY_IMAGE }}@${{ steps.scan.outputs.digest }} | |
| format: sarif | |
| output: trivy-results.sarif | |
| severity: CRITICAL,HIGH | |
| trivyignores: .trivyignore | |
| - name: Upload Trivy results to Security tab | |
| uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4 | |
| continue-on-error: true | |
| with: | |
| sarif_file: trivy-results.sarif | |
| category: trivy-image-scan | |
| # anchore/sbom-action uploads the SBOM it generates as a workflow | |
| # artifact itself (and attaches it to the GitHub Release on release | |
| # events), so there is no separate upload-artifact step. Grype below | |
| # scans the pushed image directly rather than consuming this SBOM | |
| # file: sbom-action's `output-file` input only reliably writes to a | |
| # temp dir (v0.24.1 stopped writing it into the workspace at all), | |
| # and chaining the two tools through an on-disk file that a patch | |
| # bump can silently relocate is exactly what broke this job. Both | |
| # scanners now take the image digest independently, like the Trivy | |
| # steps above. | |
| - name: Generate SBOM | |
| uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 | |
| with: | |
| image: ${{ env.REGISTRY_IMAGE }}@${{ steps.scan.outputs.digest }} | |
| format: cyclonedx-json | |
| artifact-name: sbom-${{ github.sha }}.cyclonedx.json | |
| - name: Scan image for vulnerabilities (Grype) | |
| id: grype | |
| uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7 | |
| with: | |
| image: ${{ env.REGISTRY_IMAGE }}@${{ steps.scan.outputs.digest }} | |
| output-format: sarif | |
| severity-cutoff: high | |
| fail-build: false | |
| - name: Upload Grype results to Security tab | |
| uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4 | |
| continue-on-error: true | |
| with: | |
| sarif_file: ${{ steps.grype.outputs.sarif }} | |
| category: supply-chain-sbom |