@flytedesk/app-kit 3.2.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -10
- package/dist/auth/client/httpClient.d.ts +79 -1
- package/dist/auth/client/httpClient.js +185 -16
- package/dist/auth/client/httpClient.js.map +1 -1
- package/dist/auth/client/index.d.ts +4 -4
- package/dist/auth/client/index.js +3 -3
- package/dist/auth/client/index.js.map +1 -1
- package/dist/auth/client/useAuthSession.d.ts +61 -13
- package/dist/auth/client/useAuthSession.js +185 -42
- package/dist/auth/client/useAuthSession.js.map +1 -1
- package/dist/auth/errors.d.ts +30 -7
- package/dist/auth/errors.js +38 -8
- package/dist/auth/errors.js.map +1 -1
- package/dist/auth/guards.d.ts +12 -0
- package/dist/auth/guards.js +24 -6
- package/dist/auth/guards.js.map +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/index.js +2 -2
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/oidc-client.js +12 -2
- package/dist/auth/oidc-client.js.map +1 -1
- package/dist/auth/plugin.d.ts +6 -0
- package/dist/auth/plugin.js +532 -304
- package/dist/auth/plugin.js.map +1 -1
- package/dist/auth/shared-types.d.ts +28 -1
- package/dist/auth/shared-types.js +35 -0
- package/dist/auth/shared-types.js.map +1 -1
- package/dist/auth/silentAuthPage.d.ts +20 -0
- package/dist/auth/silentAuthPage.js +75 -0
- package/dist/auth/silentAuthPage.js.map +1 -0
- package/dist/auth/testing/fake-idp.d.ts +35 -0
- package/dist/auth/testing/fake-idp.js +86 -2
- package/dist/auth/testing/fake-idp.js.map +1 -1
- package/dist/auth/testing/index.d.ts +3 -1
- package/dist/auth/testing/index.js +3 -1
- package/dist/auth/testing/index.js.map +1 -1
- package/dist/auth/testing/memory-store.d.ts +44 -0
- package/dist/auth/testing/memory-store.js +120 -0
- package/dist/auth/testing/memory-store.js.map +1 -0
- package/dist/auth/tokens.d.ts +24 -0
- package/dist/auth/tokens.js +31 -1
- package/dist/auth/tokens.js.map +1 -1
- package/dist/auth/types.d.ts +231 -96
- package/dist/auth/unavailable.d.ts +10 -0
- package/dist/auth/unavailable.js +11 -0
- package/dist/auth/unavailable.js.map +1 -0
- package/dist/bigquery/errors.d.ts +21 -3
- package/dist/bigquery/errors.js +66 -12
- package/dist/bigquery/errors.js.map +1 -1
- package/dist/bigquery/index.d.ts +5 -3
- package/dist/bigquery/index.js +4 -2
- package/dist/bigquery/index.js.map +1 -1
- package/dist/bigquery/query.d.ts +9 -2
- package/dist/bigquery/query.js +38 -16
- package/dist/bigquery/query.js.map +1 -1
- package/dist/bigquery/types.d.ts +45 -1
- package/dist/flags/plugin.js +4 -20
- package/dist/flags/plugin.js.map +1 -1
- package/dist/postgres/advisoryLock.d.ts +58 -0
- package/dist/postgres/advisoryLock.js +71 -0
- package/dist/postgres/advisoryLock.js.map +1 -0
- package/dist/postgres/index.d.ts +6 -0
- package/dist/postgres/index.js +7 -0
- package/dist/postgres/index.js.map +1 -0
- package/dist/profile/plugin.js +3 -20
- package/dist/profile/plugin.js.map +1 -1
- package/package.json +8 -2
- package/scripts/pending-release-count.mjs +0 -37
- package/scripts/release.sh +0 -126
- package/scripts/release.test.ts +0 -205
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flytedesk/app-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Shared platform kit for flytedesk apps: flytedesk-id auth (BFF/OIDC client) and a Postgres-native trace/audit layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
27
|
"prisma",
|
|
28
|
-
"scripts"
|
|
28
|
+
"scripts/postinstall-eslint-ts6-shim.cjs"
|
|
29
29
|
],
|
|
30
30
|
"bin": {
|
|
31
31
|
"trace-sync": "./dist/cli/trace-sync.js",
|
|
@@ -77,6 +77,10 @@
|
|
|
77
77
|
"./spa-server": {
|
|
78
78
|
"types": "./dist/spa-server/index.d.ts",
|
|
79
79
|
"import": "./dist/spa-server/index.js"
|
|
80
|
+
},
|
|
81
|
+
"./postgres": {
|
|
82
|
+
"types": "./dist/postgres/index.d.ts",
|
|
83
|
+
"import": "./dist/postgres/index.js"
|
|
80
84
|
}
|
|
81
85
|
},
|
|
82
86
|
"scripts": {
|
|
@@ -116,10 +120,12 @@
|
|
|
116
120
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
117
121
|
"@changesets/changelog-github": "^1.0.0",
|
|
118
122
|
"@changesets/cli": "^3.0.1",
|
|
123
|
+
"@google-cloud/bigquery": "^9.1.0",
|
|
119
124
|
"@prisma/client": "^7.10.0",
|
|
120
125
|
"@testcontainers/postgresql": "^12.1.0",
|
|
121
126
|
"@testing-library/react": "^16.3.3",
|
|
122
127
|
"@types/cross-spawn": "^6.0.6",
|
|
128
|
+
"@types/jsdom": "^30.0.0",
|
|
123
129
|
"@types/node": "^24.0.0",
|
|
124
130
|
"@types/pg": "^8.23.1",
|
|
125
131
|
"@types/react": "^19.3.0",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Prints how many package releases the changesets committed in the current
|
|
3
|
-
// directory would produce. scripts/release.sh uses it to decide between
|
|
4
|
-
// "version + publish" and "no-op".
|
|
5
|
-
//
|
|
6
|
-
// It counts every changeset on disk. It deliberately does NOT pass
|
|
7
|
-
// `--since=origin/main`: a release runs from a clone of main, where the
|
|
8
|
-
// pending changesets are already on origin/main, so `--since` always
|
|
9
|
-
// reports zero. That is exactly how the first real release run failed
|
|
10
|
-
// (AK-13, 2026-09-24).
|
|
11
|
-
import { spawnSync } from "node:child_process";
|
|
12
|
-
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
13
|
-
import { createRequire } from "node:module";
|
|
14
|
-
import { tmpdir } from "node:os";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
|
|
17
|
-
const require = createRequire(import.meta.url);
|
|
18
|
-
const changesetBin = require.resolve("@changesets/cli/bin.js");
|
|
19
|
-
|
|
20
|
-
const outDir = mkdtempSync(join(tmpdir(), "changeset-status-"));
|
|
21
|
-
const outFile = join(outDir, "status.json");
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const result = spawnSync(
|
|
25
|
-
process.execPath,
|
|
26
|
-
[changesetBin, "status", `--output=${outFile}`],
|
|
27
|
-
{ cwd: process.cwd(), encoding: "utf8" },
|
|
28
|
-
);
|
|
29
|
-
if (result.status !== 0) {
|
|
30
|
-
process.stderr.write(result.stderr || result.stdout);
|
|
31
|
-
process.exit(result.status ?? 1);
|
|
32
|
-
}
|
|
33
|
-
const status = JSON.parse(readFileSync(outFile, "utf8"));
|
|
34
|
-
process.stdout.write(String(status.releases.length));
|
|
35
|
-
} finally {
|
|
36
|
-
rmSync(outDir, { recursive: true, force: true });
|
|
37
|
-
}
|
package/scripts/release.sh
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# app-kit release script.
|
|
4
|
-
#
|
|
5
|
-
# GitHub Actions no longer starts jobs for the Flytedesk org (billing
|
|
6
|
-
# refusal), so this script replaces .github/workflows/ci.yml + release.yml.
|
|
7
|
-
# It runs the exact gates CI used to run, then versions/publishes/tags
|
|
8
|
-
# exactly like the old two-stage changesets/action flow did — but from a
|
|
9
|
-
# human-triggered local run in a dedicated deploy clone, instead of GitHub.
|
|
10
|
-
#
|
|
11
|
-
# Usage:
|
|
12
|
-
# bash scripts/release.sh [<commit-or-ref>]
|
|
13
|
-
#
|
|
14
|
-
# <commit-or-ref> defaults to origin/main's tip. It must be reachable from
|
|
15
|
-
# origin/main (i.e. already merged) — this script never releases unmerged
|
|
16
|
-
# work.
|
|
17
|
-
#
|
|
18
|
-
# Run this ONLY from the sanctioned deploy clone at
|
|
19
|
-
# C:/Users/newms/deploy/app-kit (see .danxbot/config/workflow.md
|
|
20
|
-
# "Publishing" for one-time setup). It refuses to run anywhere else.
|
|
21
|
-
set -euo pipefail
|
|
22
|
-
|
|
23
|
-
log() { printf '[release] %s\n' "$1"; }
|
|
24
|
-
die() { printf '[release] ERROR: %s\n' "$1" >&2; exit 1; }
|
|
25
|
-
|
|
26
|
-
# --- Guard 1: this must be the sanctioned deploy clone ---------------------
|
|
27
|
-
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" \
|
|
28
|
-
|| die "not inside a git repository"
|
|
29
|
-
|
|
30
|
-
MARKER="$REPO_ROOT/.deploy-clone-marker"
|
|
31
|
-
[[ -f "$MARKER" ]] \
|
|
32
|
-
|| die "missing $MARKER — this is not the sanctioned deploy clone. \
|
|
33
|
-
Release runs only happen from a dedicated clone at C:/Users/newms/deploy/app-kit; \
|
|
34
|
-
see .danxbot/config/workflow.md 'Publishing' for one-time setup."
|
|
35
|
-
|
|
36
|
-
cd "$REPO_ROOT"
|
|
37
|
-
|
|
38
|
-
# --- Guard 2: refuse a dirty tree ------------------------------------------
|
|
39
|
-
if [[ -n "$(git status --porcelain)" ]]; then
|
|
40
|
-
die "working tree is dirty — refusing to release from an unclean clone. \
|
|
41
|
-
Run 'git status' in $REPO_ROOT and clean it up first."
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
# --- Guard 3: take the lock -------------------------------------------------
|
|
45
|
-
LOCKFILE="$REPO_ROOT/.release.lock"
|
|
46
|
-
if ! ( set -o noclobber; printf '%s\n' "$$" > "$LOCKFILE" ) 2>/dev/null; then
|
|
47
|
-
die "lock file $LOCKFILE already exists — another release run is in \
|
|
48
|
-
progress (or a previous run crashed without cleaning up; if you're sure \
|
|
49
|
-
none is running, delete the lock file and retry)."
|
|
50
|
-
fi
|
|
51
|
-
trap 'rm -f "$LOCKFILE"' EXIT
|
|
52
|
-
|
|
53
|
-
# --- Fetch + resolve the target commit --------------------------------------
|
|
54
|
-
log "fetching origin..."
|
|
55
|
-
git fetch origin
|
|
56
|
-
|
|
57
|
-
TARGET_REF="${1:-origin/main}"
|
|
58
|
-
TARGET_SHA="$(git rev-parse "$TARGET_REF")" \
|
|
59
|
-
|| die "could not resolve '$TARGET_REF' to a commit"
|
|
60
|
-
|
|
61
|
-
git merge-base --is-ancestor "$TARGET_SHA" origin/main \
|
|
62
|
-
|| die "$TARGET_REF ($TARGET_SHA) is not reachable from origin/main — \
|
|
63
|
-
refusing to release unmerged work."
|
|
64
|
-
|
|
65
|
-
log "target commit: $TARGET_SHA"
|
|
66
|
-
|
|
67
|
-
# Move local main onto the target commit so a version-bump commit (if any)
|
|
68
|
-
# lands on top of it and can be pushed straight to origin/main.
|
|
69
|
-
git checkout -B main "$TARGET_SHA"
|
|
70
|
-
|
|
71
|
-
# --- Install + gates ---------------------------------------------------------
|
|
72
|
-
log "npm ci..."
|
|
73
|
-
npm ci
|
|
74
|
-
|
|
75
|
-
log "running gates (npm run check: lint, typecheck, build, test, test:integration, check:exports)..."
|
|
76
|
-
npm run check
|
|
77
|
-
|
|
78
|
-
log "gates passed."
|
|
79
|
-
|
|
80
|
-
# --- Version / publish ------------------------------------------------------
|
|
81
|
-
PENDING_COUNT="$(node scripts/pending-release-count.mjs)"
|
|
82
|
-
|
|
83
|
-
CURRENT_VERSION="$(node -p "require('./package.json').version")"
|
|
84
|
-
|
|
85
|
-
if [[ "$PENDING_COUNT" -eq 0 ]]; then
|
|
86
|
-
PUBLISHED_VERSION="$(npm view @flytedesk/app-kit version)"
|
|
87
|
-
if [[ "$PUBLISHED_VERSION" == "$CURRENT_VERSION" ]]; then
|
|
88
|
-
log "no changesets pending and $CURRENT_VERSION is already on npm — no-op."
|
|
89
|
-
printf 'RESULT: sha=%s version=%s status=no-op\n' "$TARGET_SHA" "$CURRENT_VERSION"
|
|
90
|
-
exit 0
|
|
91
|
-
else
|
|
92
|
-
die "no changesets pending, but package.json ($CURRENT_VERSION) does not \
|
|
93
|
-
match the published npm version ($PUBLISHED_VERSION) — refusing to guess \
|
|
94
|
-
what to do; this needs a human to reconcile versions."
|
|
95
|
-
fi
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
log "$PENDING_COUNT changeset(s) pending — versioning..."
|
|
99
|
-
# @changesets/changelog-github looks up each changeset's commit and author on
|
|
100
|
-
# GitHub, so `changeset version` needs a token. Take it from gh-as for this one
|
|
101
|
-
# command only: it is never written to disk or exported to later steps.
|
|
102
|
-
CHANGELOG_TOKEN="$(gh-as auth token)" \
|
|
103
|
-
|| die "could not get a GitHub token from gh-as for the changelog step."
|
|
104
|
-
[[ -n "$CHANGELOG_TOKEN" ]] \
|
|
105
|
-
|| die "gh-as returned an empty GitHub token for the changelog step."
|
|
106
|
-
GITHUB_TOKEN="$CHANGELOG_TOKEN" npx changeset version
|
|
107
|
-
unset CHANGELOG_TOKEN
|
|
108
|
-
|
|
109
|
-
NEW_VERSION="$(node -p "require('./package.json').version")"
|
|
110
|
-
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] \
|
|
111
|
-
|| die "changeset version did not bump the version — aborting before publish."
|
|
112
|
-
|
|
113
|
-
log "bumped to $NEW_VERSION. Committing..."
|
|
114
|
-
git add -A
|
|
115
|
-
git commit -m "chore(release): v$NEW_VERSION"
|
|
116
|
-
|
|
117
|
-
log "pushing version-bump commit to origin/main (fails on non-fast-forward)..."
|
|
118
|
-
git push origin main
|
|
119
|
-
|
|
120
|
-
log "publishing to npm (account flytedan, token from ~/.npmrc)..."
|
|
121
|
-
npx changeset publish
|
|
122
|
-
|
|
123
|
-
log "pushing tag v$NEW_VERSION..."
|
|
124
|
-
git push origin "v$NEW_VERSION"
|
|
125
|
-
|
|
126
|
-
printf 'RESULT: sha=%s version=%s status=published\n' "$(git rev-parse HEAD)" "$NEW_VERSION"
|
package/scripts/release.test.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, beforeAll } from "vitest";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join, resolve } from "node:path";
|
|
6
|
-
|
|
7
|
-
// scripts/release.sh refuses to do anything except from the sanctioned
|
|
8
|
-
// deploy clone, with a clean tree, without a live lock, and only for a
|
|
9
|
-
// commit reachable from origin/main. Those guards run before npm ci / the
|
|
10
|
-
// gates, so they're cheap and fully testable against throwaway git repos
|
|
11
|
-
// (no Docker, no network, no real npm publish).
|
|
12
|
-
|
|
13
|
-
const SCRIPT_PATH = resolve(__dirname, "release.sh");
|
|
14
|
-
|
|
15
|
-
// On Windows, a bare `bash` on PATH can resolve to the WSL launcher in
|
|
16
|
-
// System32 instead of Git Bash, which can't reliably run against a
|
|
17
|
-
// C:/Users/... working tree. Find Git Bash explicitly when it exists;
|
|
18
|
-
// fall back to `bash` (e.g. on Linux CI, where `bash` is unambiguous).
|
|
19
|
-
function resolveBash(): string {
|
|
20
|
-
const candidates = [
|
|
21
|
-
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
22
|
-
"C:\\Program Files\\Git\\usr\\bin\\bash.exe",
|
|
23
|
-
];
|
|
24
|
-
for (const c of candidates) {
|
|
25
|
-
if (existsSync(c)) return c;
|
|
26
|
-
}
|
|
27
|
-
return "bash";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const BASH = resolveBash();
|
|
31
|
-
|
|
32
|
-
function run(cwd: string, args: string[] = []) {
|
|
33
|
-
const result = spawnSync(BASH, [SCRIPT_PATH, ...args], {
|
|
34
|
-
cwd,
|
|
35
|
-
encoding: "utf8",
|
|
36
|
-
env: { ...process.env },
|
|
37
|
-
});
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function git(cwd: string, args: string[]) {
|
|
42
|
-
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
|
|
43
|
-
if (result.status !== 0) {
|
|
44
|
-
throw new Error(
|
|
45
|
-
`git ${args.join(" ")} failed in ${cwd}: ${result.stderr}${result.stdout}`,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
return result.stdout;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function initRepoWithCommit(dir: string) {
|
|
52
|
-
git(dir, ["init", "-q", "-b", "main"]);
|
|
53
|
-
git(dir, ["config", "user.email", "test@example.com"]);
|
|
54
|
-
git(dir, ["config", "user.name", "Test"]);
|
|
55
|
-
// Ignore the script's own local-only marker/lock files so creating them
|
|
56
|
-
// doesn't itself count as "dirty" in these throwaway repos.
|
|
57
|
-
writeFileSync(join(dir, ".gitignore"), ".deploy-clone-marker\n.release.lock\n");
|
|
58
|
-
writeFileSync(join(dir, "README.md"), "test repo\n");
|
|
59
|
-
git(dir, ["add", "."]);
|
|
60
|
-
git(dir, ["commit", "-q", "-m", "initial commit"]);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function addMarker(dir: string) {
|
|
64
|
-
writeFileSync(join(dir, ".deploy-clone-marker"), "test marker\n");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
describe("scripts/release.sh guards", () => {
|
|
68
|
-
let workDir: string;
|
|
69
|
-
|
|
70
|
-
beforeAll(() => {
|
|
71
|
-
workDir = mkdtempSync(join(tmpdir(), "release-sh-test-"));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("refuses to run outside the deploy clone (no marker file)", () => {
|
|
75
|
-
const dir = mkdtempSync(join(workDir, "no-marker-"));
|
|
76
|
-
initRepoWithCommit(dir);
|
|
77
|
-
// deliberately no .deploy-clone-marker
|
|
78
|
-
|
|
79
|
-
const result = run(dir);
|
|
80
|
-
|
|
81
|
-
expect(result.status).not.toBe(0);
|
|
82
|
-
expect(result.stderr).toMatch(/not the sanctioned deploy clone/i);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("refuses a dirty working tree even with the marker present", () => {
|
|
86
|
-
const dir = mkdtempSync(join(workDir, "dirty-"));
|
|
87
|
-
initRepoWithCommit(dir);
|
|
88
|
-
addMarker(dir);
|
|
89
|
-
// Make an uncommitted, non-ignored change.
|
|
90
|
-
writeFileSync(join(dir, "README.md"), "modified, uncommitted\n");
|
|
91
|
-
|
|
92
|
-
const result = run(dir);
|
|
93
|
-
|
|
94
|
-
expect(result.status).not.toBe(0);
|
|
95
|
-
expect(result.stderr).toMatch(/dirty/i);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("refuses to run when the lock file is already held", () => {
|
|
99
|
-
const dir = mkdtempSync(join(workDir, "locked-"));
|
|
100
|
-
initRepoWithCommit(dir);
|
|
101
|
-
addMarker(dir);
|
|
102
|
-
writeFileSync(join(dir, ".release.lock"), "99999\n");
|
|
103
|
-
|
|
104
|
-
const result = run(dir);
|
|
105
|
-
|
|
106
|
-
expect(result.status).not.toBe(0);
|
|
107
|
-
expect(result.stderr).toMatch(/lock file .* already exists/i);
|
|
108
|
-
// The guard must not have deleted a lock file it didn't create itself
|
|
109
|
-
// (the trap only fires once *this* run successfully takes the lock).
|
|
110
|
-
expect(existsSync(join(dir, ".release.lock"))).toBe(true);
|
|
111
|
-
rmSync(join(dir, ".release.lock"));
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("refuses a target commit that is not reachable from origin/main", () => {
|
|
115
|
-
// A bare "origin" with just the initial commit on main.
|
|
116
|
-
const originDir = mkdtempSync(join(workDir, "origin-"));
|
|
117
|
-
git(originDir, ["init", "-q", "--bare", "-b", "main"]);
|
|
118
|
-
|
|
119
|
-
const seedDir = mkdtempSync(join(workDir, "seed-"));
|
|
120
|
-
initRepoWithCommit(seedDir);
|
|
121
|
-
git(seedDir, ["remote", "add", "origin", originDir]);
|
|
122
|
-
git(seedDir, ["push", "-q", "origin", "main"]);
|
|
123
|
-
|
|
124
|
-
// The deploy clone: clone of origin, plus a local commit on a throwaway
|
|
125
|
-
// branch that is never pushed, so it is not an ancestor of origin/main.
|
|
126
|
-
// main itself stays exactly at origin/main's tip, so the tree the
|
|
127
|
-
// guards see is clean.
|
|
128
|
-
const cloneDir = join(workDir, "clone-unreachable");
|
|
129
|
-
git(workDir, ["clone", "-q", originDir, cloneDir]);
|
|
130
|
-
addMarker(cloneDir);
|
|
131
|
-
git(cloneDir, ["checkout", "-q", "-b", "throwaway"]);
|
|
132
|
-
writeFileSync(join(cloneDir, "unmerged.txt"), "local-only change\n");
|
|
133
|
-
git(cloneDir, ["add", "unmerged.txt"]);
|
|
134
|
-
git(cloneDir, ["commit", "-q", "-m", "unmerged local commit"]);
|
|
135
|
-
const unmergedSha = git(cloneDir, ["rev-parse", "HEAD"]).trim();
|
|
136
|
-
git(cloneDir, ["checkout", "-q", "main"]);
|
|
137
|
-
|
|
138
|
-
const result = run(cloneDir, [unmergedSha]);
|
|
139
|
-
|
|
140
|
-
expect(result.status).not.toBe(0);
|
|
141
|
-
expect(result.stderr).toMatch(/not reachable from origin\/main/i);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// scripts/pending-release-count.mjs decides "version + publish" vs "no-op".
|
|
146
|
-
// A release runs from a clone of main, so pending changesets are already on
|
|
147
|
-
// origin/main. The first real release (AK-13, 2026-09-24) counted zero because
|
|
148
|
-
// it asked `changeset status --since=origin/main`; these tests pin the fix by
|
|
149
|
-
// reproducing that exact shape: changesets committed AND pushed.
|
|
150
|
-
describe("scripts/pending-release-count.mjs", () => {
|
|
151
|
-
const COUNT_SCRIPT = resolve(__dirname, "pending-release-count.mjs");
|
|
152
|
-
let workDir: string;
|
|
153
|
-
|
|
154
|
-
beforeAll(() => {
|
|
155
|
-
workDir = mkdtempSync(join(tmpdir(), "pending-count-test-"));
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
function cloneOfPushedPackage(name: string, changesets: Record<string, string>) {
|
|
159
|
-
const originDir = mkdtempSync(join(workDir, `${name}-origin-`));
|
|
160
|
-
git(originDir, ["init", "-q", "--bare", "-b", "main"]);
|
|
161
|
-
|
|
162
|
-
const seedDir = mkdtempSync(join(workDir, `${name}-seed-`));
|
|
163
|
-
initRepoWithCommit(seedDir);
|
|
164
|
-
writeFileSync(
|
|
165
|
-
join(seedDir, "package.json"),
|
|
166
|
-
JSON.stringify({ name: "@test/pkg", version: "0.3.2" }, null, 2),
|
|
167
|
-
);
|
|
168
|
-
mkdirSync(join(seedDir, ".changeset"));
|
|
169
|
-
writeFileSync(
|
|
170
|
-
join(seedDir, ".changeset", "config.json"),
|
|
171
|
-
JSON.stringify({ changelog: false, commit: false, baseBranch: "main", access: "public" }),
|
|
172
|
-
);
|
|
173
|
-
for (const [file, body] of Object.entries(changesets)) {
|
|
174
|
-
writeFileSync(join(seedDir, ".changeset", file), body);
|
|
175
|
-
}
|
|
176
|
-
git(seedDir, ["add", "."]);
|
|
177
|
-
git(seedDir, ["commit", "-q", "-m", "package with changesets"]);
|
|
178
|
-
git(seedDir, ["remote", "add", "origin", originDir]);
|
|
179
|
-
git(seedDir, ["push", "-q", "origin", "main"]);
|
|
180
|
-
|
|
181
|
-
const cloneDir = join(workDir, `${name}-clone`);
|
|
182
|
-
git(workDir, ["clone", "-q", originDir, cloneDir]);
|
|
183
|
-
return cloneDir;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function count(cwd: string) {
|
|
187
|
-
const result = spawnSync(process.execPath, [COUNT_SCRIPT], { cwd, encoding: "utf8" });
|
|
188
|
-
if (result.status !== 0) throw new Error(result.stderr || result.stdout);
|
|
189
|
-
return result.stdout;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
it("counts changesets that are already on origin/main", () => {
|
|
193
|
-
const clone = cloneOfPushedPackage("pending", {
|
|
194
|
-
"minor-feature.md": '---\n"@test/pkg": minor\n---\n\nA feature.\n',
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
expect(count(clone)).toBe("1");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("reports zero when no changesets are pending", () => {
|
|
201
|
-
const clone = cloneOfPushedPackage("none", {});
|
|
202
|
-
|
|
203
|
-
expect(count(clone)).toBe("0");
|
|
204
|
-
});
|
|
205
|
-
});
|