hushrun 0.0.0-stage → 0.3.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/bin/hushrun.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ // The `hushrun` command of the hushrun package. It runs the hushrun binary for this platform
3
+ // with the same arguments, stdio and environment, and ends as the binary ended: with its exit
4
+ // code, or by the same signal.
5
+ //
6
+ // The binary comes from, in order: the platform package (@hushrun/cli-<os>-<cpu>, an optional
7
+ // dependency that npm installs only on its own platform), vendor/ in this package (a local build,
8
+ // npm/build.mjs --local), and the file HUSHRUN_BINARY names.
9
+ 'use strict';
10
+
11
+ const { spawn } = require('node:child_process');
12
+ const fs = require('node:fs');
13
+ const os = require('node:os');
14
+ const path = require('node:path');
15
+
16
+ const pkg = require('../package.json');
17
+
18
+ const RELEASES = 'https://github.com/hushrun/hushrun/releases/latest/download';
19
+
20
+ /** The platform package for an OS and CPU as Node names them, such as @hushrun/cli-darwin-arm64. */
21
+ function platformPackage(platform, arch) {
22
+ return `@hushrun/cli-${platform}-${arch}`;
23
+ }
24
+
25
+ /** The binary's file name, in a platform package's bin/ or in vendor/. */
26
+ function binaryName(platform) {
27
+ return platform === 'win32' ? 'hushrun.exe' : 'hushrun';
28
+ }
29
+
30
+ /** The binary to run, as `{ file, from }`, or `{ missing }`: the line to print when there is none. */
31
+ function findBinary({ env = process.env, platform = process.platform, arch = process.arch } = {}) {
32
+ const name = platformPackage(platform, arch);
33
+ const exe = binaryName(platform);
34
+ try {
35
+ return { file: require.resolve(`${name}/bin/${exe}`), from: name };
36
+ } catch {
37
+ // Not installed: npm leaves out the other platforms' packages, and every optional
38
+ // dependency with --omit=optional.
39
+ }
40
+ const vendored = path.join(__dirname, '..', 'vendor', exe);
41
+ if (fs.existsSync(vendored)) {
42
+ return { file: vendored, from: 'vendor' };
43
+ }
44
+ if (env.HUSHRUN_BINARY) {
45
+ return { file: path.resolve(env.HUSHRUN_BINARY), from: 'HUSHRUN_BINARY' };
46
+ }
47
+ return { missing: missingLine(platform, arch) };
48
+ }
49
+
50
+ /** What to say when there is no binary: why, and the installer to use instead. */
51
+ function missingLine(platform, arch) {
52
+ const install =
53
+ platform === 'win32'
54
+ ? `powershell -ExecutionPolicy Bypass -c "irm ${RELEASES}/hushrun-installer.ps1 | iex"`
55
+ : `curl --proto '=https' --tlsv1.2 -LsSf ${RELEASES}/hushrun-installer.sh | sh`;
56
+ const name = platformPackage(platform, arch);
57
+ if (Object.hasOwn(pkg.optionalDependencies || {}, name)) {
58
+ return (
59
+ `hushrun: ${name}, which holds the hushrun binary, isn't installed (were optional ` +
60
+ `dependencies left out?); reinstall ${pkg.name}, or install hushrun with: ${install}`
61
+ );
62
+ }
63
+ return (
64
+ `hushrun: ${pkg.name} has no hushrun binary for ${platform}-${arch}; set HUSHRUN_BINARY ` +
65
+ `to one, or install hushrun with: ${install}`
66
+ );
67
+ }
68
+
69
+ /** Whether `file` is this script, as node_modules/.bin/hushrun is: running it would loop. */
70
+ function isThisScript(file) {
71
+ try {
72
+ return fs.realpathSync(file) === fs.realpathSync(__filename);
73
+ } catch {
74
+ return false;
75
+ }
76
+ }
77
+
78
+ /** Runs `file` with `args`, and ends this process as it ended. */
79
+ function run(file, args) {
80
+ // Signals sent to this process alone, such as a CI runner's or a process manager's, go on to
81
+ // the binary. A terminal's Ctrl+C reaches both anyway. On Windows, Ctrl+C reaches every process
82
+ // on the console and killing the binary would end it at once, so this process only waits.
83
+ const windows = process.platform === 'win32';
84
+ const signals = windows ? ['SIGINT', 'SIGBREAK'] : ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];
85
+ let child;
86
+ const relay = (signal) => {
87
+ if (!windows && child && child.exitCode === null && child.signalCode === null) {
88
+ child.kill(signal);
89
+ }
90
+ };
91
+ const release = () => signals.forEach((signal) => process.removeListener(signal, relay));
92
+ signals.forEach((signal) => process.on(signal, relay));
93
+
94
+ const cannotRun = (err) => {
95
+ release();
96
+ const why =
97
+ err.code === 'ENOENT'
98
+ ? 'no such file'
99
+ : err.code === 'EACCES'
100
+ ? 'permission denied (is it executable?)'
101
+ : err.message;
102
+ process.stderr.write(`hushrun: can't run ${file}: ${why}\n`);
103
+ process.exitCode = 1;
104
+ };
105
+ try {
106
+ child = spawn(file, args, { stdio: 'inherit' });
107
+ } catch (err) {
108
+ cannotRun(err);
109
+ return;
110
+ }
111
+ child.on('error', (err) => {
112
+ // After a start, an error is a signal that couldn't be relayed: the exit still comes.
113
+ if (child.pid === undefined) {
114
+ cannotRun(err);
115
+ }
116
+ });
117
+ child.on('exit', (code, signal) => {
118
+ release();
119
+ if (signal) {
120
+ // The same signal, now acting as by default, so that whatever waits for this process (a
121
+ // shell, npm, a CI runner) sees the binary's ending.
122
+ process.kill(process.pid, signal);
123
+ // A signal Node ignores, such as SIGPIPE: exit as shells report one.
124
+ setTimeout(() => process.exit(128 + (os.constants.signals[signal] || 0)), 1000);
125
+ return;
126
+ }
127
+ process.exitCode = code;
128
+ });
129
+ }
130
+
131
+ function main() {
132
+ const found = findBinary();
133
+ if (found.missing) {
134
+ process.stderr.write(`${found.missing}\n`);
135
+ process.exitCode = 1;
136
+ return;
137
+ }
138
+ if (found.from === 'HUSHRUN_BINARY' && isThisScript(found.file)) {
139
+ process.stderr.write(
140
+ `hushrun: HUSHRUN_BINARY names this npm command (${found.file}); name a hushrun binary\n`,
141
+ );
142
+ process.exitCode = 1;
143
+ return;
144
+ }
145
+ // The package `hushrun init` adds to devDependencies: this one, unless already set (a test
146
+ // can name a tarball).
147
+ if (process.env.HUSHRUN_NPM_PACKAGE === undefined) {
148
+ process.env.HUSHRUN_NPM_PACKAGE = `${pkg.name}@${pkg.version}`;
149
+ }
150
+ run(found.file, process.argv.slice(2));
151
+ }
152
+
153
+ if (require.main === module) {
154
+ main();
155
+ }
156
+
157
+ module.exports = { platformPackage, binaryName, findBinary, missingLine };
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "hushrun",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
4
+ "description": "Skip tests, lint and typecheck when this exact code already passed them, on laptops, in CI and in coding agents' sandboxes.",
5
+ "keywords": [
6
+ "ci",
7
+ "ci-cd",
8
+ "continuous-integration",
9
+ "github-actions",
10
+ "gitlab-ci",
11
+ "checks",
12
+ "test",
13
+ "testing",
14
+ "lint",
15
+ "cache",
16
+ "test-cache",
17
+ "skip-ci",
18
+ "monorepo",
19
+ "coding-agents",
20
+ "ai-agents",
21
+ "pre-push",
22
+ "artifact-promotion",
23
+ "devops"
24
+ ],
25
+ "homepage": "https://hushrun.com",
26
+ "bugs": {
27
+ "email": "support@hushrun.com"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/hushrun/hushrun.git"
32
+ },
33
+ "license": "SEE LICENSE IN LICENSE.md",
34
+ "bin": {
35
+ "hushrun": "bin/hushrun.js"
36
+ },
37
+ "files": [
38
+ "bin/",
39
+ "vendor/",
40
+ "LICENSE.md",
41
+ "THIRD-PARTY-NOTICES.md"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "optionalDependencies": {
47
+ "@hushrun/cli-darwin-arm64": "0.3.0",
48
+ "@hushrun/cli-darwin-x64": "0.3.0",
49
+ "@hushrun/cli-linux-arm64": "0.3.0",
50
+ "@hushrun/cli-linux-x64": "0.3.0",
51
+ "@hushrun/cli-win32-arm64": "0.3.0",
52
+ "@hushrun/cli-win32-x64": "0.3.0"
53
+ }
54
+ }