@dk/hipp 0.1.5
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/README.md +87 -0
- package/hipp.js +357 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# ๐ HIPP: High Integrity Package Publisher
|
|
2
|
+
|
|
3
|
+
By Dmytri Kleiner <dev@dmytri.to>
|
|
4
|
+
|
|
5
|
+
**HIPP** is a minimalist, stateless publishing tool designed to eliminate the
|
|
6
|
+
friction of version-bump commits. It treats your **Git Tags** as the single
|
|
7
|
+
source of truth, enforcing a "Ground State" where your `package.json` version
|
|
8
|
+
remains permanently at `0.0.0`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ๐ง Why HIPP?
|
|
13
|
+
|
|
14
|
+
Traditional NPM versioning requires you to store your "Version of Truth" inside
|
|
15
|
+
your source code files (`package.json`). This creates a **State Conflict** that
|
|
16
|
+
leads to several systemic problems:
|
|
17
|
+
|
|
18
|
+
### 1. Integrity Failure in standard workflow
|
|
19
|
+
`npm version` and `git tag` are two distinct, non-atomic actions. If you tag a
|
|
20
|
+
commit but forget to update the JSON (or vice-versa), your registry package and
|
|
21
|
+
your Git history diverge. This scenario makes it impossible to guarantee that
|
|
22
|
+
the code in the registry matches the code at that tag.
|
|
23
|
+
|
|
24
|
+
**HIPP ensures they are fundamentally linked by extracting the version directly
|
|
25
|
+
from the Git Tag.**
|
|
26
|
+
|
|
27
|
+
### 2. Chore" Noise & Merge Conflicts
|
|
28
|
+
Every release usually requires a "chore: bump version" commit. When multiple
|
|
29
|
+
branches are developed simultaneously, these version changes cause constant,
|
|
30
|
+
trivial merge conflicts.
|
|
31
|
+
|
|
32
|
+
**HIPP makes your `package.json` version immutable (0.0.0), so it never
|
|
33
|
+
conflicts and your git history stays clean.**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ๐ Usage
|
|
38
|
+
|
|
39
|
+
### 1. The Setup Set your project's `package.json` version to `0.0.0`.
|
|
40
|
+
This is the **HIPP Doctrine**.
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{ "name": "your-package", "version": "0.0.0" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Tag and Publish with HIPP
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git tag v1.0.0
|
|
50
|
+
npx @dk/hipp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
HIPP will:
|
|
54
|
+
1. **Verify**: Ensure the `0.0.0` doctrine is being followed.
|
|
55
|
+
2. **Clean Check**: Ensure your git status is clean (no uncommitted local
|
|
56
|
+
"drift").
|
|
57
|
+
3. **Validate**: Extract and verify the latest tag against Semver rules.
|
|
58
|
+
4. **Confirm**: Ask for a ๐ confirmation before ignition.
|
|
59
|
+
5. **Restore**: Automatically return your local files to `0.0.0` after the
|
|
60
|
+
smoke clears.
|
|
61
|
+
|
|
62
|
+
### Options
|
|
63
|
+
* `-y, --yes`: Skip the confirmation (ideal for CI/CD pipelines).
|
|
64
|
+
|
|
65
|
+
If you need to pass additional flags to npm publish (like access or a custom registry), use the -- separator:
|
|
66
|
+
Bash
|
|
67
|
+
|
|
68
|
+
`npx @dk/hipp -- --access public --tag beta`
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## โ๏ธ License
|
|
74
|
+
|
|
75
|
+
**0BSD** (BSD Zero Clause License) By Dmytri Kleiner <dev@dmytri.to>
|
|
76
|
+
|
|
77
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
78
|
+
purpose with or without fee is hereby granted.
|
|
79
|
+
|
|
80
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
81
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
82
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
83
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
84
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
85
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
86
|
+
PERFORMANCE OF THIS SOFTWARE. ```
|
|
87
|
+
|
package/hipp.js
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync, execFileSync } = require('child_process');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const semver = require('semver');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
|
|
10
|
+
const log = {
|
|
11
|
+
error: (msg) => console.error(`\x1b[31m${msg}\x1b[0m`),
|
|
12
|
+
info: (msg) => console.log(`\x1b[36m${msg}\x1b[0m`),
|
|
13
|
+
success: (msg) => console.log(`\x1b[32m${msg}\x1b[0m`),
|
|
14
|
+
warn: (msg) => console.warn(`\x1b[33m${msg}\x1b[0m`),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function fail(msg) {
|
|
18
|
+
log.error(msg);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function git(args, options = {}) {
|
|
23
|
+
return execFileSync('git', args, {
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
|
+
...options,
|
|
27
|
+
}).trim();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function runCmd(cmd, args, options = {}) {
|
|
31
|
+
const result = spawnSync(cmd, args, {
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
34
|
+
...options,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (result.error) throw result.error;
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sha256(input) {
|
|
42
|
+
return crypto.createHash('sha256').update(input).digest('hex');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function safeStageName(name) {
|
|
46
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getVersionFromExactTagOnHead() {
|
|
50
|
+
try {
|
|
51
|
+
const rawTag = git(['describe', '--tags', '--exact-match', 'HEAD']);
|
|
52
|
+
if (!rawTag.startsWith('v')) {
|
|
53
|
+
throw new Error(`tag "${rawTag}" must start with "v"`);
|
|
54
|
+
}
|
|
55
|
+
const clean = semver.clean(rawTag);
|
|
56
|
+
if (!clean) {
|
|
57
|
+
throw new Error(`tag "${rawTag}" is not valid semver`);
|
|
58
|
+
}
|
|
59
|
+
return { rawTag, version: clean };
|
|
60
|
+
} catch (err) {
|
|
61
|
+
fail(`โ Integrity Error: HEAD must have an exact v-prefixed semver tag. ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ensureCleanRepo(pkg) {
|
|
66
|
+
if (pkg.version !== '0.0.0') {
|
|
67
|
+
fail('โ Integrity Violation: package.json version must be 0.0.0');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (pkg.workspaces) {
|
|
71
|
+
fail('โ Workspace Error: HIPP currently only supports single-package repositories.');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const status = git(['status', '--porcelain']);
|
|
75
|
+
if (status) {
|
|
76
|
+
fail('โ Integrity Error: Uncommitted changes found.');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function ensureMutableRefPolicy() {
|
|
81
|
+
let branch;
|
|
82
|
+
try {
|
|
83
|
+
branch = git(['symbolic-ref', '--short', 'HEAD']);
|
|
84
|
+
} catch {
|
|
85
|
+
fail('โ Ref Error: Detached HEAD is not allowed for publish.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let upstream;
|
|
89
|
+
try {
|
|
90
|
+
upstream = git(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']);
|
|
91
|
+
} catch {
|
|
92
|
+
fail(`โ Ref Error: Branch "${branch}" must track an upstream branch.`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const head = git(['rev-parse', 'HEAD']);
|
|
96
|
+
const upstreamHead = git(['rev-parse', '@{u}']);
|
|
97
|
+
|
|
98
|
+
if (head !== upstreamHead) {
|
|
99
|
+
fail(`โ Ref Error: HEAD (${head.slice(0, 12)}) must exactly match upstream (${upstream} ${upstreamHead.slice(0, 12)}).`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { branch, upstream, head };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function ensureRemoteProvenance(rawTag, headSha) {
|
|
106
|
+
let remoteUrl;
|
|
107
|
+
try {
|
|
108
|
+
remoteUrl = git(['remote', 'get-url', 'origin']);
|
|
109
|
+
} catch {
|
|
110
|
+
fail('โ Provenance Error: Remote "origin" is required.');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const tagObjectLocal = git(['rev-parse', rawTag]);
|
|
114
|
+
const tagCommitLocal = git(['rev-list', '-n', '1', rawTag]);
|
|
115
|
+
|
|
116
|
+
const remoteTagObject = git(['ls-remote', '--tags', 'origin', `refs/tags/${rawTag}`])
|
|
117
|
+
.split('\t')[0]
|
|
118
|
+
.trim();
|
|
119
|
+
|
|
120
|
+
const remoteTagCommit = git(['ls-remote', '--tags', 'origin', `refs/tags/${rawTag}^{}`])
|
|
121
|
+
.split('\t')[0]
|
|
122
|
+
.trim();
|
|
123
|
+
|
|
124
|
+
if (!remoteTagObject) {
|
|
125
|
+
fail(`โ Provenance Error: Tag "${rawTag}" does not exist on origin (${remoteUrl}).`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (remoteTagObject !== tagObjectLocal) {
|
|
129
|
+
fail(`โ Provenance Error: Local tag object for "${rawTag}" does not match origin.`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (remoteTagCommit && remoteTagCommit !== tagCommitLocal) {
|
|
133
|
+
fail(`โ Provenance Error: Local tag target commit for "${rawTag}" does not match origin.`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const remoteContains = runCmd('git', ['branch', '-r', '--contains', headSha], {
|
|
137
|
+
encoding: 'utf8',
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (remoteContains.status !== 0) {
|
|
141
|
+
fail('โ Provenance Error: Could not verify remote containment for HEAD.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const remoteBranches = remoteContains.stdout
|
|
145
|
+
.split('\n')
|
|
146
|
+
.map((s) => s.trim().replace(/^\* /, ''))
|
|
147
|
+
.filter(Boolean);
|
|
148
|
+
|
|
149
|
+
const onOrigin = remoteBranches.some((b) => b.startsWith('origin/'));
|
|
150
|
+
if (!onOrigin) {
|
|
151
|
+
fail('โ Provenance Error: HEAD commit is not contained in any origin remote branch.');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { remoteUrl };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function ensureLockIntegrity(pkg) {
|
|
158
|
+
const lockPath = path.join(process.cwd(), 'package-lock.json');
|
|
159
|
+
if (!fs.existsSync(lockPath)) {
|
|
160
|
+
fail('โ Lock Error: package-lock.json is required.');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
git(['ls-files', '--error-unmatch', 'package-lock.json']);
|
|
165
|
+
} catch {
|
|
166
|
+
fail('โ Lock Error: package-lock.json must be tracked by git.');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const pkgJsonRaw = fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8');
|
|
170
|
+
const lockJsonRaw = fs.readFileSync(lockPath, 'utf8');
|
|
171
|
+
|
|
172
|
+
let lock;
|
|
173
|
+
try {
|
|
174
|
+
lock = JSON.parse(lockJsonRaw);
|
|
175
|
+
} catch {
|
|
176
|
+
fail('โ Lock Error: package-lock.json is not valid JSON.');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!lock.name || !lock.version) {
|
|
180
|
+
fail('โ Lock Error: package-lock.json is missing top-level name/version.');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (lock.name !== pkg.name) {
|
|
184
|
+
fail(`โ Lock Error: package-lock.json name mismatch. Expected ${pkg.name}, got ${lock.name}.`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (lock.version !== pkg.version) {
|
|
188
|
+
fail(`โ Lock Error: package-lock.json version mismatch. Expected ${pkg.version}, got ${lock.version}.`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const ciCheck = runCmd('npm', ['ci', '--ignore-scripts', '--dry-run'], {
|
|
192
|
+
cwd: process.cwd(),
|
|
193
|
+
env: { ...process.env, npm_config_fund: 'false', npm_config_audit: 'false' },
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (ciCheck.status !== 0) {
|
|
197
|
+
process.stderr.write(ciCheck.stderr || '');
|
|
198
|
+
fail('โ Lock Error: `npm ci --ignore-scripts --dry-run` failed.');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
lockfileSha256: sha256(lockJsonRaw),
|
|
203
|
+
packageJsonSha256: sha256(pkgJsonRaw),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function getTrackedFiles() {
|
|
208
|
+
const out = execFileSync('git', ['ls-files', '-z'], {
|
|
209
|
+
encoding: 'buffer',
|
|
210
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return out
|
|
214
|
+
.toString('utf8')
|
|
215
|
+
.split('\0')
|
|
216
|
+
.filter(Boolean);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function copyTrackedFiles(stageDir, files) {
|
|
220
|
+
const repoRoot = process.cwd();
|
|
221
|
+
|
|
222
|
+
for (const rel of files) {
|
|
223
|
+
const src = path.join(repoRoot, rel);
|
|
224
|
+
const dest = path.join(stageDir, rel);
|
|
225
|
+
const stat = fs.lstatSync(src);
|
|
226
|
+
|
|
227
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
228
|
+
|
|
229
|
+
if (stat.isSymbolicLink()) {
|
|
230
|
+
const target = fs.readlinkSync(src);
|
|
231
|
+
fs.symlinkSync(target, dest);
|
|
232
|
+
} else if (stat.isDirectory()) {
|
|
233
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
234
|
+
} else if (stat.isFile()) {
|
|
235
|
+
fs.copyFileSync(src, dest);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function confirmPrompt(name, version) {
|
|
241
|
+
const rl = readline.createInterface({
|
|
242
|
+
input: process.stdin,
|
|
243
|
+
output: process.stdout,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const answer = await new Promise((resolve) => {
|
|
248
|
+
rl.question(`๐ Confirm launch of ${name}@${version}? [y/N] `, resolve);
|
|
249
|
+
});
|
|
250
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
251
|
+
} finally {
|
|
252
|
+
rl.close();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function run() {
|
|
257
|
+
const args = process.argv.slice(2);
|
|
258
|
+
const sep = args.indexOf('--');
|
|
259
|
+
const hippArgs = sep !== -1 ? args.slice(0, sep) : args;
|
|
260
|
+
const npmArgs = sep !== -1 ? args.slice(sep + 1) : [];
|
|
261
|
+
const skipPrompt = hippArgs.includes('--yes') || hippArgs.includes('-y');
|
|
262
|
+
|
|
263
|
+
const pkgPath = path.resolve(process.cwd(), 'package.json');
|
|
264
|
+
if (!fs.existsSync(pkgPath)) {
|
|
265
|
+
fail('โ Error: No package.json found.');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
269
|
+
ensureCleanRepo(pkg);
|
|
270
|
+
|
|
271
|
+
const { rawTag, version } = getVersionFromExactTagOnHead();
|
|
272
|
+
const refInfo = ensureMutableRefPolicy();
|
|
273
|
+
const provenance = ensureRemoteProvenance(rawTag, refInfo.head);
|
|
274
|
+
const lockInfo = ensureLockIntegrity(pkg);
|
|
275
|
+
const trackedFiles = getTrackedFiles();
|
|
276
|
+
|
|
277
|
+
log.info('๐ HIPP: High Integrity Package Publisher');
|
|
278
|
+
log.success(`๐ท๏ธ Git Tag Truth: ${rawTag}`);
|
|
279
|
+
log.success(`๐ฟ Ref Truth: ${refInfo.branch} == ${refInfo.upstream}`);
|
|
280
|
+
log.success(`๐ Origin Truth: ${provenance.remoteUrl}`);
|
|
281
|
+
log.success(`๐ Lock Truth: ${lockInfo.lockfileSha256.slice(0, 12)}โฆ`);
|
|
282
|
+
|
|
283
|
+
if (!skipPrompt) {
|
|
284
|
+
const confirmed = await confirmPrompt(pkg.name, version);
|
|
285
|
+
if (!confirmed) {
|
|
286
|
+
log.warn('Aborted.');
|
|
287
|
+
process.exit(0);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const stageDir = fs.mkdtempSync(
|
|
292
|
+
path.join(os.tmpdir(), `hipp-${safeStageName(pkg.name)}-`)
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
log.info(`๐๏ธ Staging tracked files to ${stageDir}...`);
|
|
297
|
+
copyTrackedFiles(stageDir, trackedFiles);
|
|
298
|
+
|
|
299
|
+
const stagedPkgPath = path.join(stageDir, 'package.json');
|
|
300
|
+
const stagedPkg = JSON.parse(fs.readFileSync(stagedPkgPath, 'utf8'));
|
|
301
|
+
stagedPkg.version = version;
|
|
302
|
+
fs.writeFileSync(stagedPkgPath, JSON.stringify(stagedPkg, null, 2) + '\n');
|
|
303
|
+
|
|
304
|
+
log.info('๐ฅ Ignition...');
|
|
305
|
+
|
|
306
|
+
const result = spawnSync('npm', ['publish', ...npmArgs], {
|
|
307
|
+
cwd: stageDir,
|
|
308
|
+
stdio: 'inherit',
|
|
309
|
+
env: {
|
|
310
|
+
...process.env,
|
|
311
|
+
npm_config_fund: 'false',
|
|
312
|
+
npm_config_audit: 'false',
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
if (result.error) {
|
|
317
|
+
throw result.error;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (result.status !== 0) {
|
|
321
|
+
throw new Error(`npm publish exited with code ${result.status}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
log.success(`\nโจ Success! Published ${pkg.name}@${version}`);
|
|
325
|
+
} catch (err) {
|
|
326
|
+
fail(`\n๐ฅ Launch failed: ${err.message}`);
|
|
327
|
+
} finally {
|
|
328
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
329
|
+
log.info('๐งน Off-site staging cleared. Source integrity preserved.');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
334
|
+
console.log(`\x1b[36mHIPP - High Integrity Package Publisher\x1b[0m
|
|
335
|
+
|
|
336
|
+
Usage:
|
|
337
|
+
npx hipp [options] [-- npm-options]
|
|
338
|
+
|
|
339
|
+
Options:
|
|
340
|
+
-y, --yes Skip confirmation prompt
|
|
341
|
+
-h, --help Show this help
|
|
342
|
+
|
|
343
|
+
Integrity rules:
|
|
344
|
+
- package.json version must be 0.0.0
|
|
345
|
+
- package-lock.json must exist and be tracked
|
|
346
|
+
- npm ci --ignore-scripts --dry-run must succeed
|
|
347
|
+
- repository must be clean
|
|
348
|
+
- HEAD must be on a branch with an upstream
|
|
349
|
+
- HEAD must exactly match upstream
|
|
350
|
+
- HEAD must have an exact v-prefixed semver tag
|
|
351
|
+
- the exact tag must exist on origin and match locally
|
|
352
|
+
- HEAD commit must be contained in an origin remote branch
|
|
353
|
+
- only git-tracked files are staged
|
|
354
|
+
- only staged package.json is rewritten`);
|
|
355
|
+
} else {
|
|
356
|
+
run();
|
|
357
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dk/hipp",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "High Integrity Package Publisher",
|
|
5
|
+
"main": "hipp.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"hipp": "./hipp.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"hipp": "npm install && node hipp.js"
|
|
11
|
+
},
|
|
12
|
+
"author": "Dmytri Kleiner <dev@dmytri.to>",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"semver": ">=7.6.0"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18.0.0"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"license": "0BSD"
|
|
23
|
+
}
|