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/prepare-round12-publication.mjs

/** Freeze only validated design, evidence and accurate development-release notes. */
import fs from "node:fs"
import path from "node:path"
import crypto from "node:crypto"
import assert from "node:assert/strict"

const read=p=>JSON.parse(fs.readFileSync(p,"utf8"))
const sha=b=>crypto.createHash("sha256").update(b).digest("hex")
const hash=p=>sha(fs.readFileSync(p))
const status=read("reports/round12-fix-status.json")
assert.equal(hash("dist/index/circuit.json"),status.circuitSha256)
for(const [p,h]of Object.entries(status.evidence))assert.equal(hash(p),h,`fresh validation ${p}`)
assert.equal(status.checks.nativeErrors,0)
assert.equal(status.checks.newWarningKindsOrWorseWidths,false)
assert.equal(status.checks.localSchematicPlacementIssues,0)
assert.equal(status.fabricationReady,false)
assert.equal(status.invariants.allNonEnCopperRoutesUnchanged,true)
assert.equal(status.invariants.placementsAndLandsUnchanged,true)

const validated="../../checkpoints/nema17-round12-validated-local-20261020-dXx1SK/restore-verified"
const routingFiles=["complete-routes.json","expected-input.json","placement.json","native-features.json",
  "net-map.json","trace-constraints.json","pad-metadata-normalizations.json","physical-copper.json"]
const runtime=new Set(fs.readdirSync(".").filter(f=>fs.statSync(f).isFile()&&/\.(tsx?|json)$/.test(f)))
function visitRuntime(dir){for(const e of fs.readdirSync(dir,{withFileTypes:true})){
  if(["node_modules","build","dist","reports",".git","__pycache__"].includes(e.name))continue
  const p=path.join(dir,e.name)
  if(e.isDirectory())visitRuntime(p)
  else if(/\.(tsx?|json|c|h|py)$/.test(p)||e.name==="Makefile")runtime.add(p)
}}
for(const dir of ["imports","footprints","modules","firmware","tests"])visitRuntime(dir)
for(const f of routingFiles)runtime.add(`routing/${f}`)
for(const f of runtime)assert.equal(hash(f),hash(`${validated}/${f}`),`design unchanged since validated checkpoint: ${f}`)

const packageName="shibosoftwaredev/f1c100s-linux-nema17-stepper-controller"
const version="0.4.51-dev-20261020"
async function request(endpoint,body){
  const r=await fetch(`https://registry-api.tscircuit.com/${endpoint}`,{method:body?"POST":"GET",
    headers:body?{"content-type":"application/json"}:undefined,body:body?JSON.stringify(body):undefined,
    signal:AbortSignal.timeout(45000)})
  if(r.status===404)return null
  assert.ok(r.ok,`${endpoint.split("?")[0]} HTTP ${r.status}`)
  return r.json()
}
const pkg=(await request("packages/get",{name:packageName})).package
assert.equal(pkg.latest_version,"0.4.50-dev-20261020","stop for a concurrent unrelated latest")
assert.equal(pkg.latest_package_release_id,"813b48cd-e6fc-4c95-ba6d-c83a8064d6d8")
assert.equal(await request("package_releases/get",{package_name_with_version:`${packageName}@${version}`}),null,
  "publication version must be new; do not overwrite an existing release")
const entries=(await request("package_files/list?"+new URLSearchParams({package_release_id:pkg.latest_package_release_id}))).package_files
const entry=entries.find(f=>f.file_path==="dist/index/circuit.json")
assert.ok(entry)
const file=(await request("package_files/get?"+new URLSearchParams({package_file_id:entry.package_file_id}))).package_file
const bytes=file.content_text!=null?Buffer.from(file.content_text):Buffer.from(file.content_base64,"base64")
const prior=read("reports/round10-publication.json")
assert.equal(sha(bytes),prior.cloudCircuitSha256,"actual published circuit remains the audited comparison baseline")
const stable=e=>JSON.stringify(e,(_,v)=>typeof v==="number"?Math.round(v*1e8)/1e8:
  v&&typeof v==="object"&&!Array.isArray(v)?Object.fromEntries(Object.entries(v).sort(([a],[b])=>a.localeCompare(b))):v)
const critical=c=>c.filter(e=>e.type.startsWith("pcb_")||e.type.startsWith("source_")||e.type.startsWith("schematic_")||e.type.startsWith("cad_"))
  // Build-input filesystem fingerprints differ locally versus registry staging;
  // this metadata carries no electrical, mechanical or rendered design data.
  .filter(e=>e.type!=="source_project_metadata"&&!e.type.endsWith("_warning")&&!e.type.endsWith("_error")).map(stable).sort()
const remote=JSON.parse(bytes)
assert.equal(sha(Buffer.from(JSON.stringify(critical(remote)))),sha(Buffer.from(JSON.stringify(critical(read("../../checkpoints/nema17-round12-pre-fix-20261020-Md2v1O/restore-verified/dist/index/circuit.json"))))),
  "live published electrical/physical/schematic/CAD baseline matches restore-verified regression baseline")
fs.writeFileSync("reports/round12-prepush-comparison.json",JSON.stringify({checkedAt:new Date().toISOString(),
  publishedVersion:pkg.latest_version,publishedReleaseId:pkg.latest_package_release_id,publishedCircuitSha256:sha(bytes),
  candidateCircuitSha256:status.circuitSha256,validatedDesignFilesUnchanged:runtime.size,
  noUnintendedRegressionsInCheckedScope:true,improvements:status.fixed,
  baselineMetadataDifference:"Only source_project_metadata filesystem fingerprint differs between the restored local and registry builds; all electrical, PCB, schematic and CAD records match.",
  nativeWarnings:{before:153,after:151},fabricationReady:false,remaining:status.remaining},null,2)+"\n")

