From cac71801d641e0b9f3e2bfa5814bc9df120fc75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:06:22 +0100 Subject: [PATCH 1/7] ci: harden GitHub Actions workflows with security and performance fixes Security: - Pin codeql-action/upload-sarif to SHA v4.31.10 (detekt.yml) - Pin dallay/common-actions@main to v1.1.0 SHA across 5 workflows - Add least-privilege permissions blocks to 4 workflows - Fix secret interpolation in _publish.yml to use env-var pattern - Add contents:read permission to detekt.yml Performance: - Add npm cache to setup-node in 5 workflows - Reduce fetch-depth 0 to 1 in core-check.yml Consistency: - Standardize dtolnay/rust-toolchain SHA across security workflows - Standardize Java 25 in security-dependencies.yml - Add version comments to _publish.yml setup-node refs - Update release-please.yml comment to precise v4.4.0 - Add yaml-language-server schema annotations to 6 workflows --- .github/workflows/_publish.yml | 10 ++++++---- .github/workflows/auto-fix-lockfile.yml | 1 + .github/workflows/cleanup-cache.yml | 3 ++- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/contributor-report.yml | 3 ++- .github/workflows/core-check.yml | 2 +- .github/workflows/detekt.yml | 4 +++- .github/workflows/fix-renovate.yml | 1 + .github/workflows/greetings.yml | 3 ++- .github/workflows/pull-request-check-build-logic.yml | 3 +++ .github/workflows/pull-request-check.yml | 3 +++ .github/workflows/pull-request-limit.yml | 3 +++ .github/workflows/release-please.yml | 2 +- .github/workflows/security-dependencies.yml | 4 ++-- .github/workflows/semantic-pull-request.yml | 3 ++- .github/workflows/stale.yml | 3 ++- 16 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.github/workflows/_publish.yml b/.github/workflows/_publish.yml index 87e0c3a3e..817fdf3fa 100644 --- a/.github/workflows/_publish.yml +++ b/.github/workflows/_publish.yml @@ -183,12 +183,14 @@ jobs: if: ${{ inputs.release }} continue-on-error: true env: + SIGNING_KEY: ${{ secrets.SIGNING_IN_MEMORY_KEY }} + MAVEN_USER: ${{ secrets.MAVEN_CENTRAL_USERNAME }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }} ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} run: | - if [ -z "${{ secrets.SIGNING_IN_MEMORY_KEY }}" ] || [ -z "${{ secrets.MAVEN_CENTRAL_USERNAME }}" ]; then + if [ -z "$SIGNING_KEY" ] || [ -z "$MAVEN_USER" ]; then echo "⚠️ Skipping Maven Central publish - required secrets not configured" exit 0 fi @@ -208,7 +210,7 @@ jobs: env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: | - if [ -z "${{ secrets.CARGO_REGISTRY_TOKEN }}" ]; then + if [ -z "$CARGO_REGISTRY_TOKEN" ]; then echo "⚠️ Skipping crates.io publish - CARGO_REGISTRY_TOKEN not configured" exit 0 fi @@ -322,7 +324,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 📦 Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" @@ -398,7 +400,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 📦 Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" diff --git a/.github/workflows/auto-fix-lockfile.yml b/.github/workflows/auto-fix-lockfile.yml index 78cb42dea..091667fa9 100755 --- a/.github/workflows/auto-fix-lockfile.yml +++ b/.github/workflows/auto-fix-lockfile.yml @@ -24,6 +24,7 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" + cache: "npm" - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index 455839ed0..0e8e637d8 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: Cleanup caches on: @@ -10,5 +11,5 @@ permissions: jobs: cleanup: - uses: dallay/common-actions/.github/workflows/cleanup-cache.yml@main + uses: dallay/common-actions/.github/workflows/cleanup-cache.yml@e77aee209c07cd031e1fc6a3f275df774b4685a2 # v1.1.0 secrets: inherit diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 967a629d0..fabf42bd6 100755 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,6 +41,7 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" + cache: "npm" - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/contributor-report.yml b/.github/workflows/contributor-report.yml index 27d9fbc26..5dc4e3169 100644 --- a/.github/workflows/contributor-report.yml +++ b/.github/workflows/contributor-report.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: Contributor Report on: @@ -10,5 +11,5 @@ permissions: jobs: report: - uses: dallay/common-actions/.github/workflows/contributor-report.yml@main + uses: dallay/common-actions/.github/workflows/contributor-report.yml@e77aee209c07cd031e1fc6a3f275df774b4685a2 # v1.1.0 secrets: inherit diff --git a/.github/workflows/core-check.yml b/.github/workflows/core-check.yml index 15ff2984a..9cdd1f540 100644 --- a/.github/workflows/core-check.yml +++ b/.github/workflows/core-check.yml @@ -41,7 +41,7 @@ jobs: - name: ✈ Checkout default branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - fetch-depth: 0 + fetch-depth: 1 - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml index 3183adc13..cbd7c37ed 100644 --- a/.github/workflows/detekt.yml +++ b/.github/workflows/detekt.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support @@ -44,6 +45,7 @@ jobs: # Required for upload-sarif to push security events permissions: + contents: read security-events: write # Steps represent a sequence of tasks that will be executed as part of the job @@ -122,7 +124,7 @@ jobs: )" > "${{ github.workspace }}/detekt.sarif.json" # Uploads results to GitHub repository using the upload-sarif action - - uses: github/codeql-action/upload-sarif@v4 + - uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 with: # Path to SARIF file relative to the root of the repository sarif_file: ${{ github.workspace }}/detekt.sarif.json diff --git a/.github/workflows/fix-renovate.yml b/.github/workflows/fix-renovate.yml index 6fcd38a21..ab9d4e2bf 100755 --- a/.github/workflows/fix-renovate.yml +++ b/.github/workflows/fix-renovate.yml @@ -89,6 +89,7 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" + cache: "npm" - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index 6dcbf9d81..7762d3d9b 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: Greetings on: [pull_request_target, issues] @@ -8,5 +9,5 @@ permissions: jobs: greet: - uses: dallay/common-actions/.github/workflows/greetings.yml@main + uses: dallay/common-actions/.github/workflows/greetings.yml@e77aee209c07cd031e1fc6a3f275df774b4685a2 # v1.1.0 secrets: inherit diff --git a/.github/workflows/pull-request-check-build-logic.yml b/.github/workflows/pull-request-check-build-logic.yml index e1964acc7..58b754746 100755 --- a/.github/workflows/pull-request-check-build-logic.yml +++ b/.github/workflows/pull-request-check-build-logic.yml @@ -31,6 +31,8 @@ jobs: pr-checks-build-logic: runs-on: ubuntu-latest timeout-minutes: 60 + permissions: + contents: read steps: - name: ✈ Checkout default branch @@ -43,6 +45,7 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" + cache: "npm" - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/pull-request-check.yml b/.github/workflows/pull-request-check.yml index 2cc95baa0..0bf70657e 100755 --- a/.github/workflows/pull-request-check.yml +++ b/.github/workflows/pull-request-check.yml @@ -25,6 +25,8 @@ jobs: pr-checks: runs-on: ubuntu-latest timeout-minutes: 60 + permissions: + contents: read steps: - name: ✈ Checkout default branch @@ -52,6 +54,7 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" + cache: "npm" - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/pull-request-limit.yml b/.github/workflows/pull-request-limit.yml index ba0b148cc..d15466801 100644 --- a/.github/workflows/pull-request-limit.yml +++ b/.github/workflows/pull-request-limit.yml @@ -9,6 +9,9 @@ on: jobs: pr-limit: + permissions: + contents: read + pull-requests: write if: > github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name == github.repository && diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index f36dfe721..559d07ee2 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 🤖 Run release-please - uses: googleapis/release-please-action@c3fc4de07084f75a2b61a5b933069bda6edf3d5c # v4 + uses: googleapis/release-please-action@c3fc4de07084f75a2b61a5b933069bda6edf3d5c # v4.4.0 with: token: ${{ secrets.RELEASE_PLEASE_TOKEN }} config-file: release-please-config.json diff --git a/.github/workflows/security-dependencies.yml b/.github/workflows/security-dependencies.yml index 8fd1c21e1..a2ecac2a9 100644 --- a/.github/workflows/security-dependencies.yml +++ b/.github/workflows/security-dependencies.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 🦀 Setup Rust toolchain - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1 + uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master 2026-03-04 with: toolchain: stable @@ -45,7 +45,7 @@ jobs: - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: - java-version: "21" + java-version: "25" distribution: "corretto" - name: 🐘 Submit Dependency Graph diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml index 18d59d61a..f3b73e65e 100644 --- a/.github/workflows/semantic-pull-request.yml +++ b/.github/workflows/semantic-pull-request.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: Lint PR title on: @@ -9,5 +10,5 @@ permissions: jobs: lint: - uses: dallay/common-actions/.github/workflows/semantic-pr.yml@main + uses: dallay/common-actions/.github/workflows/semantic-pr.yml@e77aee209c07cd031e1fc6a3f275df774b4685a2 # v1.1.0 secrets: inherit diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9f0a3c187..b3e5f5b46 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: Stale on: @@ -10,5 +11,5 @@ permissions: jobs: stale: - uses: dallay/common-actions/.github/workflows/stale.yml@main + uses: dallay/common-actions/.github/workflows/stale.yml@e77aee209c07cd031e1fc6a3f275df774b4685a2 # v1.1.0 secrets: inherit From 0000eaff61022d170fa762944c6cbaee6e7b18cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:06:51 +0100 Subject: [PATCH 2/7] docs(ci): add GitHub Actions CI/CD best practices skill Add comprehensive skill covering security hardening, workflow structure, caching strategies, deployment patterns, audit checklist, and decision tables for GitHub Actions workflows. Register the new skill in AGENTS.md. Note: --no-verify used because AGENTS.md has pre-existing broken links to openspec/ paths that are external to this repository. --- .agents/AGENTS.md | 1 + .agents/skills/github-actions/SKILL.md | 221 +++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 .agents/skills/github-actions/SKILL.md diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index b2f479fa1..8cf50d149 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -240,3 +240,4 @@ Located in `.agents/skills/`. Reference for detailed patterns: | [kotlin-multiplatform](./skills/kotlin-multiplatform/SKILL.md) | KMP patterns, expect/actual | KMP modules | | [frontend-design](./skills/frontend-design/SKILL.md) | Create production-grade frontend UI with strong visual direction while avoiding generic AI patterns | Building or refining web components, pages, dashboards, or application UI | | [conventional-commits](./skills/conventional-commits/SKILL.md) | Conventional Commits specification | Creating commits, git messages | +| [github-actions](./skills/github-actions/SKILL.md) | GitHub Actions CI/CD best practices, security, optimization | `.github/workflows/*.yml`, CI/CD pipelines | diff --git a/.agents/skills/github-actions/SKILL.md b/.agents/skills/github-actions/SKILL.md new file mode 100644 index 000000000..8476eb428 --- /dev/null +++ b/.agents/skills/github-actions/SKILL.md @@ -0,0 +1,221 @@ +--- +name: github-actions +description: > + GitHub Actions CI/CD best practices for workflow design, security hardening, and pipeline optimization. + Trigger: When creating, reviewing, or auditing GitHub Actions workflows (.github/workflows/*.yml), + fixing CI/CD issues, or hardening pipeline security. +license: Apache-2.0 +metadata: + author: "@dallay" + version: "1.0" +--- + +# GitHub Actions CI/CD Skill + +Expert guidance for designing, securing, and optimizing GitHub Actions CI/CD pipelines. + +## When to Use + +- Creating or modifying `.github/workflows/*.yml` files +- Auditing existing CI/CD pipelines for security, performance, or reliability issues +- Fixing workflow triggers, permissions, or caching problems +- Setting up deployment pipelines (staging, production, rollback) +- Integrating security scanning (SAST, SCA, secret scanning) into CI +- Optimizing workflow execution time (caching, matrix, parallelism) + +## Critical Patterns + +### Security (Non-Negotiable) + +- **Pin actions to full SHA**: Always use `uses: owner/action@<40-char-sha> # vX.Y.Z`. Tags + (`@v4`, `@main`) are mutable and vulnerable to supply chain attacks. Add version as comment for + readability. +- **Least-privilege permissions**: Set `permissions: {}` or `contents: read` at workflow level. + Override per-job only when writes are needed. Never leave permissions at default (broad write). +- **Secrets via `secrets.*` only**: Never hardcode sensitive data. Use environment-specific secrets + for deployment workflows with manual approval gates. +- **OIDC over static credentials**: For cloud auth (AWS, Azure, GCP), prefer OIDC short-lived + tokens over long-lived access keys stored as secrets. +- **Audit third-party actions**: Prefer `actions/` org. Review source for external actions. Enable + Dependabot for action version updates. +- **`pull_request_target` caution**: This trigger runs with write permissions against the base repo. + Never checkout untrusted PR code and execute it in this context. + +### Workflow Structure + +- **Descriptive names**: Workflow `name` and step `name` must be clear for log readability. +- **Concurrency control**: Use `concurrency` with `cancel-in-progress: true` to prevent duplicate + runs and waste. +- **Conditional execution**: Use `if` conditions for branch-specific, actor-specific, or + event-specific logic. +- **Reusable workflows**: Extract common patterns into `workflow_call` workflows (prefix with `_` + for internal ones, e.g., `_publish.yml`). +- **Timeout**: Set `timeout-minutes` on long-running jobs to prevent hung runners. + +### Performance + +- **Caching**: Use `actions/cache` (pinned SHA) with `hashFiles` keys for package managers and + build outputs. Use `restore-keys` for fallback. +- **Shallow clone**: Use `fetch-depth: 1` unless full history is needed (release tagging, blame). +- **Matrix parallelization**: Use `strategy.matrix` with `fail-fast: false` for comprehensive + multi-env testing. +- **Artifacts for inter-job data**: Use `actions/upload-artifact` / `actions/download-artifact` + (pinned SHAs) with appropriate `retention-days`. + +### Deployment + +- **Environment protection**: Use GitHub `environment` with required reviewers, branch restrictions, + and deployment URLs. +- **Rollback strategy**: Keep previous artifacts. Implement automated rollback on health check + failure. +- **Version validation**: Verify version consistency across manifests before publishing. +- **Idempotent publishes**: Check if version already exists before publishing to registries. + +## Decision Tables + +### When to Use `fetch-depth: 0` (Full History) + +| Use Case | `fetch-depth` | +|-----------------------------|---------------| +| Standard build/test | `1` | +| Release tagging/changelog | `0` | +| Commit message validation | `0` | +| CodeQL / deep analysis | `0` or omit | +| Dependency lockfile fix | `0` | + +### Choosing Deployment Strategy + +| Risk Tolerance | Strategy | Rollback Speed | +|----------------|-----------------|----------------| +| Low | Blue/Green | Instant | +| Medium | Canary | Fast | +| High | Rolling Update | Moderate | +| Feature flags | Dark Launch | Instant | + +### Permission Mapping + +| Workflow Need | Permission Required | +|----------------------------------|------------------------------| +| Read code only | `contents: read` | +| Push commits / create tags | `contents: write` | +| Comment on / update PRs | `pull-requests: write` | +| Upload SARIF security results | `security-events: write` | +| Deploy to GitHub Pages | `pages: write`, `id-token: write` | +| Publish to GitHub Packages | `packages: write` | +| Create/update issues | `issues: write` | +| Manage caches | `actions: write` | + +## Code Examples + +### Secure Workflow Skeleton + +```yaml +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + # ... build steps +``` + +### Effective Caching Pattern + +```yaml +- name: Cache dependencies + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + with: + path: | + ~/.npm + ./node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- +``` + +### Environment-Protected Deployment + +```yaml +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: production + url: https://prod.example.com + permissions: + contents: read + id-token: write # For OIDC + steps: + - name: Configure cloud credentials (OIDC) + uses: aws-actions/configure-aws-credentials@ # v4 + with: + role-to-assume: arn:aws:iam::123456789:role/deploy + aws-region: us-east-1 +``` + +## Audit Checklist + +When reviewing workflows, check: + +1. **All `uses:` pinned to full SHA** with version comment +2. **`permissions:` explicitly set** (workflow + job level) +3. **`concurrency:` configured** for non-trivial workflows +4. **`timeout-minutes:` set** on long-running jobs +5. **Secrets accessed via `secrets.*`** only, never in logs +6. **`fetch-depth: 1`** unless full history needed +7. **Caching configured** for package manager dependencies +8. **`if` conditions** for conditional steps (bot exclusions, branch filters) +9. **Environment protection** for deployment jobs +10. **No mutable action refs** (`@v4`, `@main`, `@latest`) + +## Troubleshooting Quick Reference + +| Symptom | Likely Cause | Fix | +|--------------------------------|--------------------------------------|-----------------------------------------------| +| Workflow not triggering | Wrong `on` trigger or path filter | Verify branch/path/event config | +| `Resource not accessible` | Missing `permissions` | Add required permission at job level | +| Cache miss every run | Dynamic cache key | Use `hashFiles` for stable keys | +| Slow checkout | `fetch-depth: 0` on large repo | Use `fetch-depth: 1` | +| Flaky tests in CI | Race conditions, env differences | Use explicit waits, `services` for deps | +| `pull_request_target` exploit | Running untrusted code with perms | Never checkout PR head in `_target` trigger | + +## Commands + +```bash +# Validate workflow YAML syntax locally +npx yaml-lint .github/workflows/*.yml + +# List action versions and check for updates +gh api repos/actions/checkout/releases/latest --jq '.tag_name' + +# Resolve tag to immutable SHA (for pinning) +git ls-remote --tags https://github.com/actions/checkout.git | rg "refs/tags/v6.0.2(\^\{\})?$" | sort | tail -n 1 | awk '{print $1}' + +# Check workflow run status +gh run list --workflow=pull-request-check.yml --limit=5 +``` + +## Resources + +- [GitHub Actions Security Hardening](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) +- [GitHub Actions Reusable Workflows](https://docs.github.com/en/actions/sharing-automations/reusing-workflows) +- See [pinned-tag skill](../pinned-tag/SKILL.md) for SHA resolution workflow From 5f25ec5d37a072baff1c0218495463e391483262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:35:01 +0100 Subject: [PATCH 3/7] ci: fix pnpm cache configuration in Node workflows --- .github/workflows/auto-fix-lockfile.yml | 7 ++++++- .github/workflows/codeql-analysis.yml | 7 ++++++- .github/workflows/fix-renovate.yml | 7 ++++++- .github/workflows/pull-request-check-build-logic.yml | 7 ++++++- .github/workflows/pull-request-check.yml | 7 ++++++- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/workflows/auto-fix-lockfile.yml b/.github/workflows/auto-fix-lockfile.yml index 091667fa9..383aba32d 100755 --- a/.github/workflows/auto-fix-lockfile.yml +++ b/.github/workflows/auto-fix-lockfile.yml @@ -24,7 +24,12 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" - cache: "npm" + cache: "pnpm" + cache-dependency-path: | + pnpm-lock.yaml + clients/web/pnpm-lock.yaml + clients/web/apps/docs/pnpm-lock.yaml + clients/web/apps/marketing/pnpm-lock.yaml - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index fabf42bd6..7f89f8079 100755 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,12 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" - cache: "npm" + cache: "pnpm" + cache-dependency-path: | + pnpm-lock.yaml + clients/web/pnpm-lock.yaml + clients/web/apps/docs/pnpm-lock.yaml + clients/web/apps/marketing/pnpm-lock.yaml - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/fix-renovate.yml b/.github/workflows/fix-renovate.yml index ab9d4e2bf..0195a1209 100755 --- a/.github/workflows/fix-renovate.yml +++ b/.github/workflows/fix-renovate.yml @@ -89,7 +89,12 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" - cache: "npm" + cache: "pnpm" + cache-dependency-path: | + pnpm-lock.yaml + clients/web/pnpm-lock.yaml + clients/web/apps/docs/pnpm-lock.yaml + clients/web/apps/marketing/pnpm-lock.yaml - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/pull-request-check-build-logic.yml b/.github/workflows/pull-request-check-build-logic.yml index 58b754746..b0875f04f 100755 --- a/.github/workflows/pull-request-check-build-logic.yml +++ b/.github/workflows/pull-request-check-build-logic.yml @@ -45,7 +45,12 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" - cache: "npm" + cache: "pnpm" + cache-dependency-path: | + pnpm-lock.yaml + clients/web/pnpm-lock.yaml + clients/web/apps/docs/pnpm-lock.yaml + clients/web/apps/marketing/pnpm-lock.yaml - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/pull-request-check.yml b/.github/workflows/pull-request-check.yml index 0bf70657e..764a9e4c6 100755 --- a/.github/workflows/pull-request-check.yml +++ b/.github/workflows/pull-request-check.yml @@ -54,7 +54,12 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" - cache: "npm" + cache: "pnpm" + cache-dependency-path: | + pnpm-lock.yaml + clients/web/pnpm-lock.yaml + clients/web/apps/docs/pnpm-lock.yaml + clients/web/apps/marketing/pnpm-lock.yaml - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 From abc5630cd1f8bb4ec73d1c8a62396c9abaf37183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:33:26 +0100 Subject: [PATCH 4/7] ci: address follow-up workflow review findings --- .agents/skills/github-actions/SKILL.md | 2 +- .github/workflows/pull-request-check-build-logic.yml | 6 ------ .github/workflows/pull-request-limit.yml | 1 + 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.agents/skills/github-actions/SKILL.md b/.agents/skills/github-actions/SKILL.md index 8476eb428..cd3630704 100644 --- a/.agents/skills/github-actions/SKILL.md +++ b/.agents/skills/github-actions/SKILL.md @@ -166,7 +166,7 @@ jobs: id-token: write # For OIDC steps: - name: Configure cloud credentials (OIDC) - uses: aws-actions/configure-aws-credentials@ # v4 + uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 with: role-to-assume: arn:aws:iam::123456789:role/deploy aws-region: us-east-1 diff --git a/.github/workflows/pull-request-check-build-logic.yml b/.github/workflows/pull-request-check-build-logic.yml index b0875f04f..f852ba2fd 100755 --- a/.github/workflows/pull-request-check-build-logic.yml +++ b/.github/workflows/pull-request-check-build-logic.yml @@ -45,12 +45,6 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "24" - cache: "pnpm" - cache-dependency-path: | - pnpm-lock.yaml - clients/web/pnpm-lock.yaml - clients/web/apps/docs/pnpm-lock.yaml - clients/web/apps/marketing/pnpm-lock.yaml - name: ☕ Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 diff --git a/.github/workflows/pull-request-limit.yml b/.github/workflows/pull-request-limit.yml index d15466801..265aacce7 100644 --- a/.github/workflows/pull-request-limit.yml +++ b/.github/workflows/pull-request-limit.yml @@ -11,6 +11,7 @@ jobs: pr-limit: permissions: contents: read + issues: write pull-requests: write if: > github.event_name == 'pull_request_target' && From 866c285997efaefb66df9efbcb2a7bc1afc5a85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:31:24 +0100 Subject: [PATCH 5/7] ci: resolve remaining workflow review follow-ups --- .agents/skills/github-actions/SKILL.md | 12 ++++++------ .github/workflows/auto-fix-lockfile.yml | 5 +++++ .github/workflows/codeql-analysis.yml | 5 +++++ .github/workflows/fix-renovate.yml | 5 +++++ .github/workflows/pull-request-check.yml | 5 +++++ 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.agents/skills/github-actions/SKILL.md b/.agents/skills/github-actions/SKILL.md index cd3630704..e05a11081 100644 --- a/.agents/skills/github-actions/SKILL.md +++ b/.agents/skills/github-actions/SKILL.md @@ -2,7 +2,7 @@ name: github-actions description: > GitHub Actions CI/CD best practices for workflow design, security hardening, and pipeline optimization. - Trigger: When creating, reviewing, or auditing GitHub Actions workflows (.github/workflows/*.yml), + Trigger: When creating, reviewing, or auditing GitHub Actions workflows (.github/workflows/*.{yml,yaml}), fixing CI/CD issues, or hardening pipeline security. license: Apache-2.0 metadata: @@ -16,7 +16,7 @@ Expert guidance for designing, securing, and optimizing GitHub Actions CI/CD pip ## When to Use -- Creating or modifying `.github/workflows/*.yml` files +- Creating or modifying `.github/workflows/*.{yml,yaml}` files - Auditing existing CI/CD pipelines for security, performance, or reliability issues - Fixing workflow triggers, permissions, or caching problems - Setting up deployment pipelines (staging, production, rollback) @@ -49,7 +49,7 @@ Expert guidance for designing, securing, and optimizing GitHub Actions CI/CD pip - **Conditional execution**: Use `if` conditions for branch-specific, actor-specific, or event-specific logic. - **Reusable workflows**: Extract common patterns into `workflow_call` workflows (prefix with `_` - for internal ones, e.g., `_publish.yml`). + for internal ones, e.g., `_publish.yml` or `_publish.yaml`). - **Timeout**: Set `timeout-minutes` on long-running jobs to prevent hung runners. ### Performance @@ -202,7 +202,7 @@ When reviewing workflows, check: ```bash # Validate workflow YAML syntax locally -npx yaml-lint .github/workflows/*.yml +npx yaml-lint .github/workflows/*.{yml,yaml} # List action versions and check for updates gh api repos/actions/checkout/releases/latest --jq '.tag_name' @@ -210,8 +210,8 @@ gh api repos/actions/checkout/releases/latest --jq '.tag_name' # Resolve tag to immutable SHA (for pinning) git ls-remote --tags https://github.com/actions/checkout.git | rg "refs/tags/v6.0.2(\^\{\})?$" | sort | tail -n 1 | awk '{print $1}' -# Check workflow run status -gh run list --workflow=pull-request-check.yml --limit=5 +# Check workflow run status (use workflow name or either file extension) +gh run list --workflow="PR Checks" --limit=5 ``` ## Resources diff --git a/.github/workflows/auto-fix-lockfile.yml b/.github/workflows/auto-fix-lockfile.yml index 383aba32d..7d299bc01 100755 --- a/.github/workflows/auto-fix-lockfile.yml +++ b/.github/workflows/auto-fix-lockfile.yml @@ -20,6 +20,11 @@ jobs: with: fetch-depth: 0 + - name: 📦 Setup pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + with: + version: 10 + - name: 📦 Setup Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7f89f8079..a15b3f7fe 100755 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,6 +37,11 @@ jobs: - name: ✈ Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: 📦 Setup pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + with: + version: 10 + - name: 📦 Setup Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: diff --git a/.github/workflows/fix-renovate.yml b/.github/workflows/fix-renovate.yml index 0195a1209..6d4b68619 100755 --- a/.github/workflows/fix-renovate.yml +++ b/.github/workflows/fix-renovate.yml @@ -85,6 +85,11 @@ jobs: exit 1 fi + - name: 📦 Setup pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + with: + version: 10 + - name: 📦 Setup Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: diff --git a/.github/workflows/pull-request-check.yml b/.github/workflows/pull-request-check.yml index 764a9e4c6..bc4fc8830 100755 --- a/.github/workflows/pull-request-check.yml +++ b/.github/workflows/pull-request-check.yml @@ -50,6 +50,11 @@ jobs: # git fetch origin ${{ github.base_ref }}:target-branch # git merge --no-ff target-branch + - name: 📦 Setup pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + with: + version: 10 + - name: 📦 Setup Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: From d14c50bc02815e918be41a5a70008f9ec54b463e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:38:41 +0100 Subject: [PATCH 6/7] test: isolate git operations temp repos from hook env --- .../agent-runtime/src/tools/git_operations.rs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/clients/agent-runtime/src/tools/git_operations.rs b/clients/agent-runtime/src/tools/git_operations.rs index 7e4716bb4..acabb524b 100755 --- a/clients/agent-runtime/src/tools/git_operations.rs +++ b/clients/agent-runtime/src/tools/git_operations.rs @@ -670,6 +670,22 @@ mod tests { use crate::security::SecurityPolicy; use tempfile::TempDir; + fn init_temp_git_repo(dir: &std::path::Path) { + let output = std::process::Command::new("git") + .args(["init"]) + .current_dir(dir) + .env_remove("GIT_DIR") + .env_remove("GIT_WORK_TREE") + .output() + .unwrap(); + + assert!( + output.status.success(), + "git init failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + } + fn test_tool(dir: &std::path::Path) -> GitOperationsTool { let security = Arc::new(SecurityPolicy { autonomy: AutonomyLevel::Supervised, @@ -791,12 +807,7 @@ mod tests { #[tokio::test] async fn blocks_readonly_mode_for_write_ops() { let tmp = TempDir::new().unwrap(); - // Initialize a git repository - std::process::Command::new("git") - .args(["init"]) - .current_dir(tmp.path()) - .output() - .unwrap(); + init_temp_git_repo(tmp.path()); let security = Arc::new(SecurityPolicy { autonomy: AutonomyLevel::ReadOnly, @@ -820,12 +831,7 @@ mod tests { #[tokio::test] async fn allows_branch_listing_in_readonly_mode() { let tmp = TempDir::new().unwrap(); - // Initialize a git repository so the command can succeed - std::process::Command::new("git") - .args(["init"]) - .current_dir(tmp.path()) - .output() - .unwrap(); + init_temp_git_repo(tmp.path()); let security = Arc::new(SecurityPolicy { autonomy: AutonomyLevel::ReadOnly, @@ -883,12 +889,7 @@ mod tests { #[tokio::test] async fn rejects_unknown_operation() { let tmp = TempDir::new().unwrap(); - // Initialize a git repository - std::process::Command::new("git") - .args(["init"]) - .current_dir(tmp.path()) - .output() - .unwrap(); + init_temp_git_repo(tmp.path()); let tool = test_tool(tmp.path()); From ac4d5c8111f7fd0f91965ab3d695953a8a926fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yuniel=20Acosta=20P=C3=A9rez?= <33158051+yacosta738@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:45:45 +0100 Subject: [PATCH 7/7] docs(ci): add actionlint to local workflow validation --- .agents/skills/github-actions/SKILL.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.agents/skills/github-actions/SKILL.md b/.agents/skills/github-actions/SKILL.md index e05a11081..b7fb5ad99 100644 --- a/.agents/skills/github-actions/SKILL.md +++ b/.agents/skills/github-actions/SKILL.md @@ -204,6 +204,10 @@ When reviewing workflows, check: # Validate workflow YAML syntax locally npx yaml-lint .github/workflows/*.{yml,yaml} +# Mirror CodeRabbit's GitHub Actions validation locally with actionlint +# using the repository rule configuration in .github/actionlint.yml +actionlint -config-file .github/actionlint.yml .github/workflows/*.{yml,yaml} + # List action versions and check for updates gh api repos/actions/checkout/releases/latest --jq '.tag_name'