Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/actionlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: rhysd/actionlint@v1.7.11
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin the remaining workflow actions

If this repo is running with the sha_pinning_required: true policy that this change is meant to satisfy, pinning only checkout still leaves the workflow blocked at startup: GitHub documents that with this setting “all actions must be pinned to a full-length commit SHA” (https://docs.github.com/en/enterprise-cloud@latest/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository), but the next step here still uses rhysd/actionlint@v1.7.11 by tag. The same partial pinning pattern appears in the other edited workflows, so these runs can continue to fail until all uses: actions are SHA-pinned.

Useful? React with 👍 / 👎.

- uses: rhysd/actionlint@393031adb9afb225ee52ae2ccd7a5af5525e03e8 # v1.7.11
6 changes: 3 additions & 3 deletions .github/workflows/ci-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

# Copy lockfile + package.json into Docker build context
- run: cp package.json .github/docker/

- uses: docker/login-action@v3
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/build-push-action@v6
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .github/docker
file: .github/docker/Dockerfile.ci
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/cve-lite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Node
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'

Expand All @@ -48,14 +48,14 @@ jobs:

- name: Upload SARIF to Code Scanning
if: always() && hashFiles('*.sarif') != ''
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@411bbbe57033eedfc1a82d68c01345aa96c737d7 # v4
with:
sarif_file: cve-lite-*.sarif
category: cve-lite

- name: Upload HTML report artifact
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: cve-lite-report
path: cve-report/
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/evals-periodic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ jobs:
outputs:
image-tag: ${{ steps.meta.outputs.tag }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- id: meta
run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json') }}" >> "$GITHUB_OUTPUT"

- uses: docker/login-action@v3
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand All @@ -46,7 +46,7 @@ jobs:
run: cp package.json .github/docker/

- if: steps.check.outputs.exists == 'false'
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .github/docker
file: .github/docker/Dockerfile.ci
Expand Down Expand Up @@ -88,7 +88,7 @@ jobs:
- name: e2e-gemini
file: test/gemini-e2e.test.ts
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

Expand Down Expand Up @@ -122,7 +122,7 @@ jobs:

- name: Upload eval results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: eval-periodic-${{ matrix.suite.name }}
path: ~/.gstack-dev/evals/*.json
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/evals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ jobs:
outputs:
image-tag: ${{ steps.meta.outputs.tag }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- id: meta
run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json') }}" >> "$GITHUB_OUTPUT"

- uses: docker/login-action@v3
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand All @@ -46,7 +46,7 @@ jobs:
run: cp package.json .github/docker/

- if: steps.check.outputs.exists == 'false'
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .github/docker
file: .github/docker/Dockerfile.ci
Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
- name: e2e-gemini
file: test/gemini-e2e.test.ts
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:

- name: Upload eval results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: eval-${{ matrix.suite.name }}
path: ~/.gstack-dev/evals/*.json
Expand All @@ -155,12 +155,12 @@ jobs:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1

- name: Download all eval artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: eval-*
path: /tmp/eval-results
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/renovate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Self-hosted Renovate
uses: renovatebot/github-action@v41
uses: renovatebot/github-action@7e1c0fa7cfd2c3e91b27cdd87ae09a6a0fafb5f2 # v41.0.0
with:
configurationFile: .github/renovate.json5
token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/skill-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ jobs:
check-freshness:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
- run: bun install
- name: Check Claude host freshness
run: bun run gen:skill-docs
Expand Down
133 changes: 133 additions & 0 deletions .github/workflows/skillspector.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: SkillSpector

on:
pull_request:
paths:
- '**/SKILL.md'
- '.agents/**'
- '.factory/**'
- 'AGENTS.md'
- '**/agents/*.yaml'
- '**/agents/*.yml'
- '**/.codex-plugin/plugin.json'
push:
branches: [main, master]
paths:
- '**/SKILL.md'
- '.agents/**'
- '.factory/**'
- 'AGENTS.md'
- '**/agents/*.yaml'
- '**/agents/*.yml'
- '**/.codex-plugin/plugin.json'

permissions:
contents: read

jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'

- name: Set up uv
uses: astral-sh/setup-uv@d0d8abe699bfb85fec6de9f7adb5ae17292296ff # v6

- name: Install SkillSpector
run: |
git clone --depth 1 https://github.com/NVIDIA/SkillSpector.git /tmp/SkillSpector
uv venv .skillspector-venv --python 3.12
uv pip install --python .skillspector-venv/bin/python /tmp/SkillSpector

- name: Scan changed skills
env:
EVENT_NAME: ${{ github.event_name }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
BEFORE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail

if [ "$EVENT_NAME" = "pull_request" ]; then
BASE_SHA="$PR_BASE_SHA"
else
BASE_SHA="$BEFORE_SHA"
fi

if [ -z "${BASE_SHA:-}" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
BASE_SHA="$(git rev-list --max-parents=0 HEAD | tail -n 1)"
fi

git diff --name-only --diff-filter=ACMR "$BASE_SHA" "$GITHUB_SHA" > /tmp/skillspector-changed.txt

python3 - <<'PY'
from pathlib import Path

repo = Path.cwd()
changed = (Path("/tmp/skillspector-changed.txt").read_text().splitlines())
roots = []
seen = set()

for rel in changed:
rel = rel.strip()
if not rel:
continue
current = (repo / rel)
current = current if current.is_dir() else current.parent

while current != repo.parent and current != current.parent:
if (current / "SKILL.md").is_file():
root = str(current.relative_to(repo))
if root not in seen:
seen.add(root)
roots.append(root)
break
if current == repo:
break
current = current.parent

Path("/tmp/skillspector-targets.txt").write_text("\n".join(roots) + ("\n" if roots else ""))
print(f"skill_dirs={len(roots)}")
for root in roots:
print(root)
PY

if [ ! -s /tmp/skillspector-targets.txt ]; then
echo "No changed skill roots detected; skipping scan."
exit 0
fi

while IFS= read -r dir; do
[ -n "$dir" ] || continue
echo "Scanning $dir"
./.skillspector-venv/bin/skillspector scan "$dir" --no-llm --format json --output /tmp/skillspector-report.json
python3 - <<'PY'
import json
import sys

with open("/tmp/skillspector-report.json", "r", encoding="utf-8") as fh:
data = json.load(fh)

findings = data.get("findings", [])
severities = {str(f.get("severity", "")).upper() for f in findings}
score = int(data.get("risk_score", 0))
blocked = score >= 70 or "CRITICAL" in severities or "HIGH" in severities

print(f"risk_score={score}")
print(f"severities={sorted(s for s in severities if s)}")
print(f"findings={len(findings)}")

if blocked:
print("SkillSpector gate failed")
sys.exit(1)
PY
done < /tmp/skillspector-targets.txt
Loading