IMPORTANT: Developer documentation for the current development branch. This content is unreleased, may change without notice, and must not be treated as Buildish release documentation.

Direct Release

Publish one exact final GitHub release without creating a release candidate.

Direct Release

The direct lifecycle turns one exact source revision into the final tag and final GitHub Release in one manually dispatched workflow. It does not create a candidate, require a vote, or introduce a candidate-shaped placeholder in final release state.

“One click” means one workflow dispatch. The workflow still separates resolution, source checks, tag creation, staging, verification, publication, and manifest attachment so a component can place policy boundaries between those jobs.

Minimal configuration

 1component:
 2  id: example
 3  display_name: Example
 4versioning:
 5  scheme: semver
 6  final_tag_template: "v{version}"
 7source:
 8  selection: explicit-ref-or-default-branch
 9  default_branch: main
10  require_default_branch_ancestor: true
11  snapshot:
12    mode: platform-generated
13  checks:
14    platform: github
15    trusted_workflow_path: .github/workflows/ci.yml
16    trusted_event: push
17    trusted_branch: main
18    required:
19      - Required Checks
20lifecycle:
21  mode: direct
22# Optional. Omit this block to publish without custom notes.
23release_notes:
24  path_template: release-notes/{version}.md
25artifacts:
26  produced: []
27  checksums: []
28publication:
29  authoritative:
30    kind: github-release
31    repository: example/example
32tags:
33  final_mode: exact-source-commit
34  moving: []
35policy_profiles: {}

This example treats GitHub as authoritative and relies on the source archives GitHub generates from the immutable final tag. It publishes no separately built source artifact. Because release_notes is configured, the selected source commit must contain a non-empty UTF-8 Markdown fragment at the rendered path. If the block is omitted, the release body contains only generated identity and asset information.

Dispatch

The repository’s generic reference workflow lives at buildish-release-tooling/harness/workflows/release-direct.yml. A component can copy that fixture into its own .github/workflows/release-direct.yml and dispatch it with:

  • version: the exact final version, for example 1.2.3;
  • source_ref: an optional exact branch, tag, or commit allowed by the configured source-selection policy.

When source_ref is omitted, the example configuration resolves main. Resolution records the exact commit; later jobs use that commit identity rather than re-resolving a moving branch. With the ancestry policy above, an explicitly supplied commit must also already be reachable from main. The trusted-workflow fields prevent a same-name check from another workflow or event from satisfying the source gate.

Workflow phases

  1. resolve creates DirectReleaseState with the release identity, exact source commit, final tag, artifact policy, and any configured release notes read from that commit.
  2. verify-source checks the configured GitHub check-run/status requirement on that exact commit.
  3. create-final-tag creates or revalidates the immutable final tag.
  4. stage creates or completes the draft final GitHub Release.
  5. verify-final compares the observed tag, release metadata, and assets with the resolved state.
  6. publish makes the exact release public or confirms that an identical publication already exists.
  7. manifest creates release-manifest-v1.json, validates both workflow handoff digests, attaches the manifest without clobbering an existing asset, and revalidates the release.

Same-run JSON files move through GitHub workflow artifacts. Every privilege-bearing consumer checks the SHA-256 emitted by the producing job before acting.

Reruns and conflicts

The mutation commands are convergent for identical state:

  • an identical tag, draft release, public release, or manifest attachment reports an already-complete outcome where applicable;
  • an existing tag at another commit, mismatched release body, unexpected asset, size mismatch, or digest mismatch fails closed;
  • asset upload never uses clobber behavior.

The workflow uses non-canceling concurrency keyed by repository and version. A second run waits instead of cancelling a run that may already have changed external release state.

Variations

  • Use a separately built and signed source artifact when the hosting-platform snapshot is not sufficient.
  • Add environment: to the jobs selected by project policy; see Workflow composition.
  • Create a release branch separately with create-release-branch when a component needs maintained release lines. The direct lifecycle itself does not require one.
  • Use the candidate lifecycle when publication must wait for an external approval or vote over an exact candidate.

The repository’s release-testpypi.yml is another direct-lifecycle composition. It resolves and retains DirectReleaseState, but deliberately exercises only the secondary TestPyPI publication: it creates no final tag or GitHub Release. It is a package-publication rehearsal, not a substitute for the generic direct GitHub workflow’s final-tag and final-manifest phases.