Skip to content

Releases: magicdrive/hephaestus

v1.5.1

Choose a tag to compare

@github-actions github-actions released this 02 Nov 08:19
Immutable release. Only release title and notes can be modified.

What's Changed

  • ci: run actionlint, bound every CI job, keep the e2e's released actions by @magicdrive in #56

Full Changelog: v1.5.0...v1.5.1

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 07:31
Immutable release. Only release title and notes can be modified.
0f76752

Hephaestus v1.5.0

Hephaestus can now be installed without Homebrew or Go: install.sh installs a release from
GitHub Releases on macOS and Linux (amd64, arm64), checked against the release's checksum file.
Nothing else changes: the commands, the configuration, the release pipeline and the action are
those of v1.4.0.

Install script

curl -fsSLO https://raw.githubusercontent.com/magicdrive/hephaestus/main/install.sh
sh install.sh                        # the latest stable release, into ~/.local/bin
sh install.sh --version v1.4.0       # a given release (older ones and pre-releases too)
sh install.sh --upgrade              # the latest stable release again
sh install.sh --dir "$HOME/bin"      # another directory (created if missing)
  • The archive must match the SHA-256 of hephaestus_checksums.txt; a missing or malformed
    checksum file stops the install. Only the hephaestus binary is taken out of the archive,
    after its entries have been checked (no absolute paths, .., links or devices), and it
    replaces the installed one with a single rename after it ran and reported the version asked
    for. A failed or interrupted run leaves the installed binary as it was.
  • It never uses sudo and never changes PATH; it refuses Homebrew's directories and does not
    replace a symbolic link (keep brew upgrade hephaestus for a Homebrew installation).
  • Every release from v0.1.0 on can be installed. The archives keep carrying build provenance
    attestations (gh attestation verify <file> --repo magicdrive/hephaestus).
  • Windows is not supported by the script (use the .zip of the release, or the action).

The script is the installer of Enma with one more
configuration line, VERSION_PREFIX, because hephaestus --version prints hephaestus v1.5.0
(unchanged). See docs/installer.md.

Documentation

  • README: the Installation section shows Homebrew, the install script, the archives, the action
    and building from source. go install github.com/magicdrive/hephaestus@latest is no longer
    documented.

Verification

  • internal/installer runs the script against a fake github.com (local TLS, no network) with
    sh, dash, bash, ksh and BusyBox, curl and GNU Wget: install, reinstall, upgrade, downgrade,
    pre-releases, HTTP failures, truncated and stalled downloads, checksum and archive attacks,
    install directories, Homebrew, interrupted and concurrent runs, and the README's commands.
  • CI installs the snapshot archives it has just built with the script (TestDistArchives).

Full Changelog: v1.4.0...v1.5.0

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 28 Oct 14:14
Immutable release. Only release title and notes can be modified.
83721c3

Hephaestus v1.4.0

hephaestus preflight, and so hephaestus publish, now checks the release
workflow's conventions before the tag is pushed; the Homebrew tap stage no
longer lets two release pull requests downgrade a formula through their merge
order; and two new commands guard the tap's merges: cmd/tap-version-guard
for the tap's required check and cmd/pr-gate for a fail-closed merge. The
publish lock no longer outlives a killed process.

Release workflow checks

hephaestus preflight reads the release workflow (preflight.workflow,
default .github/workflows/release.yml) and the CI workflow it runs from the
work tree, and checks the conventions hephaestus publish relies on, which
were verified only after the release was published:

Check What it requires
workflow.release tag pushes only, read-only default permissions, one publishing job in an environment that needs the CI job
workflow.pins every action pinned to a commit SHA; Hephaestus pins carry their release as a comment, and CI runs the release's commit
workflow.tap the tap token only in the two named tap steps, after the plan step, with if: steps.plan.outputs.tap == 'publish'
workflow.attestation every planned asset attested before publishing
workflow.ci-gate ci-gate needs every CI job, with if: always()

They read YAML only, so they work for any language, and fail closed on what
they cannot read unambiguously. They are warnings: a violation makes the
preflight WARN (exit 0) and fails --strict, so hephaestus publish stops
before pushing the tag. preflight.skip turns one off.

hephaestus check --workflows   # the same checks without a tag, for pull request CI (exit 1 on a violation)

See docs/preflight.md.

Homebrew tap: formula downgrades

In pull request mode every release opens its own formula pull request; merged
in the wrong order, an older one lowered the formula. The tap stage now:

  • reads the other open pull requests against the tap's base branch before
    proposing a formula. When one proposes a newer version (by SemVer
    precedence), nothing is written and the formula is skipped, as for a newer
    base branch. Older ones are named in a warning and in the new pull
    request's body, which states the merge order; the scan is repeated after
    opening, so racing releases report each other. brews[].allow_downgrade
    turns the skip into a warning;
  • counts only pull requests from a branch of the tap (or of the configured
    head repository). One from someone else's fork is a warning: anyone can
    open one on a public tap;
  • fails, before writing anything and in a dry run too, when the base
    branch, the pull request branch or an open pull request from the tap
    already has the version being published with another sha256 for the same
    archive url: the release's archives were replaced or tampered with. The
    release workflows' read-only rehearsal stops such a release before it is
    published. allow_downgrade does not apply. tap.ChecksumChanges is the
    rule, for the tap's CI to reuse.

