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: 5 additions & 3 deletions .github/workflows/sdk-version-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ jobs:
exit 1
fi

- name: Verify @nself/sdk and @nself/cli-api versions (sdk/ts-sdk, sdk/ts-cli-api)
- name: Verify every sdk npm package version (sdk/*/package.json)
run: |
TARGET='${{ steps.target.outputs.target }}'
for dir in sdk/ts-sdk sdk/ts-cli-api; do
V=$(node -p "require('./${dir}/package.json').version")
# Globbed, not listed: a package missing from a hand-kept list was skipped by the bumper.
for pkg in sdk/*/package.json; do
dir="${pkg%/package.json}"
V=$(node -p "require('./${pkg}').version")
echo "${dir} declared: ${V}"
if [ "${V}" != "${TARGET}" ]; then
echo "MISMATCH: ${dir} is ${V}, expected ${TARGET}"
Expand Down
31 changes: 16 additions & 15 deletions .github/workflows/version-lockstep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ on:
- '.github/VERSION'
- 'internal/version/version.go'
- 'sdk/go/doc.go'
- 'sdk/ts/package.json'
- 'sdk/ts-sdk/package.json'
- 'sdk/*/package.json'
- 'sdk/py/pyproject.toml'
- '.github/workflows/version-lockstep.yml'
- 'scripts/check-version-lockstep.sh'
Expand Down Expand Up @@ -136,20 +135,22 @@ jobs:
| grep -oE '"[0-9]+\.[0-9]+\.[0-9]+"' | tr -d '"' | head -1)
check "cli/sdk/go/doc.go" "${V}" 1

# 4. cli/sdk/ts/package.json (@nself/plugin-sdk)
V=$(node -p "require('${NSELF_REPO_ROOT}/sdk/ts/package.json').version")
check "cli/sdk/ts/package.json" "${V}" 1

# 4b. cli/sdk/ts-sdk/package.json (@nself/sdk)
# 4. cli/sdk/*/package.json (@nself/plugin-sdk, @nself/sdk, @nself/cli-api, ...)
#
# There are TWO TypeScript packages and only the first was listed
# here, so a release bumped one and left the other behind. Its publish
# workflow then saw its version already on npm, no-opped as
# "already published", and reported success — and the SDK Version
# Coherence check caught the drift a day later on its schedule,
# after the release had shipped.
V=$(node -p "require('${NSELF_REPO_ROOT}/sdk/ts-sdk/package.json').version")
check "cli/sdk/ts-sdk/package.json" "${V}" 1
# Discovered by glob, never listed by hand: a hand-kept list let a release
# bump one npm package and leave another behind (sdk/ts-sdk once, then
# sdk/ts-cli-api at v1.4.15). bump-version.sh uses the same glob.
n=0
for pkg in "${NSELF_REPO_ROOT}"/sdk/*/package.json; do
[ -f "${pkg}" ] || continue
n=$((n + 1))
V=$(node -p "require('${pkg}').version")
check "cli/${pkg#${NSELF_REPO_ROOT}/}" "${V}" 1
done
if [ "${n}" -eq 0 ]; then
printf '::error::no sdk/*/package.json found\n'
exit 1
fi

