@atolis-hq/wake 0.2.12 → 0.2.13

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.
@@ -57,6 +57,9 @@ function buildRunArgs(input) {
57
57
  }
58
58
  export function createDockerCli(deps) {
59
59
  return {
60
+ async inspectContainerImage(containerName) {
61
+ return (await deps.inspectContainerImage?.(containerName)) ?? null;
62
+ },
60
63
  async build(input) {
61
64
  const buildArgFlags = Object.entries(input.buildArgs ?? {}).flatMap(([key, value]) => [
62
65
  '--build-arg',
@@ -12,6 +12,13 @@ const START_PROCESS_CHECK = [
12
12
  'tr "\\0" " " < "/proc/$pid/cmdline" | grep -F "node /app/dist/src/main.js start --wake-root /wake" >/dev/null',
13
13
  ].join(' && '),
14
14
  ];
15
+ function tagFromImage(imageRepository, image) {
16
+ if (image === null) {
17
+ return null;
18
+ }
19
+ const prefix = `${imageRepository}:`;
20
+ return image.startsWith(prefix) ? image.slice(prefix.length) : null;
21
+ }
15
22
  function readFlag(name, args) {
16
23
  const index = args.indexOf(name);
17
24
  return index === -1 ? undefined : args[index + 1];
@@ -69,6 +76,13 @@ export async function runSelfUpdateCommand(input) {
69
76
  logger: input.logger,
70
77
  });
71
78
  const newImage = `${input.imageRepository}:${tag}`;
79
+ const previousImage = (await input.docker.inspectContainerImage?.(input.containerName)) ?? null;
80
+ const previousImageTag = tagFromImage(input.imageRepository, previousImage);
81
+ const rollbackImage = previousImage ??
82
+ (ledger.lastKnownGoodTag !== null
83
+ ? `${input.imageRepository}:${ledger.lastKnownGoodTag}`
84
+ : null);
85
+ const rollbackTag = previousImageTag ?? ledger.lastKnownGoodTag;
72
86
  const updateInput = {
73
87
  containerName: input.containerName,
74
88
  wakeRoot: input.wakeRoot,
@@ -106,9 +120,10 @@ export async function runSelfUpdateCommand(input) {
106
120
  catch (error) {
107
121
  const reason = error instanceof Error ? error.message : String(error);
108
122
  input.logger.error(`[self-update] rollout of ${tag} failed: ${reason}`);
109
- if (ledger.lastKnownGoodTag !== null) {
110
- const rollbackImage = `${input.imageRepository}:${ledger.lastKnownGoodTag}`;
111
- await input.git.checkoutTag(ledger.lastKnownGoodTag);
123
+ if (rollbackImage !== null) {
124
+ if (rollbackTag !== null) {
125
+ await input.git.checkoutTag(rollbackTag);
126
+ }
112
127
  await input.docker.update({ ...updateInput, image: rollbackImage });
113
128
  input.logger.info('[self-update] recreated rollback container; entrypoint will keep wake start running');
114
129
  await verifyResidentStart({
@@ -119,21 +134,21 @@ export async function runSelfUpdateCommand(input) {
119
134
  sleep: input.sleep,
120
135
  context: 'rollback',
121
136
  });
122
- input.logger.info(`[self-update] rolled back to ${ledger.lastKnownGoodTag}`);
137
+ input.logger.info(`[self-update] rolled back to ${rollbackTag ?? rollbackImage}`);
123
138
  }
124
139
  else {
125
140
  input.logger.error('[self-update] no previous known-good tag to roll back to');
126
141
  }
127
142
  await input.writeLedger({
128
- lastAppliedTag: ledger.lastKnownGoodTag,
129
- lastKnownGoodTag: ledger.lastKnownGoodTag,
143
+ lastAppliedTag: rollbackTag,
144
+ lastKnownGoodTag: rollbackTag,
130
145
  badTags: [...ledger.badTags, { tag, reason, recordedAt: new Date().toISOString() }],
131
146
  });
132
147
  try {
133
148
  await input.issueReporter.createIssue({
134
149
  title: `Self-update to ${tag} failed and was rolled back`,
135
150
  body: [
136
- `Automated update to \`${tag}\` failed during rollout and was rolled back to \`${ledger.lastKnownGoodTag ?? 'unknown'}\`.`,
151
+ `Automated update to \`${tag}\` failed during rollout and was rolled back to \`${rollbackTag ?? rollbackImage ?? 'unknown'}\`.`,
137
152
  '',
138
153
  '```',
139
154
  reason,
package/dist/src/main.js CHANGED
@@ -346,6 +346,28 @@ async function inspectDockerContainer(containerName) {
346
346
  });
347
347
  });
348
348
  }
349
+ async function inspectDockerContainerImage(containerName) {
350
+ return await new Promise((resolveInspect, reject) => {
351
+ const child = spawn('docker', ['container', 'inspect', '-f', '{{.Config.Image}}', containerName], {
352
+ cwd: process.cwd(),
353
+ env: process.env,
354
+ stdio: ['ignore', 'pipe', 'ignore'],
355
+ });
356
+ let stdout = '';
357
+ child.stdout.on('data', (chunk) => {
358
+ stdout += chunk.toString();
359
+ });
360
+ child.on('error', reject);
361
+ child.on('close', (exitCode) => {
362
+ if (exitCode !== 0) {
363
+ resolveInspect(null);
364
+ return;
365
+ }
366
+ const image = stdout.trim();
367
+ resolveInspect(image.length > 0 ? image : null);
368
+ });
369
+ });
370
+ }
349
371
  async function dockerDaemonReachable() {
350
372
  return await new Promise((resolveReachable) => {
351
373
  const child = spawn('docker', ['info'], {
@@ -370,6 +392,7 @@ function createHostDockerCli() {
370
392
  run: (dockerArgs) => runCommand('docker', dockerArgs, { ...process.env, DOCKER_BUILDKIT: '1' }),
371
393
  inspectImage: inspectDockerImage,
372
394
  inspectContainer: inspectDockerContainer,
395
+ inspectContainerImage: inspectDockerContainerImage,
373
396
  spawnExec: (dockerArgs) => {
374
397
  const child = spawn('docker', dockerArgs, {
375
398
  cwd: process.cwd(),
@@ -1 +1 @@
1
- export const wakeVersion = "0.2.12+g6314122";
1
+ export const wakeVersion = "0.2.13+g3da36d3";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {