paratix 0.5.0 → 0.7.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 +2 -0
- package/dist/{chunk-LI47NIKN.js → chunk-D4CS2GCH.js} +264 -47
- package/dist/chunk-D4CS2GCH.js.map +1 -0
- package/dist/{chunk-3WK4QNJK.js → chunk-IUY5BJHA.js} +2 -2
- package/dist/{chunk-MHPFGCEY.js → chunk-JJRF37BP.js} +34 -1
- package/dist/chunk-JJRF37BP.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/modules/index.d.ts +2 -2
- package/dist/modules/index.js +2 -2
- package/dist/{types-BPzPHfax.d.ts → types-Cl2Muw1x.d.ts} +2 -0
- package/dist/{user-CraCJci7.d.ts → user-BJMqDePy.d.ts} +65 -14
- package/llm-guide.md +4 -3
- package/package.json +1 -1
- package/dist/chunk-LI47NIKN.js.map +0 -1
- package/dist/chunk-MHPFGCEY.js.map +0 -1
- /package/dist/{chunk-3WK4QNJK.js.map → chunk-IUY5BJHA.js.map} +0 -0
package/README.md
CHANGED
|
@@ -104,6 +104,8 @@ Signals are deferred side effects such as `service.reload(...)` or `service.rest
|
|
|
104
104
|
|
|
105
105
|
For Podman-native services, Paratix now also includes `quadlet.container(...)`. It writes a `.container` file under `/etc/containers/systemd`, reloads systemd when the content changes, and works cleanly with `service.enabled(...)` and `service.running(...)` for the generated service.
|
|
106
106
|
|
|
107
|
+
When you need a targeted image refresh outside the normal deploy flow, `quadlet.updateImage(...)` pulls exactly one image, reuses existing Podman registry auth on the host, optionally supports `authFile`, and only restarts the generated service when the pull actually downloaded a newer image. Changed runs now also print the new image ID in parentheses in the CLI output.
|
|
108
|
+
|
|
107
109
|
### Guards
|
|
108
110
|
|
|
109
111
|
Paratix also supports declarative host-state guards. Use `when.packageInstalled(...)`, `when.commandExists(...)`, `when.fileExists(...)`, `when.pathExists(...)`, `when.symlinkExists(...)`, or `when.socketExists(...)` and their inverted forms to gate modules or recipes on remote host state without shell-heavy playbooks.
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
shellQuote,
|
|
10
10
|
sshdPortMeta,
|
|
11
11
|
validateMode
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JJRF37BP.js";
|
|
13
13
|
|
|
14
14
|
// src/moduleFailure.ts
|
|
15
15
|
function firstNonEmptyLine(text) {
|
|
@@ -3360,57 +3360,157 @@ var pkg = {
|
|
|
3360
3360
|
}
|
|
3361
3361
|
};
|
|
3362
3362
|
|
|
3363
|
-
// src/modules/
|
|
3363
|
+
// src/modules/quadletHelpers.ts
|
|
3364
3364
|
var CONTAINERS_SYSTEMD_DIRECTORY = "/etc/containers/systemd";
|
|
3365
|
-
var
|
|
3366
|
-
|
|
3367
|
-
|
|
3365
|
+
var QUADLET_PULL_CHANGED_OUTPUT_PATTERNS = [
|
|
3366
|
+
"Copying blob",
|
|
3367
|
+
"Copying config",
|
|
3368
|
+
"Downloaded newer image",
|
|
3369
|
+
"Pulling fs layer",
|
|
3370
|
+
"Storing signatures",
|
|
3371
|
+
"Writing manifest"
|
|
3372
|
+
];
|
|
3368
3373
|
function sanitizeQuadletValue(value) {
|
|
3369
3374
|
return value.replaceAll(new RegExp("[\\n\\r]", "gv"), "");
|
|
3370
3375
|
}
|
|
3371
3376
|
function renderQuadletLine(key, value) {
|
|
3372
3377
|
return `${key}=${sanitizeQuadletValue(value)}`;
|
|
3373
3378
|
}
|
|
3374
|
-
function renderQuadletSection(name, lines) {
|
|
3375
|
-
return [`[${name}]`, ...lines, ""].join("\n");
|
|
3376
|
-
}
|
|
3377
|
-
function validateQuadletName(name) {
|
|
3378
|
-
if (!UNIT_NAME_PATTERN2.test(name)) {
|
|
3379
|
-
throw new Error(`quadlet.container: name must match ${String(UNIT_NAME_PATTERN2)}, got: ${name}`);
|
|
3380
|
-
}
|
|
3381
|
-
}
|
|
3382
|
-
function renderQuadletEnvironment(environment) {
|
|
3383
|
-
return Object.entries(environment).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => renderQuadletLine("Environment", `${key}=${value}`));
|
|
3384
|
-
}
|
|
3385
3379
|
function renderQuadletRepeated(key, values) {
|
|
3386
3380
|
return values.map((value) => renderQuadletLine(key, value));
|
|
3387
3381
|
}
|
|
3388
|
-
function
|
|
3382
|
+
function renderQuadletKeyValue(key, record) {
|
|
3383
|
+
return Object.entries(record).sort(([left], [right]) => left.localeCompare(right)).map(([k, v]) => renderQuadletLine(key, `${k}=${v}`));
|
|
3384
|
+
}
|
|
3385
|
+
function maybeRenderQuadletLine(key, value) {
|
|
3389
3386
|
if (value == null || value === "") return null;
|
|
3390
|
-
return renderQuadletLine(key,
|
|
3387
|
+
return renderQuadletLine(key, value);
|
|
3388
|
+
}
|
|
3389
|
+
function maybeRenderQuadletBool(key, value) {
|
|
3390
|
+
if (value == null) return null;
|
|
3391
|
+
return renderQuadletLine(key, String(value));
|
|
3392
|
+
}
|
|
3393
|
+
function maybeRenderQuadletNumber(key, value) {
|
|
3394
|
+
if (value == null) return null;
|
|
3395
|
+
return renderQuadletLine(key, String(value));
|
|
3391
3396
|
}
|
|
3392
3397
|
function compactQuadletLines(lines) {
|
|
3393
3398
|
return lines.filter((line) => line != null);
|
|
3394
3399
|
}
|
|
3395
|
-
function
|
|
3396
|
-
return
|
|
3397
|
-
renderQuadletLine("Description", options.description ?? `Podman container: ${options.name}`),
|
|
3398
|
-
"Wants=network-online.target",
|
|
3399
|
-
"After=network-online.target"
|
|
3400
|
-
]);
|
|
3400
|
+
function renderQuadletSection(name, lines) {
|
|
3401
|
+
return [`[${name}]`, ...lines, ""].join("\n");
|
|
3401
3402
|
}
|
|
3402
|
-
function
|
|
3403
|
-
return
|
|
3403
|
+
function buildQuadletIdentityLines(options) {
|
|
3404
|
+
return [
|
|
3404
3405
|
renderQuadletLine("Image", options.image),
|
|
3405
3406
|
maybeRenderQuadletLine("ContainerName", options.containerName),
|
|
3406
3407
|
maybeRenderQuadletLine("AutoUpdate", options.autoUpdate),
|
|
3408
|
+
maybeRenderQuadletLine("Pull", options.pull),
|
|
3409
|
+
maybeRenderQuadletLine("Entrypoint", options.entrypoint?.join(" ")),
|
|
3407
3410
|
maybeRenderQuadletLine("Exec", options.exec?.join(" ")),
|
|
3408
|
-
maybeRenderQuadletLine("
|
|
3411
|
+
maybeRenderQuadletLine("WorkingDir", options.workingDir),
|
|
3412
|
+
maybeRenderQuadletLine("User", options.user),
|
|
3413
|
+
maybeRenderQuadletLine("UserNS", options.userNs)
|
|
3414
|
+
];
|
|
3415
|
+
}
|
|
3416
|
+
function buildQuadletNetworkLines(options) {
|
|
3417
|
+
return [
|
|
3418
|
+
maybeRenderQuadletLine("HostName", options.hostName),
|
|
3409
3419
|
...renderQuadletRepeated("Network", options.networks ?? []),
|
|
3410
|
-
...renderQuadletRepeated("
|
|
3420
|
+
...renderQuadletRepeated("DNS", options.dns ?? []),
|
|
3421
|
+
...renderQuadletRepeated("DNSOption", options.dnsOption ?? []),
|
|
3422
|
+
...renderQuadletRepeated("DNSSearch", options.dnsSearch ?? []),
|
|
3423
|
+
maybeRenderQuadletLine("IP", options.ip),
|
|
3424
|
+
maybeRenderQuadletLine("IP6", options.ip6)
|
|
3425
|
+
];
|
|
3426
|
+
}
|
|
3427
|
+
function buildQuadletSecurityLines(options) {
|
|
3428
|
+
return [
|
|
3429
|
+
...renderQuadletRepeated("AddCapability", options.addCapability ?? []),
|
|
3430
|
+
...renderQuadletRepeated("DropCapability", options.dropCapability ?? []),
|
|
3431
|
+
maybeRenderQuadletBool("SecurityLabelDisable", options.securityLabelDisable),
|
|
3432
|
+
maybeRenderQuadletLine("SecurityLabelType", options.securityLabelType),
|
|
3433
|
+
maybeRenderQuadletLine("SeccompProfile", options.seccompProfile),
|
|
3434
|
+
maybeRenderQuadletBool("NoNewPrivileges", options.noNewPrivileges),
|
|
3435
|
+
maybeRenderQuadletBool("ReadOnly", options.readOnly)
|
|
3436
|
+
];
|
|
3437
|
+
}
|
|
3438
|
+
function buildQuadletRuntimeLines(options) {
|
|
3439
|
+
return [
|
|
3440
|
+
maybeRenderQuadletBool("Notify", options.notify),
|
|
3441
|
+
maybeRenderQuadletBool("RunInit", options.runInit),
|
|
3442
|
+
maybeRenderQuadletLine("LogDriver", options.logDriver),
|
|
3443
|
+
maybeRenderQuadletLine("Timezone", options.timezone),
|
|
3444
|
+
maybeRenderQuadletNumber("StopTimeout", options.stopTimeout)
|
|
3445
|
+
];
|
|
3446
|
+
}
|
|
3447
|
+
function buildQuadletStorageLines(options) {
|
|
3448
|
+
return [
|
|
3411
3449
|
...renderQuadletRepeated("PublishPort", options.publishPorts ?? []),
|
|
3450
|
+
...renderQuadletRepeated("ExposeHostPort", options.exposeHostPort ?? []),
|
|
3412
3451
|
...renderQuadletRepeated("Volume", options.volumes ?? []),
|
|
3413
|
-
...
|
|
3452
|
+
...renderQuadletRepeated("Mount", options.mount ?? []),
|
|
3453
|
+
...renderQuadletRepeated("Tmpfs", options.tmpfs ?? []),
|
|
3454
|
+
...renderQuadletRepeated("AddDevice", options.addDevice ?? [])
|
|
3455
|
+
];
|
|
3456
|
+
}
|
|
3457
|
+
function buildQuadletMetadataLines(options) {
|
|
3458
|
+
return [
|
|
3459
|
+
...renderQuadletRepeated("Secret", options.secret ?? []),
|
|
3460
|
+
...renderQuadletEnvironment(options.environment ?? {}),
|
|
3461
|
+
...renderQuadletRepeated("EnvironmentFile", options.environmentFiles ?? []),
|
|
3462
|
+
...renderQuadletKeyValue("Label", options.label ?? {}),
|
|
3463
|
+
...renderQuadletKeyValue("Annotation", options.annotation ?? {})
|
|
3464
|
+
];
|
|
3465
|
+
}
|
|
3466
|
+
function renderQuadletEnvironment(environment) {
|
|
3467
|
+
return Object.entries(environment).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => renderQuadletLine("Environment", `${key}=${value}`));
|
|
3468
|
+
}
|
|
3469
|
+
function buildQuadletTuningLines(options) {
|
|
3470
|
+
return [
|
|
3471
|
+
...renderQuadletKeyValue("Sysctl", options.sysctl ?? {}),
|
|
3472
|
+
...renderQuadletRepeated("Ulimit", options.ulimit ?? []),
|
|
3473
|
+
...renderQuadletRepeated("GroupAdd", options.groupAdd ?? []),
|
|
3474
|
+
...renderQuadletRepeated("Mask", options.mask ?? []),
|
|
3475
|
+
...renderQuadletRepeated("Unmask", options.unmask ?? [])
|
|
3476
|
+
];
|
|
3477
|
+
}
|
|
3478
|
+
function buildQuadletHealthcheckLines(options) {
|
|
3479
|
+
if (options.healthCmd == null) return [];
|
|
3480
|
+
return compactQuadletLines([
|
|
3481
|
+
renderQuadletLine("HealthCmd", options.healthCmd),
|
|
3482
|
+
maybeRenderQuadletLine("HealthInterval", options.healthInterval),
|
|
3483
|
+
maybeRenderQuadletLine("HealthTimeout", options.healthTimeout),
|
|
3484
|
+
maybeRenderQuadletNumber("HealthRetries", options.healthRetries),
|
|
3485
|
+
maybeRenderQuadletLine("HealthStartPeriod", options.healthStartPeriod),
|
|
3486
|
+
maybeRenderQuadletLine("HealthOnFailure", options.healthOnFailure)
|
|
3487
|
+
]);
|
|
3488
|
+
}
|
|
3489
|
+
function buildQuadletContainerLines(options) {
|
|
3490
|
+
return compactQuadletLines([
|
|
3491
|
+
...buildQuadletIdentityLines(options),
|
|
3492
|
+
...buildQuadletNetworkLines(options),
|
|
3493
|
+
...buildQuadletSecurityLines(options),
|
|
3494
|
+
...buildQuadletRuntimeLines(options),
|
|
3495
|
+
...renderQuadletRepeated("PodmanArgs", options.podmanArgs ?? []),
|
|
3496
|
+
...buildQuadletStorageLines(options),
|
|
3497
|
+
...buildQuadletMetadataLines(options),
|
|
3498
|
+
...buildQuadletTuningLines(options),
|
|
3499
|
+
...buildQuadletHealthcheckLines(options)
|
|
3500
|
+
]);
|
|
3501
|
+
}
|
|
3502
|
+
function buildQuadletServiceLines(options) {
|
|
3503
|
+
return compactQuadletLines([
|
|
3504
|
+
maybeRenderQuadletLine("Restart", options.restart),
|
|
3505
|
+
maybeRenderQuadletNumber("TimeoutStartSec", options.timeoutStartSec),
|
|
3506
|
+
maybeRenderQuadletNumber("TimeoutStopSec", options.timeoutStopSec)
|
|
3507
|
+
]);
|
|
3508
|
+
}
|
|
3509
|
+
function buildQuadletUnitSection(options) {
|
|
3510
|
+
return renderQuadletSection("Unit", [
|
|
3511
|
+
renderQuadletLine("Description", options.description ?? `Podman container: ${options.name}`),
|
|
3512
|
+
"Wants=network-online.target",
|
|
3513
|
+
"After=network-online.target"
|
|
3414
3514
|
]);
|
|
3415
3515
|
}
|
|
3416
3516
|
function buildQuadletInstallSection(options) {
|
|
@@ -3418,15 +3518,59 @@ function buildQuadletInstallSection(options) {
|
|
|
3418
3518
|
renderQuadletLine("WantedBy", options.wantedBy ?? "multi-user.target")
|
|
3419
3519
|
]);
|
|
3420
3520
|
}
|
|
3521
|
+
function buildQuadletImagePullCommand(options) {
|
|
3522
|
+
const authFileFlag = options.authFile == null ? "" : ` --authfile ${shellQuoteForQuadlet(options.authFile)}`;
|
|
3523
|
+
return `podman pull${authFileFlag} ${shellQuoteForQuadlet(options.image)} 2>&1`;
|
|
3524
|
+
}
|
|
3525
|
+
function buildQuadletImageInspectCommand(image) {
|
|
3526
|
+
return `podman image inspect --format '{{.Id}}' ${shellQuoteForQuadlet(image)}`;
|
|
3527
|
+
}
|
|
3528
|
+
function formatQuadletImageIdDetail(imageId) {
|
|
3529
|
+
return `(${imageId})`;
|
|
3530
|
+
}
|
|
3531
|
+
function getQuadletContainerFilePath(name) {
|
|
3532
|
+
return `${CONTAINERS_SYSTEMD_DIRECTORY}/${name}.container`;
|
|
3533
|
+
}
|
|
3534
|
+
function getQuadletContainerServiceName(options) {
|
|
3535
|
+
return options.serviceName ?? options.name;
|
|
3536
|
+
}
|
|
3537
|
+
function quadletPullOutputIndicatesChange(output) {
|
|
3538
|
+
return QUADLET_PULL_CHANGED_OUTPUT_PATTERNS.some((pattern) => output.includes(pattern));
|
|
3539
|
+
}
|
|
3540
|
+
function readQuadletImageIdFromInspectOutput(output) {
|
|
3541
|
+
for (const line of output.split("\n")) {
|
|
3542
|
+
const trimmed = line.trim();
|
|
3543
|
+
if (trimmed.length > 0) return trimmed;
|
|
3544
|
+
}
|
|
3545
|
+
return null;
|
|
3546
|
+
}
|
|
3547
|
+
var UNIT_NAME_PATTERN2 = new RegExp("^[\\w@.\\-]+$", "v");
|
|
3548
|
+
function validateQuadletName(name) {
|
|
3549
|
+
if (!UNIT_NAME_PATTERN2.test(name)) {
|
|
3550
|
+
throw new Error(`quadlet: name must match ${String(UNIT_NAME_PATTERN2)}, got: ${name}`);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
function shellQuoteForQuadlet(value) {
|
|
3554
|
+
const escapedQuote = "'\\''";
|
|
3555
|
+
return `'${value.replaceAll("'", escapedQuote)}'`;
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
// src/modules/quadlet.ts
|
|
3559
|
+
var CONTAINERS_SYSTEMD_DIRECTORY_COMMAND = "mkdir -p '/etc/containers/systemd'";
|
|
3560
|
+
var QUADLET_FILE_MODE = "0644";
|
|
3561
|
+
var SYSTEMCTL = "systemctl";
|
|
3421
3562
|
function generateContainerQuadlet(options) {
|
|
3422
|
-
|
|
3563
|
+
const serviceLines = buildQuadletServiceLines(options);
|
|
3564
|
+
const sections = [
|
|
3423
3565
|
buildQuadletUnitSection(options),
|
|
3424
3566
|
renderQuadletSection("Container", buildQuadletContainerLines(options)),
|
|
3567
|
+
...serviceLines.length > 0 ? [renderQuadletSection("Service", serviceLines)] : [],
|
|
3425
3568
|
buildQuadletInstallSection(options)
|
|
3426
|
-
]
|
|
3569
|
+
];
|
|
3570
|
+
return sections.join("\n").trimEnd();
|
|
3427
3571
|
}
|
|
3428
3572
|
async function createQuadletDirectory(ssh2) {
|
|
3429
|
-
return ssh2.exec(
|
|
3573
|
+
return ssh2.exec(CONTAINERS_SYSTEMD_DIRECTORY_COMMAND, {
|
|
3430
3574
|
ignoreExitCode: true,
|
|
3431
3575
|
silent: true
|
|
3432
3576
|
});
|
|
@@ -3457,6 +3601,58 @@ async function checkQuadletFile(parameters) {
|
|
|
3457
3601
|
const remoteContent = await parameters.ssh.readFile(parameters.filePath);
|
|
3458
3602
|
return remoteContent.trim() === parameters.content.trim() ? "ok" : NEEDS_APPLY;
|
|
3459
3603
|
}
|
|
3604
|
+
async function inspectQuadletImageId(parameters) {
|
|
3605
|
+
const inspectResult = await parameters.ssh.exec(parameters.inspectCommand, {
|
|
3606
|
+
ignoreExitCode: true,
|
|
3607
|
+
silent: true
|
|
3608
|
+
});
|
|
3609
|
+
if (inspectResult.code !== 0) {
|
|
3610
|
+
return failedCommand(
|
|
3611
|
+
`[quadlet.updateImage: ${parameters.name}] podman image inspect failed`,
|
|
3612
|
+
inspectResult
|
|
3613
|
+
);
|
|
3614
|
+
}
|
|
3615
|
+
const imageId = readQuadletImageIdFromInspectOutput(inspectResult.stdout);
|
|
3616
|
+
if (imageId == null) {
|
|
3617
|
+
return failed(
|
|
3618
|
+
`[quadlet.updateImage: ${parameters.name}] podman image inspect returned no image ID`
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
3621
|
+
return imageId;
|
|
3622
|
+
}
|
|
3623
|
+
async function restartQuadletService(parameters) {
|
|
3624
|
+
const restartResult = await parameters.ssh.exec(
|
|
3625
|
+
`${SYSTEMCTL} restart ${shellQuote(parameters.serviceName)}`,
|
|
3626
|
+
{
|
|
3627
|
+
ignoreExitCode: true,
|
|
3628
|
+
silent: true
|
|
3629
|
+
}
|
|
3630
|
+
);
|
|
3631
|
+
return restartResult.code === 0 ? { detail: formatQuadletImageIdDetail(parameters.imageId), status: "changed" } : failedCommand(
|
|
3632
|
+
`[quadlet.updateImage: ${parameters.name}] systemctl restart failed`,
|
|
3633
|
+
restartResult
|
|
3634
|
+
);
|
|
3635
|
+
}
|
|
3636
|
+
async function applyQuadletImageUpdate(parameters) {
|
|
3637
|
+
const pullResult = await parameters.ssh.exec(parameters.pullCommand, {
|
|
3638
|
+
ignoreExitCode: true,
|
|
3639
|
+
silent: true
|
|
3640
|
+
});
|
|
3641
|
+
if (pullResult.code !== 0) {
|
|
3642
|
+
return failedCommand(`[quadlet.updateImage: ${parameters.name}] podman pull failed`, pullResult);
|
|
3643
|
+
}
|
|
3644
|
+
if (!quadletPullOutputIndicatesChange(pullResult.stdout)) {
|
|
3645
|
+
return { status: "ok" };
|
|
3646
|
+
}
|
|
3647
|
+
const imageId = await inspectQuadletImageId(parameters);
|
|
3648
|
+
if (typeof imageId !== "string") return imageId;
|
|
3649
|
+
return restartQuadletService({
|
|
3650
|
+
imageId,
|
|
3651
|
+
name: parameters.name,
|
|
3652
|
+
serviceName: parameters.serviceName,
|
|
3653
|
+
ssh: parameters.ssh
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3460
3656
|
var quadlet = {
|
|
3461
3657
|
/**
|
|
3462
3658
|
* Write a Podman Quadlet `.container` definition and reload systemd when it changes.
|
|
@@ -3465,24 +3661,11 @@ var quadlet = {
|
|
|
3465
3661
|
* and `service.running(name)`.
|
|
3466
3662
|
*
|
|
3467
3663
|
* @param options - Configuration for the Quadlet container definition.
|
|
3468
|
-
* @param options.name - Quadlet base name without `.container`.
|
|
3469
|
-
* @param options.image - Container image reference.
|
|
3470
|
-
* @param options.description - Optional systemd unit description.
|
|
3471
|
-
* @param options.containerName - Optional explicit Podman container name.
|
|
3472
|
-
* @param options.autoUpdate - Optional Podman auto-update policy.
|
|
3473
|
-
* @param options.environment - Optional environment variables.
|
|
3474
|
-
* @param options.exec - Optional command and arguments for `Exec=`.
|
|
3475
|
-
* @param options.networks - Optional `Network=` entries.
|
|
3476
|
-
* @param options.podmanArgs - Optional `PodmanArgs=` entries.
|
|
3477
|
-
* @param options.publishPorts - Optional `PublishPort=` entries.
|
|
3478
|
-
* @param options.restart - Optional `Restart=` policy in the `[Container]` section.
|
|
3479
|
-
* @param options.volumes - Optional `Volume=` entries.
|
|
3480
|
-
* @param options.wantedBy - Optional install target. Defaults to `multi-user.target`.
|
|
3481
3664
|
* @returns A Module that ensures the Quadlet file is present and up to date.
|
|
3482
3665
|
*/
|
|
3483
3666
|
container(options) {
|
|
3484
3667
|
validateQuadletName(options.name);
|
|
3485
|
-
const filePath =
|
|
3668
|
+
const filePath = getQuadletContainerFilePath(options.name);
|
|
3486
3669
|
const content = generateContainerQuadlet(options);
|
|
3487
3670
|
return {
|
|
3488
3671
|
async apply(ssh2) {
|
|
@@ -3495,6 +3678,40 @@ var quadlet = {
|
|
|
3495
3678
|
},
|
|
3496
3679
|
name: `quadlet.container: ${options.name}`
|
|
3497
3680
|
};
|
|
3681
|
+
},
|
|
3682
|
+
/**
|
|
3683
|
+
* Pull the latest image for a Quadlet-managed container and restart the service
|
|
3684
|
+
* only when the image changed.
|
|
3685
|
+
*
|
|
3686
|
+
* Accepts the same `name` and `image` fields as `quadlet.container(...)`, so a
|
|
3687
|
+
* shared config object can drive both deployment and targeted image refreshes.
|
|
3688
|
+
*
|
|
3689
|
+
* @param options - Image pull and restart configuration for the Quadlet service.
|
|
3690
|
+
* @returns A Module that updates the image and conditionally restarts the service.
|
|
3691
|
+
*/
|
|
3692
|
+
updateImage(options) {
|
|
3693
|
+
validateQuadletName(options.name);
|
|
3694
|
+
if (options.serviceName != null) validateQuadletName(options.serviceName);
|
|
3695
|
+
const pullCommand = buildQuadletImagePullCommand(options);
|
|
3696
|
+
const inspectCommand = buildQuadletImageInspectCommand(options.image);
|
|
3697
|
+
const serviceName = getQuadletContainerServiceName(options);
|
|
3698
|
+
return {
|
|
3699
|
+
async apply(ssh2) {
|
|
3700
|
+
if (!ssh2) return failed(`[quadlet.updateImage: ${options.name}] SSH connection is required`);
|
|
3701
|
+
return applyQuadletImageUpdate({
|
|
3702
|
+
inspectCommand,
|
|
3703
|
+
name: options.name,
|
|
3704
|
+
pullCommand,
|
|
3705
|
+
serviceName,
|
|
3706
|
+
ssh: ssh2
|
|
3707
|
+
});
|
|
3708
|
+
},
|
|
3709
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- Signal-style module
|
|
3710
|
+
async check() {
|
|
3711
|
+
return NEEDS_APPLY;
|
|
3712
|
+
},
|
|
3713
|
+
name: `quadlet.updateImage: ${options.name}`
|
|
3714
|
+
};
|
|
3498
3715
|
}
|
|
3499
3716
|
};
|
|
3500
3717
|
|
|
@@ -5577,4 +5794,4 @@ export {
|
|
|
5577
5794
|
ufw,
|
|
5578
5795
|
user
|
|
5579
5796
|
};
|
|
5580
|
-
//# sourceMappingURL=chunk-
|
|
5797
|
+
//# sourceMappingURL=chunk-D4CS2GCH.js.map
|