diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6c5049e..2d559bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,52 @@ +# Managed by netresearch/.github/templates/skill/ +# +# Declares only the ecosystems every skill consumer is guaranteed to have: +# github-actions (CI workflows) and composer (every skill repo ships a +# composer.json for split-licensing / Packagist distribution). +# +# npm and devcontainers are OPT-IN: a repo that actually has package.json +# fixtures (e.g. skills//references/examples/**) or a devcontainer adds +# the block below to its own dependabot.yml and lists `.github/dependabot.yml` +# under `intentional-drift:` in .github/template.yaml so the template sync +# stops managing the file. Declaring an ecosystem without its manifest makes +# the Dependabot run fail with `dependency_file_not_found`. +# +# Opt-in npm (use Dependabot's plural `directories:` to consolidate multiple +# fixture dirs into one grouped PR): +# - package-ecosystem: npm +# directories: +# - /skills//references/examples/ +# schedule: +# interval: weekly +# day: monday +# open-pull-requests-limit: 5 +# groups: +# npm: +# patterns: ['*'] +# cooldown: +# default-days: 7 version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: weekly + day: monday + open-pull-requests-limit: 5 groups: github-actions: - patterns: - - "*" + patterns: ['*'] + cooldown: + default-days: 7 + + - package-ecosystem: composer + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + groups: + composer: + patterns: ['*'] + cooldown: + default-days: 7 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..12d2e0b --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,23 @@ +documentation: + - changed-files: + - any-glob-to-any-file: ['**/*.md', 'docs/**/*', '**/SKILL.md', 'README.md'] +ci: + - changed-files: + - any-glob-to-any-file: ['.github/**/*', 'Makefile'] +dependencies: + - changed-files: + - any-glob-to-any-file: + - 'composer.json' + - 'composer.lock' + - 'package.json' + - 'package-lock.json' + - 'bun.lock' + - 'bun.lockb' + - 'yarn.lock' + - '.devcontainer/**/*' +skill: + - changed-files: + - any-glob-to-any-file: ['skills/**/*', '.claude-plugin/**/*', 'plugin.json'] +evals: + - changed-files: + - any-glob-to-any-file: ['**/evals/**/*', '**/eval/**/*', '**/*.eval.yaml', '**/*.eval.yml'] diff --git a/.github/template.yaml b/.github/template.yaml new file mode 100644 index 0000000..53bcc09 --- /dev/null +++ b/.github/template.yaml @@ -0,0 +1,20 @@ +# Managed by netresearch/.github/templates/skill/ +# Drift from the template is blocking CI in this repo (check-template-drift.yml). +# Record explicit exceptions under intentional-drift[] to unblock. +# +# CodeQL: skill repos use GitHub **CodeQL default setup** (a repo setting, +# enabled via Settings > Code security, or `gh api -X PUT +# repos///code-scanning/default-setup -f state=configured`). +# Default setup auto-detects the repo's interpreted languages and conflicts +# with an advanced codeql.yml workflow — so this template intentionally ships +# NO codeql.yml. Scorecard (scorecard.yml) is separate and IS templated. +# +# Common opt-ins for skill repos (list the file here so the template sync +# stops managing it, then customize it in-repo): +# - .github/workflows/validate-agents.yml (only for skills that ship an +# AGENTS.md generator under skills//scripts; the reusable's +# scripts-path input is repo-specific so this is never byte-identical) +# - .github/dependabot.yml (when the repo has npm fixtures/devcontainers +# beyond the guaranteed github-actions + composer ecosystems) +template: skill +intentional-drift: [] diff --git a/.github/workflows/auto-merge-deps.yml b/.github/workflows/auto-merge-deps.yml index d8de1b3..6b6c8ad 100644 --- a/.github/workflows/auto-merge-deps.yml +++ b/.github/workflows/auto-merge-deps.yml @@ -1,7 +1,10 @@ name: Auto-merge dependency PRs on: - pull_request_target: + # auto-merge only calls `gh pr merge` via the reusable with the base-repo + # token; it never checks out or runs PR head code, so pull_request_target + # (required for a write token on Dependabot/Renovate fork PRs) is safe here. + pull_request_target: # zizmor: ignore[dangerous-triggers] permissions: {} diff --git a/.github/workflows/check-template-drift.yml b/.github/workflows/check-template-drift.yml new file mode 100644 index 0000000..d7617f8 --- /dev/null +++ b/.github/workflows/check-template-drift.yml @@ -0,0 +1,18 @@ +name: Template Drift + +on: + pull_request: + branches: [main] + push: + branches: [main] + merge_group: + +permissions: {} + +jobs: + drift: + uses: netresearch/.github/.github/workflows/check-template-drift.yml@main + with: + template: skill + permissions: + contents: read diff --git a/.github/workflows/eval-validate.yml b/.github/workflows/eval-validate.yml index 6f02cd1..54b335c 100644 --- a/.github/workflows/eval-validate.yml +++ b/.github/workflows/eval-validate.yml @@ -1,13 +1,18 @@ name: Eval Validation +# Runs the skill's eval suite via the skill-repo-skill reusable. The reusable +# declares `permissions: contents: read` at top level (read-only validator); +# the calling job mirrors it explicitly. + on: push: branches: [main] pull_request: -permissions: - contents: read +permissions: {} jobs: eval-validate: uses: netresearch/skill-repo-skill/.github/workflows/eval-validate.yml@main + permissions: + contents: read diff --git a/.github/workflows/harness-verify.yml b/.github/workflows/harness-verify.yml index 17668cd..773a929 100644 --- a/.github/workflows/harness-verify.yml +++ b/.github/workflows/harness-verify.yml @@ -1,12 +1,17 @@ name: Harness Verification +# Verifies agent-harness consistency (AGENTS.md index, docs drift) via the +# skill-repo-skill reusable. The reusable declares `permissions: contents: +# read` at top level (read-only validator); the calling job mirrors it. + on: pull_request: branches: [main] -permissions: - contents: read +permissions: {} jobs: harness-verify: uses: netresearch/skill-repo-skill/.github/workflows/harness-verify.yml@main + permissions: + contents: read diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..f8a5853 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,17 @@ +name: Labeler + +on: + # labeler only applies labels via the reusable using the base-repo token; it + # never checks out or runs PR head code. pull_request_target is required to + # label fork PRs. + pull_request_target: # zizmor: ignore[dangerous-triggers] + types: [opened, synchronize, reopened] + +permissions: {} + +jobs: + labeler: + uses: netresearch/.github/.github/workflows/labeler.yml@main + permissions: + contents: read + pull-requests: write diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fbb14df..976e342 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,14 +1,20 @@ name: Lint +# Skill validation (SKILL.md word cap, plugin.json schema, markdown lint, …) +# via the skill-repo-skill reusable. The reusable declares `permissions: +# contents: read` at top level; the calling job mirrors it explicitly so the +# grant is visible at the call site and independent of the repo default. + on: push: branches: [main] pull_request: -permissions: - contents: read +permissions: {} jobs: validate: name: Skill Validation uses: netresearch/skill-repo-skill/.github/workflows/validate.yml@main + permissions: + contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d7c4cf..11a604a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,14 +1,22 @@ name: Release +# Skill release pipeline (composer/npm package publish + Sigstore cosign + +# SHA256SUMS attestation) via the skill-repo-skill reusable. The reusable's +# release job declares contents/id-token/attestations: write; the calling job +# grants exactly that union. id-token + attestations are required for the +# OIDC-backed Sigstore signing and the GitHub native attestation API. + on: push: tags: - 'v*' +permissions: {} + jobs: release: uses: netresearch/skill-repo-skill/.github/workflows/release.yml@main permissions: contents: write # release upload - id-token: write # OIDC for sigstore (required by the attest job) - attestations: write # GitHub native attestation API (required by the attest job) + id-token: write # OIDC for sigstore (cosign + attest) + attestations: write # GitHub native attestation API diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..d43143e --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,23 @@ +name: OpenSSF Scorecard + +# Closes the skill-repo security gap: supply-chain posture scoring (branch +# protection, pinned actions, token permissions, …). Runs on default-branch +# push and on a weekly schedule; results upload to the code-scanning dashboard. + +on: + push: + branches: [main] + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: + +permissions: {} + +jobs: + scorecard: + uses: netresearch/.github/.github/workflows/scorecard.yml@main + permissions: + contents: read + security-events: write + id-token: write + actions: read diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index fcf498e..c3e0b7e 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -1,9 +1,15 @@ name: Security -# Aggregated security scans for skill repos: secret scanning (betterleaks), -# workflow static analysis (zizmor), dependency review on PRs, and a composer -# audit (every skill repo ships a composer.json for split-licensing / -# Packagist distribution). +# Aggregated security scans for skill repos: +# gitleaks — secret scanning. The job and the reusable keep the +# historical `gitleaks` name; the scan itself runs +# betterleaks, which is OSS and needs no license. +# zizmor — static analysis of this repo's own workflows. +# dependency-review — on pull requests only. +# composer-audit — `composer audit` AND an Opengrep SAST scan; the called +# reusable runs both unless skip-opengrep is set. Every +# skill repo ships a composer.json for split-licensing / +# Packagist distribution. # # Top-level `permissions: {}` denies everything by default; each reusable # caller job re-declares the exact union its reusable's jobs require, so the diff --git a/.github/zizmor.yml b/.github/zizmor.yml index b9d8464..5ba7884 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -6,8 +6,9 @@ rules: unpinned-uses: config: policies: - # First-party reusable workflows track @main by policy and are - # never SHA-pinned, so fixes propagate to all consumers. + # First-party `uses:` — reusable workflows AND composite actions — + # track @main by policy and are never SHA-pinned, so a fix propagates + # to every consumer without a bump in dozens of repos. "netresearch/*": ref-pin # Everything else must be pinned to a full commit SHA. "*": hash-pin