- update rust crate similar to v3 (#105) (01d3132)
- update rust crate similar to v3.1.0 (#124) (e27377b)
- update rust crate clap to v4.6.1 (#131) (bf34b0e)
- ensure that adding blank lines between steps doesn't bleed into other areas (#136) (6ca7fad)
- Extend dependency changeset workflow to cover Dependabot PRs in addition to Renovate, and rename the workflow file accordingly. (0c91c3c)
- Bump rand from 0.9.2 to 0.9.4 (0c91c3c)
- chore(deps): update taiki-e/install-action action to v2.75.18 (d919609)
- chore(deps): update apple-actions/import-codesign-certs action to v7 (ee2c850)
- chore(deps): update taiki-e/install-action action to v2.75.19 (6a89723)
- chore(deps): update taiki-e/install-action action to v2.75.22 (45901f3)
strategy-sorter now silently skips include, exclude, and dimension
keys whose values are GitHub Actions expressions (e.g.
${{ fromJSON(inputs.platforms) }}), rather than emitting a
TypeMismatch: expected sequence, got non-sequence warning.
Fixes #88. (83f66d2)
Remove the version input and instead hardcode ACTION_VERSION as an env var
in the download step, kept in lockstep with Cargo.toml via a new Knope step
that updates it automatically during release preparation. (9209263)
Fix the --mode flag construction in the composite action so users can pass
mode: check rather than mode: mode=check.
Remove the version input and derive the binary version directly from
github.action_ref, keeping the action version and binary version in lockstep. (57b11a1)
Use a fine-grained PAT (RELEASE_PAT) for the knope release step so that
tag creation is not blocked by the repository ruleset restricting ref
creation for the default GITHUB_TOKEN.
Remove the set-cargo-version yq TOML-output workaround now that the
ubuntu-24.04-arm runner image ships a yq version with full TOML support. (c10672c)
When a step ID was renamed (e.g. cache-thing → cache_thing), references
to that ID in if: conditions written without ${{ }} delimiters were left
unchanged, producing a broken workflow.
GitHub Actions allows if: fields to omit the expression wrapper, so
if: steps.cache-thing.outputs.cache-hit != 'true' is valid. The reference
updater now processes /if paths regardless of whether they contain ${{. (453bd89)
Added a cargo update -p ghafmt step to the prepare-release workflow so
that Cargo.lock reflects the new version before the release commit is made. (a05b484)
Add explicit permissions: contents: read to all read-only jobs in
code_checks.yml, workflow_checks.yml, and the setup job in
nightly_build.yml, so that a default-permissions change cannot silently
grant write access.
Fix nightly_build.yml to upload artifacts/ghafmt-* instead of
artifacts/* to the nightly release, excluding the Docker layer-cache
artifact that would otherwise appear as a spurious release asset. (e104571)
- chore(deps): update taiki-e/install-action action to v2.69.7 (b52b460)
- chore(deps): update taiki-e/install-action action to v2.69.8 (cb38611)
ghafmt now detects and formats GitHub Actions action metadata files
(action.yml / action.yaml) in addition to workflow files.
- Composite actions (
runs.using: composite) — top-level key ordering,runskey ordering, step key ordering, alphabeticalwithsorting inside steps, alphabeticalinputs/outputssorting with idiomatic per-entry key ordering,brandingkey ordering, and snake_case enforcement on step IDs - JavaScript actions (
runs.using: node20/node24) — top-level key ordering,runskey ordering (using → pre → pre-if → main → post → post-if), alphabeticalinputs/outputssorting - Docker container actions (
runs.using: docker) — top-level key ordering,runskey ordering (using → image → args → env → pre-entrypoint → entrypoint → post-entrypoint), alphabeticalinputs/outputssorting
Files that cannot be identified as a workflow or action pass through with presentation transforms only (blank lines, variable spacing).
Pre-built binaries for aarch64-apple-darwin and x86_64-unknown-linux-musl
are now attached to each GitHub Release as assets. Support for
aarch64-unknown-linux-musl and x86_64-pc-windows-msvc is planned for
future releases. (247bd28)
ghafmt is now available as a Docker image on GitHub Container Registry:
docker pull ghcr.io/jonathanrainer/ghafmt:latest
Nightly builds are also published as ghcr.io/jonathanrainer/ghafmt:nightly. (097523e)
ghafmt is now available as a composite GitHub Action:
- uses: jonathanrainer/ghafmt@v0.2.0
with:
path: .github/workflows/
mode: checkSupports Linux x86_64, Linux ARM64, and macOS ARM64 and Intel. (90ca03b)
Pre-built binaries for x86_64-apple-darwin (macOS Intel) are now included in
each GitHub Release, cross-compiled from the ARM64 runner using Apple's clang. (839d6cb)
Released macOS binaries are now signed with a Developer ID Application certificate and notarized with Apple, so Gatekeeper no longer blocks them on download.
Signing and notarization is implemented as a composite action
(.github/actions/sign-and-notarize) following the existing set-cargo-version pattern, and
is called from the build artifacts workflow for macOS targets only. It runs exclusively on main
so signing credentials are never exposed during PR or feature branch builds. (638ec79)
main.rshas been split into focused modules:src/lib.rs— orchestration andFormatterResultsrc/cli.rs— Clap argument definitionssrc/commands/— one file per mode (check,format,write,list), sharing aCommandtraitsrc/fs.rs—expand_paths,atomic_write, stdin reading
read_from_stdinpreviously usedtake(LIMIT)to cap reads but then checkedn > LIMIT, a guard that could never fire. The function now probes for an overflow byte after reading, correctly detecting inputs that exceed the limit while accepting files of exactly the limit size.
- Added
atomic-write-file— replaces the hand-rolled temp-file-then-rename implementation ofatomic_write. - Added
patharg— typedInputArgenum wrapping file paths and stdin (-). - Added
strum_macros— derive-based enum string conversions for CLI mode/colour variants.
- Added unit tests for
read_with_limit(under limit, exactly at limit, over limit) andatomic_write(overwrites rather than appends). (809abb2)
Added an Acknowledgements section to the README crediting @pantoniou for libfyaml and @0k for the fyaml Rust bindings.
Added a LICENSE symlink pointing to LICENSE-MIT so that GitHub's licence detector
correctly identifies the repository licence rather than reporting it as unknown.
Updated the pre-commit hooks to detect if a changeset exists on the branch, just to save time forgetting one on PRs (3f8595e)
A .pre-commit-config.yaml is now provided for local development. It runs
clippy, cargo deny, actionlint, and ghafmt (from source) as opt-in pre-commit
checks. (ce899c8)
Adds a README with usage examples, CI integration snippets, and a before/after example. Adds CONTRIBUTING.md with dev setup and transformer guide. Switches license from GPL-3.0-only to MIT OR Apache-2.0. (490bf69)
Integrates fixes from the restructured jonathanrainer/libfyaml fork and
adds the Rust-side presentation work that depends on them.
- Add
TopLevelCommentSpacerpresentation transformer: inserts a blank line before any block of standalone top-level comments (col 0) that follows content at a deeper indentation level, covering end-of-file comments and other top-level comment blocks not preceded by a known top-level key
insert_blank_line_before_comment_blockgains amax_comment_indentparameter; when set, a comment preceded by an indent deeper than the threshold is treated as content rather than part of the preceding comment block, preventing the scan from skipping deeply-nested comments that belong to the previous section- Fix
TopLevelCommentSpacernot triggering on inline right-hand comments: only inserts a blank line when the preceding event is aLinebreak, so comments that appear on the same line as a value are correctly ignored (7d783e2)
Address findings from a review of the codebase.
- Fix typo in
FilterSorter:on/pull_request_targe/branches-ignore→on/pull_request_target/branches-ignore, which was silently skipping that sort path - Remove
outputsandsecretsfromWorkflowDispatchSorter; those keys belong toworkflow_callonly and were being incorrectly sorted - Fix
Rename::Ord/PartialOrd/Eq/PartialEqimpls inCaseEnforcerthat compared only depth, violating theEq/Ordcontract; sorting is now done viasort_by_keywithReverseon depth - Reject
stdin (-)with--mode=list, consistent with the existing--mode=writerestriction; produces a structured error via the sameStdinCannotBeUsedWithListvariant
- Replace ad-hoc
eprintln!calls for early validation errors inGhafmt::runwith structuredErrorvariants rendered throughrender_error, so colour mode is respected and diagnostics are consistent - Extract
build_handlerandrender_erroraspub(crate)free functions; remove them from theCommandtrait entirely — callers use the free functions directly - Simplify
FormatterResult: replaceoriginal: Option<String>+ accessor method with a plainpub(crate) original: Stringfield - Change
Ghafmt::runsignature frommut selfto&mut self - Pre-compute static key-ordering
Vec<String>fields in transformer struct constructors instead of allocating on everyprocess()call; affected transformers:TopLevelSorter,JobSorter,StepSorter,ConcurrencySorter,StrategySorter,ContainerSorter,RunsOnSorter,EnvironmentSorter,WorkflowCallSorter - Change
sort_mapping_at_pathandclassify_renamessignatures to accept&[T]instead of&Vec<T> - Make the
WorkflowProcessortransformer pipeline injectable viaWorkflowProcessor::new(Vec<Box<dyn StructureTransformer>>)with aDefaultimpl that builds the standard pipeline; removes the need for a test-only constructor - Derive
CopyonStateenums inJobsBlankLinesandStepsBlankLines; remove now-redundant.clone()calls at match sites - Remove stale
#[allow(unused_assignments)]fromerrors.rs - Fix
--colourflag spelling in the CLI integration test (was--color, causing the test to pass vacuously against a clap parse error)
- Add unit tests for
source_windowandline_col_to_byte_offsetinerrors.rs - Add integration tests for
WorkflowProcessorerror recovery: failed transformer emits a warning, document is restored, and subsequent transformers still run - Add
list_mode_rejects_stdinCLI integration test - Add
pull_request_target_branches_ignore_sortedregression test inFilterSorter - Remove duplicate
multi_job_comments_between_stepstest case inStepsBlankLines(5fd7503)
--check now prints a unified diff to stderr for each file that differs from its
formatted form, instead of silently exiting 1. The no-flag multi-file error message
now mentions --check and --list alongside --write. (4d2689b)
The Docker build was failing because bindgen (used by fyaml-sys) invoked clang with the
default glibc include paths, causing a fatal error when it couldn't find
bits/libc-header-start.h.
Fixed by setting BINDGEN_EXTRA_CLANG_ARGS=--sysroot=/usr/local/musl/${RUST_MUSL_CROSS_TARGET}
in the cargo build step, pointing clang at the musl sysroot already bundled in the
rust-musl-cross image. This works for both linux/amd64 and linux/arm64 targets. (f569f76)
When a string value used a >/>-/>+ folded scalar an extra trailing blank line
would be emitted after it.
This was due to an underlying bug in the YAML parsing library (libfyaml) which
is now resolved. (062854a)
Replaced the LICENSE symlink with a real file (renamed from LICENSE-MIT) so that
GitHub's licence detector correctly identifies the repository as MIT without reporting
duplicates. LICENSE-APACHE is retained for dual-licence completeness. (fffec1f)
When a string value used a | block scalar (e.g. multi-line ${{ }} expressions),
set_yaml_at would silently fold newlines to spaces instead of failing, causing the
updated value to be written as a single-line string. The enforcer now detects literal
block style up front and formats the replacement value as a proper | block scalar
before writing. (1d769c1)
When a step used a |+ (literal keep) block scalar, formatting would insert an extra
blank line after it on every pass, breaking idempotency.
The underlying libfyaml bug that caused |+ to emit a spurious extra trailing newline
was fixed upstream (PR #268). However the blank-line insertion logic in ghafmt was not
accounting for the blank line already present in the event stream from the keep-chomping
semantics, so it would add another on top.
The fix detects when two consecutive line-break events already exist at the insertion point and skips insertion in that case. (6f07276)
Nightly builds now report nightly.YYYYMMDD.SHORTSHA from --version instead of the previous
0.0.0-nightly.YYYYMMDD.SHORTSHA. Nightly artifact filenames follow the same format. (088fed0)
Address findings from an internal security audit of the binary, CI/CD pipeline, supply chain, and repository hygiene.
Supply chain:
- Pin
fyamlandfyaml-syspatch dependencies to exact commit SHAs (rev =) instead of a mutable branch, preventing silent updates oncargo update - Pin
similarto=2.7.0for consistency with all other exact-pinned deps
CI/CD:
- Replace the unauthenticated
curl | bashactionlint install with a pinned release download and SHA-256 checksum verification (version sourced from the release checksums file, so Renovate bumps only the version) - Remove dead Windows packaging code from
_build_artifacts.yml(Windows is not in the build matrix) - Generate
.sha256files alongside release tarballs and verify them inaction.ymlbefore extracting the downloaded binary - Digest-pin the Dockerfile base images (
amd64-muslandarm64-musl) using per-platform digest pinning via named multi-stageFROMaliases - Pin the fallback
yqdownload inset-cargo-versionto a specific release with checksum verification (SHA-256 position derived fromchecksums_hashes_order)
Binary hardening:
- Disable symlink following in
--writemode to prevent path-traversal writes via symlinks pointing outside the target directory - Atomic file writes via
tempfile::NamedTempFile+ rename, preventing partial files on interrupted writes - Cap stdin consumption at 10 MB, returning an error if the limit is exceeded
- Source code embedded in parse error diagnostics is now limited to ±5 lines around the error, preventing secrets elsewhere in the file from leaking to stderr
- Replace
.unwrap()with.unwrap_or_default()in YAML sort closures to avoid panics on non-scalar keys
Repository hygiene:
- Add
SECURITY.mdwith a private vulnerability disclosure path - Add explanatory comments to suppressed advisory and
multiple-versions = "allow"indeny.toml(29d5880)
Switch the deny CI job from EmbarkStudios/cargo-deny-action to
taiki-e/install-action, fetching a pre-compiled binary instead of
compiling from source on every run. (deac02e)
- Add rust-toolchain.toml as single source of truth for Rust version (28b49be)
- Migrate Renovate config (96edec2)
Sets up the full CI pipeline and release tooling for the project:
- Code checks (
code_checks.yml): nightlyrustfmt,clippy,cargo test(withactionlintinstalled), andcargo denyfor security advisories - Workflow checks (
workflow_checks.yml):actionlintvia Docker image andghafmt --checkto keep workflow files formatted - Dependency updates (
renovate.json): Renovate configured to update GitHub Actions SHA pins, Cargo dependencies, and the pinned Rust toolchain version - Changeset tracking (
knope.toml):knope create-changesetworkflow for documenting changes per PR
Also fixes several pre-existing issues uncovered by the new checks: invalid GPL-3 SPDX identifier, pending tests not
being skipped in CI, failing actionlint tests caused by unresolvable reusable workflow references, and clippy
warnings in integration test helpers. (8f871f7)
Releases now require a GitHub milestone titled v{version} (e.g. v0.1.0)
to exist before the release workflow will proceed. Once the release is
published the milestone is closed automatically. (664180a)
A nightly GitHub pre-release is published each night at
https://github.com/jonathanrainer/ghafmt/releases/tag/nightly with
pre-built binaries for all four supported platforms. Nightly binaries
report their build date and commit in --version output. (247bd28)
The nightly build workflow can now be triggered manually via
workflow_dispatch in addition to the scheduled nightly run. (882b45c)
The PR and commit created by prepare-release now use
chore: Prepare To Release v$version instead of
chore: prepare release $version. (cfe4dd9)
Renovate changesets are now named renovate_pr_<number>.md instead of deriving a slug from
the branch name. The old slug-based approach silently skipped changeset creation when a file
for the same package already existed from a previous PR, meaning repeated dependency bumps
went undocumented. Using the PR number guarantees a unique file per PR. (96edec2)
Added secrets: inherit to both callers of _build_artifacts.yml (nightly_build.yml and
release.yml) so that repository secrets are available inside the reusable workflow. (ca26e4b)
mode so we can support our nightly builds (4092bab)
Added CODEOWNERS to auto-request review on all PRs. Tightened workflow permissions in
release.yml and prepare_release.yml by moving broad top-level permissions blocks down
to per-job level, so each job holds only the minimum permissions it needs. (796104d)
The GitHub Action usage example in the README now pins to a full commit SHA rather than a floating version tag, following the recommended security practice for GitHub Actions:
- uses: jonathanrainer/ghafmt@1cc43c68845e56ea46b7a9c6017e024283081648The prepare-release workflow in knope.toml now automatically updates this pin to the correct
SHA and version comment (e.g. # v0.3.0) as part of the release prep commit, so the docs stay
accurate across releases.
Also suppresses a Dockerfile linter warning about --platform=linux/amd64, with an explanatory
comment clarifying the intent. (4237b9d)
Adds a renovate_changeset.yml workflow that automatically commits a .changeset/*.md file
to every Renovate PR, preventing CI failures caused by missing changesets. The workflow
conditions on the PR author (github.event.pull_request.user.login) rather than the event
actor so that manually closing and reopening a PR still triggers changeset generation.
Also adds a Dependencies changelog section to knope.toml so dependency updates appear
under their own heading in the CHANGELOG. (8f871f7)
Removes the default weekly schedule so Renovate raises PRs as soon as updates are detected. (8f871f7)
- Update Rust crate tracing-subscriber to v0.3.23 (96edec2)
- Update taiki-e/install-action action to v2.68.33 (originally merged in #28) (96edec2)
- Update Rust crate assert_cmd to v2.2.0 (96edec2)
- Update Rust crate clap to v4.6.0 (96edec2)
- Update actions/checkout action to v6 (96edec2)
- chore(deps): update dependency rust-lang/rust to v1.94.0 (96edec2)
- chore(deps): update taiki-e/install-action action to v2.68.34 (96edec2)
- chore(deps): update taiki-e/install-action action to v2.68.35 (96edec2)
- chore(deps): update taiki-e/install-action action to v2.69.1 (ecb3daa)
- chore(deps): update taiki-e/install-action action to v2.69.2 (e8bc0cc)
- chore(deps): update taiki-e/install-action action to v2.69.5 (78ffcf8)
- chore(deps): update taiki-e/install-action action to v2.69.6 (9cc85d9)