From bf5c39d1673699116682f2db5fe9609a3788a881 Mon Sep 17 00:00:00 2001 From: merlin Date: Tue, 19 May 2026 18:57:50 +0800 Subject: [PATCH 1/2] feat(ci): add CodeQL reusable workflow and Workflow Sanity check (P1 batch) reusable-codeql.yml: - workflow_call with language (go | javascript-typescript) and optional build-command inputs - Minimal permissions: security-events:write + contents:read + actions:read - github/codeql-action pinned to SHA (v4: 7c1e4cf0...) - queries: security-extended for comprehensive coverage - autobuild or custom build depending on input workflow-sanity.yml: - Triggers on PR / push to main when .github/workflows/** or .github/actions/** change - no-tabs job: Python3 scan across *.yml and *.yaml - actionlint job: v1.7.12, SHA-256 verified, extracted to RUNNER_TEMP + GITHUB_PATH - actionlint runs without path args (auto-discovers all workflow files) --- .github/workflows/reusable-codeql.yml | 52 +++++++++++++++++ .github/workflows/workflow-sanity.yml | 83 +++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 .github/workflows/reusable-codeql.yml create mode 100644 .github/workflows/workflow-sanity.yml diff --git a/.github/workflows/reusable-codeql.yml b/.github/workflows/reusable-codeql.yml new file mode 100644 index 0000000..d530e3a --- /dev/null +++ b/.github/workflows/reusable-codeql.yml @@ -0,0 +1,52 @@ +name: Reusable — CodeQL Analysis + +on: + workflow_call: + inputs: + language: + description: 'CodeQL language: go or javascript-typescript' + type: string + required: true + build-command: + description: 'Optional build command before analysis (e.g. "go build ./...")' + type: string + required: false + default: '' + +permissions: {} + +jobs: + analyze: + name: CodeQL (${{ inputs.language }}) + permissions: + actions: read + contents: read + security-events: write + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + fetch-depth: 2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@7c1e4cf0b20d7c1872b26569c00ba908797a59bf # v4 + with: + languages: ${{ inputs.language }} + queries: security-extended + # config-file is optional; repos can add .github/codeql/codeql-config.yml + + - name: Autobuild + if: inputs.build-command == '' + uses: github/codeql-action/autobuild@7c1e4cf0b20d7c1872b26569c00ba908797a59bf # v4 + + - name: Custom build + if: inputs.build-command != '' + run: ${{ inputs.build-command }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@7c1e4cf0b20d7c1872b26569c00ba908797a59bf # v4 + with: + category: /language:${{ inputs.language }} diff --git a/.github/workflows/workflow-sanity.yml b/.github/workflows/workflow-sanity.yml new file mode 100644 index 0000000..af086c6 --- /dev/null +++ b/.github/workflows/workflow-sanity.yml @@ -0,0 +1,83 @@ +name: Workflow Sanity + +on: + pull_request: + paths: + - '.github/workflows/**' + - '.github/actions/**' + push: + branches: [main] + paths: + - '.github/workflows/**' + - '.github/actions/**' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: workflow-sanity-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + no-tabs: + name: No tabs in workflow files + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Fail on tabs in workflow files + run: | + python3 - <<'PY' + from __future__ import annotations + import pathlib, sys + + bad: list[str] = [] + for root in [pathlib.Path(".github/workflows"), pathlib.Path(".github/actions")]: + if not root.exists(): + continue + for path in sorted(root.rglob("*.yml")): + if b"\t" in path.read_bytes(): + bad.append(str(path)) + for path in sorted(root.rglob("*.yaml")): + if b"\t" in path.read_bytes(): + bad.append(str(path)) + + if bad: + print("Tabs found in workflow file(s):") + for p in bad: + print(f" - {p}") + sys.exit(1) + + print(f"No tabs found in {len(list(pathlib.Path('.github').rglob('*.yml')))} workflow file(s). ✓") + PY + + actionlint: + name: actionlint + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Install actionlint + run: | + set -euo pipefail + VERSION="v1.7.12" + TARBALL="actionlint_1.7.12_linux_amd64.tar.gz" + SHA256="8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" + curl -fsSL "https://github.com/rhysd/actionlint/releases/download/${VERSION}/${TARBALL}" \ + -o "${RUNNER_TEMP}/${TARBALL}" + echo "${SHA256} ${RUNNER_TEMP}/${TARBALL}" | sha256sum -c - + tar -xzf "${RUNNER_TEMP}/${TARBALL}" -C "${RUNNER_TEMP}" actionlint + echo "${RUNNER_TEMP}" >> "${GITHUB_PATH}" + "${RUNNER_TEMP}/actionlint" --version + + - name: Run actionlint + run: | + # No path args — actionlint scans all *.yml and *.yaml under .github/workflows/ + actionlint -color From 88248bed3983e518b836249e0de6cd945087b802 Mon Sep 17 00:00:00 2001 From: merlin Date: Tue, 19 May 2026 19:27:10 +0800 Subject: [PATCH 2/2] fix(ci): add workflow_call trigger to workflow-sanity.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without on.workflow_call:, external repos cannot call this workflow via 'uses:' — GitHub requires reusable workflows to explicitly declare the workflow_call trigger. Keeps existing pull_request / push / workflow_dispatch triggers so the .github repo's own CI still runs sanity checks on its own workflow files. --- .github/workflows/workflow-sanity.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/workflow-sanity.yml b/.github/workflows/workflow-sanity.yml index af086c6..7c917cc 100644 --- a/.github/workflows/workflow-sanity.yml +++ b/.github/workflows/workflow-sanity.yml @@ -1,6 +1,9 @@ name: Workflow Sanity on: + # Allow external repos to call this as a reusable workflow + workflow_call: + pull_request: paths: - '.github/workflows/**'