diff --git a/.github/setup/action.yml b/.github/setup/action.yml new file mode 100644 index 0000000..01cedd0 --- /dev/null +++ b/.github/setup/action.yml @@ -0,0 +1,25 @@ +name: Setup development tools +description: Install the locked Devbox environment and restore Go and ko caches. +runs: + using: composite + steps: + - uses: jetify-com/devbox-install-action@8c6a66ed6273138b1915457069de78cb52fe3bd7 # v0.15.0 + with: + devbox-version: 0.18.4 + enable-cache: 'true' + disable-nix-access-token: 'true' + - name: Locate Go caches + id: cache + shell: bash + run: | + : "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" + printf 'build=%s\nmodules=%s\n' "$(devbox run -- go env GOCACHE)" "$(devbox run -- go env GOMODCACHE)" >> "$GITHUB_OUTPUT" + printf 'KO_CACHE=%s/ko-cache\n' "$RUNNER_TEMP" >> "$GITHUB_ENV" + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: | + ${{ steps.cache.outputs.build }} + ${{ steps.cache.outputs.modules }} + ${{ runner.temp }}/ko-cache + key: ${{ runner.os }}-${{ runner.arch }}-tools-${{ hashFiles('devbox.lock', '**/go.sum', '.ko.yaml') }} + restore-keys: ${{ runner.os }}-${{ runner.arch }}-tools- diff --git a/.github/workflows/check-actions.yaml b/.github/workflows/check-actions.yaml index d7e6adb..520365e 100644 --- a/.github/workflows/check-actions.yaml +++ b/.github/workflows/check-actions.yaml @@ -16,10 +16,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + with: + persist-credentials: false - name: Ensure SHA pinned actions uses: zgosalvez/github-actions-ensure-sha-pinned-actions@60e3a74c7b74a319e8e53e46bc455205f7173e1a # v5.0.8 with: # slsa-github-generator requires using a semver tag for reusable workflows. # See: https://github.com/slsa-framework/slsa-github-generator#referencing-slsa-builders-and-generators allowlist: | - slsa-framework/slsa-github-generator \ No newline at end of file + slsa-framework/slsa-github-generator diff --git a/.github/workflows/check-commit.yaml b/.github/workflows/check-commit.yaml index 2927354..147bc78 100644 --- a/.github/workflows/check-commit.yaml +++ b/.github/workflows/check-commit.yaml @@ -20,4 +20,5 @@ jobs: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: fetch-depth: 0 - - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 \ No newline at end of file + persist-credentials: false + - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 6f3e981..c5417ae 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -8,50 +8,18 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 - with: - node-version: '24' - cache: npm - - run: npm ci --ignore-scripts --no-audit --no-fund - - name: Unit and local integration tests - run: npm test - - uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 - with: - version: v4.2.0 - - name: Real Helm packaging integration - run: npm run test:helm - - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 - with: - go-version: '1.25' - cache: false - - name: Install actionlint and Task - run: | - go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 - go install github.com/go-task/task/v3/cmd/task@v3.53.1 - - name: Real Task target integration - run: npm run test:task - - name: Validate workflows and composite actions - run: npm run lint - - name: Check shell scripts - run: shellcheck helm-oci-chart/publish.sh task-ko-publish/publish.sh - - name: Exercise exists action - id: exists - uses: ./exists - with: - value: example - - name: Exercise empty input - id: empty - uses: ./exists + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - value: '' - - name: Assert exists outputs - env: - PRESENT: ${{ steps.exists.outputs.result }} - EMPTY: ${{ steps.empty.outputs.result }} - run: test "$PRESENT" = true && test "$EMPTY" = false + persist-credentials: false + - uses: ./.github/setup + - name: Run all checks + run: devbox run -- task check:full diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..7050b0b --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,98 @@ +version: '3' + +silent: true + +set: [errexit, nounset, pipefail] + +vars: + ACTIONS: + sh: find . -name 'action.yaml' -not -path './node_modules/*' -print0 | sort -z | xargs -0 + +tasks: + default: + desc: List available tasks. + cmds: + - task --list + + ### Node.js tasks ### + + npm:ci: + desc: Install Node.js dependencies. + cmds: + - npm ci --ignore-scripts --no-audit --no-fund + + test: + desc: Run unit and local integration tests. + deps: [npm:ci] + cmds: + - npm test + + test:helm: + desc: Run Helm packaging integration tests. + deps: [npm:ci] + cmds: + - npm run test:helm + + test:task: + desc: Run Task target integration tests. + deps: [npm:ci] + cmds: + - npm run test:task + + ### Lint tasks ### + + lint:actions: + desc: Validate GitHub Actions and composite actions. + aliases: [lint-actions] + cmds: + - node scripts/lint.mjs + + lint:shell: + desc: Check shell scripts with shellcheck. + aliases: [lint-shell] + cmds: + - shellcheck helm-oci-chart/publish.sh task-ko-publish/publish.sh + + lint:zizmor: + desc: Security-lint GitHub Actions workflows and composite actions with zizmor. + aliases: [lint-zizmor] + cmds: + - zizmor -q .github/workflows/ {{.ACTIONS}} + + lint:zizmor-fix: + desc: Security-lint GitHub Actions workflows and composite actions with zizmor and auto-fix. + aliases: [lint-zizmor-fix] + cmds: + - zizmor -q .github/workflows/ {{.ACTIONS}} --fix=all + + ### Action exercises ### + + exercise:exists: + desc: Exercise the exists action with sample inputs. + cmds: + - | + # Simulate the exists action's composite step logic + value="example" + if [ -n "$value" ]; then echo "true"; else echo "false"; fi + - | + value="" + if [ -n "$value" ]; then echo "true"; else echo "false"; fi + + ### Aggregate checks ### + + check: + desc: Run the checks used by CI (e2e runs separately). + cmds: + - task: npm:ci + - task: test + - task: test:helm + - task: test:task + - task: lint:actions + - task: lint:shell + - task: lint:zizmor + + check:full: + desc: Run all CI checks including action exercises. + cmds: + - task: check + - task: exercise:exists diff --git a/devbox.json b/devbox.json new file mode 100644 index 0000000..c2a2f4e --- /dev/null +++ b/devbox.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.17.5/.schema/devbox.schema.json", + "packages": { + "zizmor": "1.25.2", + "go-task": "3.53.1", + "actionlint": "1.7.7", + "nodejs": "24", + "go": "1.27.1", + "shellcheck": "", + "kubernetes-helm": "4.2.0" + } +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 0000000..28034ca --- /dev/null +++ b/devbox.lock @@ -0,0 +1,335 @@ +{ + "lockfile_version": "1", + "packages": { + "actionlint@1.7.7": { + "last_modified": "2025-10-22T20:59:19Z", + "resolved": "github:NixOS/nixpkgs/01b6809f7f9d1183a2b3e081f0a1e6f8f415cb09#actionlint", + "source": "devbox-search", + "version": "1.7.7", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/f3cfldvgqa0m35dwr3br22bhpkr3smwz-actionlint-1.7.7", + "default": true + } + ], + "store_path": "/nix/store/f3cfldvgqa0m35dwr3br22bhpkr3smwz-actionlint-1.7.7" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/x4lfnmw2dw0rgjy0z2a7vq90d3lbv7l0-actionlint-1.7.7", + "default": true + } + ], + "store_path": "/nix/store/x4lfnmw2dw0rgjy0z2a7vq90d3lbv7l0-actionlint-1.7.7" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mbb6dmgd0pwwna3jlb4pgxrh1a738ngi-actionlint-1.7.7", + "default": true + } + ], + "store_path": "/nix/store/mbb6dmgd0pwwna3jlb4pgxrh1a738ngi-actionlint-1.7.7" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/nq00yby189n61rrriisvjj2x7drn7r6c-actionlint-1.7.7", + "default": true + } + ], + "store_path": "/nix/store/nq00yby189n61rrriisvjj2x7drn7r6c-actionlint-1.7.7" + } + } + }, + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "last_modified": "2026-10-16T02:15:57Z", + "resolved": "github:NixOS/nixpkgs/f20cd90e28fe0403873f473b29e42662cd86e8c4?lastModified=1792116957&narHash=sha256-EjZqHKObBfIOeHZnMRVV87HB3pPq%2FWBkmRJWyFxmZu8%3D" + }, + "go-task@3.53.1": { + "last_modified": "2026-10-02T22:55:44Z", + "resolved": "github:NixOS/nixpkgs/55ba7f49ef2962b42cbd126522b7df5f95037679#go-task", + "source": "devbox-search", + "version": "3.53.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mp3m86aziykrwshmqfr7i6w4ak4c8qz4-go-task-3.53.1", + "default": true + } + ], + "store_path": "/nix/store/mp3m86aziykrwshmqfr7i6w4ak4c8qz4-go-task-3.53.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/mwy1kwfwgq3diqxqkrkj8qbhrsr8xa4x-go-task-3.53.1", + "default": true + } + ], + "store_path": "/nix/store/mwy1kwfwgq3diqxqkrkj8qbhrsr8xa4x-go-task-3.53.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/3gax8wb82c05afr6bsi56ighadlzaqkc-go-task-3.53.1", + "default": true + } + ], + "store_path": "/nix/store/3gax8wb82c05afr6bsi56ighadlzaqkc-go-task-3.53.1" + } + } + }, + "go@1.27.1": { + "last_modified": "2026-10-02T22:55:44Z", + "resolved": "github:NixOS/nixpkgs/55ba7f49ef2962b42cbd126522b7df5f95037679#go_1_27", + "source": "devbox-search", + "version": "1.27.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/v0l8ihkyjdqqx97g0b8lkllicmalj0lq-go-1.27.1", + "default": true + } + ], + "store_path": "/nix/store/v0l8ihkyjdqqx97g0b8lkllicmalj0lq-go-1.27.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ba7p38wfl0pm8c1k4a7x9z35yk8kv719-go-1.27.1", + "default": true + } + ], + "store_path": "/nix/store/ba7p38wfl0pm8c1k4a7x9z35yk8kv719-go-1.27.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/pn86sk63a2lpmwx16vj4s5871slkyzfw-go-1.27.1", + "default": true + } + ], + "store_path": "/nix/store/pn86sk63a2lpmwx16vj4s5871slkyzfw-go-1.27.1" + } + } + }, + "kubernetes-helm@4.2.0": { + "last_modified": "2026-06-11T01:27:03Z", + "resolved": "github:NixOS/nixpkgs/b503dde361500433ca25a32e8f4d218bf58fb659#kubernetes-helm", + "source": "devbox-search", + "version": "4.2.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/3y7jjsm5k83gj9rwkb27dhsy2vampj3p-kubernetes-helm-4.2.0", + "default": true + } + ], + "store_path": "/nix/store/3y7jjsm5k83gj9rwkb27dhsy2vampj3p-kubernetes-helm-4.2.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/106wx01cxsvj46ick4avr8cim9dxzgld-kubernetes-helm-4.2.0", + "default": true + } + ], + "store_path": "/nix/store/106wx01cxsvj46ick4avr8cim9dxzgld-kubernetes-helm-4.2.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/byv18slx3iz3rdg27jwkp96dlxfk3xh3-kubernetes-helm-4.2.0", + "default": true + } + ], + "store_path": "/nix/store/byv18slx3iz3rdg27jwkp96dlxfk3xh3-kubernetes-helm-4.2.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/ws5b9zj77d5v3cagla29v8w89z5bfgkp-kubernetes-helm-4.2.0", + "default": true + } + ], + "store_path": "/nix/store/ws5b9zj77d5v3cagla29v8w89z5bfgkp-kubernetes-helm-4.2.0" + } + } + }, + "nodejs@24": { + "last_modified": "2025-12-29T16:45:58Z", + "plugin_version": "0.0.4", + "resolved": "github:NixOS/nixpkgs/346dd96ad74dc4457a9db9de4f4f57dab2e5731d#nodejs_24", + "source": "devbox-search", + "version": "24.12.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/b1h0af3yb3z9jz048phclcqw6ihiww67-nodejs-24.12.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/ksn4nj0nbp0ikmh4scy9x4biqqdswwhy-nodejs-24.12.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/3x72bm7l30ib7l7cximrp0sklcq8jqwy-nodejs-24.12.0-libv8" + } + ], + "store_path": "/nix/store/b1h0af3yb3z9jz048phclcqw6ihiww67-nodejs-24.12.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9jfsyhcr9c6rpm94lrkibv75jkggvwss-nodejs-24.12.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/7izhv9x1yvsmyrhmmyps6bxjjclajm80-nodejs-24.12.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/8hl74pnc1qyz68rg10ixw7fz32hcxqkk-nodejs-24.12.0-libv8" + } + ], + "store_path": "/nix/store/9jfsyhcr9c6rpm94lrkibv75jkggvwss-nodejs-24.12.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/cj1243xswxvnwgifyiyg8axhn0r2vl48-nodejs-24.12.0", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/kzia3rkaf9vwpn3py23q9v49jpjm6zx7-nodejs-24.12.0-dev" + }, + { + "name": "libv8", + "path": "/nix/store/xszji81xj7ghjhz7jk0jkhb84nfwyd8f-nodejs-24.12.0-libv8" + } + ], + "store_path": "/nix/store/cj1243xswxvnwgifyiyg8axhn0r2vl48-nodejs-24.12.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9z1v3wyrxp6fpyzw21lcakd5w7aknzyc-nodejs-24.12.0", + "default": true + }, + { + "name": "libv8", + "path": "/nix/store/5yvx909wlis13qd2y1ahwl7ij21ma69a-nodejs-24.12.0-libv8" + }, + { + "name": "dev", + "path": "/nix/store/7ll99p08gmpg0n048669p80zjibl5akr-nodejs-24.12.0-dev" + } + ], + "store_path": "/nix/store/9z1v3wyrxp6fpyzw21lcakd5w7aknzyc-nodejs-24.12.0" + } + } + }, + "shellcheck": { + "resolved": "github:NixOS/nixpkgs/319e4cebef10cbb636c6ea392137b52ad105336a?narHash=sha256-t1hT8UW97rFCWCJO7%2FpClvu0AhLN5V3uir2M0ic4w%2FQ%3D#shellcheck", + "source": "nixpkg", + "systems": { + "x86_64-linux": { + "outputs": [ + { + "name": "doc", + "path": "/nix/store/d6qq2890rl3022l9l5737750qvmplcnc-shellcheck-0.11.0-doc", + "default": true + }, + { + "name": "man", + "path": "/nix/store/ky7sala6zn1i3gny08ngzywp6bc8db50-shellcheck-0.11.0-man", + "default": true + }, + { + "name": "bin", + "path": "/nix/store/x3ssi7xb2qz6p8h4xqjg1xy0j61l998m-shellcheck-0.11.0-bin", + "default": true + } + ] + } + } + }, + "zizmor@1.25.2": { + "last_modified": "2026-06-27T07:37:20Z", + "resolved": "github:NixOS/nixpkgs/3d46470bb3030020f7e1361f33514854f5bfa86d#zizmor", + "source": "devbox-search", + "version": "1.25.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vb83j795kmrfx0ywnkv8ginprmw8gbk0-zizmor-1.25.2", + "default": true + } + ], + "store_path": "/nix/store/vb83j795kmrfx0ywnkv8ginprmw8gbk0-zizmor-1.25.2" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/y6gka4nxgz8w1r9vdlppp0aag2srg2m9-zizmor-1.25.2", + "default": true + } + ], + "store_path": "/nix/store/y6gka4nxgz8w1r9vdlppp0aag2srg2m9-zizmor-1.25.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/y67dqc53bv9z4q6pfwa4bmm3j9bwqmy0-zizmor-1.25.2", + "default": true + } + ], + "store_path": "/nix/store/y67dqc53bv9z4q6pfwa4bmm3j9bwqmy0-zizmor-1.25.2" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/qzw65zsn85za1hsd4ll3k5qajz52qar8-zizmor-1.25.2", + "default": true + } + ], + "store_path": "/nix/store/qzw65zsn85za1hsd4ll3k5qajz52qar8-zizmor-1.25.2" + } + } + } + } +}