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-usb-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 = p => JSON.parse(fs.readFileSync(p, "utf8"))
const sha = bytes => crypto.createHash("sha256").update(bytes).digest("hex")
const hash = p => sha(fs.readFileSync(p))
const manifestPath = "reports/usb-top-publish-manifest.json"
const action = process.argv[2] ?? "status"
const stable = value => JSON.stringify(value, (_, 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 records = c => c.filter(e => e.type !== "source_project_metadata").map(stable).sort()
async function request(endpoint, { body, write = false, timeout = 45000, missing = false } = {}) {
  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 = name => request("packages/get", { body: { name } }).then(r => r.package)
const releaseInfo = name => request("package_releases/get", { body: { package_name_with_version: name }, missing: true }).then(r => r?.package_release ?? null)
async function releaseFiles(releaseId) {
  return (await request("package_files/list?" + new URLSearchParams({ package_release_id: releaseId }))).package_files
}
async function fileBytes(file) {
  const f = (await request("package_files/get?" + new URLSearchParams({ package_file_id: file.package_file_id }))).package_file
  assert.ok(f.content_text != null || f.content_base64 != null, `missing bytes ${file.file_path}`)
  return f.content_text != null ? Buffer.from(f.content_text) : Buffer.from(f.content_base64, "base64")
}
if (action === "prepare") {
  const status = read("reports/usb-top-validation.json"), old = read("reports/stencil-publish-manifest.json")
  assert.equal(status.noRegressionsInCheckedScope, true)
  assert.equal(status.testsPassed, 47); assert.equal(status.testsFailed, 0)
  assert.equal(status.nativeErrors, 0); assert.equal(status.nativeWarnings, 151)
  assert.equal(status.fabricationReady, false)
  assert.equal(hash("dist/index/circuit.json"), status.circuitSha256)
  for (const [name, digest] of Object.entries(status.logSha256)) assert.equal(hash(`reports/usb-top-${name}.log`), digest, name)
  const pkg = await packageInfo(old.packageName)
  assert.equal(pkg.latest_version, "0.4.52"); assert.equal(pkg.latest_package_release_id, "8202c15e-c8aa-4593-863d-b2808543f948")
  const previous = await releaseInfo(`${old.packageName}@${pkg.latest_version}`)
  const previousFiles = await releaseFiles(previous.package_release_id)
  const previousCircuit = previousFiles.find(f => f.file_path === "dist/index/circuit.json")
  assert.ok(previousCircuit)
  const cloudBytes = await fileBytes(previousCircuit)
  const baseline = read("../../checkpoints/nema17-before-usb-top-v052-UkdpHy/restore-verified/dist/index/circuit.json")
  assert.deepEqual(records(JSON.parse(cloudBytes)), records(baseline), "live latest is exactly the reviewed baseline")
  const version = "0.4.53"
  assert.equal(await releaseInfo(`${old.packageName}@${version}`), null, "do not overwrite an existing numbered release")
  fs.writeFileSync("reports/usb-top-previous-cloud-circuit.json", cloudBytes)
  const comparison = { checkedAt: new Date().toISOString(), previousVersion: pkg.latest_version, previousReleaseId: previous.package_release_id,
    previousCloudCircuitSha256: sha(cloudBytes), baselineLocalCircuitSha256: status.baselineCircuitSha256,
    candidateCircuitSha256: status.circuitSha256, liveMatchesReviewedBaseline: true, noRegressionsInCheckedScope: true,
    nativeErrors: 0, nativeWarnings: 151, testsPassed: 47, fabricationReady: false, physicalDualPlugFitVerified: false }
  fs.writeFileSync("reports/usb-top-prepush-comparison.json", JSON.stringify(comparison, null, 2) + "\n")
  const selected = new Set(old.files.map(f => f.file))
  for (const file of ["usb-top-placement.json", "tests/usb-top-placement.test.ts", "routing/review-clock.json",
    "scripts/prepare-usb-top.py", "scripts/check-usb-top-seed.py", "scripts/install-usb-top.mjs", "scripts/check-usb-top-records.mjs",
    "scripts/check-usb-top-protected-copper.py", "scripts/validate-usb-top.mjs", "scripts/finalize-usb-top.mjs", "scripts/publish-usb-top.mjs",
    "reports/usb-top-validation.json", "reports/usb-top-record-regression.json", "reports/usb-top-protected-copper.json", "reports/usb-top-prepush-comparison.json",
    ...Object.keys(status.logSha256).map(name => `reports/usb-top-${name}.log`)]) selected.add(file)
  const destination = fs.mkdtempSync(path.resolve("../../publish-staging/nema17-usb-top-v053-"))
  for (const file of selected) {
    assert.ok(!file.includes("..") && !/(?:^|\/)(?:node_modules|\.env|\.npmrc|\.netrc)(?:\/|$)/.test(file))
    const bytes = fs.readFileSync(file)
    assert.ok(!bytes.includes(0) && Buffer.from(bytes.toString("utf8")).equals(bytes), `text source ${file}`)
    const to = path.join(destination, file)
    fs.mkdirSync(path.dirname(to), { recursive: true }); fs.copyFileSync(file, to)
  }
  const write = (file, data) => fs.writeFileSync(path.join(destination, file), JSON.stringify(data, null, 2) + "\n")
  const metadata = read("package.json"); metadata.version = version; write("package.json", metadata)
  const lock = read("package-lock.json"); lock.version = version; if (lock.packages?.[""]) lock.packages[""].version = version; write("package-lock.json", lock)
  let readme = fs.readFileSync("README.md", "utf8")
  readme = readme.replace("### Unpublished local USB-top revision", `### Release ${version}: both USB-C ports on top`)
  readme = readme.replace("This revision has not been pushed.", `Release \`${version}\` contains this revision; website-build verification is recorded separately in the publication receipt.`)
  readme = readme.replace("Release `0.4.52` improves", "Previous release `0.4.52` improved")
  readme = readme.replace("The unchanged USB-C connector types share the north edge: DATA on top, PD on\nthe reverse.", "The unchanged USB-C connector types share the north edge, both on top, at 14 mm center spacing. Simultaneous fit of actual cable overmoulds is not yet physically verified.")
  fs.writeFileSync(path.join(destination, "README.md"), readme)
  const verification = read("verification.json"); verification.releaseVersion = version; verification.localChecksPassed = true; verification.fabricationReady = false; write("verification.json", verification)
  fs.writeFileSync(path.join(destination, "CHECKPOINT_SAVED.md"), `# Validated USB-top revision\n\nRelease ${version}. Audited circuit SHA-256: ${status.circuitSha256}.\nRestore-verified checkpoint: nema17-usb-top-validated-20261021-aAfnto.\n47 tests pass, native errors 0, unchanged 151 warnings. No regression found in checked scope; not fabrication-approved.\nActual dual cable-plug fit and existing qualification gaps remain open.\n`)
  assert.equal(hash(path.join(destination, "tscircuit.config.json")), hash("tscircuit.config.json"))
  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: old.packageName, version,
    previous: { version: pkg.latest_version, releaseId: pkg.latest_package_release_id }, circuitSha256: status.circuitSha256,
    fabricationReady: false, files, cloudBuildConfigUnchanged: true }
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n")
  console.log(JSON.stringify({ version, destination, files: files.length, totalBytes: files.reduce((n, f) => n + f.bytes, 0), noRegressionsInCheckedScope: true, fabricationReady: false }))
} else {
  const manifest = read(manifestPath), fullName = `${manifest.packageName}@${manifest.version}`
  let release = await releaseInfo(fullName)
  let pkg = await packageInfo(manifest.packageName)
  assert.ok([manifest.previous.releaseId, release?.package_release_id].includes(pkg.latest_package_release_id), "stop for unrelated concurrent latest")
  if (action === "hold" || action === "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 === "upload") {
    if (!release) {
      release = (await request("package_releases/create", { write: true, body: { package_name_with_version: fullName } })).package_release
      await request("package_releases/update", { write: true, body: { package_release_id: manifest.previous.releaseId, is_latest: true } })
    }
    const files = manifest.files.map(file => {
      const bytes = fs.readFileSync(path.join(manifest.destination, file.file))
      assert.equal(sha(bytes), file.sha256, file.file)
      assert.ok(!bytes.includes(0) && Buffer.from(bytes.toString("utf8")).equals(bytes), file.file)
      return { file_path: file.file, content_text: bytes.toString("utf8") }
    })
    const archive = gzipSync(JSON.stringify({ files }), { level: 9 })
    console.log(JSON.stringify({ phase: "uploading-exact-frozen-release", version: manifest.version, releaseId: release.package_release_id, files: files.length, gzipBytes: archive.length }))
    const result = await request("package_files/upload_archive", { write: true, timeout: 180000, body: { package_name_with_version: fullName, archive_base64: archive.toString("base64") } })
    fs.writeFileSync("reports/usb-top-upload-response.json", JSON.stringify({ checkedAt: new Date().toISOString(), result }, null, 2) + "\n")
    console.log(JSON.stringify({ phase: "uploaded", files: files.length }))
  }
  if (action === "verify" || action === "promote") {
    assert.ok(release)
    const byName = new Map((await releaseFiles(release.package_release_id)).map(f => [f.file_path, f]))
    const pending = [...manifest.files], checked = []
    await Promise.all(Array.from({ length: 4 }, async () => {
      while (pending.length) {
        const file = pending.shift(), entry = byName.get(file.file)
        assert.ok(entry, `uploaded ${file.file}`)
        assert.equal(sha(await fileBytes(entry)), file.sha256, `uploaded bytes ${file.file}`); checked.push(file.file)
      }
    }))
    release = await releaseInfo(fullName)
    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 be complete")
    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(f => [f.file_path, f]))
    assert.ok(outputs.has("dist/index/circuit.json"), "actual website-generated circuit")
    const bytes = await fileBytes(outputs.get("dist/index/circuit.json")), cloud = JSON.parse(bytes), local = read("dist/index/circuit.json")
    assert.equal(hash("dist/index/circuit.json"), manifest.circuitSha256)
    assert.deepEqual(records(cloud), records(local), "all cloud electrical, PCB, schematic, CAD, paste and warning records match audited local design")
    assert.deepEqual(cloud.filter(e => e.type.endsWith("_error")), [])
    assert.equal(cloud.filter(e => e.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/usb-top-website-circuit.json", bytes)
    if (action === "promote") {
      const current = await packageInfo(manifest.packageName)
      assert.ok([manifest.previous.releaseId, release.package_release_id].includes(current.latest_package_release_id), "stop for a concurrent latest change during verification")
      await request("package_releases/update", { write: true, body: { package_release_id: release.package_release_id, is_latest: true } })
    }
    pkg = await packageInfo(manifest.packageName)
    if (action === "promote") assert.equal(pkg.latest_package_release_id, release.package_release_id)
    const report = { checkedAt: new Date().toISOString(), packageName: manifest.packageName, version: manifest.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: 47, noRegressionsInCheckedScope: true, fabricationReady: false, physicalDualPlugFitVerified: false,
      url: `https://tscircuit.com/${manifest.packageName}?version=${manifest.version}#3d` }
    fs.writeFileSync("reports/usb-top-publication.json", JSON.stringify(report, null, 2) + "\n")
    console.log(JSON.stringify(report))
  } else {
    release = await releaseInfo(fullName)
    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
    pkg = await packageInfo(manifest.packageName)
    console.log(JSON.stringify({ version: manifest.version, releaseId: release?.package_release_id, readyToBuild: release?.ready_to_build, latestVersion: pkg.latest_version, build }))
  }
}