promigence 0.1.0-beta.6 → 0.1.0-beta.7
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/dist/account.js +23 -1
- package/dist/agent.js +11 -1
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +20 -15
- package/dist/credits.js +10 -7
- package/dist/devcontainer.d.ts +5 -0
- package/dist/devcontainer.js +5 -0
- package/dist/dirsnap.js +1 -1
- package/dist/generated.js +9 -0
- package/dist/github.js +2 -3
- package/dist/gitlab.js +2 -2
- package/dist/guide.js +5 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/login.js +2 -0
- package/dist/pricing.d.ts +3 -1
- package/dist/pricing.js +10 -7
- package/dist/runners-spec.js +22 -11
- package/dist/runners.js +2 -2
- package/dist/sdk/client.d.ts +62 -24
- package/dist/sdk/client.js +17 -20
- package/dist/sdk/core.d.ts +1 -1
- package/dist/sdk/core.js +4 -2
- package/dist/sdk/models.gen.d.ts +73 -19
- package/dist/sdk/sandbox.d.ts +2 -4
- package/dist/sdk/sandbox.js +0 -2
- package/dist/surface.js +3 -4
- package/llms.txt +4 -4
- package/package.json +1 -1
package/dist/account.js
CHANGED
|
@@ -4,6 +4,21 @@ import { duration } from "./report.js";
|
|
|
4
4
|
import { emit, ms, okEnvelope, overMost, parseCount, parseDuration, parseUsd, usageError } from "./io.js";
|
|
5
5
|
import { sh } from "./term.js";
|
|
6
6
|
const usd = (n) => n === undefined ? "—" : `$${n.toFixed(n < 0.01 && n > 0 ? 4 : 2)}`;
|
|
7
|
+
export const PAYMENT_METHOD_NAMES = {
|
|
8
|
+
apple_pay: "Apple Pay",
|
|
9
|
+
google_pay: "Google Pay",
|
|
10
|
+
samsung_pay: "Samsung Pay",
|
|
11
|
+
link: "Link",
|
|
12
|
+
cashapp: "Cash App Pay",
|
|
13
|
+
amazon_pay: "Amazon Pay",
|
|
14
|
+
};
|
|
15
|
+
export function paymentMethodText(card) {
|
|
16
|
+
const key = card?.brand ?? "";
|
|
17
|
+
const named = Object.prototype.hasOwnProperty.call(PAYMENT_METHOD_NAMES, key) ? PAYMENT_METHOD_NAMES[key] : undefined;
|
|
18
|
+
if (named)
|
|
19
|
+
return card?.last4 ? `${named} •••• ${card.last4}` : named;
|
|
20
|
+
return `${card?.brand ?? "card"} ••${card?.last4 ?? "??"}`;
|
|
21
|
+
}
|
|
7
22
|
function limitOf(ctx, fallback, most) {
|
|
8
23
|
const raw = str(ctx, "--limit");
|
|
9
24
|
if (raw === undefined)
|
|
@@ -244,7 +259,7 @@ const billingUsage = async (ctx) => {
|
|
|
244
259
|
const trial = b.trial;
|
|
245
260
|
if (trial?.credits_remaining !== undefined)
|
|
246
261
|
lines.push(` trial credits left: ${usd(trial.credits_remaining)}`);
|
|
247
|
-
lines.push(` card: ${b.card_on_file ?
|
|
262
|
+
lines.push(` card: ${b.card_on_file ? paymentMethodText(b.card) : `none on file${billingOff ? " (card payments are not open yet)" : ""}`}`);
|
|
248
263
|
if (b.upcoming_invoice?.amount_due !== undefined && b.upcoming_invoice?.amount_due !== null) {
|
|
249
264
|
lines.push(` next invoice: ${usd(b.upcoming_invoice.amount_due)}`);
|
|
250
265
|
}
|
|
@@ -520,6 +535,12 @@ export function sandboxFactLines(s, facts, nowMs = Date.now()) {
|
|
|
520
535
|
}
|
|
521
536
|
return out;
|
|
522
537
|
}
|
|
538
|
+
export function supportStopLines(s) {
|
|
539
|
+
if (s.outcome !== "stopped_by_support")
|
|
540
|
+
return [];
|
|
541
|
+
const message = s.failure?.message;
|
|
542
|
+
return [` ${typeof message === "string" && message.trim() ? message : "Stopped by Promigence support"}`];
|
|
543
|
+
}
|
|
523
544
|
const sandboxInfo = async (ctx) => {
|
|
524
545
|
const sid = positional(ctx, "a sandbox id", "promigence sandbox info sbx_01");
|
|
525
546
|
const c = client(ctx);
|
|
@@ -539,6 +560,7 @@ const sandboxInfo = async (ctx) => {
|
|
|
539
560
|
: why ? [] : [[sh `promigence exec --sandbox ${sid} -- CMD`, "run something in it"]]));
|
|
540
561
|
const lines = [
|
|
541
562
|
`sandbox ${sid}: ${s.state}${why ? ` (${why})` : ""}${s.outcome ? ` (${s.outcome})` : ""}${s.run_id ? ` · run ${s.run_id}` : ""}`,
|
|
563
|
+
...supportStopLines(s),
|
|
542
564
|
...(why ? [" its files and state are kept, and paused time is not billed"] : []),
|
|
543
565
|
...queuedLines([s]),
|
|
544
566
|
...(s.snapshot_id ? [` snapshot ${s.snapshot_id}`] : []),
|
package/dist/agent.js
CHANGED
|
@@ -42,9 +42,17 @@ export const SETUP_SCRIPT = [
|
|
|
42
42
|
'if [ -n "$3" ] && [ ! -d "$4/.git" ]; then',
|
|
43
43
|
' mkdir -p "$(dirname "$4")"',
|
|
44
44
|
' git clone --quiet -- "$3" "$4" >&2',
|
|
45
|
+
' if [ -n "${5:-}" ]; then chown -R -- "$5" "$4"; fi',
|
|
45
46
|
"fi",
|
|
46
47
|
'"$1" --version',
|
|
47
48
|
].join("\n");
|
|
49
|
+
async function snapshotUser(c, snapshot) {
|
|
50
|
+
if (snapshot === DEFAULT_SNAPSHOT)
|
|
51
|
+
return undefined;
|
|
52
|
+
const rec = await c.snapshots.get(snapshot).catch(() => undefined);
|
|
53
|
+
const u = rec?.user;
|
|
54
|
+
return typeof u === "string" && u !== "" && u !== "root" && u !== "0" ? u : undefined;
|
|
55
|
+
}
|
|
48
56
|
export function repoDir(url) {
|
|
49
57
|
const last = url.replace(/[/]+$/, "").split(/[/:]/).pop() ?? "";
|
|
50
58
|
const name = last.replace(/\.git$/, "").replace(/[^A-Za-z0-9._-]/g, "") || "repo";
|
|
@@ -269,8 +277,10 @@ const agentStart = async (ctx) => {
|
|
|
269
277
|
throw e;
|
|
270
278
|
}
|
|
271
279
|
};
|
|
280
|
+
const runAs = await snapshotUser(c, snapshot);
|
|
272
281
|
const setup = await endOnThrow(c.sandboxes.exec(sid, {
|
|
273
|
-
argv: ["sh", "-c", SETUP_SCRIPT, "agent-setup", agent.bin, agent.pkg, repo ?? "", cwd ?? ""],
|
|
282
|
+
argv: ["sh", "-c", SETUP_SCRIPT, "agent-setup", agent.bin, agent.pkg, repo ?? "", cwd ?? "", runAs ?? ""],
|
|
283
|
+
user: "root",
|
|
274
284
|
timeoutMs: 10 * 60_000,
|
|
275
285
|
}));
|
|
276
286
|
if (setup.exit_code !== 0) {
|
package/dist/commands.d.ts
CHANGED
|
@@ -99,6 +99,10 @@ export declare function outputTails(c: PromigenceClient, sandboxes: ReadonlyArra
|
|
|
99
99
|
}>;
|
|
100
100
|
}>): Promise<Map<string, OutputTail>>;
|
|
101
101
|
export declare function positional(ctx: Ctx, what: string, example: string): string;
|
|
102
|
+
export declare function supportStoppedLines(sandboxes: ReadonlyArray<{
|
|
103
|
+
sandbox_id: string;
|
|
104
|
+
outcome?: string | null;
|
|
105
|
+
}>): string[];
|
|
102
106
|
export declare function filesPutTarArgs(out: string, parent: string, base: string, noMetadata?: string[]): string[];
|
|
103
107
|
export declare const HANDLERS: Readonly<Record<string, Handler>>;
|
|
104
108
|
export {};
|
package/dist/commands.js
CHANGED
|
@@ -6,7 +6,7 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
6
6
|
import { DEFAULT_API_URL, DEFAULT_SNAPSHOT, ERRORS_DOCS_URL, MAX_OUTPUT_BYTES_DEFAULT, CONTEXT_GIT_RE, PromigenceClient, checkRepoUrl, withoutPin, PromigenceError, SpendCapError, TimeoutError, checkImageRef, contextRelative, credentialsPath, FILE_PUT_CHUNK_BYTES, imageSnapshot, isWaiting, execOutputBytes, listContext, packContext, readCredentials, } from "./sdk/index.js";
|
|
7
7
|
import { fmtMs, recordTiming } from "./timing.js";
|
|
8
8
|
import { capacityLine, queuedClause, queuedLines, statusLine } from "./capacity.js";
|
|
9
|
-
import { DevcontainerError, detectOne, load, planJson, planLines, planSetup, resolveGiven, snapshotEnv, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|
|
9
|
+
import { DevcontainerError, detectOne, load, planJson, planLines, planSetup, resolveGiven, snapshotEnv, snapshotUsers, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|
|
10
10
|
import { detect, ecosystemOfCommands, imageCreateCommand, readConfig } from "./init.js";
|
|
11
11
|
import { CREDENTIAL_REMEDY, confirmOverride, credentialRefusal, emitUploadList, localTarNoMetadataArgs } from "./uploadguard.js";
|
|
12
12
|
import { emit, errorEnvelope, errorTextLines, exitWith, forkLine, forkSummary, gradeLine, ms, noCommandFix, okEnvelope, parseMaxOutput, recordNotice, sandboxGrade, parseCommandTimeout, parseCount, parseDuration, parseUsd, pickExit, reportSummaryLines, toPromigenceError, totalsLine, usageError, write, writeBytes, } from "./io.js";
|
|
@@ -212,7 +212,7 @@ function reportExits(report) {
|
|
|
212
212
|
return codes;
|
|
213
213
|
if (t.env_failed > 0 || (t.grader_failed ?? 0) > 0)
|
|
214
214
|
codes.push(3);
|
|
215
|
-
if (t.not_started > 0 || t.cap_killed > 0 || t.expired > 0)
|
|
215
|
+
if (t.not_started > 0 || t.cap_killed > 0 || t.expired > 0 || (t.stopped_by_support ?? 0) > 0)
|
|
216
216
|
codes.push(12);
|
|
217
217
|
if (t.task_failed > 0 || t.snapshot_failed > 0)
|
|
218
218
|
codes.push(4);
|
|
@@ -385,7 +385,7 @@ function boundUseLines(res) {
|
|
|
385
385
|
" requests the sandbox sends to this address are authenticated with it:",
|
|
386
386
|
...urls.map((u) => ` ${urlLine(u, res.env)}`),
|
|
387
387
|
` a request sent straight to ${hosts.map((h) => `https://${h}`).join(" or ")} is not authenticated: send it to the address above`,
|
|
388
|
-
...(res.scan_responses ===
|
|
388
|
+
...(res.scan_responses === true ? [" the host's answers never show the value: an echo service shows asterisks instead"] : []),
|
|
389
389
|
];
|
|
390
390
|
return lines;
|
|
391
391
|
}
|
|
@@ -723,7 +723,10 @@ async function devcontainerCreate(ctx, plan) {
|
|
|
723
723
|
}
|
|
724
724
|
const setup = planSetup(plan, { repo, commit, userSetup: list(ctx, "--setup") });
|
|
725
725
|
const env = snapshotEnv(plan);
|
|
726
|
-
const
|
|
726
|
+
const users = snapshotUsers(plan);
|
|
727
|
+
const opts = {
|
|
728
|
+
...source, ...(setup.length ? { setup } : {}), ...(env ? { env } : {}), ...(users ?? {}),
|
|
729
|
+
};
|
|
727
730
|
const verify = str(ctx, "--verify");
|
|
728
731
|
if (verify !== undefined)
|
|
729
732
|
opts.verify = { cmd: wrapCommand(plan, verify) };
|
|
@@ -762,7 +765,6 @@ const snapshotCreate = async (ctx) => {
|
|
|
762
765
|
const plan = gh ? undefined : devcontainerPlan(ctx, form);
|
|
763
766
|
const setup = list(ctx, "--setup");
|
|
764
767
|
const alias = str(ctx, "--alias");
|
|
765
|
-
const cpuClass = str(ctx, "--cpu-class");
|
|
766
768
|
let opts;
|
|
767
769
|
let label;
|
|
768
770
|
let submitted;
|
|
@@ -833,8 +835,6 @@ const snapshotCreate = async (ctx) => {
|
|
|
833
835
|
const c = client(ctx);
|
|
834
836
|
if (alias !== undefined)
|
|
835
837
|
opts.alias = alias;
|
|
836
|
-
if (cpuClass !== undefined)
|
|
837
|
-
opts.cpuClass = cpuClass;
|
|
838
838
|
const grade = gradeOpts(ctx);
|
|
839
839
|
if (grade)
|
|
840
840
|
opts.grade = grade;
|
|
@@ -1472,8 +1472,6 @@ const exec = async (ctx) => {
|
|
|
1472
1472
|
stdout: res.stdout ?? "",
|
|
1473
1473
|
stderr: res.stderr ?? "",
|
|
1474
1474
|
truncated: res.truncated ?? false,
|
|
1475
|
-
...(res.stdout_ref === undefined ? {} : { stdout_ref: res.stdout_ref }),
|
|
1476
|
-
...(res.stderr_ref === undefined ? {} : { stderr_ref: res.stderr_ref }),
|
|
1477
1475
|
...(res.stdout_b64 === undefined ? {} : { stdout_b64: res.stdout_b64 }),
|
|
1478
1476
|
...(res.stderr_b64 === undefined ? {} : { stderr_b64: res.stderr_b64 }),
|
|
1479
1477
|
...(res["idempotent_replay"] === true ? { idempotent_replay: true } : {}),
|
|
@@ -1598,7 +1596,7 @@ async function runImageSnapshot(ctx, c) {
|
|
|
1598
1596
|
if (cap < price + (quoted ?? 0)) {
|
|
1599
1597
|
const existing = (quoted ?? 0) <= cap ? await existingImageSnapshot(c, image) : undefined;
|
|
1600
1598
|
throw new SpendCapError(`a new image's snapshot build costs ${moneyUsd(price)} on top of the run` +
|
|
1601
|
-
`${quoted === undefined ? "" : ` (about ${moneyUsd(quoted)})`}, over --cap ${moneyUsd(cap)}; nothing was built or started`, (existing === undefined ? "" : `This image already has snapshot ${existing}: run it under this cap with ${sh `promigence run --snapshot ${existing}`} instead of --image. Or `) +
|
|
1599
|
+
`${quoted === undefined ? "" : ` (about ${moneyUsd(quoted)})`}, over --cap ${moneyUsd(cap)}; nothing was built or started`, (existing === undefined ? "" : `This image already has snapshot ${existing}: run it under this cap with ${sh `promigence run --snapshot ${existing}`} instead of --image (any start command or settings it was made with come with it). Or `) +
|
|
1602
1600
|
`${existing === undefined ? "Pass" : "pass"} --cap ${needed.toFixed(2)} or more (a build is charged once, only when this image has no snapshot yet, and reused free after), ` +
|
|
1603
1601
|
`or build it first, ${sh `promigence snapshot create --image ${image} --wait`}, and run its snapshot with --snapshot.`);
|
|
1604
1602
|
}
|
|
@@ -1633,7 +1631,7 @@ async function existingImageSnapshot(c, image) {
|
|
|
1633
1631
|
for (let page = 0; page < 5; page++) {
|
|
1634
1632
|
const p = await c.snapshots.listPage({ limit: 100, ...(cursor === undefined ? {} : { cursor }) });
|
|
1635
1633
|
const hit = p.items.find((s) => s.source?.kind === "image" && typeof s.source.digest === "string" && digests.has(s.source.digest) &&
|
|
1636
|
-
s.source["from_sandbox"] === undefined && !s.unavailable && !(s.setup?.length) && !s.
|
|
1634
|
+
s.source["from_sandbox"] === undefined && !s.unavailable && !(s.setup?.length) && !s.verify?.cmd &&
|
|
1637
1635
|
(s.tier === undefined || s.tier === "small"));
|
|
1638
1636
|
if (hit)
|
|
1639
1637
|
return hit.snapshot_id;
|
|
@@ -1967,11 +1965,12 @@ function settledWhy(report, keep) {
|
|
|
1967
1965
|
return `${open.length} sandbox${open.length === 1 ? " has" : "es have"} not ended yet, so ${open.length === 1 ? "its" : "their"} last charge is still to land`;
|
|
1968
1966
|
}
|
|
1969
1967
|
const OUTCOME_EXIT = {
|
|
1970
|
-
env_failed: 3, grader_failed: 3, not_started: 12, cap_killed: 12, expired: 12,
|
|
1968
|
+
env_failed: 3, grader_failed: 3, not_started: 12, cap_killed: 12, expired: 12, stopped_by_support: 12,
|
|
1969
|
+
task_failed: 4, snapshot_failed: 4,
|
|
1971
1970
|
};
|
|
1972
1971
|
const OUTCOME_CODE = {
|
|
1973
1972
|
env_failed: "env_failed", grader_failed: "grader_failed", not_started: "not_started", cap_killed: "cap_killed",
|
|
1974
|
-
expired: "expired", task_failed: "nonzero_exit", snapshot_failed: "snapshot_failed",
|
|
1973
|
+
expired: "expired", stopped_by_support: "stopped_by_support", task_failed: "nonzero_exit", snapshot_failed: "snapshot_failed",
|
|
1975
1974
|
};
|
|
1976
1975
|
function failureFix(runId, s) {
|
|
1977
1976
|
const f = s.failure;
|
|
@@ -2005,7 +2004,7 @@ export function runHeadline(report, count) {
|
|
|
2005
2004
|
const t = (report.totals ?? {});
|
|
2006
2005
|
const others = Object.entries(t)
|
|
2007
2006
|
.filter(([k, v]) => !["completed", "env_verified", "evidence_missing"].includes(k) && typeof v === "number" && v > 0)
|
|
2008
|
-
.map(([k, v]) => `${v} ${k}`);
|
|
2007
|
+
.map(([k, v]) => `${v} ${k === "stopped_by_support" ? "stopped by Promigence support" : k}`);
|
|
2009
2008
|
const done = typeof t["completed"] === "number" ? t["completed"] : 0;
|
|
2010
2009
|
return `run ${report.run_id}: ${done} of ${count} completed${others.length ? ` · ${others.join(" · ")}` : ""}` +
|
|
2011
2010
|
`${report.cost ? ` · charged ${moneyUsd(report.cost.charged)}` : ""}`;
|
|
@@ -2125,6 +2124,12 @@ const runReport = async (ctx) => {
|
|
|
2125
2124
|
const next = nexts([sh `promigence run status ${runId}`, "the run's current state"]);
|
|
2126
2125
|
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { run_id: runId, report }, next), reportLines(report, view), 0, ctx.pointer);
|
|
2127
2126
|
};
|
|
2127
|
+
export function supportStoppedLines(sandboxes) {
|
|
2128
|
+
const stopped = sandboxes.filter((s) => s.outcome === "stopped_by_support");
|
|
2129
|
+
if (!stopped.length)
|
|
2130
|
+
return [];
|
|
2131
|
+
return [` ${stopped.length} stopped by Promigence support; ${sh `promigence sandbox info ${stopped[0].sandbox_id}`} says why`];
|
|
2132
|
+
}
|
|
2128
2133
|
const runStatus = async (ctx) => {
|
|
2129
2134
|
const runId = positional(ctx, "a run id", "promigence run status run_01 --wait");
|
|
2130
2135
|
const c = client(ctx);
|
|
@@ -2141,7 +2146,7 @@ const runStatus = async (ctx) => {
|
|
|
2141
2146
|
states[s.state] = (states[s.state] ?? 0) + 1;
|
|
2142
2147
|
const next = nexts([sh `promigence run report ${runId} --json`, "the validity report"]);
|
|
2143
2148
|
const tail = Object.keys(states).length ? ` - ${Object.entries(states).map(([k, v]) => `${k} ${v}`).join(" - ")}` : "";
|
|
2144
|
-
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { run_id: runId, state: run.state, sandbox_states: states, run }, next), [`run ${runId}: ${run.state}${tail}`, ...queuedLines(run.sandboxes ?? [])], 0, ctx.pointer);
|
|
2149
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { run_id: runId, state: run.state, sandbox_states: states, run }, next), [`run ${runId}: ${run.state}${tail}`, ...supportStoppedLines(run.sandboxes ?? []), ...queuedLines(run.sandboxes ?? [])], 0, ctx.pointer);
|
|
2145
2150
|
};
|
|
2146
2151
|
const runKill = async (ctx) => {
|
|
2147
2152
|
const runId = positional(ctx, "a run id", "promigence run kill run_01");
|
package/dist/credits.js
CHANGED
|
@@ -150,18 +150,21 @@ export async function claimOnRefusal(ctx, io = {}) {
|
|
|
150
150
|
const opened = maybeOpen(ctx, link.url);
|
|
151
151
|
return [...claimLines(o, link, opened), "Once it is claimed, run the command again."];
|
|
152
152
|
}
|
|
153
|
-
async function
|
|
153
|
+
async function cardState(c, o) {
|
|
154
|
+
const none = { off: false, onFile: false };
|
|
154
155
|
if (!(typeof o.card_bonus_usd === "number" && o.card_bonus_usd > 0) || o.state === "none")
|
|
155
|
-
return
|
|
156
|
-
return c.billing.usage().then((b) => b.billing_enabled === false).catch(() =>
|
|
156
|
+
return none;
|
|
157
|
+
return c.billing.usage().then((b) => ({ off: b.billing_enabled === false, onFile: b.card_on_file === true })).catch(() => none);
|
|
157
158
|
}
|
|
158
|
-
function offerLines(head, o,
|
|
159
|
+
function offerLines(head, o, st = { off: false, onFile: false }) {
|
|
159
160
|
const lines = [`${head}: ${say(o)}`];
|
|
160
161
|
if (o.state !== "none") {
|
|
161
162
|
if (typeof o.credit_usd === "number")
|
|
162
163
|
lines.push(` credit: ${dollars(o.credit_usd)}`);
|
|
163
164
|
if (typeof o.card_bonus_usd === "number" && o.card_bonus_usd > 0) {
|
|
164
|
-
lines.push(
|
|
165
|
+
lines.push(st.onFile
|
|
166
|
+
? ` card bonus: ${dollars(o.card_bonus_usd)} more with your first paid invoice`
|
|
167
|
+
: ` card bonus: ${dollars(o.card_bonus_usd)} more when you add a card${st.off ? " (card payments are not open yet)" : " (promigence billing card)"}`);
|
|
165
168
|
}
|
|
166
169
|
if (o.expires_at)
|
|
167
170
|
lines.push(` expires: ${o.expires_at}`);
|
|
@@ -174,14 +177,14 @@ function offerLines(head, o, off = false) {
|
|
|
174
177
|
const credits = async (ctx) => {
|
|
175
178
|
const c = new PromigenceClient({ ...clientOpts(ctx), onNotice: recordNotice });
|
|
176
179
|
const o = await getOffer(c);
|
|
177
|
-
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...o }, offerNext(o)), offerLines("promigence credits", o, await
|
|
180
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...o }, offerNext(o)), offerLines("promigence credits", o, await cardState(c, o)), 0, ctx.pointer);
|
|
178
181
|
};
|
|
179
182
|
const creditsClaim = async (ctx) => {
|
|
180
183
|
const opts = { ...clientOpts(ctx), onNotice: recordNotice };
|
|
181
184
|
const c = new PromigenceClient(opts);
|
|
182
185
|
const o = await getOffer(c);
|
|
183
186
|
if (o.state !== "claimable") {
|
|
184
|
-
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...o, claim_url: null }, offerNext(o)), offerLines("promigence credits claim", o, await
|
|
187
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...o, claim_url: null }, offerNext(o)), offerLines("promigence credits claim", o, await cardState(c, o)), 0, ctx.pointer);
|
|
185
188
|
}
|
|
186
189
|
const link = await getClaimLink(claimClient(opts));
|
|
187
190
|
const opened = maybeOpen(ctx, link.url);
|
package/dist/devcontainer.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface Plan {
|
|
|
62
62
|
env: Record<string, string>;
|
|
63
63
|
workspaceFolder: string;
|
|
64
64
|
user: string | undefined;
|
|
65
|
+
remoteUser?: string | undefined;
|
|
65
66
|
lifecycle: LifecycleStep[];
|
|
66
67
|
ports: number[];
|
|
67
68
|
tier: string | undefined;
|
|
@@ -84,6 +85,10 @@ export declare function lifecycleSteps(v: unknown, field: string, ctx: Substitut
|
|
|
84
85
|
export declare function shellQuote(s: string): string;
|
|
85
86
|
export declare function wrapCommand(plan: Plan, command: string): string;
|
|
86
87
|
export declare function snapshotEnv(plan: Plan): Record<string, string> | undefined;
|
|
88
|
+
export declare function snapshotUsers(plan: Plan): {
|
|
89
|
+
user: string;
|
|
90
|
+
setupUser: string;
|
|
91
|
+
} | undefined;
|
|
87
92
|
export interface SetupOptions {
|
|
88
93
|
repo?: string | undefined;
|
|
89
94
|
commit?: string | undefined;
|
package/dist/devcontainer.js
CHANGED
|
@@ -462,6 +462,7 @@ export function translate(loaded, opts) {
|
|
|
462
462
|
env,
|
|
463
463
|
workspaceFolder,
|
|
464
464
|
user,
|
|
465
|
+
...(remoteUser === undefined ? {} : { remoteUser }),
|
|
465
466
|
lifecycle,
|
|
466
467
|
ports,
|
|
467
468
|
tier,
|
|
@@ -682,6 +683,10 @@ export function wrapCommand(plan, command) {
|
|
|
682
683
|
export function snapshotEnv(plan) {
|
|
683
684
|
return Object.keys(plan.env).length ? { ...plan.env } : undefined;
|
|
684
685
|
}
|
|
686
|
+
export function snapshotUsers(plan) {
|
|
687
|
+
const user = plan.remoteUser ?? plan.user;
|
|
688
|
+
return user === undefined ? undefined : { user, setupUser: "root" };
|
|
689
|
+
}
|
|
685
690
|
export function planSetup(plan, opts = {}) {
|
|
686
691
|
const steps = [];
|
|
687
692
|
if (opts.repo && opts.commit) {
|
package/dist/dirsnap.js
CHANGED
|
@@ -25,7 +25,7 @@ const INTERRUPTED = {
|
|
|
25
25
|
};
|
|
26
26
|
const NOT_WITH_DIR = [
|
|
27
27
|
"--repo", "--commit", "--ref", "--track", "--also-repo", "--image", "--dockerfile", "--context",
|
|
28
|
-
"--devcontainer", "--start", "--
|
|
28
|
+
"--devcontainer", "--start", "--grade", "--holdout", "--grade-score", "--grade-timeout",
|
|
29
29
|
"--grade-output", "--tier",
|
|
30
30
|
];
|
|
31
31
|
export { secretLike };
|
package/dist/generated.js
CHANGED
|
@@ -83,6 +83,9 @@ export const ERRORS = [
|
|
|
83
83
|
{ code: "aborted_by_client", category: "other", http: 200, exit: 130, billed: "yes",
|
|
84
84
|
message: "sandbox killed by the client",
|
|
85
85
|
fix: "Nothing to fix; billed for the seconds used." },
|
|
86
|
+
{ code: "stopped_by_support", category: "other", http: 200, exit: 12, billed: "unless_ours",
|
|
87
|
+
message: "Stopped by Promigence support: {reason}",
|
|
88
|
+
fix: "Billed for the seconds it ran, as a kill is, unless support found the cause was on our side; then that time is credited back. Questions: tell support@promigence.ai the sandbox id." },
|
|
86
89
|
{ code: "expired", category: "other", http: 200, exit: 12, billed: "yes",
|
|
87
90
|
message: "sandbox timeout reached (counted from hand-off)",
|
|
88
91
|
fix: "Raise `--timeout` if the task needs longer." },
|
|
@@ -176,6 +179,9 @@ export const ERRORS = [
|
|
|
176
179
|
{ code: "credit_unclaimed", category: "other", http: 402, exit: 5, billed: "no",
|
|
177
180
|
message: "this account has free credit waiting to be claimed",
|
|
178
181
|
fix: "Claim it in your browser: `promigence credits claim` (owners; members ask an owner). Or add a card: `promigence billing card`. Nothing was started or charged." },
|
|
182
|
+
{ code: "setup_required", category: "other", http: 403, exit: 7, billed: "no",
|
|
183
|
+
message: "finish setting up your account before starting new work",
|
|
184
|
+
fix: "Open {setup_url} and answer the setup questions (under a minute), then run this again. Nothing was started or charged." },
|
|
179
185
|
{ code: "card_required", category: "other", http: 402, exit: 5, billed: "no",
|
|
180
186
|
message: "a card is required for new work since {effective_at}",
|
|
181
187
|
fix: "Add a card (owners: `promigence billing card`, or POST /v1/billing/checkout; members ask an owner). Your trial credits are kept and used first; reads and results stay available." },
|
|
@@ -233,6 +239,9 @@ export const ERRORS = [
|
|
|
233
239
|
{ code: "key_expired", category: "other", http: 401, exit: 7, billed: "no",
|
|
234
240
|
message: "this API key expired at {expired_at}",
|
|
235
241
|
fix: "Create a new key (`promigence keys create`, or sign in again with `promigence login`) and replace this one wherever it is used." },
|
|
242
|
+
{ code: "account_blocked", category: "other", http: 401, exit: 7, billed: "no",
|
|
243
|
+
message: "this account is blocked",
|
|
244
|
+
fix: "Write to support@promigence.ai to have it reviewed." },
|
|
236
245
|
];
|
|
237
246
|
export const ERROR_BY_CODE = Object.fromEntries(ERRORS.map((e) => [e.code, e]));
|
|
238
247
|
export const CLIENT_ERRORS = [
|
package/dist/github.js
CHANGED
|
@@ -14,7 +14,7 @@ function pollMs() {
|
|
|
14
14
|
}
|
|
15
15
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
16
16
|
function notConfigured(s) {
|
|
17
|
-
return usageError(s.message ?? "GitHub
|
|
17
|
+
return usageError(s.message ?? "Connecting GitHub isn't available here yet", "Bring the code in another way: `promigence snapshot create --dir .` uploads the files git tracks here, or clone a public repository in a --setup step.");
|
|
18
18
|
}
|
|
19
19
|
function connectionLine(c) {
|
|
20
20
|
return ` ${c.account} ${c.account_type} · ${c.repository_selection} repositories · installation ${c.installation_id}${c.suspended ? " SUSPENDED on GitHub" : ""}`;
|
|
@@ -107,7 +107,7 @@ const githubStatus = async (ctx) => {
|
|
|
107
107
|
const s = await client(ctx).github.status();
|
|
108
108
|
if (!s.configured) {
|
|
109
109
|
const next = nexts(["promigence snapshot create --dir . --alias app --wait", "bring the files git tracks here instead"]);
|
|
110
|
-
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...s }, next), [`github: ${s.message ?? "GitHub
|
|
110
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...s }, next), [`github: ${s.message ?? "Connecting GitHub isn't available here yet"}`], 0, ctx.pointer);
|
|
111
111
|
}
|
|
112
112
|
const lines = [
|
|
113
113
|
s.connections.length
|
|
@@ -215,7 +215,6 @@ const NOT_WITH_GITHUB = [
|
|
|
215
215
|
["--devcontainer", "it is not read for a connected repository; use --setup steps"],
|
|
216
216
|
["--start", "a background process is part of an image snapshot"],
|
|
217
217
|
["--also-repo", "clone further repositories in a --setup step"],
|
|
218
|
-
["--cpu-class", "the build goes wherever there is room"],
|
|
219
218
|
["--dir", "--dir uploads a local tree instead; pass one of them"],
|
|
220
219
|
];
|
|
221
220
|
const GITHUB_ONLY = ["--ref", "--from", "--track"];
|
package/dist/gitlab.js
CHANGED
|
@@ -12,7 +12,7 @@ function pollMs() {
|
|
|
12
12
|
}
|
|
13
13
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
14
14
|
function notConfigured(s) {
|
|
15
|
-
return usageError(s.message ?? "GitLab
|
|
15
|
+
return usageError(s.message ?? "Connecting GitLab isn't available here yet", "Bring the code in another way: `promigence snapshot create --dir .` uploads the files git tracks here, or clone in a --setup step.");
|
|
16
16
|
}
|
|
17
17
|
function connectionLine(c) {
|
|
18
18
|
return ` ${c.username} on ${c.gitlab_url} · ${c.connection_id}${c.broken ? ` BROKEN: ${c.broken_reason ?? "connect again"}` : ""}`;
|
|
@@ -108,7 +108,7 @@ const gitlabStatus = async (ctx) => {
|
|
|
108
108
|
const s = await client(ctx).gitlab.status();
|
|
109
109
|
if (!s.configured) {
|
|
110
110
|
const next = nexts(["promigence snapshot create --dir . --alias app --wait", "bring the files git tracks here instead"]);
|
|
111
|
-
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...s }, next), [`gitlab: ${s.message ?? "GitLab
|
|
111
|
+
return emit(ctx.mode, okEnvelope(ctx.cmd.name, { ...s }, next), [`gitlab: ${s.message ?? "Connecting GitLab isn't available here yet"}`], 0, ctx.pointer);
|
|
112
112
|
}
|
|
113
113
|
const lines = [
|
|
114
114
|
s.connections.length
|
package/dist/guide.js
CHANGED
|
@@ -10,7 +10,7 @@ export const SECTIONS = ["core", "commands", "errors", "cost"];
|
|
|
10
10
|
export const BUDGETS = {
|
|
11
11
|
core: 1200,
|
|
12
12
|
commands: 4475,
|
|
13
|
-
errors:
|
|
13
|
+
errors: 2325,
|
|
14
14
|
cost: 600,
|
|
15
15
|
};
|
|
16
16
|
export function lookupError(code) {
|
|
@@ -222,8 +222,10 @@ function renderCost(prices) {
|
|
|
222
222
|
const source = prices === undefined
|
|
223
223
|
? "List prices when this CLI was built. Live, and your plan's: `promigence pricing`."
|
|
224
224
|
: prices.source === "api"
|
|
225
|
-
? `Live from the API (${view?.as_of ?? "now"}):
|
|
226
|
-
:
|
|
225
|
+
? `Live from the API (${view?.as_of ?? "now"}): your organization's prices. \`promigence pricing\` shows the rest.`
|
|
226
|
+
: prices.source === "published"
|
|
227
|
+
? "Published list prices. Your organization's own: `promigence login`, then `promigence pricing`."
|
|
228
|
+
: `${prices.note ?? "list prices at build time; could not reach the API"} (\`promigence pricing\` asks again).`;
|
|
227
229
|
const limit = view?.limits.max_sandbox_vcpus;
|
|
228
230
|
const out = [
|
|
229
231
|
`# Promigence — what it costs (promigence@${VERSION})`,
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ export { FENCE_BEGIN, FENCE_END, FENCE_MAX_LINES, fenceBody, install, spliceFenc
|
|
|
9
9
|
export type { Artifact, InstallResult } from "./install.js";
|
|
10
10
|
export { detect, gitFacts, normalizeRemote, readConfig, renderConfig, stripRemoteCredentials } from "./init.js";
|
|
11
11
|
export type { Config, Detection, Ecosystem, GitFacts, ReadConfig } from "./init.js";
|
|
12
|
-
export { DEVCONTAINER_NAMES, DEVCONTAINER_SPEC, DevcontainerError, detectOne, discover, load, parseJsonc, planJson, planLines, planSetup, resolveGiven, shellQuote, snapshotEnv, substituteString, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|
|
12
|
+
export { DEVCONTAINER_NAMES, DEVCONTAINER_SPEC, DevcontainerError, detectOne, discover, load, parseJsonc, planJson, planLines, planSetup, resolveGiven, shellQuote, snapshotEnv, snapshotUsers, substituteString, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|
|
13
13
|
export type { LoadedDevcontainer, Plan, PlanSource, LifecycleStep, IgnoredProperty } from "./devcontainer.js";
|
|
14
14
|
export type { Command, Flag, Group, CommandStatus, ExitCode, Envelope, EnvelopeError, NextHint } from "./surface.js";
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { ALL_ERROR_CODES, BILLED_TEXT, BUDGETS, SECTIONS, estimateTokens, guideJ
|
|
|
4
4
|
export { CLIENT_ERRORS, CLIENT_ERROR_BY_CODE, ERRORS, ERROR_BY_CODE, TIERS, BUILD_PRICE, PRICING } from "./generated.js";
|
|
5
5
|
export { FENCE_BEGIN, FENCE_END, FENCE_MAX_LINES, fenceBody, install, spliceFence } from "./install.js";
|
|
6
6
|
export { detect, gitFacts, normalizeRemote, readConfig, renderConfig, stripRemoteCredentials } from "./init.js";
|
|
7
|
-
export { DEVCONTAINER_NAMES, DEVCONTAINER_SPEC, DevcontainerError, detectOne, discover, load, parseJsonc, planJson, planLines, planSetup, resolveGiven, shellQuote, snapshotEnv, substituteString, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|
|
7
|
+
export { DEVCONTAINER_NAMES, DEVCONTAINER_SPEC, DevcontainerError, detectOne, discover, load, parseJsonc, planJson, planLines, planSetup, resolveGiven, shellQuote, snapshotEnv, snapshotUsers, substituteString, translate, workspaceRootFor, wrapCommand, } from "./devcontainer.js";
|
package/dist/login.js
CHANGED
|
@@ -765,6 +765,8 @@ async function signInAndStore(ctx, isSignup) {
|
|
|
765
765
|
if (!idp) {
|
|
766
766
|
throw usageError(`--provider: '${provider}' is not a login method`, `Pass --provider ${Object.keys(cfg.identity_providers).join(" | ")}, or omit it to choose in the browser.`);
|
|
767
767
|
}
|
|
768
|
+
if (provider === "email")
|
|
769
|
+
idp = undefined;
|
|
768
770
|
}
|
|
769
771
|
const keyName = (str(ctx, "--key-name") ?? `cli@${deviceName()}`).slice(0, 64);
|
|
770
772
|
const ref = str(ctx, "--ref")?.trim();
|
package/dist/pricing.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PromigenceClient, type PricingView } from "./sdk/index.js";
|
|
2
2
|
import { type Ctx, type Handler } from "./commands.js";
|
|
3
3
|
export declare const COMPILED_NOTE = "list prices at build time; could not reach the API";
|
|
4
|
+
export declare const PUBLISHED_NOTE = "published list prices; your organization's own need a key (promigence login)";
|
|
4
5
|
export interface PriceTier {
|
|
5
6
|
name: string;
|
|
6
7
|
vcpus: number;
|
|
@@ -12,7 +13,7 @@ export interface PriceTier {
|
|
|
12
13
|
available?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export interface Prices {
|
|
15
|
-
source: "api" | "compiled";
|
|
16
|
+
source: "api" | "compiled" | "published";
|
|
16
17
|
note?: string;
|
|
17
18
|
reason?: string;
|
|
18
19
|
view?: PricingView;
|
|
@@ -24,6 +25,7 @@ export interface Prices {
|
|
|
24
25
|
granularity_seconds: number;
|
|
25
26
|
tiers: PriceTier[];
|
|
26
27
|
}
|
|
28
|
+
export declare function publishedPrices(): Prices;
|
|
27
29
|
export declare function compiledPrices(note?: string, reason?: string): Prices;
|
|
28
30
|
export declare function pricesFrom(view: PricingView): Prices;
|
|
29
31
|
export declare function livePricing(c: PromigenceClient, opts?: {
|
package/dist/pricing.js
CHANGED
|
@@ -4,7 +4,11 @@ import { BUILD_PRICE, PRICING, TIERS } from "./generated.js";
|
|
|
4
4
|
import { emit, okEnvelope, recordNotice } from "./io.js";
|
|
5
5
|
import { usd } from "./money.js";
|
|
6
6
|
export const COMPILED_NOTE = "list prices at build time; could not reach the API";
|
|
7
|
+
export const PUBLISHED_NOTE = "published list prices; your organization's own need a key (promigence login)";
|
|
7
8
|
const nano = (n) => Math.round(n * 1e9) / 1e9;
|
|
9
|
+
export function publishedPrices() {
|
|
10
|
+
return { ...compiledPrices(PUBLISHED_NOTE), source: "published" };
|
|
11
|
+
}
|
|
8
12
|
export function compiledPrices(note = COMPILED_NOTE, reason) {
|
|
9
13
|
const perSecond = (v, g) => (t) => nano(t.vcpus * v + t.mem_gib * g);
|
|
10
14
|
const od = perSecond(PRICING.vcpu_second_usd, PRICING.gib_second_usd);
|
|
@@ -45,15 +49,16 @@ const CALLERS_OWN = new Set(["auth", "key_expired", "forbidden", "invalid_argume
|
|
|
45
49
|
const CACHE_MS = 60_000;
|
|
46
50
|
const cache = new Map();
|
|
47
51
|
export async function livePricing(c, opts = {}) {
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
if (opts.newAccount === true || !c.authenticated)
|
|
53
|
+
return publishedPrices();
|
|
54
|
+
const key = `${c.apiUrl}|${c.apiKey ?? ""}`;
|
|
50
55
|
const hit = cache.get(key);
|
|
51
56
|
let p;
|
|
52
57
|
if (hit && Date.now() - hit.at < CACHE_MS) {
|
|
53
58
|
p = hit.p;
|
|
54
59
|
}
|
|
55
60
|
else {
|
|
56
|
-
p = c.pricing(
|
|
61
|
+
p = c.pricing().then(pricesFrom);
|
|
57
62
|
cache.set(key, { at: Date.now(), p });
|
|
58
63
|
p.catch(() => cache.delete(key));
|
|
59
64
|
}
|
|
@@ -86,9 +91,7 @@ const hours = (s) => s % 3600 === 0 ? `${s / 3600} h` : s % 60 === 0 ? `${s / 60
|
|
|
86
91
|
export function pricingLines(p, opts = {}) {
|
|
87
92
|
const v = p.view;
|
|
88
93
|
const head = p.source === "api" && v
|
|
89
|
-
? `promigence pricing — live, ${v.as_of} · ${v.
|
|
90
|
-
? "what a new account gets"
|
|
91
|
-
: `your organization (${v.org_id ?? "?"}), plan ${v.plan.display_name}${v.plan.version === null ? "" : ` v${v.plan.version}`}`}`
|
|
94
|
+
? `promigence pricing — live, ${v.as_of} · your organization (${v.org_id ?? "?"}), plan ${v.plan.display_name}${v.plan.version === null ? "" : ` v${v.plan.version}`}`
|
|
92
95
|
: `promigence pricing — ${p.note ?? COMPILED_NOTE}`;
|
|
93
96
|
const lines = [
|
|
94
97
|
head,
|
|
@@ -123,7 +126,7 @@ export function pricingJson(p) {
|
|
|
123
126
|
if (p.source === "api" && p.view)
|
|
124
127
|
return { source: "api", note: null, ...p.view };
|
|
125
128
|
return {
|
|
126
|
-
source: "compiled",
|
|
129
|
+
source: p.source === "published" ? "published" : "compiled",
|
|
127
130
|
note: p.note ?? COMPILED_NOTE,
|
|
128
131
|
...(p.reason === undefined ? {} : { reason: p.reason }),
|
|
129
132
|
schema: "promigence.pricing/v1",
|
package/dist/runners-spec.js
CHANGED
|
@@ -3,9 +3,10 @@ export const ACTIONS_RUNNER_VERSION = "2.337.0";
|
|
|
3
3
|
export const ACTIONS_RUNNER_SHA256 = "70920811a4f8ad4328818682bca5c6469c1c942fab52448868071d0063816613";
|
|
4
4
|
export const GITLAB_RUNNER_VERSION = "19.4.1";
|
|
5
5
|
export const GITLAB_RUNNER_SHA256 = "5a159b36c5ff54fda9248e00bb0cdfb62c1112ab3741f5282c04a41e94da1306";
|
|
6
|
+
export const GITLAB_RUNNER_DEB_SHA256 = "47d2fd991e0b17af6f85af8fe3cc0cc429aae782d4f9982724c1f11a4de4a47c";
|
|
6
7
|
export const GITLAB_HELPER_IMAGE = `registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:x86_64-v${GITLAB_RUNNER_VERSION}`;
|
|
7
8
|
export const GITLAB_DEFAULT_JOB_IMAGE = "ubuntu:24.04";
|
|
8
|
-
export const RUNNER_SPEC_REV = { github:
|
|
9
|
+
export const RUNNER_SPEC_REV = { github: 4, gitlab: 4 };
|
|
9
10
|
export const RUNNER_TIERS = ["small", "medium", "large"];
|
|
10
11
|
export const GITHUB_CONFIG_PATH = "/root/.ci-runner-config";
|
|
11
12
|
export const GITLAB_CONFIG_PATH = "/etc/gitlab-runner/config.toml";
|
|
@@ -72,6 +73,9 @@ const b64 = (s) => Buffer.from(s, "utf8").toString("base64");
|
|
|
72
73
|
function writeStep(path, content, mode) {
|
|
73
74
|
return `printf '%s' '${b64(content)}' | base64 -d > ${path}; chmod ${mode} ${path}`;
|
|
74
75
|
}
|
|
76
|
+
export function runnerBuildSteps(s) {
|
|
77
|
+
return [...s.setup, ...s.platform_setup];
|
|
78
|
+
}
|
|
75
79
|
export function runnerImageSpec(kind, tier) {
|
|
76
80
|
if (kind === "github") {
|
|
77
81
|
const url = `https://github.com/actions/runner/releases/download/v${ACTIONS_RUNNER_VERSION}/actions-runner-linux-x64-${ACTIONS_RUNNER_VERSION}.tar.gz`;
|
|
@@ -86,11 +90,6 @@ export function runnerImageSpec(kind, tier) {
|
|
|
86
90
|
`echo '${ACTIONS_RUNNER_SHA256} /tmp/actions-runner.tar.gz' | sha256sum -c -; ` +
|
|
87
91
|
`tar -xzf /tmp/actions-runner.tar.gz -C ${GITHUB_RUNNER_ROOT}; rm -f /tmp/actions-runner.tar.gz`,
|
|
88
92
|
`set -eu; export DEBIAN_FRONTEND=noninteractive; ${GITHUB_RUNNER_ROOT}/bin/installdependencies.sh; rm -rf /var/lib/apt/lists/*`,
|
|
89
|
-
"set -eu; D=/.promigence/docker; test -x \"$D/bin/docker\"; mkdir -p /usr/local/libexec/ci-docker /usr/local/lib/docker/cli-plugins; " +
|
|
90
|
-
`cp "$D/bin/docker" ${USER_DOCKER_CLI}; ` +
|
|
91
|
-
"for p in docker-buildx docker-compose; do rm -f /usr/local/lib/docker/cli-plugins/$p; cp \"$D/cli-plugins/$p\" /usr/local/lib/docker/cli-plugins/$p; done; " +
|
|
92
|
-
`chmod 0755 ${USER_DOCKER_CLI} /usr/local/lib/docker/cli-plugins/docker-buildx /usr/local/lib/docker/cli-plugins/docker-compose; ` +
|
|
93
|
-
`rm -f /usr/local/bin/docker; ${writeStep("/usr/local/bin/docker", DOCKER_WRAPPER_SCRIPT, "0755")}`,
|
|
94
93
|
`set -eu; ${writeStep(GITHUB_LAUNCHER, GITHUB_LAUNCHER_SCRIPT, "0755")}; ` +
|
|
95
94
|
`${writeStep(`${GITHUB_RUNNER_ROOT}/.env`, GITHUB_RUNNER_ENV, "0644")}; chown -R runner:runner /home/runner`,
|
|
96
95
|
];
|
|
@@ -98,20 +97,32 @@ export function runnerImageSpec(kind, tier) {
|
|
|
98
97
|
"git --version; jq --version; python3 --version; lsb_release -rs; " +
|
|
99
98
|
"setpriv --reuid=runner --regid=runner --init-groups env HOME=/home/runner sh -c 'docker --version && docker compose version && docker buildx version'; " +
|
|
100
99
|
`sudo -n -u runner true; id runner; test -x ${GITHUB_LAUNCHER}`;
|
|
101
|
-
|
|
100
|
+
const platform_setup = [
|
|
101
|
+
"set -eu; D=/.promigence/docker; test -x \"$D/bin/docker\"; mkdir -p /usr/local/libexec/ci-docker /usr/local/lib/docker/cli-plugins; " +
|
|
102
|
+
`cp "$D/bin/docker" ${USER_DOCKER_CLI}; ` +
|
|
103
|
+
"for p in docker-buildx docker-compose; do rm -f /usr/local/lib/docker/cli-plugins/$p; cp \"$D/cli-plugins/$p\" /usr/local/lib/docker/cli-plugins/$p; done; " +
|
|
104
|
+
`chmod 0755 ${USER_DOCKER_CLI} /usr/local/lib/docker/cli-plugins/docker-buildx /usr/local/lib/docker/cli-plugins/docker-compose; ` +
|
|
105
|
+
`rm -f /usr/local/bin/docker; ${writeStep("/usr/local/bin/docker", DOCKER_WRAPPER_SCRIPT, "0755")}`,
|
|
106
|
+
];
|
|
107
|
+
return { image: RUNNER_BASE_IMAGE, setup, platform_setup, verify, tier, alias: runnerAlias(kind, tier) };
|
|
102
108
|
}
|
|
103
|
-
const
|
|
109
|
+
const deb = `https://packages.gitlab.com/runner/gitlab-runner/ubuntu/pool/main/g/gitlab-runner/gitlab-runner_${GITLAB_RUNNER_VERSION}-1_amd64.deb`;
|
|
104
110
|
const setup = [
|
|
105
111
|
APT_STEP,
|
|
106
|
-
"set -eu; curl -fsSL --retry 3 -o /
|
|
112
|
+
"set -eu; curl -fsSL --retry 3 -o /tmp/gitlab-runner.deb " + deb + "; " +
|
|
113
|
+
"sha256sum /tmp/gitlab-runner.deb; " +
|
|
114
|
+
`echo '${GITLAB_RUNNER_DEB_SHA256} /tmp/gitlab-runner.deb' | sha256sum -c -; ` +
|
|
115
|
+
"dpkg-deb -x /tmp/gitlab-runner.deb /tmp/gitlab-runner-deb; " +
|
|
116
|
+
"mv /tmp/gitlab-runner-deb/usr/bin/gitlab-runner /usr/local/bin/gitlab-runner; rm -rf /tmp/gitlab-runner.deb /tmp/gitlab-runner-deb; " +
|
|
107
117
|
"sha256sum /usr/local/bin/gitlab-runner; " +
|
|
108
118
|
`echo '${GITLAB_RUNNER_SHA256} /usr/local/bin/gitlab-runner' | sha256sum -c -; ` +
|
|
109
119
|
"chmod 0755 /usr/local/bin/gitlab-runner; mkdir -p /etc/gitlab-runner; chmod 0700 /etc/gitlab-runner",
|
|
110
120
|
`set -eu; ${writeStep(GITLAB_LAUNCHER, GITLAB_LAUNCHER_SCRIPT, "0755")}`,
|
|
111
|
-
`set -eu; docker pull -q ${GITLAB_HELPER_IMAGE}; docker pull -q ${GITLAB_DEFAULT_JOB_IMAGE}
|
|
121
|
+
`set -eu; docker pull -q ${GITLAB_HELPER_IMAGE}; docker pull -q ${GITLAB_DEFAULT_JOB_IMAGE}`,
|
|
112
122
|
];
|
|
123
|
+
const platform_setup = ["set -eu; promigence-dockerd stop"];
|
|
113
124
|
const verify = `set -eu; gitlab-runner --version; git --version; test -x ${GITLAB_LAUNCHER}`;
|
|
114
|
-
return { image: RUNNER_BASE_IMAGE, setup, verify, tier, alias: runnerAlias(kind, tier) };
|
|
125
|
+
return { image: RUNNER_BASE_IMAGE, setup, platform_setup, verify, tier, alias: runnerAlias(kind, tier) };
|
|
115
126
|
}
|
|
116
127
|
export function isRunnerTier(t) {
|
|
117
128
|
return RUNNER_TIERS.includes(t);
|
package/dist/runners.js
CHANGED
|
@@ -9,7 +9,7 @@ import { TIERS } from "./generated.js";
|
|
|
9
9
|
import { COMPILED_NOTE, livePricing } from "./pricing.js";
|
|
10
10
|
import { emit, errorEnvelope, errorTextLines, okEnvelope, parseCount, parseDuration, parseUsd, usageError, write } from "./io.js";
|
|
11
11
|
import { escapeLine, jsonLine, sh } from "./term.js";
|
|
12
|
-
import { GITHUB_CONFIG_PATH, GITHUB_LAUNCHER, GITLAB_CONFIG_PATH, GITLAB_LAUNCHER, RUNNER_TIERS, isRunnerTier, runnerAlias, runnerImageSpec, } from "./runners-spec.js";
|
|
12
|
+
import { GITHUB_CONFIG_PATH, GITHUB_LAUNCHER, GITLAB_CONFIG_PATH, GITLAB_LAUNCHER, RUNNER_TIERS, isRunnerTier, runnerAlias, runnerBuildSteps, runnerImageSpec, } from "./runners-spec.js";
|
|
13
13
|
import * as gh from "./runners-github.js";
|
|
14
14
|
import * as gl from "./runners-gitlab.js";
|
|
15
15
|
export const TICK_MS = 2500;
|
|
@@ -915,7 +915,7 @@ async function ensureSnapshot(ctx, c, kind, tier, mid) {
|
|
|
915
915
|
if (!ok)
|
|
916
916
|
return undefined;
|
|
917
917
|
}
|
|
918
|
-
const b = await c.snapshots.create({ image: s.image, setup: s
|
|
918
|
+
const b = await c.snapshots.create({ image: s.image, setup: runnerBuildSteps(s), verify: { cmd: s.verify }, tier, alias: s.alias });
|
|
919
919
|
const charged = b.deduped || b.joined ? 0 : typeof b.build_price === "number" ? b.build_price : prices.build_usd;
|
|
920
920
|
await write(process.stderr, ctx.mode === "json"
|
|
921
921
|
? jsonLine(process.stderr, { hint: "build_price", build_price: charged, unit: "USD", tier, alias: s.alias, build_id: b.build_id })
|