cmd/tap-version-guard

A check for the tap's own CI that fails a change lowering a formula's
version:

go run github.com/magicdrive/hephaestus/cmd/tap-version-guard@<commit> [-base HEAD^1 [-each] | -against <tip>] [-head HEAD] [-strict] \
  [-rollback PATH=FROM->TO ... | -approvals <comments.json> -permissions <permissions.json> -approval-head <sha> [-approval-before <time>]] \
  [-summary <file>] [-allow-downgrade]
  • -against <tip> checks an unmerged pull request head against the base
    branch's current tip; -each checks every first-parent step of
    base..head (a merge queue entry, a push of several merges).
  • -strict, for a required check, also fails a formula with no or a
    non-semantic version, versions that cannot be compared, the same version
    with other artifacts, a url of another version, a removed formula, a
    formula that is not a regular file, an unsafe path and a Formula entry
    that is not a directory. It reads only the formulae Homebrew loads, from
    git objects only.
  • A rollback is authorized for exactly one downgrade, PATH=FROM->TO: by
    -rollback, or by an approval comment (/formula-rollback with head:,
    formula: and reason:) whose author is an admin of the tap, that was
    never edited and names the pull request head. Every other finding stays an
    error, and an authorization the change does not use fails.
    -allow-downgrade stays for compatibility.

examples/workflows/tap-version-guard.yml
runs the required check on pull_request_target (the default branch's
workflow; the pull request is fetched as objects only), needs the
formula-rollback label and an admin's approval for a rollback, posts the
commit status version-guard as well, and re-checks after each merge,
opening an issue when a downgrade landed. See
docs/tap-version-guard.md.

Merge gate

cmd/pr-gate decides whether a pull request may be merged and, only with
-merge, merges it pinned to the reviewed head (PUT pulls/{n}/merge with
sha, sent once) and verifies the merge commit's parents. Every condition
fails closed: open, not a draft, exact head and base commits, behind 0,
mergeable and clean, required checks read from the branch rules and branch
protection (or named with -require), every check, status and workflow run
completed, and a required check satisfied only by success. Without
-merge it only reads. See
docs/pr-gate.md.

Fixes

  • Publish lock. The lock on hephaestus-publish.lock is an operating
    system file lock (flock on Unix, LockFileEx on Windows) instead of a file
    created with O_EXCL. A publish killed with SIGKILL (or crashed) left the
    file behind and every later publish failed until it was removed by hand;
    the system now releases the lock with the process. A live publish's lock is
    still refused, naming its pid, host and start time. Older versions, which
    lock with O_EXCL on the same file, are still excluded. Publish refuses a
    file system that cannot lock, and a clone on a network or user-space file
    system (NFS, SMB, WebDAV, FUSE, 9p, a network drive), and checks right
    before creating and pushing the tag that the lock file is still the one it
    locked.
  • Tap head branch. A pull request head branch that someone else created
    between the tap stage's check and its create (a 422 "Reference already
    exists") is verified like any existing branch, instead of being used as it
    was. A create whose response was lost (5xx, timeout) is followed by a read
    of the branch, instead of failing the tap step.
  • Release stage. When the commit being released is known (--commit,
    $GITHUB_SHA) but the tag cannot be read (an API error other than 404),
    the release stage stops before it creates, uploads or publishes anything.
    It only warned and went on, so the build could have been published under a
    tag of another commit.
  • hephaestus publish says to delete the local tag when another clone
    pushed the same tag at another commit first.
  • Scripts. scripts/fuzz.sh fails when go list or go test -list
    fails, instead of fuzzing nothing; scripts/go-env-snapshot.sh fails when
    go version or go env fails, and runs them outside any module. make fmt-check fails when gofmt itself fails.
  • Hephaestus's own release workflow. A new provenance job releases only
    a tag whose commit is in the history of main, and the release jobs run on
    ubuntu-24.04 instead of ubuntu-latest.

Upgrade notes

These changes fail closed where the previous version went on:

  • A release workflow that violates a workflow.* convention now stops
    hephaestus publish (and hephaestus preflight --strict) before the tag
    push. Fix the workflow, or turn the check off with preflight.skip.
  • A tap that already has the version being published with another sha256
    for the same archive url now fails the tap stage, in a dry run too. Release
    a new version instead.
  • A hephaestus-publish.lock written by an older version (pid N) is
    refused, never taken over by its pid: empty the file once no older publish
    runs.
  • hephaestus publish refuses a clone on a network or user-space file
    system; publish from a clone on a local disk.

Compatibility

  • Unchanged: configuration keys, the Action, publish plan and publish status, and the existing JSON fields and check IDs.
  • New: the preflight checks workflow.release, workflow.pins,
    workflow.tap, workflow.attestation and workflow.ci-gate (warnings),
    hephaestus check --workflows, cmd/tap-version-guard, cmd/pr-gate and
    examples/workflows/tap-version-guard.yml.
  • Changed: in pull request mode the tap stage skips a formula when an
    open pull request from the tap proposes a newer version (a warning with
    brews[].allow_downgrade), and fails on the same version with other
    checksums (above).

