coderifts 1.8.4 → 2.0.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/README.md CHANGED
@@ -64,6 +64,48 @@ Interactive configuration generator. Creates a `.coderifts.yml` file with indust
64
64
 
65
65
  Save your CodeRifts API key for cloud features. Get a free key at [app.coderifts.com/api/signup](https://app.coderifts.com/api/signup).
66
66
 
67
+ ### `coderifts publish-gate`
68
+
69
+ Gate **npm publish** on the same contract-preflight family as the pre-push hook:
70
+ `before` = last published / merge-base baseline, `after` = working tree. Exit **0**
71
+ only when `execution_action` permits publish; **BLOCK**, resolver errors, empty
72
+ before, and preflight unreachability all exit **1** (fail-closed). Empty `before`
73
+ is **never** treated as a silent NEW_ARTIFACT pass.
74
+
75
+ | Flag | Description | Default |
76
+ |------|-------------|---------|
77
+ | `--spec <path>` | Contract artifact path | `git config coderifts.specPath` or `api/openapi.yaml` |
78
+ | `--json` | Machine-readable result | `false` |
79
+
80
+ **Recommended package.json wiring:**
81
+
82
+ ```json
83
+ {
84
+ "scripts": {
85
+ "prepublishOnly": "coderifts publish-gate"
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Before resolution order (fail-closed):**
91
+ 1. Git tag of `package.json` version (`vX.Y.Z`, then `X.Y.Z`)
92
+ 2. `git merge-base HEAD origin/main` (fallbacks: `origin/master`, `main`, `master`)
93
+ 3. Exit 1 with a clear error — never invent an empty baseline
94
+
95
+ On success the command prints a **receipt reference** when the preflight path issues one.
96
+
97
+ ### Git hook (`coderifts hook install`)
98
+
99
+ Installs a **pre-push** hook that diffs the configured OpenAPI path against a
100
+ resolved baseline (remote tip, or for new branches a merge-base with the
101
+ default branch). Empty-before allow is not used. The hook prefers closed-set
102
+ `execution_action` from the diff API (legacy `omega_decision` only when action
103
+ is absent). Configure with `git config coderifts.apiKey` and optional
104
+ `git config coderifts.specPath`.
105
+
106
+ After upgrading the `coderifts` CLI, re-run `coderifts hook install` so the
107
+ installed hook matches the package (already-installed hooks are not
108
+ auto-updated).
67
109
  ## CI/CD Integration
68
110
 
69
111
  ### GitHub Actions
package/bin/coderifts.js CHANGED
@@ -25,6 +25,40 @@ program
25
25
  await diff(oldSpec, newSpec, options);
26
26
  });
27
27
 
28
+ // ── deploy-gate command (deploy-gate #8, phase-1 ADVISORY) ──
29
+ // Gates a deploy on the current { environment, artifact } via a preflight receipt. Phase 1 is
30
+ // advisory: it reports but exits 0 even on deny unless the step is marked --enforce /
31
+ // CODERIFTS_DEPLOY_ENFORCE=true. Composes the validated pure deployGate (@coderifts/agent-guard).
32
+ program
33
+ .command('deploy-gate')
34
+ .description('Gate a deploy on the current { environment, artifact } using a preflight receipt (phase-1 advisory)')
35
+ .option('--env <environment>', 'Target environment (e.g. production, staging)')
36
+ .option('--artifact <artifact_id>', 'Immutable artifact identity being deployed (content digest or commit SHA)')
37
+ .option('--receipt <file>', 'Path to the deploy-scoped receipt JSON produced by preflight')
38
+ .option('--json', 'Output the binding result as JSON')
39
+ .option('--enforce', 'Treat the step as enforcing: attest ENFORCING and exit non-zero on a gate failure')
40
+ .action(async (options) => {
41
+ const { runDeployGate } = require('../src/commands/deploy-gate');
42
+ await runDeployGate(options);
43
+ });
44
+
45
+ // ── publish-gate — npm prepublishOnly (same decision family as pre-push, fail-closed before) ──
46
+ // Exit status is set only at this command boundary (library never process.exit).
47
+ // program.parse() does not await async actions; process.exit here makes the gate's
48
+ // fail-closed status authoritative for npm prepublishOnly / child_process callers.
49
+ program
50
+ .command('publish-gate')
51
+ .description('Gate npm publish on contract-artifact preflight (before=git baseline, after=working tree)')
52
+ .option('--spec <path>', 'Contract artifact path (default: git config coderifts.specPath or api/openapi.yaml)')
53
+ .option('--json', 'Machine-readable JSON result')
54
+ .action(async (options) => {
55
+ const { runPublishGate } = require('../src/commands/publish-gate');
56
+ const result = await runPublishGate(options);
57
+ const code = result && typeof result.exitCode === 'number' ? result.exitCode : 1;
58
+ process.exitCode = code;
59
+ process.exit(code);
60
+ });
61
+
28
62
  // ── init command ──
29
63
  program
30
64
  .command('init [template]')
@@ -90,4 +124,13 @@ corpusCmd
90
124
  corpusVerify(options);
91
125
  });
92
126
 
93
- program.parse();
127
+ // parseAsync: required so async command actions (publish-gate, diff, deploy-gate, …)
128
+ // complete before the process ends. program.parse() discards the action promise and
129
+ // can leave process.exitCode at the default 0 even after a fail-closed gate printed errors.
130
+ program.parseAsync(process.argv).catch((err) => {
131
+ console.error(err);
132
+ process.exitCode = typeof process.exitCode === 'number' && process.exitCode !== 0
133
+ ? process.exitCode
134
+ : 1;
135
+ process.exit(process.exitCode);
136
+ });