Build Workflow Automation
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Build Workflow Automation in the numan-plugins repository serves as a CI/CD pipeline feeder for the official numan registry. Its primary purpose is to bridge the gap for Nushell plugins that only ship as source code by providing a standardized, automated process for cross-compiling, packaging, and publishing signed binary artifacts. This system ensures that plugins are built from immutable upstream commits, verified for provenance, and delivered as hash-pinned archives compatible with the numan installation process.
Sources: README.md:1-17, REVIEW.md:20-30
The automation lifecycle is strictly controlled, employing manual dispatch triggers to maintain a clear trust boundary. While the pipeline handles binary generation and hosting, it never manages signing keys; the final trust root signing is deferred to the numan-registry intake process.
Sources: README.md:19-27, REVIEW.md:32-41
Build Lifecycle and Orchestration
The build process follows a "Draft-then-Finalize" transaction model to ensure release immutability and asset integrity. A release is only made public after the complete set of platform-specific binaries is verified.
High-Level Workflow
The automation flow involves manifest validation, cross-platform compilation, deterministic packaging, and a multi-step release transaction.
This diagram illustrates the sequential progression from manual trigger to the emission of a registry-compatible specification.
Sources: README.md:52-64, scripts/release_transaction.py:82-125, REVIEW.md:43-47
Manifest Management
The manifest.json serves as the single source of truth for the build matrix. It defines which plugins are active, their immutable source_commit, human-facing tag, and target exclusions.
| Field | Description |
|---|---|
repo | Upstream GitHub repository (owner/name). |
source_commit | Immutable 40-character SHA of the source code. |
tag | Human-facing version tag (e.g., v1.4.15). |
nu_version | Compatibility range for Nushell. |
default_targets | Global list of supported Rust target triples. |
exclude_targets | Target triples to skip for a specific plugin entry. |
Sources: manifest.json:4-46, scripts/validate_manifest.py:15-27
Security and Safety Gates
The automation enforces strict safety invariants to prevent accidental overwrites and maintain the trust boundary.
Safety Checks
- Manual Dispatch Only: Publication builds run only via
workflow_dispatchwith a non-empty package list. Pushes and PRs are restricted to validation only. - Release Immutability: The script
ensure_release_absent.pyfails the workflow if a Git tag or release already exists for the targeted version. - Tag-to-Commit Verification:
validate_manifest.py --verify-upstreamensures the pinnedsource_commitmatches the upstreamtagbefore building. - Pinned Actions: All GitHub Actions references are pinned to 40-character commit SHAs to prevent supply chain attacks.
Sources: REVIEW.md:8-18, scripts/ensure_release_absent.py:22-54, scripts/test_workflow_safety.py:53-60, scripts/test_workflow_safety.py:64-70
Trust Boundary Architecture
This diagram shows the separation between build/hosting and the official signing boundary.
Sources: README.md:19-27, REVIEW.md:32-35
Packaging and Specification Generation
The system normalizes build outputs into deterministic archives to ensure stable hashes for registry pinning.
Deterministic Packaging
The package_plugin.py script creates .tar.gz for Unix-like systems and .zip for Windows. It uses a fixed timestamp (1980-01-01 UTC) and normalized file modes (0o755) so that rebuilding from the same source produces identical byte-for-byte archives.
Sources: scripts/package_plugin.py:28-66
Registry Spec Emission
After all matrix jobs complete, gen_spec.py aggregates the results into a kind: binary specification.
# Logic for building the spec (truncated)
def build_spec(entry, packaged_rows, release_base, expected):
targets = {}
for r in packaged_rows:
targets[r["target"]] = {
"url": f"{release_base}/{r['filename']}",
"executable_path": r["exe"],
}
return {
"name": entry["name"],
"version": entry["version"],
"source": {
"git": f"https://github.com/%7Bentry%5B%27repo%27%5D%7D%22%2C
"rev": entry["source_commit"],
},
"artifact": { "kind": "binary", "targets": targets }
}
Sources: scripts/gen_spec.py:108-149
Release Transaction Management
The release_transaction.py utility manages the lifecycle of a GitHub release as an atomic operation.
- Claim: Creates a Git tag and a draft release associated with a specific commit.
- Upload: Uploads all platform binaries to the specific
release_id(not the tag) to avoid race conditions. - Finalize: Compares the local asset set (size and SHA256) against the remote assets on GitHub before flipping the release from
drafttopublic. - Cleanup: In case of failure, it deletes the draft release and the associated tag if they haven't been published yet.
Sources: scripts/release_transaction.py:82-125, scripts/release_transaction.py:164-199
Conclusion
The Build Workflow Automation system provides a robust, safety-first pipeline for distributing Nushell plugins. By enforcing immutability, deterministic builds, and clear trust boundaries, it ensures that the numan ecosystem remains secure while significantly expanding the availability of pre-compiled binaries for end-users.