Known limitations

  • cmd/pr-gate serializes merges on one machine with a lock file created
    with O_EXCL in the temporary directory: a gate killed with SIGKILL
    leaves it behind (remove it by hand), and another user of a shared
    temporary directory can create it first. A merge's safety rests on the
    pinned sha and the check of the merge commit's parents, not on the lock.
  • hephaestus check shows the tap pull request branch as a template;
    hephaestus preflight --tag renders it.

Verification

CI succeeded on the release commit's parent on main (run 40511957192),
including the tests added with these changes: every check between creating
and pushing the ...

Read more

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 13:03
Immutable release. Only release title and notes can be modified.
e544acf

Hephaestus v1.3.0

hephaestus publish no longer assumes that
releases are cut from main: it tells the repository's default branch
from the release branch, and checks everything that guards a release on
the release branch.

Release branch

hephaestus publish check v1.2.3                          # the default branch: main, master, develop...
hephaestus publish check -branch release/stable v1.2.3   # a named release branch
preflight:
  branch: release/stable   # read by hephaestus publish and hephaestus preflight

The release branch is -branch, else preflight.branch, else the release
repository's default branch on GitHub. A repository whose default branch is
develop can release from release/stable; one on master needs no
setting. The preflight prints both:

  [ok  ] default branch               develop (GitHub, origin/HEAD)
  [ok  ] release branch               release/stable (preflight.branch) at 1a2b3c4d5e6f on GitHub

Everything is checked on the release branch, never on the default branch on
its behalf:

  • it is a branch name that exists on GitHub: not a tag, a commit SHA, a full
    ref or a refspec;
  • the clone is on it, clean, with HEAD = origin/<branch> = its head on
    GitHub;
  • CI succeeded on it, for exactly that commit: a green default branch does
    not count;
  • an active ruleset covering it requires ci-gate;
  • right before the tag push it is fetched again and its head on GitHub read
    again: a branch that moved stops publish before anything is pushed;
  • hephaestus preflight --strict, which publish also runs, checks the same
    branch.

When a branch cannot be read (no network, no access), or a local
refs/remotes/origin/HEAD names another branch than the default branch,
nothing further is checked and nothing is tagged.

See docs/publish.md.

Fixes

  • A ruleset covering ~DEFAULT_BRANCH counted as protecting whichever branch
    was checked. It now counts only for the repository's default branch, in
    hephaestus publish and in hephaestus preflight (github.ruleset.branch
    and the required checks of github.ci).

Release candidate

v1.3.0-rc.1 was released with this hephaestus publish itself (make publish): the preflight resolved main as both the default branch and the
release branch, re-read its head on GitHub before the tag push, and the
release was verified (assets, checksums, provenance attestations, the tap
left untouched). Its binaries ran on Linux, macOS and Windows, amd64 and
arm64 (verify-release.yml), and passed publish check on a repository
whose default branch is master.

Compatibility

  • Unchanged: configuration (preflight.branch is the existing key), the
    Action, publish plan and publish status, JSON fields and check IDs.
  • New: the -branch flag of hephaestus publish, and its default branch and release branch checks. A project on main gets the same
    checks as before plus these two; the ruleset check keeps its name
    (main ruleset).
  • Stricter: hephaestus preflight with a preflight.branch other than the
    default branch now warns (github.ruleset.branch) when only the default
    branch's ruleset covers it.

Known limitations

  • hephaestus check shows the tap pull request branch as a template;
    hephaestus preflight --tag renders it.

Full Changelog: v1.2.0...v1.3.0

v1.3.0-rc.1

v1.3.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 26 Oct 12:34
Immutable release. Only release title and notes can be modified.
23b3069

Hephaestus v1.3.0-rc.1

Release candidate of v1.3.0. hephaestus publish no longer assumes that
releases are cut from main: it tells the repository's default branch
from the release branch, and checks everything that guards a release on
the release branch.

Release branch

hephaestus publish check v1.2.3                          # the default branch: main, master, develop...
hephaestus publish check -branch release/stable v1.2.3   # a named release branch
preflight:
  branch: release/stable   # read by hephaestus publish and hephaestus preflight

The release branch is -branch, else preflight.branch, else the release
repository's default branch on GitHub. A repository whose default branch is
develop can release from release/stable; one on master needs no
setting. The preflight prints both:

  [ok  ] default branch               develop (GitHub, origin/HEAD)
  [ok  ] release branch               release/stable (preflight.branch) at 1a2b3c4d5e6f on GitHub

Everything is checked on the release branch, never on the default branch on
its behalf:

  • it is a branch name that exists on GitHub: not a tag, a commit SHA, a full
    ref or a refspec;
  • the clone is on it, clean, with HEAD = origin/<branch> = its head on
    GitHub;
  • CI succeeded on it, for exactly that commit: a green default branch does
    not count;
  • an active ruleset covering it requires ci-gate;
  • right before the tag push it is fetched again and its head on GitHub read
    again: a branch that moved stops publish before anything is pushed;
  • hephaestus preflight --strict, which publish also runs, checks the same
    branch.

When a branch cannot be read (no network, no access), or a local
refs/remotes/origin/HEAD names another branch than the default branch,
nothing further is checked and nothing is tagged.

See docs/publish.md.

Fixes

  • A ruleset covering ~DEFAULT_BRANCH counted as protecting whichever branch
    was checked. It now counts only for the repository's default branch, in
    hephaestus publish and in hephaestus preflight (github.ruleset.branch
    and the required checks of github.ci).

