Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions .github/workflows/formal-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ jobs:
# toolchain is identical), so the first shard to finish install
# warms the cache for all the others on subsequent runs.
kani-shard:
name: kani-shard / ${{ matrix.crate }} :: ${{ matrix.harness }}
name: "kani-shard / ${{ matrix.crate }} :: ${{ matrix.harness }}"
runs-on: ubuntu-latest
timeout-minutes: 15
# Must exceed install + proof step ceilings plus checkout/cache/rust-cache overhead.
# Cold Kani install + CBMC setup can approach 15 min on busy runners; install+proof
# ceilings plus checkout/cache need headroom (avoid cancelling mid-proof).
timeout-minutes: 35
strategy:
# `fail-fast: false` lets all harnesses report results even
# if one regresses; otherwise GHA cancels the rest and we lose
Expand Down Expand Up @@ -186,7 +189,7 @@ jobs:
# action's last step runs `cargo-kani` at the workspace root with
# no `-p` flag, which on this multi-crate workspace tries to
# verify every member and hangs (cancelled at ~9-10 min, well
# before our 15-min step budget). Direct install + explicit
# before the job timeout). Direct install + explicit
# `-p kcore-sanitize --harness` below gives predictable timing.
#
# No protobuf compiler is needed: `kcore-sanitize` is a leaf
Expand All @@ -197,7 +200,7 @@ jobs:
# every time it runs even when the layout is already in place.
- name: Install kani-verifier
id: kani-install
timeout-minutes: 10
timeout-minutes: 18
run: |
set -euo pipefail
if ! command -v cargo-kani >/dev/null 2>&1; then
Expand Down Expand Up @@ -253,7 +256,7 @@ jobs:
# match. `--exact` would be stricter but does not exist in
# kani-verifier 0.67.0.
- name: Run bounded Kani proof
timeout-minutes: 8
timeout-minutes: 12
run: |
set -euo pipefail
cargo-kani --version
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
/target
/result
/result-*
/result-kctl
/dist
# Local release notes (copy from RELEASE_NOTES.template.md before make release-publish)
/RELEASE_NOTES.md
*.iso
26 changes: 23 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all build check fmt clippy audit lint-nix test test-all test-rust test-nix test-vm test-tla test-tla-trace test-replication-soak coverage test-controller test-node-agent test-kctl test-rust-filter loc iso iso-remote kctl clean install-hooks kani help
.PHONY: all build check fmt clippy audit lint-nix test test-all test-rust test-nix test-vm test-tla test-tla-trace test-replication-soak coverage test-controller test-node-agent test-kctl test-rust-filter loc iso iso-remote kctl clean install-hooks kani help release-build release-dist release-publish release

