paratix 0.6.0 → 0.8.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 +1 -1
- package/dist/{chunk-IUY5BJHA.js → chunk-47PTUZZR.js} +2 -2
- package/dist/{chunk-ENWMSERJ.js → chunk-FFQ6FR4N.js} +121 -22
- package/dist/chunk-FFQ6FR4N.js.map +1 -0
- package/dist/{chunk-JJRF37BP.js → chunk-NRDLYHJL.js} +5 -2
- package/dist/chunk-NRDLYHJL.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +9 -6
- 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-CiAMlpWO.d.ts → user-BJMqDePy.d.ts} +1 -1
- package/llm-guide.md +1 -1
- package/package.json +1 -1
- package/dist/chunk-ENWMSERJ.js.map +0 -1
- package/dist/chunk-JJRF37BP.js.map +0 -1
- /package/dist/{chunk-IUY5BJHA.js.map → chunk-47PTUZZR.js.map} +0 -0
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ 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.
|
|
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 registry digest in parentheses in the CLI output, with a local image ID fallback when no repo digest is available.
|
|
108
108
|
|
|
109
109
|
### Guards
|
|
110
110
|
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CommandError,
|
|
3
3
|
assertValidModuleMetaEntries,
|
|
4
4
|
mergeEnvironmentFromMeta
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NRDLYHJL.js";
|
|
6
6
|
|
|
7
7
|
// src/output.ts
|
|
8
8
|
import pc from "picocolors";
|
|
@@ -492,4 +492,4 @@ export {
|
|
|
492
492
|
printSummary,
|
|
493
493
|
runSignalModules
|
|
494
494
|
};
|
|
495
|
-
//# sourceMappingURL=chunk-
|
|
495
|
+
//# sourceMappingURL=chunk-47PTUZZR.js.map
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
shellQuote,
|
|
10
10
|
sshdPortMeta,
|
|
11
11
|
validateMode
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NRDLYHJL.js";
|
|
13
13
|
|
|
14
14
|
// src/moduleFailure.ts
|
|
15
15
|
function firstNonEmptyLine(text) {
|
|
@@ -3542,6 +3542,62 @@ function shellQuoteForQuadlet(value) {
|
|
|
3542
3542
|
return `'${value.replaceAll("'", escapedQuote)}'`;
|
|
3543
3543
|
}
|
|
3544
3544
|
|
|
3545
|
+
// src/modules/quadletImageInspectHelpers.ts
|
|
3546
|
+
function shellQuoteForQuadletImageInspect(value) {
|
|
3547
|
+
const escapedQuote = "'\\''";
|
|
3548
|
+
return `'${value.replaceAll("'", escapedQuote)}'`;
|
|
3549
|
+
}
|
|
3550
|
+
function buildQuadletImageInspectCommand(image) {
|
|
3551
|
+
return `podman image inspect ${shellQuoteForQuadletImageInspect(image)}`;
|
|
3552
|
+
}
|
|
3553
|
+
function formatQuadletImageIdentifierDetail(imageIdentifier) {
|
|
3554
|
+
return `(${imageIdentifier})`;
|
|
3555
|
+
}
|
|
3556
|
+
function readQuadletImageIdentifierFromInspectOutput(image, output) {
|
|
3557
|
+
const parsed = parseQuadletImageInspectOutput(output);
|
|
3558
|
+
if (parsed == null) return null;
|
|
3559
|
+
const repoDigest = findQuadletRepoDigest(image, parsed.repoDigests);
|
|
3560
|
+
return repoDigest ?? parsed.id;
|
|
3561
|
+
}
|
|
3562
|
+
function findQuadletRepoDigest(image, repoDigests) {
|
|
3563
|
+
const repository = getQuadletImageRepository(image);
|
|
3564
|
+
for (const repoDigest of repoDigests) {
|
|
3565
|
+
const separatorIndex = repoDigest.indexOf("@");
|
|
3566
|
+
if (separatorIndex === -1) continue;
|
|
3567
|
+
const repo = repoDigest.slice(0, separatorIndex);
|
|
3568
|
+
const digest = repoDigest.slice(separatorIndex + 1);
|
|
3569
|
+
if (repo === repository && digest.length > 0) return digest;
|
|
3570
|
+
}
|
|
3571
|
+
return null;
|
|
3572
|
+
}
|
|
3573
|
+
function getQuadletImageRepository(image) {
|
|
3574
|
+
const digestSeparatorIndex = image.indexOf("@");
|
|
3575
|
+
if (digestSeparatorIndex !== -1) return image.slice(0, digestSeparatorIndex);
|
|
3576
|
+
const lastSlashIndex = image.lastIndexOf("/");
|
|
3577
|
+
const lastColonIndex = image.lastIndexOf(":");
|
|
3578
|
+
return lastColonIndex > lastSlashIndex ? image.slice(0, lastColonIndex) : image;
|
|
3579
|
+
}
|
|
3580
|
+
function parseQuadletImageInspectOutput(output) {
|
|
3581
|
+
try {
|
|
3582
|
+
const parsed = JSON.parse(output);
|
|
3583
|
+
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
3584
|
+
const [firstEntry] = parsed;
|
|
3585
|
+
if (!isQuadletInspectEntry(firstEntry)) return null;
|
|
3586
|
+
const repoDigests = Array.isArray(firstEntry.RepoDigests) ? firstEntry.RepoDigests : [];
|
|
3587
|
+
return {
|
|
3588
|
+
id: typeof firstEntry.Id === "string" && firstEntry.Id.length > 0 ? firstEntry.Id : null,
|
|
3589
|
+
repoDigests: repoDigests.filter(
|
|
3590
|
+
(entry) => typeof entry === "string" && entry.length > 0
|
|
3591
|
+
)
|
|
3592
|
+
};
|
|
3593
|
+
} catch {
|
|
3594
|
+
return null;
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
function isQuadletInspectEntry(value) {
|
|
3598
|
+
return value != null && typeof value === "object";
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3545
3601
|
// src/modules/quadlet.ts
|
|
3546
3602
|
var CONTAINERS_SYSTEMD_DIRECTORY_COMMAND = "mkdir -p '/etc/containers/systemd'";
|
|
3547
3603
|
var QUADLET_FILE_MODE = "0644";
|
|
@@ -3588,6 +3644,61 @@ async function checkQuadletFile(parameters) {
|
|
|
3588
3644
|
const remoteContent = await parameters.ssh.readFile(parameters.filePath);
|
|
3589
3645
|
return remoteContent.trim() === parameters.content.trim() ? "ok" : NEEDS_APPLY;
|
|
3590
3646
|
}
|
|
3647
|
+
async function inspectQuadletImageId(parameters) {
|
|
3648
|
+
const inspectResult = await parameters.ssh.exec(parameters.inspectCommand, {
|
|
3649
|
+
ignoreExitCode: true,
|
|
3650
|
+
silent: true
|
|
3651
|
+
});
|
|
3652
|
+
if (inspectResult.code !== 0) {
|
|
3653
|
+
return failedCommand(
|
|
3654
|
+
`[quadlet.updateImage: ${parameters.name}] podman image inspect failed`,
|
|
3655
|
+
inspectResult
|
|
3656
|
+
);
|
|
3657
|
+
}
|
|
3658
|
+
const imageId = readQuadletImageIdentifierFromInspectOutput(
|
|
3659
|
+
parameters.image,
|
|
3660
|
+
inspectResult.stdout
|
|
3661
|
+
);
|
|
3662
|
+
if (imageId == null) {
|
|
3663
|
+
return failed(
|
|
3664
|
+
`[quadlet.updateImage: ${parameters.name}] podman image inspect returned no digest or image ID`
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
return imageId;
|
|
3668
|
+
}
|
|
3669
|
+
async function restartQuadletService(parameters) {
|
|
3670
|
+
const restartResult = await parameters.ssh.exec(
|
|
3671
|
+
`${SYSTEMCTL} restart ${shellQuote(parameters.serviceName)}`,
|
|
3672
|
+
{
|
|
3673
|
+
ignoreExitCode: true,
|
|
3674
|
+
silent: true
|
|
3675
|
+
}
|
|
3676
|
+
);
|
|
3677
|
+
return restartResult.code === 0 ? { detail: formatQuadletImageIdentifierDetail(parameters.imageId), status: "changed" } : failedCommand(
|
|
3678
|
+
`[quadlet.updateImage: ${parameters.name}] systemctl restart failed`,
|
|
3679
|
+
restartResult
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
async function applyQuadletImageUpdate(parameters) {
|
|
3683
|
+
const pullResult = await parameters.ssh.exec(parameters.pullCommand, {
|
|
3684
|
+
ignoreExitCode: true,
|
|
3685
|
+
silent: true
|
|
3686
|
+
});
|
|
3687
|
+
if (pullResult.code !== 0) {
|
|
3688
|
+
return failedCommand(`[quadlet.updateImage: ${parameters.name}] podman pull failed`, pullResult);
|
|
3689
|
+
}
|
|
3690
|
+
if (!quadletPullOutputIndicatesChange(pullResult.stdout)) {
|
|
3691
|
+
return { status: "ok" };
|
|
3692
|
+
}
|
|
3693
|
+
const imageId = await inspectQuadletImageId(parameters);
|
|
3694
|
+
if (typeof imageId !== "string") return imageId;
|
|
3695
|
+
return restartQuadletService({
|
|
3696
|
+
imageId,
|
|
3697
|
+
name: parameters.name,
|
|
3698
|
+
serviceName: parameters.serviceName,
|
|
3699
|
+
ssh: parameters.ssh
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3591
3702
|
var quadlet = {
|
|
3592
3703
|
/**
|
|
3593
3704
|
* Write a Podman Quadlet `.container` definition and reload systemd when it changes.
|
|
@@ -3628,31 +3739,19 @@ var quadlet = {
|
|
|
3628
3739
|
validateQuadletName(options.name);
|
|
3629
3740
|
if (options.serviceName != null) validateQuadletName(options.serviceName);
|
|
3630
3741
|
const pullCommand = buildQuadletImagePullCommand(options);
|
|
3742
|
+
const inspectCommand = buildQuadletImageInspectCommand(options.image);
|
|
3631
3743
|
const serviceName = getQuadletContainerServiceName(options);
|
|
3632
3744
|
return {
|
|
3633
3745
|
async apply(ssh2) {
|
|
3634
3746
|
if (!ssh2) return failed(`[quadlet.updateImage: ${options.name}] SSH connection is required`);
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
pullResult
|
|
3643
|
-
);
|
|
3644
|
-
}
|
|
3645
|
-
if (!quadletPullOutputIndicatesChange(pullResult.stdout)) {
|
|
3646
|
-
return { status: "ok" };
|
|
3647
|
-
}
|
|
3648
|
-
const restartResult = await ssh2.exec(`${SYSTEMCTL} restart ${shellQuote(serviceName)}`, {
|
|
3649
|
-
ignoreExitCode: true,
|
|
3650
|
-
silent: true
|
|
3747
|
+
return applyQuadletImageUpdate({
|
|
3748
|
+
image: options.image,
|
|
3749
|
+
inspectCommand,
|
|
3750
|
+
name: options.name,
|
|
3751
|
+
pullCommand,
|
|
3752
|
+
serviceName,
|
|
3753
|
+
ssh: ssh2
|
|
3651
3754
|
});
|
|
3652
|
-
return restartResult.code === 0 ? { status: "changed" } : failedCommand(
|
|
3653
|
-
`[quadlet.updateImage: ${options.name}] systemctl restart failed`,
|
|
3654
|
-
restartResult
|
|
3655
|
-
);
|
|
3656
3755
|
},
|
|
3657
3756
|
// eslint-disable-next-line @typescript-eslint/require-await -- Signal-style module
|
|
3658
3757
|
async check() {
|
|
@@ -5742,4 +5841,4 @@ export {
|
|
|
5742
5841
|
ufw,
|
|
5743
5842
|
user
|
|
5744
5843
|
};
|
|
5745
|
-
//# sourceMappingURL=chunk-
|
|
5844
|
+
//# sourceMappingURL=chunk-FFQ6FR4N.js.map
|