@tsplc/build 0.1.0 → 0.2.1

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/index.js CHANGED
@@ -124,6 +124,7 @@ class CodesysCompilerError extends Error {
124
124
  }
125
125
  }
126
126
  var buildFailureFile = ".codesys/docker/failure.json";
127
+ var controllerFailureFile = ".codesys/controller/failure.txt";
127
128
  var buildFailureSchema = z2.strictObject({
128
129
  operationId: z2.uuid(),
129
130
  message: z2.string(),
@@ -190,8 +191,6 @@ var prepareContainerRun = async (config, requestDirectory, options) => {
190
191
  runtime: undefined
191
192
  }));
192
193
  return [
193
- "run",
194
- "--rm",
195
194
  "--init",
196
195
  ...config.docker?.cpuSet ? ["--cpuset-cpus", config.docker.cpuSet] : [],
197
196
  "--network",
@@ -269,6 +268,13 @@ var configureRuntime = async () => {
269
268
  await runDocker(["build", "--platform", "linux/amd64", "--progress", "plain", "-t", runtimeImage, context], (status) => `Runtime image build failed (${status}). Review the image-build output`);
270
269
  return runtimeImage;
271
270
  };
271
+ var gatewayImage = "tsplc-edge-gateway:4.22.0.0";
272
+ var configureGateway = async () => {
273
+ const context = resolve5(packageDirectory, "docker/edge-gateway");
274
+ console.log(`Preparing the CODESYS gateway (${gatewayImage}).`);
275
+ await runDocker(["build", "--platform", "linux/amd64", "--progress", "plain", "-t", gatewayImage, context], (status) => `Gateway image build failed (${status}). Review the image-build output`);
276
+ return gatewayImage;
277
+ };
272
278
  var buildInDocker = async (config, image) => {
273
279
  const requestDirectory = await mkdtemp2(resolve5(tmpdir2(), "codesys-docker-"));
274
280
  try {
@@ -277,6 +283,8 @@ var buildInDocker = async (config, image) => {
277
283
  const failurePath = resolve5(config.outDir, buildFailureFile);
278
284
  await rm3(failurePath, { force: true });
279
285
  const args = [
286
+ "run",
287
+ "--rm",
280
288
  ...await prepareContainerRun(config, requestDirectory, { network: "none", sources: true }),
281
289
  image,
282
290
  "--request",
@@ -1222,10 +1230,12 @@ var buildProject = async (config) => {
1222
1230
  };
1223
1231
  // packages/build/src/controller-online.ts
1224
1232
  import { execFile as execFile4 } from "node:child_process";
1233
+ import { existsSync } from "node:fs";
1225
1234
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
1226
1235
  import { mkdir as mkdir5, mkdtemp as mkdtemp3, readFile as readFile6, rm as rm5 } from "node:fs/promises";
1227
1236
  import { tmpdir as tmpdir3 } from "node:os";
1228
1237
  import { resolve as resolve8 } from "node:path";
1238
+ import { setTimeout as delay2 } from "node:timers/promises";
1229
1239
  import { promisify as promisify4 } from "node:util";
1230
1240
  import { z as z5 } from "zod";
1231
1241
  var execute4 = promisify4(execFile4);
@@ -1256,7 +1266,7 @@ var controllerResultSchema = z5.strictObject({
1256
1266
  state: z5.string().nonempty(),
1257
1267
  values: z5.record(z5.string(), z5.string())
1258
1268
  });
1259
- var controllerOperation = async (config, operation, options) => {
1269
+ var controllerConnection = async (config) => {
1260
1270
  if (!config.docker || !config.online) {
1261
1271
  throw new Error("Docker image and online gateway/device configuration are required for controller operations");
1262
1272
  }
@@ -1269,20 +1279,30 @@ var controllerOperation = async (config, operation, options) => {
1269
1279
  const { stdout } = await execute4("docker", ["image", "inspect", "--format", "{{.Id}}", dockerImage], { timeout: config.timeouts.hostCommand }).catch((cause) => {
1270
1280
  throw new Error(`Cannot inspect Docker builder image ${dockerImage}. Install and start Docker, then run tsplc-build configure if the image is missing`, { cause });
1271
1281
  });
1272
- const imageId = imageIdSchema.parse(stdout.trim());
1273
- const templateHash = createHash2("sha256").update(await readFile6(config.template)).digest("hex");
1274
- const operationId = randomUUID2();
1275
- const target = {
1276
- network: config.online.network,
1277
- gateway: config.online.gateway,
1278
- device: config.online.device,
1279
- controller: config.controller
1282
+ return {
1283
+ environmentNames,
1284
+ imageId: imageIdSchema.parse(stdout.trim()),
1285
+ templateHash: createHash2("sha256").update(await readFile6(config.template)).digest("hex"),
1286
+ target: {
1287
+ network: config.online.network,
1288
+ gateway: config.online.gateway,
1289
+ device: config.online.device,
1290
+ controller: config.controller
1291
+ }
1280
1292
  };
1293
+ };
1294
+ var controllerOperation = async (config, operation, options) => {
1295
+ const { environmentNames, imageId, templateHash, target } = await controllerConnection(config);
1296
+ const operationId = randomUUID2();
1281
1297
  const requestDirectory = await mkdtemp3(resolve8(tmpdir3(), "codesys-controller-"));
1282
1298
  const diagnostics = resolve8(config.outDir, ".codesys/controller");
1299
+ const failure = resolve8(config.outDir, controllerFailureFile);
1283
1300
  try {
1284
1301
  await mkdir5(config.outDir, { recursive: true });
1302
+ await rm5(failure, { force: true });
1285
1303
  await runDocker([
1304
+ "run",
1305
+ "--rm",
1286
1306
  ...await prepareContainerRun(config, requestDirectory, {
1287
1307
  network: target.network,
1288
1308
  environment: environmentNames,
@@ -1298,7 +1318,13 @@ var controllerOperation = async (config, operation, options) => {
1298
1318
  imageId,
1299
1319
  ...options.initializeUser ? ["--initialize-user"] : [],
1300
1320
  ...options.read ? ["--read", JSON.stringify(options.read)] : []
1301
- ], (status) => `Docker controller operation failed (${status}). Inspect ${diagnostics} for diagnostics`);
1321
+ ], (status) => `Docker controller operation failed (${status}). Inspect ${diagnostics} for diagnostics`).catch(async (cause) => {
1322
+ if (!existsSync(failure))
1323
+ throw cause;
1324
+ const reason = (await readFile6(failure, "utf8")).trim().split(`
1325
+ `).at(-1);
1326
+ throw new Error(`${reason}. Inspect ${diagnostics} for diagnostics`, { cause });
1327
+ });
1302
1328
  const result = await readFile6(resolve8(diagnostics, "result.json"), "utf8").then((content) => controllerResultSchema.parse(JSON.parse(content))).catch((cause) => {
1303
1329
  throw new Error(`Docker did not produce a valid controller receipt. Inspect ${diagnostics} for diagnostics`, { cause });
1304
1330
  });
@@ -1312,12 +1338,110 @@ var controllerOperation = async (config, operation, options) => {
1312
1338
  };
1313
1339
  var updateController = (config, operation, options = {}) => controllerOperation(config, operation, options);
1314
1340
  var inspectController = (config, read) => controllerOperation(config, "inspect", { read });
1341
+ var explicitDownload = "use an explicit download";
1342
+ var loginCommandSchema = z5.discriminatedUnion("command", [
1343
+ z5.strictObject({ command: z5.literal("state") }),
1344
+ z5.strictObject({
1345
+ command: z5.literal("read"),
1346
+ expressions: z5.array(z5.string().nonempty()).nonempty()
1347
+ }),
1348
+ z5.strictObject({
1349
+ command: z5.enum(["write", "force"]),
1350
+ values: z5.record(z5.string().nonempty(), z5.string().nonempty())
1351
+ }),
1352
+ z5.strictObject({
1353
+ command: z5.literal("unforce"),
1354
+ expressions: z5.array(z5.string().nonempty()),
1355
+ restore: z5.boolean()
1356
+ }),
1357
+ z5.strictObject({ command: z5.enum(["start", "stop"]) }),
1358
+ z5.strictObject({ command: z5.literal("reset"), kind: z5.enum(["Warm", "Cold"]) })
1359
+ ]);
1360
+ var loginResultSchema = z5.union([
1361
+ z5.strictObject({ values: z5.record(z5.string(), z5.string()) }),
1362
+ z5.strictObject({ state: z5.string(), forced: z5.array(z5.string()) })
1363
+ ]);
1364
+ var loginReplySchema = z5.discriminatedUnion("ok", [
1365
+ z5.strictObject({ ok: z5.literal(true), result: z5.unknown() }),
1366
+ z5.strictObject({ ok: z5.literal(false), error: z5.string() })
1367
+ ]);
1368
+ var loginPort = 4851;
1369
+ var docker = (args, timeout) => execute4("docker", args, { timeout }).then(({ stdout }) => stdout.trim());
1370
+ var isRunning = async (name, timeout) => await docker(["container", "ls", "--quiet", "--filter", `name=^${name}$`], timeout) !== "";
1371
+ var attachLogin = async (name, timeout) => {
1372
+ const published = await docker(["port", name, String(loginPort)], timeout);
1373
+ const url = `http://${published.split(`
1374
+ `)[0]}`;
1375
+ const call = async (init) => {
1376
+ const reply = loginReplySchema.parse(await (await fetch(url, init)).json());
1377
+ if (!reply.ok)
1378
+ throw new Error(reply.error);
1379
+ return reply.result;
1380
+ };
1381
+ return {
1382
+ name,
1383
+ ready: () => fetch(url).then((response) => response.ok).catch(() => false),
1384
+ send: async (command) => loginResultSchema.parse(await call({
1385
+ method: "POST",
1386
+ headers: { "Content-Type": "application/json" },
1387
+ body: JSON.stringify(command)
1388
+ })),
1389
+ close: async () => {
1390
+ await call({ method: "DELETE" });
1391
+ await docker(["wait", name], timeout);
1392
+ await docker(["rm", name], timeout);
1393
+ }
1394
+ };
1395
+ };
1396
+ var findControllerLogin = async (name) => await isRunning(name, 15000) ? attachLogin(name, 15000) : undefined;
1397
+ var openControllerLogin = async (config, name) => {
1398
+ const { environmentNames, imageId, target } = await controllerConnection(config);
1399
+ const timeout = config.timeouts.hostCommand;
1400
+ await docker(["rm", "--force", name], timeout);
1401
+ const requestDirectory = resolve8(config.outDir, ".codesys/login-request");
1402
+ await mkdir5(requestDirectory, { recursive: true });
1403
+ await docker([
1404
+ "run",
1405
+ "--detach",
1406
+ "--name",
1407
+ name,
1408
+ "--publish",
1409
+ `127.0.0.1::${loginPort}`,
1410
+ ...await prepareContainerRun(config, requestDirectory, {
1411
+ network: target.network,
1412
+ environment: environmentNames,
1413
+ sources: false
1414
+ }),
1415
+ imageId,
1416
+ "login",
1417
+ "--request",
1418
+ "/input/config.json",
1419
+ "--image-id",
1420
+ imageId,
1421
+ "--port",
1422
+ String(loginPort)
1423
+ ], timeout);
1424
+ const deadline = Date.now() + config.timeouts.startup + config.timeouts.build;
1425
+ while (Date.now() < deadline) {
1426
+ if (!await isRunning(name, timeout)) {
1427
+ const logs = await execute4("docker", ["logs", "--tail", "40", name], { timeout });
1428
+ throw new Error(`The controller login failed:
1429
+ ${logs.stdout}${logs.stderr}`);
1430
+ }
1431
+ const login = await attachLogin(name, timeout);
1432
+ if (await login.ready())
1433
+ return login;
1434
+ await delay2(2000);
1435
+ }
1436
+ await docker(["rm", "--force", name], timeout);
1437
+ throw new Error("The controller login did not open in time");
1438
+ };
1315
1439
  // packages/build/src/run.ts
1316
1440
  import { execFile as execFile5 } from "node:child_process";
1317
1441
  import { mkdtemp as mkdtemp4, readFile as readFile7, rm as rm6, writeFile as writeFile5 } from "node:fs/promises";
1318
1442
  import { tmpdir as tmpdir4 } from "node:os";
1319
1443
  import { basename as basename4, dirname as dirname2, join } from "node:path";
1320
- import { setTimeout as delay2 } from "node:timers/promises";
1444
+ import { setTimeout as delay3 } from "node:timers/promises";
1321
1445
  import { promisify as promisify5 } from "node:util";
1322
1446
  import { z as z6 } from "zod";
1323
1447
 
@@ -1402,7 +1526,7 @@ ${log.split(`
1402
1526
  `).slice(-25).join(`
1403
1527
  `)}`);
1404
1528
  }
1405
- await delay2(250);
1529
+ await delay3(250);
1406
1530
  }
1407
1531
  } finally {
1408
1532
  await rm6(definitionDirectory, { force: true, recursive: true });
@@ -1533,13 +1657,20 @@ export {
1533
1657
  buildProject,
1534
1658
  configSchema,
1535
1659
  configureDocker,
1660
+ configureGateway,
1536
1661
  configureRuntime,
1537
1662
  defineBuildConfig as defineConfig,
1663
+ explicitDownload,
1664
+ findControllerLogin,
1665
+ gatewayImage,
1538
1666
  inspectController,
1539
1667
  installLibraries,
1540
1668
  libraryIndex,
1541
1669
  loadConfig,
1542
1670
  loadSources,
1671
+ loginCommandSchema,
1672
+ loginResultSchema,
1673
+ openControllerLogin,
1543
1674
  parseSource,
1544
1675
  readBuild,
1545
1676
  resolveConfig,
@@ -1548,5 +1679,5 @@ export {
1548
1679
  validateLibrary
1549
1680
  };
1550
1681
 
1551
- //# debugId=C64EC9830995F99164756E2164756E21
1682
+ //# debugId=B75F268E384DC61264756E2164756E21
1552
1683
  //# sourceMappingURL=index.js.map