Skip to content

feat: add reusable lint.yml workflow (SK-415)#6

Merged
RobEasthope merged 3 commits into
mainfrom
sk-415-reusable-lintyml-workflow
Jun 26, 2026
Merged

feat: add reusable lint.yml workflow (SK-415)#6
RobEasthope merged 3 commits into
mainfrom
sk-415-reusable-lintyml-workflow

Conversation

@RobEasthope

@RobEasthope RobEasthope commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

What

Authors the Layer-2 lint bundle: reusable-lint.yml (on: workflow_call) — one job that pays setup-project once, then composes the shared Layer-1 lint composite actions:

  • eslint
  • lint-markdown
  • lint-yaml (yamllint + actionlint)
  • changelog-validate

per ADR 0001 §5.7. Consumer repos drop their per-repo lint jobs and call this instead (SHA-pinned + Dependabot).

Why these decisions

  • Boolean opt-out per check (eslint: false, markdown: false, yaml: false, changelog: false) — §5.7's first opt-out rung: drop a sub-check without leaving the bundle (e.g. agent-skills runs eslint: false until SK-394). The remaining inputs are thin pass-throughs to each composite action, fed in a caller stub from the consumer's repo-config.yaml.
  • Prettier deferred — there is no prettier composite action to compose and it is not in §5.7's canonical bundle. Added later when an action exists.
  • Same-repo action references use full cross-repo paths, SHA-pinned, not ./.github/actions/…: inside a workflow_call job a ./ path resolves against the caller's workspace, not this repo (community #18601), and sha_pinning_required rejects an unpinned local ref. The full-path form is also what populates github.action_path so lint-yaml's centralised .yamllint.yml injection resolves (SK-438).
  • Bootstrap pin — the actions live on main at 132d746; there are no release tags yet, so each uses: pins to that commit. Documented in CLAUDE.md; the release process maintains it once tags land.
  • HUSKY: 0, persist-credentials: false, root permissions: {} + least-privilege job — mirrors the existing reusable workflows and ci.yml.

Verification

This repo deliberately does not dogfood the reusable workflow (same sha_pinning_required circularity that inlines ci.yml/claude.yml), so it is validated statically here; true end-to-end exercise is deferred to a consumer (npm-package-template, SK-413/424):

  • actionlint (schema + expressions)
  • yamllint (centralised config; only unavoidable line-length warnings on the SHA-pinned uses: lines, same as the existing Claude workflows)
  • prettier --check
  • markdownlint

Closes SK-415.

Summary by CodeRabbit

  • New Features
    • Added a reusable GitHub Actions “lint bundle” workflow that other repositories can call to run ESLint, markdown, YAML/action linting, and changelog validation with configurable options.
  • Documentation
    • Updated workflow composition guidance to reference the new reusable lint workflow and clarify cross-repo composite action referencing rules.
  • Bug Fixes
    • Bumped actionlint to v1.7.12 across CI, composite action defaults, and developer tooling (including pre-push and lint-staged fallbacks).

Author the Layer-2 lint bundle: reusable-lint.yml (`on: workflow_call`)
composes the shared Layer-1 lint composite actions (eslint, lint-markdown,
lint-yaml, changelog-validate) in one job that pays `setup-project` once,
per ADR 0001 §5.7. Each sub-check has a boolean opt-out input so a consumer
drops a check without leaving the bundle.

Prettier is deferred (no `prettier` composite action exists; not in §5.7's
canonical bundle).

The composite actions are referenced by full cross-repo path, SHA-pinned to
their commit on main — a `workflow_call` job's local `./` paths resolve
against the caller's workspace, and the full-path form is also what populates
`github.action_path` for lint-yaml's centralised `.yamllint.yml` injection.
@claude

claude Bot commented Jun 26, 2026

Copy link
Copy Markdown

Claude finished @RobEasthope's task in 1m 58s —— View job


Code Review

  • Read all changed files
  • Review reusable-lint.yml workflow
  • Review actionlint version bumps
  • Review CLAUDE.md changes
  • Compile and post findings

CI: passing ✅ — the 📝 YAML & Workflows and 📝 Markdown jobs are both green after the 1.7.5 → 1.7.12 bump.

This is a clean, well-structured PR. The main deliverable (reusable-lint.yml) follows established patterns throughout the repo. A few observations below, roughly in priority order.


Open finding: mutable tag in local install hints

The one outstanding CodeRabbit finding is technically valid but warrants nuance. package.json:48 and .husky/pre-push:61 both reference the mutable v1.7.12 tag in the developer install hint:

bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/v1.7.12/scripts/download-actionlint.bash)

CI and the composite action use the commit-pinned SHA (914e7df…). This is a genuine inconsistency with the estate's pinning philosophy.

The counterargument — which I find reasonable — is that these are human-facing hints printed when a developer doesn't have actionlint installed locally. The developer runs the command manually; it isn't automated. More importantly, download-actionlint.bash checksum-verifies the downloaded binary regardless of how the installer script itself was fetched, so a tampered tag would need to simultaneously poison both the installer script and produce a binary with the correct checksum to cause harm.

