repopilot 0.10.0 → 0.12.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 +251 -302
- package/bin/repopilot.js +129 -12
- package/package.json +8 -3
- package/scripts/install.js +0 -141
- package/scripts/smoke-product.sh +0 -266
- package/scripts/verify-release.sh +0 -84
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.12.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.12.0",
|
|
30
|
+
"@repopilot/darwin-x64": "0.12.0",
|
|
31
|
+
"@repopilot/linux-arm64-gnu": "0.12.0",
|
|
32
|
+
"@repopilot/linux-x64-gnu": "0.12.0",
|
|
33
|
+
"@repopilot/win32-x64-msvc": "0.12.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,266 +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" --receipt "$TMP_DIR/receipt.json"
|
|
210
|
-
assert_non_empty "$TMP_DIR/scan.json"
|
|
211
|
-
validate_json_if_possible "$TMP_DIR/scan.json"
|
|
212
|
-
assert_non_empty "$TMP_DIR/receipt.json"
|
|
213
|
-
validate_json_if_possible "$TMP_DIR/receipt.json"
|
|
214
|
-
|
|
215
|
-
run_repopilot scan . --format markdown --output "$TMP_DIR/scan.md"
|
|
216
|
-
assert_non_empty "$TMP_DIR/scan.md"
|
|
217
|
-
|
|
218
|
-
if git -C "$REPO_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
219
|
-
run_repopilot review . --format markdown --output "$TMP_DIR/review.md"
|
|
220
|
-
assert_non_empty "$TMP_DIR/review.md"
|
|
221
|
-
else
|
|
222
|
-
echo "==> Skipping review smoke check because repo path is not inside a Git worktree" >&2
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
run_repopilot ai context . --focus security --budget 2k --output "$TMP_DIR/ai-context.md"
|
|
226
|
-
assert_non_empty "$TMP_DIR/ai-context.md"
|
|
227
|
-
|
|
228
|
-
run_repopilot ai plan . --focus all --budget 2k --output "$TMP_DIR/ai-plan.md"
|
|
229
|
-
assert_non_empty "$TMP_DIR/ai-plan.md"
|
|
230
|
-
|
|
231
|
-
run_repopilot ai prompt . --focus quality --budget 2k --output "$TMP_DIR/ai-prompt.md"
|
|
232
|
-
assert_non_empty "$TMP_DIR/ai-prompt.md"
|
|
233
|
-
|
|
234
|
-
run_repopilot inspect knowledge --format json --output "$TMP_DIR/inspect-knowledge.json"
|
|
235
|
-
assert_non_empty "$TMP_DIR/inspect-knowledge.json"
|
|
236
|
-
validate_json_if_possible "$TMP_DIR/inspect-knowledge.json"
|
|
237
|
-
|
|
238
|
-
run_repopilot inspect knowledge --section rules --format markdown --output "$TMP_DIR/inspect-knowledge-rules.md"
|
|
239
|
-
assert_non_empty "$TMP_DIR/inspect-knowledge-rules.md"
|
|
240
|
-
|
|
241
|
-
run_repopilot inspect explain "$EXPLAIN_FILE" --format json --output "$TMP_DIR/inspect-explain.json"
|
|
242
|
-
assert_non_empty "$TMP_DIR/inspect-explain.json"
|
|
243
|
-
validate_json_if_possible "$TMP_DIR/inspect-explain.json"
|
|
244
|
-
|
|
245
|
-
if [[ "$RUN_LEGACY" -eq 1 ]]; then
|
|
246
|
-
run_repopilot vibe . --focus security --budget 2k --output "$TMP_DIR/legacy-vibe.md"
|
|
247
|
-
assert_non_empty "$TMP_DIR/legacy-vibe.md"
|
|
248
|
-
|
|
249
|
-
run_repopilot harden . --focus all --budget 2k --output "$TMP_DIR/legacy-harden.md"
|
|
250
|
-
assert_non_empty "$TMP_DIR/legacy-harden.md"
|
|
251
|
-
|
|
252
|
-
run_repopilot prompt . --focus quality --budget 2k --output "$TMP_DIR/legacy-prompt.md"
|
|
253
|
-
assert_non_empty "$TMP_DIR/legacy-prompt.md"
|
|
254
|
-
|
|
255
|
-
run_repopilot knowledge --format json --output "$TMP_DIR/legacy-knowledge.json"
|
|
256
|
-
assert_non_empty "$TMP_DIR/legacy-knowledge.json"
|
|
257
|
-
validate_json_if_possible "$TMP_DIR/legacy-knowledge.json"
|
|
258
|
-
|
|
259
|
-
run_repopilot explain "$EXPLAIN_FILE" --format json --output "$TMP_DIR/legacy-explain.json"
|
|
260
|
-
assert_non_empty "$TMP_DIR/legacy-explain.json"
|
|
261
|
-
validate_json_if_possible "$TMP_DIR/legacy-explain.json"
|
|
262
|
-
fi
|
|
263
|
-
|
|
264
|
-
echo
|
|
265
|
-
echo "==> Product smoke suite passed"
|
|
266
|
-
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"
|