VERSION := $(shell cat VERSION)
V ?= v$(VERSION)
Expand Down Expand Up @@ -92,7 +92,7 @@ iso:
@echo ""
@ls -lh result-iso/iso/*.iso
@echo ""
@echo "ISO built: result-iso/iso/kcoreos-$(VERSION)-x86_64-linux.iso"
@echo "ISO built under result-iso/iso/ (release-dist copies it to kcoreos-$(VERSION)-x86_64-linux.iso)"

iso-remote:
@echo "Building kcore ISO $(V) on remote Linux server..."
Expand All @@ -101,6 +101,22 @@ iso-remote:
kctl:
cargo build --release -p kcore-kctl

# Release (operator machine: Linux x86_64, Nix + flakes, gh CLI authenticated).
# Flow: bump VERSION → merge PR → tag v$(VERSION) → push tag → edit RELEASE_NOTES.md → make release-build release-dist → make release-publish
release-build:
bash ./scripts/release.sh build

release-dist:
bash ./scripts/release.sh dist

release-publish:
bash ./scripts/release.sh publish

release: release-build release-dist
@echo ""
@echo "Artifacts are under dist/. Next: ensure git tag v$(VERSION) exists on origin,"
@echo "fill RELEASE_NOTES.md (from RELEASE_NOTES.template.md), then: make release-publish"

install-hooks:
@for hook in scripts/hooks/*; do \
name=$$(basename "$$hook"); \
Expand All @@ -110,7 +126,7 @@ install-hooks:

clean:
cargo clean
rm -rf result result-iso dist
rm -rf result result-iso result-kctl dist

help:
@echo "kcore $(V)"
Expand Down Expand Up @@ -140,6 +156,10 @@ help:
@echo " iso Build NixOS ISO (Linux only)"
@echo " iso-remote Build NixOS ISO on remote Linux server (from macOS)"
@echo " kctl Build kctl CLI only"
@echo " release-build Nix-build ISO + kcore-kctl (result-iso, result-kctl)"
@echo " release-dist Tarball + ISO under dist/ + SHA256SUMS"
@echo " release-publish Create GitHub Release from tag (needs gh, RELEASE_NOTES.md)"
@echo " release release-build + release-dist (then publish manually)"
@echo " install-hooks Install git pre-commit/pre-push hooks"
@echo " clean Remove build artifacts"
@echo " help Show this help"
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ cargo fmt --check
cargo audit
```

**Releases** (version bump, Nix build, GitHub Release assets): [docs/release.md](docs/release.md).

## Known Gaps / Next Priorities

1. Add certificate lifecycle management (rotation, expiry handling, revocation strategy).
Expand Down
14 changes: 14 additions & 0 deletions RELEASE_NOTES.template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# kcore X.Y.Z

## Highlights

-

## Upgrade / install

- **ISO**: flash `kcoreos-X.Y.Z-x86_64-linux.iso` (verify `SHA256SUMS`).
- **kctl**: extract `kcore-kctl-X.Y.Z-linux-x86_64.tar.gz` and run `./kcore-kctl --help` (glibc Linux x86_64, built via this repo’s Nix flake).

## Checksums

See `SHA256SUMS` attached to the GitHub Release.
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
| Day-2 filesystem operations | [day-2-filesystem-operations.md](./day-2-filesystem-operations.md) |
| CRDT / HA replication | [ha-crdt-replication.md](./ha-crdt-replication.md) |
| File layout | [file-structure.md](./file-structure.md) |
| Releases (Make + Nix + `gh`) | [release.md](./release.md) |
| Formal methods | [formal-methods-and-verification.md](./formal-methods-and-verification.md) |
| Heartbeat | [heartbeat.md](./heartbeat.md) |
| kctl workflows | [kctl-commands-and-workflows.md](./kctl-commands-and-workflows.md) |
Expand Down
6 changes: 5 additions & 1 deletion docs/file-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ kcore-rust/
│ └── CrossDcReplication.cfg TLC configuration for cross-DC replication model
├── scripts/
│ └── build-iso-remote.sh SSH helper to build the kcore ISO on a remote Linux host
│ ├── build-iso-remote.sh SSH helper to build the kcore ISO on a remote Linux host
│ └── release.sh release helper: Nix-build ISO and kcore-kctl, package dist/ + SHA256SUMS, gh publish (see docs/release.md)
└── docs/
├── Architecture.md high-level flow diagrams (Mermaid) and component responsibilities
Expand All @@ -119,6 +120,7 @@ kcore-rust/
├── nix-vm-config-generation.md when/how Nix VM configs are generated and applied
├── mtls-bootstrap-and-auth.md certificate creation, node bootstrap, runtime mTLS
├── formal-methods-and-verification.md notes on formal verification approaches
├── release.md release checklist: VERSION bump, tag, Nix build, GitHub assets via Make/gh
└── file-structure.md this file
```

Expand Down Expand Up @@ -250,6 +252,7 @@ For each file: purpose + where it is used in runtime/operator flows.
- `specs/tla/CrossDcReplication.tla` — multi-DC replication convergence model with link failures.
- `specs/tla/CrossDcReplication.cfg` — TLC constants/properties for cross-DC replication model.
- `scripts/build-iso-remote.sh` — remote build automation script; orchestrates ISO build steps on a remote Linux builder host.
- `scripts/release.sh` — non-interactive release helper: Nix-build ISO and `kcore-kctl`, package `dist/` + `SHA256SUMS`, `gh release create` (see `docs/release.md`).

### Documentation

Expand All @@ -268,3 +271,4 @@ For each file: purpose + where it is used in runtime/operator flows.
- `docs/mtls-bootstrap-and-auth.md` — certificate lifecycle guide; bootstrapping, cert roles, and auth usage in runtime calls.
- `docs/formal-methods-and-verification.md` — verification strategy notes; current checks and future formalization directions.
- `docs/file-structure.md` — repository map and deep catalog; explains file responsibilities and runtime/operator relevance.
- `docs/release.md` — release checklist: bump `VERSION`, tag, Nix build, GitHub Release assets via Make/`gh`.
84 changes: 84 additions & 0 deletions docs/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Release process (Make + Nix + GitHub Releases)

Releases are **operator-driven**: build artifacts locally with Nix, publish to GitHub with `gh`. There is no release workflow in GitHub Actions; CI on `main` remains the quality gate only.

## Version sources (policy)

| Source | Role |
|--------|------|
| [`VERSION`](../VERSION) (single line, e.g. `0.2.0`) | **Product / packaging version**: Nix `kcoreVersion`, ISO filename `kcoreos-$(VERSION)-x86_64-linux.iso`, Git tag `v$(VERSION)`, release assets. **Bump this for every release.** |
| `crates/*/Cargo.toml` `version = "…"` | Rust crate semver (currently `0.1.0` for workspace members). **Not automatically tied to `VERSION`.** Optional: bump crate versions in the same PR as `VERSION` if you want `kcore-kctl --version` to match the product version everywhere. |

## Preconditions

- **Host**: Linux **x86_64** with Nix and flakes working (`nix build .#kcore-kctl`).
- **GitHub CLI**: `gh` installed and authenticated (`gh auth login`) with `contents: write` on the repo. Optional: set `GH_REPO=owner/repo` if not using the default remote.
- **Tag**: The annotated tag `v$(cat VERSION)` must exist **on the remote** before `make release-publish` (the script uses `gh release create --verify-tag`).