Given that, this is genuinely minor. Two sensible paths forward:

  1. Keep the tag, add a comment — add an inline comment to each hint noting the checksum verification mitigates the mutable-tag risk (makes the deliberate choice visible rather than looking like an oversight).
  2. Pin to SHA — replace v1.7.12 with 914e7df21a07ef503a81201c76d2b11c789d3fca in both hints for strict consistency, even at slight UX cost.

Either is fine. This shouldn't block the merge. Fix this →


Minor observations (non-blocking)

Bootstrap pin maintenance — all five action uses: lines are pinned to 132d746, documented as the pre-release internal pin. This is correct, but it creates a process dependency: once the first release tag lands, all five references need updating in lockstep. The comment in the workflow header documents this, which is good. Worth ensuring the release process checklist captures it explicitly.

changelog: true default — any consumer that adopts this workflow without a dated changelog will hit a pnpm changelog:validate failure on first run. The input description is clear about needing changelog: false for those repos, and the PR body calls it out. Flagging it here only because it's the adoption footgun most likely to bite the first consumer (SK-413/424).

Boolean→string bridge on actionlint input — the lint-yaml composite action checks if: inputs.actionlint == 'true' (string comparison). This workflow passes the boolean workflow_call input via ${{ inputs.actionlint }} in a with: block, which serialises to the string "true"/"false". This works correctly today; just worth keeping in mind if lint-yaml's implementation changes.


What's correct

  • Root permissions: {} + job-level contents: read is correct least-privilege posture ✅
  • persist-credentials: false on checkout ✅
  • HUSKY: 0 at job level to suppress the consumer's prepare hook ✅
  • Conditional setup-project — only when at least one pnpm-backed check is enabled; YAML-only consumers skip the full pnpm install ✅
  • All version defaults (yamllint: 1.37.1, actionlint: 1.7.12) mirror ci.yml lockstep ✅
  • timeout-minutes: 15
  • actionlint bump applied consistently across ci.yml, lint-yaml/action.yml (default, SHA, comment), and the installer hints ✅
  • CLAUDE.md cross-repo reference guidance is accurate and fills a genuine documentation gap ✅

The PR looks ready to merge once the CodeRabbit finding is acknowledged (either way).

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6bbc9c1c-81bc-4137-81db-a39711b9c592

📥 Commits

Reviewing files that changed from the base of the PR and between 2da270b and 0b2b2a6.

📒 Files selected for processing (1)
  • .github/workflows/reusable-lint.yml

📝 Walkthrough

Walkthrough

Adds a reusable lint workflow with configurable ESLint, markdown, YAML, and changelog checks, updates repository guidance for Layer-2 workflow composition, and bumps actionlint pins across workflow and local hook entry points.

Changes

Reusable lint workflow

Layer / File(s) Summary
Workflow contract and permissions
.github/workflows/reusable-lint.yml, CLAUDE.md
Defines the reusable workflow metadata, workflow_call inputs, workflow-root permissions, and adds the workflow name to the repository layout guide.
Lint job wiring
.github/workflows/reusable-lint.yml, CLAUDE.md
Implements the lint job, checks out the caller repository, conditionally installs dependencies, runs the configured lint actions, and adds the Layer-2 cross-repo uses: guidance.

actionlint version pins

Layer / File(s) Summary
actionlint version pins
.github/actions/lint-yaml/action.yml, .github/workflows/ci.yml, .husky/pre-push, package.json
Updates the actionlint version and installer checksum pins in the composite action, CI workflow, pre-push hook, and lint-staged fallback command.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarises the main change: adding a reusable lint workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sk-415-reusable-lintyml-workflow
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch sk-415-reusable-lintyml-workflow

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/reusable-lint.yml:
- Around line 139-166: Update the reusable lint workflow to use bracket notation
for every hyphenated input reference in the job steps, since expressions like
inputs.foo-bar are invalid. In the YAML step definitions for node-version-file,
eslint-args, markdown-globs, yaml-paths, yamllint-version, actionlint-version,
and changelog-script, switch to the corresponding inputs['...'] form so the
workflow parses and validates correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9f88dc4e-3ecc-447b-a09f-356097d38b79

📥 Commits

Reviewing files that changed from the base of the PR and between 132d746 and 36338c2.

📒 Files selected for processing (2)
  • .github/workflows/reusable-lint.yml
  • CLAUDE.md

Comment thread .github/workflows/reusable-lint.yml
actionlint 1.7.5 has a false positive — it reports `context "inputs" is
not allowed here` for expressions under a step's `with: args:` (and
`entrypoint:`), the Docker-action reserved inputs. Upstream fixed it in
v1.7.6. The new reusable-lint.yml forwards the `eslint` composite action's
`args` input dynamically, so it tripped the bug in CI.

