Skip to content

feat(workflows): host the reusable GitHub workflows in this repo - #120

Draft
metalwarrior665 wants to merge 8 commits into
masterfrom
claude/repo-merge-actions-package-ep3lzw
Draft

feat(workflows): host the reusable GitHub workflows in this repo#120
metalwarrior665 wants to merge 8 commits into
masterfrom
claude/repo-merge-actions-package-ep3lzw

Conversation

@metalwarrior665

Copy link
Copy Markdown
Member

This PR introduces a complete set of reusable GitHub workflows and supporting infrastructure that consumer repositories can use for testing, building, and releasing their Actors.

Summary

The package now includes production-ready reusable workflows alongside the CLI, enabling consumer repos to standardize their CI/CD pipelines. These workflows handle platform testing, PR validation, releases, and Claude-powered automated investigation and fixes for failing tests.

Key Changes

  • Reusable Workflows (.github/workflows/):

    • pr-build-test.yaml: Runs unit tests and platform tests on pull requests
    • platform-tests.yaml: Scheduled platform test runs with Slack reporting
    • push-build-latest.yaml: Builds and releases Actors on master push
    • claude.yaml: Responds to @claude mentions to implement fixes via Claude Code
    • platform-tests-claude-investigate-and-fix.yaml: Two-phase workflow that uses Claude to investigate failing tests and automatically open issues and PRs
    • _move_major_tag.yaml: Manages the floating @v1 tag, only moving it when the required package version is published
    • manual_move_major_tag.yaml: Escape hatch for manual tag management
  • GitHub Actions (.github/actions/):

    • checkout-restore-dependencies/action.yaml: Composite action for checkout, Node setup, and dependency caching with npm token isolation
  • Helper Scripts (.github/scripts/):

    • run-with-apify-tokens.mjs: Securely passes only required Actor tokens to CLI commands, preventing secret leakage to node_modules
  • Configuration:

    • .github/workflows-min-package-version: Declares the minimum CLI version required by the workflows, gating tag movement until that version is published
  • Documentation:

    • Updated README.md with workflow usage examples, secret handling details, and versioning guidance
    • Updated CONTRIBUTING.md to document the workflows as a third component of the package

Notable Implementation Details

  • Secret Isolation: Secrets are passed only to the steps that need them, never as job-wide environment variables. The run-with-apify-tokens.mjs script reads apify-test-tools.config.json to determine which Actor tokens to pass, preventing accidental exposure.

  • Versioning Strategy: Consumer repos pin workflows to @v1 (a floating major tag), not @master. The tag only moves when the declared minimum package version is published on npm, preventing workflows from calling unreleased CLI features.

  • Claude Integration: The platform-tests-claude-investigate-and-fix.yaml workflow chains investigation and fix phases as job dependencies rather than label triggers, working around GitHub's limitation that scheduled runs cannot trigger other workflows.

  • Backward Compatibility: The platform-tests.yaml workflow supports both the deprecated subtest input and the new test-files-glob input for gradual migration.

https://claude.ai/code/session_01SkUZADZ6gzWW4GE6CFMreM

Moves the contents of apify-store/github-actions-source here, so a change that
spans a CLI feature and the workflow calling it is one PR against one branch
instead of two repos with a manual ref dance between them.

Consumers pin the `@v1` major tag rather than `@master`, and the setup action
installs `apify-test-tools@>=<floor>` (from .github/workflows-min-package-version)
instead of `@latest`. That resolves to the same newest stable in the normal case,
but makes a workflow's package requirement explicit.

The release cadences stay independent, because most changes only touch one side:

- workflow-only change: merge, `v1` moves, live, no npm release
- package-only change: merge and release when ready, workflows untouched
- workflow calling a new CLI feature: raise the floor in the same PR. On merge the
  `v1` move is held with a warning until that version is on npm, so consumers keep
  running the previous workflows instead of calling a CLI that doesn't exist yet.
  A stable release moves the tag; `manual_move_major_tag` is the escape hatch.

Also:
- run-with-apify-tokens.mjs moves to .github/scripts/, next to the other CI helper
  and out of eslint's path; the action's `scripts-path` output follows it
- pass github.head_ref/base_ref through the environment in pr-build-test, since
  branch names are attacker-controlled on fork PRs and actionlint gates master here
- prettier formatting on the imported files, which husky enforces here