Compatibility

  • Unchanged: configuration (preflight.branch is the existing key), the
    Action, publish plan and publish status, JSON fields and check IDs.
  • New: the -branch flag of hephaestus publish, and its default branch and release branch checks. A project on main gets the same
    checks as before plus these two; the ruleset check keeps its name
    (main ruleset).
  • Stricter: hephaestus preflight with a preflight.branch other than the
    default branch now warns (github.ruleset.branch) when only the default
    branch's ruleset covers it.

Known limitations

  • hephaestus check shows the tap pull request branch as a template;
    hephaestus preflight --tag renders it.

Full Changelog: v1.2.0...v1.3.0-rc.1

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 14:31
Immutable release. Only release title and notes can be modified.
160990d

Hephaestus v1.2.0

The GitHub Action no longer compiles
Hephaestus: it installs the release binary that belongs to the commit you
pinned, verified by its checksum and its provenance attestation, and leaves
your toolchain alone. This release also brings hephaestus publish, and the
fixes prepared for v1.1.1 (never released).

The Action runs the verified release binary

- uses: magicdrive/hephaestus@<full commit SHA of v1.2.0> # v1.2.0

Before your build runs, the Action:

  1. reads the release named in action/VERSION of the pinned commit; the
    release workflow refuses a tag that this file does not name;
  2. downloads hephaestus_<version>_<os>_<arch>.tar.gz (.zip on Windows)
    and hephaestus_checksums.txt, from that release only;
  3. checks the archive's SHA-256;
  4. verifies the archive's provenance attestation (gh attestation verify).
    It must have been signed by magicdrive/hephaestus's release.yml on a
    GitHub-hosted runner, for the release tag and, when pinned by SHA, for
    exactly the pinned commit;
  5. extracts only the regular file hephaestus[.exe] at the archive root,
    and checks that it reports the expected version.

Any failure stops the Action. It never falls back to building from source.

As a result:

  • No toolchain is involved. Nothing is compiled, no Go is downloaded or
    set up, and nothing is written to $GITHUB_PATH or $GITHUB_ENV. Your
    build commands use the toolchain your workflow set up. A runner without Go
    can run the Action for a project in any language.
  • The binary is bound to your pin. The attestation proves that the
    binary was built by Hephaestus's release workflow from the commit you
    pinned.
  • Supported platforms are unchanged: Linux, macOS and Windows, on x64
    and ARM64. A runner needs bash, curl, tar and gh, which
    GitHub-hosted runners have.

Reference the Action by a release. Use the full SHA of a release tag's
commit (recommended) or the tag itself. A branch such as @main now fails,
because no release binary belongs to it.

hephaestus publish

The tool that releases Hephaestus is now a command for any project:
make publish vX.Y.Z that runs hephaestus publish. In order, it:

  1. checks that the release can be made: branch, clean worktree, in sync
    with origin, the tag unused, CI green on the commit, rulesets, the
    release environment, the tap, and the generic preflight;
  2. asks for confirmation;
  3. pushes an annotated tag;
  4. follows the release workflow;
  5. verifies the release: assets, checksums, attestations and the tap pull
    request.

See docs/publish.md.

The Action gains command: plan, with the outputs prerelease and tap.
The steps that use the tap token can then run only when the tap is
published. A prerelease never even receives the token.

Fixes (prepared for v1.1.1)

  • The Action no longer changes your Go toolchain. v1.1.0 put its own Go
    first on PATH and exported GOTOOLCHAIN=local, so your Go modules were
    built with Go 1.26.0, or failed to build.
  • PWD is no longer redacted as a credential. Paths under the current
    directory are no longer printed as ***. A value matched only by its
    variable's name is not redacted when it is the path of an existing file or
    directory. Known tokens are always redacted.
  • --fresh removes the previous run's outputs (archives, checksum files
    and formulae) from dist, if they are unchanged and Hephaestus wrote them.

Release candidate

v1.2.0-rc.1 (commit bae0e2b) was published as a prerelease. Before this
release, it was verified on GitHub in two ways:

  • The Action's release path, pinned to that commit. The Action
    downloaded the release binary, checked its checksum, verified its
    attestation, extracted it and checked its version. This ran on Linux,
    macOS and Windows, x64 and ARM64. The job's toolchain was unchanged
    afterwards.
  • The five projects that adopted Hephaestus. git-parallel, kjak, ark,
    enma and goreg ran CI, snapshots and Homebrew end-to-end tests.

v1.2.0 differs from the release candidate only in action/VERSION, these
notes, the changelog and CI.

Compatibility

  • Unchanged: configuration, CLI flags, Action inputs and outputs, JSON
    fields and check IDs. New: the publish command, and the Action's plan command with the outputs prerelease and tap.
  • The Action: it must be referenced by a release tag or that tag's
    commit, and it no longer provides Go. Set up your own Go, as the examples
    always did.
  • Template variables: {{ .Env.PWD }} and {{ .Env.OLDPWD }} are now
    visible to templates.

Known limitations

  • Homebrew 4.6 parses the version of archive URLs such as
    tool_1.2.3_darwin_arm64.tar.gz as 64; Homebrew 7.0 parses it
    correctly. brew update updates Homebrew itself.
  • hephaestus check shows the tap pull request branch as a template;
    hephaestus preflight --tag renders it.