Bump deliberately in lockstep (SK-422) across every pin: ci.yml, the
lint-yaml composite action default + installer SHA, reusable-lint.yml's
default, and the lint-staged / pre-push install hints. Re-pinned the
installer to the v1.7.12 commit (914e7df). Aligns CI with the version
local dev already runs (Homebrew 1.7.12).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 48: The local actionlint install hints in package.json and the pre-push
hook still point to the mutable v1.7.12 tag, which should be aligned with the
commit-pinned URL used elsewhere. Update the actionlint fallback message in
package.json and the matching hint in pre-push to reference the same pinned
download-actionlint.bash URL as .github/workflows/ci.yml and
.github/actions/lint-yaml/action.yml so all guidance stays consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a938159c-f017-481c-a4eb-ed89ae95ffb7

📥 Commits

Reviewing files that changed from the base of the PR and between 36338c2 and 2da270b.

📒 Files selected for processing (5)
  • .github/actions/lint-yaml/action.yml
  • .github/workflows/ci.yml
  • .github/workflows/reusable-lint.yml
  • .husky/pre-push
  • package.json

Comment thread package.json
Clarify the `changelog` input contract (flagged in review): a consumer
without a dated changelog must set `changelog: false`, otherwise the
default `pnpm changelog:validate` step fails on first adoption.
@RobEasthope
RobEasthope merged commit b67e60c into main Jun 26, 2026
13 checks passed
@RobEasthope
RobEasthope deleted the sk-415-reusable-lintyml-workflow branch June 26, 2026 15:19
RobEasthope added a commit that referenced this pull request Jun 30, 2026
## What

Reconstructs `shared-workflows`' **entire release history** and
graduates the current code to a stable **`v1.0.0`** (A-585). The repo
publishes no npm package, so a "release" here is a **git tag (`vX.Y.Z`)
+ GitHub release** over the reusable workflows and composite actions.

## Versioning methodology

The merged PR **titles are not reliable** — Conventional-Commit
discipline was loose when the repo was stood up (the GO/NO GO gate
shipped as `ci:`, the versioned ruleset as `chore:`). So every PR was
re-classified by **what it actually changed to the product surface**
(the `reusable-*.yml` workflows + `.github/actions/*` consumers use, and
their reference docs), read from the diff. Pre-1.0 rules: `feature` →
minor, `fix` → patch, breaking → minor (stay in `0.x`), and `1.0.0` is a
deliberate "graduate to stable" milestone.

| Version | PR | True category | Headline |
|---|---|---|---|
| 0.1.0 | #1 | feature | bootstrap: 3 reusable workflows |
| 0.2.0 | #5 | feature | Layer-1 composite-action layer |
| 0.3.0 | #6 | feature | reusable-lint bundle |
| 0.4.0 | #7 | feature | reusable build-test bundle + `build` action |
| 0.4.1 | #8 | fix | repoint build-test pins so the build lane resolves
|
| 0.5.0 | #9 | feature | reusable pkg-release workflow |
| 0.6.0 | #15 | refactor **(breaking)** | rename release → pkg-release |
| 0.7.0 | #16 | **feature** *(titled `ci:`)* | GO/NO GO aggregator gate
+ docs |
| 0.8.0 | #17 | **feature** *(titled `chore:`)* | versioned canonical
estate ruleset |
| 1.0.0 | HEAD | milestone | graduate to stable public surface |

Non-release PRs (self-host CI, ADR, dep bump, SK→A rename, skill
adoptions/re-syncs, check-run rename) are folded into the body of the
release window they merged in — the way a release-please changelog
aggregates.

## Changes

- **`changelog/`** — hand-authored dated entries (one per release) + the
schema doc (`changelog/README.md`), adapted from the estate's
`eslint-config`/`agent-skills` convention.
- **`package.json`** — `0.0.0` → `1.0.0` (stays `private`; the version
is the release anchor, not an npm publish).
- **`reusable-lint.yml`** — pin the five sibling Layer-1 actions to the
**`v0.8.0`** tag (`@7f543be`) instead of the bare pre-release commit,
so Dependabot can track them (`v0.8.0` is the latest backfilled tag that
carries every action and predates this commit, avoiding a self-reference
to `v1.0.0`).
- **`CLAUDE.md` / `README`** — replace the "no release tag exists yet"
wording.

## After merge

The git tags + GitHub releases are cut once this lands:
`v0.1.0`–`v0.8.0` at their historical merge commits, `v1.0.0` at this
PR's squash-merge commit, with notes sourced from each `changelog/`
entry. This unblocks consumers pinning a tag (+ Dependabot) instead of a
moving SHA (A-420 / A-446).

## Deferred (follow-up)

- **release-please automation** + a tag-only release workflow — the
forward release process is a separate decision now the backfill exists.

## Note for review

> Backfilling git tags at historical commits is a deliberate divergence
from the estate precedent (`eslint-config`/`agent-skills` backfilled
changelog *entries* but tagged only going forward) — chosen here because
A-585 explicitly wants the *full tag history* reconstructed.

The pre-existing Prettier drift on the vendored `.claude`/`.agents`
skill bundles is untouched (out of scope).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Marked the reusable workflows and composite actions as stable at
**v1.0.0**.
  * Updated workflow references to use the newer version tags.

* **Documentation**
  * Clarified release and versioning guidance across the repository.
* Added and expanded changelog documentation and release notes
structure.

* **Chores**
  * Updated internal workflow pins to the latest approved release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant