diff --git a/.gitea/workflows/port-to-omp.yml b/.gitea/workflows/port-to-omp.yml index 5307128..4c887dd 100644 --- a/.gitea/workflows/port-to-omp.yml +++ b/.gitea/workflows/port-to-omp.yml @@ -33,24 +33,28 @@ jobs: run: | set -euo pipefail URL="https://oauth2:${PORTING_TOKEN}@git.freno.me/Mike/${OMP_REPO}.git" + # The omp checkout lives in $RUNNER_TEMP, outside the pi checkout: + # the port script refuses to write into a subdirectory of its own + # source (cpSync would recurse into itself). + PORT_DIR="${RUNNER_TEMP:-/tmp}/omp-port" if git ls-remote "$URL" HEAD >/dev/null 2>&1; then - git clone --depth 1 "$URL" omp-port - git -C omp-port config user.name "omp-port" - git -C omp-port config user.email "omp-port@freno.me" + git clone --depth 1 "$URL" "$PORT_DIR" + git -C "$PORT_DIR" config user.name "omp-port" + git -C "$PORT_DIR" config user.email "omp-port@freno.me" else - git init -b main omp-port - git -C omp-port remote add origin "$URL" - git -C omp-port config user.name "omp-port" - git -C omp-port config user.email "omp-port@freno.me" + git init -b main "$PORT_DIR" + git -C "$PORT_DIR" remote add origin "$URL" + git -C "$PORT_DIR" config user.name "omp-port" + git -C "$PORT_DIR" config user.email "omp-port@freno.me" fi # Regenerate the port directly into the omp checkout. The script # preserves .git, asserts every patch rule, and runs `bun install` # (refreshing bun.lock + node_modules). - bun "$GITHUB_WORKSPACE/port-to-omp.mjs" --out "$PWD/omp-port" + bun "$GITHUB_WORKSPACE/port-to-omp.mjs" --out "$PORT_DIR" - cd omp-port + cd "$PORT_DIR" # The port must compile against the pinned @oh-my-pi SDK before it # ships to users. bun run typecheck diff --git a/port-to-omp.mjs b/port-to-omp.mjs index fbfb705..e9234fe 100644 --- a/port-to-omp.mjs +++ b/port-to-omp.mjs @@ -21,10 +21,11 @@ import { mkdirSync, readdirSync, readFileSync, + realpathSync, rmSync, writeFileSync, } from "node:fs"; -import { join } from "node:path"; +import { join, relative, resolve, isAbsolute, dirname, basename } from "node:path"; import { execSync } from "node:child_process"; import { homedir } from "node:os"; @@ -113,7 +114,33 @@ function mirrorTree(srcDir, dstDir) { } } -function walk(dir) { +function real(p) { + try { + return realpathSync(p); + } catch { + // walk to the nearest existing ancestor and realpath it, then re-append + const tail = []; + let cur = resolve(p); + for (;;) { + try { + return join(realpathSync(cur), ...tail); + } catch {} + const parent = dirname(cur); + if (parent === cur) return resolve(p); + tail.unshift(basename(cur)); + cur = parent; + } + } +} + +function assertDstOutsideSrc(srcDir, dstDir) { + const rel = relative(real(srcDir), real(dstDir)); + if (rel === "" || (!rel.startsWith("..") && !isAbsolute(rel))) { + throw new Error( + `refusing to port into a subdirectory of the source: ${dstDir} is inside ${srcDir}` + ); + } +}function walk(dir) { const out = []; for (const entry of readdirSync(dir, { withFileTypes: true })) { if (SKIP.has(entry.name)) continue; @@ -262,6 +289,7 @@ function portExtension() { if (!dstDir) throw new Error("--out requires a directory argument"); if (!existsSync(srcDir)) throw new Error(`no base extension at ${srcDir}`); + assertDstOutsideSrc(srcDir, dstDir); console.log(`== ${srcDir} -> ${dstDir}`); mirrorTree(srcDir, dstDir);