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
8 changes: 8 additions & 0 deletions .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,14 @@ jobs:
echo "Publishing ${dir}"
publish_one "${dir%/}"
done
# The meta publish runs its prepublishOnly hook ON PURPOSE — unlike
# sdk-publish it must NOT use --ignore-scripts. `napi prepublish`
# injects the per-platform optionalDependencies (absent from the
# committed package.json) and copies the addons; skipping it ships a
# meta with no optionalDependencies and no native binary for
# consumers. The immutable-Release write that hook used to attempt is
# disabled at its source via `--no-gh-release` in the prepublishOnly
# script (crates/akua-napi/package.json).
echo "Publishing meta @akua-dev/native"
publish_one crates/akua-napi

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ That's it. akua does **not** specify `App`, `Environment`, `Cluster`, `Secret`,

**Embedded-engine builds + feature wiring** (these cost real time when missed): the engine wasm assets (`crates/{helm,kustomize}-engine-wasm/assets/*.wasm`) are **gitignored** — a fresh clone or `git worktree` can't compile `akua-core` until `task build:engines` runs (or you copy the built assets in). The Go engine source (`crates/helm-engine-wasm/go-src/`) rebuilds via `task build:helm-engine-wasm` (Go→wasip1) — a *different* artifact from the Rust render-worker's `task build:render-worker`; when you change Go engine code, rebuild the engine, not the worker. And a new `akua-core` feature is **dead in the shipped binary/SDK** unless it's added to the `akua-core` dep `features = [...]` in **both** `crates/akua-cli/Cargo.toml` and `crates/akua-napi/Cargo.toml` — otherwise it silently compiles to its `#[cfg(not(feature))]` stub there even though `cargo test -p akua-core --features …` passes.

**Releases are tag-triggered and expensive — batch them.** A pushed `v*` tag fires the full build matrix (Windows + macOS jobs dominate at ~30min and can't be self-hosted), npm publishing, and container publishing. Accumulate fixes on `main` and cut **one** tag when a human explicitly asks — never tag per change/chunk. The release derives its version from the tag (`scripts/set-cargo-version.sh` + a smoke-test guard asserting `akua -V == tag`); the committed `Cargo.toml` version is a dev placeholder, so don't expect hand-bumping it to affect a release. Tags are immutable: never delete and re-push one. Follow [docs/releasing.md](docs/releasing.md) for the release and fail-closed recovery contract.
**Releases are tag-triggered and expensive — batch them.** A pushed `v*` tag fires the full build matrix (Windows + macOS jobs dominate at ~30min and can't be self-hosted), npm publishing, and container publishing. Accumulate fixes on `main` and cut **one** tag when a human explicitly asks — never tag per change/chunk. The release derives its version from the tag (`scripts/set-cargo-version.sh` + a smoke-test guard asserting `akua -V == tag`); the committed `Cargo.toml` version is a dev placeholder, so don't expect hand-bumping it to affect a release. Tags are immutable: never delete and re-push one. The `@akua-dev/native` publish must not use `--ignore-scripts`; see [docs/releasing.md](docs/releasing.md) for the release contract and fail-closed recovery procedure.

**Project domain is `akua.dev`.** Reverse-DNS namespaces (OCI annotations, Java-style package roots, anything following the `org.kcllang.*` shape) use **`dev.akua.*`** — *not* `org.akua.*`. The npm scope is `@akua-dev` because npm scopes have to be unique on the registry and bare `@akua` was taken; the scope name doesn't follow the reverse-DNS rule.

Expand Down
2 changes: 1 addition & 1 deletion crates/akua-napi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"scripts": {
"build": "napi build --platform --release",
"build:debug": "napi build --platform",
"prepublishOnly": "napi prepublish -t npm",
"prepublishOnly": "napi prepublish -t npm --no-gh-release",
"artifacts": "napi artifacts",
"version": "napi version"
},
Expand Down
7 changes: 7 additions & 0 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ prerelease, and do not update the container's `latest` tag.
Do not delete and re-push a release tag. If a tagged run fails, use the SHA-bound
recovery path below after correcting and reviewing the workflow on `main`.

The `@akua-dev/native` meta-package must publish with lifecycle scripts enabled. Its
`prepublishOnly` hook runs `napi prepublish -t npm --no-gh-release`: `napi prepublish`
injects the generated per-platform `optionalDependencies` and copies the native
addons into the package, while `--no-gh-release` prevents the hook from uploading
assets to the immutable GitHub Release. Do not add `--ignore-scripts` to the native
publish; that flag is only valid for the separately staged SDK publish.

## npm trusted publisher contract

npm trusted-publisher configuration is external registry state. Source code can
Expand Down
39 changes: 38 additions & 1 deletion scripts/check-release-workflows.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ line_in_job() {
' "$file"
}

command_line_in_job() {
local file="$1"
local job="$2"
local pattern="$3"

awk -v job="$job" -v pattern="$pattern" '
$0 ~ "^ " job ":$" { in_job = 1; next }
in_job && $0 ~ /^ [A-Za-z0-9_-]+:$/ { exit }
in_job {
command = $0
sub(/^[[:space:]]+/, "", command)
if (command !~ /^#/ && index(command, pattern)) { print NR; exit }
}
' "$file"
}

assert_before() {
local file="$1"
local job="$2"
Expand Down Expand Up @@ -46,13 +62,26 @@ assert_job_contains() {
local pattern="$3"
local line

line="$(line_in_job "$file" "$job" "$pattern")"
line="$(command_line_in_job "$file" "$job" "$pattern")"
if [[ -z "$line" ]]; then
echo "ERROR: $file job '$job' is missing '$pattern'" >&2
exit 1
fi
}

assert_job_excludes() {
local file="$1"
local job="$2"
local pattern="$3"
local line

line="$(command_line_in_job "$file" "$job" "$pattern")"
if [[ -n "$line" ]]; then
echo "ERROR: $file job '$job' still contains forbidden text '$pattern'" >&2
exit 1
fi
}

assert_file_contains() {
local file="$1"
local pattern="$2"
Expand Down Expand Up @@ -273,6 +302,14 @@ assert_before ".github/workflows/release-publish.yml" \
"native-publish" \
"publish_one crates/akua-native-engines-npm" \
"publish_one crates/akua-napi"
if [[ "$(jq -r '.scripts.prepublishOnly' crates/akua-napi/package.json)" != \
"napi prepublish -t npm --no-gh-release" ]]; then
echo "ERROR: native meta prepublish must preserve package injection while disabling GitHub Release uploads" >&2
exit 1
fi
assert_job_excludes ".github/workflows/release-publish.yml" \
"native-publish" \
"--ignore-scripts"
assert_job_contains ".github/workflows/release-publish.yml" \
"sdk-publish" \
"needs: [detect-version, native-publish]"
Expand Down