@le-space/rootfs 0.6.28 → 0.6.30
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 +4 -0
- package/index.d.ts +1 -0
- package/index.js +6 -0
- package/package.json +1 -1
- package/reference/orbitdb-relay/rootfs/build-rootfs-image.sh +1 -0
- package/reference/orbitdb-relay/rootfs/orbitdb-relay-bootstrap-deregister.service +20 -0
- package/reference/orbitdb-relay/rootfs/orbitdb-relay-bootstrap-refresh.py +48 -11
- package/reference/orbitdb-relay/rootfs/orbitdb-relay-configure.sh +5 -0
- package/reference/playwright-runner/README.md +21 -0
- package/reference/playwright-runner/contract.json +40 -0
- package/reference/playwright-runner/rootfs/Caddyfile +18 -0
- package/reference/playwright-runner/rootfs/Dockerfile.rootfs +16 -0
- package/reference/playwright-runner/rootfs/build-rootfs-image.sh +84 -0
- package/reference/playwright-runner/rootfs/playwright-runner-bootstrap.service +13 -0
- package/reference/playwright-runner/rootfs/playwright-runner-bootstrap.sh +26 -0
- package/reference/playwright-runner/rootfs/playwright-runner-proxy.service +28 -0
- package/reference/playwright-runner/rootfs/playwright-runner-ttl.service +6 -0
- package/reference/playwright-runner/rootfs/playwright-runner-ttl.timer +10 -0
- package/reference/playwright-runner/rootfs/playwright-runner.service +26 -0
- package/reference/uc-go-peer/rootfs/__pycache__/uc-go-peer-describe.cpython-312.pyc +0 -0
- package/reference/uc-go-peer/rootfs/uc-go-peer-bootstrap-deregister.service +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ Shared rootfs contract parsing, reference profile assets, and build helpers.
|
|
|
11
11
|
including the delayed first-start, setup endpoint, Caddy, and AutoTLS flow
|
|
12
12
|
- added a `ucan-store` reference contract scaffold with documented service
|
|
13
13
|
ports and consumer env expectations
|
|
14
|
+
- added an ephemeral `playwright-runner` profile with Node.js 24, exact
|
|
15
|
+
Playwright/Chromium `1.61.1`, authenticated WSS, bounded logs, and a guest TTL
|
|
14
16
|
|
|
15
17
|
## Reference assets
|
|
16
18
|
|
|
@@ -22,3 +24,5 @@ Current shared reference profiles live under:
|
|
|
22
24
|
- `reference/orbitdb-relay/rootfs/*`
|
|
23
25
|
- `reference/ucan-store/contract.json`
|
|
24
26
|
- `reference/ucan-store/rootfs/*`
|
|
27
|
+
- `reference/playwright-runner/contract.json`
|
|
28
|
+
- `reference/playwright-runner/rootfs/*`
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -274,6 +274,9 @@ function validateRootfsManifest(manifest) {
|
|
|
274
274
|
return { manifest, valid: false, errors: ["Rootfs manifest is missing."] };
|
|
275
275
|
}
|
|
276
276
|
if (!manifest.version) errors.push("Rootfs manifest version is missing.");
|
|
277
|
+
if (manifest.profile === "playwright-runner" && !/^\d+\.\d+\.\d+$/u.test(manifest.playwrightVersion ?? "")) {
|
|
278
|
+
errors.push("Playwright runner manifest must expose an exact Playwright version.");
|
|
279
|
+
}
|
|
277
280
|
if (manifest.rootfsInstallStrategy != null && manifest.rootfsInstallStrategy !== "thin" && manifest.rootfsInstallStrategy !== "prebaked") {
|
|
278
281
|
errors.push('Rootfs install strategy must be "thin" or "prebaked" when provided.');
|
|
279
282
|
}
|
|
@@ -345,6 +348,9 @@ function createRootfsManifest(plan, contract, options = {}) {
|
|
|
345
348
|
createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
346
349
|
notes: contract.manifest.notes ?? ""
|
|
347
350
|
};
|
|
351
|
+
if (contract.rootfs.profile === "playwright-runner") {
|
|
352
|
+
manifest.playwrightVersion = "1.61.1";
|
|
353
|
+
}
|
|
348
354
|
if (typeof options.rootfsSourceSizeBytes === "number" && Number.isFinite(options.rootfsSourceSizeBytes) && options.rootfsSourceSizeBytes > 0) {
|
|
349
355
|
manifest.rootfsSourceSizeBytes = options.rootfsSourceSizeBytes;
|
|
350
356
|
}
|
package/package.json
CHANGED
|
@@ -243,6 +243,7 @@ case "${ROOTFS_PROFILE}" in
|
|
|
243
243
|
--copy-in "${SCRIPT_DIR}/orbitdb-relay-bootstrap.service:/etc/systemd/system"
|
|
244
244
|
--copy-in "${SCRIPT_DIR}/orbitdb-relay-bootstrap-refresh.service:/etc/systemd/system"
|
|
245
245
|
--copy-in "${SCRIPT_DIR}/orbitdb-relay-bootstrap-refresh.timer:/etc/systemd/system"
|
|
246
|
+
--copy-in "${SCRIPT_DIR}/orbitdb-relay-bootstrap-deregister.service:/etc/systemd/system"
|
|
246
247
|
--copy-in "${SCRIPT_DIR}/orbitdb-relay-autotls-refresh.service:/etc/systemd/system"
|
|
247
248
|
--copy-in "${SCRIPT_DIR}/orbitdb-relay-bootstrap.conf:/etc/systemd/system/orbitdb-relay.service.d"
|
|
248
249
|
--run-command "tar -xf /opt/$(basename "${APP_TAR}") -C /opt/orbitdb-relay"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Best-effort Aleph bootstrap deregistration on shutdown for orbitdb-relay
|
|
3
|
+
After=network-online.target orbitdb-relay.service
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
ConditionPathExists=/etc/default/orbitdb-relay.ready
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=oneshot
|
|
9
|
+
RemainAfterExit=yes
|
|
10
|
+
Environment=ENV_FILE=/etc/default/orbitdb-relay
|
|
11
|
+
Environment=DESCRIBE_SCRIPT=/usr/local/sbin/orbitdb-relay-describe.py
|
|
12
|
+
Environment=ALEPH_BOOTSTRAP_DEREGISTER_ONLY=1
|
|
13
|
+
ExecStart=/bin/true
|
|
14
|
+
ExecStop=/usr/bin/python3 /usr/local/sbin/orbitdb-relay-bootstrap-refresh.py
|
|
15
|
+
StandardOutput=journal
|
|
16
|
+
StandardError=journal
|
|
17
|
+
TimeoutStopSec=25
|
|
18
|
+
|
|
19
|
+
[Install]
|
|
20
|
+
WantedBy=multi-user.target
|
|
@@ -476,6 +476,7 @@ def fetch_previous_hashes(
|
|
|
476
476
|
sender: str,
|
|
477
477
|
current_item_hash: str,
|
|
478
478
|
now_ms: int,
|
|
479
|
+
deregister_all: bool = False,
|
|
479
480
|
) -> list[str]:
|
|
480
481
|
stale_cutoff_ms = now_ms - (STALE_RECORD_MAX_AGE_SECONDS * 1000)
|
|
481
482
|
normalized_sender = normalize_address(sender)
|
|
@@ -503,7 +504,10 @@ def fetch_previous_hashes(
|
|
|
503
504
|
}
|
|
504
505
|
if not same_publisher:
|
|
505
506
|
continue
|
|
506
|
-
if parsed["item_hash"] == current_item_hash:
|
|
507
|
+
if not deregister_all and parsed["item_hash"] == current_item_hash:
|
|
508
|
+
continue
|
|
509
|
+
if deregister_all:
|
|
510
|
+
found.append(str(parsed["item_hash"]))
|
|
507
511
|
continue
|
|
508
512
|
updated_at = timestamp_ms(parsed["updated_at"])
|
|
509
513
|
if updated_at is not None and updated_at < stale_cutoff_ms:
|
|
@@ -556,6 +560,49 @@ def main() -> None:
|
|
|
556
560
|
print(json_dumps({"status": "skipped", "reason": "missing publisher key"}))
|
|
557
561
|
return
|
|
558
562
|
|
|
563
|
+
publisher_address = address_from_private_key(publisher_private_key)
|
|
564
|
+
now_ms = int(time.time() * 1000)
|
|
565
|
+
channel = env_values.get("ALEPH_BOOTSTRAP_CHANNEL", DEFAULT_CHANNEL).strip() or DEFAULT_CHANNEL
|
|
566
|
+
ref = env_values.get("ALEPH_BOOTSTRAP_REF", DEFAULT_REF).strip() or DEFAULT_REF
|
|
567
|
+
post_type = env_values.get("ALEPH_BOOTSTRAP_POST_TYPE", POST_TYPE).strip() or POST_TYPE
|
|
568
|
+
if post_type != POST_TYPE:
|
|
569
|
+
raise RuntimeError(
|
|
570
|
+
f"Unsupported ALEPH_BOOTSTRAP_POST_TYPE: {post_type}. Expected {POST_TYPE}."
|
|
571
|
+
)
|
|
572
|
+
api_host = env_values.get("ALEPH_BOOTSTRAP_API_HOST", DEFAULT_API_HOST).strip() or DEFAULT_API_HOST
|
|
573
|
+
deregister_only = os.environ.get("ALEPH_BOOTSTRAP_DEREGISTER_ONLY", "").strip().lower() in {
|
|
574
|
+
"1",
|
|
575
|
+
"true",
|
|
576
|
+
"yes",
|
|
577
|
+
}
|
|
578
|
+
if deregister_only:
|
|
579
|
+
hashes = fetch_previous_hashes(
|
|
580
|
+
api_host,
|
|
581
|
+
channel,
|
|
582
|
+
ref,
|
|
583
|
+
post_type,
|
|
584
|
+
publisher_address,
|
|
585
|
+
"",
|
|
586
|
+
now_ms,
|
|
587
|
+
deregister_all=True,
|
|
588
|
+
)
|
|
589
|
+
forget_response = broadcast_forget(
|
|
590
|
+
api_host, publisher_address, publisher_private_key, hashes, channel
|
|
591
|
+
)
|
|
592
|
+
print(
|
|
593
|
+
json_dumps(
|
|
594
|
+
{
|
|
595
|
+
"status": "forgotten" if hashes else "skipped",
|
|
596
|
+
"reason": None if hashes else "no matching bootstrap records",
|
|
597
|
+
"sender": publisher_address,
|
|
598
|
+
"registrationId": registration_id,
|
|
599
|
+
"forgottenHashes": hashes,
|
|
600
|
+
"forgetResponse": forget_response,
|
|
601
|
+
}
|
|
602
|
+
)
|
|
603
|
+
)
|
|
604
|
+
return
|
|
605
|
+
|
|
559
606
|
describe = subprocess.run([DESCRIBE_SCRIPT], check=True, capture_output=True, text=True)
|
|
560
607
|
metadata = json.loads(describe.stdout.strip() or "{}")
|
|
561
608
|
peer_id = metadata.get("peer_id")
|
|
@@ -567,19 +614,9 @@ def main() -> None:
|
|
|
567
614
|
metadata.get("browser_bootstrap_multiaddrs") or [], browser_only=True
|
|
568
615
|
)
|
|
569
616
|
|
|
570
|
-
publisher_address = address_from_private_key(publisher_private_key)
|
|
571
|
-
now_ms = int(time.time() * 1000)
|
|
572
617
|
now_seconds = now_ms / 1000
|
|
573
618
|
profile = env_values.get("ALEPH_BOOTSTRAP_PROFILE", DEFAULT_PROFILE).strip() or DEFAULT_PROFILE
|
|
574
619
|
version = env_values.get("ALEPH_BOOTSTRAP_VERSION", "").strip() or None
|
|
575
|
-
channel = env_values.get("ALEPH_BOOTSTRAP_CHANNEL", DEFAULT_CHANNEL).strip() or DEFAULT_CHANNEL
|
|
576
|
-
ref = env_values.get("ALEPH_BOOTSTRAP_REF", DEFAULT_REF).strip() or DEFAULT_REF
|
|
577
|
-
post_type = env_values.get("ALEPH_BOOTSTRAP_POST_TYPE", POST_TYPE).strip() or POST_TYPE
|
|
578
|
-
if post_type != POST_TYPE:
|
|
579
|
-
raise RuntimeError(
|
|
580
|
-
f"Unsupported ALEPH_BOOTSTRAP_POST_TYPE: {post_type}. Expected {POST_TYPE}."
|
|
581
|
-
)
|
|
582
|
-
api_host = env_values.get("ALEPH_BOOTSTRAP_API_HOST", DEFAULT_API_HOST).strip() or DEFAULT_API_HOST
|
|
583
620
|
published_multiaddrs = select_compact_multiaddrs(browser_multiaddrs or multiaddrs)
|
|
584
621
|
published_browser_multiaddrs: list[str] = []
|
|
585
622
|
if not published_multiaddrs:
|
|
@@ -15,6 +15,8 @@ CADDY_UPSTREAM_HOST="${CADDY_UPSTREAM_HOST:-127.0.0.1}"
|
|
|
15
15
|
CADDY_UPSTREAM_METRICS_PORT="${CADDY_UPSTREAM_METRICS_PORT:-9090}"
|
|
16
16
|
AUTOTLS_REFRESH_SERVICE="${AUTOTLS_REFRESH_SERVICE:-orbitdb-relay-autotls-refresh.service}"
|
|
17
17
|
BOOTSTRAP_REFRESH_TIMER="${BOOTSTRAP_REFRESH_TIMER:-orbitdb-relay-bootstrap-refresh.timer}"
|
|
18
|
+
BOOTSTRAP_REFRESH_SERVICE="${BOOTSTRAP_REFRESH_SERVICE:-orbitdb-relay-bootstrap-refresh.service}"
|
|
19
|
+
BOOTSTRAP_DEREGISTER_SERVICE="${BOOTSTRAP_DEREGISTER_SERVICE:-orbitdb-relay-bootstrap-deregister.service}"
|
|
18
20
|
PUBLIC_IPV4=""
|
|
19
21
|
PUBLIC_IPV6=""
|
|
20
22
|
TCP_PORT=""
|
|
@@ -278,7 +280,10 @@ if [ "${START_SERVICE}" -eq 1 ]; then
|
|
|
278
280
|
systemctl restart "${SERVICE_NAME}"
|
|
279
281
|
systemctl enable "${AUTOTLS_REFRESH_SERVICE}"
|
|
280
282
|
systemctl enable "${BOOTSTRAP_REFRESH_TIMER}"
|
|
283
|
+
systemctl enable "${BOOTSTRAP_DEREGISTER_SERVICE}"
|
|
284
|
+
systemctl restart "${BOOTSTRAP_DEREGISTER_SERVICE}"
|
|
281
285
|
systemctl start "${BOOTSTRAP_REFRESH_TIMER}"
|
|
286
|
+
systemctl restart --no-block "${BOOTSTRAP_REFRESH_SERVICE}" || true
|
|
282
287
|
if [ -n "${PROXY_HOSTNAME}" ]; then
|
|
283
288
|
write_caddyfile "${PROXY_HOSTNAME}"
|
|
284
289
|
touch "${CADDY_READY_FILE}"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Playwright runner RootFS
|
|
2
|
+
|
|
3
|
+
This profile prebakes Node.js 24, Playwright `1.61.1`, its matching Chromium
|
|
4
|
+
headless shell, Caddy, and systemd units. It intentionally contains no endpoint
|
|
5
|
+
credential or TLS private key. The full headed Chromium bundle is omitted
|
|
6
|
+
because this remote CI runner only launches headless browsers.
|
|
7
|
+
|
|
8
|
+
After Aleph allocates the VM, the reusable action must inject:
|
|
9
|
+
|
|
10
|
+
- `/etc/default/playwright-runner` with `PLAYWRIGHT_RUNNER_SECRET` and the exact
|
|
11
|
+
`PLAYWRIGHT_VERSION=1.61.1`;
|
|
12
|
+
- a per-run certificate and key readable by the dedicated proxy service;
|
|
13
|
+
- the requested TTL override, if shorter than the default 45 minutes.
|
|
14
|
+
|
|
15
|
+
The bootstrap unit refuses to start the browser and proxy until those files are
|
|
16
|
+
present. Caddy protects `/version` and the websocket upgrade with the same
|
|
17
|
+
Bearer credential. The Playwright server only listens on `127.0.0.1:3000`.
|
|
18
|
+
|
|
19
|
+
The TTL is a last-resort shutdown, not authoritative Aleph cleanup. The owner
|
|
20
|
+
must still erase and FORGET the exact INSTANCE and verify scheduler
|
|
21
|
+
deallocation.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "playwright-runner",
|
|
4
|
+
"displayName": "Ephemeral Playwright Chromium Runner",
|
|
5
|
+
"source": {
|
|
6
|
+
"repository": "NiKrause/relay-button",
|
|
7
|
+
"subdirectory": "packages/rootfs/reference/playwright-runner"
|
|
8
|
+
},
|
|
9
|
+
"rootfs": {
|
|
10
|
+
"profile": "playwright-runner",
|
|
11
|
+
"installMode": "prebaked",
|
|
12
|
+
"installDir": "/opt/playwright-runner",
|
|
13
|
+
"binaryPath": "/opt/playwright-runner/node_modules/.bin/playwright",
|
|
14
|
+
"dataDir": "/var/lib/playwright-runner",
|
|
15
|
+
"envFile": "/etc/default/playwright-runner"
|
|
16
|
+
},
|
|
17
|
+
"services": {
|
|
18
|
+
"bootstrap": "playwright-runner-bootstrap.service",
|
|
19
|
+
"main": "playwright-runner.service",
|
|
20
|
+
"autotlsRefresh": "playwright-runner-ttl.timer"
|
|
21
|
+
},
|
|
22
|
+
"ports": [
|
|
23
|
+
{
|
|
24
|
+
"port": 22,
|
|
25
|
+
"tcp": true,
|
|
26
|
+
"udp": false,
|
|
27
|
+
"purpose": "SSH bootstrap and diagnostics"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"port": 443,
|
|
31
|
+
"tcp": true,
|
|
32
|
+
"udp": false,
|
|
33
|
+
"purpose": "Authenticated Playwright WSS and version endpoint"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"manifest": {
|
|
37
|
+
"copyTarget": "packages/rootfs/reference/playwright-runner/latest.json",
|
|
38
|
+
"notes": "Node.js 24 and Playwright 1.61.1 with matching Chromium are prebaked. A random bearer secret and per-run TLS material must be injected after boot. The image contains no reusable credential and shuts down through a bounded TTL timer."
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
admin off
|
|
3
|
+
auto_https disable_redirects
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
:443 {
|
|
7
|
+
tls /etc/playwright-runner/tls.crt /etc/playwright-runner/tls.key
|
|
8
|
+
|
|
9
|
+
@unauthorized not header Authorization "Bearer {$PLAYWRIGHT_RUNNER_SECRET}"
|
|
10
|
+
respond @unauthorized 401
|
|
11
|
+
|
|
12
|
+
handle /version {
|
|
13
|
+
header Content-Type application/json
|
|
14
|
+
respond `{"playwrightVersion":"{$PLAYWRIGHT_VERSION}"}` 200
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
reverse_proxy 127.0.0.1:3000
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
FROM debian:12
|
|
2
|
+
|
|
3
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
4
|
+
ENV LIBGUESTFS_BACKEND=direct
|
|
5
|
+
|
|
6
|
+
RUN apt-get update \
|
|
7
|
+
&& apt-get install -y --no-install-recommends \
|
|
8
|
+
ca-certificates \
|
|
9
|
+
curl \
|
|
10
|
+
libguestfs-tools \
|
|
11
|
+
linux-image-amd64 \
|
|
12
|
+
qemu-system-x86 \
|
|
13
|
+
qemu-utils \
|
|
14
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
15
|
+
|
|
16
|
+
WORKDIR /workspace/shared-rootfs
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
OUT_DIR="${OUT_DIR:-${SCRIPT_DIR}/../dist-rootfs}"
|
|
6
|
+
BASE_URL="${BASE_URL:-https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2}"
|
|
7
|
+
BASE_IMAGE="${OUT_DIR}/debian-12-genericcloud-amd64.qcow2"
|
|
8
|
+
IMAGE="${OUT_DIR}/aleph-playwright-runner.qcow2"
|
|
9
|
+
ROOTFS_IMAGE_SIZE="${ROOTFS_IMAGE_SIZE:-20G}"
|
|
10
|
+
PLAYWRIGHT_VERSION="${PLAYWRIGHT_VERSION:-1.61.1}"
|
|
11
|
+
HOST_UID="${HOST_UID:-}"
|
|
12
|
+
HOST_GID="${HOST_GID:-}"
|
|
13
|
+
ROOTFS_SPARSIFY_TMPDIR=""
|
|
14
|
+
|
|
15
|
+
require() {
|
|
16
|
+
command -v "$1" >/dev/null 2>&1 || { echo "Missing required command: $1" >&2; exit 1; }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for command in curl qemu-img virt-customize; do require "${command}"; done
|
|
20
|
+
test "${PLAYWRIGHT_VERSION}" = "1.61.1" || {
|
|
21
|
+
echo "playwright-runner only supports Playwright 1.61.1" >&2
|
|
22
|
+
exit 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
cleanup() {
|
|
26
|
+
if [ -n "${ROOTFS_SPARSIFY_TMPDIR}" ] && [ -d "${ROOTFS_SPARSIFY_TMPDIR}" ]; then
|
|
27
|
+
rm -rf "${ROOTFS_SPARSIFY_TMPDIR}"
|
|
28
|
+
fi
|
|
29
|
+
}
|
|
30
|
+
trap cleanup EXIT
|
|
31
|
+
|
|
32
|
+
mkdir -p "${OUT_DIR}"
|
|
33
|
+
if [ ! -f "${BASE_IMAGE}" ]; then
|
|
34
|
+
curl --fail --show-error --location --retry 5 --retry-all-errors \
|
|
35
|
+
--connect-timeout 20 --max-time 300 "${BASE_URL}" -o "${BASE_IMAGE}"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
cp "${BASE_IMAGE}" "${IMAGE}"
|
|
39
|
+
qemu-img resize "${IMAGE}" "${ROOTFS_IMAGE_SIZE}"
|
|
40
|
+
|
|
41
|
+
virt-customize \
|
|
42
|
+
-a "${IMAGE}" \
|
|
43
|
+
--network \
|
|
44
|
+
--mkdir /opt/playwright-runner \
|
|
45
|
+
--mkdir /opt/playwright-browsers \
|
|
46
|
+
--mkdir /etc/playwright-runner \
|
|
47
|
+
--mkdir /etc/systemd/journald.conf.d \
|
|
48
|
+
--mkdir /var/lib/playwright-runner \
|
|
49
|
+
--copy-in "${SCRIPT_DIR}/Caddyfile:/etc/playwright-runner" \
|
|
50
|
+
--copy-in "${SCRIPT_DIR}/playwright-runner-bootstrap.sh:/usr/local/sbin" \
|
|
51
|
+
--copy-in "${SCRIPT_DIR}/playwright-runner.service:/etc/systemd/system" \
|
|
52
|
+
--copy-in "${SCRIPT_DIR}/playwright-runner-proxy.service:/etc/systemd/system" \
|
|
53
|
+
--copy-in "${SCRIPT_DIR}/playwright-runner-bootstrap.service:/etc/systemd/system" \
|
|
54
|
+
--copy-in "${SCRIPT_DIR}/playwright-runner-ttl.service:/etc/systemd/system" \
|
|
55
|
+
--copy-in "${SCRIPT_DIR}/playwright-runner-ttl.timer:/etc/systemd/system" \
|
|
56
|
+
--run-command "apt-get update && apt-get install -y --no-install-recommends ca-certificates caddy curl gnupg" \
|
|
57
|
+
--run-command "curl -fsSL https://deb.nodesource.com/setup_24.x | bash -" \
|
|
58
|
+
--run-command "apt-get install -y --no-install-recommends nodejs" \
|
|
59
|
+
--run-command "useradd --system --home /var/lib/playwright-runner --shell /usr/sbin/nologin playwright-runner" \
|
|
60
|
+
--run-command "useradd --system --home /var/lib/playwright-runner --shell /usr/sbin/nologin playwright-proxy" \
|
|
61
|
+
--run-command "cd /opt/playwright-runner && npm init -y && npm install --omit=dev --no-audit --no-fund playwright@${PLAYWRIGHT_VERSION}" \
|
|
62
|
+
--run-command "cd /opt/playwright-runner && PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers ./node_modules/.bin/playwright install --with-deps --only-shell chromium" \
|
|
63
|
+
--run-command "chown -R playwright-runner:playwright-runner /opt/playwright-runner /opt/playwright-browsers /var/lib/playwright-runner" \
|
|
64
|
+
--run-command "chown -R root:playwright-proxy /etc/playwright-runner && chmod 0750 /etc/playwright-runner" \
|
|
65
|
+
--run-command "chmod 0755 /usr/local/sbin/playwright-runner-bootstrap.sh" \
|
|
66
|
+
--run-command "printf '[Journal]\\nSystemMaxUse=128M\\nRuntimeMaxUse=64M\\nMaxRetentionSec=2day\\n' >/etc/systemd/journald.conf.d/playwright-runner.conf" \
|
|
67
|
+
--run-command "npm cache clean --force" \
|
|
68
|
+
--run-command "apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* /root/.npm /tmp/* /var/tmp/*" \
|
|
69
|
+
--run-command "systemctl enable playwright-runner-bootstrap.service"
|
|
70
|
+
|
|
71
|
+
if command -v virt-sparsify >/dev/null 2>&1; then
|
|
72
|
+
SPARSE_IMAGE="${IMAGE%.qcow2}.sparse.qcow2"
|
|
73
|
+
ROOTFS_SPARSIFY_TMPDIR="$(mktemp -d "${OUT_DIR}/virt-sparsify-tmp.XXXXXX")"
|
|
74
|
+
TMPDIR="${ROOTFS_SPARSIFY_TMPDIR}" virt-sparsify --check-tmpdir=continue --compress \
|
|
75
|
+
"${IMAGE}" "${SPARSE_IMAGE}"
|
|
76
|
+
mv "${SPARSE_IMAGE}" "${IMAGE}"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
if [ -n "${HOST_UID}" ] && [ -n "${HOST_GID}" ]; then
|
|
80
|
+
chown -R "${HOST_UID}:${HOST_GID}" "${OUT_DIR}"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo "Compressed RootFS size: $(stat -c '%s bytes' "${IMAGE}") ($(du -h "${IMAGE}" | cut -f1) on disk)"
|
|
84
|
+
echo "Playwright ${PLAYWRIGHT_VERSION} RootFS ready: ${IMAGE}"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Validate and start the injected Playwright runner configuration
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
ConditionPathExists=/etc/default/playwright-runner
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=oneshot
|
|
9
|
+
ExecStart=/usr/local/sbin/playwright-runner-bootstrap.sh
|
|
10
|
+
RemainAfterExit=yes
|
|
11
|
+
|
|
12
|
+
[Install]
|
|
13
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ENV_FILE=/etc/default/playwright-runner
|
|
5
|
+
TLS_CERT=/etc/playwright-runner/tls.crt
|
|
6
|
+
TLS_KEY=/etc/playwright-runner/tls.key
|
|
7
|
+
|
|
8
|
+
test -s "${ENV_FILE}"
|
|
9
|
+
test -s "${TLS_CERT}"
|
|
10
|
+
test -s "${TLS_KEY}"
|
|
11
|
+
|
|
12
|
+
set -a
|
|
13
|
+
# shellcheck disable=SC1090
|
|
14
|
+
source "${ENV_FILE}"
|
|
15
|
+
set +a
|
|
16
|
+
|
|
17
|
+
test "${PLAYWRIGHT_VERSION:-}" = "1.61.1"
|
|
18
|
+
test -n "${PLAYWRIGHT_RUNNER_SECRET:-}"
|
|
19
|
+
|
|
20
|
+
chown root:playwright-proxy "${TLS_CERT}" "${TLS_KEY}"
|
|
21
|
+
chmod 0644 "${TLS_CERT}"
|
|
22
|
+
chmod 0640 "${TLS_KEY}"
|
|
23
|
+
|
|
24
|
+
systemctl restart playwright-runner.service
|
|
25
|
+
systemctl restart playwright-runner-proxy.service
|
|
26
|
+
systemctl restart playwright-runner-ttl.timer
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Authenticated TLS proxy for the Playwright runner
|
|
3
|
+
After=network-online.target playwright-runner.service
|
|
4
|
+
Requires=playwright-runner.service
|
|
5
|
+
ConditionPathExists=/etc/default/playwright-runner
|
|
6
|
+
ConditionPathExists=/etc/playwright-runner/tls.crt
|
|
7
|
+
ConditionPathExists=/etc/playwright-runner/tls.key
|
|
8
|
+
|
|
9
|
+
[Service]
|
|
10
|
+
Type=simple
|
|
11
|
+
User=playwright-proxy
|
|
12
|
+
Group=playwright-proxy
|
|
13
|
+
EnvironmentFile=/etc/default/playwright-runner
|
|
14
|
+
ExecStart=/usr/bin/caddy run --config /etc/playwright-runner/Caddyfile --adapter caddyfile
|
|
15
|
+
ExecReload=/usr/bin/caddy reload --config /etc/playwright-runner/Caddyfile --adapter caddyfile
|
|
16
|
+
Restart=on-failure
|
|
17
|
+
RestartSec=2
|
|
18
|
+
NoNewPrivileges=true
|
|
19
|
+
PrivateTmp=true
|
|
20
|
+
ProtectSystem=strict
|
|
21
|
+
ProtectHome=true
|
|
22
|
+
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
23
|
+
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
|
24
|
+
StandardOutput=journal
|
|
25
|
+
StandardError=journal
|
|
26
|
+
|
|
27
|
+
[Install]
|
|
28
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Ephemeral Playwright 1.61.1 browser server
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
ConditionPathExists=/etc/default/playwright-runner
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=simple
|
|
9
|
+
User=playwright-runner
|
|
10
|
+
Group=playwright-runner
|
|
11
|
+
EnvironmentFile=/etc/default/playwright-runner
|
|
12
|
+
Environment=PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
|
|
13
|
+
WorkingDirectory=/opt/playwright-runner
|
|
14
|
+
ExecStart=/opt/playwright-runner/node_modules/.bin/playwright run-server --host 127.0.0.1 --port 3000
|
|
15
|
+
Restart=on-failure
|
|
16
|
+
RestartSec=2
|
|
17
|
+
NoNewPrivileges=true
|
|
18
|
+
PrivateTmp=true
|
|
19
|
+
ProtectSystem=strict
|
|
20
|
+
ProtectHome=true
|
|
21
|
+
ReadWritePaths=/var/lib/playwright-runner
|
|
22
|
+
StandardOutput=journal
|
|
23
|
+
StandardError=journal
|
|
24
|
+
|
|
25
|
+
[Install]
|
|
26
|
+
WantedBy=multi-user.target
|
|
Binary file
|