feat(installer): prefer npm next lane (#688)
--next now prefers a fast npm @next install (CLI + gateway from the Gitea registry) and falls back to source build at next if the dist-tag is unavailable. Registry lane gated to non-dev, non-explicit-ref next installs; CLI/gateway prerelease versions must share a pipeline suffix. Adds tools/install-next-lane.test.sh (wired into CI). PR-event CI 1635 fully green + review-of-record APPROVE (functional install test, head 2fd7cfc3).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #688.
This commit is contained in:
@@ -1,40 +1,63 @@
|
|||||||
# Planned Design — npm `@next` prerelease lane
|
# npm `@next` prerelease lane
|
||||||
|
|
||||||
Status: **PLANNED / not yet built**
|
Status: **IMPLEMENTED**
|
||||||
|
|
||||||
## Current state
|
## Current behavior
|
||||||
|
|
||||||
`tools/install.sh --next` provides a prerelease integration lane by building the Mosaic CLI and gateway from source at the permanent `next` branch. This is correct for validating integration-branch source, but it is slower than the stable npm lane because it downloads the archive, installs workspace dependencies, builds packages, packs local tarballs, and installs those tarballs globally.
|
`tools/install.sh --next` provides the prerelease integration lane for the permanent `next` branch.
|
||||||
|
|
||||||
## Medium-term target
|
The lane is fast-by-default:
|
||||||
|
|
||||||
Publish every accepted `next` integration build to the npm registry under the `@next` dist-tag, for example:
|
1. Install framework files from the `next` source archive.
|
||||||
|
2. Resolve the Gitea npm registry `next` dist-tag for the globally installed packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm view @mosaicstack/gateway@next version
|
||||||
|
npm view @mosaicstack/mosaic@next version
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Require both resolved versions to share the same `next.<pipeline>` suffix, then install the exact resolved versions.
|
||||||
|
4. If either `@next` package is missing, unreachable, mismatched, or fails to install, fall back to the source-build path at `next`.
|
||||||
|
|
||||||
|
`--next` never hard-fails solely because the prerelease npm dist-tag is unavailable.
|
||||||
|
|
||||||
|
## Published packages
|
||||||
|
|
||||||
|
The `next` publish pipeline publishes non-private `@mosaicstack/*` packages to the Mosaic Gitea npm registry:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@mosaicstack/mosaic@0.0.49-next.1
|
https://git.mosaicstack.dev/api/packages/mosaicstack/npm/
|
||||||
@mosaicstack/mosaic@0.0.49-next.2
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then move `tools/install.sh --next` from source-build behavior to a fast npm install:
|
Observed `next` dist-tags after enabling the pipeline:
|
||||||
|
|
||||||
```bash
|
```text
|
||||||
npm install -g @mosaicstack/mosaic@next
|
@mosaicstack/mosaic@next -> 0.0.49-next.1633
|
||||||
|
@mosaicstack/gateway@next -> 0.0.7-next.1633
|
||||||
```
|
```
|
||||||
|
|
||||||
The framework archive should still resolve from the matching `next` source/ref until framework packaging has a registry-backed equivalent.
|
The gateway also publishes a Docker image as `gateway:sha-<short>` on `next` merges. The installer fast path uses the npm gateway package when available; the Docker image is for deployed gateway/runtime harness flows.
|
||||||
|
|
||||||
|
## Explicit source lanes
|
||||||
|
|
||||||
|
Source builds remain available and are still the authority for explicit ref validation:
|
||||||
|
|
||||||
|
- `--dev` always builds from source.
|
||||||
|
- `--ref <ref>` / `MOSAIC_REF=<ref>` wins over `--next` and uses the source path for that exact ref.
|
||||||
|
|
||||||
## Pipeline shape
|
## Pipeline shape
|
||||||
|
|
||||||
1. Trigger on successful CI for `next`.
|
1. Trigger on `next` merges.
|
||||||
2. Compute the next prerelease version from the upcoming stable version plus a monotonic prerelease counter (`0.0.49-next.N`).
|
2. Compute the next prerelease version from the upcoming stable version plus the Woodpecker pipeline number (`<target-stable>-next.<CI_PIPELINE_NUMBER>`).
|
||||||
3. Build and pack publishable packages in CI.
|
3. Build and publish non-private packages in CI.
|
||||||
4. Publish to the Mosaic Gitea npm registry with dist-tag `next`.
|
4. Publish to the Mosaic Gitea npm registry with dist-tag `next`.
|
||||||
5. Keep `latest` untouched; only main/release promotion can update `latest`.
|
5. Keep `latest` untouched; only main/release promotion can update `latest`.
|
||||||
6. Teach the installer to prefer `@next` for the CLI/gateway prerelease lane once the registry tag is reliable.
|
6. Publish gateway Docker images from `next` as `gateway:sha-<short>` only.
|
||||||
|
|
||||||
## Guardrails
|
## Guardrails
|
||||||
|
|
||||||
- `@next` is mutable prerelease convenience, not a deployment pin.
|
- `@next` is mutable prerelease convenience, not a deployment pin.
|
||||||
- Stable installs continue to use `@latest`.
|
- Stable installs continue to use `@latest`.
|
||||||
- Contributor validation remains available through `--dev --ref <branch>`.
|
- Contributor validation remains available through `--dev --ref <branch>`.
|
||||||
- Pipeline must be reproducible and trace every prerelease package back to the source commit on `next`.
|
- Pipeline output traces every prerelease package back to the source commit on `next`.
|
||||||
|
- The installer falls back to source rather than hard-failing on prerelease registry issues.
|
||||||
|
|||||||
@@ -179,13 +179,13 @@ The installer places the `mosaic` binary at `~/.npm-global/bin/mosaic`.
|
|||||||
|
|
||||||
Install lanes:
|
Install lanes:
|
||||||
|
|
||||||
| Lane | Command | Source |
|
| Lane | Command | Source |
|
||||||
| ------------------------ | ------------------------------------- | -------------------------------------------- |
|
| ------------------------ | ------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||||
| Stable | `bash tools/install.sh` | npm `@mosaicstack/mosaic@latest` + `main` |
|
| Stable | `bash tools/install.sh` | npm `@mosaicstack/mosaic@latest` + `main` |
|
||||||
| Prerelease integration | `bash tools/install.sh --next` | Build-from-source at permanent branch `next` |
|
| Prerelease integration | `bash tools/install.sh --next` | Fast npm `@mosaicstack/mosaic@next` + `@mosaicstack/gateway@next`; source fallback at `next` |
|
||||||
| Contributor/source build | `bash tools/install.sh --dev --ref X` | Build-from-source at the requested ref |
|
| Contributor/source build | `bash tools/install.sh --dev --ref X` | Build-from-source at the requested ref |
|
||||||
|
|
||||||
`--next` implies source-build mode at `next`; explicit `--ref` or `MOSAIC_REF` wins.
|
`--next` is fast-by-default from the Gitea npm `next` dist-tag and falls back to a source build at the permanent `next` branch if the dist-tag is missing or unreachable. Explicit `--ref` or `MOSAIC_REF` still wins and uses the source path.
|
||||||
Flags for non-interactive use:
|
Flags for non-interactive use:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
40
docs/scratchpads/installer-next-fast-npm-20260625.md
Normal file
40
docs/scratchpads/installer-next-fast-npm-20260625.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Installer `--next` fast npm lane — 2026-06-25
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Flip `tools/install.sh --next` from source-build-first to fast npm `@next` first, with source fallback.
|
||||||
|
|
||||||
|
## Registry reality check
|
||||||
|
|
||||||
|
Gitea npm registry: `https://git.mosaicstack.dev/api/packages/mosaicstack/npm/`
|
||||||
|
|
||||||
|
Verified before implementation:
|
||||||
|
|
||||||
|
- `@mosaicstack/mosaic@next` resolves to `0.0.49-next.1633`.
|
||||||
|
- `@mosaicstack/gateway@next` resolves to `0.0.7-next.1633`.
|
||||||
|
- `@mosaicstack/gateway` dist-tags include `latest: 0.0.6` and `next: 0.0.7-next.1633`.
|
||||||
|
- `apps/gateway/package.json` is non-private and has Gitea npm `publishConfig`.
|
||||||
|
|
||||||
|
Conclusion: the installer can fast-install both CLI and gateway npm packages for `--next`. The gateway Docker `gateway:sha-<short>` remains the deployment/harness artifact; the npm gateway package is valid for the installer global package path.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
- `--next` with no explicit ref:
|
||||||
|
1. framework archive from `next`;
|
||||||
|
2. resolve `@mosaicstack/gateway@next` and `@mosaicstack/mosaic@next`;
|
||||||
|
3. require both resolved versions to share the same `next.<pipeline>` suffix;
|
||||||
|
4. install the exact resolved package versions;
|
||||||
|
5. set `MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1` so wizard does not overwrite the prerelease gateway;
|
||||||
|
6. if either package is missing/unreachable/mismatched/fails, fall back to existing source build at `next`.
|
||||||
|
- `--dev` remains pure source build.
|
||||||
|
- explicit `--ref` / `MOSAIC_REF` still wins over `--next` and uses the source path for that exact ref.
|
||||||
|
|
||||||
|
## Install detail
|
||||||
|
|
||||||
|
The installer writes the scoped npmrc mapping (`@mosaicstack:registry=...`) and then runs npm install without overriding npm's default registry. Passing `--registry=<gitea>` to `npm install` forces public transitive dependencies (for example `@anthropic-ai/sdk`) to resolve from Gitea and breaks the fast path; the scoped npmrc mapping is the correct split-registry behavior.
|
||||||
|
|
||||||
|
## Verification notes
|
||||||
|
|
||||||
|
- Added `tools/install-next-lane.test.sh` with a fake npm/source harness for exact-version fast install, registry failure source fallback, explicit-ref precedence, and mismatched suffix warning.
|
||||||
|
- Wired the installer harness into `pnpm test` via `pnpm run test:installer`.
|
||||||
|
- Real temp-prefix fast install succeeded with `@mosaicstack/gateway@0.0.7-next.1633` and `@mosaicstack/mosaic@0.0.49-next.1633`.
|
||||||
@@ -7,7 +7,8 @@
|
|||||||
"dev": "turbo run dev",
|
"dev": "turbo run dev",
|
||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
"typecheck": "turbo run typecheck",
|
"typecheck": "turbo run typecheck",
|
||||||
"test": "turbo run test",
|
"test": "turbo run test && pnpm run test:installer",
|
||||||
|
"test:installer": "bash tools/install-next-lane.test.sh",
|
||||||
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
||||||
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ The installer:
|
|||||||
|
|
||||||
### Install lanes
|
### Install lanes
|
||||||
|
|
||||||
| Lane | Command | Use when | Source |
|
| Lane | Command | Use when | Source |
|
||||||
| ------------------------ | ------------------------------------- | ---------------------------------------------- | ------------------------------------------ |
|
| ------------------------ | ------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||||
| Stable | `bash tools/install.sh` | You want the released framework and CLI | npm `@mosaicstack/mosaic@latest` + `main` |
|
| Stable | `bash tools/install.sh` | You want the released framework and CLI | npm `@mosaicstack/mosaic@latest` + `main` |
|
||||||
| Prerelease integration | `bash tools/install.sh --next` | You want the permanent `next` integration lane | Build-from-source at `next` |
|
| Prerelease integration | `bash tools/install.sh --next` | You want the permanent `next` integration lane | Fast npm `@mosaicstack/mosaic@next` + `@mosaicstack/gateway@next`; source fallback at `next` |
|
||||||
| Contributor/source build | `bash tools/install.sh --dev --ref X` | You are validating a branch before release | Build-from-source at the requested git ref |
|
| Contributor/source build | `bash tools/install.sh --dev --ref X` | You are validating a branch before release | Build-from-source at the requested git ref |
|
||||||
|
|
||||||
`--next` is shorthand for source-build mode at `next`; explicit `--ref` or `MOSAIC_REF` wins when both are present.
|
`--next` is fast-by-default from the Gitea npm `next` dist-tag and falls back to a source build at the permanent `next` branch if the dist-tag is missing or unreachable. Explicit `--ref` or `MOSAIC_REF` wins and uses the source path.
|
||||||
|
|
||||||
## First Run
|
## First Run
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ The installer preserves local `SOUL.md`, `USER.md`, `TOOLS.md`, and `memory/` by
|
|||||||
bash tools/install.sh --check # Version check only
|
bash tools/install.sh --check # Version check only
|
||||||
bash tools/install.sh --framework # Framework only (skip npm CLI)
|
bash tools/install.sh --framework # Framework only (skip npm CLI)
|
||||||
bash tools/install.sh --cli # npm CLI only (skip framework)
|
bash tools/install.sh --cli # npm CLI only (skip framework)
|
||||||
bash tools/install.sh --next # Prerelease lane: source build from next
|
bash tools/install.sh --next # Prerelease lane: npm @next, source fallback
|
||||||
bash tools/install.sh --dev # Contributor lane: source build at --ref/main
|
bash tools/install.sh --dev # Contributor lane: source build at --ref/main
|
||||||
bash tools/install.sh --ref v1.0 # Install from a specific git ref (--ref wins over --next)
|
bash tools/install.sh --ref v1.0 # Install from a specific git ref (--ref wins over --next)
|
||||||
```
|
```
|
||||||
|
|||||||
222
tools/install-next-lane.test.sh
Executable file
222
tools/install-next-lane.test.sh
Executable file
@@ -0,0 +1,222 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TMP="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-next-install-test-XXXXXX")"
|
||||||
|
trap 'rm -rf "$TMP"' EXIT
|
||||||
|
|
||||||
|
FAKE_BIN="$TMP/bin"
|
||||||
|
HOME_DIR="$TMP/home"
|
||||||
|
PREFIX="$TMP/prefix"
|
||||||
|
MOSAIC_HOME="$TMP/mosaic"
|
||||||
|
STATE="$TMP/state"
|
||||||
|
LOG="$TMP/npm.log"
|
||||||
|
mkdir -p "$FAKE_BIN" "$HOME_DIR" "$STATE"
|
||||||
|
|
||||||
|
cat > "$FAKE_BIN/npm" <<'FAKE_NPM'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
LOG="${MOSAIC_TEST_NPM_LOG:?}"
|
||||||
|
STATE="${MOSAIC_TEST_STATE:?}"
|
||||||
|
echo "$*" >> "$LOG"
|
||||||
|
|
||||||
|
if [[ "$1" == "view" ]]; then
|
||||||
|
case "$2 $3" in
|
||||||
|
"@mosaicstack/mosaic@next version") echo "0.0.49-next.999" ;;
|
||||||
|
"@mosaicstack/gateway@next version") echo "${MOSAIC_TEST_GATEWAY_NEXT_VERSION:-0.0.7-next.999}" ;;
|
||||||
|
"@mosaicstack/mosaic version") echo "0.0.48" ;;
|
||||||
|
*) echo "unexpected npm view: $*" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$1" == "install" ]]; then
|
||||||
|
case "$*" in
|
||||||
|
*"@mosaicstack/mosaic@0.0.49-next.999"*)
|
||||||
|
echo "0.0.49-next.999" > "$STATE/mosaic"
|
||||||
|
;;
|
||||||
|
*"@mosaicstack/gateway@0.0.7-next.999"*)
|
||||||
|
if [[ "${MOSAIC_TEST_FAIL_NEXT_GATEWAY_INSTALL:-0}" == "1" ]]; then
|
||||||
|
echo "forced gateway install failure" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "0.0.7-next.999" > "$STATE/gateway"
|
||||||
|
;;
|
||||||
|
*"mosaicstack-mosaic-0.0.0-source.tgz"*)
|
||||||
|
echo "0.0.0-source" > "$STATE/mosaic"
|
||||||
|
;;
|
||||||
|
*"mosaicstack-gateway-0.0.0-source.tgz"*)
|
||||||
|
echo "0.0.0-source" > "$STATE/gateway"
|
||||||
|
;;
|
||||||
|
*) echo "unexpected npm install: $*" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$1" == "ls" ]]; then
|
||||||
|
cli="$(cat "$STATE/mosaic" 2>/dev/null || true)"
|
||||||
|
gateway="$(cat "$STATE/gateway" 2>/dev/null || true)"
|
||||||
|
node -e '
|
||||||
|
const cli = process.argv[1];
|
||||||
|
const gateway = process.argv[2];
|
||||||
|
const dependencies = {};
|
||||||
|
if (cli) dependencies["@mosaicstack/mosaic"] = { version: cli };
|
||||||
|
if (gateway) dependencies["@mosaicstack/gateway"] = { version: gateway };
|
||||||
|
process.stdout.write(JSON.stringify({ dependencies }));
|
||||||
|
' "$cli" "$gateway"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "unexpected npm command: $*" >&2
|
||||||
|
exit 1
|
||||||
|
FAKE_NPM
|
||||||
|
chmod +x "$FAKE_BIN/npm"
|
||||||
|
|
||||||
|
cat > "$FAKE_BIN/curl" <<'FAKE_CURL'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
# The fake tar creates the source tree; curl only needs to keep the pipe alive.
|
||||||
|
exit 0
|
||||||
|
FAKE_CURL
|
||||||
|
chmod +x "$FAKE_BIN/curl"
|
||||||
|
|
||||||
|
cat > "$FAKE_BIN/tar" <<'FAKE_TAR'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
dest=""
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-C) dest="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [[ -z "$dest" ]]; then
|
||||||
|
echo "fake tar missing -C destination" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$dest/stack/packages/mosaic" "$dest/stack/apps/gateway"
|
||||||
|
FAKE_TAR
|
||||||
|
chmod +x "$FAKE_BIN/tar"
|
||||||
|
|
||||||
|
cat > "$FAKE_BIN/pnpm" <<'FAKE_PNPM'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
LOG="${MOSAIC_TEST_NPM_LOG:?}"
|
||||||
|
echo "pnpm $*" >> "$LOG"
|
||||||
|
|
||||||
|
if [[ "$1" == "pack" ]]; then
|
||||||
|
out=""
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--pack-destination) out="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [[ -z "$out" ]]; then
|
||||||
|
echo "fake pnpm pack missing destination" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$out"
|
||||||
|
case "$PWD" in
|
||||||
|
*/apps/gateway) touch "$out/mosaicstack-gateway-0.0.0-source.tgz" ;;
|
||||||
|
*/packages/mosaic) touch "$out/mosaicstack-mosaic-0.0.0-source.tgz" ;;
|
||||||
|
*) echo "unexpected pnpm pack cwd: $PWD" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# install/build commands are no-ops in this harness.
|
||||||
|
exit 0
|
||||||
|
FAKE_PNPM
|
||||||
|
chmod +x "$FAKE_BIN/pnpm"
|
||||||
|
|
||||||
|
reset_state() {
|
||||||
|
: > "$LOG"
|
||||||
|
rm -f "$STATE"/*
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_state
|
||||||
|
echo "[test] --next fast path pins resolved package versions"
|
||||||
|
OUTPUT="$(
|
||||||
|
HOME="$HOME_DIR" \
|
||||||
|
MOSAIC_HOME="$MOSAIC_HOME" \
|
||||||
|
MOSAIC_PREFIX="$PREFIX" \
|
||||||
|
MOSAIC_NO_COLOR=1 \
|
||||||
|
MOSAIC_TEST_NPM_LOG="$LOG" \
|
||||||
|
MOSAIC_TEST_STATE="$STATE" \
|
||||||
|
PATH="$FAKE_BIN:$PATH" \
|
||||||
|
bash "$ROOT/tools/install.sh" --cli --next --yes --no-auto-launch
|
||||||
|
)"
|
||||||
|
|
||||||
|
grep -qF 'Installed @next packages: CLI 0.0.49-next.999, gateway 0.0.7-next.999' <<<"$OUTPUT"
|
||||||
|
grep -qF 'install -g @mosaicstack/gateway@0.0.7-next.999' "$LOG"
|
||||||
|
grep -qF 'install -g @mosaicstack/mosaic@0.0.49-next.999' "$LOG"
|
||||||
|
if grep -qE '^install -g .+@next( |$)' "$LOG"; then
|
||||||
|
echo "expected exact-version installs, found mutable @next install" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if grep -qF 'Downloading source from next' <<<"$OUTPUT"; then
|
||||||
|
echo "fast path unexpectedly fell back to source" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
reset_state
|
||||||
|
echo "[test] fast path failure falls back to source build"
|
||||||
|
OUTPUT="$(
|
||||||
|
HOME="$HOME_DIR" \
|
||||||
|
MOSAIC_HOME="$MOSAIC_HOME" \
|
||||||
|
MOSAIC_PREFIX="$PREFIX" \
|
||||||
|
MOSAIC_NO_COLOR=1 \
|
||||||
|
MOSAIC_TEST_NPM_LOG="$LOG" \
|
||||||
|
MOSAIC_TEST_STATE="$STATE" \
|
||||||
|
MOSAIC_TEST_FAIL_NEXT_GATEWAY_INSTALL=1 \
|
||||||
|
PATH="$FAKE_BIN:$PATH" \
|
||||||
|
bash "$ROOT/tools/install.sh" --cli --next --yes --no-auto-launch
|
||||||
|
)"
|
||||||
|
|
||||||
|
grep -qF 'Fast gateway @next install failed.' <<<"$OUTPUT"
|
||||||
|
grep -qF 'Falling back to source build at ref next; --next will not hard-fail on registry issues.' <<<"$OUTPUT"
|
||||||
|
grep -qF 'Downloading source from next' <<<"$OUTPUT"
|
||||||
|
grep -qF 'Installed from source: CLI 0.0.0-source' <<<"$OUTPUT"
|
||||||
|
grep -qF 'install -g @mosaicstack/mosaic@0.0.49-next.999' "$LOG"
|
||||||
|
grep -qE 'install -g .*/mosaicstack-gateway-0\.0\.0-source\.tgz' "$LOG"
|
||||||
|
grep -qE 'install -g .*/mosaicstack-mosaic-0\.0\.0-source\.tgz' "$LOG"
|
||||||
|
[[ "$(cat "$STATE/mosaic")" == "0.0.0-source" ]]
|
||||||
|
[[ "$(cat "$STATE/gateway")" == "0.0.0-source" ]]
|
||||||
|
|
||||||
|
reset_state
|
||||||
|
echo "[test] explicit --ref keeps source lane and avoids @next lookup"
|
||||||
|
OUTPUT="$(
|
||||||
|
HOME="$HOME_DIR" \
|
||||||
|
MOSAIC_HOME="$MOSAIC_HOME" \
|
||||||
|
MOSAIC_PREFIX="$PREFIX" \
|
||||||
|
MOSAIC_NO_COLOR=1 \
|
||||||
|
MOSAIC_TEST_NPM_LOG="$LOG" \
|
||||||
|
MOSAIC_TEST_STATE="$STATE" \
|
||||||
|
PATH="$FAKE_BIN:$PATH" \
|
||||||
|
bash "$ROOT/tools/install.sh" --check --cli --next --ref feature-x
|
||||||
|
)"
|
||||||
|
|
||||||
|
grep -qF 'explicit ref wins, build-from-source' <<<"$OUTPUT"
|
||||||
|
if grep -qF '@next version' "$LOG"; then
|
||||||
|
echo "explicit ref should not query @next dist-tags" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
reset_state
|
||||||
|
echo "[test] --check --next warns on mismatched prerelease pipeline suffixes"
|
||||||
|
OUTPUT="$(
|
||||||
|
HOME="$HOME_DIR" \
|
||||||
|
MOSAIC_HOME="$MOSAIC_HOME" \
|
||||||
|
MOSAIC_PREFIX="$PREFIX" \
|
||||||
|
MOSAIC_NO_COLOR=1 \
|
||||||
|
MOSAIC_TEST_NPM_LOG="$LOG" \
|
||||||
|
MOSAIC_TEST_STATE="$STATE" \
|
||||||
|
MOSAIC_TEST_GATEWAY_NEXT_VERSION="0.0.7-next.1000" \
|
||||||
|
PATH="$FAKE_BIN:$PATH" \
|
||||||
|
bash "$ROOT/tools/install.sh" --check --cli --next
|
||||||
|
)"
|
||||||
|
|
||||||
|
grep -qF '@next registry lane incomplete, mismatched, or unreachable; --next would fall back to source.' <<<"$OUTPUT"
|
||||||
|
|
||||||
|
echo "[test] installer next lane tests passed"
|
||||||
146
tools/install.sh
146
tools/install.sh
@@ -16,9 +16,10 @@
|
|||||||
# --framework Install/upgrade framework only (skip npm CLI)
|
# --framework Install/upgrade framework only (skip npm CLI)
|
||||||
# --cli Install/upgrade npm CLI only (skip framework)
|
# --cli Install/upgrade npm CLI only (skip framework)
|
||||||
# --ref <branch> Git ref for framework archive (default: main)
|
# --ref <branch> Git ref for framework archive (default: main)
|
||||||
# --next Prerelease lane: build CLI + gateway FROM SOURCE at the
|
# --next Prerelease lane: try fast npm @next install for CLI +
|
||||||
# permanent next integration branch. Shorthand for --dev
|
# gateway from the Gitea registry, then fall back to a
|
||||||
# with ref=next; explicit --ref/MOSAIC_REF wins.
|
# source build at next if unavailable. Explicit
|
||||||
|
# --ref/MOSAIC_REF wins and uses the source path.
|
||||||
# --dev Build CLI + gateway FROM SOURCE at --ref instead of the
|
# --dev Build CLI + gateway FROM SOURCE at --ref instead of the
|
||||||
# registry @latest. Zero registry writes — packs local
|
# registry @latest. Zero registry writes — packs local
|
||||||
# tarballs and installs them globally. Use to test a branch
|
# tarballs and installs them globally. Use to test a branch
|
||||||
@@ -70,10 +71,10 @@ if [[ "${MOSAIC_DEV:-0}" == "1" ]]; then
|
|||||||
FLAG_DEV=true
|
FLAG_DEV=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# MOSAIC_NEXT env var acts the same as --next: source build from the
|
# MOSAIC_NEXT env var acts the same as --next: fast npm @next install with
|
||||||
# permanent next integration branch unless MOSAIC_REF/--ref explicitly wins.
|
# source fallback from the permanent next integration branch unless
|
||||||
|
# MOSAIC_REF/--ref explicitly wins.
|
||||||
if [[ "${MOSAIC_NEXT:-0}" == "1" ]]; then
|
if [[ "${MOSAIC_NEXT:-0}" == "1" ]]; then
|
||||||
FLAG_DEV=true
|
|
||||||
FLAG_NEXT=true
|
FLAG_NEXT=true
|
||||||
if [[ "$GIT_REF_EXPLICIT" == "false" ]]; then
|
if [[ "$GIT_REF_EXPLICIT" == "false" ]]; then
|
||||||
GIT_REF="next"
|
GIT_REF="next"
|
||||||
@@ -87,7 +88,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--cli) FLAG_FRAMEWORK=false; shift ;;
|
--cli) FLAG_FRAMEWORK=false; shift ;;
|
||||||
--ref) GIT_REF="${2:-main}"; GIT_REF_EXPLICIT=true; shift 2 ;;
|
--ref) GIT_REF="${2:-main}"; GIT_REF_EXPLICIT=true; shift 2 ;;
|
||||||
--dev) FLAG_DEV=true; shift ;;
|
--dev) FLAG_DEV=true; shift ;;
|
||||||
--next) FLAG_DEV=true; FLAG_NEXT=true; if [[ "$GIT_REF_EXPLICIT" == "false" ]]; then GIT_REF="next"; fi; shift ;;
|
--next) FLAG_NEXT=true; if [[ "$GIT_REF_EXPLICIT" == "false" ]]; then GIT_REF="next"; fi; shift ;;
|
||||||
--yes|-y) FLAG_YES=true; shift ;;
|
--yes|-y) FLAG_YES=true; shift ;;
|
||||||
--no-auto-launch) FLAG_NO_AUTO_LAUNCH=true; shift ;;
|
--no-auto-launch) FLAG_NO_AUTO_LAUNCH=true; shift ;;
|
||||||
--uninstall) FLAG_UNINSTALL=true; shift ;;
|
--uninstall) FLAG_UNINSTALL=true; shift ;;
|
||||||
@@ -95,6 +96,13 @@ while [[ $# -gt 0 ]]; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Explicit refs represent a request for that exact source tree. Keep --next as
|
||||||
|
# a lane selector, but do not install the registry @next package for a different
|
||||||
|
# ref than the permanent next branch.
|
||||||
|
if [[ "$FLAG_NEXT" == "true" && "$GIT_REF_EXPLICIT" == "true" ]]; then
|
||||||
|
FLAG_DEV=true
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$FLAG_YES" == "true" ]]; then
|
if [[ "$FLAG_YES" == "true" ]]; then
|
||||||
export MOSAIC_ASSUME_YES=1
|
export MOSAIC_ASSUME_YES=1
|
||||||
fi
|
fi
|
||||||
@@ -105,6 +113,7 @@ REGISTRY="${MOSAIC_REGISTRY:-https://git.mosaicstack.dev/api/packages/mosaicstac
|
|||||||
SCOPE="${MOSAIC_SCOPE:-@mosaicstack}"
|
SCOPE="${MOSAIC_SCOPE:-@mosaicstack}"
|
||||||
PREFIX="${MOSAIC_PREFIX:-$HOME/.npm-global}"
|
PREFIX="${MOSAIC_PREFIX:-$HOME/.npm-global}"
|
||||||
CLI_PKG="${SCOPE}/mosaic"
|
CLI_PKG="${SCOPE}/mosaic"
|
||||||
|
GATEWAY_PKG="${SCOPE}/gateway"
|
||||||
REPO_BASE="https://git.mosaicstack.dev/mosaicstack/stack"
|
REPO_BASE="https://git.mosaicstack.dev/mosaicstack/stack"
|
||||||
ARCHIVE_URL="${REPO_BASE}/archive/${GIT_REF}.tar.gz"
|
ARCHIVE_URL="${REPO_BASE}/archive/${GIT_REF}.tar.gz"
|
||||||
|
|
||||||
@@ -252,9 +261,15 @@ fail() { echo "${R}✖${RESET} $*" >&2; }
|
|||||||
dim() { echo "${DIM}$*${RESET}"; }
|
dim() { echo "${DIM}$*${RESET}"; }
|
||||||
step() { echo ""; echo "${BOLD}$*${RESET}"; }
|
step() { echo ""; echo "${BOLD}$*${RESET}"; }
|
||||||
|
|
||||||
|
is_next_registry_lane() {
|
||||||
|
[[ "$FLAG_NEXT" == "true" && "$FLAG_DEV" == "false" && "$GIT_REF" == "next" && "$GIT_REF_EXPLICIT" == "false" ]]
|
||||||
|
}
|
||||||
|
|
||||||
source_ref_details() {
|
source_ref_details() {
|
||||||
if [[ "$FLAG_NEXT" == "true" && "$GIT_REF" == "next" ]]; then
|
if is_next_registry_lane; then
|
||||||
echo "ref: next, --next prerelease lane"
|
echo "ref: next, --next prerelease lane"
|
||||||
|
elif [[ "$FLAG_NEXT" == "true" && "$GIT_REF" == "next" ]]; then
|
||||||
|
echo "ref: next, --next prerelease lane (build-from-source)"
|
||||||
elif [[ "$FLAG_NEXT" == "true" ]]; then
|
elif [[ "$FLAG_NEXT" == "true" ]]; then
|
||||||
echo "ref: ${GIT_REF}, --next requested, explicit ref wins"
|
echo "ref: ${GIT_REF}, --next requested, explicit ref wins"
|
||||||
else
|
else
|
||||||
@@ -284,10 +299,43 @@ installed_cli_version() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
installed_gateway_version() {
|
||||||
|
local json
|
||||||
|
json="$(npm ls -g --depth=0 --json --prefix="$PREFIX" 2>/dev/null)" || true
|
||||||
|
if [[ -n "$json" ]]; then
|
||||||
|
node -e "
|
||||||
|
const d = JSON.parse(process.argv[1]);
|
||||||
|
const v = d?.dependencies?.['${GATEWAY_PKG}']?.version ?? '';
|
||||||
|
process.stdout.write(v);
|
||||||
|
" "$json" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
latest_cli_version() {
|
latest_cli_version() {
|
||||||
npm view "${CLI_PKG}" version --registry="$REGISTRY" 2>/dev/null || true
|
npm view "${CLI_PKG}" version --registry="$REGISTRY" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next_cli_version() {
|
||||||
|
npm view "${CLI_PKG}@next" version --registry="$REGISTRY" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
next_gateway_version() {
|
||||||
|
npm view "${GATEWAY_PKG}@next" version --registry="$REGISTRY" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
next_pipeline_suffix() {
|
||||||
|
printf '%s' "$1" | sed -n 's/.*-next\.\([0-9][0-9]*\)$/\1/p'
|
||||||
|
}
|
||||||
|
|
||||||
|
next_versions_share_pipeline() {
|
||||||
|
local cli_next="$1"
|
||||||
|
local gateway_next="$2"
|
||||||
|
local cli_pipeline gateway_pipeline
|
||||||
|
cli_pipeline="$(next_pipeline_suffix "$cli_next")"
|
||||||
|
gateway_pipeline="$(next_pipeline_suffix "$gateway_next")"
|
||||||
|
[[ -n "$cli_pipeline" && -n "$gateway_pipeline" && "$cli_pipeline" == "$gateway_pipeline" ]]
|
||||||
|
}
|
||||||
|
|
||||||
version_lt() {
|
version_lt() {
|
||||||
node -e "
|
node -e "
|
||||||
const a=process.argv[1], b=process.argv[2];
|
const a=process.argv[1], b=process.argv[2];
|
||||||
@@ -403,6 +451,49 @@ install_cli_from_source() {
|
|||||||
ok "Installed from source: CLI $(installed_cli_version)"
|
ok "Installed from source: CLI $(installed_cli_version)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_next_cli_from_registry() {
|
||||||
|
local cli_next gateway_next
|
||||||
|
cli_next="$(next_cli_version)"
|
||||||
|
gateway_next="$(next_gateway_version)"
|
||||||
|
|
||||||
|
if [[ -z "$cli_next" ]]; then
|
||||||
|
warn "${CLI_PKG}@next is unavailable from $REGISTRY."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ -z "$gateway_next" ]]; then
|
||||||
|
warn "${GATEWAY_PKG}@next is unavailable from $REGISTRY."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! next_versions_share_pipeline "$cli_next" "$gateway_next"; then
|
||||||
|
warn "@next CLI/gateway versions do not share a pipeline suffix (${cli_next}, ${gateway_next})."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Installing ${CLI_PKG}@${cli_next} from registry…"
|
||||||
|
if ! npm install -g "${CLI_PKG}@${cli_next}" --prefix="$PREFIX" 2>&1 | sed 's/^/ /'; then
|
||||||
|
warn "Fast CLI @next install failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Installing ${GATEWAY_PKG}@${gateway_next} from registry…"
|
||||||
|
if ! npm install -g "${GATEWAY_PKG}@${gateway_next}" --prefix="$PREFIX" 2>&1 | sed 's/^/ /'; then
|
||||||
|
warn "Fast gateway @next install failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local installed_cli installed_gateway
|
||||||
|
installed_cli="$(installed_cli_version)"
|
||||||
|
installed_gateway="$(installed_gateway_version)"
|
||||||
|
if [[ "$installed_cli" != "$cli_next" || "$installed_gateway" != "$gateway_next" ]]; then
|
||||||
|
warn "Installed @next versions did not match resolved versions (CLI: ${installed_cli:-missing}, gateway: ${installed_gateway:-missing})."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1
|
||||||
|
ok "Installed @next packages: CLI ${installed_cli}, gateway ${installed_gateway}"
|
||||||
|
}
|
||||||
|
|
||||||
# ─── preflight ────────────────────────────────────────────────────────────────
|
# ─── preflight ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
require_cmd node
|
require_cmd node
|
||||||
@@ -503,8 +594,12 @@ if [[ "$FLAG_CLI" == "true" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
CURRENT="$(installed_cli_version)"
|
CURRENT="$(installed_cli_version)"
|
||||||
|
NEXT_GATEWAY=""
|
||||||
if [[ "$FLAG_DEV" == "true" ]]; then
|
if [[ "$FLAG_DEV" == "true" ]]; then
|
||||||
LATEST=""
|
LATEST=""
|
||||||
|
elif is_next_registry_lane; then
|
||||||
|
LATEST="$(next_cli_version)"
|
||||||
|
NEXT_GATEWAY="$(next_gateway_version)"
|
||||||
else
|
else
|
||||||
LATEST="$(latest_cli_version)"
|
LATEST="$(latest_cli_version)"
|
||||||
fi
|
fi
|
||||||
@@ -517,6 +612,18 @@ if [[ "$FLAG_CLI" == "true" ]]; then
|
|||||||
|
|
||||||
if [[ "$FLAG_DEV" == "true" ]]; then
|
if [[ "$FLAG_DEV" == "true" ]]; then
|
||||||
dim " Source: ${REPO_BASE} ($(source_ref_details), build-from-source)"
|
dim " Source: ${REPO_BASE} ($(source_ref_details), build-from-source)"
|
||||||
|
elif is_next_registry_lane; then
|
||||||
|
if [[ -n "$LATEST" ]]; then
|
||||||
|
dim " Next CLI: ${CLI_PKG}@${LATEST}"
|
||||||
|
else
|
||||||
|
dim " Next CLI: (registry @next unreachable)"
|
||||||
|
fi
|
||||||
|
if [[ -n "$NEXT_GATEWAY" ]]; then
|
||||||
|
dim " Next GW: ${GATEWAY_PKG}@${NEXT_GATEWAY}"
|
||||||
|
else
|
||||||
|
dim " Next GW: (registry @next unreachable)"
|
||||||
|
fi
|
||||||
|
dim " Fallback: ${REPO_BASE} (ref: next, build-from-source)"
|
||||||
elif [[ -n "$LATEST" ]]; then
|
elif [[ -n "$LATEST" ]]; then
|
||||||
dim " Latest: ${CLI_PKG}@${LATEST}"
|
dim " Latest: ${CLI_PKG}@${LATEST}"
|
||||||
else
|
else
|
||||||
@@ -527,6 +634,12 @@ if [[ "$FLAG_CLI" == "true" ]]; then
|
|||||||
if [[ "$FLAG_CHECK" == "true" ]]; then
|
if [[ "$FLAG_CHECK" == "true" ]]; then
|
||||||
if [[ "$FLAG_DEV" == "true" ]]; then
|
if [[ "$FLAG_DEV" == "true" ]]; then
|
||||||
info "Dev mode: installed version is ${CURRENT:-(none)} (no registry comparison)."
|
info "Dev mode: installed version is ${CURRENT:-(none)} (no registry comparison)."
|
||||||
|
elif is_next_registry_lane; then
|
||||||
|
if [[ -n "$LATEST" && -n "$NEXT_GATEWAY" ]] && next_versions_share_pipeline "$LATEST" "$NEXT_GATEWAY"; then
|
||||||
|
ok "@next registry lane available: ${CLI_PKG}@${LATEST}, ${GATEWAY_PKG}@${NEXT_GATEWAY}."
|
||||||
|
else
|
||||||
|
warn "@next registry lane incomplete, mismatched, or unreachable; --next would fall back to source."
|
||||||
|
fi
|
||||||
elif [[ -z "$LATEST" ]]; then
|
elif [[ -z "$LATEST" ]]; then
|
||||||
warn "Could not reach registry."
|
warn "Could not reach registry."
|
||||||
elif [[ -z "$CURRENT" ]]; then
|
elif [[ -z "$CURRENT" ]]; then
|
||||||
@@ -543,6 +656,23 @@ if [[ "$FLAG_CLI" == "true" ]]; then
|
|||||||
ensure_monorepo
|
ensure_monorepo
|
||||||
install_cli_from_source
|
install_cli_from_source
|
||||||
|
|
||||||
|
# PATH check for npm prefix
|
||||||
|
if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then
|
||||||
|
warn "$PREFIX/bin is not on your PATH"
|
||||||
|
dim " Add to your shell rc: export PATH=\"$PREFIX/bin:\$PATH\""
|
||||||
|
fi
|
||||||
|
elif is_next_registry_lane; then
|
||||||
|
info "Next mode — trying fast npm @next install from ${REGISTRY}…"
|
||||||
|
if install_next_cli_from_registry; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
warn "Falling back to source build at ref ${GIT_REF}; --next will not hard-fail on registry issues."
|
||||||
|
unset MOSAIC_GATEWAY_SKIP_NPM_INSTALL
|
||||||
|
ensure_monorepo
|
||||||
|
install_cli_from_source
|
||||||
|
export MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1
|
||||||
|
fi
|
||||||
|
|
||||||
# PATH check for npm prefix
|
# PATH check for npm prefix
|
||||||
if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then
|
if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then
|
||||||
warn "$PREFIX/bin is not on your PATH"
|
warn "$PREFIX/bin is not on your PATH"
|
||||||
|
|||||||
Reference in New Issue
Block a user