repopilot 0.9.0 → 0.11.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 +52 -14
- package/bin/repopilot.js +129 -12
- package/package.json +8 -3
- package/scripts/install.js +0 -141
- package/scripts/smoke-product.sh +0 -264
- package/scripts/verify-release.sh +0 -84
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ and framework health checks. It gives developers useful terminal reports, CI gat
|
|
|
15
15
|
SARIF for GitHub Code Scanning, and Markdown briefs that can be pasted into Claude
|
|
16
16
|
Code, ChatGPT, Cursor, or another coding assistant.
|
|
17
17
|
|
|
18
|
+
Version `0.11.0` is a signal quality and CI polish release: it reduces
|
|
19
|
+
low-value self-audit findings, adds first-party Action parity for priority/rule
|
|
20
|
+
filters, and tightens the `doctor -> scan/review -> ai context -> CI gate` loop
|
|
21
|
+
while keeping all scans local.
|
|
22
|
+
|
|
18
23
|
## Why RepoPilot?
|
|
19
24
|
|
|
20
25
|
Most linters and audit tools stop at the terminal. RepoPilot bridges the gap to your AI assistant:
|
|
@@ -36,23 +41,25 @@ repopilot ai context . | pbcopy # macOS: paste into your coding assistant
|
|
|
36
41
|
|
|
37
42
|
- Repository scanning for projects, folders, and individual files
|
|
38
43
|
- Gitignore-aware walking with built-in ignores for common build, cache, vendor, and native platform directories
|
|
39
|
-
- Architecture findings: oversized files, deep nesting, too many modules per directory
|
|
44
|
+
- Architecture findings: oversized files, deep nesting, deep relative imports, risky barrel files, too many modules per directory
|
|
40
45
|
- Coupling analysis: excessive fan-out, high-instability hubs, circular dependencies
|
|
41
46
|
- Code quality findings: cyclomatic complexity density, long functions, TODO/FIXME/HACK markers
|
|
42
47
|
- Security findings: hardcoded secret candidates, committed private keys, committed `.env` files
|
|
43
48
|
- Testing findings: missing test folder, source files without test counterparts
|
|
44
|
-
- Framework findings for JavaScript, React, React Native, and
|
|
49
|
+
- Framework findings for JavaScript, React, React Native, Expo, and Django projects
|
|
45
50
|
- React Native architecture profile: project kind, New Architecture, Hermes, Codegen, platform mismatch, and package manager signals
|
|
46
51
|
- Workspace scanning (`--workspace`) for npm, yarn, pnpm, and Cargo workspaces with per-package risk summary — parallel per-package scans
|
|
47
52
|
- Evidence-backed findings with stable rule IDs, severity, file paths, line numbers, and snippets
|
|
48
|
-
-
|
|
53
|
+
- 46 built-in rules with titles, descriptions, recommendations, and docs links
|
|
49
54
|
- `repopilot.toml` configuration generated by `repopilot init`
|
|
50
55
|
- Configuration presets (`--preset strict|balanced|lenient`) for quick threshold tuning
|
|
51
56
|
- Baseline workflow for accepting existing findings in legacy repositories
|
|
52
57
|
- CI-friendly failure thresholds with `--fail-on`
|
|
53
58
|
- Git diff-aware review mode for prioritizing findings introduced by changed lines
|
|
59
|
+
- Doctor adoption guidance for config, baseline, CI gate, report, and receipt readiness
|
|
60
|
+
- Audit receipt JSON from `repopilot scan --receipt` for reproducible scan evidence
|
|
54
61
|
- Console, JSON, Markdown, HTML, and SARIF (2.1.0) scan output — SARIF includes per-result category and workspace package properties
|
|
55
|
-
- First-party GitHub Action wrapper for `scan`, `review`, `compare`, and `ai` workflows, with optional SARIF upload for scans
|
|
62
|
+
- First-party GitHub Action wrapper for `scan`, `review`, `compare`, and `ai` workflows, with optional SARIF upload and receipt output for scans
|
|
56
63
|
- Compare mode for diffing two JSON scan reports
|
|
57
64
|
- **`repopilot ai context`** — formats scan output as LLM-ready markdown, paste into Claude Code or ChatGPT to start remediating
|
|
58
65
|
- **`repopilot ai plan`** — turns findings into a prioritized remediation plan
|
|
@@ -105,17 +112,38 @@ cargo build --release
|
|
|
105
112
|
|
|
106
113
|
## Quick Start
|
|
107
114
|
|
|
115
|
+
No config needed. Run this in any repository and see findings immediately:
|
|
116
|
+
|
|
108
117
|
```bash
|
|
109
|
-
repopilot init
|
|
110
118
|
repopilot scan .
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Then get an AI-ready brief to paste into Claude Code, Cursor, or ChatGPT:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
repopilot ai context . | pbcopy # macOS — copies to clipboard
|
|
125
|
+
repopilot ai context . # print to terminal
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
That's the two-minute loop. Scan, read the output, paste the context into your assistant, fix.
|
|
129
|
+
|
|
130
|
+
### Going deeper
|
|
131
|
+
|
|
132
|
+
Once you want CI integration, baseline tracking, or tighter configuration:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
repopilot init # generate repopilot.toml
|
|
136
|
+
repopilot doctor . # check CI / baseline readiness
|
|
137
|
+
repopilot scan . --min-priority p2 --rule security.secret-candidate
|
|
138
|
+
repopilot ai context . --focus security # prepare local AI context
|
|
139
|
+
repopilot baseline create . # accept existing findings as known debt
|
|
140
|
+
repopilot review . --base origin/main --fail-on-priority p1 # CI gate on high-priority risk
|
|
113
141
|
```
|
|
114
142
|
|
|
115
143
|
Save a shareable report:
|
|
116
144
|
|
|
117
145
|
```bash
|
|
118
|
-
repopilot scan . --format markdown --output repopilot-report.md
|
|
146
|
+
repopilot scan . --format markdown --output repopilot-report.md --receipt .repopilot/receipt.json
|
|
119
147
|
```
|
|
120
148
|
|
|
121
149
|
Reduce scan noise while iterating:
|
|
@@ -192,7 +220,7 @@ RepoPilot is a local-first safety layer for AI-assisted and vibe-coded changes.
|
|
|
192
220
|
repopilot ai context . --focus security # get an LLM brief for Claude Code
|
|
193
221
|
repopilot ai plan . --focus security # get a prioritized hardening plan
|
|
194
222
|
repopilot ai prompt . --budget 8k # generate a paste-ready remediation prompt
|
|
195
|
-
repopilot review . --base origin/main --baseline .repopilot/baseline.json --fail-on
|
|
223
|
+
repopilot review . --base origin/main --baseline .repopilot/baseline.json --fail-on-priority p1
|
|
196
224
|
repopilot scan . --workspace --min-severity medium --format markdown --output repopilot-report.md
|
|
197
225
|
```
|
|
198
226
|
|
|
@@ -356,7 +384,8 @@ If your installed RepoPilot version does not support `--output`, redirect stdout
|
|
|
356
384
|
repopilot scan . --format sarif > repopilot.sarif
|
|
357
385
|
```
|
|
358
386
|
|
|
359
|
-
Use JSON when custom scripts need to parse RepoPilot results. Console, Markdown, and HTML reports include the RepoPilot version, risk summary, top rules, and grouped findings for human review. Use SARIF for CI and code scanning integrations, including GitHub Code Scanning.
|
|
387
|
+
Use JSON when custom scripts need to parse RepoPilot results. Console, Markdown, and HTML reports include the RepoPilot version, risk summary, top risk clusters, top rules, and grouped findings for human review. Use SARIF for CI and code scanning integrations, including GitHub Code Scanning.
|
|
388
|
+
Use `--receipt <path>` with `scan` when CI or release processes need compact evidence of the exact RepoPilot version, git state, scan scope, finding counts, language counts, and health score.
|
|
360
389
|
|
|
361
390
|
See [docs/integrations/github-code-scanning.md](docs/integrations/github-code-scanning.md) for a copy-paste GitHub Actions workflow, required permissions, and local validation commands.
|
|
362
391
|
|
|
@@ -377,7 +406,7 @@ Use `--fail-on new-high` to fail CI only when new high or critical findings are
|
|
|
377
406
|
When `--fail-on new-*` is used without `--baseline`, RepoPilot treats all current findings as new. For baseline-based adoption, commit an accepted baseline and scan against it in CI.
|
|
378
407
|
|
|
379
408
|
To upload RepoPilot findings to GitHub Code Scanning, generate SARIF and use `github/codeql-action/upload-sarif`. The workflow must include `security-events: write`.
|
|
380
|
-
The first-party action can also run `command: ai-context`, `command: ai-plan`, or `command: ai-prompt`;
|
|
409
|
+
The first-party action can also run `command: doctor`, `command: ai-context`, `command: ai-plan`, or `command: ai-prompt`; AI commands emit Markdown and do not produce SARIF. Prefer typed action inputs such as `path`, `config`, `baseline`, `fail-on`, `fail-on-priority`, `min-priority`, `rule`, `timing`, `focus`, `budget`, `output`, and `receipt`; `args` remains available for advanced flags.
|
|
381
410
|
|
|
382
411
|
```yaml
|
|
383
412
|
name: RepoPilot
|
|
@@ -403,7 +432,7 @@ jobs:
|
|
|
403
432
|
run: cargo install repopilot
|
|
404
433
|
|
|
405
434
|
- name: Run RepoPilot
|
|
406
|
-
run: repopilot scan . --baseline .repopilot/baseline.json --fail-on new-high
|
|
435
|
+
run: repopilot scan . --baseline .repopilot/baseline.json --fail-on new-high --receipt repopilot-receipt.json
|
|
407
436
|
|
|
408
437
|
- name: Run RepoPilot (SARIF)
|
|
409
438
|
run: repopilot scan . --format sarif --output repopilot.sarif
|
|
@@ -413,6 +442,13 @@ jobs:
|
|
|
413
442
|
if: always()
|
|
414
443
|
with:
|
|
415
444
|
sarif_file: repopilot.sarif
|
|
445
|
+
|
|
446
|
+
- name: Upload audit receipt
|
|
447
|
+
uses: actions/upload-artifact@v4
|
|
448
|
+
if: always()
|
|
449
|
+
with:
|
|
450
|
+
name: repopilot-receipt
|
|
451
|
+
path: repopilot-receipt.json
|
|
416
452
|
```
|
|
417
453
|
|
|
418
454
|
## Roadmap
|
|
@@ -421,7 +457,6 @@ These are planned ideas, not current features:
|
|
|
421
457
|
|
|
422
458
|
- Change Risk Map
|
|
423
459
|
- Better architecture drift detection
|
|
424
|
-
- Optional per-platform npm binary packages for faster installs
|
|
425
460
|
|
|
426
461
|
## Documentation
|
|
427
462
|
|
|
@@ -432,13 +467,16 @@ These are planned ideas, not current features:
|
|
|
432
467
|
| [docs/security.md](docs/security.md) | Local-first trust model, installer security, and vulnerability reporting |
|
|
433
468
|
| [docs/configuration.md](docs/configuration.md) | `repopilot.toml`, presets, ignore files, and baseline adoption |
|
|
434
469
|
| [docs/language-support.md](docs/language-support.md) | Supported language/framework tiers, rule families, and limitations |
|
|
470
|
+
| [docs/risk-engine.md](docs/risk-engine.md) | Risk scoring, priority buckets, signals, and calibration policy |
|
|
435
471
|
| [docs/cli.md](docs/cli.md) | Complete CLI reference — all commands, flags, and exit codes |
|
|
436
472
|
| [docs/commands.md](docs/commands.md) | Task-oriented command guide and common patterns |
|
|
437
473
|
| [docs/rulesets.md](docs/rulesets.md) | Implemented audit rules, categories, and severity levels |
|
|
438
474
|
| [docs/react-native.md](docs/react-native.md) | React Native and Expo detection, findings, and limitations |
|
|
439
475
|
| [docs/integrations/github-code-scanning.md](docs/integrations/github-code-scanning.md) | GitHub Code Scanning SARIF workflow |
|
|
440
476
|
| [docs/release.md](docs/release.md) | Manual release process |
|
|
441
|
-
| [docs/release-
|
|
477
|
+
| [docs/release-announcement-0.11.md](docs/release-announcement-0.11.md) | 0.11.0 launch post and post-publish checks |
|
|
478
|
+
| [docs/release-checklist-0.11.md](docs/release-checklist-0.11.md) | 0.11.0 release readiness checklist |
|
|
479
|
+
| [docs/release-checklist-0.10.md](docs/release-checklist-0.10.md) | 0.10.0 release readiness checklist |
|
|
442
480
|
| [docs/distribution.md](docs/distribution.md) | Distribution channels |
|
|
443
481
|
| [docs/github-ruleset.md](docs/github-ruleset.md) | GitHub branch ruleset configuration |
|
|
444
482
|
| [CHANGELOG.md](CHANGELOG.md) | Version history |
|
package/bin/repopilot.js
CHANGED
|
@@ -4,29 +4,141 @@ const fs = require("node:fs");
|
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const { spawn } = require("node:child_process");
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const SUPPORTED_TARGETS = [
|
|
8
|
+
{ platform: "darwin", arch: "arm64", packageName: "@repopilot/darwin-arm64", binary: "repopilot" },
|
|
9
|
+
{ platform: "darwin", arch: "x64", packageName: "@repopilot/darwin-x64", binary: "repopilot" },
|
|
10
|
+
{
|
|
11
|
+
platform: "linux",
|
|
12
|
+
arch: "arm64",
|
|
13
|
+
libc: "glibc",
|
|
14
|
+
packageName: "@repopilot/linux-arm64-gnu",
|
|
15
|
+
binary: "repopilot",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
platform: "linux",
|
|
19
|
+
arch: "x64",
|
|
20
|
+
libc: "glibc",
|
|
21
|
+
packageName: "@repopilot/linux-x64-gnu",
|
|
22
|
+
binary: "repopilot",
|
|
23
|
+
},
|
|
24
|
+
{ platform: "win32", arch: "x64", packageName: "@repopilot/win32-x64-msvc", binary: "repopilot.exe" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
class UnsupportedPlatformError extends Error {
|
|
28
|
+
constructor(platform, arch, libc) {
|
|
29
|
+
const runtime = libc ? `${platform}/${arch}/${libc}` : `${platform}/${arch}`;
|
|
30
|
+
super(`Unsupported npm platform: ${runtime}`);
|
|
31
|
+
this.name = "UnsupportedPlatformError";
|
|
32
|
+
this.code = "ERR_REPOPILOT_UNSUPPORTED_PLATFORM";
|
|
33
|
+
this.platform = platform;
|
|
34
|
+
this.arch = arch;
|
|
35
|
+
this.libc = libc;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class MissingOptionalPackageError extends Error {
|
|
40
|
+
constructor(packageName, cause) {
|
|
41
|
+
super(`Missing optional native package: ${packageName}`);
|
|
42
|
+
this.name = "MissingOptionalPackageError";
|
|
43
|
+
this.code = "ERR_REPOPILOT_MISSING_OPTIONAL_PACKAGE";
|
|
44
|
+
this.packageName = packageName;
|
|
45
|
+
this.cause = cause;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectLibc(platform = process.platform) {
|
|
50
|
+
if (platform !== "linux") {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const report = typeof process.report?.getReport === "function" ? process.report.getReport() : undefined;
|
|
55
|
+
if (report?.header?.glibcVersionRuntime) {
|
|
56
|
+
return "glibc";
|
|
57
|
+
}
|
|
58
|
+
return "musl";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function platformPackage(platform = process.platform, arch = process.arch, libc = detectLibc(platform)) {
|
|
62
|
+
const match = SUPPORTED_TARGETS.find((target) => {
|
|
63
|
+
if (target.platform !== platform || target.arch !== arch) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return target.platform !== "linux" || target.libc === libc;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!match) {
|
|
70
|
+
throw new UnsupportedPlatformError(platform, arch, platform === "linux" ? libc : undefined);
|
|
71
|
+
}
|
|
72
|
+
return match;
|
|
9
73
|
}
|
|
10
74
|
|
|
11
|
-
function
|
|
12
|
-
|
|
75
|
+
function packageRoot(packageName, resolver = require.resolve) {
|
|
76
|
+
try {
|
|
77
|
+
return path.dirname(resolver(`${packageName}/package.json`));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error && error.code === "MODULE_NOT_FOUND") {
|
|
80
|
+
throw new MissingOptionalPackageError(packageName, error);
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
13
84
|
}
|
|
14
85
|
|
|
15
|
-
function resolveBinaryPath() {
|
|
16
|
-
|
|
17
|
-
|
|
86
|
+
function resolveBinaryPath(options = {}) {
|
|
87
|
+
const env = options.env ?? process.env;
|
|
88
|
+
if (env.REPOPILOT_BINARY_PATH) {
|
|
89
|
+
return env.REPOPILOT_BINARY_PATH;
|
|
18
90
|
}
|
|
19
|
-
|
|
91
|
+
|
|
92
|
+
const target = platformPackage(options.platform, options.arch, options.libc);
|
|
93
|
+
return path.join(packageRoot(target.packageName, options.resolver), "bin", target.binary);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function supportedTargetsText() {
|
|
97
|
+
return SUPPORTED_TARGETS.map((target) => {
|
|
98
|
+
const runtime = target.libc
|
|
99
|
+
? `${target.platform}/${target.arch}/${target.libc}`
|
|
100
|
+
: `${target.platform}/${target.arch}`;
|
|
101
|
+
return `${runtime} (${target.packageName})`;
|
|
102
|
+
}).join(", ");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatResolveError(error) {
|
|
106
|
+
if (error.code === "ERR_REPOPILOT_UNSUPPORTED_PLATFORM") {
|
|
107
|
+
return [
|
|
108
|
+
"RepoPilot does not provide an npm native package for this platform.",
|
|
109
|
+
`Detected: ${error.libc ? `${error.platform}/${error.arch}/${error.libc}` : `${error.platform}/${error.arch}`}`,
|
|
110
|
+
`Supported npm targets: ${supportedTargetsText()}`,
|
|
111
|
+
"Install with cargo, Homebrew, GitHub Releases, or set REPOPILOT_BINARY_PATH to an existing repopilot binary.",
|
|
112
|
+
].join("\n");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (error.code === "ERR_REPOPILOT_MISSING_OPTIONAL_PACKAGE") {
|
|
116
|
+
return [
|
|
117
|
+
"RepoPilot optional native package was not installed.",
|
|
118
|
+
`Expected npm package: ${error.packageName}`,
|
|
119
|
+
"Reinstall without omitting optional dependencies, for example: npm install -g repopilot",
|
|
120
|
+
"If your environment blocks optional dependencies, install with cargo or set REPOPILOT_BINARY_PATH to an existing repopilot binary.",
|
|
121
|
+
].join("\n");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return `Failed to resolve RepoPilot binary: ${error.message}`;
|
|
20
125
|
}
|
|
21
126
|
|
|
22
127
|
function main() {
|
|
23
|
-
|
|
128
|
+
let binary;
|
|
129
|
+
try {
|
|
130
|
+
binary = resolveBinaryPath();
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(formatResolveError(error));
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
24
136
|
if (!fs.existsSync(binary)) {
|
|
25
137
|
console.error(
|
|
26
138
|
[
|
|
27
139
|
"RepoPilot binary was not found.",
|
|
28
140
|
`Expected: ${binary}`,
|
|
29
|
-
"
|
|
141
|
+
"Reinstall repopilot, install with cargo, or set REPOPILOT_BINARY_PATH to an existing repopilot binary.",
|
|
30
142
|
].join("\n")
|
|
31
143
|
);
|
|
32
144
|
process.exit(1);
|
|
@@ -55,7 +167,12 @@ if (require.main === module) {
|
|
|
55
167
|
}
|
|
56
168
|
|
|
57
169
|
module.exports = {
|
|
58
|
-
|
|
59
|
-
|
|
170
|
+
SUPPORTED_TARGETS,
|
|
171
|
+
MissingOptionalPackageError,
|
|
172
|
+
UnsupportedPlatformError,
|
|
173
|
+
detectLibc,
|
|
174
|
+
formatResolveError,
|
|
175
|
+
platformPackage,
|
|
60
176
|
resolveBinaryPath,
|
|
177
|
+
supportedTargetsText,
|
|
61
178
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repopilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Local-first CLI for repository audit, architecture risk detection, baseline tracking, and CI-friendly code review.",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"repopilot": "bin/repopilot.js"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"postinstall": "node scripts/install.js",
|
|
19
18
|
"test": "npm run test:npm",
|
|
20
19
|
"test:npm": "node --test tests/npm_wrapper.test.js",
|
|
21
20
|
"release:check": "bash scripts/verify-release.sh",
|
|
@@ -23,10 +22,16 @@
|
|
|
23
22
|
},
|
|
24
23
|
"files": [
|
|
25
24
|
"bin",
|
|
26
|
-
"scripts",
|
|
27
25
|
"README.md",
|
|
28
26
|
"LICENSE"
|
|
29
27
|
],
|
|
28
|
+
"optionalDependencies": {
|
|
29
|
+
"@repopilot/darwin-arm64": "0.11.0",
|
|
30
|
+
"@repopilot/darwin-x64": "0.11.0",
|
|
31
|
+
"@repopilot/linux-arm64-gnu": "0.11.0",
|
|
32
|
+
"@repopilot/linux-x64-gnu": "0.11.0",
|
|
33
|
+
"@repopilot/win32-x64-msvc": "0.11.0"
|
|
34
|
+
},
|
|
30
35
|
"keywords": [
|
|
31
36
|
"cli",
|
|
32
37
|
"audit",
|
package/scripts/install.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const crypto = require("node:crypto");
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const https = require("node:https");
|
|
6
|
-
const os = require("node:os");
|
|
7
|
-
const path = require("node:path");
|
|
8
|
-
const { spawnSync } = require("node:child_process");
|
|
9
|
-
|
|
10
|
-
const ROOT = path.join(__dirname, "..");
|
|
11
|
-
const VENDOR_DIR = path.join(ROOT, "vendor");
|
|
12
|
-
const PACKAGE_JSON = require(path.join(ROOT, "package.json"));
|
|
13
|
-
|
|
14
|
-
function targetTriple(platform = process.platform, arch = process.arch) {
|
|
15
|
-
if (platform === "darwin" && arch === "x64") return "x86_64-apple-darwin";
|
|
16
|
-
if (platform === "darwin" && arch === "arm64") return "aarch64-apple-darwin";
|
|
17
|
-
if (platform === "linux" && arch === "x64") return "x86_64-unknown-linux-gnu";
|
|
18
|
-
if (platform === "linux" && arch === "arm64") return "aarch64-unknown-linux-gnu";
|
|
19
|
-
if (platform === "win32" && arch === "x64") return "x86_64-pc-windows-msvc";
|
|
20
|
-
throw new Error(`Unsupported platform: ${platform}/${arch}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function archiveExt(target) {
|
|
24
|
-
return target.endsWith("windows-msvc") ? "zip" : "tar.gz";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function assetName(version, target) {
|
|
28
|
-
return `repopilot-v${version}-${target}.${archiveExt(target)}`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function releaseUrl(version, file) {
|
|
32
|
-
return `https://github.com/MykytaStel/repopilot/releases/download/v${version}/${file}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function download(url, destination) {
|
|
36
|
-
return new Promise((resolve, reject) => {
|
|
37
|
-
const file = fs.createWriteStream(destination);
|
|
38
|
-
https
|
|
39
|
-
.get(url, (response) => {
|
|
40
|
-
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
41
|
-
file.close();
|
|
42
|
-
fs.rmSync(destination, { force: true });
|
|
43
|
-
download(response.headers.location, destination).then(resolve, reject);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (response.statusCode !== 200) {
|
|
47
|
-
file.close();
|
|
48
|
-
fs.rmSync(destination, { force: true });
|
|
49
|
-
reject(new Error(`Download failed (${response.statusCode}): ${url}`));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
response.pipe(file);
|
|
53
|
-
file.on("finish", () => file.close(resolve));
|
|
54
|
-
})
|
|
55
|
-
.on("error", (error) => {
|
|
56
|
-
file.close();
|
|
57
|
-
fs.rmSync(destination, { force: true });
|
|
58
|
-
reject(error);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function sha256(file) {
|
|
64
|
-
const hash = crypto.createHash("sha256");
|
|
65
|
-
hash.update(fs.readFileSync(file));
|
|
66
|
-
return hash.digest("hex");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function expectedChecksum(checksumText) {
|
|
70
|
-
const first = checksumText.trim().split(/\s+/)[0];
|
|
71
|
-
if (!/^[a-fA-F0-9]{64}$/.test(first)) {
|
|
72
|
-
throw new Error("Invalid checksum file format");
|
|
73
|
-
}
|
|
74
|
-
return first.toLowerCase();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function verifyChecksum(archivePath, checksumText) {
|
|
78
|
-
const expected = expectedChecksum(checksumText);
|
|
79
|
-
const actual = sha256(archivePath);
|
|
80
|
-
if (actual !== expected) {
|
|
81
|
-
throw new Error(`Checksum mismatch for ${path.basename(archivePath)}: expected ${expected}, got ${actual}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function extractArchive(archivePath, destination, target) {
|
|
86
|
-
fs.rmSync(destination, { recursive: true, force: true });
|
|
87
|
-
fs.mkdirSync(destination, { recursive: true });
|
|
88
|
-
|
|
89
|
-
const result = target.endsWith("windows-msvc")
|
|
90
|
-
? spawnSync("powershell", ["-NoProfile", "-Command", `Expand-Archive -Force '${archivePath}' '${destination}'`], { stdio: "inherit" })
|
|
91
|
-
: spawnSync("tar", ["-xzf", archivePath, "-C", destination], { stdio: "inherit" });
|
|
92
|
-
|
|
93
|
-
if (result.status !== 0) {
|
|
94
|
-
throw new Error(`Failed to extract ${archivePath}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function install() {
|
|
99
|
-
if (process.env.REPOPILOT_SKIP_DOWNLOAD === "1" || process.env.REPOPILOT_SKIP_DOWNLOAD === "true") {
|
|
100
|
-
console.log("Skipping RepoPilot binary download because REPOPILOT_SKIP_DOWNLOAD is set.");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const target = targetTriple();
|
|
105
|
-
const version = PACKAGE_JSON.version;
|
|
106
|
-
const archive = assetName(version, target);
|
|
107
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "repopilot-"));
|
|
108
|
-
const archivePath = path.join(tmp, archive);
|
|
109
|
-
const checksumPath = `${archivePath}.sha256`;
|
|
110
|
-
const extractDir = path.join(tmp, "extract");
|
|
111
|
-
const binaryName = target.endsWith("windows-msvc") ? "repopilot.exe" : "repopilot";
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
await download(releaseUrl(version, archive), archivePath);
|
|
115
|
-
await download(releaseUrl(version, `${archive}.sha256`), checksumPath);
|
|
116
|
-
verifyChecksum(archivePath, fs.readFileSync(checksumPath, "utf8"));
|
|
117
|
-
extractArchive(archivePath, extractDir, target);
|
|
118
|
-
|
|
119
|
-
fs.mkdirSync(VENDOR_DIR, { recursive: true });
|
|
120
|
-
fs.copyFileSync(path.join(extractDir, binaryName), path.join(VENDOR_DIR, binaryName));
|
|
121
|
-
fs.chmodSync(path.join(VENDOR_DIR, binaryName), 0o755);
|
|
122
|
-
} finally {
|
|
123
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (require.main === module) {
|
|
128
|
-
install().catch((error) => {
|
|
129
|
-
console.error(`Failed to install RepoPilot binary: ${error.message}`);
|
|
130
|
-
console.error("Set REPOPILOT_SKIP_DOWNLOAD=1 to skip download or REPOPILOT_BINARY_PATH to use an existing binary.");
|
|
131
|
-
process.exit(1);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
module.exports = {
|
|
136
|
-
archiveExt,
|
|
137
|
-
assetName,
|
|
138
|
-
expectedChecksum,
|
|
139
|
-
targetTriple,
|
|
140
|
-
verifyChecksum,
|
|
141
|
-
};
|
package/scripts/smoke-product.sh
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
-
|
|
6
|
-
BINARY=""
|
|
7
|
-
REPO_PATH="$ROOT_DIR"
|
|
8
|
-
TMP_DIR=""
|
|
9
|
-
TMP_CREATED=0
|
|
10
|
-
KEEP_TMP=0
|
|
11
|
-
RUN_LEGACY=1
|
|
12
|
-
|
|
13
|
-
usage() {
|
|
14
|
-
cat <<'EOF'
|
|
15
|
-
Usage: scripts/smoke-product.sh [OPTIONS]
|
|
16
|
-
|
|
17
|
-
Run product-readiness smoke checks against RepoPilot.
|
|
18
|
-
|
|
19
|
-
Options:
|
|
20
|
-
--binary PATH Use an already built repopilot binary.
|
|
21
|
-
--repo PATH Repository or project path to smoke test. Defaults to this repo.
|
|
22
|
-
--tmp-dir PATH Directory for generated smoke artifacts. Defaults to mktemp.
|
|
23
|
-
--keep-tmp Keep generated smoke artifacts when using a temporary directory.
|
|
24
|
-
--skip-legacy Skip hidden 0.x compatibility command checks.
|
|
25
|
-
-h, --help Show this help message.
|
|
26
|
-
|
|
27
|
-
Examples:
|
|
28
|
-
scripts/smoke-product.sh
|
|
29
|
-
scripts/smoke-product.sh --binary ./target/release/repopilot
|
|
30
|
-
scripts/smoke-product.sh --repo /path/to/project --keep-tmp
|
|
31
|
-
EOF
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
while [[ $# -gt 0 ]]; do
|
|
35
|
-
case "$1" in
|
|
36
|
-
--binary)
|
|
37
|
-
BINARY="${2:-}"
|
|
38
|
-
shift 2
|
|
39
|
-
;;
|
|
40
|
-
--repo)
|
|
41
|
-
REPO_PATH="${2:-}"
|
|
42
|
-
shift 2
|
|
43
|
-
;;
|
|
44
|
-
--tmp-dir)
|
|
45
|
-
TMP_DIR="${2:-}"
|
|
46
|
-
shift 2
|
|
47
|
-
;;
|
|
48
|
-
--keep-tmp)
|
|
49
|
-
KEEP_TMP=1
|
|
50
|
-
shift
|
|
51
|
-
;;
|
|
52
|
-
--skip-legacy)
|
|
53
|
-
RUN_LEGACY=0
|
|
54
|
-
shift
|
|
55
|
-
;;
|
|
56
|
-
-h|--help)
|
|
57
|
-
usage
|
|
58
|
-
exit 0
|
|
59
|
-
;;
|
|
60
|
-
*)
|
|
61
|
-
echo "Unknown argument: $1" >&2
|
|
62
|
-
usage >&2
|
|
63
|
-
exit 2
|
|
64
|
-
;;
|
|
65
|
-
esac
|
|
66
|
-
done
|
|
67
|
-
|
|
68
|
-
if [[ -z "$REPO_PATH" ]]; then
|
|
69
|
-
echo "--repo cannot be empty" >&2
|
|
70
|
-
exit 2
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
REPO_PATH="$(cd "$REPO_PATH" && pwd)"
|
|
74
|
-
|
|
75
|
-
if [[ -z "$BINARY" ]]; then
|
|
76
|
-
if [[ ! -x "$ROOT_DIR/target/debug/repopilot" ]]; then
|
|
77
|
-
echo "==> Building debug binary for product smoke checks" >&2
|
|
78
|
-
cargo build --manifest-path "$ROOT_DIR/Cargo.toml"
|
|
79
|
-
fi
|
|
80
|
-
BINARY="$ROOT_DIR/target/debug/repopilot"
|
|
81
|
-
else
|
|
82
|
-
case "$BINARY" in
|
|
83
|
-
/*) ;;
|
|
84
|
-
*)
|
|
85
|
-
BINARY="$(cd "$(dirname "$BINARY")" && pwd)/$(basename "$BINARY")"
|
|
86
|
-
;;
|
|
87
|
-
esac
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
if [[ ! -x "$BINARY" ]]; then
|
|
91
|
-
echo "RepoPilot binary is not executable: $BINARY" >&2
|
|
92
|
-
exit 3
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
if [[ -z "$TMP_DIR" ]]; then
|
|
96
|
-
TMP_DIR="$(mktemp -d)"
|
|
97
|
-
TMP_CREATED=1
|
|
98
|
-
else
|
|
99
|
-
rm -rf "$TMP_DIR"
|
|
100
|
-
mkdir -p "$TMP_DIR"
|
|
101
|
-
TMP_DIR="$(cd "$TMP_DIR" && pwd)"
|
|
102
|
-
fi
|
|
103
|
-
|
|
104
|
-
cleanup() {
|
|
105
|
-
if [[ "$TMP_CREATED" -eq 1 && "$KEEP_TMP" -eq 0 ]]; then
|
|
106
|
-
rm -rf "$TMP_DIR"
|
|
107
|
-
fi
|
|
108
|
-
}
|
|
109
|
-
trap cleanup EXIT
|
|
110
|
-
|
|
111
|
-
run_repopilot() {
|
|
112
|
-
echo "==> repopilot $*" >&2
|
|
113
|
-
(
|
|
114
|
-
cd "$REPO_PATH"
|
|
115
|
-
"$BINARY" "$@"
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
assert_non_empty() {
|
|
120
|
-
local file="$1"
|
|
121
|
-
if [[ ! -s "$file" ]]; then
|
|
122
|
-
echo "Expected non-empty file: $file" >&2
|
|
123
|
-
exit 3
|
|
124
|
-
fi
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
assert_contains() {
|
|
128
|
-
local file="$1"
|
|
129
|
-
local expected="$2"
|
|
130
|
-
|
|
131
|
-
if ! grep -Fq "$expected" "$file"; then
|
|
132
|
-
echo "Expected '$file' to contain: $expected" >&2
|
|
133
|
-
echo "---- file content ----" >&2
|
|
134
|
-
cat "$file" >&2 || true
|
|
135
|
-
echo "----------------------" >&2
|
|
136
|
-
exit 3
|
|
137
|
-
fi
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
validate_json_if_possible() {
|
|
141
|
-
local file="$1"
|
|
142
|
-
|
|
143
|
-
if command -v python3 >/dev/null 2>&1; then
|
|
144
|
-
python3 -m json.tool "$file" >/dev/null
|
|
145
|
-
fi
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
find_explain_file() {
|
|
149
|
-
local preferred="$REPO_PATH/src/main.rs"
|
|
150
|
-
|
|
151
|
-
if [[ -f "$preferred" ]]; then
|
|
152
|
-
printf '%s\n' "$preferred"
|
|
153
|
-
return 0
|
|
154
|
-
fi
|
|
155
|
-
|
|
156
|
-
local discovered=""
|
|
157
|
-
while IFS= read -r candidate; do
|
|
158
|
-
discovered="$candidate"
|
|
159
|
-
break
|
|
160
|
-
done < <(
|
|
161
|
-
find "$REPO_PATH" \
|
|
162
|
-
-path "$REPO_PATH/.git" -prune -o \
|
|
163
|
-
-path "$REPO_PATH/target" -prune -o \
|
|
164
|
-
-path "$REPO_PATH/node_modules" -prune -o \
|
|
165
|
-
-type f \( \
|
|
166
|
-
-name '*.rs' -o \
|
|
167
|
-
-name '*.ts' -o \
|
|
168
|
-
-name '*.tsx' -o \
|
|
169
|
-
-name '*.js' -o \
|
|
170
|
-
-name '*.jsx' -o \
|
|
171
|
-
-name '*.py' -o \
|
|
172
|
-
-name '*.go' \
|
|
173
|
-
\) \
|
|
174
|
-
-print
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
if [[ -n "$discovered" && -f "$discovered" ]]; then
|
|
178
|
-
printf '%s\n' "$discovered"
|
|
179
|
-
return 0
|
|
180
|
-
fi
|
|
181
|
-
|
|
182
|
-
printf '%s\n' "$REPO_PATH/Cargo.toml"
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
EXPLAIN_FILE="$(find_explain_file)"
|
|
186
|
-
|
|
187
|
-
echo "==> RepoPilot product smoke suite"
|
|
188
|
-
echo "Binary: $BINARY"
|
|
189
|
-
echo "Repo: $REPO_PATH"
|
|
190
|
-
echo "Tmp: $TMP_DIR"
|
|
191
|
-
echo
|
|
192
|
-
|
|
193
|
-
run_repopilot --version > "$TMP_DIR/version.txt"
|
|
194
|
-
assert_non_empty "$TMP_DIR/version.txt"
|
|
195
|
-
|
|
196
|
-
run_repopilot --help > "$TMP_DIR/help.txt"
|
|
197
|
-
assert_non_empty "$TMP_DIR/help.txt"
|
|
198
|
-
assert_contains "$TMP_DIR/help.txt" "scan"
|
|
199
|
-
assert_contains "$TMP_DIR/help.txt" "review"
|
|
200
|
-
assert_contains "$TMP_DIR/help.txt" "ai"
|
|
201
|
-
assert_contains "$TMP_DIR/help.txt" "inspect"
|
|
202
|
-
|
|
203
|
-
run_repopilot init --force --path "$TMP_DIR/repopilot.toml"
|
|
204
|
-
assert_non_empty "$TMP_DIR/repopilot.toml"
|
|
205
|
-
|
|
206
|
-
run_repopilot doctor . --format markdown --output "$TMP_DIR/doctor.md"
|
|
207
|
-
assert_non_empty "$TMP_DIR/doctor.md"
|
|
208
|
-
|
|
209
|
-
run_repopilot scan . --format json --output "$TMP_DIR/scan.json"
|
|
210
|
-
assert_non_empty "$TMP_DIR/scan.json"
|
|
211
|
-
validate_json_if_possible "$TMP_DIR/scan.json"
|
|
212
|
-
|
|
213
|
-
run_repopilot scan . --format markdown --output "$TMP_DIR/scan.md"
|
|
214
|
-
assert_non_empty "$TMP_DIR/scan.md"
|
|
215
|
-
|
|
216
|
-
if git -C "$REPO_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
217
|
-
run_repopilot review . --format markdown --output "$TMP_DIR/review.md"
|
|
218
|
-
assert_non_empty "$TMP_DIR/review.md"
|
|
219
|
-
else
|
|
220
|
-
echo "==> Skipping review smoke check because repo path is not inside a Git worktree" >&2
|
|
221
|
-
fi
|
|
222
|
-
|
|
223
|
-
run_repopilot ai context . --focus security --budget 2k --output "$TMP_DIR/ai-context.md"
|
|
224
|
-
assert_non_empty "$TMP_DIR/ai-context.md"
|
|
225
|
-
|
|
226
|
-
run_repopilot ai plan . --focus all --budget 2k --output "$TMP_DIR/ai-plan.md"
|
|
227
|
-
assert_non_empty "$TMP_DIR/ai-plan.md"
|
|
228
|
-
|
|
229
|
-
run_repopilot ai prompt . --focus quality --budget 2k --output "$TMP_DIR/ai-prompt.md"
|
|
230
|
-
assert_non_empty "$TMP_DIR/ai-prompt.md"
|
|
231
|
-
|
|
232
|
-
run_repopilot inspect knowledge --format json --output "$TMP_DIR/inspect-knowledge.json"
|
|
233
|
-
assert_non_empty "$TMP_DIR/inspect-knowledge.json"
|
|
234
|
-
validate_json_if_possible "$TMP_DIR/inspect-knowledge.json"
|
|
235
|
-
|
|
236
|
-
run_repopilot inspect knowledge --section rules --format markdown --output "$TMP_DIR/inspect-knowledge-rules.md"
|
|
237
|
-
assert_non_empty "$TMP_DIR/inspect-knowledge-rules.md"
|
|
238
|
-
|
|
239
|
-
run_repopilot inspect explain "$EXPLAIN_FILE" --format json --output "$TMP_DIR/inspect-explain.json"
|
|
240
|
-
assert_non_empty "$TMP_DIR/inspect-explain.json"
|
|
241
|
-
validate_json_if_possible "$TMP_DIR/inspect-explain.json"
|
|
242
|
-
|
|
243
|
-
if [[ "$RUN_LEGACY" -eq 1 ]]; then
|
|
244
|
-
run_repopilot vibe . --focus security --budget 2k --output "$TMP_DIR/legacy-vibe.md"
|
|
245
|
-
assert_non_empty "$TMP_DIR/legacy-vibe.md"
|
|
246
|
-
|
|
247
|
-
run_repopilot harden . --focus all --budget 2k --output "$TMP_DIR/legacy-harden.md"
|
|
248
|
-
assert_non_empty "$TMP_DIR/legacy-harden.md"
|
|
249
|
-
|
|
250
|
-
run_repopilot prompt . --focus quality --budget 2k --output "$TMP_DIR/legacy-prompt.md"
|
|
251
|
-
assert_non_empty "$TMP_DIR/legacy-prompt.md"
|
|
252
|
-
|
|
253
|
-
run_repopilot knowledge --format json --output "$TMP_DIR/legacy-knowledge.json"
|
|
254
|
-
assert_non_empty "$TMP_DIR/legacy-knowledge.json"
|
|
255
|
-
validate_json_if_possible "$TMP_DIR/legacy-knowledge.json"
|
|
256
|
-
|
|
257
|
-
run_repopilot explain "$EXPLAIN_FILE" --format json --output "$TMP_DIR/legacy-explain.json"
|
|
258
|
-
assert_non_empty "$TMP_DIR/legacy-explain.json"
|
|
259
|
-
validate_json_if_possible "$TMP_DIR/legacy-explain.json"
|
|
260
|
-
fi
|
|
261
|
-
|
|
262
|
-
echo
|
|
263
|
-
echo "==> Product smoke suite passed"
|
|
264
|
-
echo "Artifacts: $TMP_DIR"
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
-
cd "$ROOT_DIR"
|
|
6
|
-
|
|
7
|
-
echo "==> RepoPilot release verification"
|
|
8
|
-
|
|
9
|
-
echo "==> Rust formatting"
|
|
10
|
-
cargo fmt --all -- --check
|
|
11
|
-
|
|
12
|
-
echo "==> Rust clippy"
|
|
13
|
-
cargo clippy --all-targets --all-features -- -D warnings
|
|
14
|
-
|
|
15
|
-
echo "==> Rust tests"
|
|
16
|
-
cargo test --all
|
|
17
|
-
|
|
18
|
-
echo "==> Rust dependency security audit"
|
|
19
|
-
cargo audit
|
|
20
|
-
|
|
21
|
-
echo "==> Cargo dependency policy"
|
|
22
|
-
cargo deny check advisories licenses
|
|
23
|
-
|
|
24
|
-
echo "==> Shell script syntax"
|
|
25
|
-
scripts=()
|
|
26
|
-
if [[ -f install.sh ]]; then
|
|
27
|
-
scripts+=(install.sh)
|
|
28
|
-
fi
|
|
29
|
-
if [[ -d scripts ]]; then
|
|
30
|
-
while IFS= read -r -d '' script; do
|
|
31
|
-
scripts+=("$script")
|
|
32
|
-
done < <(find scripts -maxdepth 1 -type f -name '*.sh' -print0)
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
if ((${#scripts[@]} > 0)); then
|
|
36
|
-
for script in "${scripts[@]}"; do
|
|
37
|
-
bash -n "$script"
|
|
38
|
-
done
|
|
39
|
-
|
|
40
|
-
echo "==> ShellCheck"
|
|
41
|
-
shellcheck "${scripts[@]}"
|
|
42
|
-
else
|
|
43
|
-
echo "==> No shell scripts found"
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
echo "==> GitHub Actions validation"
|
|
47
|
-
workflows=()
|
|
48
|
-
if [[ -d .github/workflows ]]; then
|
|
49
|
-
while IFS= read -r -d '' workflow; do
|
|
50
|
-
workflows+=("$workflow")
|
|
51
|
-
done < <(find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) -print0)
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
if ((${#workflows[@]} > 0)); then
|
|
55
|
-
actionlint "${workflows[@]}"
|
|
56
|
-
else
|
|
57
|
-
echo "==> No GitHub Actions workflows found"
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
echo "==> CLI release smoke tests"
|
|
61
|
-
cargo test --test cli_release_smoke
|
|
62
|
-
|
|
63
|
-
echo "==> Cargo package contents"
|
|
64
|
-
cargo package --list
|
|
65
|
-
|
|
66
|
-
echo "==> Cargo publish dry-run"
|
|
67
|
-
cargo publish --dry-run
|
|
68
|
-
|
|
69
|
-
echo "==> npm wrapper tests"
|
|
70
|
-
npm run test:npm
|
|
71
|
-
|
|
72
|
-
echo "==> npm package dry-run"
|
|
73
|
-
npm pack --dry-run
|
|
74
|
-
|
|
75
|
-
echo "==> Build release binary"
|
|
76
|
-
cargo build --release
|
|
77
|
-
|
|
78
|
-
echo "==> Product readiness smoke suite"
|
|
79
|
-
./scripts/smoke-product.sh \
|
|
80
|
-
--binary ./target/release/repopilot \
|
|
81
|
-
--repo . \
|
|
82
|
-
--tmp-dir /tmp/repopilot-release-smoke
|
|
83
|
-
|
|
84
|
-
echo "==> Release verification passed"
|