@enformik/cli 0.5.0 → 0.6.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 +72 -31
- package/dist/auth.js +13 -9
- package/dist/cli.js +47 -38
- package/dist/codegen.js +6 -6
- package/dist/commands/ai.js +29 -25
- package/dist/commands/app.js +32 -26
- package/dist/commands/codegen.js +4 -4
- package/dist/commands/link.js +12 -12
- package/dist/commands/schema.js +43 -39
- package/dist/commands/site.js +22 -23
- package/dist/commands/whoami.js +19 -15
- package/dist/config.js +4 -4
- package/dist/context.js +32 -17
- package/dist/errors.js +5 -1
- package/dist/http.js +32 -5
- package/dist/precheck.js +21 -21
- package/dist/schema.js +29 -27
- package/dist/site.js +47 -15
- package/dist/zip.js +3 -3
- package/package.json +2 -2
package/dist/site.js
CHANGED
|
@@ -11,7 +11,7 @@ export class DeployClient {
|
|
|
11
11
|
constructor(o) {
|
|
12
12
|
this.o = o;
|
|
13
13
|
// Her kullanım yolunda (deploy, status, whoami, app check) dağıtım anahtarı düz metin gitmesin.
|
|
14
|
-
assertSecureUrl(o.consoleUrl, '
|
|
14
|
+
assertSecureUrl(o.consoleUrl, 'Console URL');
|
|
15
15
|
}
|
|
16
16
|
url(path) { return joinUrl(this.o.consoleUrl, `/api/deploy/v1${path}`); }
|
|
17
17
|
headers(extra = {}) {
|
|
@@ -46,6 +46,31 @@ export class DeployClient {
|
|
|
46
46
|
return (await this.call(`/deployments/${id}`, { retries: 2 })).deployment;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Finalizes a deployment. A `409 invalid_state` can mean the first finalize worked and only its reply was lost (the request was retried), or
|
|
51
|
+
* another run of the same CI job already finalized it, so the deployment's real state decides: running or finished -> carry on with that state;
|
|
52
|
+
* still waiting for its upload / finalize -> the error stands.
|
|
53
|
+
*/
|
|
54
|
+
export async function finalizeDeployment(client, dep) {
|
|
55
|
+
try {
|
|
56
|
+
await client.finalize(dep.deploymentId);
|
|
57
|
+
return { ...dep, status: 'validating' };
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
if (!(e instanceof CliError) || e.code !== 'invalid_state')
|
|
61
|
+
throw e;
|
|
62
|
+
let cur;
|
|
63
|
+
try {
|
|
64
|
+
cur = await client.status(dep.deploymentId);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw e;
|
|
68
|
+
} // the state could not be read: the original (actionable) error is the better message
|
|
69
|
+
if (cur.status === 'awaiting_upload' || cur.status === 'uploaded')
|
|
70
|
+
throw e;
|
|
71
|
+
return { ...dep, ...cur };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
49
74
|
/** Üst üste bu kadar yoklama hatasından sonra bırakılır. */
|
|
50
75
|
const MAX_POLL_FAILURES = 5;
|
|
51
76
|
const TERMINAL = new Set(['live', 'failed', 'expired', 'superseded']);
|
|
@@ -67,7 +92,8 @@ export async function waitForDeployment(client, id, opts) {
|
|
|
67
92
|
catch (e) {
|
|
68
93
|
// Geçici hata (ağ, 5xx): sunucu dağıtımı yayınlıyor olabilir, tek bir yoklama hatası CI'ı kırmasın.
|
|
69
94
|
// Kalıcı hatalar (geçersiz anahtar, 404…) kodla gelir ve hemen iletilir.
|
|
70
|
-
|
|
95
|
+
// Bir 429 (kodsuz) da geçicidir: sunucu dağıtımı sürdürüyor, yoklamayı kısıtlama yüzünden bırakmayız.
|
|
96
|
+
const transient = (e instanceof CliError && (!e.code || e.code === 'deploy_unavailable')) || (e instanceof ApiError && e.status === 429 && !e.code);
|
|
71
97
|
if (!transient || ++failures >= MAX_POLL_FAILURES)
|
|
72
98
|
throw e;
|
|
73
99
|
if (now() - start > opts.timeoutMs)
|
|
@@ -82,7 +108,7 @@ export async function waitForDeployment(client, id, opts) {
|
|
|
82
108
|
if (isTerminal(v.status))
|
|
83
109
|
return v;
|
|
84
110
|
if (now() - start > opts.timeoutMs)
|
|
85
|
-
throw new CliError(`
|
|
111
|
+
throw new CliError(`Timed out: the deployment did not finish within ${Math.round(opts.timeoutMs / 1000)} s (last status: ${v.status}). You can follow it on the console's "Releases" tab.`);
|
|
86
112
|
await sleep(interval);
|
|
87
113
|
interval = Math.min(interval + 500, 5000);
|
|
88
114
|
}
|
|
@@ -128,25 +154,31 @@ export function idempotencyKey(env, rand = randomUUID, contentSha) {
|
|
|
128
154
|
return `cli-${rand()}`.slice(0, 64);
|
|
129
155
|
}
|
|
130
156
|
const HINTS = {
|
|
131
|
-
invalid_key: '
|
|
132
|
-
key_expired: '
|
|
133
|
-
rate_limited: '
|
|
134
|
-
developer_site_disabled: '
|
|
135
|
-
environment_inactive: '
|
|
136
|
-
deploy_in_progress: '
|
|
137
|
-
deploy_unavailable: '
|
|
138
|
-
deployment_expired: '
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
invalid_key: 'The deploy key is invalid, revoked, or does not belong to this site. Check the ENFORMIK_DEPLOY_KEY value.',
|
|
158
|
+
key_expired: 'The deploy key has expired. Create a new key in the console and update the CI secret variable.',
|
|
159
|
+
rate_limited: 'Too many invalid key attempts; wait a moment and try again.',
|
|
160
|
+
developer_site_disabled: 'The developer site is not enabled in this environment. Enable it from the "Developer site" dialog in the console.',
|
|
161
|
+
environment_inactive: 'The website environment is currently unavailable (e.g. the subscription is suspended).',
|
|
162
|
+
deploy_in_progress: 'A deployment is already in progress in this environment; wait for it to finish and try again.',
|
|
163
|
+
deploy_unavailable: 'The deployment infrastructure is not ready right now; try again shortly.',
|
|
164
|
+
deployment_expired: 'The upload window has expired; run the command again.',
|
|
165
|
+
invalid_state: 'The deployment is not in a state that allows this step (for example it was already finalized or its upload is incomplete); run the command again.',
|
|
166
|
+
quota_exceeded: 'The archive exceeds the allowed size (50 MB max).',
|
|
167
|
+
size_mismatch: 'The uploaded content does not match the declared size; run the command again.',
|
|
168
|
+
checksum_mismatch: 'The checksum of the uploaded content does not match (the upload may be corrupted); run the command again.',
|
|
142
169
|
};
|
|
143
170
|
export function describeDeployError(e) {
|
|
144
171
|
if (!(e instanceof ApiError))
|
|
145
172
|
return e;
|
|
173
|
+
// Any 429 except one with a known hint (the deploy-key limiter) keeps its rate-limit message and JSON shape (status, retryAfterSeconds).
|
|
174
|
+
if (e.status === 429 && !(e.code && HINTS[e.code]))
|
|
175
|
+
return e;
|
|
146
176
|
const hint = e.code ? HINTS[e.code] : undefined;
|
|
147
177
|
const detail = e.body?.details;
|
|
148
178
|
let msg = hint ?? e.message ?? `HTTP ${e.status}`;
|
|
149
179
|
if (e.code === 'artifact_invalid' && Array.isArray(detail))
|
|
150
|
-
msg = `
|
|
180
|
+
msg = `Invalid archive: ${detail.map((d) => d.message).join('; ')}`;
|
|
181
|
+
if (e.status === 429 && e.retryAfterSeconds !== undefined)
|
|
182
|
+
msg += ` (retry in about ${e.retryAfterSeconds} s)`;
|
|
151
183
|
return new CliError(msg, 1, e.code, e.body);
|
|
152
184
|
}
|
package/dist/zip.js
CHANGED
|
@@ -28,7 +28,7 @@ const MAX_U32 = 0xffffffff;
|
|
|
28
28
|
/** Girdileri sıralı (deterministik) olarak outPath'e zipler. */
|
|
29
29
|
export function writeZipFile(files, outPath) {
|
|
30
30
|
if (files.length > MAX_ENTRIES)
|
|
31
|
-
throw new Error(`
|
|
31
|
+
throw new Error(`A zip can contain at most ${MAX_ENTRIES} entries.`);
|
|
32
32
|
const sorted = [...files].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
33
33
|
const fd = openSync(outPath, 'w');
|
|
34
34
|
let offset = 0;
|
|
@@ -44,7 +44,7 @@ export function writeZipFile(files, outPath) {
|
|
|
44
44
|
for (const f of sorted) {
|
|
45
45
|
const data = readFileSync(f.path);
|
|
46
46
|
if (data.length >= MAX_U32)
|
|
47
|
-
throw new Error(`
|
|
47
|
+
throw new Error(`File too large (zip64 is not supported): ${f.name}`);
|
|
48
48
|
const name = Buffer.from(f.name, 'utf8');
|
|
49
49
|
const deflated = deflateRawSync(data, { level: 6 });
|
|
50
50
|
const useDeflate = deflated.length < data.length;
|
|
@@ -96,7 +96,7 @@ export function writeZipFile(files, outPath) {
|
|
|
96
96
|
end.writeUInt32LE(cdStart, 16);
|
|
97
97
|
write(end);
|
|
98
98
|
if (offset >= MAX_U32)
|
|
99
|
-
throw new Error('
|
|
99
|
+
throw new Error('The zip exceeds the 4 GB limit.');
|
|
100
100
|
return { entries: sorted.length, bytes: offset };
|
|
101
101
|
}
|
|
102
102
|
finally {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enformik/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Enformik
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Enformik developer CLI: website deployment and application module (schema) upgrades.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|