Full Changelog: v1.1.0...v1.2.0

v1.2.0-rc.1

v1.2.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 13:20
Immutable release. Only release title and notes can be modified.
bae0e2b

Hephaestus v1.2.0-rc.1

Release candidate of v1.2.0. The GitHub Action no longer compiles
Hephaestus: it installs the release binary that belongs to the commit you
pinned, verified by its checksum and its provenance attestation, and leaves
your toolchain alone. This release also brings hephaestus publish, and the
fixes prepared for v1.1.1 (never released).

The Action runs the verified release binary

- uses: magicdrive/hephaestus@<full commit SHA of v1.2.0> # v1.2.0

Before your build runs, the Action:

  1. reads the release named in action/VERSION of the pinned commit; the
    release workflow refuses a tag that this file does not name;
  2. downloads hephaestus_<version>_<os>_<arch>.tar.gz (.zip on Windows)
    and hephaestus_checksums.txt, from that release only;
  3. checks the archive's SHA-256;
  4. verifies the archive's provenance attestation (gh attestation verify).
    It must have been signed by magicdrive/hephaestus's release.yml on a
    GitHub-hosted runner, for the release tag and, when pinned by SHA, for
    exactly the pinned commit;
  5. extracts only the regular file hephaestus[.exe] at the archive root,
    and checks that it reports the expected version.

Any failure stops the Action. It never falls back to building from source.

As a result:

  • No toolchain is involved. Nothing is compiled, no Go is downloaded or
    set up, and nothing is written to $GITHUB_PATH or $GITHUB_ENV. Your
    build commands use the toolchain your workflow set up. A runner without Go
    can run the Action for a project in any language.
  • The binary is bound to your pin. The attestation proves that the
    binary was built by Hephaestus's release workflow from the commit you
    pinned.
  • Supported platforms are unchanged: Linux, macOS and Windows, on x64
    and ARM64. A runner needs bash, curl, tar and gh, which
    GitHub-hosted runners have.

Reference the Action by a release. Use the full SHA of a release tag's
commit (recommended) or the tag itself. A branch such as @main now fails,
because no release binary belongs to it.

hephaestus publish

The tool that releases Hephaestus is now a command for any project:
make publish vX.Y.Z that runs hephaestus publish. In order, it:

  1. checks that the release can be made: branch, clean worktree, in sync
    with origin, the tag unused, CI green on the commit, rulesets, the
    release environment, the tap, and the generic preflight;
  2. asks for confirmation;
  3. pushes an annotated tag;
  4. follows the release workflow;
  5. verifies the release: assets, checksums, attestations and the tap pull
    request.

See docs/publish.md.

The Action gains command: plan, with the outputs prerelease and tap.
The steps that use the tap token can then run only when the tap is
published. A prerelease never even receives the token.

Fixes (prepared for v1.1.1)

  • The Action no longer changes your Go toolchain. v1.1.0 put its own Go
    first on PATH and exported GOTOOLCHAIN=local, so your Go modules were
    built with Go 1.26.0, or failed to build.
  • PWD is no longer redacted as a credential. Paths under the current
    directory are no longer printed as ***. A value matched only by its
    variable's name is not redacted when it is the path of an existing file or
    directory. Known tokens are always redacted.
  • --fresh removes the previous run's outputs (archives, checksum files
    and formulae) from dist, if they are unchanged and Hephaestus wrote them.

Compatibility

  • Unchanged: configuration, CLI flags, Action inputs and outputs, JSON
    fields and check IDs. New: the publish command, and the Action's plan command with the outputs prerelease and tap.
  • The Action: it must be referenced by a release tag or that tag's
    commit, and it no longer provides Go. Set up your own Go, as the examples
    always did.
  • Template variables: {{ .Env.PWD }} and {{ .Env.OLDPWD }} are now
    visible to templates.

Known limitations

  • Homebrew 4.6 parses the version of archive URLs such as
    tool_1.2.3_darwin_arm64.tar.gz as 64; Homebrew 7.0 parses it
    correctly. brew update updates Homebrew itself.
  • hephaestus check shows the tap pull request branch as a template;
    hephaestus preflight --tag renders it.

Full Changelog: v1.1.0...v1.2.0-rc.1

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 07:03
Immutable release. Only release title and notes can be modified.
550d48f

Hephaestus v1.1.0

Shell completion, for Hephaestus itself and for the formulae it generates.
A minor release: it adds a command and a configuration key, as the
compatibility policy
allows in 1.x.0.

Hephaestus completes itself

hephaestus completion bash|zsh|fish prints a completion script for bash
(3.2 or later), zsh and fish. It completes:

  • commands;
  • each command's flags, in --flag or -flag form. A flag already given
    is not offered again, unless it can be repeated.
  • the fixed values of --format, --template, --phase, --stages and
    --skip, as --flag value or --flag=value. Comma-separated lists
    continue after a comma.
  • file names for --config, --report and --output.

Candidates come from the same flag definitions the commands parse, so
completion offers only what Hephaestus accepts. Generating them reads no
configuration, token or repository and uses no network.