# 5. cli/sdk/py/pyproject.toml
V=$(grep -E '^version\s*=' "${NSELF_REPO_ROOT}/sdk/py/pyproject.toml" \
Expand Down
2 changes: 1 addition & 1 deletion cmd/commands/canon_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ func TestCanonEnginePrepare(t *testing.T) {
t.Helper()
compattest.Set(t, v15)
savedArgs, savedMount := os.Args, mountInstalledPlugins
defer func() { os.Args, mountInstalledPlugins = savedArgs, savedMount }()
t.Cleanup(func() { os.Args, mountInstalledPlugins = savedArgs, savedMount })
mountInstalledPlugins = func(*cobra.Command) {}
if mount {
mountInstalledPlugins = func(r *cobra.Command) {
Expand Down
6 changes: 6 additions & 0 deletions internal/build/plugins_entitlement_mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ func TestMountIgnoresOverride(t *testing.T) {
}

func TestMountEmptyFileE125(t *testing.T) {
// Missing-token behavior must not renew a license configured on the test host.
t.Setenv("NSELF_PING_API_URL", "http://127.0.0.1:1")
t.Setenv("NSELF_PLUGIN_LICENSE_KEY", "synthetic-license-key")
for i := 1; i <= 10; i++ {
t.Setenv("NSELF_LICENSE_KEY_"+strconv.Itoa(i), "")
}
compattest.Set(t, true)
// No licence key from the host: a stored or env key would renew a real JWT online.
t.Setenv("HOME", t.TempDir())
Expand Down
6 changes: 6 additions & 0 deletions internal/license/entitlement/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ func TestEntitlementRefreshWrites(t *testing.T) {
}

func TestBuildE125UnderV15(t *testing.T) {
// Missing-token behavior must not renew a license configured on the test host.
t.Setenv("NSELF_PING_API_URL", "http://127.0.0.1:1")
t.Setenv("NSELF_PLUGIN_LICENSE_KEY", "synthetic-license-key")
for i := 1; i <= 10; i++ {
t.Setenv("NSELF_LICENSE_KEY_"+strconv.Itoa(i), "")
}
compattest.Both(t, func(t *testing.T) {
// No licence key from the host: a stored or env key would renew a real JWT online.
t.Setenv("HOME", t.TempDir())
Expand Down
42 changes: 23 additions & 19 deletions scripts/bump-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
# 1. cli/.github/VERSION
# 2. cli/internal/version/version.go Version string = "x.y.z" (type-annotated)
# 3. cli/sdk/go/doc.go const Version = "x.y.z"
# 4. cli/sdk/ts/package.json "version": "x.y.z" (via jq)
# 5. cli/sdk/ts-sdk/package.json "version": "x.y.z" (via jq)
# 4. cli/sdk/*/package.json "version": "x.y.z" (via jq; every TS package
# found under sdk/: ts, ts-sdk, ts-cli-api, ...)
# 6. cli/sdk/py/pyproject.toml version = "x.y.z" under [project]
# 7. admin/package.json "version": "x.y.z" (via jq)
# 8. admin/src/lib/cli-version.ts CLI_VERSION = 'x.y.z'
Expand Down Expand Up @@ -102,7 +102,7 @@ fi
# ── Tool availability checks ──────────────────────────────────────────────────

if ! command -v jq >/dev/null 2>&1; then
printf 'ERROR: jq is required for JSON file bumps (sdk/ts/package.json, sdk/ts-sdk/package.json, admin/package.json)\n' >&2
printf 'ERROR: jq is required for JSON file bumps (sdk/*/package.json, admin/package.json)\n' >&2
printf ' Install: brew install jq (macOS) | apt-get install jq (Linux)\n' >&2
exit 1
fi
Expand All @@ -114,9 +114,7 @@ VERSION_FILE="${REPO_ROOT}/.github/VERSION"
OLD_VERSION="$(tr -d '[:space:]' < "${VERSION_FILE}" 2>/dev/null || true)"
VERSION_GO="${REPO_ROOT}/internal/version/version.go"
SDK_GO="${REPO_ROOT}/sdk/go/doc.go"
SDK_TS="${REPO_ROOT}/sdk/ts/package.json"
SDK_PY="${REPO_ROOT}/sdk/py/pyproject.toml"
SDK_TS_SDK="${REPO_ROOT}/sdk/ts-sdk/package.json"
# SDK_FLUTTER removed 2026-06-30 (#159, ASI Policy 2)
ADMIN_PKG="${NSELF_ROOT}/admin/package.json"
ADMIN_CLIVER="${NSELF_ROOT}/admin/src/lib/cli-version.ts"
Expand Down Expand Up @@ -248,17 +246,24 @@ update_sdk_go_doc() {
report "${f}" "${old}" "${NEW_VERSION}"
}

update_sdk_ts_pkg() {
update_json_version "${SDK_TS}"
}

# sdk/ts-sdk/package.json — the @nself/sdk consumer client (NselfClient),
# distinct from sdk/ts (@nself/plugin-sdk). Publishes in lockstep with the
# CLI per sdk-ts-sdk-publish.yml ("Triggers on: cli release tag (v*)").
# Added 2026-07-06 after #179 (missing from this bumper caused @nself/sdk
# to lag at 1.1.9 while VERSION moved to 1.2.4 — SDK Version Coherence fail).
update_sdk_ts_sdk_pkg() {
update_json_version "${SDK_TS_SDK}"
# Every sdk/*/package.json is one npm package that ships in lockstep with the CLI
# (sdk/ts = @nself/plugin-sdk, sdk/ts-sdk = @nself/sdk, sdk/ts-cli-api = @nself/cli-api).
# The packages are discovered, never listed by hand: a hand-kept list lost sdk/ts-sdk
# once (#179, @nself/sdk lagged at 1.1.9) and sdk/ts-cli-api again at v1.4.15, where
# SDK Version Sync went red on main. A new sdk/<name>/package.json is bumped from the
# moment it exists. The same glob drives the lockstep checks in version-lockstep.yml
# and check-version-lockstep.sh.
update_sdk_ts_pkgs() {
local f n=0
for f in "${REPO_ROOT}"/sdk/*/package.json; do
[ -f "${f}" ] || continue
update_json_version "${f}"
n=$((n + 1))
done
if [ "${n}" -eq 0 ]; then
printf 'ERROR: no sdk/*/package.json found under %s\n' "${REPO_ROOT}" >&2
exit 1
fi
}

# pyproject.toml shape: `version = "2.0.0"` under [project]
Expand Down Expand Up @@ -558,13 +563,12 @@ if [ "${HOMEBREW}" = "true" ]; then
exit 0
fi

printf 'Phase 1 — updating 11 lockstep files + 4 version-hash goldens (Homebrew deferred to phase 2)\n\n'
printf 'Phase 1 — updating the lockstep files (every sdk/*/package.json included) + 4 version-hash goldens (Homebrew deferred to phase 2)\n\n'

update_version_file
update_version_go
update_sdk_go_doc
update_sdk_ts_pkg
update_sdk_ts_sdk_pkg
update_sdk_ts_pkgs
update_sdk_py_pyproject
update_admin_pkg
update_admin_cliver
Expand Down
20 changes: 12 additions & 8 deletions scripts/check-version-lockstep.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# 1. cli/internal/version/version.go (Go constant)
# 2. cli/.github/VERSION (plain text)
# 3. cli/sdk/go/doc.go (Go const)
# 4. cli/sdk/ts/package.json (JSON version field)
# 4. cli/sdk/*/package.json (JSON version field; every sdk npm package)
# 5. cli/sdk/py/pyproject.toml (TOML version field)
# 6. admin/package.json (JSON version field)
# 7. admin/src/lib/cli-version.ts (TS constant)
Expand Down Expand Up @@ -170,18 +170,22 @@ else
log_warn "cli/sdk/go/doc.go" "(missing)"
fi

# 4. cli/sdk/ts/package.json (warn-only)
FILE4="${CLI_ROOT}/sdk/ts/package.json"
if [ -f "${FILE4}" ]; then
# 4. cli/sdk/*/package.json (warn-only): every npm package under sdk/ (ts, ts-sdk,
# ts-cli-api, ...) is discovered by glob, the same way bump-version.sh bumps them.
SDK_PKG_N=0
for FILE4 in "${CLI_ROOT}"/sdk/*/package.json; do
[ -f "${FILE4}" ] || continue
SDK_PKG_N=$((SDK_PKG_N + 1))
if command -v python3 >/dev/null 2>&1; then
V4=$(python3 -c "import json,sys; d=json.load(open('${FILE4}')); print(d['version'])" 2>/dev/null || true)
V4=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(d['version'])" "${FILE4}" 2>/dev/null || true)
else
V4=$(grep -E '"version"\s*:' "${FILE4}" \
| grep -oE '"[0-9]+\.[0-9]+\.[0-9]+"' | tr -d '"' | head -1)
fi
check_version "cli/sdk/ts/package.json" "${V4}" 0
else
log_warn "cli/sdk/ts/package.json" "(missing)"
check_version "cli/${FILE4#"${CLI_ROOT}/"}" "${V4}" 0
done
if [ "${SDK_PKG_N}" -eq 0 ]; then
log_warn "cli/sdk/*/package.json" "(none found)"
fi

# 5. cli/sdk/py/pyproject.toml (warn-only)
Expand Down
19 changes: 17 additions & 2 deletions scripts/release/fixtures/cases4.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ mk_bump_env() {
B=$(mktemp -d "$BASE/bump.XXXXXX"); C=$B/nself/cli; A=$B/nself/admin
# mk_env's TMPDIR was deleted with its env; bump-version.sh's `mktemp -d` needs a live one (GNU mktemp fails without).
mkdir -p "$BASE/tmp"; export TMPDIR=$BASE/tmp
mkdir -p "$C/scripts" "$C/internal/version" "$C/sdk/go" "$C/sdk/ts" "$C/sdk/ts-sdk" "$C/sdk/py" "$C/.github" \
mkdir -p "$C/scripts" "$C/internal/version" "$C/sdk/go" "$C/sdk/py" "$C/.github" \
"$C/internal/build" "$C/cmd/commands" "$A/src/lib" "$B/bin"
cp "$ROOT/scripts/bump-version.sh" "$C/scripts/"
echo 1.4.13 > "$C/.github/VERSION"
printf 'package version\n\nvar (\n\tVersion string = "1.4.13"\n)\n' > "$C/internal/version/version.go"
printf 'package sdk\n\nconst Version = "1.4.13"\n' > "$C/sdk/go/doc.go"
for f in sdk/ts sdk/ts-sdk; do echo '{"version":"1.4.13"}' > "$C/$f/package.json"; done
# One fixture package per sdk/*/package.json in the real tree, so a package added there
# is part of every bump test without touching this file.
for f in "$ROOT"/sdk/*/package.json; do
d=$(basename "$(dirname "$f")"); mkdir -p "$C/sdk/$d"; echo '{"version":"1.4.13"}' > "$C/sdk/$d/package.json"
done
printf '[project]\nversion = "1.4.13"\n' > "$C/sdk/py/pyproject.toml"
echo '{"version":"1.4.13"}' > "$A/package.json"
echo "export const CLI_VERSION = '1.4.13'" > "$A/src/lib/cli-version.ts"
Expand Down Expand Up @@ -65,6 +69,17 @@ check "bump kept the other golden lines" gold_all 'other keep'
check "bump ran the build golden with UPDATE_GOLDEN=1" grep -q '^UPDATE_GOLDEN=1 test .*./internal/build ' "$GO_LOG"
check "bump ran the cmd golden with UPDATE_GOLDEN=1" grep -q '^UPDATE_GOLDEN=1 test .*./cmd/commands ' "$GO_LOG"
check "bump re-ran both tests plain" test "$(grep -c '^UPDATE_GOLDEN= test' "$GO_LOG")" -eq 2
sdk_pkgs_at() { local f; for f in "$C"/sdk/*/package.json; do grep -q "\"$1\"" "$f" || return 1; done; }
check "bump found the real sdk npm packages (ts-sdk, ts-cli-api among them)" \
test -f "$C/sdk/ts-sdk/package.json" -a -f "$C/sdk/ts-cli-api/package.json"
check "bump moved every sdk/*/package.json, none skipped" sdk_pkgs_at 9.9.9
rm -rf "$B"

# 13b. a package the bumper skips is caught: sdk_pkgs_at fails when one stays behind -------
mk_bump_env
run_bump 9.9.9
echo '{"version":"1.4.14"}' > "$C/sdk/ts-cli-api/package.json"
check "a lagging sdk package fails the lockstep assertion" test "$(sdk_pkgs_at 9.9.9 && echo ok || echo lag)" = lag
rm -rf "$B"

# 14. other drift is refused and the goldens come back -------------------------------------
Expand Down
Loading