bulletin-deploy 0.6.7 → 0.6.8
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 +20 -2
- package/bin/bulletin-deploy +11 -0
- package/dist/bug-report.d.ts +14 -0
- package/dist/bug-report.js +108 -0
- package/dist/{chunk-FBXG7YMT.js → chunk-KHLH5WQY.js} +1 -1
- package/dist/{chunk-TD73EZ5I.js → chunk-LVCOE5CC.js} +5 -2
- package/dist/{chunk-ZGP4DMFK.js → chunk-SLARK556.js} +24 -4
- package/dist/chunk-SYJ2L6RU.js +146 -0
- package/dist/deploy.js +3 -3
- package/dist/dotns.js +2 -2
- package/dist/index.js +3 -3
- package/dist/telemetry.d.ts +3 -1
- package/dist/telemetry.js +5 -1
- package/dist/version-check.d.ts +31 -0
- package/dist/version-check.js +20 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -135,6 +135,7 @@ If you see **"Requires Full Personhood verification"**, the deploy will fail ear
|
|
|
135
135
|
|---|---|---|
|
|
136
136
|
| `BULLETIN_RPC` | `wss://paseo-bulletin-rpc.polkadot.io` | Bulletin chain WebSocket RPC |
|
|
137
137
|
| `BULLETIN_DEPLOY_TELEMETRY` | `1` (enabled) | Set to `0` to disable Sentry telemetry |
|
|
138
|
+
| `BULLETIN_DEPLOY_UPDATE_CHECK` | `1` (enabled) | Set to `0` to disable version check on errors |
|
|
138
139
|
| `DOTNS_STATUS` | `full` (testnet) / `none` (mainnet) | PoP level to self-grant before registration: `none`, `lite`, or `full` |
|
|
139
140
|
| `IPFS_CID` | _(none)_ | Skip storage, use pre-existing CID |
|
|
140
141
|
|
|
@@ -202,6 +203,22 @@ Instead of using a single account for all storage transactions, bulletin-deploy
|
|
|
202
203
|
|
|
203
204
|
When a pool account's authorization drops below thresholds (50 transactions or 50MB), bulletin-deploy automatically tops it up by submitting an `authorize_account` transaction from Alice.
|
|
204
205
|
|
|
206
|
+
## Version Checking
|
|
207
|
+
|
|
208
|
+
When a deploy fails, bulletin-deploy checks whether a newer version is available. This helps catch cases where the error was already fixed in a later release.
|
|
209
|
+
|
|
210
|
+
Two sources are checked in parallel:
|
|
211
|
+
- **npm registry** — reads the `minimumVersion` field from the latest published version
|
|
212
|
+
- **GitHub kill switch** (`min-version.json` in the repo) — allows emergency deprecation without a release
|
|
213
|
+
|
|
214
|
+
If either source indicates your version is below the minimum, the CLI tells you the version is unsupported and why. If you're simply outdated (but above the minimum), you get a suggestion to update.
|
|
215
|
+
|
|
216
|
+
For **paritytech** contributors in an interactive terminal, the CLI offers to update and retry automatically. For everyone else (external users, CI), it prints the update command and exits.
|
|
217
|
+
|
|
218
|
+
If you're already on the latest version and hit an error, the CLI offers to open a GitHub issue with collected debug info (paritytech contributors only).
|
|
219
|
+
|
|
220
|
+
Set `BULLETIN_DEPLOY_UPDATE_CHECK=0` to disable version checking.
|
|
221
|
+
|
|
205
222
|
## Telemetry
|
|
206
223
|
|
|
207
224
|
Sentry telemetry is enabled by default for deploy observability. Set `BULLETIN_DEPLOY_TELEMETRY=0` to disable.
|
|
@@ -212,10 +229,11 @@ What's tracked:
|
|
|
212
229
|
- DotNS phase timing (registration, contenthash update)
|
|
213
230
|
- Pool account selection
|
|
214
231
|
- Source metadata (repo, branch, PR number, CI vs local)
|
|
232
|
+
- Tool version (`deploy.tool_version`)
|
|
215
233
|
|
|
216
234
|
Dashboard:
|
|
217
|
-
- Bulletin Deploy Health: https://paritytech.sentry.io/dashboard/1669817
|
|
218
|
-
- Deploy Failures Detail: https://paritytech.sentry.io/dashboard/1669818
|
|
235
|
+
- Bulletin Deploy Health: https://paritytech.sentry.io/dashboard/1669817/?project=4511093597405264
|
|
236
|
+
- Deploy Failures Detail: https://paritytech.sentry.io/dashboard/1669818/?project=4511093597405264
|
|
219
237
|
|
|
220
238
|
## Troubleshooting
|
|
221
239
|
|
package/bin/bulletin-deploy
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY } from "../dist/deploy.js";
|
|
4
4
|
import { bootstrapPool } from "../dist/pool.js";
|
|
5
5
|
import { VERSION } from "../dist/telemetry.js";
|
|
6
|
+
import { handleFailedDeploy } from "../dist/version-check.js";
|
|
7
|
+
import { setDeployContext } from "../dist/bug-report.js";
|
|
6
8
|
import * as fs from "fs";
|
|
7
9
|
|
|
8
10
|
const args = process.argv.slice(2);
|
|
@@ -57,6 +59,14 @@ try {
|
|
|
57
59
|
if (!domain) { console.error("Error: domain required (e.g. my-app.dot)"); process.exit(1); }
|
|
58
60
|
if (!fs.existsSync(buildDir)) { console.error(`Error: ${buildDir} does not exist`); process.exit(1); }
|
|
59
61
|
|
|
62
|
+
setDeployContext({
|
|
63
|
+
domain,
|
|
64
|
+
rpc: flags.rpc,
|
|
65
|
+
repo: process.env.GITHUB_REPOSITORY,
|
|
66
|
+
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME,
|
|
67
|
+
signerMode: flags.mnemonic ? "direct" : "pool",
|
|
68
|
+
});
|
|
69
|
+
|
|
60
70
|
const result = await deploy(buildDir, domain, {
|
|
61
71
|
playground: flags.playground,
|
|
62
72
|
mnemonic: flags.mnemonic,
|
|
@@ -79,5 +89,6 @@ try {
|
|
|
79
89
|
} catch (error) {
|
|
80
90
|
const noRetry = error instanceof NonRetryableError;
|
|
81
91
|
console.error(`Deployment failed${noRetry ? " (not retryable)" : ""}:`, error.message);
|
|
92
|
+
await handleFailedDeploy(error);
|
|
82
93
|
process.exit(noRetry ? EXIT_CODE_NO_RETRY : 1);
|
|
83
94
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface DeployContext {
|
|
2
|
+
domain?: string;
|
|
3
|
+
repo?: string;
|
|
4
|
+
branch?: string;
|
|
5
|
+
signerMode?: string;
|
|
6
|
+
chunkCount?: number;
|
|
7
|
+
totalSize?: string;
|
|
8
|
+
rpc?: string;
|
|
9
|
+
sentryTraceId?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function setDeployContext(ctx: Partial<DeployContext>): void;
|
|
12
|
+
declare function offerBugReport(error: Error): Promise<void>;
|
|
13
|
+
|
|
14
|
+
export { offerBugReport, setDeployContext };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyErrorArea,
|
|
3
|
+
isInteractive,
|
|
4
|
+
promptYesNo
|
|
5
|
+
} from "./chunk-SYJ2L6RU.js";
|
|
6
|
+
import {
|
|
7
|
+
VERSION
|
|
8
|
+
} from "./chunk-SLARK556.js";
|
|
9
|
+
import "./chunk-QGM4M3NI.js";
|
|
10
|
+
|
|
11
|
+
// src/bug-report.ts
|
|
12
|
+
import { execSync, execFileSync } from "child_process";
|
|
13
|
+
import * as os from "os";
|
|
14
|
+
var _deployContext = {};
|
|
15
|
+
function setDeployContext(ctx) {
|
|
16
|
+
_deployContext = { ..._deployContext, ...ctx };
|
|
17
|
+
}
|
|
18
|
+
function hasGhCli() {
|
|
19
|
+
try {
|
|
20
|
+
execSync("gh --version", { stdio: "pipe" });
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function buildReportBody(error) {
|
|
27
|
+
const lines = [
|
|
28
|
+
"## Environment",
|
|
29
|
+
"",
|
|
30
|
+
`- **bulletin-deploy**: ${VERSION}`,
|
|
31
|
+
`- **Node.js**: ${process.version}`,
|
|
32
|
+
`- **OS**: ${os.platform()} ${os.arch()} ${os.release()}`,
|
|
33
|
+
"",
|
|
34
|
+
"## Error",
|
|
35
|
+
"",
|
|
36
|
+
"```",
|
|
37
|
+
error.stack || error.message,
|
|
38
|
+
"```",
|
|
39
|
+
""
|
|
40
|
+
];
|
|
41
|
+
const ctx = _deployContext;
|
|
42
|
+
if (ctx.domain || ctx.repo || ctx.rpc) {
|
|
43
|
+
lines.push("## Deploy Context", "");
|
|
44
|
+
if (ctx.domain) lines.push(`- **Domain**: ${ctx.domain}`);
|
|
45
|
+
if (ctx.repo) lines.push(`- **Repo**: ${ctx.repo}`);
|
|
46
|
+
if (ctx.branch) lines.push(`- **Branch**: ${ctx.branch}`);
|
|
47
|
+
if (ctx.signerMode) lines.push(`- **Signer mode**: ${ctx.signerMode}`);
|
|
48
|
+
if (ctx.chunkCount != null) lines.push(`- **Chunks**: ${ctx.chunkCount}`);
|
|
49
|
+
if (ctx.totalSize) lines.push(`- **Total size**: ${ctx.totalSize}`);
|
|
50
|
+
if (ctx.rpc) lines.push(`- **RPC**: ${ctx.rpc}`);
|
|
51
|
+
if (ctx.sentryTraceId) lines.push(`- **Sentry trace**: ${ctx.sentryTraceId}`);
|
|
52
|
+
lines.push("");
|
|
53
|
+
}
|
|
54
|
+
return lines.join("\n");
|
|
55
|
+
}
|
|
56
|
+
function buildTitle(error) {
|
|
57
|
+
const msg = error.message.slice(0, 60);
|
|
58
|
+
return `[deploy-bug] ${msg}`;
|
|
59
|
+
}
|
|
60
|
+
function buildLabels(error) {
|
|
61
|
+
const labels = ["bug", "auto-report"];
|
|
62
|
+
const area = classifyErrorArea(error.message);
|
|
63
|
+
if (area) labels.push(area);
|
|
64
|
+
return labels;
|
|
65
|
+
}
|
|
66
|
+
async function offerBugReport(error) {
|
|
67
|
+
if (!isInteractive()) return;
|
|
68
|
+
const yes = await promptYesNo("\n This looks like a bug. Open an issue with debug info? [Y/n] ");
|
|
69
|
+
if (!yes) return;
|
|
70
|
+
const title = buildTitle(error);
|
|
71
|
+
const body = buildReportBody(error);
|
|
72
|
+
const labels = buildLabels(error);
|
|
73
|
+
if (hasGhCli()) {
|
|
74
|
+
try {
|
|
75
|
+
const args = [
|
|
76
|
+
"issue",
|
|
77
|
+
"create",
|
|
78
|
+
"--repo",
|
|
79
|
+
"paritytech/bulletin-deploy",
|
|
80
|
+
"--title",
|
|
81
|
+
title,
|
|
82
|
+
...labels.flatMap((l) => ["--label", l]),
|
|
83
|
+
"--body-file",
|
|
84
|
+
"-"
|
|
85
|
+
];
|
|
86
|
+
execFileSync("gh", args, { input: body, stdio: ["pipe", "inherit", "inherit"] });
|
|
87
|
+
console.error(" Issue created.");
|
|
88
|
+
} catch {
|
|
89
|
+
console.error(" Failed to create issue. Debug info below:\n");
|
|
90
|
+
printFallback(title, body, labels);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
console.error("\n gh CLI not found. Debug info below \u2014 paste into a new issue:\n");
|
|
94
|
+
console.error(` https://github.com/paritytech/bulletin-deploy/issues/new
|
|
95
|
+
`);
|
|
96
|
+
printFallback(title, body, labels);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function printFallback(title, body, labels) {
|
|
100
|
+
console.error(` Title: ${title}`);
|
|
101
|
+
console.error(` Labels: ${labels.join(", ")}
|
|
102
|
+
`);
|
|
103
|
+
console.error(body);
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
offerBugReport,
|
|
107
|
+
setDeployContext
|
|
108
|
+
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
TX_TIMEOUT_MS,
|
|
5
5
|
fetchNonce,
|
|
6
6
|
validateDomainLabel
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KHLH5WQY.js";
|
|
8
8
|
import {
|
|
9
9
|
merkleizeJS
|
|
10
10
|
} from "./chunk-GZ5UUECB.js";
|
|
@@ -18,10 +18,12 @@ import {
|
|
|
18
18
|
VERSION,
|
|
19
19
|
captureWarning,
|
|
20
20
|
initTelemetry,
|
|
21
|
+
resolveRunner,
|
|
22
|
+
resolveRunnerType,
|
|
21
23
|
setDeployAttribute,
|
|
22
24
|
withDeploySpan,
|
|
23
25
|
withSpan
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-SLARK556.js";
|
|
25
27
|
|
|
26
28
|
// src/deploy.ts
|
|
27
29
|
import { Buffer } from "buffer";
|
|
@@ -824,6 +826,7 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
824
826
|
console.log("=".repeat(60));
|
|
825
827
|
console.log(` Domain: ${name}.dot`);
|
|
826
828
|
if (typeof content === "string") console.log(` Build dir: ${path.resolve(content)}`);
|
|
829
|
+
if (process.env.CI) console.log(` Runner: ${resolveRunner()} (${resolveRunnerType()})`);
|
|
827
830
|
if (options.password) console.log(` Encrypted: yes`);
|
|
828
831
|
let provider;
|
|
829
832
|
const reconnect = options.mnemonic ? () => getDirectProvider(options.mnemonic) : () => getProvider();
|
|
@@ -6,7 +6,7 @@ import * as path from "path";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "bulletin-deploy",
|
|
9
|
-
version: "0.6.
|
|
9
|
+
version: "0.6.8",
|
|
10
10
|
private: false,
|
|
11
11
|
repository: {
|
|
12
12
|
type: "git",
|
|
@@ -34,7 +34,7 @@ var package_default = {
|
|
|
34
34
|
"cdm.json"
|
|
35
35
|
],
|
|
36
36
|
scripts: {
|
|
37
|
-
build: "tsup src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/merkle.ts --format esm --dts --clean --target node22",
|
|
37
|
+
build: "tsup src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/merkle.ts src/version-check.ts src/bug-report.ts --format esm --dts --clean --target node22",
|
|
38
38
|
test: "npm run build && node --test test/test.js",
|
|
39
39
|
benchmark: "npm run build && node benchmark.js"
|
|
40
40
|
},
|
|
@@ -61,6 +61,7 @@ var package_default = {
|
|
|
61
61
|
tsup: "^8.5.0",
|
|
62
62
|
typescript: "^5.9.3"
|
|
63
63
|
},
|
|
64
|
+
minimumVersion: "0.5.6",
|
|
64
65
|
engines: {
|
|
65
66
|
node: ">=22"
|
|
66
67
|
}
|
|
@@ -81,6 +82,7 @@ function initTelemetry() {
|
|
|
81
82
|
if (!Sentry) return;
|
|
82
83
|
Sentry.init({
|
|
83
84
|
dsn: process.env.SENTRY_DSN || DEFAULT_DSN,
|
|
85
|
+
release: `${package_default.name}@${VERSION}`,
|
|
84
86
|
tracesSampleRate: 1,
|
|
85
87
|
environment: process.env.CI ? "ci" : "local"
|
|
86
88
|
});
|
|
@@ -114,12 +116,25 @@ function tryGitBranch() {
|
|
|
114
116
|
return "unknown";
|
|
115
117
|
}
|
|
116
118
|
}
|
|
119
|
+
function resolveRunner() {
|
|
120
|
+
if (!process.env.CI) return "local";
|
|
121
|
+
if (process.env.RUNNER_NAME?.startsWith("parity-")) return process.env.RUNNER_NAME;
|
|
122
|
+
return process.env.RUNNER_NAME || "unknown";
|
|
123
|
+
}
|
|
124
|
+
function resolveRunnerType() {
|
|
125
|
+
if (!process.env.CI) return "local";
|
|
126
|
+
if (process.env.RUNNER_NAME?.startsWith("parity-")) return "self-hosted";
|
|
127
|
+
return "github-hosted";
|
|
128
|
+
}
|
|
117
129
|
function getDeployAttributes(domain) {
|
|
118
130
|
return {
|
|
119
131
|
"deploy.repo": resolveRepo(domain),
|
|
120
132
|
"deploy.branch": process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || tryGitBranch(),
|
|
121
133
|
"deploy.source": process.env.CI ? "ci" : "local",
|
|
122
|
-
"deploy.pr": process.env.GITHUB_PR_NUMBER || void 0
|
|
134
|
+
"deploy.pr": process.env.GITHUB_PR_NUMBER || void 0,
|
|
135
|
+
"deploy.tool_version": VERSION,
|
|
136
|
+
"deploy.runner": resolveRunner(),
|
|
137
|
+
"deploy.runner_type": resolveRunnerType()
|
|
123
138
|
};
|
|
124
139
|
}
|
|
125
140
|
function isExpectedError(msg) {
|
|
@@ -146,7 +161,9 @@ async function withDeploySpan(domain, fn) {
|
|
|
146
161
|
"deploy.repo": attrs["deploy.repo"],
|
|
147
162
|
"deploy.branch": attrs["deploy.branch"],
|
|
148
163
|
"deploy.domain": domain,
|
|
149
|
-
"deploy.source": attrs["deploy.source"]
|
|
164
|
+
"deploy.source": attrs["deploy.source"],
|
|
165
|
+
"deploy.tool_version": VERSION,
|
|
166
|
+
"deploy.runner_type": resolveRunnerType()
|
|
150
167
|
});
|
|
151
168
|
try {
|
|
152
169
|
return await fn();
|
|
@@ -154,6 +171,7 @@ async function withDeploySpan(domain, fn) {
|
|
|
154
171
|
const msg = error.message;
|
|
155
172
|
span.setAttribute("deploy.status", "error");
|
|
156
173
|
span.setAttribute("deploy.error", msg.slice(0, 200));
|
|
174
|
+
span.setAttribute("deploy.sad", true);
|
|
157
175
|
const isExpected = isExpectedError(msg);
|
|
158
176
|
if (!isExpected) {
|
|
159
177
|
span.setStatus({ code: 2, message: "internal_error" });
|
|
@@ -189,6 +207,8 @@ export {
|
|
|
189
207
|
VERSION,
|
|
190
208
|
initTelemetry,
|
|
191
209
|
resolveRepo,
|
|
210
|
+
resolveRunner,
|
|
211
|
+
resolveRunnerType,
|
|
192
212
|
isExpectedError,
|
|
193
213
|
withSpan,
|
|
194
214
|
withDeploySpan,
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VERSION
|
|
3
|
+
} from "./chunk-SLARK556.js";
|
|
4
|
+
|
|
5
|
+
// src/version-check.ts
|
|
6
|
+
import { execSync, execFileSync } from "child_process";
|
|
7
|
+
import { createInterface } from "readline";
|
|
8
|
+
var REGISTRY_URL = "https://registry.npmjs.org/bulletin-deploy/latest";
|
|
9
|
+
var KILL_SWITCH_URL = "https://raw.githubusercontent.com/paritytech/bulletin-deploy/main/min-version.json";
|
|
10
|
+
var FETCH_TIMEOUT = 3e3;
|
|
11
|
+
function compareSemver(a, b) {
|
|
12
|
+
const pa = a.split(".").map(Number);
|
|
13
|
+
const pb = b.split(".").map(Number);
|
|
14
|
+
for (let i = 0; i < 3; i++) {
|
|
15
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
16
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
17
|
+
}
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
async function fetchJson(url) {
|
|
21
|
+
try {
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
|
|
24
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
if (!res.ok) return null;
|
|
27
|
+
return await res.json();
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function fetchVersionInfo() {
|
|
33
|
+
const [registry, killSwitch] = await Promise.all([
|
|
34
|
+
fetchJson(REGISTRY_URL),
|
|
35
|
+
fetchJson(KILL_SWITCH_URL)
|
|
36
|
+
]);
|
|
37
|
+
if (!registry && !killSwitch) return null;
|
|
38
|
+
return {
|
|
39
|
+
latest: registry?.version ?? VERSION,
|
|
40
|
+
minimumFromRegistry: registry?.minimumVersion ?? null,
|
|
41
|
+
minimumFromKillSwitch: killSwitch?.minimumVersion ?? null,
|
|
42
|
+
killSwitchMessage: killSwitch?.message ?? null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function isInternalUser() {
|
|
46
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
47
|
+
if (repo?.startsWith("paritytech/")) return true;
|
|
48
|
+
try {
|
|
49
|
+
const remote = execSync("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
50
|
+
return remote.includes("paritytech/");
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isInteractive() {
|
|
56
|
+
return Boolean(process.stdin.isTTY && !process.env.CI);
|
|
57
|
+
}
|
|
58
|
+
async function promptYesNo(question) {
|
|
59
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
rl.question(question, (answer) => {
|
|
62
|
+
rl.close();
|
|
63
|
+
resolve(!answer || answer.toLowerCase().startsWith("y"));
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function updateAndRetry() {
|
|
68
|
+
console.error("\n Updating bulletin-deploy...");
|
|
69
|
+
try {
|
|
70
|
+
execSync("npm install -g bulletin-deploy@latest", { stdio: "inherit" });
|
|
71
|
+
console.error(" Updated. Retrying deploy...\n");
|
|
72
|
+
execFileSync(process.argv[0], process.argv.slice(1), { stdio: "inherit" });
|
|
73
|
+
process.exit(0);
|
|
74
|
+
} catch {
|
|
75
|
+
console.error(" Update failed. Please run: npm install -g bulletin-deploy@latest");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function classifyErrorArea(msg) {
|
|
80
|
+
if (/personhood|owned by|owner mismatch|reserved for original|domain|dotns|commit-reveal/i.test(msg)) return "area:dotns";
|
|
81
|
+
if (/chunk|storage|authorized|authorization|pool|alice/i.test(msg)) return "area:storage";
|
|
82
|
+
if (/ipfs|cid|pin|dag/i.test(msg)) return "area:ipfs";
|
|
83
|
+
if (/connect|timeout|websocket|rpc|ECONNREFUSED|ENOTFOUND/i.test(msg)) return "area:network";
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function assessVersion(currentVersion, info, internal) {
|
|
87
|
+
const effectiveMinimum = [info.minimumFromRegistry, info.minimumFromKillSwitch].filter(Boolean).sort((a, b) => compareSemver(b, a))[0] ?? null;
|
|
88
|
+
if (effectiveMinimum && compareSemver(currentVersion, effectiveMinimum) < 0) {
|
|
89
|
+
return { action: "forced_update", currentVersion, minimumVersion: effectiveMinimum, message: info.killSwitchMessage };
|
|
90
|
+
}
|
|
91
|
+
if (compareSemver(currentVersion, info.latest) < 0) {
|
|
92
|
+
return { action: "suggest_update", currentVersion, latestVersion: info.latest, internal };
|
|
93
|
+
}
|
|
94
|
+
if (internal) {
|
|
95
|
+
return { action: "bug_report", internal: true };
|
|
96
|
+
}
|
|
97
|
+
return { action: "none" };
|
|
98
|
+
}
|
|
99
|
+
async function handleFailedDeploy(error) {
|
|
100
|
+
if (process.env.BULLETIN_DEPLOY_UPDATE_CHECK === "0") return;
|
|
101
|
+
const info = await fetchVersionInfo();
|
|
102
|
+
if (!info) return;
|
|
103
|
+
let verdict = assessVersion(VERSION, info, false);
|
|
104
|
+
if (verdict.action === "suggest_update" || verdict.action === "none") {
|
|
105
|
+
const internal = isInternalUser();
|
|
106
|
+
if (internal) verdict = assessVersion(VERSION, info, true);
|
|
107
|
+
}
|
|
108
|
+
switch (verdict.action) {
|
|
109
|
+
case "forced_update":
|
|
110
|
+
console.error(`
|
|
111
|
+
bulletin-deploy ${verdict.currentVersion} is no longer supported (minimum: ${verdict.minimumVersion}).`);
|
|
112
|
+
if (verdict.message) console.error(` Reason: ${verdict.message}`);
|
|
113
|
+
console.error(` Please update: npm install -g bulletin-deploy@latest
|
|
114
|
+
`);
|
|
115
|
+
break;
|
|
116
|
+
case "suggest_update":
|
|
117
|
+
if (verdict.internal && isInteractive()) {
|
|
118
|
+
const yes = await promptYesNo(`
|
|
119
|
+
bulletin-deploy ${verdict.currentVersion} \u2192 ${verdict.latestVersion} available. Update and retry? [Y/n] `);
|
|
120
|
+
if (yes) updateAndRetry();
|
|
121
|
+
else console.error(` Skipped. Run: npm install -g bulletin-deploy@latest
|
|
122
|
+
`);
|
|
123
|
+
} else {
|
|
124
|
+
console.error(`
|
|
125
|
+
A newer version of bulletin-deploy is available (${verdict.currentVersion} \u2192 ${verdict.latestVersion}).`);
|
|
126
|
+
console.error(` Run: npm install -g bulletin-deploy@latest
|
|
127
|
+
`);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
case "bug_report": {
|
|
131
|
+
const { offerBugReport } = await import("./bug-report.js");
|
|
132
|
+
await offerBugReport(error);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export {
|
|
139
|
+
compareSemver,
|
|
140
|
+
isInternalUser,
|
|
141
|
+
isInteractive,
|
|
142
|
+
promptYesNo,
|
|
143
|
+
classifyErrorArea,
|
|
144
|
+
assessVersion,
|
|
145
|
+
handleFailedDeploy
|
|
146
|
+
};
|
package/dist/deploy.js
CHANGED
|
@@ -21,11 +21,11 @@ import {
|
|
|
21
21
|
storeChunkedContent,
|
|
22
22
|
storeDirectory,
|
|
23
23
|
storeFile
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-LVCOE5CC.js";
|
|
25
|
+
import "./chunk-KHLH5WQY.js";
|
|
26
26
|
import "./chunk-GZ5UUECB.js";
|
|
27
27
|
import "./chunk-LGPTJYA3.js";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-SLARK556.js";
|
|
29
29
|
import "./chunk-QGM4M3NI.js";
|
|
30
30
|
export {
|
|
31
31
|
DEFAULT_BULLETIN_RPC,
|
package/dist/dotns.js
CHANGED
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
sanitizeDomainLabel,
|
|
20
20
|
stripTrailingDigits,
|
|
21
21
|
validateDomainLabel
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-KHLH5WQY.js";
|
|
23
|
+
import "./chunk-SLARK556.js";
|
|
24
24
|
import "./chunk-QGM4M3NI.js";
|
|
25
25
|
export {
|
|
26
26
|
CONNECTION_TIMEOUT_MS,
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deploy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LVCOE5CC.js";
|
|
4
4
|
import {
|
|
5
5
|
DotNS
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KHLH5WQY.js";
|
|
7
7
|
import {
|
|
8
8
|
merkleizeJS
|
|
9
9
|
} from "./chunk-GZ5UUECB.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
fetchPoolAuthorizations,
|
|
15
15
|
selectAccount
|
|
16
16
|
} from "./chunk-LGPTJYA3.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-SLARK556.js";
|
|
18
18
|
import "./chunk-QGM4M3NI.js";
|
|
19
19
|
export {
|
|
20
20
|
DotNS,
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
declare const VERSION: string;
|
|
2
2
|
declare function initTelemetry(): void;
|
|
3
3
|
declare function resolveRepo(domain: string): string;
|
|
4
|
+
declare function resolveRunner(): string;
|
|
5
|
+
declare function resolveRunnerType(): string;
|
|
4
6
|
declare function isExpectedError(msg: string): boolean;
|
|
5
7
|
declare function withSpan<T>(op: string, description: string, attributes: Record<string, string | number | boolean | undefined>, fn: () => T | Promise<T>): Promise<T>;
|
|
6
8
|
declare function withDeploySpan<T>(domain: string, fn: () => T | Promise<T>): Promise<T>;
|
|
@@ -8,4 +10,4 @@ declare function setDeployAttribute(key: string, value: string | number | boolea
|
|
|
8
10
|
declare function captureWarning(message: string, context?: Record<string, unknown>): void;
|
|
9
11
|
declare function flush(): Promise<void>;
|
|
10
12
|
|
|
11
|
-
export { VERSION, captureWarning, flush, initTelemetry, isExpectedError, resolveRepo, setDeployAttribute, withDeploySpan, withSpan };
|
|
13
|
+
export { VERSION, captureWarning, flush, initTelemetry, isExpectedError, resolveRepo, resolveRunner, resolveRunnerType, setDeployAttribute, withDeploySpan, withSpan };
|
package/dist/telemetry.js
CHANGED
|
@@ -5,10 +5,12 @@ import {
|
|
|
5
5
|
initTelemetry,
|
|
6
6
|
isExpectedError,
|
|
7
7
|
resolveRepo,
|
|
8
|
+
resolveRunner,
|
|
9
|
+
resolveRunnerType,
|
|
8
10
|
setDeployAttribute,
|
|
9
11
|
withDeploySpan,
|
|
10
12
|
withSpan
|
|
11
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-SLARK556.js";
|
|
12
14
|
import "./chunk-QGM4M3NI.js";
|
|
13
15
|
export {
|
|
14
16
|
VERSION,
|
|
@@ -17,6 +19,8 @@ export {
|
|
|
17
19
|
initTelemetry,
|
|
18
20
|
isExpectedError,
|
|
19
21
|
resolveRepo,
|
|
22
|
+
resolveRunner,
|
|
23
|
+
resolveRunnerType,
|
|
20
24
|
setDeployAttribute,
|
|
21
25
|
withDeploySpan,
|
|
22
26
|
withSpan
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
interface VersionInfo {
|
|
2
|
+
latest: string;
|
|
3
|
+
minimumFromRegistry: string | null;
|
|
4
|
+
minimumFromKillSwitch: string | null;
|
|
5
|
+
killSwitchMessage: string | null;
|
|
6
|
+
}
|
|
7
|
+
declare function compareSemver(a: string, b: string): number;
|
|
8
|
+
declare function isInternalUser(): boolean;
|
|
9
|
+
declare function isInteractive(): boolean;
|
|
10
|
+
declare function promptYesNo(question: string): Promise<boolean>;
|
|
11
|
+
declare function classifyErrorArea(msg: string): string | null;
|
|
12
|
+
type VersionVerdict = {
|
|
13
|
+
action: "forced_update";
|
|
14
|
+
currentVersion: string;
|
|
15
|
+
minimumVersion: string;
|
|
16
|
+
message: string | null;
|
|
17
|
+
} | {
|
|
18
|
+
action: "suggest_update";
|
|
19
|
+
currentVersion: string;
|
|
20
|
+
latestVersion: string;
|
|
21
|
+
internal: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
action: "bug_report";
|
|
24
|
+
internal: boolean;
|
|
25
|
+
} | {
|
|
26
|
+
action: "none";
|
|
27
|
+
};
|
|
28
|
+
declare function assessVersion(currentVersion: string, info: VersionInfo, internal: boolean): VersionVerdict;
|
|
29
|
+
declare function handleFailedDeploy(error: Error): Promise<void>;
|
|
30
|
+
|
|
31
|
+
export { type VersionVerdict, assessVersion, classifyErrorArea, compareSemver, handleFailedDeploy, isInteractive, isInternalUser, promptYesNo };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assessVersion,
|
|
3
|
+
classifyErrorArea,
|
|
4
|
+
compareSemver,
|
|
5
|
+
handleFailedDeploy,
|
|
6
|
+
isInteractive,
|
|
7
|
+
isInternalUser,
|
|
8
|
+
promptYesNo
|
|
9
|
+
} from "./chunk-SYJ2L6RU.js";
|
|
10
|
+
import "./chunk-SLARK556.js";
|
|
11
|
+
import "./chunk-QGM4M3NI.js";
|
|
12
|
+
export {
|
|
13
|
+
assessVersion,
|
|
14
|
+
classifyErrorArea,
|
|
15
|
+
compareSemver,
|
|
16
|
+
handleFailedDeploy,
|
|
17
|
+
isInteractive,
|
|
18
|
+
isInternalUser,
|
|
19
|
+
promptYesNo
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bulletin-deploy",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"cdm.json"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"build": "tsup src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/merkle.ts --format esm --dts --clean --target node22",
|
|
31
|
+
"build": "tsup src/index.ts src/deploy.ts src/dotns.ts src/pool.ts src/telemetry.ts src/merkle.ts src/version-check.ts src/bug-report.ts --format esm --dts --clean --target node22",
|
|
32
32
|
"test": "npm run build && node --test test/test.js",
|
|
33
33
|
"benchmark": "npm run build && node benchmark.js"
|
|
34
34
|
},
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"tsup": "^8.5.0",
|
|
56
56
|
"typescript": "^5.9.3"
|
|
57
57
|
},
|
|
58
|
+
"minimumVersion": "0.5.6",
|
|
58
59
|
"engines": {
|
|
59
60
|
"node": ">=22"
|
|
60
61
|
}
|