mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-04 23:02:55 +00:00
Guard the remaining Windows Testbox workflow ref logging against GitHub Actions template injection by moving `target_ref` through step env before PowerShell reads it. Extend the local workflow check wrapper to run pinned `zizmor` across every workflow file, and keep Workflow Sanity's CI audit explicit with trusted-base pre-commit and zizmor configs for pull-request runs. Thanks @WT-WSL for the original report and patch. Co-authored-by: dev111-actor <captaintobb@outlook.com>
209 lines
8.1 KiB
YAML
209 lines
8.1 KiB
YAML
name: Workflow Sanity
|
|
|
|
on:
|
|
pull_request:
|
|
paths-ignore:
|
|
- "CHANGELOG.md"
|
|
push:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- "CHANGELOG.md"
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
|
|
jobs:
|
|
no-tabs:
|
|
if: github.event_name != 'workflow_dispatch'
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
CHECKOUT_REPO: ${{ github.repository }}
|
|
CHECKOUT_SHA: ${{ github.sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init "$GITHUB_WORKSPACE"
|
|
git -C "$GITHUB_WORKSPACE" config gc.auto 0
|
|
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
|
|
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
|
|
-c protocol.version=2 \
|
|
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
|
|
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
|
|
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
|
|
|
|
- name: Fail on tabs in workflow files
|
|
run: |
|
|
python - <<'PY'
|
|
from __future__ import annotations
|
|
|
|
import pathlib
|
|
import sys
|
|
|
|
root = pathlib.Path(".github/workflows")
|
|
bad: list[str] = []
|
|
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 path in bad:
|
|
print(f"- {path}")
|
|
sys.exit(1)
|
|
PY
|
|
|
|
actionlint:
|
|
if: github.event_name != 'workflow_dispatch'
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
CHECKOUT_REPO: ${{ github.repository }}
|
|
CHECKOUT_SHA: ${{ github.sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init "$GITHUB_WORKSPACE"
|
|
git -C "$GITHUB_WORKSPACE" config gc.auto 0
|
|
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
|
|
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
|
|
-c protocol.version=2 \
|
|
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
|
|
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
|
|
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Prepare trusted workflow audit configs
|
|
if: github.event_name == 'pull_request'
|
|
env:
|
|
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
trusted_config="$RUNNER_TEMP/pre-commit-base.yaml"
|
|
trusted_zizmor_config="$RUNNER_TEMP/zizmor-base.yml"
|
|
|
|
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
|
|
timeout --signal=TERM --kill-after=10s 30s git fetch --no-tags --depth=1 origin \
|
|
"+${BASE_SHA}:refs/remotes/origin/security-base" ||
|
|
timeout --signal=TERM --kill-after=10s 30s git fetch --no-tags --depth=1 origin \
|
|
"+refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
|
|
fi
|
|
|
|
if git cat-file -e "${BASE_SHA}:.pre-commit-config.yaml" 2>/dev/null; then
|
|
git show "${BASE_SHA}:.pre-commit-config.yaml" > "$trusted_config"
|
|
elif git show "refs/remotes/origin/${BASE_REF}:.pre-commit-config.yaml" \
|
|
> "$trusted_config" 2>/dev/null; then
|
|
echo "Base SHA ${BASE_SHA} does not expose .pre-commit-config.yaml; using origin/${BASE_REF} instead."
|
|
else
|
|
echo "::error title=trusted pre-commit config unavailable::Could not read .pre-commit-config.yaml from ${BASE_SHA} or origin/${BASE_REF}."
|
|
exit 1
|
|
fi
|
|
|
|
if git cat-file -e "${BASE_SHA}:.github/zizmor.yml" 2>/dev/null; then
|
|
git show "${BASE_SHA}:.github/zizmor.yml" > "$trusted_zizmor_config"
|
|
elif git show "refs/remotes/origin/${BASE_REF}:.github/zizmor.yml" \
|
|
> "$trusted_zizmor_config" 2>/dev/null; then
|
|
echo "Base SHA ${BASE_SHA} does not expose .github/zizmor.yml; using origin/${BASE_REF} instead."
|
|
else
|
|
echo "::error title=trusted zizmor config unavailable::Could not read .github/zizmor.yml from ${BASE_SHA} or origin/${BASE_REF}."
|
|
exit 1
|
|
fi
|
|
|
|
python3 - "$trusted_config" "$trusted_zizmor_config" <<'PY'
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
config_path = Path(sys.argv[1])
|
|
zizmor_config_path = sys.argv[2]
|
|
text = config_path.read_text()
|
|
if ".github/zizmor.yml" not in text:
|
|
raise SystemExit("trusted pre-commit config does not reference .github/zizmor.yml")
|
|
config_path.write_text(text.replace(".github/zizmor.yml", zizmor_config_path))
|
|
PY
|
|
|
|
echo "PRE_COMMIT_CONFIG_PATH=$trusted_config" >> "$GITHUB_ENV"
|
|
|
|
- name: Install pre-commit
|
|
run: python -m pip install --disable-pip-version-check pre-commit==4.2.0
|
|
|
|
- name: Install actionlint
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
ACTIONLINT_VERSION="1.7.11"
|
|
archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
|
|
base_url="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}"
|
|
# GitHub release downloads occasionally return transient 5xx responses.
|
|
# Retry all curl errors here so workflow-sanity does not fail closed on
|
|
# a one-off release edge outage.
|
|
curl --retry 5 --retry-delay 2 --retry-all-errors -sSfL -o "${archive}" "${base_url}/${archive}"
|
|
curl --retry 5 --retry-delay 2 --retry-all-errors -sSfL -o checksums.txt "${base_url}/actionlint_${ACTIONLINT_VERSION}_checksums.txt"
|
|
grep " ${archive}\$" checksums.txt | sha256sum -c -
|
|
tar -xzf "${archive}" actionlint
|
|
sudo install -m 0755 actionlint /usr/local/bin/actionlint
|
|
|
|
- name: Lint workflows
|
|
run: actionlint
|
|
|
|
- name: Audit all workflows with zizmor
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mapfile -t workflow_files < <(
|
|
find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) | sort
|
|
)
|
|
pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" zizmor --files "${workflow_files[@]}"
|
|
|
|
- name: Disallow direct inputs interpolation in composite run blocks
|
|
run: python3 scripts/check-composite-action-input-interpolation.py
|
|
|
|
- name: Disallow tracked merge conflict markers
|
|
run: node scripts/check-no-conflict-markers.mjs
|
|
|
|
generated-doc-baselines:
|
|
if: github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
CHECKOUT_REPO: ${{ github.repository }}
|
|
CHECKOUT_SHA: ${{ github.sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init "$GITHUB_WORKSPACE"
|
|
git -C "$GITHUB_WORKSPACE" config gc.auto 0
|
|
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
|
|
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
|
|
-c protocol.version=2 \
|
|
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
|
|
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
|
|
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
install-bun: "false"
|
|
|
|
- name: Check config docs drift statefile
|
|
run: pnpm config:docs:check
|
|
|
|
- name: Check plugin SDK API baseline drift
|
|
run: pnpm plugin-sdk:api:check
|