@tsplc/build 0.2.5 → 0.2.7

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.
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { copyFile, cp, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { cp, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import { z } from "zod";
5
5
  import type { ResolvedConfig } from "./config";
@@ -123,12 +123,11 @@ export const runControllerSession = async (
123
123
  const candidate = resolve(root, "generations", generation);
124
124
  const project = resolve(candidate, "project", "Application.project");
125
125
  await mkdir(resolve(candidate, "project"), { recursive: true });
126
+ // A download saves the template here, with its controller and device tree.
126
127
  if (previous) {
127
128
  await cp(previous.project, resolve(candidate, "project"), {
128
129
  recursive: true,
129
130
  });
130
- } else {
131
- await copyFile(config.template, project);
132
131
  }
133
132
  const jobConfig = {
134
133
  ...config,
@@ -140,11 +139,16 @@ export const runControllerSession = async (
140
139
  const request = {
141
140
  libraryReferences: config.libraryReferences,
142
141
  // A download starts from the template; an online change keeps the downloaded project's tree.
143
- ...(config.deviceTree &&
144
- operation === "download" && {
142
+ ...(operation === "download" && {
143
+ template: await toWindowsPath(config.template, config.codesys.wine),
144
+ templateHash,
145
+ // Kept across jobs: generating a device tree takes minutes, and the same inputs make the same one.
146
+ caches: await toWindowsPath(root, config.codesys.wine),
147
+ ...(config.deviceTree && {
145
148
  deviceTree: await writeDeviceTree(config.deviceTree, requestDirectory),
146
149
  devices: await toWindowsPath(requestDirectory, config.codesys.wine),
147
150
  }),
151
+ }),
148
152
  libraries: await Promise.all(
149
153
  (config.libraries ?? []).map(async (library) => ({
150
154
  ...library,
@@ -13,6 +13,31 @@ import { packageDirectory } from "./package-path.ts";
13
13
 
14
14
  const execute = promisify(execFile);
15
15
 
16
+ /** Each tsPLC version's images, published so that setup pulls them instead of installing CODESYS. */
17
+ export const publishedImage = async (image: string) => {
18
+ const { version } = JSON.parse(
19
+ await readFile(resolve(packageDirectory, "package.json"), "utf8"),
20
+ ) as { version: string };
21
+ return `ghcr.io/livingforjesus/${image.split(":")[0]}:${version}`;
22
+ };
23
+
24
+ /** Pulls this version's published `image`; false when it isn't available, so the caller builds it. */
25
+ const pullPublished = async (image: string) => {
26
+ const published = await publishedImage(image);
27
+ console.log(`Pulling ${published}`);
28
+ try {
29
+ await runDocker(
30
+ ["pull", "--platform", "linux/amd64", published],
31
+ (status) => `Docker pull failed (${status})`,
32
+ );
33
+ } catch {
34
+ console.log(`${published} is not available; building ${image} locally instead.`);
35
+ return false;
36
+ }
37
+ await runDocker(["tag", published, image], (status) => `Docker tag failed (${status})`);
38
+ return true;
39
+ };
40
+
16
41
  /** Install the IDE and its prerequisites in the Docker builder image. */
17
42
  export const configureDocker = async () => {
18
43
  const image = dockerImage;
@@ -30,6 +55,7 @@ export const configureDocker = async () => {
30
55
  if (!arm && architecture !== "linux/x86_64" && architecture !== "linux/amd64") {
31
56
  throw new Error("The builder requires a Linux Docker server on ARM64 or AMD64");
32
57
  }
58
+ if (await pullPublished(image)) return image;
33
59
  const context = await mkdtemp(resolve(tmpdir(), "codesys-installer-"));
34
60
  let buildContext: string | undefined;
35
61
  try {
@@ -94,6 +120,7 @@ export const gatewayImage = "tsplc-edge-gateway:4.22.0.0";
94
120
 
95
121
  /** Build the CODESYS Edge Gateway that builder containers reach a plant controller through. */
96
122
  export const configureGateway = async () => {
123
+ if (await pullPublished(gatewayImage)) return gatewayImage;
97
124
  const context = resolve(packageDirectory, "docker/edge-gateway");
98
125
  console.log(`Preparing the CODESYS gateway (${gatewayImage}).`);
99
126
  await runDocker(
@@ -218,6 +218,7 @@ export const createCodesysWorker = (config: ResolvedConfig) => {
218
218
  .update(
219
219
  JSON.stringify({
220
220
  builder: await readFile(resolve(pythonDirectory, "build.py"), "utf8"),
221
+ hardware: await readFile(resolve(pythonDirectory, "hardware.py"), "utf8"),
221
222
  online: await readFile(resolve(pythonDirectory, "codesys_online.py"), "utf8"),
222
223
  login: await readFile(resolve(pythonDirectory, "codesys_login.py"), "utf8"),
223
224
  command: codesysCommand,
@@ -244,9 +245,13 @@ export const createCodesysWorker = (config: ResolvedConfig) => {
244
245
  const directory = resolve(config.outDir, ".codesys/worker");
245
246
  await rm(directory, { recursive: true, force: true });
246
247
  await mkdir(directory, { recursive: true });
247
- await copyFile(resolve(pythonDirectory, "build.py"), resolve(directory, "build.py"));
248
- await copyFile(resolve(pythonDirectory, "worker.py"), resolve(directory, "worker.py"));
249
- for (const script of ["codesys_online.py", "codesys_login.py"]) {
248
+ for (const script of [
249
+ "build.py",
250
+ "hardware.py",
251
+ "worker.py",
252
+ "codesys_online.py",
253
+ "codesys_login.py",
254
+ ]) {
250
255
  await copyFile(resolve(pythonDirectory, script), resolve(directory, script));
251
256
  }
252
257
  console.log(`Starting CODESYS. Log: ${directory}/codesys.log`);