Homebrew users get the completions with this release. The formula
installs the three scripts into Homebrew's completion directories, on a new
install and on brew upgrade from 1.0.0:

  • etc/bash_completion.d/hephaestus
  • share/zsh/site-functions/_hephaestus
  • share/fish/vendor_completions.d/hephaestus.fish

Homebrew installs the files, but each shell must also be set up to load
them. A fish installed by Homebrew needs nothing more. bash needs
bash-completion@2, and zsh needs Homebrew's site-functions on FPATH
before compinit. The
README
has the lines to add, and the commands for installing without Homebrew.

Completions in the formulae Hephaestus generates

brews[].completions installs bash, zsh and fish completions with a
formula, whatever language the CLI is written in. There are two ways to
provide them, and they can be combined for different shells:

  • Generated: the installed executable prints each script at install
    time, through Homebrew's generate_completions_from_executable. You set
    the arguments (args, e.g. [completion]), how the shell is passed
    (shell_parameter_format: arg, flag, none, cobra, clap,
    click or typer) and the shells.
  • Shipped: the scripts are included in the archive (files) and
    installed with bash_completion.install, zsh_completion.install and
    fish_completion.install under the names Homebrew expects.
brews:
  - name: mycli
    completions:
      generate:
        args: [completion]

The configuration is checked before anything is published:

  • the shell names and the format are valid;
  • the executable is installed into bin for every archive;
  • no shell's completion is installed twice;
  • every shipped file exists in each archive.

Every value is written as a Ruby string literal. A formula without
completions is generated exactly as before. Details are in the
configuration reference.

Testing

  • Real shells: bash 5 and macOS's bash 3.2, zsh (through its completion
    system, in a pseudo-terminal) and fish, on Linux and macOS in CI. The
    cases cover commands, flags, values, --flag=value, comma lists, file
    names, a quoted value with a space, an already given flag, -- and an
    invalid shell.
  • Homebrew, with a shell-script fixture whose bash and zsh completions
    are generated and whose fish completion is shipped: brew style,
    brew audit --strict, install, the three files in place, completion in
    the three shells, brew test, uninstall.
  • Homebrew, upgrading Hephaestus itself: v1.0.0 is installed from the
    production tap's formula, then upgraded with brew upgrade to a formula
    generated from this release's hephaestus.yaml. The three completion
    files appear and match hephaestus completion, completion works in the
    three shells with the installed binary, and brew test and uninstall
    pass.

Compatibility

  • Additions only: the completion command and the brews[].completions
    key. No existing command, flag, configuration key, Action input or
    output, JSON field or check ID changed. hephaestus help lists two more
    commands (completion, help).
  • A configuration that uses brews[].completions is rejected by
    Hephaestus 1.0.0 and older, because unknown keys are errors.

Limitations

  • Tags, repositories and other free-form values are not completed.
  • PowerShell completion is not provided. generate installs only bash,
    zsh and fish, including with shell_parameter_format: cobra or typer,
    whose Homebrew default would add PowerShell.
  • A custom shell_parameter_format prefix string is not supported; use
    extra_install for such a case.

Full Changelog: v1.0.0...v1.1.0

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 03:55
Immutable release. Only release title and notes can be modified.
b2ea250

Hephaestus v1.0.0

Hephaestus 1.0 is release automation for command-line tools written in any
language. From one hephaestus.yaml it turns your executables into
reproducible archives, a checksum file and a GitHub Release, then delivers
a Homebrew formula to your tap as a pull request. You choose how the
executables are made: Hephaestus runs your build command per target, or
imports binaries that are already built. It is a single Go binary and a
composite GitHub Action.

1.0 is about releasing without surprises:

  • Check before you tag. hephaestus preflight checks the repository,
    the configuration, the release notes and GitHub (rulesets, environment,
    CI, existing releases) and tells you what would block the release.
  • Never half publish. A release is created as a draft. It is published
    only after every asset has been uploaded and verified against the local
    build.
  • Never destroy what is already public. A published release is never
    edited, its assets are never replaced or deleted, and Hephaestus never
    deletes a release.
  • Know where you stand after a failure. hephaestus status reads, and
    never writes, what a release run left behind and names the next safe
    action. Re-running resumes from that point.

From 1.0.0 Hephaestus follows Semantic Versioning, with the stable surface
defined in the
compatibility policy.

Key features

Release pipeline

  • Build: your own command per target, or prebuilt binaries imported from
    a path template.
  • Archive: tar.gz and zip archives with an explicit file list. They
    are deterministic: fixed timestamps, order and modes, so the same inputs
    give the same bytes. A noarch archive (targets: [{os: all, arch: all}])
    is one archive for every platform, for scripts.
  • Checksum: one SHA-256 file over the archives.
  • GitHub Release: draft first, idempotent and compatible with immutable
    releases. Every asset is compared by digest; make_latest and
    prerelease: auto are supported.
  • Homebrew formula: generated from the manifest of what was built,
    uploaded and checksummed, in current Homebrew style (on_macos,
    on_linux, on_arm, on_intel), or a single url for a noarch archive.
  • Tap pull request: the formula is delivered to your tap as a pull
    request, never by force-push. Prereleases can skip the tap
    (skip_upload: auto).