## Steps

1. **Bump version**
Edit [`VERSION`](../VERSION) to `X.Y.Z` (and optionally align `crates/kctl/Cargo.toml` and other crates if you follow the policy above). Open a PR, get CI green, merge to `main`.

2. **Tag the release commit** (on `main` after merge):

```bash
git fetch origin main && git checkout main && git pull
git tag -a "v$(tr -d '\n' < VERSION)" -m "kcore $(tr -d '\n' < VERSION)"
git push origin "v$(tr -d '\n' < VERSION)"
```

3. **Quality gate (recommended)**
`make check` and/or `make test-all` per [rust-quality-checks](../.cursor/rules/rust-quality-checks.mdc).

4. **Build artifacts (Nix)**
At the same commit as the tag (or clean tree on `main` at that commit):

```bash
make release-build
```

This runs [`scripts/release.sh`](../scripts/release.sh) `build`: ISO → `result-iso/`, `kcore-kctl` → `result-kctl/`.

5. **Package `dist/`**
Produces the GitHub upload set:

```bash
make release-dist
```

- `dist/kcore-kctl-$(VERSION)-linux-x86_64.tar.gz` (binary at archive root: `kcore-kctl`)
- `dist/kcoreos-$(VERSION)-x86_64-linux.iso` (release asset name; copied from the single ISO produced under `result-iso/iso/`)
- `dist/SHA256SUMS` for both files
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Or in one step after a successful build: `make release` (build + dist; does not publish).

6. **Release notes**
Copy the template and edit:

```bash
cp RELEASE_NOTES.template.md RELEASE_NOTES.md
# edit RELEASE_NOTES.md (not committed; see .gitignore)
```

Publish uses `RELEASE_NOTES` env if you need another path: `RELEASE_NOTES=path/to/notes.md make release-publish`.

7. **Publish the GitHub Release**

```bash
make release-publish
```

This runs `nix develop --command gh release create v$(VERSION) --verify-tag` (GitHub CLI from the dev shell) and uploads the tarball, ISO, and `SHA256SUMS`.

## Artifact notes

- **kctl** in the tarball is the **Nix-built** `kcore-kctl` from `.#kcore-kctl` (same lineage as the ISO), not a raw `cargo build`.
- **Platform**: **linux x86_64** (glibc via Nix). No musl/static build in this flow.
- **Large files**: ISOs are ~1–2 GiB; GitHub per-file limit is 2 GiB. Stay under that or split hosting for huge artifacts.

## Troubleshooting