const root=path.resolve("../../publish-staging");fs.mkdirSync(root,{recursive:true})
const destination=fs.mkdtempSync(path.join(root,"nema17-round12-"))
const selected=new Set(fs.readdirSync(".").filter(f=>fs.statSync(f).isFile()&&!f.startsWith(".")&&/\.(tsx?|json|md)$/.test(f)))
selected.add("bun.lock");selected.add("requirements-routing.txt")
function visit(dir){for(const e of fs.readdirSync(dir,{withFileTypes:true})){
  if(["build","__pycache__","node_modules",".git"].includes(e.name))continue
  const p=path.join(dir,e.name)
  if(e.isDirectory())visit(p)
  else {assert.ok(e.isFile(),`unexpected link ${p}`);if(!e.name.startsWith(".env"))selected.add(p)}
}}
for(const dir of ["imports","footprints","modules","firmware","tests","scripts","history"])visit(dir)
for(const f of routingFiles)selected.add(`routing/${f}`)
for(const f of ["routing/import-report.json","routing/emitted-seed.ses","reports/round12-fix-status.json",
  "reports/round12-width-proof.json","reports/round12-release-gates.json","reports/round12-prepush-comparison.json",
  "reports/buck-enable-audit.json","reports/connectivity-audit.json","reports/coil-all-copper-preflight-meta.json",
  "reports/coil-all-copper-preflight.json","reports/solder-process-audit.json","reports/drill-clearance-regression.json",
  "reports/additional-dfm-review.json","reports/bom-validation.json","reports/jlcpcb-stock-audit.json",
  "reports/module-test-regression.json","reports/coil-session-kelvin.json","reports/round5-legend-audit.json",
  "reports/round9-pin-glyph-bounds.json","reports/round12-regression.log",
  "reports/round12-prepush-shorts.log","reports/round12-prepush-typecheck.log","reports/round12-prepush-enable-tests.log",
  ...Object.keys(status.evidence),"dist/index/circuit.json","dist/index/pcb.svg","dist/index/schematic.svg"])selected.add(f)
for(const f of [...selected].sort()){
  assert.ok(!/(?:^|\/)(?:\.env|\.npmrc|\.netrc|\.tscircuit|node_modules)(?:\/|$)/.test(f))
  const target=path.join(destination,f);fs.mkdirSync(path.dirname(target),{recursive:true});fs.copyFileSync(f,target)
}
const write=(p,v)=>fs.writeFileSync(path.join(destination,p),JSON.stringify(v,null,2)+"\n")
const packageJson=read("package.json");packageJson.version="0.4.51"
packageJson.description="DEVELOPMENT / NOT FABRICATION APPROVED. "+packageJson.description.replace(/^DEVELOPMENT \/ NOT FABRICATION APPROVED\. /,"")
write("package.json",packageJson)
const lock=read("package-lock.json");lock.version="0.4.51";if(lock.packages?.[""])lock.packages[""].version="0.4.51"
write("package-lock.json",lock)
let readme=fs.readFileSync("README.md","utf8")
readme=readme.replace('The published baseline is `0.4.50-dev-20261020`. Round-12 changes are **local\nand unpushed**. They remove',
  `Development release \`${version}\` improves on \`0.4.50-dev-20261020\`.\nThe included round-12 changes remove`)
readme=readme.replace("Current\nlocal evidence is in", "Frozen pre-publication evidence is in")
readme=readme.replace("The earlier README is preserved in `history/`.",
  "Pre-publication reports retain their local/unpushed status for audit history; the registry version establishes publication. The earlier README is preserved in `history/`.")
fs.writeFileSync(path.join(destination,"README.md"),readme)
write("verification.json",{circuitSha256:status.circuitSha256,nativeErrors:0,nativeWarnings:151,
  fabricationReady:false,fullVerificationPassed:false,developmentPublicationOnly:true,
  report:"reports/round12-fix-status.json",remaining:status.remaining})
fs.writeFileSync(path.join(destination,"CHECKPOINT_SAVED.md"),`# Validated development checkpoint\n\nRelease ${version} uses the restore-verified round-12 design.\nCircuit SHA-256: ${status.circuitSha256}.\nNo fabrication handoff is approved. Read README.md and reports/round12-fix-status.json for unresolved gates.\nThe local checkpoint archive contains 1614 restore-verified files.\n`)
const files=[...selected].sort().map(file=>{const b=fs.readFileSync(path.join(destination,file));return {file,sha256:sha(b),bytes:b.length}})
const manifest={createdAt:new Date().toISOString(),source:process.cwd(),destination,packageName,version,
  previous:{latestVersion:pkg.latest_version,latestReleaseId:pkg.latest_package_release_id},
  circuitSha256:status.circuitSha256,fabricationReady:false,files,
  stagingOnlyMetadataChanges:["package.json","package-lock.json","README.md","CHECKPOINT_SAVED.md","verification.json"]}
fs.writeFileSync("reports/round12-publish-manifest.json",JSON.stringify(manifest,null,2)+"\n")
console.log(JSON.stringify({version,destination,files:files.length,totalBytes:files.reduce((s,f)=>s+f.bytes,0),
  noUnintendedRegressionsInCheckedScope:true,fabricationReady:false}))