This file provides guidance to AI coding agents when working with code in this repository.
A composite GitHub Action that downloads a pinned, checksum-verified rubyfmt release binary and runs rubyfmt --check. It doesn't use Ruby, gems, or Docker. It only needs curl and tar on the runner. The user-facing docs for inputs and outputs are in README.md.
action.ymldeclares the inputs andoutputs.outcome, and has one composite step that runsaction.sh. Every input is passed in as aGHA_RUBYFMT_*env var (e.g.fail-fastbecomesGHA_RUBYFMT_FAIL_FAST), never interpolated into the script with${{ }}. A new input needs three changes: theinputs:block, theenv:mapping, and the handling inaction.sh.action.shdoes all the work, in this order:- Maps
RUNNER_OS-RUNNER_ARCHto a rubyfmt release platform. OnlyLinux-x86_64,Linux-aarch64, andDarwin-arm64are supported. - Resolves
version(latest→ contents ofsupport/latest). - Looks up the sha256 for the (version, platform) pair in
support/versions. - Downloads the release into
$RUNNER_TEMP/rubyfmt-action, verifies the checksum, and extracts it. - Runs
rubyfmt --check -- ${GHA_RUBYFMT_PATHS}. The variable is intentionally unquoted so it word-splits, hence theSC2086disable. - Writes
outcometo$GITHUB_OUTPUT. On failure it also appends the diff to$GITHUB_STEP_SUMMARYand exits with rubyfmt's exit code.
- Maps
support/versionsis a space-separated<version> <platform> <sha256>table. Any version missing from it is rejected at runtime. The file is generated, so don't hand-edit it.support/latestis a single line holding the version thatlatestresolves to.support/sync-versions.shpins a release: it downloads each platform's asset and rewrites that version's rows insupport/versions. The platform list lives inPLATFORMShere and in thecaseinaction.sh, so keep the two in sync.
- It must run on bash 3.2, the default
/bin/bashon macOS runners. That rules out associative arrays (declare -A),mapfile/readarray,${var,,}, and other bash 4+ features. Underset -u, expanding an empty array as"${arr[@]}"also errors on 3.2.argumentsavoids this because it always starts with--check. Use/bin/bash ./action.shto test locally on a Mac, which matches the macOS runner. - Log through the
dbg/err/diehelpers, which emit GitHub workflow commands (::debug::,::error::). - Use
sha256_of, which falls back fromsha256sumtoshasum -a 256for macOS.
The CI lint job runs shellcheck and actionlint. zizmor runs through a reusable workflow in rubyatscale/shared-config. To run all three locally:
find . -type f -name "*.sh" -print0 | xargs -0 shellcheck
actionlint -ignore 'specifying action "\$/" in invalid format'
zizmor --offline .The -ignore is needed because actionlint doesn't understand the uses: $/ self-repository syntax that ci.yml uses to test the action from the current checkout (rhysd/actionlint#711).
There is no test runner. CI tests the action end to end by running it against test/fixtures/formatted.rb (expects success, on Linux and macOS) and test/fixtures/unformatted.rb (expects failure). To run the same check locally, set the env vars a runner would provide. Use RUNNER_OS=Linux RUNNER_ARCH=X64 on Linux.
mkdir -p /tmp/rubyfmt-run && : > /tmp/rubyfmt-run/out
RUNNER_OS=macOS RUNNER_ARCH=ARM64 RUNNER_TEMP=/tmp/rubyfmt-run \
GITHUB_ACTION_PATH="$PWD" GITHUB_OUTPUT=/tmp/rubyfmt-run/out \
GHA_RUBYFMT_PATHS=test/fixtures/unformatted.rb GHA_RUBYFMT_VERSION=latest \
GHA_RUBYFMT_FAIL_FAST=false GHA_RUBYFMT_INCLUDE_GITIGNORED=false \
GHA_RUBYFMT_HEADER_MODE=none GHA_RUBYFMT_SUMMARY=false \
/bin/bash ./action.sh; echo "exit=$?"; cat /tmp/rubyfmt-run/outOn the unformatted fixture this should print the diff, exit 5, and write outcome=failure.
To pin a new rubyfmt release, use support/sync-versions.sh v0.15.0. Pass --historical as the second argument to pin it without changing support/latest.
-
Third-party actions are pinned to a full commit SHA with a
# vX.Y.Zcomment. Dependabot updates them monthly as one group. -
The workflows set
permissions: {}at the top level and grant permissions per job. Checkouts usepersist-credentials: false. zizmor enforces this, so new workflows and jobs should follow the same pattern. -
Self-hosted Renovate (
.github/renovate-config.json, weekly) only managescustom.regexpins:support/latest,SHELLCHECK_VERSIONinci.yml, and the SHA-pinned actionlint download script URL inci.yml. Renovate bumps the version but can't update checksums, so CI on those PRs fails until a person:- for rubyfmt, runs
support/sync-versions.sh <tag>; - for shellcheck, recomputes the sha256 of
shellcheck-<version>.linux.x86_64.tar.xzand updatesSHELLCHECK_SHA256.
Renovate's regexes match the exact text of these lines (e.g.
# renovate: datasource=... depName=...directly aboveSHELLCHECK_VERSION:), so reformatting them will silently break tracking. - for rubyfmt, runs