ci: fix duplicate publish

This commit is contained in:
Nurul Huda (Apon) 2026-03-29 15:17:43 +06:00
parent e5f7397276
commit c3e2cc127a
No known key found for this signature in database
GPG Key ID: 5D3F1DE2855A2F79
6 changed files with 129 additions and 31 deletions

View File

@ -223,16 +223,6 @@ jobs:
cd pkg/@ziex
bash prepare.sh ${{ needs.resolve-version.outputs.version }}
- name: Publish platform packages to NPM
env:
NODE_AUTH_TOKEN: ""
run: |
cd pkg/@ziex
for pkg in cli-darwin-arm64 cli-darwin-x64 cli-linux-x64 cli-linux-arm64 cli-win32-x64 cli-win32-arm64; do
echo "Publishing @ziex/$pkg..."
npm publish --workspace "$pkg" --access public --provenance --tag ${{ needs.resolve-version.outputs.dist-tag }} || echo "Failed to publish $pkg, continuing..."
done
- name: Publish @ziex/cli* to NPM
env:
NODE_AUTH_TOKEN: ""

View File

@ -6,7 +6,8 @@ import { downloadTemplate } from 'giget';
import { replaceInFile } from 'replace-in-file';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { randomInt } from 'node:crypto';
import { crc32 } from 'node:zlib';
const STATIC_TEMPLATES = [
{ value: 'starter', label: 'Starter', hint: 'Ziex starter app' },
@ -99,9 +100,9 @@ async function main() {
}
// 3. Perform String Replacements
const projectName = sanitizeProjectName(path.basename(targetDir));
s.start('Customizing project files...');
try {
const projectName = sanitizeProjectName(path.basename(targetDir));
await replaceInFile({
files: `${targetDir}/**/*`,
from: /ziex_app/g,
@ -112,25 +113,19 @@ async function main() {
s.stop('Replacement failed', 1);
}
// 4. Fix Zig fingerprint — Zig reports the correct value when the package
// name changes; we capture it and patch build.zig.zon automatically.
// 4. Fix Zig fingerprint — the fingerprint is a u64 packed struct:
// lower 32 bits = random id, upper 32 bits = CRC-32 of the package name.
s.start('Updating package fingerprint...');
try {
const result = spawnSync('zig', ['build'], { cwd: targetDir, encoding: 'utf8' });
const output = (result.stderr || '') + (result.stdout || '');
const match = output.match(/use this value: (0x[0-9a-f]+)/);
if (match) {
await replaceInFile({
files: `${targetDir}/build.zig.zon`,
from: /\.fingerprint = 0x[0-9a-f]+/,
to: `.fingerprint = ${match[1]}`,
});
s.stop('Package fingerprint updated!');
} else {
s.stop('Package fingerprint OK');
}
const fingerprint = generateZigFingerprint(projectName);
await replaceInFile({
files: `${targetDir}/build.zig.zon`,
from: /\.fingerprint = 0x[0-9a-f]+/,
to: `.fingerprint = ${fingerprint}`,
});
s.stop('Package fingerprint updated!');
} catch (err) {
s.stop('Could not update fingerprint (is zig installed?)');
s.stop('Could not update fingerprint');
}
outro(`Successfully created ${color.cyan(project)}!`);
@ -142,4 +137,13 @@ function sanitizeProjectName(name) {
return name.replace(/[^a-zA-Z0-9_]/g, '_');
}
// Generate a Zig package fingerprint: packed u64 with
// lower 32 bits = random id in [1, 0xFFFFFFFE], upper 32 bits = CRC-32 of name
export function generateZigFingerprint(name) {
const id = BigInt(randomInt(1, 0xfffffffe));
const checksum = BigInt(crc32(Buffer.from(name)) >>> 0);
const fingerprint = (checksum << 32n) | id;
return '0x' + fingerprint.toString(16).padStart(16, '0');
}
main().catch(console.error);

View File

@ -4,7 +4,7 @@
"workspaces": {
"": {
"name": "create-ziex",
"dependencies": {
"devDependencies": {
"@clack/prompts": "^1.1.0",
"giget": "^3.1.2",
"picocolors": "^1.1.1",

View File

@ -1,8 +1,10 @@
{
"name": "create-ziex",
"version": "0.1.0-dev.6",
"version": "0.1.0-dev.7",
"type": "module",
"bin": "dist/index.js",
"bin": {
"create-ziex": "dist/index.js"
},
"files": [
"dist",
"README.md",

View File

@ -0,0 +1,38 @@
import { describe, test, expect } from "bun:test";
import { execSync } from "node:child_process";
import { generateZigFingerprint } from "../bin/index.js";
const ZIG_RUN = "zig run test/fingerprint.zig -- ";
const cwd = import.meta.dirname + "/..";
function zigGenerate(name: string): string {
return execSync(`${ZIG_RUN} generate ${name}`, { cwd, encoding: "utf8" }).trim();
}
function zigValidate(name: string, hex: string): boolean {
return execSync(`${ZIG_RUN} validate ${name} ${hex}`, { cwd, encoding: "utf8" }).trim() === "true";
}
describe("fingerprint", () => {
const names = ["my_app", "ziex_app", "zx", "hello_world", "a"];
test.each(names)("node fingerprint is valid in zig for '%s'", (name) => {
const nodeFp = generateZigFingerprint(name);
expect(zigValidate(name, nodeFp)).toBe(true);
});
test.each(names)("zig fingerprint is valid in node for '%s'", (name) => {
const zigFp = zigGenerate(name);
const nodeFp = generateZigFingerprint(name);
// Extract checksum (upper 32 bits) from both — the random id will differ
const zigChecksum = BigInt(zigFp) >> 32n;
const nodeChecksum = BigInt(nodeFp) >> 32n;
expect(nodeChecksum).toBe(zigChecksum);
});
test("rejects fingerprint with wrong name", () => {
const fp = generateZigFingerprint("my_app");
expect(zigValidate("wrong_name", fp)).toBe(false);
});
});

View File

@ -0,0 +1,64 @@
const std = @import("std");
pub const Fingerprint = packed struct(u64) {
id: u32,
checksum: u32,
pub fn generate(name: []const u8) Fingerprint {
return .{
.id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff),
.checksum = std.hash.Crc32.hash(name),
};
}
pub fn validate(n: Fingerprint, name: []const u8) bool {
switch (n.id) {
0x00000000, 0xffffffff => return false,
else => return std.hash.Crc32.hash(name) == n.checksum,
}
}
pub fn int(n: Fingerprint) u64 {
return @bitCast(n);
}
};
pub fn main() !void {
const args = try std.process.argsAlloc(std.heap.page_allocator);
defer std.process.argsFree(std.heap.page_allocator, args);
const stderr = std.fs.File.stderr();
const stdout = std.fs.File.stdout();
if (args.len < 3) {
try stderr.writeAll("Usage: fingerprint <generate|validate> <name> [hex]\n");
std.process.exit(1);
}
const cmd = args[1];
const name = args[2];
if (std.mem.eql(u8, cmd, "generate")) {
const fp = Fingerprint.generate(name);
var buf: [20]u8 = undefined;
const out = std.fmt.bufPrint(&buf, "0x{x:0>16}\n", .{fp.int()}) catch unreachable;
try stdout.writeAll(out);
} else if (std.mem.eql(u8, cmd, "validate")) {
if (args.len < 4) {
try stderr.writeAll("Usage: fingerprint validate <name> <hex>\n");
std.process.exit(1);
}
const hex = args[3];
const raw = if (std.mem.startsWith(u8, hex, "0x")) hex[2..] else hex;
const value = std.fmt.parseInt(u64, raw, 16) catch {
try stderr.writeAll("Invalid hex value\n");
std.process.exit(1);
};
const fp: Fingerprint = @bitCast(value);
const valid = fp.validate(name);
try stdout.writeAll(if (valid) "true\n" else "false\n");
} else {
try stderr.writeAll("Unknown command. Use 'generate' or 'validate'.\n");
std.process.exit(1);
}
}