Skip to content

ci(release): runner-native release workflow + actionlint#710

Merged
nekomoto911 merged 6 commits into
Galxe:mainfrom
nekomoto911:release-workflow-container
May 13, 2026
Merged

ci(release): runner-native release workflow + actionlint#710
nekomoto911 merged 6 commits into
Galxe:mainfrom
nekomoto911:release-workflow-container

Conversation

@nekomoto911

@nekomoto911 nekomoto911 commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

New .github/workflows/release.yml for shipping gravity_node / gravity_cli binaries when a gravity-mainnet-* or gravity-testnet-* tag is pushed. Workflow runs natively on the self-hosted Ubuntu 24.04 runner — no container.

Decision: align production node OS with the runner OS, not the other way around. Runner is already Ubuntu 24.04 (glibc 2.39); the planned production-VM Packer image is being moved to ubuntu-2404-lts-amd64 to match. Native runner build keeps the workflow simple (no per-run apt-install, target/ cache reuse) at the cost of requiring runner state stays in sync.

The earlier containerized direction (rust:1.93.0-bullseye) is preserved in git history (abf88dd1..af3e3711) and can be brought back with git revert d75fc1f2 if the OS migration slips.

Changes

.github/workflows/release.yml

  • Native build on [self-hosted, linux] — no container: field.
  • Trigger: push: tags: ['gravity-mainnet-*', 'gravity-testnet-*'] and pull_request: paths: ['.github/workflows/release.yml'].
  • Show build environment step logs /etc/os-release, ldd --version, rustc --version, cargo --version every run, so runner OS drift surfaces in the run record.
  • Assert rustc matches rust-toolchain.toml so toolchain drift fails fast with a clear remediation message.
  • PR-trigger path uploads the binary as a 7-day release-dryrun-<sha> artifact for reviewer inspection; real GH-release upload is skipped on PR events.
  • Tag-push path requires the GitHub Release to already exist (created via UI with human-authored notes); workflow only attaches binaries via gh release upload --clobber. No auto-create.
  • Resolve tag step rejects any tag that doesn't match gravity-{mainnet,testnet}-*.