Refs in the consumer snippets in the README were still `@new_master` and are
updated along with the repo path.
@metalwarrior665 metalwarrior665 changed the title Add reusable GitHub workflows and Claude integration feat(workflows): host the reusable GitHub workflows in this repo Aug 20, 2026
actionlint gates master here and the action wrapper runs shellcheck, which the
old repo's CI did not, so this SC2086 came in with the import. Unquoted it would
word-split if the runner ever pointed $GITHUB_OUTPUT at a path with spaces.
claude and others added 6 commits September 10, 2026 13:15
Catches the merged copy up with the four commits that landed on
github-actions-source master after the import (#57, #59, #60, #63), all of which
built the claude-review action. Two new files, nothing else changed upstream.

`review.yaml` fetches its instructions over HTTP rather than from a checkout,
because a reusable workflow runs with the caller's repo checked out and never
gets its own, so the URL had to follow the move to this repo. Its `prompt-ref`
default moves from `master` to `v1`: consumers call the workflow at `@v1`, and
defaulting the prompt to master would run released workflows against unreleased
instructions, which is the skew the tag gate exists to prevent.

Also drops `RUN_PLATFORM_TESTS` from pr-build-test. Master removed it in #122 in
favour of gating on `TESTER_APIFY_TOKEN`, which that step already sets, so after
merging master the variable was config nothing reads.

Two notes on the checks:

- .github/actionlint.yaml ignores two errors on review.yaml. actionlint bakes in
  a snapshot of popular actions' interfaces from when the pinned version shipped,
  and claude-code-action has grown since, so it flags `display_report` and the
  `conclusion` output as undefined. Both are declared in the action's action.yml
  at @v1 — verified before suppressing, and the patterns name those two symbols
  so unrelated bad inputs and outputs in that file still fail.
- .github/review-prompt.md is prettier-ignored. Prettier collapses the nested
  bullet list under "do not visit, fetch, infer, or evaluate the following
  external links" into one run-on line, changing what the model is told. Keeping
  it byte-identical also makes re-syncing it a plain copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkUZADZ6gzWW4GE6CFMreM
… the refs

Cuts the major tag as `v0` rather than `v1`, since the package is on 0.x and will
keep releasing 0.x for a while. Nothing in the gate logic reads the number, so
this is only the tag name and the refs that repeat it.

Keeping consumers on `@master` until the package hits 1.0 was the alternative,
and it reopens the gap this was built to close: the floor gate has teeth only
because consumers track a tag that can be held. On `@master` a workflow calling
an unreleased CLI feature ships on merge and fails in every consumer's CI,
instead of simply not shipping.

The tag tracks the workflows' contract, not the package version — they move
independently on purpose, so `v0` is expected to outlive the package reaching
1.0. Bump it when a workflow breaks its callers, not when the package does.

Adds check-major-tag-refs.mjs to _check_code, because `uses:` cannot take an
expression, so the tag is repeated in all seven refs into this repo plus the
README snippets people copy. Missing one during a bump is silent in the case
that matters: right after a bump both tags exist, so workflows called at the new
tag pull the composite action from the old one and run a stale version of it
without failing. The check compares every self-reference and review.yaml's
prompt-ref default against MAJOR_TAG and names the file and line that disagree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkUZADZ6gzWW4GE6CFMreM
GitHub only reads workflow files at the top level of .github/workflows, so the
workflows other repos call sit in the same directory as this repo's own CI with
nothing distinguishing them. The prefix makes the boundary readable: `public_` is
the API other repos depend on, `_` is internal plumbing, `on_`/`manual_` are this
repo's own triggers.

Renamed: public_pr-build-test, public_platform-tests, public_push-build-latest,
public_claude, public_review, public_platform-tests-claude-investigate-and-fix.

Breaking for anyone already calling these by path, which today is nobody: the v0
tag does not exist yet and consumers still point at the old repo. Doing it now
costs nothing; after the first tag it would cost a major bump, since a filename
is baked into every consumer's `uses:` line. CONTRIBUTING says so where the
convention is introduced.

Two references would have broken quietly rather than loudly:

- .github/actionlint.yaml keys its ignores by path, so a stale key silently stops
  suppressing and the claude-code-action false positives come back. Verified by
  pointing the key at the old name and watching both errors return.
- check-major-tag-refs.mjs reads review.yaml by path to check its prompt-ref
  default; a stale path would have downgraded that to a soft "could not read".

The separators are now mixed (public_platform-tests), since the prefix is
underscore-style and the imported names are hyphenated. Left as-is because the
rest of the filename is the part consumers type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkUZADZ6gzWW4GE6CFMreM
…t a floor bump

The floor is a hand-written declaration, so the tag hold only protects consumers
if someone remembers to raise it. Forgetting is silent at merge time and loud
everywhere else: the tag moves, the workflow goes live, and it calls a CLI that
is not on npm, breaking every consumer's CI at once.

The check fires only when a single PR touches both sides — a `public_` workflow
or the composite action, and `bin/`, `lib/` or `index.ts` — and no floor bump.
`lib/` counts because the platform tests import it, so a workflow can depend on
its behaviour as much as on a CLI flag.

Deliberately scoped to one PR. A workflow could also start using something from
an earlier unreleased PR, which this will not see. Catching that means comparing
against the floor's release tag, which flags every workflow edit made while any
package change sits unreleased — far more noise than a case that needs someone to
land a CLI change and then sit on it.

Unrelated changes do land in one PR, so `no-floor-bump-needed` skips the check
rather than leaving no way past it. The failure message names both halves of the
diff and spells out both exits.

Verified against ten path combinations, including the real diff of this branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkUZADZ6gzWW4GE6CFMreM
Syncs github-actions-source#62, the one commit that landed there since the last
sync. Gives the investigate phase read-only access to the failing run through the
Apify MCP server, so it can read the run, its log and its storages instead of
working from workflow logs alone, and pins claude-code-action to a SHA.

Applied to the renamed public_ file; the patch landed unchanged otherwise.

Documents the new TESTER_APIFY_TOKEN_READ_ONLY in the README secrets table, since
it is consumer-facing: the workflow declares it `required: true`, so a repo
calling this workflow without that secret set will fail the call. Upstream has it
on master already, which is live, so this is not new breakage — but repos that
pick it up when they migrate to @v0 need the secret in place first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkUZADZ6gzWW4GE6CFMreM
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.

3 participants