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
12 changes: 10 additions & 2 deletions content/docs/guide/ci-cd-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,15 @@ comment), so every promotion would stale a hand-copied enumeration here; it alre
Backend pins live in `e2e/live/ci/backend.env` and must match the `@objectstack/spec` version in
`pnpm-lock.yaml` — bump both in the same PR, or the run proves nothing.

That file carries a **third** pin, `BETTER_AUTH_VERSION`, and it is a different kind of thing:
`OBJECTSTACK_VERSION` is the only `@objectstack` value that file declares, and moving it is the
only move. The showcase-app commit the lane sparse-checks-out is **derived**, not pinned:
`start-backend.sh` resolves the `@objectstack/cli@$OBJECTSTACK_VERSION` release tag at boot
(`git ls-remote --tags`, peeled sha) and refuses to start when it does not resolve. It used to be
a second key, `OBJECTSTACK_REF`, moved by hand under a MUST that nothing could check — the same
shape as the version drift above, and retired for the same reason (objectui#7964). Deriving it
means the app source and the published packages it runs on come from one release by construction.

That file carries a **second** pin, `BETTER_AUTH_VERSION`, and it is a different kind of thing:
not a matched-pair pin but a workaround for a break inside the published packages themselves.
`@objectstack/plugin-auth` imports `createLocalAccountIssuer` from `@better-auth/core/db` and
declares `@better-auth/core` with a caret; `@better-auth/core@1.7.3` removed that export in a
Expand All @@ -431,7 +439,7 @@ its 300-second readiness timeout having run zero specs (objectstack#16186, objec
pinning a dependency to turn a lane green is a gate weakening — `e2e/live/ci/better-auth-pin.mjs`
runs on **every** start, cache hits included, and fails by name if the override was not declared,
did not resolve, or resolved and still lacks the export. ⛔ It is not, and must not become, a
repair of the two pins above: objectui#7689's triage forbids repairing this lane by moving those.
repair of the version pin above: objectui#7689's triage forbids repairing this lane by moving it.
Retire the pin and its guard together in the PR that bumps `OBJECTSTACK_VERSION` past the
upstream fix.

Expand Down
40 changes: 31 additions & 9 deletions e2e/live/ci/backend.env
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,38 @@
# had drifted two minor versions (17.0.0-rc.2 against a 17.2.0 lockfile)
# before anything compared the two numbers — objectui#7689.
#
# OBJECTSTACK_REF — the objectstack-ai/objectstack commit the showcase app
# metadata is checked out from. Always the commit the release tag
# `@objectstack/cli@${OBJECTSTACK_VERSION}` points at, so the app source and
# the published packages it runs on come from the same tree. (Mirror image
# of the framework repo's `.objectui-sha` console pin.)
# NOT machine-checked, and deliberately so: resolving that tag needs the
# objectstack repository, which the unit lane cannot reach. Move this by
# hand whenever the version above moves, from the tag's own commit.
# This file declares exactly ONE @objectstack pin, and that is the whole
# design: the commit is derived from it, see below.
OBJECTSTACK_VERSION=17.3.0
OBJECTSTACK_REF=8a1bad8b8ee7189a54229368400b5b427e3ad5e2

# There is deliberately no OBJECTSTACK_REF key here. The objectstack-ai/objectstack
# commit the showcase app metadata is sparse-checked-out from is DERIVED at
# boot by start-backend.sh, from the release tag
# `@objectstack/cli@${OBJECTSTACK_VERSION}` — `git ls-remote --tags`, taking
# the peeled `^{}` sha of the annotated tag — and the script refuses to start
# when that tag does not resolve to a 40-character sha. The app source and the
# published packages it runs on therefore come from the same release BY
# CONSTRUCTION; there is no second value to move, so the two cannot disagree.
# (Mirror image of the framework repo's `.objectui-sha` console pin.)
#
# It used to be a hand-moved sha here, carrying a stated MUST — "always the
# commit the release tag points at" — that nothing could check, on the
# reasoning that resolving the tag needs the objectstack repository and the
# unit lane cannot reach it. True of the unit lane, and beside the point: the
# live lane clones that very repository at boot to get the app, so the lane
# that CONSUMES the value could always resolve it. objectui#7964 retired the
# pin rather than add a check inside an `informational` lane nobody reads.
#
# ⚠️ One consequence, stated rather than papered over. The workflow caches the
# prepared fixture under `live-backend-<os>-<hashFiles(backend.env)>`, and the
# resolved sha is no longer in this file. A version bump still moves the key,
# because the version is what the sha is derived from. But if the
# `@objectstack/cli@${OBJECTSTACK_VERSION}` tag were ever re-pointed at another
# commit after a fixture had been cached, that stale fixture would still be
# served until OBJECTSTACK_VERSION itself moved. start-backend.sh's own stamp
# carries the RESOLVED sha, so a re-point is caught in a reused scratch dir;
# the Actions cache is the one path it is not caught on. Published release tags
# are not expected to move, and moving one is the anomaly, not this lane.

# BETTER_AUTH_VERSION — the version the whole floating `better-auth` family is
# pinned to in the backend's install path, via an npm `overrides` block that
Expand Down
8 changes: 5 additions & 3 deletions e2e/live/ci/better-auth-pin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
* binds the port, loads 42 plugins and prints `Server is ready` while auth is
* absent (objectui#8084).
*
* ⛔ The pin is NOT a repair of `OBJECTSTACK_VERSION` / `OBJECTSTACK_REF`.
* objectui#7689's triage forbids repairing this lane by moving those, and this
* does not move them. It pins a TRANSITIVE dependency of the published artifact
* ⛔ The pin is NOT a repair of `OBJECTSTACK_VERSION` (nor of the showcase-app
* commit start-backend.sh derives from it — objectui#7964 retired the second,
* hand-moved `OBJECTSTACK_REF` key that used to sit beside it).
* objectui#7689's triage forbids repairing this lane by moving that, and this
* does not move it. It pins a TRANSITIVE dependency of the published artifact
* to the version that artifact's own manifest was authored against, restoring
* the resolution the publisher intended. When objectstack#16186 lands upstream
* — plugin-auth pinning `@better-auth/core` itself, or moving off the removed
Expand Down
61 changes: 53 additions & 8 deletions e2e/live/ci/start-backend.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
# @objectstack/* packages, serving the showcase app the live specs target.
#
# What it does (idempotent; a stamp file skips prepare when pins are unchanged):
# 1. Sparse-checkout `examples/app-showcase` from objectstack-ai/objectstack
# at the pinned OBJECTSTACK_REF (backend.env).
# 1. Resolve the `@objectstack/cli@$OBJECTSTACK_VERSION` release tag to a
# commit, and sparse-checkout `examples/app-showcase` from
# objectstack-ai/objectstack at it. The commit is DERIVED, never pinned:
# see the note above OBJECTSTACK_VERSION in backend.env (objectui#7964).
# 2. Rewrite its package.json: every `@objectstack/*` workspace dep -> the
# pinned published OBJECTSTACK_VERSION; dev-only tooling dropped.
# 3. `npm install` (published tarballs only — nothing is built from source,
Expand Down Expand Up @@ -37,10 +39,53 @@ STAMP="$BACKEND_DIR/.prepared"
# floating family is the exact outcome this pin exists to prevent, and it costs
# 300 seconds to discover downstream (objectstack#16186).
: "${BETTER_AUTH_VERSION:?backend.env must declare BETTER_AUTH_VERSION — see its header and objectstack#16186}"
# The pin is IN the stamp: without it, a fixture prepared before a pin change
# satisfies the reuse test and the new pin is never installed. The workflow's
# fixture cache key is the hash of backend.env, which carries the pin for the
# same reason.

# The showcase-app commit is DERIVED here, and is declared nowhere: it is
# whatever the `@objectstack/cli@$OBJECTSTACK_VERSION` release tag points at, so
# the metadata checked out below and the published packages installed on top of
# it cannot come from different trees. backend.env used to carry it as a second,
# hand-moved pin whose stated MUST ("always the commit the release tag points
# at") nothing could check — and a pair that can disagree eventually does
# (objectui#7964, objectui#7689 for the version half).
#
# Annotated tags resolve in two lines: the tag object, then the peeled `^{}`
# commit. Take the peeled sha when present, else the tag's own (lightweight tags
# do not peel). Both patterns are passed explicitly — asking for the tag alone
# does NOT return its peeled line.
OBJECTSTACK_TAG="@objectstack/cli@$OBJECTSTACK_VERSION"
# GIT_TERMINAL_PROMPT=0 so an unreachable/private remote fails in seconds with
# the message below instead of blocking the job on a credential prompt.
if ! TAG_LINES="$(GIT_TERMINAL_PROMPT=0 git ls-remote --tags "$REPO_URL" \
"refs/tags/$OBJECTSTACK_TAG" "refs/tags/$OBJECTSTACK_TAG^{}" 2>&1)"; then
echo "[live-backend] cannot reach $REPO_URL to resolve the release tag" >&2
echo "[live-backend] $TAG_LINES" >&2
exit 1
fi
OBJECTSTACK_REF="$(printf '%s\n' "$TAG_LINES" | awk -v t="refs/tags/$OBJECTSTACK_TAG" '
$2 == t "^{}" { peeled = $1 }
$2 == t { plain = $1 }
END { print (peeled != "" ? peeled : plain) }
')"
# A missing tag is not an error to `git ls-remote` — it prints nothing and exits
# 0 — so the shape check below is the actual guard, not a formality. Refuse
# loudly and by name: the alternative is `git fetch --depth 1 origin ""` failing
# 300 seconds downstream, in a log nobody reads until the job goes red.
if [[ ! "$OBJECTSTACK_REF" =~ ^[0-9a-f]{40}$ ]]; then
echo "[live-backend] release tag '$OBJECTSTACK_TAG' does not resolve to a commit in" >&2
echo "[live-backend] $REPO_URL" >&2
echo "[live-backend] OBJECTSTACK_VERSION=$OBJECTSTACK_VERSION (backend.env) names a version" >&2
echo "[live-backend] with no published release tag, or the tag scheme moved. Refusing to" >&2
echo "[live-backend] start: there is no showcase-app commit matching that release." >&2
exit 1
fi
echo "[live-backend] resolved $OBJECTSTACK_TAG -> $OBJECTSTACK_REF"

# The RESOLVED sha, not the version, is in the stamp: without it, a fixture
# prepared before a pin change satisfies the reuse test and the new pin is never
# installed — and a re-pointed tag is exactly such a change with no key of its
# own to hash. The workflow's fixture cache key is the hash of backend.env,
# which carries the version for the same reason; see the ⚠️ note there for the
# one path that key can no longer see.
WANT_STAMP="$OBJECTSTACK_REF $OBJECTSTACK_VERSION better-auth@$BETTER_AUTH_VERSION"

mkdir -p "$BACKEND_DIR"
Expand All @@ -50,10 +95,10 @@ prepare() {
echo "[live-backend] prepare: pins unchanged ($WANT_STAMP), reusing $APP_DIR"
return
fi
echo "[live-backend] prepare: showcase@${OBJECTSTACK_REF:0:12} on published @objectstack/*@$OBJECTSTACK_VERSION"
echo "[live-backend] prepare: showcase@${OBJECTSTACK_REF:0:12} (from $OBJECTSTACK_TAG) on published @objectstack/*@$OBJECTSTACK_VERSION"
rm -rf "$APP_DIR" "$BACKEND_DIR/src" "$STAMP"

# Shallow, sparse fetch of the pinned commit — metadata source only.
# Shallow, sparse fetch of the resolved commit — metadata source only.
git init -q "$BACKEND_DIR/src"
git -C "$BACKEND_DIR/src" remote add origin "$REPO_URL"
git -C "$BACKEND_DIR/src" sparse-checkout set examples/app-showcase
Expand Down
17 changes: 9 additions & 8 deletions e2e/live/saved-view-filter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { test, expect } from '@playwright/test';
* This is the empirical pin for the defect, run against the REAL stack because
* only the server can answer the question the issue asked: does it accept a
* rule array in `$filter`? It does not. Measured against the published backend
* pair this lane pins — `OBJECTSTACK_VERSION` and `OBJECTSTACK_REF` in
* `e2e/live/ci/backend.env`, which move together and are now held to the
* lockfile by `scripts/__tests__/ci-cd-pipeline-doc.test.ts` (objectui#7689).
* That pair has moved since this measurement was first taken, which is why it
* is named by FILE and not by value: a version literal written here goes stale
* behind the pin silently, and the whole point of objectui#7689 is that it did
* — for two minor versions, in the pin itself. The showcase app is the one that
* ref checks out:
* backend this lane pins — `OBJECTSTACK_VERSION` in `e2e/live/ci/backend.env`,
* held to the lockfile by `scripts/__tests__/ci-cd-pipeline-doc.test.ts`
* (objectui#7689), with the showcase-app commit derived from it at boot from
* the matching `@objectstack/cli` release tag (objectui#7964). That pin has
* moved since this measurement was first taken, which is why it is named by
* FILE and not by value: a version literal written here goes stale behind the
* pin silently, and the whole point of objectui#7689 is that it did — for two
* minor versions, in the pin itself. The showcase app is the one that release
* checks out:
*
* GET /api/v1/data/showcase_task
* ?$filter=[{"field":"status","operator":"equals","value":"in_progress"}]
Expand Down
Loading
Loading