.github/workflows/lint-workflows.yml

  • actionlint@1.7.7 on PRs touching .github/workflows/**. Shellcheck integration disabled (-shellcheck=) — pre-existing SC2086 / SC2053 noise in gate.yml / rust-ci.yml is out of scope; re-enable after a separate cleanup PR.

Runner prerequisites (install once on the runner host)

  • Rust toolchain matching rust-toolchain.toml (currently 1.93.0)
  • gh CLI (via cli.github.com apt repo) — uses workflow-injected GH_TOKEN, no gh auth login needed
  • Build deps: clang llvm libclang-dev build-essential pkg-config libssl-dev libudev-dev

These are installed on gravity-grafana-20250806-070307.

Test plan

  • Lint workflows / actionlint green
  • Release / Build and release dry-run green (run 25787189549, attempt 2, 10m57s)
  • Dry-run artifact verified:
    • objdump -T gravity_node | grep GLIBC_ → max GLIBC_2.39 (matches Ubuntu 24.04)
    • sha256sum -c matches workflow output
    • ./gravity_node --version on runner shows non-empty build_commit_hash and build_time (read by shadow_rs at compile time from the checkout; PR path shows merge sha, tag-push path will show tag commit)
  • Pending Packer image rollout: do not push a real gravity-mainnet-* tag until staging-mainnet VMs are running Ubuntu 24.04.

Follow-ups (separate PRs)

  • Production-VM Packer image migration (ubuntu-os-cloud/ubuntu-2404-lts-amd64). Runner-native binaries depend on GLIBC_2.39; older OS targets will refuse to start.
  • Shellcheck cleanup in gate.yml / rust-ci.yml, then drop -shellcheck= from lint-workflows.yml.

Notes for reviewers

  • The build-info crate has a runtime std::env::var(\"GIT_SHA\") override path (crates/build-info/src/lib.rs:84-101). It looks like it could be wired to GitHub Actions context to populate build_branch / build_commit_hash from the env, but the override is runtime-only — production systemd units won't set those env vars, so it's a no-op for shipped binaries. Compile-time shadow_rs already fills build_commit_hash and build_tag correctly on tag-push builds; build_branch is empty under detached-HEAD checkout (acceptable for tag releases). Fixing build_branch properly is a crate change (option_env! + cargo:rerun-if-env-changed), tracked separately.
  • An earlier attempt added these env vars to the workflow (commit 03fcff29, since reset) before this was understood; the commit has been dropped via force-push.

@nekomoto911 nekomoto911 changed the title ci(release): containerize build for glibc compat, add PR dry-run + actionlint ci(release): runner-native build + PR dry-run + actionlint May 13, 2026
Richard1048576
Richard1048576 previously approved these changes May 13, 2026

@Richard1048576 Richard1048576 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

…tionlint

Self-hosted runner is Ubuntu 24.04 (glibc 2.39); staging-mainnet nodes are
Debian 11 (glibc 2.31). Binaries built directly on the runner require
GLIBC_2.32+ symbols and fail to start on target with `GLIBC_2.34 not found`.

Changes to .github/workflows/release.yml:
- Build inside container image, default `rust:1.93.0-bullseye` (glibc 2.31),
  overridable via `vars.RELEASE_BUILD_IMAGE` for future OS bumps.
- Assert `rustc --version` matches `rust-toolchain.toml` so image drift
  surfaces as a clear error instead of silently building with the wrong
  toolchain.
- Install gh CLI inside the container (the rust:* base image lacks it).
- Require the GitHub Release to exist before tag push (UI-driven release
  notes flow); CI now only uploads assets and fails fast if no release.
- Drop workflow_dispatch and the auto-create-release fallback — single
  trigger source avoids the dispatch-vs-tag commit mismatch and keeps
  release notes human-authored.
- Add a pull_request trigger scoped to .github/workflows/release.yml so
  changes to this file run a full container build as a dry-run; binaries
  are uploaded as a 7-day artifact for reviewer inspection. Real upload
  step is skipped on PR events.

New .github/workflows/lint-workflows.yml:
- Runs actionlint@1.7.7 on PRs touching any .github/workflows/** file.
- gh v2.62.0 was never published; download 404'd and broke the dry-run.
  Bump to v2.92.0 (current latest) and add --retry 3 for transient
  self-hosted-runner network blips.
- actionlint's bundled shellcheck integration treats info-level issues
  (SC2086 unquoted vars, SC2053 unquoted RHS) as errors, which fails on
  pre-existing scripts in gate.yml / rust-ci.yml that are out of scope
  for this PR. Disable shellcheck via `-shellcheck=`; the workflow still
  validates yaml + expression syntax. Re-enable after a separate cleanup.
The gh CLI source repo is `cli/cli`, not `cli/gh` — the prior URL 404'd on
every release tag, not just 2.62.0. Both previous failed dry-runs were
hitting this same path bug, masked as "version doesn't exist".

Verified `https://github.com/cli/cli/releases/download/v2.92.0/gh_2.92.0_linux_amd64.tar.gz`
returns HTTP 200.
bindgen needs libclang at build time; the bare `rust:1.93.0-bullseye`
image doesn't ship it, so the previous dry-run died at:
  thread 'main' panicked at bindgen-0.72.1/lib.rs:616
  Unable to find libclang: ...

Mirror the apt list from gravity_e2e/build_docker.sh (the known-working
in-container build env), minus python/test packages. On bullseye the
`clang` package does not pull the unversioned libclang.so symlink that
clang-sys searches for, so libclang-dev is added explicitly (bookworm
gets it transitively, which is why e2e didn't need to call it out).
Decision change: instead of containerizing the build to match production
glibc, the runner OS itself will be aligned to production OS. Native
build is simpler — no container plumbing, no apt-install on every run,
target/ cache reuse between builds.

- Drop the `container:` block and the `vars.RELEASE_BUILD_IMAGE` override.
- Drop the in-container `Install build dependencies` step (apt-get clang/llvm/...).
- Drop the in-container `Install gh CLI` curl-tarball step.
- Drop the `cli/gh → cli/cli` URL fix (step no longer exists).
- Add a `Show build environment` step that logs runner os-release / ldd /
  rustc / cargo so any future runner OS drift from production is visible
  in the run record.
- Keep the `Assert rustc matches rust-toolchain.toml` check (rephrased
  for the native-runner context).
- Keep PR dry-run trigger + artifact upload (still valuable for review).

Invariant now: runner OS must equal production node OS. Currently
staging-mainnet is Debian 11 / glibc 2.31, runner is Ubuntu 24.04 /
glibc 2.39 — that gap must be closed on the runner side before this
workflow can produce shippable binaries.

Runner prerequisites (install once on the runner host):
  - rust toolchain per rust-toolchain.toml
  - gh CLI
  - clang llvm libclang-dev build-essential pkg-config libssl-dev libudev-dev
Testnet releases follow the same publish flow as mainnet — human builds
the release notes in GitHub UI, publishes, tag push triggers binary
upload via this workflow.

Trigger filter and the Resolve tag case-guard updated symmetrically so
the validation message stays accurate.

@Richard1048576 Richard1048576 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nekomoto911 nekomoto911 changed the title ci(release): runner-native build + PR dry-run + actionlint ci(release): runner-native release workflow + actionlint May 13, 2026
@nekomoto911 nekomoto911 merged commit e8594e7 into Galxe:main May 13, 2026
9 of 11 checks passed
@nekomoto911 nekomoto911 deleted the release-workflow-container branch May 13, 2026 12:29
nekomoto911 pushed a commit that referenced this pull request May 14, 2026
## Summary

The `Release` workflow in PR #710 builds `gravity_node` with `default =
[]` features, so the released v1.6.0 binary (commit `e8594e7b`) panics
on every Staging Mainnet node whose identity is sourced from GCP Secret
Manager:

\`\`\`
panicked at .../config/network_config.rs:281:25:
load identity blob for peer_id from GCP secret
projects/mainnet-storage/secrets/staging-vfn-1-identity/versions/latest:
GCP Secret Manager support not compiled in. Rebuild aptos-config with
\`--features gcp-secret-manager\` (or enable the forwarding feature on
the
downstream crate, e.g. \`gaptos/gcp-secret-manager\`).
\`\`\`

Observed live on `vfn-1` and `vfn-2` after the v1.6.0 rollout
(2026-05-13 ~15:37 UTC): both had `NRestarts > 2700`,
`ActiveState=activating / auto-restart`, every ~5s, with the panic above
logged in `journalctl`. \`core-1..4 / rpc-1\` showed the same v1.6.0
binary on disk but had not yet started (separate \`/data\` / unit
ordering issue — not in this PR's scope).

## Fix

Split the build step. \`gravity_node\` is now built with:

\`\`\`bash
cargo build --profile quick-release -p gravity_node --bin gravity_node
--features gcp-secret-manager
\`\`\`

\`gravity_cli\` is built in its own step (the feature is declared
per-package on \`bin/gravity_node/Cargo.toml\`; cargo's workspace-level
\`--features\` would not resolve it for the combined multi-bin build).

## Test plan

- [x] PR-trigger dry-run (this PR) — confirm both \`Build gravity_node\`
and \`Build gravity_cli\` steps succeed and the resulting binary is
uploaded as \`release-dryrun-<sha>\` artifact.
- [x] Local build on LA Private Mainnet build host
(\`gravity-mainnet-node-la-0\`, Debian 11, rustc/cargo 1.93.0 matching
\`rust-toolchain.toml\`) using the same cargo invocation: succeeded in
**3m 15s**, sha256
\`921dd5bfc8ee11ba0360c5343f105b579d2e3aed17ef29b7ab02513cdf6d278d\`.
(LA artifact dynamic-links \`libssl.so.1.1\` so it's only usable on
Debian 11 / Private Mainnet, but this validates that the cargo
invocation works and gravity_node compiles cleanly with the feature on.)
- [ ] After merge: cut a new release tag (e.g.
\`gravity-mainnet-v1.6.1\`) via the Releases UI — the workflow only
attaches assets to a pre-existing release, so create the release with
notes first, then publish to push the tag. Release notes can simply
reference this PR.
- [ ] Verify the new release binary on a Staging Mainnet VFN (e.g.
\`vfn-1\`): the panic at \`network_config.rs:281\` should be gone,
\`journalctl -u gravity-vfn-1.service\` should advance past the
identity-load step.

## Why this slipped through v1.6.0

The first end-to-end use of the \`Release\` workflow in PR #710 was the
v1.6.0 tag itself, which happened after the Packer image switch to
Ubuntu 24.04. The dry-run did exercise build + upload-artifact, but did
not exercise running the binary against a node that uses \`source =
"gcp_secret"\` identities — that path only lives in the deployed Staging
Mainnet / mainnet config, and isn't covered by SDK unit/integration
tests. Consider adding a smoke job that runs \`gravity_node --help\` (or
a tiny config-load test) under \`--features gcp-secret-manager\` so
future regressions are caught at PR time, not at deploy time.

## References

- v1.6.0 panic backtrace captured live on \`vfn-1\` 2026-05-14 ~00:21
UTC
- Feature declaration: \`bin/gravity_node/Cargo.toml:13-21\`
(\`gcp-secret-manager = ["gaptos/gcp-secret-manager"]\`), explicitly
notes \`Off by default to keep the HTTP/TLS stack out of the default
build graph; enable when cluster.toml configures any node with
\`identity = { source = "gcp_secret", ... }\`.\`
- Original release workflow: PR #710 (commit \`e8594e7b\`)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
keanji-x added a commit that referenced this pull request May 14, 2026
)

## Summary

Removes the optional GCP Secret Manager build mode in `bin/gravity_node`
and reverts the workflow split that #713 forced as a band-aid.

- **`bin/gravity_node/Cargo.toml`** — drop the local
`gcp-secret-manager` forwarding feature. Instead pull the `gaptos` dep
with that feature always enabled (`features =
[\"gcp-secret-manager\"]`). `reqwest` + `base64` become a permanent part
of every `gravity_node` build.
- **`.github/workflows/release.yml`** — collapse the two-step
`gravity_node` / `gravity_cli` build back to the single pre-#713 step.
With the feature permanently on at the dep level, the explicit
`--features` flag (and the `-p gravity_node` scoping it required) is
redundant.

No upstream `gravity-aptos` change: the feature flag on `aptos-config`
is left in place; we just unconditionally enable it from this side.
`cargo` pin is unchanged at `e9544c8`.

## Why

The feature was a footgun. v1.6.0 shipped with `default = []` and the
Staging Mainnet VFNs auto-restart-looped on the panic in
`aptos-config/.../network_config.rs:281` because their identity source
is `gcp_secret`. #713 patched the workflow but the underlying design —
an optional build flag that every production deploy must remember to
enable — is what should go.

`reqwest` is already in the workspace via `gravity_cli`'s upload-side
`secret_manager.rs`, so making it unconditional in `aptos-config` (via
the gaptos feature being always on) does not introduce a new transitive
dep at workspace scope.

## Test plan

- [x] Local \`cargo check -p gravity_node --profile=quick-release\`
(with `RUSTFLAGS=\"--cfg tokio_unstable\"`) passes — finished in 2m27s.
Confirms `gaptos = { features = [\"gcp-secret-manager\"] }` resolves and
pulls `reqwest` + `base64` into the build graph.
- [ ] CI release workflow dry-run (PR-trigger) — the workflow attaches
\`release-dryrun-<sha>\` artifact when triggered on a PR. Single \`Build
gravity_node and gravity_cli\` step should succeed.
- [ ] After merge: cut a new release tag (e.g.
\`gravity-mainnet-v1.6.2\`), then verify on a Staging Mainnet VFN that
the panic at \`network_config.rs:281\` is gone and \`journalctl -u
gravity-vfn-1.service\` advances past the identity-load step.

## References

- #713 — the workflow-level fix this supersedes
- #710 — original release workflow that shipped without the feature
- #688 / #695 — the cluster.toml-side wiring that originally introduced
the optional build mode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants