shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.
- Version
- 0.4.56
- License
- unset
- Stars
- 0
scripts/publish-bulk-top.mjs
import fs from "node:fs"
import path from "node:path"
import os from "node:os"
import crypto from "node:crypto"
import assert from "node:assert/strict"
import { gzipSync } from "node:zlib"
const read = file => JSON.parse(fs.readFileSync(file, "utf8"))
const sha = bytes => crypto.createHash("sha256").update(bytes).digest("hex")
const hash = file => sha(fs.readFileSync(file))
const manifestFile = "reports/bulk-top-publish-manifest.json"
const action = process.argv[2] ?? "status"
const packageName = "shibosoftwaredev/f1c100s-linux-nema17-stepper-controller"
const version = "0.4.54"
const stable = value => JSON.stringify(value, (_, item) => typeof item === "number" ? Math.round(item * 1e8) / 1e8 :
item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b))) : item)
const records = circuit => circuit.filter(item => item.type !== "source_project_metadata").map(stable).sort()
async function request(endpoint, options = {}) {
const { body, write = false, missing = false, timeout = 45000 } = options
const token = write ? (process.env.TSCIRCUIT_TOKEN ?? read(path.join(os.homedir(), "Library/Preferences/tscircuit-nodejs/config.json")).sessionToken) : null
const response = await fetch(`https://registry-api.tscircuit.com/${endpoint}`, {
method: body ? "POST" : "GET",
headers: { ...(body ? { "content-type": "application/json" } : {}), ...(token ? { authorization: `Bearer ${token}` } : {}) },
body: body ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(timeout),
})
if (missing && response.status === 404) return null
assert.ok(response.ok, `${endpoint.split("?")[0]} HTTP ${response.status}`)
return response.json()
}
const packageInfo = () => request("packages/get", { body: { name: packageName } }).then(result => result.package)
const releaseInfo = releaseVersion => request("package_releases/get", { body: { package_name_with_version: `${packageName}@${releaseVersion}` }, missing: true }).then(result => result?.package_release ?? null)
const releaseFiles = releaseId => request("package_files/list?" + new URLSearchParams({ package_release_id: releaseId })).then(result => result.package_files)
async function fileBytes(entry) {
const file = (await request("package_files/get?" + new URLSearchParams({ package_file_id: entry.package_file_id }))).package_file
assert.ok(file.content_text != null || file.content_base64 != null, entry.file_path)
return file.content_text != null ? Buffer.from(file.content_text) : Buffer.from(file.content_base64, "base64")
}
function walk(directory) {
return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => entry.isDirectory() ? walk(path.join(directory, entry.name)) : [path.join(directory, entry.name)])
}
if (action === "prepare") {
const acceptance = read("reports/bulk-top-review-acceptance.json")
assert.equal(acceptance.status, "local_hardware_and_schematic_regression_gates_passed")
assert.equal(hash("dist/index/circuit.json"), acceptance.circuitSha256)
for (const [file, digest] of Object.entries(acceptance.reports)) assert.equal(hash(file), digest, file)
assert.equal(acceptance.testsPassed, 54); assert.equal(acceptance.testsFailed, 0)
assert.equal(acceptance.schematicStyleIssues, 0); assert.equal(acceptance.addedWarnings, 0)
assert.equal(acceptance.fabricationReady, false)
const previous = await packageInfo()
assert.equal(previous.latest_version, "0.4.53")
assert.equal(previous.latest_package_release_id, "e720f67b-aa7b-4662-8379-4a3c2dd391ea")
assert.equal(await releaseInfo(version), null, "Never overwrite an existing release")
const baselineEntry = (await releaseFiles(previous.latest_package_release_id)).find(file => file.file_path === "dist/index/circuit.json")
assert.ok(baselineEntry)
const cloudBaseline = JSON.parse(await fileBytes(baselineEntry))
const baseline = read("../../checkpoints/nema17-before-rishabh-review-v053-DuFOGi/restore-verified/dist/index/circuit.json")
assert.deepEqual(records(cloudBaseline), records(baseline), "Live latest must match the audited baseline")
const selected = new Set(read("reports/usb-top-publish-manifest.json").files.map(file => file.file))
for (const file of ["schematic-guide.tsx", "review-placement.json", "routing/review-emitted-physical.json", "routing/bulk-top-emitted-physical.json",
"reports/bulk-top-review-acceptance.json", "reports/bulk-top-regression.json", "reports/review-protected-copper.json",
"reports/review-style/analysis.json", "reports/swap-validation.log", ...Object.keys(acceptance.reports), ...walk("scripts"), ...walk("tests"),
...fs.readdirSync("reports").filter(file => /^review-.*\.log$/.test(file)).map(file => `reports/${file}`)]) selected.add(file)
const destination = fs.mkdtempSync(path.resolve("../../publish-staging/nema17-bulk-top-v054-"))
for (const file of selected) {
assert.ok(!file.includes("..") && !/(?:^|\/)(?:node_modules|\.env|\.npmrc|\.netrc|__pycache__)(?:\/|$)/.test(file), file)
const bytes = fs.readFileSync(file)
assert.ok(!bytes.includes(0) && Buffer.from(bytes.toString("utf8")).equals(bytes), `text-only source: ${file}`)
const target = path.join(destination, file)
fs.mkdirSync(path.dirname(target), { recursive: true }); fs.copyFileSync(file, target)
}
for (const name of ["package.json", "package-lock.json"]) {
const metadata = read(name); metadata.version = version
if (metadata.packages?.[""]) metadata.packages[""].version = version
fs.writeFileSync(path.join(destination, name), JSON.stringify(metadata, null, 2) + "\n")
}
let readme = fs.readFileSync("README.md", "utf8")
readme = readme.replace("UNPUBLISHED LOCAL HARDWARE REVISION (22 October 2026), based on 0.4.53.", "RELEASE 0.4.54 (22 October 2026), based on 0.4.53.")
readme = readme.replace("### Unpublished bulk-capacitor and schematic review (22 October 2026)", "### Release 0.4.54: top-side bulk capacitor and explained schematic")
readme = readme.replace("recorded in `reports/bulk-top-review-acceptance.json`.", "recorded in `reports/bulk-top-review-acceptance.json`. Its unpublished flag records the pre-upload validation state; publication is verified separately.")
fs.writeFileSync(path.join(destination, "README.md"), readme)
fs.writeFileSync(path.join(destination, "CHECKPOINT_SAVED.md"), `# Validated bulk-capacitor and schematic revision\n\nRelease ${version}. Audited circuit SHA256: ${acceptance.circuitSha256}.\nRestore-verified checkpoint: nema17-bulk-top-validated-H2lBGy.\n54 tests pass, zero native errors / shorts / style issues, no added warnings.\n151 inherited warnings remain. Not fabrication-qualified; see verification.json.\n`)
for (const [file, digest] of Object.entries(acceptance.reports)) assert.equal(hash(path.join(destination, file)), digest, file)
const files = [...selected].sort().map(file => ({ file, sha256: hash(path.join(destination, file)), bytes: fs.statSync(path.join(destination, file)).size }))
const manifest = { createdAt: new Date().toISOString(), source: process.cwd(), destination, packageName, version,
previous: { version: previous.latest_version, releaseId: previous.latest_package_release_id }, circuitSha256: acceptance.circuitSha256,
acceptanceSha256: hash("reports/bulk-top-review-acceptance.json"), fabricationReady: false, files }
fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 2) + "\n")
console.log(JSON.stringify({ version, destination, files: files.length, bytes: files.reduce((total, file) => total + file.bytes, 0), fabricationReady: false }))
} else {
const manifest = read(manifestFile)
let release = await releaseInfo(manifest.version), pkg = await packageInfo()
assert.ok([manifest.previous.releaseId, release?.package_release_id].includes(pkg.latest_package_release_id), "Stop for an unrelated concurrent update")
if (action === "hold" || action === "recover-upload") {
if (pkg.latest_package_release_id !== manifest.previous.releaseId) await request("package_releases/update", { write: true, body: { package_release_id: manifest.previous.releaseId, is_latest: true } })
}
if (action === "recover-upload") {
assert.ok(release, "Recovery resumes the same release created by tsci push")
const files = manifest.files.map(file => {
const bytes = fs.readFileSync(path.join(manifest.destination, file.file)); assert.equal(sha(bytes), file.sha256, file.file)
return { file_path: file.file, content_text: bytes.toString("utf8") }
})
const archive = gzipSync(JSON.stringify({ files }), { level: 9 })
console.log(JSON.stringify({ uploading: true, releaseId: release.package_release_id, files: files.length, gzipBytes: archive.length }))
await request("package_files/upload_archive", { write: true, timeout: 240000, body: { package_name_with_version: `${packageName}@${version}`, archive_base64: archive.toString("base64") } })
await request("package_releases/update", { write: true, body: { package_release_id: release.package_release_id, ready_to_build: true } })
}
if (action === "verify" || action === "promote") {
assert.ok(release)
const entries = new Map((await releaseFiles(release.package_release_id)).map(file => [file.file_path, file]))
const pending = [...manifest.files], checked = []
await Promise.all(Array.from({ length: 4 }, async () => {
while (pending.length) {
const file = pending.shift(), entry = entries.get(file.file)
assert.ok(entry, file.file); assert.equal(sha(await fileBytes(entry)), file.sha256, file.file); checked.push(file.file)
}
}))
release = await releaseInfo(version)
const build = release.latest_package_build_id ? (await request("package_builds/get?" + new URLSearchParams({ package_build_id: release.latest_package_build_id }))).package_build : null
assert.ok(build?.user_code_job_completed_at && !build.build_in_progress, "Website build must finish")
assert.ok(!build.user_code_job_error && !build.circuit_json_build_error && !build.transpilation_error && !build.image_generation_error, "Website build must succeed")
const outputs = new Map((await releaseFiles(release.package_release_id)).map(file => [file.file_path, file]))
assert.ok(outputs.has("dist/index/circuit.json"))
const bytes = await fileBytes(outputs.get("dist/index/circuit.json")), cloud = JSON.parse(bytes)
assert.equal(hash("dist/index/circuit.json"), manifest.circuitSha256)
assert.deepEqual(records(cloud), records(read("dist/index/circuit.json")), "Website-generated board must match the audited local board")
assert.deepEqual(cloud.filter(item => item.type.endsWith("_error")), [])
assert.equal(cloud.filter(item => item.type.endsWith("_warning")).length, 151)
for (const file of ["dist/index/pcb.svg", "dist/index/schematic.svg", "dist/index/3d.png", "dist/index.html"]) assert.ok(outputs.has(file), file)
fs.writeFileSync("reports/bulk-top-website-circuit.json", bytes)
if (action === "promote") {
pkg = await packageInfo()
assert.ok([manifest.previous.releaseId, release.package_release_id].includes(pkg.latest_package_release_id))
await request("package_releases/update", { write: true, body: { package_release_id: release.package_release_id, is_latest: true } })
}
pkg = await packageInfo()
if (action === "promote") assert.equal(pkg.latest_package_release_id, release.package_release_id)
const report = { checkedAt: new Date().toISOString(), version, releaseId: release.package_release_id, buildId: release.latest_package_build_id,
buildCompletedAt: build.user_code_job_completed_at, sourceFilesVerified: checked.length, localCircuitSha256: manifest.circuitSha256,
cloudCircuitSha256: sha(bytes), cloudMatchesAuditedBoard: true, currentLatest: pkg.latest_version,
latestVerified: pkg.latest_package_release_id === release.package_release_id, nativeErrors: 0, nativeWarnings: 151,
testsPassed: 54, schematicStyleIssues: 0, noRegressionsInCheckedScope: true, fabricationReady: false,
url: `https://tscircuit.com/${packageName}?version=${version}#schematic` }
fs.writeFileSync("reports/bulk-top-publication.json", JSON.stringify(report, null, 2) + "\n")
console.log(JSON.stringify(report))
} else {
release = await releaseInfo(version); pkg = await packageInfo()
const build = release?.latest_package_build_id ? (await request("package_builds/get?" + new URLSearchParams({ package_build_id: release.latest_package_build_id }))).package_build : null
console.log(JSON.stringify({ version, releaseId: release?.package_release_id, readyToBuild: release?.ready_to_build, latestVersion: pkg.latest_version, build }))
}
}