paratix 0.7.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 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. Changed runs now also print the new image ID in parentheses in the CLI output.
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-JJRF37BP.js";
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-IUY5BJHA.js.map
495
+ //# sourceMappingURL=chunk-47PTUZZR.js.map
@@ -9,7 +9,7 @@ import {
9
9
  shellQuote,
10
10
  sshdPortMeta,
11
11
  validateMode
12
- } from "./chunk-JJRF37BP.js";
12
+ } from "./chunk-NRDLYHJL.js";
13
13
 
14
14
  // src/moduleFailure.ts
15
15
  function firstNonEmptyLine(text) {
@@ -3522,12 +3522,6 @@ function buildQuadletImagePullCommand(options) {
3522
3522
  const authFileFlag = options.authFile == null ? "" : ` --authfile ${shellQuoteForQuadlet(options.authFile)}`;
3523
3523
  return `podman pull${authFileFlag} ${shellQuoteForQuadlet(options.image)} 2>&1`;
3524
3524
  }
3525
- function buildQuadletImageInspectCommand(image) {
3526
- return `podman image inspect --format '{{.Id}}' ${shellQuoteForQuadlet(image)}`;
3527
- }
3528
- function formatQuadletImageIdDetail(imageId) {
3529
- return `(${imageId})`;
3530
- }
3531
3525
  function getQuadletContainerFilePath(name) {
3532
3526
  return `${CONTAINERS_SYSTEMD_DIRECTORY}/${name}.container`;
3533
3527
  }
@@ -3537,13 +3531,6 @@ function getQuadletContainerServiceName(options) {
3537
3531
  function quadletPullOutputIndicatesChange(output) {
3538
3532
  return QUADLET_PULL_CHANGED_OUTPUT_PATTERNS.some((pattern) => output.includes(pattern));
3539
3533
  }
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
3534
  var UNIT_NAME_PATTERN2 = new RegExp("^[\\w@.\\-]+$", "v");
3548
3535
  function validateQuadletName(name) {
3549
3536
  if (!UNIT_NAME_PATTERN2.test(name)) {
@@ -3555,6 +3542,62 @@ function shellQuoteForQuadlet(value) {
3555
3542
  return `'${value.replaceAll("'", escapedQuote)}'`;
3556
3543
  }
3557
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
+
3558
3601
  // src/modules/quadlet.ts
3559
3602
  var CONTAINERS_SYSTEMD_DIRECTORY_COMMAND = "mkdir -p '/etc/containers/systemd'";
3560
3603
  var QUADLET_FILE_MODE = "0644";
@@ -3612,10 +3655,13 @@ async function inspectQuadletImageId(parameters) {
3612
3655
  inspectResult
3613
3656
  );
3614
3657
  }
3615
- const imageId = readQuadletImageIdFromInspectOutput(inspectResult.stdout);
3658
+ const imageId = readQuadletImageIdentifierFromInspectOutput(
3659
+ parameters.image,
3660
+ inspectResult.stdout
3661
+ );
3616
3662
  if (imageId == null) {
3617
3663
  return failed(
3618
- `[quadlet.updateImage: ${parameters.name}] podman image inspect returned no image ID`
3664
+ `[quadlet.updateImage: ${parameters.name}] podman image inspect returned no digest or image ID`
3619
3665
  );
3620
3666
  }
3621
3667
  return imageId;
@@ -3628,7 +3674,7 @@ async function restartQuadletService(parameters) {
3628
3674
  silent: true
3629
3675
  }
3630
3676
  );
3631
- return restartResult.code === 0 ? { detail: formatQuadletImageIdDetail(parameters.imageId), status: "changed" } : failedCommand(
3677
+ return restartResult.code === 0 ? { detail: formatQuadletImageIdentifierDetail(parameters.imageId), status: "changed" } : failedCommand(
3632
3678
  `[quadlet.updateImage: ${parameters.name}] systemctl restart failed`,
3633
3679
  restartResult
3634
3680
  );
@@ -3699,6 +3745,7 @@ var quadlet = {
3699
3745
  async apply(ssh2) {
3700
3746
  if (!ssh2) return failed(`[quadlet.updateImage: ${options.name}] SSH connection is required`);
3701
3747
  return applyQuadletImageUpdate({
3748
+ image: options.image,
3702
3749
  inspectCommand,
3703
3750
  name: options.name,
3704
3751
  pullCommand,
@@ -5794,4 +5841,4 @@ export {
5794
5841
  ufw,
5795
5842
  user
5796
5843
  };
5797
- //# sourceMappingURL=chunk-D4CS2GCH.js.map
5844
+ //# sourceMappingURL=chunk-FFQ6FR4N.js.map