- **`gh release create` fails on `--verify-tag`**: push the tag first: `git push origin vX.Y.Z`.
- **Wrong ISO name**: Nix may place the built ISO under a NixOS-derived name in `result-iso/iso/`; the dist step discovers the single `*.iso` there and copies it to the release asset name `kcoreos-$(VERSION)-x86_64-linux.iso`.
- **Token in automation**: set `GH_TOKEN` in the environment for non-interactive `gh` (e.g. CI in the future); this doc targets local operator use.
94 changes: 94 additions & 0 deletions scripts/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Build release artifacts (Nix ISO + kcore-kctl), package dist/, publish GitHub Release.
# Usage:
# ./scripts/release.sh build # nix build ISO + kcore-kctl -> result-iso, result-kctl
# ./scripts/release.sh dist # dist/*.tar.gz, ISO copy, dist/SHA256SUMS
# ./scripts/release.sh publish # gh release create (needs tag on remote, RELEASE_NOTES.md)
# Environment:
# RELEASE_NOTES Path to release notes file (default: RELEASE_NOTES.md)
# GH_REPO owner/repo override for gh (optional; defaults to git remote)
set -euo pipefail

ROOT="$(cd "$(dirname "${0}")/.." && pwd)"
cd "${ROOT}"

VERSION="$(tr -d '\n' < VERSION)"
ISO_NAME="kcoreos-${VERSION}-x86_64-linux.iso"
KCTL_ARCHIVE="kcore-kctl-${VERSION}-linux-x86_64.tar.gz"

die() {
echo "release.sh: $*" >&2
exit 1
}

require_cmd() {
command -v "${1}" >/dev/null 2>&1 || die "missing required command: ${1}"
}

cmd_build() {
require_cmd nix
echo "==> Building ISO (${ISO_NAME})..."
nix build ".#nixosConfigurations.kcore-iso.config.system.build.isoImage" -o result-iso
echo "==> Building kcore-kctl..."
nix build ".#kcore-kctl" -o result-kctl
echo "==> Build outputs:"
ls -lh result-iso/iso/*.iso
ls -lh result-kctl/bin/kcore-kctl
}

cmd_dist() {
require_cmd tar
require_cmd sha256sum
[[ -f result-kctl/bin/kcore-kctl ]] || die "run '${0} build' first (missing result-kctl/bin/kcore-kctl)"
shopt -s nullglob
iso_candidates=(result-iso/iso/*.iso)
shopt -u nullglob
[[ "${#iso_candidates[@]}" -eq 1 ]] || die "expected exactly one ISO under result-iso/iso/; run '${0} build' first"
ISO_SRC="${iso_candidates[0]}"

mkdir -p dist
echo "==> Packaging ${KCTL_ARCHIVE}..."
tar -C result-kctl/bin -czf "dist/${KCTL_ARCHIVE}" kcore-kctl
echo "==> Copying $(basename "${ISO_SRC}") to dist/${ISO_NAME}..."
cp -f "${ISO_SRC}" "dist/${ISO_NAME}"
echo "==> Writing dist/SHA256SUMS..."
(
cd dist
sha256sum "${ISO_NAME}" "${KCTL_ARCHIVE}" >SHA256SUMS
)
echo "==> dist layout:"
ls -lh dist/
cat dist/SHA256SUMS
}

cmd_publish() {
require_cmd nix
NOTES="${RELEASE_NOTES:-RELEASE_NOTES.md}"
[[ -f "${NOTES}" ]] || die "missing ${NOTES} - copy RELEASE_NOTES.template.md to RELEASE_NOTES.md and edit"
[[ -f "dist/${KCTL_ARCHIVE}" ]] || die "run '${0} dist' first"
[[ -f "dist/${ISO_NAME}" ]] || die "run '${0} dist' first"
[[ -f dist/SHA256SUMS ]] || die "run '${0} dist' first"

TAG="v${VERSION}"
echo "==> Creating GitHub release ${TAG} (verify-tag)..."
nix develop --command gh release create "${TAG}" \
--verify-tag \
--title "kcore ${VERSION}" \
--notes-file "${NOTES}" \
"dist/${KCTL_ARCHIVE}" \
"dist/${ISO_NAME}" \
dist/SHA256SUMS
echo "==> Done: nix develop --command gh release view ${TAG}"
}

usage() {
echo "Usage: ${0} {build|dist|publish}"
exit 1
}

case "${1:-}" in
build) cmd_build ;;
dist) cmd_dist ;;
publish) cmd_publish ;;
*) usage ;;
esac
Loading