ShiboSoftwareDev/linux-gameboy-advance
Multilayer PCB carrier board integrating USB‑C, battery charging/power management, buck‑boost regulation, MCU/SoC, LCD/SD interfaces, audio controls, status LEDs, backlight, and UART/I²C/SPI service connectors with reviewed multilayer copper and vias.
- Version
- 1.0.134
- License
- unset
- Stars
- 0
scripts/cloud-artifact-sources.mjs
import { createHash } from "node:crypto"
import {
readFileSync,
readdirSync,
statSync,
} from "node:fs"
import { join, relative, resolve, sep } from "node:path"
const sourceInputs = [
"index.circuit.tsx",
"carrier-router.ts",
"carrier-routes-v2.json",
"backlight-routes.json",
"assembly-parts.json",
"mechanical",
"scripts/render-manual-enclosure.mjs",
"tscircuit.config.json",
"assets/er-tft035ips-6-oem-installed-v5.glb",
"assets/sod-123fl.glb",
"footprints",
"handheld-imports",
"imports",
"modules/f1c100s/imports",
"modules/f1c100s/src",
"schematic",
"subcircuits",
]
const collectFiles = (path) => {
const stats = statSync(path)
if (stats.isFile()) return [path]
return readdirSync(path, { withFileTypes: true })
// Generated Circuit JSON/metrics/route caches are reproducible outputs,
// not build inputs. Excluding them keeps the freshness manifest identical
// in the lean registry package, where those multi-megabyte caches are
// intentionally omitted.
.filter(
(entry) =>
!entry.name.startsWith(".") &&
entry.name !== "generated" &&
!entry.name.endsWith(".step"),
)
.sort((a, b) => a.name.localeCompare(b.name))
.flatMap((entry) => collectFiles(join(path, entry.name)))
}
export const sha256File = (path) =>
createHash("sha256").update(readFileSync(path)).digest("hex")
const sha256Text = (text) =>
createHash("sha256").update(text).digest("hex")
const sortObject = (value) => {
if (Array.isArray(value)) return value.map(sortObject)
if (!value || typeof value !== "object") return value
return Object.fromEntries(
Object.entries(value)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, child]) => [key, sortObject(child)]),
)
}
export const getCloudArtifactSourceState = (projectDirectory) => {
const root = resolve(projectDirectory)
const packageJsonPath = resolve(root, "package.json")
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"))
const files = sourceInputs
.flatMap((input) => collectFiles(resolve(root, input)))
.sort()
const dependencyDeclarations = sortObject({
dependencies: packageJson.dependencies ?? {},
devDependencies: packageJson.devDependencies ?? {},
optionalDependencies: packageJson.optionalDependencies ?? {},
peerDependencies: packageJson.peerDependencies ?? {},
})
const sourceFiles = files.map((path) => ({
path: relative(root, path).split(sep).join("/"),
sha256: sha256File(path),
mtimeMs: statSync(path).mtimeMs,
})).concat({
// The registry increments package.json.version during `tsci push`, and Bun
// may rewrite bun.lock during its cloud install. Hash only the immutable
// dependency declarations so those expected mutations cannot make a fresh
// board artifact appear stale.
path: "package.json#dependencies",
sha256: sha256Text(JSON.stringify(dependencyDeclarations)),
// package.json's mtime also changes when `tsci push` increments only its
// version, so it cannot be used as a reliable build-staleness timestamp.
mtimeMs: 0,
}).sort((left, right) => left.path.localeCompare(right.path))
const combinedHash = createHash("sha256")
for (const file of sourceFiles) {
combinedHash.update(file.path)
combinedHash.update("\0")
combinedHash.update(file.sha256)
combinedHash.update("\n")
}
return {
sourceSha256: combinedHash.digest("hex"),
newestSourceMtimeMs: Math.max(...sourceFiles.map((file) => file.mtimeMs)),
sourceFiles: sourceFiles.map(({ path, sha256 }) => ({ path, sha256 })),
}
}