Safety

  • Strict preflight: hephaestus preflight --tag vX.Y.Z --strict runs 26
    read-only checks. It is also available in the Action (command: preflight), with a JSON report.
  • Release notes validation: the notes file and the changelog must name
    the tag as a whole version. v1.2.0 is not satisfied by v1.2.0-rc.1.
  • Published releases are left as published: a published release's
    title, notes and prerelease flag are never edited; a difference is only
    reported. Its assets are never replaced or deleted, on mutable releases
    too.
  • Conflict detection: an asset that differs from the local build stops
    the run. So does a tag that points to another commit, or a release for the
    same tag created by a concurrent run; nothing is overwritten or deleted.
  • Re-reading before every write: a draft is read again right before it
    is published, before one of its assets is deleted, and before its title or
    notes are updated. A release that was published in the meantime is left
    alone.
  • Safe recovery: a lost API response is followed by a lookup, never by
    a blind second create, and an interrupted run resumes from where it
    stopped. The recovery guide
    covers 12 failure scenarios.
  • Read-only status: hephaestus status --tag vX.Y.Z reports one of
    not-started, tagged, draft, published-incomplete, conflict,
    published or completed. It compares assets by digest and finds each
    formula's tap pull request. Use --format json for scripts.
  • Token hygiene: publishing tokens are removed from the build's
    environment and redacted from every output.

Artifact quality

  • Reproducible: rebuilding the v1.0.0-rc.2 tag from a clean checkout
    reproduced all of its published archives byte for byte.
  • Checksums and attestations: every archive and the checksum file carry
    a build provenance attestation from the release workflow
    (gh attestation verify <file> --repo magicdrive/hephaestus).
  • Formula install collision detection: a formula that would install
    the same file twice, a directory and a file inside it, or two files to
    one destination is rejected when it is generated. Such a formula could
    never install.

Developer experience

  • hephaestus init --template go|rust|script|prebuilt writes a
    starting hephaestus.yaml that passes check and run --snapshot, and
    whose formula passes brew audit --strict. For Rust, run --snapshot
    needs the template's Rust targets installed. Prereleases skip the tap.
    init never overwrites a file.
  • run --snapshot builds archives and checksums without a tag. Release,
    formula and tap stages need a tag and are skipped.
  • Composite GitHub Action: builds Hephaestus from the commit you pin,
    and runs run, check, any single stage, or preflight.
  • Documentation: a CLI reference,
    configuration, archives, the GitHub Action, recovery, GoReleaser
    compatibility and migration.

Supported platforms

Release archives are published for six platforms. What was verified differs
by platform:

Platform Archive Tests from source in CI Published binary run (v1.0.0-rc.2)
macOS amd64 yes weekly extended CI (macos-15-intel) yes: verify-release workflow; full local dogfooding (templates, Homebrew install)
macOS arm64 yes every change (macos-latest) yes: verify-release workflow
Linux amd64 yes every change yes: verify-release workflow; container
Linux arm64 yes every change (ubuntu-24.04-arm) yes: verify-release workflow; container
Windows amd64 yes every change yes: verify-release workflow
Windows arm64 yes — yes: verify-release workflow (windows-11-arm)

"Published binary run" means the archive was downloaded from the release
and checked against the checksum file, then version, --help, init and
check were run. Generated formulas are installed and tested with Homebrew
on macOS and Linux in CI.

Compatibility with v0.2.0

  • Configuration: v0.2.0 configurations load unchanged; Hephaestus's own
    and magicdrive/t's are kept as regression fixtures.

  • CLI, Action, check IDs, JSON: changes are additions only:

    • commands status and init;
    • preflight --report;
    • Action inputs strict and offline, and outputs preflight-status,
      preflight-exit-code and preflight-report;
    • the check config.release-notes;
    • configuration preflight.changelog and noarch targets.

    Nothing was removed or renamed. The preflight JSON report keeps
    schema_version: 1.

Some behaviors changed, each to protect what is already published, or to
reject something that could never have worked:

  • A published release's title, notes and prerelease flag are never
    edited.
    v0.2.0 updated them on a re-run. A difference is now a warning,
    and the formula and tap can still be resumed.
  • A published release's assets are never replaced or deleted.
    replace_existing_assets now applies to drafts only. v0.2.0 replaced a
    differing asset of a published mutable release when it was set. An
    incomplete asset on a published release is reported, not removed.
  • A draft that lost a concurrent race is not deleted. When two runs
    race for a tag, the losing run stops with a conflict and leaves its empty
    draft for you to delete.
  • run --snapshot does not run the formula stage, just as it already
    skipped release and tap: a formula needs a tag. Naming formula, release or
    tap in --stages together with --snapshot is an error.
  • A formula that installs the same file twice is rejected when it is
    generated, as are the other install collisions above.

Known limitations

  • Rust cross-builds: the Rust template builds each target with cargo build --target, so every target needs its Rust target and a linker
    installed. Alternatively, build on one runner per platform and import the
    binaries as prebuilt. The template was verified for the host target
    (x86_64-apple-darwin) only; its other targets were not built.
  • Recovery is tested against a fake GitHub API. Failures were injected
    there: lost responses, concurrent runs, digest conflicts and failed tap
    pull requests. Against the real GitHub API, the normal release path is
    exercised by every release.
  • No conditional updates on GitHub: Hephaestus re-reads a release right
    before each write. Another writer can still act in the single API call
    between that read and the write. The release workflow serializes runs per
    tag (concurrency), which avoids this in normal use.
  • Incomplete uploads wait: if a failed upload leaves an incomplete
    asset, that asset is deleted only when it is more than 10 minutes old, so
    that an upload by another run in progress is never removed. A re-run after
    that time continues.
  • Prereleases and the tap: brews[].skip_upload defaults to false, so
    a prerelease is published to the tap unless you set skip_upload: auto
    (as init does) or true. The default changes only in a major release.

