Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
614b1858cb
|
|||
|
45aae46925
|
|||
|
d47007d905
|
|||
|
6daadc97fc
|
|||
|
10c7092313
|
|||
|
4973e1e52c
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hamradio/meshcore",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.2",
|
||||
"description": "MeshCore protocol support for Typescript",
|
||||
"keywords": [
|
||||
"MeshCore",
|
||||
|
||||
115
scripts/release.js
Executable file
115
scripts/release.js
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env node
|
||||
// Minimal safe release script.
|
||||
// Usage: node scripts/release.js [major|minor|patch|<version>]
|
||||
const { execSync } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const root = path.resolve(__dirname, "..");
|
||||
const pkgPath = path.join(root, "package.json");
|
||||
|
||||
function run(cmd, opts = {}) {
|
||||
return execSync(cmd, { stdio: "inherit", cwd: root, ...opts });
|
||||
}
|
||||
function runOutput(cmd) {
|
||||
return execSync(cmd, { cwd: root }).toString().trim();
|
||||
}
|
||||
function bumpSemver(current, spec) {
|
||||
if (["major","minor","patch"].includes(spec)) {
|
||||
const [maj, min, patch] = current.split(".").map(n=>parseInt(n,10));
|
||||
if (spec==="major") return `${maj+1}.0.0`;
|
||||
if (spec==="minor") return `${maj}.${min+1}.0`;
|
||||
return `${maj}.${min}.${patch+1}`;
|
||||
}
|
||||
if (!/^\d+\.\d+\.\d+$/.test(spec)) throw new Error("Invalid version spec");
|
||||
return spec;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const arg = process.argv[2] || "patch";
|
||||
const pkgRaw = fs.readFileSync(pkgPath, "utf8");
|
||||
const pkg = JSON.parse(pkgRaw);
|
||||
const oldVersion = pkg.version;
|
||||
const newVersion = bumpSemver(oldVersion, arg);
|
||||
let committed = false;
|
||||
let tagged = false;
|
||||
let pushedTags = false;
|
||||
try {
|
||||
// refuse to run if there are unstaged/uncommitted changes
|
||||
const status = runOutput("git status --porcelain");
|
||||
if (status) throw new Error("Repository has uncommitted changes; please commit or stash before releasing.");
|
||||
|
||||
console.log("Running tests...");
|
||||
run("npm run test:ci");
|
||||
|
||||
console.log("Building...");
|
||||
run("npm run build");
|
||||
|
||||
// write new version
|
||||
pkg.version = newVersion;
|
||||
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
||||
console.log(`Bumped version: ${oldVersion} -> ${newVersion}`);
|
||||
|
||||
// commit
|
||||
run(`git add ${pkgPath}`);
|
||||
run(`git commit -m "chore(release): v${newVersion} - bump from v${oldVersion}"`);
|
||||
committed = true;
|
||||
|
||||
// ensure tag doesn't already exist locally
|
||||
let localTagExists = false;
|
||||
try {
|
||||
runOutput(`git rev-parse --verify refs/tags/v${newVersion}`);
|
||||
localTagExists = true;
|
||||
} catch (_) {
|
||||
localTagExists = false;
|
||||
}
|
||||
if (localTagExists) throw new Error(`Tag v${newVersion} already exists locally — aborting to avoid overwrite.`);
|
||||
|
||||
// ensure tag doesn't exist on remote
|
||||
const remoteTagInfo = (() => {
|
||||
try { return runOutput(`git ls-remote --tags origin v${newVersion}`); } catch (_) { return ""; }
|
||||
})();
|
||||
if (remoteTagInfo) throw new Error(`Tag v${newVersion} already exists on remote — aborting to avoid overwrite.`);
|
||||
|
||||
// tag
|
||||
run(`git tag -a v${newVersion} -m "Release v${newVersion}"`);
|
||||
tagged = true;
|
||||
|
||||
// push commit and tags
|
||||
run("git push");
|
||||
run("git push --tags");
|
||||
pushedTags = true;
|
||||
|
||||
// publish
|
||||
console.log("Publishing to npm...");
|
||||
const publishCmd = pkg.name && pkg.name.startsWith("@") ? "npm publish --access public" : "npm publish";
|
||||
run(publishCmd);
|
||||
|
||||
console.log(`Release v${newVersion} succeeded.`);
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
console.error("Release failed:", err.message || err);
|
||||
try {
|
||||
// delete local tag
|
||||
if (tagged) {
|
||||
try { run(`git tag -d v${newVersion}`); } catch {}
|
||||
if (pushedTags) {
|
||||
try { run(`git push origin :refs/tags/v${newVersion}`); } catch {}
|
||||
}
|
||||
}
|
||||
// undo commit if made
|
||||
if (committed) {
|
||||
try { run("git reset --hard HEAD~1"); } catch {
|
||||
// fallback: restore package.json content
|
||||
fs.writeFileSync(pkgPath, pkgRaw, "utf8");
|
||||
}
|
||||
} else {
|
||||
// restore package.json
|
||||
fs.writeFileSync(pkgPath, pkgRaw, "utf8");
|
||||
}
|
||||
} catch (rbErr) {
|
||||
console.error("Rollback error:", rbErr.message || rbErr);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
@@ -119,33 +119,52 @@ export class Packet implements IPacket {
|
||||
|
||||
this.structure = [
|
||||
/* Header segment */
|
||||
{ name: "header", data: new Uint8Array([this.header, this.pathLength, ...this.path]), fields: [
|
||||
{
|
||||
name: "header",
|
||||
data: new Uint8Array([this.header]),
|
||||
fields: [
|
||||
/* Header flags */
|
||||
{
|
||||
name: "flags",
|
||||
type: FieldType.BITS,
|
||||
size: 1,
|
||||
bits: [
|
||||
{ name: "route type", size: 2 },
|
||||
{ name: "payload version", size: 2 },
|
||||
{ name: "payload type", size: 4 },
|
||||
{ name: "route type", size: 2 },
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
/* Transport codes */
|
||||
...(Packet.hasTransportCodes(this.routeType) ? [
|
||||
...(Packet.hasTransportCodes(this.routeType) ? [{
|
||||
name: "transport codes",
|
||||
data: new Uint8Array([
|
||||
(this.transport![0] >> 8) & 0xff, this.transport![0] & 0xff,
|
||||
(this.transport![1] >> 8) & 0xff, this.transport![1] & 0xff
|
||||
]),
|
||||
fields: [
|
||||
{
|
||||
name: "transport code 1",
|
||||
type: FieldType.UINT16_BE,
|
||||
size: 2
|
||||
size: 2,
|
||||
value: this.transport![0]
|
||||
},
|
||||
{
|
||||
name: "transport code 2",
|
||||
type: FieldType.UINT16_BE,
|
||||
size: 2
|
||||
size: 2,
|
||||
value: this.transport![1]
|
||||
},
|
||||
] : []),
|
||||
]
|
||||
}] : []),
|
||||
|
||||
/* Path length and hashes */
|
||||
{
|
||||
name: "path",
|
||||
data: new Uint8Array([this.pathLength, ...this.path]),
|
||||
fields: [
|
||||
{
|
||||
name: "path length",
|
||||
type: FieldType.UINT8,
|
||||
@@ -160,7 +179,8 @@ export class Packet implements IPacket {
|
||||
type: pathHashType,
|
||||
size: this.path.length
|
||||
}
|
||||
]},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -379,11 +399,11 @@ export class Packet implements IPacket {
|
||||
}
|
||||
if (typeof withSegment === "boolean" && withSegment) {
|
||||
segment!.fields.push({ type: FieldType.BITS, name: "flags", size: 1, value: flags, bits: [
|
||||
{ size: 4, name: "node type" },
|
||||
{ size: 1, name: "location flag" },
|
||||
{ size: 1, name: "feature1 flag" },
|
||||
{ size: 1, name: "feature2 flag" },
|
||||
{ size: 1, name: "name flag" },
|
||||
{ size: 1, name: "feature2 flag" },
|
||||
{ size: 1, name: "feature1 flag" },
|
||||
{ size: 1, name: "location flag" },
|
||||
{ size: 4, name: "node type" },
|
||||
]});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user