@git.zone/tsrust 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +2 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/mod_artifact/classes.artifactassembler.d.ts +48 -0
- package/dist_ts/mod_artifact/classes.artifactassembler.js +842 -0
- package/dist_ts/mod_artifact/index.d.ts +1 -0
- package/dist_ts/mod_artifact/index.js +2 -0
- package/dist_ts/mod_cli/classes.tsrustcli.d.ts +5 -2
- package/dist_ts/mod_cli/classes.tsrustcli.js +110 -75
- package/dist_ts/mod_cli/helpers.targets.d.ts +20 -0
- package/dist_ts/mod_cli/helpers.targets.js +106 -0
- package/dist_ts/mod_cli/index.d.ts +1 -0
- package/dist_ts/mod_cli/index.js +2 -1
- package/dist_ts/mod_elf/classes.provenance.d.ts +2 -1
- package/dist_ts/mod_elf/classes.provenance.js +0 -0
- package/dist_ts/mod_provenance/classes.gitstate.d.ts +7 -0
- package/dist_ts/mod_provenance/classes.gitstate.js +34 -0
- package/dist_ts/mod_provenance/classes.provenancestore.d.ts +11 -0
- package/dist_ts/mod_provenance/classes.provenancestore.js +184 -0
- package/dist_ts/mod_provenance/helpers.owneridentity.d.ts +2 -0
- package/dist_ts/mod_provenance/helpers.owneridentity.js +57 -0
- package/dist_ts/mod_provenance/index.d.ts +3 -0
- package/dist_ts/mod_provenance/index.js +4 -0
- package/package.json +3 -3
- package/readme.hints.md +6 -3
- package/readme.md +77 -5
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +2 -0
- package/ts/mod_artifact/classes.artifactassembler.ts +1028 -0
- package/ts/mod_artifact/index.ts +6 -0
- package/ts/mod_cli/classes.tsrustcli.ts +162 -95
- package/ts/mod_cli/helpers.targets.ts +141 -0
- package/ts/mod_cli/index.ts +10 -0
- package/ts/mod_elf/classes.provenance.ts +0 -0
- package/ts/mod_provenance/classes.gitstate.ts +49 -0
- package/ts/mod_provenance/classes.provenancestore.ts +194 -0
- package/ts/mod_provenance/helpers.owneridentity.ts +57 -0
- package/ts/mod_provenance/index.ts +10 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as childProcess from 'child_process';
|
|
2
|
+
import * as util from 'util';
|
|
3
|
+
const execFile = util.promisify(childProcess.execFile);
|
|
4
|
+
export async function captureGitSnapshot(cwdArg) {
|
|
5
|
+
let commit;
|
|
6
|
+
try {
|
|
7
|
+
const result = await execFile('git', ['-C', cwdArg, 'rev-parse', 'HEAD'], {
|
|
8
|
+
encoding: 'utf8',
|
|
9
|
+
});
|
|
10
|
+
commit = result.stdout.trim();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return { available: false, commit: 'unknown', status: '' };
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const status = await execFile('git', ['-C', cwdArg, 'status', '--porcelain=v1', '--untracked-files=all'], { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
17
|
+
return {
|
|
18
|
+
available: true,
|
|
19
|
+
commit,
|
|
20
|
+
status: status.stdout,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error('Failed to inspect Git worktree state');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function assertGitSnapshotUnchanged(beforeArg, afterArg) {
|
|
28
|
+
if (beforeArg.available !== afterArg.available ||
|
|
29
|
+
beforeArg.commit !== afterArg.commit ||
|
|
30
|
+
beforeArg.status !== afterArg.status) {
|
|
31
|
+
throw new Error('Git source state changed while Cargo was building');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5naXRzdGF0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9wcm92ZW5hbmNlL2NsYXNzZXMuZ2l0c3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLFlBQVksTUFBTSxlQUFlLENBQUM7QUFDOUMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFFN0IsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDLENBQUM7QUFRdkQsTUFBTSxDQUFDLEtBQUssVUFBVSxrQkFBa0IsQ0FBQyxNQUFjO0lBQ3JELElBQUksTUFBYyxDQUFDO0lBQ25CLElBQUksQ0FBQztRQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDLElBQUksRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxFQUFFO1lBQ3hFLFFBQVEsRUFBRSxNQUFNO1NBQ2pCLENBQUMsQ0FBQztRQUNILE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsQ0FBQztJQUM3RCxDQUFDO0lBQ0QsSUFBSSxDQUFDO1FBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQzNCLEtBQUssRUFDTCxDQUFDLElBQUksRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLGdCQUFnQixFQUFFLHVCQUF1QixDQUFDLEVBQ25FLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLEVBQUUsQ0FDbEQsQ0FBQztRQUNGLE9BQU87WUFDTCxTQUFTLEVBQUUsSUFBSTtZQUNmLE1BQU07WUFDTixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07U0FDdEIsQ0FBQztJQUNKLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxNQUFNLElBQUksS0FBSyxDQUFDLHNDQUFzQyxDQUFDLENBQUM7SUFDMUQsQ0FBQztBQUNILENBQUM7QUFFRCxNQUFNLFVBQVUsMEJBQTBCLENBQ3hDLFNBQXVCLEVBQ3ZCLFFBQXNCO0lBRXRCLElBQ0UsU0FBUyxDQUFDLFNBQVMsS0FBSyxRQUFRLENBQUMsU0FBUztRQUMxQyxTQUFTLENBQUMsTUFBTSxLQUFLLFFBQVEsQ0FBQyxNQUFNO1FBQ3BDLFNBQVMsQ0FBQyxNQUFNLEtBQUssUUFBUSxDQUFDLE1BQU0sRUFDcEMsQ0FBQztRQUNELE1BQU0sSUFBSSxLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQztJQUN2RSxDQUFDO0FBQ0gsQ0FBQyJ9
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ITsrustBuildInfo } from '../mod_elf/index.js';
|
|
2
|
+
export declare const PROVENANCE_SIDECAR_SUFFIX = ".tsrust-build.json";
|
|
3
|
+
export declare class ProvenanceStore {
|
|
4
|
+
static sidecarPath(binaryPathArg: string): string;
|
|
5
|
+
static sha256(binaryPathArg: string): Promise<string>;
|
|
6
|
+
static write(binaryPathArg: string, infoArg: ITsrustBuildInfo): Promise<string>;
|
|
7
|
+
private static readExistingSidecar;
|
|
8
|
+
private static recoverTemporaryFiles;
|
|
9
|
+
static readSidecar(binaryPathArg: string): Promise<ITsrustBuildInfo>;
|
|
10
|
+
static read(binaryPathArg: string): Promise<ITsrustBuildInfo | undefined>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { ProvenanceStamper } from '../mod_elf/index.js';
|
|
5
|
+
import { getHostIdentity, isLocalProcessRunning } from './helpers.owneridentity.js';
|
|
6
|
+
const SIDECAR_FORMAT = 'tsrust.build-provenance.v2';
|
|
7
|
+
export const PROVENANCE_SIDECAR_SUFFIX = '.tsrust-build.json';
|
|
8
|
+
function isBuildInfo(valueArg) {
|
|
9
|
+
if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg))
|
|
10
|
+
return false;
|
|
11
|
+
const value = valueArg;
|
|
12
|
+
return (typeof value.binary === 'string' &&
|
|
13
|
+
typeof value.target === 'string' &&
|
|
14
|
+
typeof value.projectName === 'string' &&
|
|
15
|
+
typeof value.projectVersion === 'string' &&
|
|
16
|
+
typeof value.gitCommit === 'string' &&
|
|
17
|
+
typeof value.builtAt === 'string' &&
|
|
18
|
+
typeof value.tsrustVersion === 'string' &&
|
|
19
|
+
(value.gitDirty === undefined || typeof value.gitDirty === 'boolean'));
|
|
20
|
+
}
|
|
21
|
+
export class ProvenanceStore {
|
|
22
|
+
static sidecarPath(binaryPathArg) {
|
|
23
|
+
return `${binaryPathArg}${PROVENANCE_SIDECAR_SUFFIX}`;
|
|
24
|
+
}
|
|
25
|
+
static async sha256(binaryPathArg) {
|
|
26
|
+
const binary = await fs.promises.lstat(binaryPathArg);
|
|
27
|
+
if (!binary.isFile() || binary.isSymbolicLink()) {
|
|
28
|
+
throw new Error(`Provenance binary is not a regular file: ${binaryPathArg}`);
|
|
29
|
+
}
|
|
30
|
+
const digest = crypto.createHash('sha256');
|
|
31
|
+
await new Promise((resolve, reject) => {
|
|
32
|
+
const stream = fs.createReadStream(binaryPathArg);
|
|
33
|
+
stream.on('data', (chunk) => digest.update(chunk));
|
|
34
|
+
stream.on('error', reject);
|
|
35
|
+
stream.on('end', resolve);
|
|
36
|
+
});
|
|
37
|
+
return digest.digest('hex');
|
|
38
|
+
}
|
|
39
|
+
static async write(binaryPathArg, infoArg) {
|
|
40
|
+
if (!isBuildInfo(infoArg)) {
|
|
41
|
+
throw new Error('Provenance build information has an invalid shape');
|
|
42
|
+
}
|
|
43
|
+
const binary = await fs.promises.lstat(binaryPathArg);
|
|
44
|
+
if (!binary.isFile() || binary.isSymbolicLink()) {
|
|
45
|
+
throw new Error(`Provenance binary is not a regular file: ${binaryPathArg}`);
|
|
46
|
+
}
|
|
47
|
+
const sidecarPath = ProvenanceStore.sidecarPath(binaryPathArg);
|
|
48
|
+
await ProvenanceStore.recoverTemporaryFiles(sidecarPath);
|
|
49
|
+
const existing = await ProvenanceStore.readExistingSidecar(binaryPathArg);
|
|
50
|
+
if (existing) {
|
|
51
|
+
if (JSON.stringify(existing) === JSON.stringify(infoArg))
|
|
52
|
+
return sidecarPath;
|
|
53
|
+
throw new Error(`Provenance sidecar already exists with different build information: ${sidecarPath}`);
|
|
54
|
+
}
|
|
55
|
+
const record = {
|
|
56
|
+
format: SIDECAR_FORMAT,
|
|
57
|
+
binarySha256: await ProvenanceStore.sha256(binaryPathArg),
|
|
58
|
+
buildInfo: infoArg,
|
|
59
|
+
};
|
|
60
|
+
const temporaryPath = `${sidecarPath}.${getHostIdentity()}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
61
|
+
let temporary;
|
|
62
|
+
try {
|
|
63
|
+
temporary = await fs.promises.open(temporaryPath, 'wx', 0o600);
|
|
64
|
+
await temporary.writeFile(`${JSON.stringify(record, null, 2)}\n`, 'utf8');
|
|
65
|
+
await temporary.chmod(0o644);
|
|
66
|
+
await temporary.sync();
|
|
67
|
+
await temporary.close();
|
|
68
|
+
temporary = undefined;
|
|
69
|
+
try {
|
|
70
|
+
await fs.promises.link(temporaryPath, sidecarPath);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error.code !== 'EEXIST')
|
|
74
|
+
throw error;
|
|
75
|
+
const concurrentlyWritten = await ProvenanceStore.readSidecar(binaryPathArg);
|
|
76
|
+
if (JSON.stringify(concurrentlyWritten) !== JSON.stringify(infoArg)) {
|
|
77
|
+
throw new Error(`Concurrent provenance write disagreed for ${binaryPathArg}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
await fs.promises.unlink(temporaryPath);
|
|
81
|
+
const directory = await fs.promises.open(path.dirname(sidecarPath), 'r');
|
|
82
|
+
try {
|
|
83
|
+
await directory.sync();
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
await directory.close();
|
|
87
|
+
}
|
|
88
|
+
return sidecarPath;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await Promise.allSettled([
|
|
92
|
+
temporary?.close() || Promise.resolve(),
|
|
93
|
+
fs.promises.rm(temporaryPath, { force: true }),
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
static async readExistingSidecar(binaryPathArg) {
|
|
98
|
+
try {
|
|
99
|
+
return await ProvenanceStore.readSidecar(binaryPathArg);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error.code === 'ENOENT')
|
|
103
|
+
return undefined;
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
static async recoverTemporaryFiles(sidecarPathArg) {
|
|
108
|
+
const directoryPath = path.dirname(sidecarPathArg);
|
|
109
|
+
const temporaryPrefix = `${path.basename(sidecarPathArg)}.`;
|
|
110
|
+
const entries = await fs.promises.readdir(directoryPath, { withFileTypes: true });
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
if (!entry.name.startsWith(temporaryPrefix) || !entry.name.endsWith('.tmp')) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!entry.isFile())
|
|
116
|
+
continue;
|
|
117
|
+
const suffix = entry.name.slice(temporaryPrefix.length, -'.tmp'.length);
|
|
118
|
+
const [hostIdentity, pidText, identifier, ...unexpected] = suffix.split('.');
|
|
119
|
+
const pid = Number(pidText);
|
|
120
|
+
if (unexpected.length > 0 ||
|
|
121
|
+
!/^[a-f0-9]{64}$/.test(hostIdentity || '') ||
|
|
122
|
+
!Number.isSafeInteger(pid) ||
|
|
123
|
+
!/^[a-f0-9-]{36}$/.test(identifier || '') ||
|
|
124
|
+
hostIdentity !== getHostIdentity() ||
|
|
125
|
+
isLocalProcessRunning(pid)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const temporaryPath = path.join(directoryPath, entry.name);
|
|
129
|
+
try {
|
|
130
|
+
const temporary = await fs.promises.lstat(temporaryPath);
|
|
131
|
+
if (temporary.isFile() && !temporary.isSymbolicLink()) {
|
|
132
|
+
await fs.promises.rm(temporaryPath, { force: true });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error.code !== 'ENOENT')
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
static async readSidecar(binaryPathArg) {
|
|
142
|
+
const sidecarPath = ProvenanceStore.sidecarPath(binaryPathArg);
|
|
143
|
+
const sidecar = await fs.promises.lstat(sidecarPath);
|
|
144
|
+
if (!sidecar.isFile() || sidecar.isSymbolicLink()) {
|
|
145
|
+
throw new Error(`Provenance sidecar is not a regular file: ${sidecarPath}`);
|
|
146
|
+
}
|
|
147
|
+
let parsed;
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(await fs.promises.readFile(sidecarPath, 'utf8'));
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
throw new Error(`Provenance sidecar is invalid JSON: ${sidecarPath}`);
|
|
153
|
+
}
|
|
154
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
155
|
+
throw new Error(`Provenance sidecar has an invalid shape: ${sidecarPath}`);
|
|
156
|
+
}
|
|
157
|
+
const record = parsed;
|
|
158
|
+
if (record.format !== SIDECAR_FORMAT ||
|
|
159
|
+
typeof record.binarySha256 !== 'string' ||
|
|
160
|
+
!/^[a-f0-9]{64}$/.test(record.binarySha256) ||
|
|
161
|
+
!isBuildInfo(record.buildInfo)) {
|
|
162
|
+
throw new Error(`Provenance sidecar has an unsupported shape: ${sidecarPath}`);
|
|
163
|
+
}
|
|
164
|
+
const actualSha256 = await ProvenanceStore.sha256(binaryPathArg);
|
|
165
|
+
if (actualSha256 !== record.binarySha256) {
|
|
166
|
+
throw new Error(`Provenance sidecar hash does not match ${binaryPathArg}`);
|
|
167
|
+
}
|
|
168
|
+
return record.buildInfo;
|
|
169
|
+
}
|
|
170
|
+
static async read(binaryPathArg) {
|
|
171
|
+
const sidecarPath = ProvenanceStore.sidecarPath(binaryPathArg);
|
|
172
|
+
try {
|
|
173
|
+
await fs.promises.lstat(sidecarPath);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error.code === 'ENOENT') {
|
|
177
|
+
return ProvenanceStamper.read(binaryPathArg);
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
return ProvenanceStore.readSidecar(binaryPathArg);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wcm92ZW5hbmNlc3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfcHJvdmVuYW5jZS9jbGFzc2VzLnByb3ZlbmFuY2VzdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUNqQyxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztBQUN6QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsZUFBZSxFQUFFLHFCQUFxQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFcEYsTUFBTSxjQUFjLEdBQUcsNEJBQTRCLENBQUM7QUFDcEQsTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsb0JBQW9CLENBQUM7QUFROUQsU0FBUyxXQUFXLENBQUMsUUFBaUI7SUFDcEMsSUFBSSxDQUFDLFFBQVEsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUN2RixNQUFNLEtBQUssR0FBRyxRQUFtQyxDQUFDO0lBQ2xELE9BQU8sQ0FDTCxPQUFPLEtBQUssQ0FBQyxNQUFNLEtBQUssUUFBUTtRQUNoQyxPQUFPLEtBQUssQ0FBQyxNQUFNLEtBQUssUUFBUTtRQUNoQyxPQUFPLEtBQUssQ0FBQyxXQUFXLEtBQUssUUFBUTtRQUNyQyxPQUFPLEtBQUssQ0FBQyxjQUFjLEtBQUssUUFBUTtRQUN4QyxPQUFPLEtBQUssQ0FBQyxTQUFTLEtBQUssUUFBUTtRQUNuQyxPQUFPLEtBQUssQ0FBQyxPQUFPLEtBQUssUUFBUTtRQUNqQyxPQUFPLEtBQUssQ0FBQyxhQUFhLEtBQUssUUFBUTtRQUN2QyxDQUFDLEtBQUssQ0FBQyxRQUFRLEtBQUssU0FBUyxJQUFJ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
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as childProcess from 'child_process';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
let cachedHostIdentity;
|
|
6
|
+
function readBootIdentity() {
|
|
7
|
+
if (process.platform === 'linux') {
|
|
8
|
+
try {
|
|
9
|
+
return fs.readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// Fall through to the portable host fingerprint.
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (process.platform === 'darwin') {
|
|
16
|
+
try {
|
|
17
|
+
return childProcess.execFileSync('/usr/sbin/sysctl', ['-n', 'kern.boottime'], {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
}).trim();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Fall through to the portable host fingerprint.
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return String(Math.round((Date.now() - os.uptime() * 1000) / 60_000));
|
|
26
|
+
}
|
|
27
|
+
export function getHostIdentity() {
|
|
28
|
+
if (cachedHostIdentity)
|
|
29
|
+
return cachedHostIdentity;
|
|
30
|
+
const macAddresses = Object.values(os.networkInterfaces())
|
|
31
|
+
.flat()
|
|
32
|
+
.map((address) => address?.mac.toLowerCase())
|
|
33
|
+
.filter((address) => !!address && address !== '00:00:00:00:00:00')
|
|
34
|
+
.sort();
|
|
35
|
+
cachedHostIdentity = crypto
|
|
36
|
+
.createHash('sha256')
|
|
37
|
+
.update(JSON.stringify({
|
|
38
|
+
platform: process.platform,
|
|
39
|
+
hostname: os.hostname(),
|
|
40
|
+
bootIdentity: readBootIdentity(),
|
|
41
|
+
macAddresses,
|
|
42
|
+
}))
|
|
43
|
+
.digest('hex');
|
|
44
|
+
return cachedHostIdentity;
|
|
45
|
+
}
|
|
46
|
+
export function isLocalProcessRunning(pidArg) {
|
|
47
|
+
if (!Number.isSafeInteger(pidArg) || pidArg <= 0)
|
|
48
|
+
return false;
|
|
49
|
+
try {
|
|
50
|
+
process.kill(pidArg, 0);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return error.code !== 'ESRCH';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5vd25lcmlkZW50aXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbW9kX3Byb3ZlbmFuY2UvaGVscGVycy5vd25lcmlkZW50aXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxZQUFZLE1BQU0sZUFBZSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBRXpCLElBQUksa0JBQXNDLENBQUM7QUFFM0MsU0FBUyxnQkFBZ0I7SUFDdkIsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sRUFBRSxDQUFDO1FBQ2pDLElBQUksQ0FBQztZQUNILE9BQU8sRUFBRSxDQUFDLFlBQVksQ0FBQyxpQ0FBaUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUMzRSxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsaURBQWlEO1FBQ25ELENBQUM7SUFDSCxDQUFDO0lBQ0QsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2xDLElBQUksQ0FBQztZQUNILE9BQU8sWUFBWSxDQUFDLFlBQVksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLElBQUksRUFBRSxlQUFlLENBQUMsRUFBRTtnQkFDNUUsUUFBUSxFQUFFLE1BQU07YUFDakIsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1osQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLGlEQUFpRDtRQUNuRCxDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFDeEUsQ0FBQztBQUVELE1BQU0sVUFBVSxlQUFlO0lBQzdCLElBQUksa0JBQWtCO1FBQUUsT0FBTyxrQkFBa0IsQ0FBQztJQUNsRCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1NBQ3ZELElBQUksRUFBRTtTQUNOLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztTQUM1QyxNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQXFCLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxJQUFJLE9BQU8sS0FBSyxtQkFBbUIsQ0FBQztTQUNwRixJQUFJLEVBQUUsQ0FBQztJQUNWLGtCQUFrQixHQUFHLE1BQU07U0FDeEIsVUFBVSxDQUFDLFFBQVEsQ0FBQztTQUNwQixNQUFNLENBQ0wsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUNiLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtRQUMxQixRQUFRLEVBQUUsRUFBRSxDQUFDLFFBQVEsRUFBRTtRQUN2QixZQUFZLEVBQUUsZ0JBQWdCLEVBQUU7UUFDaEMsWUFBWTtLQUNiLENBQUMsQ0FDSDtTQUNBLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqQixPQUFPLGtCQUFrQixDQUFDO0FBQzVCLENBQUM7QUFFRCxNQUFNLFVBQVUscUJBQXFCLENBQUMsTUFBYztJQUNsRCxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxNQUFNLElBQUksQ0FBQztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQy9ELElBQUksQ0FBQztRQUNILE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3hCLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFRLEtBQStCLENBQUMsSUFBSSxLQUFLLE9BQU8sQ0FBQztJQUMzRCxDQUFDO0FBQ0gsQ0FBQyJ9
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { captureGitSnapshot, assertGitSnapshotUnchanged, type IGitSnapshot, } from './classes.gitstate.js';
|
|
2
|
+
export { ProvenanceStore, PROVENANCE_SIDECAR_SUFFIX, } from './classes.provenancestore.js';
|
|
3
|
+
export { getHostIdentity, isLocalProcessRunning } from './helpers.owneridentity.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { captureGitSnapshot, assertGitSnapshotUnchanged, } from './classes.gitstate.js';
|
|
2
|
+
export { ProvenanceStore, PROVENANCE_SIDECAR_SUFFIX, } from './classes.provenancestore.js';
|
|
3
|
+
export { getHostIdentity, isLocalProcessRunning } from './helpers.owneridentity.js';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfcHJvdmVuYW5jZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLDBCQUEwQixHQUUzQixNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFDTCxlQUFlLEVBQ2YseUJBQXlCLEdBQzFCLE1BQU0sOEJBQThCLENBQUM7QUFDdEMsT0FBTyxFQUFFLGVBQWUsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@git.zone/tsrust",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A tool for compiling Rust projects, detecting Cargo workspaces, building with cargo, and placing binaries in a conventional dist_rust directory.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"@push.rocks/smartconfig": "^6.1.1",
|
|
35
35
|
"@push.rocks/smartfile": "^13.1.3",
|
|
36
36
|
"@push.rocks/smartpath": "^6.0.0",
|
|
37
|
-
"@push.rocks/smartshell": "^
|
|
38
|
-
"smol-toml": "^1.
|
|
37
|
+
"@push.rocks/smartshell": "^4.1.1",
|
|
38
|
+
"smol-toml": "^1.7.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@git.zone/tsbuild": "^4.4.2",
|
package/readme.hints.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# tsrust hints
|
|
2
2
|
|
|
3
3
|
## Architecture
|
|
4
|
-
- Follows tsbuild patterns
|
|
5
|
-
- Uses smartcli for CLI, smartshell for
|
|
6
|
-
-
|
|
4
|
+
- Follows tsbuild patterns for the `cli.js` entry point and TypeScript module layout.
|
|
5
|
+
- Uses smartcli for CLI dispatch, smartshell for Cargo execution, native `child_process.execFile` for Git snapshots, and smol-toml for TOML parsing.
|
|
6
|
+
- `mod_artifact` owns strict multi-host assembly; `mod_provenance` owns byte-preserving, hash-bound sidecars and Git source snapshots.
|
|
7
7
|
|
|
8
8
|
## Key patterns
|
|
9
9
|
- smartshell `.exec()` streams output to terminal (non-silent)
|
|
10
10
|
- smartshell `.execSilent()` captures output without printing
|
|
11
11
|
- Cargo workspace detection: check for `[workspace]` section in Cargo.toml
|
|
12
12
|
- Binary targets: look for `[[bin]]` entries in member crate Cargo.toml files
|
|
13
|
+
- New builds never append provenance to binaries. Keep legacy `ProvenanceStamper` reads for tsrust 1.7/1.8 artifacts, but use `ProvenanceStore` sidecars for all writes.
|
|
14
|
+
- Host target precedence is CLI, exact host, OS family, legacy targets, then native. Assembly uses the union of all configured targets unless repeated CLI `--target` values define an explicit matrix, and rejects dirty or incomplete matrices.
|
|
15
|
+
- Assembly owns only `dist_rust/`, `.nogit/tsrust-assembly*`, and transient `.nogit/.tsrust-assembly-*.tmp` owner records. Its atomically linked owner lock, serialized recovery claim, and phase journal recover readable, owner-consistent transactions; malformed or inconsistent state fails closed.
|
package/readme.md
CHANGED
|
@@ -50,7 +50,8 @@ This will:
|
|
|
50
50
|
4. Run `cargo build --release` with full streaming output
|
|
51
51
|
5. Store Cargo intermediates in `.nogit/tsrust-target` by default
|
|
52
52
|
6. Copy each binary to `dist_rust/` with executable permissions (`chmod 755`)
|
|
53
|
-
7.
|
|
53
|
+
7. Write a SHA-256-bound provenance sidecar without changing the binary bytes
|
|
54
|
+
8. Report file sizes and total build time
|
|
54
55
|
|
|
55
56
|
**Example output:**
|
|
56
57
|
|
|
@@ -63,6 +64,7 @@ Running: CARGO_TARGET_DIR="/path/to/project/.nogit/tsrust-target" cargo build --
|
|
|
63
64
|
Compiling rustproxy v0.1.0
|
|
64
65
|
Finished `release` profile [optimized] target(s) in 29.01s
|
|
65
66
|
Copied rustproxy (13.4 MB) -> dist_rust/rustproxy
|
|
67
|
+
Wrote provenance: @example/rustproxy@1.0.0 abc123def456 (native)
|
|
66
68
|
Done in 29.2s
|
|
67
69
|
```
|
|
68
70
|
|
|
@@ -127,7 +129,9 @@ When using `--target`, output binaries are named `<binname>_<os>_<arch>`:
|
|
|
127
129
|
```
|
|
128
130
|
dist_rust/
|
|
129
131
|
├── rustproxy_linux_arm64
|
|
130
|
-
|
|
132
|
+
├── rustproxy_linux_arm64.tsrust-build.json
|
|
133
|
+
├── rustproxy_linux_amd64
|
|
134
|
+
└── rustproxy_linux_amd64.tsrust-build.json
|
|
131
135
|
```
|
|
132
136
|
|
|
133
137
|
`tsrust` automatically installs missing rustup targets via `rustup target add` when needed.
|
|
@@ -147,12 +151,56 @@ You can set default cross-compilation targets in your project's `.smartconfig.js
|
|
|
147
151
|
}
|
|
148
152
|
```
|
|
149
153
|
|
|
150
|
-
When targets are configured in `.smartconfig.json`, simply running `tsrust` will cross-compile for all listed targets. CLI `--target` flags
|
|
154
|
+
When targets are configured in `.smartconfig.json`, simply running `tsrust` will cross-compile for all listed targets. CLI `--target` flags determine the selected targets when provided. The complete configuration is still validated first, so malformed legacy or host-specific entries are always rejected.
|
|
155
|
+
|
|
156
|
+
For builds split across Linux and macOS hosts, select targets by build host:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"@git.zone/tsrust": {
|
|
161
|
+
"targetsByHost": {
|
|
162
|
+
"linux": ["linux_amd64", "linux_arm64"],
|
|
163
|
+
"macos": ["macos_amd64", "macos_arm64"]
|
|
164
|
+
},
|
|
165
|
+
"locked": true
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Host selection uses this precedence: CLI `--target`, exact host key, OS-family key, legacy `targets`, then a native build. Exact host keys are `linux_amd64`, `linux_arm64`, `macos_amd64`, and `macos_arm64`; OS-family keys are `linux` and `macos`. The selected entries are not merged, so an exact host entry completely replaces its family entry for that host.
|
|
151
171
|
|
|
152
172
|
`locked: true` runs Cargo builds with `--locked` for deterministic dependency resolution. This requires `Cargo.lock` to exist and remain consistent with the manifests; Cargo fails instead of updating an inconsistent lockfile.
|
|
153
173
|
|
|
154
174
|
`targetDir` is optional. It must point to a tsrust-owned path under `.nogit/`, such as `.nogit/tsrust-target` or `.nogit/tsrust-custom-target`. You can also set `TSRUST_TARGET_DIR` for one-off runs. `pruneAfterBuild: true` or `TSRUST_PRUNE_AFTER_BUILD=true` removes the marked managed target cache after binaries have been copied to `dist_rust/`.
|
|
155
175
|
|
|
176
|
+
### Build Provenance
|
|
177
|
+
|
|
178
|
+
Every new build writes `<binary>.tsrust-build.json` beside the binary. In a Git checkout, the sidecar records the consuming package name and version, exact commit and dirty state, target, build time, and tsrust version. Non-Git builds record `gitCommit: "unknown"` and omit `gitDirty`. The sidecar also contains the binary's SHA-256 digest, so provenance reads fail if either file no longer belongs to the pair. The binary itself remains byte-identical to Cargo's output, preserving Mach-O signatures and other binary-format integrity checks.
|
|
179
|
+
|
|
180
|
+
Inspect a built artifact with:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
tsrust inspect dist_rust/rustproxy_linux_amd64
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`inspect` verifies and reads the sidecar. It retains read compatibility with provenance trailers produced by tsrust 1.7 and 1.8, but new builds no longer append those trailers.
|
|
187
|
+
|
|
188
|
+
tsrust snapshots the Git commit and complete worktree status before each Cargo build and verifies that neither changed before copying artifacts. Dirty builds remain possible for development and are marked as dirty; strict multi-host assembly rejects them.
|
|
189
|
+
|
|
190
|
+
### Multi-Host Assembly
|
|
191
|
+
|
|
192
|
+
Each host build owns and cleans its own `dist_rust/`. Copy those host outputs into separate artifact directories, then assemble the complete matrix from the same clean Git checkout and package version:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
tsrust assemble .nogit/artifacts/linux .nogit/artifacts/macos
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The union of `targets` and `targetsByHost` defines the required target matrix. Repeated `--target` options can define an explicit matrix instead. Assembly always publishes to the tsrust-owned `dist_rust/` directory.
|
|
199
|
+
|
|
200
|
+
Assembly requires every Cargo binary for every expected target. It rejects missing, duplicate, unexpected, non-executable, symlinked, hash-mismatched, dirty, wrong-commit, wrong-package, and wrong-tsrust-version inputs. Validation and copying happen in a same-filesystem transaction under `.nogit/tsrust-assembly/`, guarded by the atomically published `.nogit/tsrust-assembly.lock`. The Git commit, clean worktree, and package identity are checked again immediately before publication. The previous output is retained until the complete staged matrix is ready, and successful publication removes files left by older matrices.
|
|
201
|
+
|
|
202
|
+
The transaction records a boot-scoped host identity, process, owner token, and publication phase. A separate atomic recovery claim serializes dead-owner recovery. When the owner lock and state are readable and consistent, a later invocation on the owning host rejects a live process, rolls back an interrupted pre-commit publication after that process exits, and completes publication or cleanup after durable commit intent. A confirmed missing state file is safe to clean because the owner-lock protocol forbids output mutation before initial state persistence; malformed, non-regular, unreadable, or owner-inconsistent state fails closed. A transaction owned by another host is never taken over automatically because portable filesystem operations cannot fence a paused remote writer. If committed cleanup cannot finish immediately, the programmatic result reports `cleanupPending: true` and the CLI prints a warning; the owning process can retry it immediately.
|
|
203
|
+
|
|
156
204
|
### Static Linking
|
|
157
205
|
|
|
158
206
|
`tsrust` can produce fully statically linked Linux binaries (static-pie) that run on both glibc distros (Debian/Ubuntu) and musl distros (Alpine). Enable it via `.smartconfig.json`:
|
|
@@ -260,7 +308,8 @@ my-project/
|
|
|
260
308
|
│ ├── Cargo.toml
|
|
261
309
|
│ └── src/
|
|
262
310
|
├── dist_rust/ # 📦 Output: compiled binaries go here
|
|
263
|
-
│
|
|
311
|
+
│ ├── my-binary
|
|
312
|
+
│ └── my-binary.tsrust-build.json
|
|
264
313
|
├── .nogit/
|
|
265
314
|
│ └── tsrust-target/ # 🧹 Managed Cargo target cache
|
|
266
315
|
├── ts/ # (your TypeScript code, built by tsbuild)
|
|
@@ -286,7 +335,18 @@ If no `rust/` directory is found, `tsrust` checks for `ts_rust/` as a fallback.
|
|
|
286
335
|
`tsrust` exports its internals for use in other Node.js/TypeScript tools:
|
|
287
336
|
|
|
288
337
|
```typescript
|
|
289
|
-
import {
|
|
338
|
+
import {
|
|
339
|
+
ArtifactAssembler,
|
|
340
|
+
CargoConfig,
|
|
341
|
+
CargoRunner,
|
|
342
|
+
FsHelpers,
|
|
343
|
+
ProvenanceStore,
|
|
344
|
+
TsRustCli,
|
|
345
|
+
configuredAssemblyTargets,
|
|
346
|
+
normalizeTargets,
|
|
347
|
+
resolveBuildTargets,
|
|
348
|
+
resolveManagedTargetDir,
|
|
349
|
+
} from '@git.zone/tsrust';
|
|
290
350
|
|
|
291
351
|
// Parse a Cargo workspace
|
|
292
352
|
const config = new CargoConfig('/path/to/rust');
|
|
@@ -309,6 +369,18 @@ const size = await FsHelpers.getFileSize(dest);
|
|
|
309
369
|
console.log(FsHelpers.formatFileSize(size)); // "13.4 MB"
|
|
310
370
|
```
|
|
311
371
|
|
|
372
|
+
Important exported build and artifact APIs:
|
|
373
|
+
|
|
374
|
+
| API | Purpose |
|
|
375
|
+
| --- | --- |
|
|
376
|
+
| `resolveBuildTargets()` | Apply CLI, exact-host, OS-family, legacy, and native target precedence. |
|
|
377
|
+
| `configuredAssemblyTargets()` | Normalize and deduplicate the union required for multi-host assembly. |
|
|
378
|
+
| `normalizeTargets()` | Resolve friendly aliases and reject invalid or colliding target names. |
|
|
379
|
+
| `ProvenanceStore` | Write, hash-verify, and read byte-preserving provenance sidecars. |
|
|
380
|
+
| `ArtifactAssembler` | Validate and transactionally publish a complete exact-commit artifact matrix to `dist_rust/`. |
|
|
381
|
+
| `captureGitSnapshot()` / `assertGitSnapshotUnchanged()` | Capture and compare Git commit and worktree state around a build. |
|
|
382
|
+
| `ProvenanceStamper` | Read legacy tsrust 1.7/1.8 embedded trailers; new builds use `ProvenanceStore`. |
|
|
383
|
+
|
|
312
384
|
## License and Legal Information
|
|
313
385
|
|
|
314
386
|
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license.md) file.
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/index.ts
CHANGED
|
@@ -3,8 +3,10 @@ plugins.early.start('@git.zone/tsrust');
|
|
|
3
3
|
|
|
4
4
|
export * from './mod_fs/index.js';
|
|
5
5
|
export * from './mod_cargo/index.js';
|
|
6
|
+
export * from './mod_artifact/index.js';
|
|
6
7
|
export * from './mod_cli/index.js';
|
|
7
8
|
export * from './mod_elf/index.js';
|
|
9
|
+
export * from './mod_provenance/index.js';
|
|
8
10
|
export * from './mod_toolchain/index.js';
|
|
9
11
|
|
|
10
12
|
plugins.early.stop();
|