Installation

  • Homebrew: when this release is published, the 1.0.0 formula goes to
    magicdrive/homebrew-tap as
    a pull request, which is merged after review. From then on,
    brew install magicdrive/tap/hephaestus installs 1.0....
Read more

v1.0.0-rc.2

v1.0.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 02:27
Immutable release. Only release title and notes can be modified.
1b462d3

Hephaestus v1.0.0-rc.2

The second release candidate of Hephaestus 1.0. It fixes what dogfooding
v1.0.0-rc.1 found (snapshots of init configurations, the init formula
test, a false failure in make publish's verification) and hardens
concurrent and repeated releases: Hephaestus no longer deletes a release
in any situation. A prerelease: it is not marked latest and does not update
the Homebrew tap; v0.2.0 stays the latest stable release.

For everything new in 1.0 (preflight in the Action, status, init,
noarch archives, the recovery guide, the compatibility policy), see the
v1.0.0-rc.1 notes
and the changelog.

Fixed since rc.1

  • Snapshots of init configurations: run --snapshot skips the formula
    stage as well as release and tap (all three need a tag). With brews
    configured, which every init template has, rc.1's run --snapshot
    failed with "a tag is required". Naming one of them in --stages with
    --snapshot is an error.
  • init formula test: the templates' test is
    system bin/"NAME", "--version"; rc.1's "#{bin}/NAME" failed
    brew audit --strict.
  • make publish verification: right after the release run, GitHub can
    report the job's steps only partially. The tap-step check now reads them
    again (6 reads, 20 seconds apart) and, if they are still incomplete,
    reports the check as inconclusive (exit 3, re-run make publish-watch)
    instead of failing a correct release. A step with the wrong result is
    still a failure. rc.1's make publish exited 1 for this reason; the
    release itself was correct.

Changed: release safety

  • No release is ever deleted. When two runs race for one tag, the run
    whose new draft loses (to another run's lower-id draft, or to a published
    release) now stops with a conflict and leaves its empty draft for you to
    delete. rc.1 deleted that draft, but once a draft is listed another run
    may already be using it. The GitHub client has no call to delete a
    release.
  • The draft is re-read right before it is published. A release
    published meanwhile is not edited; a draft whose assets changed (added,
    removed, incomplete or different) or that lost to another release is not
    published. A draft is also re-read before one of its assets is deleted
    and before draft: true updates its title or notes, and after publishing
    each asset's digest is checked before its URL is recorded in the
    manifest.
  • A published release's assets are never replaced or deleted, on
    mutable releases too. replace_existing_assets now applies to drafts
    only; an incomplete asset on a published release is reported, not
    removed. (rc.1 and v0.2.0 replaced a differing asset of a published
    mutable release when replace_existing_assets: true was set.)
  • An incomplete asset seen right after a failed upload is deleted only
    when it is older than the 10-minute grace period: a recent one cannot be
    told apart from another run's upload in progress, so the asset fails and
    a later re-run continues.

Testing

  • Regression tests for each fix: snapshots of every init template (Go,
    script and prebuilt in CI; Rust when cargo and the template's four
    targets are installed), the formula test line, and lagging step data
    (complete later; never complete is inconclusive).
  • Recovery and concurrency tests against a fake GitHub API: the losing
    draft (to a lower-id draft and to a published release) deletes nothing
    and a re-run continues with the winner; a lost create reusing another
    run's draft; the pre-publish re-read (published meanwhile, asset added,
    changed or removed, another release published); a draft published
    before an asset delete or a draft: true update; another build's
    content published meanwhile; starters after a failed
    upload; published assets never replaced or deleted. Plus the rc.1 suite:
    lost create, upload and publish responses, digest conflicts, tag pointing
    to another commit, immutable re-runs, tap pull request failure after
    publishing.

Compatibility

  • v0.2.0 and rc.1 configurations load unchanged. No configuration key,
    CLI flag, Action input or output, or check ID was removed or renamed.
  • Two behaviors are narrower, both for safety: replace_existing_assets
    no longer touches a published release, and a lost concurrent race stops
    with a conflict instead of deleting a draft.
  • brews[].skip_upload still defaults to false; init templates set
    skip_upload: auto.

Limitations

  • Windows arm64 archives are built but not run (no CI runner).
  • The Rust template does not cross-compile by itself: each target needs its
    Rust target and a linker, or a build per runner imported as prebuilt
    binaries.
  • Recovery is tested against a fake GitHub API; against the real API only
    the normal release path is exercised. The attestation step is covered by
    real releases, not by a test.
  • GitHub has no conditional update: between a re-read and the write that
    follows it (one API call), another writer could still change the
    release.
  • A transient upload failure that leaves an incomplete asset now fails the
    run instead of retrying at once; re-run after 10 minutes.

Full Changelog: v1.0.0-rc.1...v1.0.0-rc.2