chore(decdn_node,charts): sync config-schema surface to decdn main @ d3bc7da7 #116
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| # Least privilege by default; jobs widen only what they need. | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # Third-party actions are pinned to a full commit SHA (a re-pointed tag can ship | |
| # malicious code — cf. the March 2026 KICS action compromise). The trailing | |
| # comment records the human-readable version; .github/dependabot.yml bumps them. | |
| jobs: | |
| # Detect whether ansible/ or the Helm chart changed so heavy jobs skip unrelated PRs. | |
| changes: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| ansible: ${{ steps.filter.outputs.ansible }} | |
| helm: ${{ steps.filter.outputs.helm }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 | |
| id: filter | |
| with: | |
| filters: | | |
| ansible: | |
| - 'ansible/**' | |
| # The chart shares the schema-key inventory and checker with molecule. | |
| helm: | |
| - 'charts/**' | |
| - 'ansible/molecule/schema/files/**' | |
| - 'Makefile' | |
| - '.github/workflows/ci.yml' | |
| # Ansible style + best-practice + the production-profile SECURITY rules, | |
| # plus a syntax-check of every playbook. Runs only when ansible/ changed. | |
| ansible-lint: | |
| needs: changes | |
| if: needs.changes.outputs.ansible == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: ansible | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Ansible tooling | |
| run: python -m pip install --upgrade ansible ansible-lint yamllint | |
| - name: Install Galaxy collections | |
| run: make deps # must precede lint/syntax-check | |
| - name: Lint (yamllint + ansible-lint) | |
| run: make lint | |
| - name: Syntax-check playbook | |
| # Dummy inventory: no real hosts.yml is committed upstream (only hosts.yml.example), | |
| # and --syntax-check only parses, it never connects. | |
| run: ansible-playbook playbooks/site.yml --syntax-check -i localhost, | |
| # Build the public `decdn.node` collection and run galaxy-importer's checks — | |
| # the same validation Galaxy runs on upload (metadata, license, README, embedded | |
| # ansible-lint). This is a readiness GATE only: it never publishes and needs no | |
| # token. Runs only when ansible/ changed. | |
| galaxy-build: | |
| needs: changes | |
| if: needs.changes.outputs.ansible == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: ansible | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Install build + import tooling | |
| run: python -m pip install --upgrade ansible-core ansible-lint galaxy-importer | |
| - name: Vendor collection dependencies | |
| # So galaxy-importer's embedded ansible-lint can resolve the roles' FQCNs | |
| # (devsec.hardening, ansible.posix). Installed under ansible/collections. | |
| run: make deps | |
| env: | |
| ANSIBLE_COLLECTIONS_PATH: collections | |
| - name: Build + validate the decdn.node collection | |
| run: make galaxy-check | |
| env: | |
| ANSIBLE_COLLECTIONS_PATH: collections | |
| - name: Upload collection artifact | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: decdn-node-collection | |
| path: ansible/build/decdn-node-*.tar.gz | |
| if-no-files-found: ignore | |
| # Helm chart: `helm lint --strict`, positive + negative render tests, kubeconform | |
| # (digest-pinned image) and the upstream schema-key check shared with molecule — | |
| # all via `make lint-helm`, the same command developers run locally. | |
| helm: | |
| needs: changes | |
| if: needs.changes.outputs.helm == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # Helm version is pinned here AND in the kics job below; bump both. | |
| - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1 | |
| with: | |
| version: v4.3.0 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Chart lint + render tests | |
| # yq (mikefarah) and Docker ship on ubuntu-latest. No decdn binary here, so | |
| # the real `decdn config validate` step reports SKIPPED; run it locally with | |
| # DECDN_CLI=... before bumping the decdn version. | |
| run: make lint-helm | |
| # Dedicated IaC security scan of the Ansible tree and the rendered Helm chart, driven straight from the | |
| # digest-pinned KICS *engine* image by `make security` — the exact command | |
| # developers run locally, so CI and local results cannot drift. KICS severities | |
| # are CRITICAL/HIGH/MEDIUM/LOW/INFO; the engine's own `--fail-on high` exit | |
| # code is the gate. | |
| # | |
| # SUPPLY-CHAIN NOTE: we deliberately do NOT use Checkmarx/kics-github-action. | |
| # Its git tags were hijacked in the March 2026 TeamPCP attack (CISA KEV), and | |
| # beyond that history its entrypoint `apk add`s nodejs/npm at *run* time inside | |
| # its digest-pinned base image and then executes the result — an unpinned fetch | |
| # that defeats the pinning it advertises. That fetch also breaks the action | |
| # outright today: Chainguard's current nodejs wants a newer glibc than the | |
| # pinned base ships, so `node dist/index.js` dies and the step exits non-zero | |
| # no matter what the scan found. Driving the engine image ourselves drops the | |
| # Node layer entirely and leaves one pinned artifact — `KICS_IMAGE` in the | |
| # Makefile, pinned by Docker Hub digest (a different artifact from the | |
| # hijacked action). See CONTRIBUTING.md. | |
| kics: | |
| needs: changes | |
| if: needs.changes.outputs.ansible == 'true' || needs.changes.outputs.helm == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1 | |
| with: | |
| version: v4.3.0 | |
| - name: KICS security scan of ansible/ + the rendered chart (fail on HIGH) | |
| run: make security | |
| # Replaces the action's `enable_jobs_summary`. | |
| - name: Summarise KICS findings | |
| if: always() | |
| run: | | |
| for scan in ansible helm; do | |
| results=kics-results/results.json | |
| [ "$scan" = helm ] && results=kics-results/helm/results.json | |
| { | |
| echo "### KICS IaC scan ($scan)" | |
| echo | |
| if [ -f "$results" ]; then | |
| jq -r '.severity_counters | |
| | "| CRITICAL | HIGH | MEDIUM | LOW | INFO |", | |
| "|---|---|---|---|---|", | |
| "| \(.CRITICAL) | \(.HIGH) | \(.MEDIUM) | \(.LOW) | \(.INFO) |"' "$results" | |
| echo | |
| jq -r 'if (.total_counter // 0) == 0 then "No findings." | |
| else (.queries[] | .query_name as $q | .severity as $s | |
| | .files[] | "- **\($s)** \($q) — `\(.file_name):\(.line)`") | |
| end' "$results" | |
| else | |
| echo "No results file — the scan did not complete." | |
| fi | |
| echo | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| done | |
| - name: Upload KICS results | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: kics-results | |
| path: kics-results/ | |
| if-no-files-found: ignore | |
| # To surface KICS findings in the repo's Security tab, enable GitHub code | |
| # scanning (free on public repos) and un-comment the block below (add | |
| # `security-events: write` to this job's permissions): | |
| # - name: Upload SARIF to code scanning | |
| # if: always() | |
| # uses: github/codeql-action/upload-sarif@d77b13a0df3134d64a457ea9003f600b09fa1c8a # v3.36.1 | |
| # with: | |
| # sarif_file: kics-results/results.sarif | |
| # Lint the workflow files themselves. | |
| actionlint: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| checks: write | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: reviewdog/action-actionlint@d290e336d5a743810aef4404f757dc862276d2ae # v1.73.4 | |
| with: | |
| reporter: github-check | |
| fail_on_error: true |