repopo 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.
Files changed (229) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +41 -2
  3. package/THIRD-PARTY-LICENSES.txt +71 -11
  4. package/bin/dev.js +5 -1
  5. package/esm/baseCommand.d.ts +1 -1
  6. package/esm/baseCommand.d.ts.map +1 -1
  7. package/esm/baseCommand.js +6 -9
  8. package/esm/baseCommand.js.map +1 -1
  9. package/esm/commands/check-native.d.ts +25 -0
  10. package/esm/commands/check-native.d.ts.map +1 -0
  11. package/esm/commands/check-native.js +137 -0
  12. package/esm/commands/check-native.js.map +1 -0
  13. package/esm/commands/check.d.ts +8 -20
  14. package/esm/commands/check.d.ts.map +1 -1
  15. package/esm/commands/check.js +77 -186
  16. package/esm/commands/check.js.map +1 -1
  17. package/esm/commands/list.d.ts.map +1 -1
  18. package/esm/commands/list.js +5 -6
  19. package/esm/commands/list.js.map +1 -1
  20. package/esm/context.d.ts +13 -3
  21. package/esm/context.d.ts.map +1 -1
  22. package/esm/context.js +11 -1
  23. package/esm/context.js.map +1 -1
  24. package/esm/index.d.ts +6 -1
  25. package/esm/index.d.ts.map +1 -1
  26. package/esm/index.js +3 -0
  27. package/esm/index.js.map +1 -1
  28. package/esm/makePolicy.d.ts +29 -2
  29. package/esm/makePolicy.d.ts.map +1 -1
  30. package/esm/makePolicy.js +46 -2
  31. package/esm/makePolicy.js.map +1 -1
  32. package/esm/perf.d.ts +7 -2
  33. package/esm/perf.d.ts.map +1 -1
  34. package/esm/perf.js.map +1 -1
  35. package/esm/policies/CargoLicenceValidated.d.ts +41 -0
  36. package/esm/policies/CargoLicenceValidated.d.ts.map +1 -0
  37. package/esm/policies/CargoLicenceValidated.js +45 -0
  38. package/esm/policies/CargoLicenceValidated.js.map +1 -0
  39. package/esm/policies/CargoLintsConfigured.d.ts +30 -0
  40. package/esm/policies/CargoLintsConfigured.d.ts.map +1 -0
  41. package/esm/policies/CargoLintsConfigured.js +61 -0
  42. package/esm/policies/CargoLintsConfigured.js.map +1 -0
  43. package/esm/policies/CargoLockPolicy.d.ts +34 -0
  44. package/esm/policies/CargoLockPolicy.d.ts.map +1 -0
  45. package/esm/policies/CargoLockPolicy.js +57 -0
  46. package/esm/policies/CargoLockPolicy.js.map +1 -0
  47. package/esm/policies/CargoTomlRequired.d.ts +34 -0
  48. package/esm/policies/CargoTomlRequired.d.ts.map +1 -0
  49. package/esm/policies/CargoTomlRequired.js +77 -0
  50. package/esm/policies/CargoTomlRequired.js.map +1 -0
  51. package/esm/policies/CargoTomlSorted.d.ts +20 -0
  52. package/esm/policies/CargoTomlSorted.d.ts.map +1 -0
  53. package/esm/policies/CargoTomlSorted.js +164 -0
  54. package/esm/policies/CargoTomlSorted.js.map +1 -0
  55. package/esm/policies/ConditionalScripts.d.ts.map +1 -1
  56. package/esm/policies/ConditionalScripts.js +3 -2
  57. package/esm/policies/ConditionalScripts.js.map +1 -1
  58. package/esm/policies/ExactScripts.d.ts.map +1 -1
  59. package/esm/policies/ExactScripts.js +3 -2
  60. package/esm/policies/ExactScripts.js.map +1 -1
  61. package/esm/policies/GleamDepVersions.d.ts +25 -0
  62. package/esm/policies/GleamDepVersions.d.ts.map +1 -0
  63. package/esm/policies/GleamDepVersions.js +67 -0
  64. package/esm/policies/GleamDepVersions.js.map +1 -0
  65. package/esm/policies/GleamDocExists.d.ts +24 -0
  66. package/esm/policies/GleamDocExists.d.ts.map +1 -0
  67. package/esm/policies/GleamDocExists.js +39 -0
  68. package/esm/policies/GleamDocExists.js.map +1 -0
  69. package/esm/policies/GleamInternalModulesConfigured.d.ts +21 -0
  70. package/esm/policies/GleamInternalModulesConfigured.d.ts.map +1 -0
  71. package/esm/policies/GleamInternalModulesConfigured.js +30 -0
  72. package/esm/policies/GleamInternalModulesConfigured.js.map +1 -0
  73. package/esm/policies/GleamLicenceConfigured.d.ts +29 -0
  74. package/esm/policies/GleamLicenceConfigured.d.ts.map +1 -0
  75. package/esm/policies/GleamLicenceConfigured.js +34 -0
  76. package/esm/policies/GleamLicenceConfigured.js.map +1 -0
  77. package/esm/policies/GleamManifestCommitted.d.ts +9 -0
  78. package/esm/policies/GleamManifestCommitted.d.ts.map +1 -0
  79. package/esm/policies/GleamManifestCommitted.js +26 -0
  80. package/esm/policies/GleamManifestCommitted.js.map +1 -0
  81. package/esm/policies/GleamMonorepoConsistency.d.ts +30 -0
  82. package/esm/policies/GleamMonorepoConsistency.d.ts.map +1 -0
  83. package/esm/policies/GleamMonorepoConsistency.js +74 -0
  84. package/esm/policies/GleamMonorepoConsistency.js.map +1 -0
  85. package/esm/policies/GleamNameRestrictions.d.ts +25 -0
  86. package/esm/policies/GleamNameRestrictions.d.ts.map +1 -0
  87. package/esm/policies/GleamNameRestrictions.js +44 -0
  88. package/esm/policies/GleamNameRestrictions.js.map +1 -0
  89. package/esm/policies/GleamNoPathDepsInPublished.d.ts +22 -0
  90. package/esm/policies/GleamNoPathDepsInPublished.d.ts.map +1 -0
  91. package/esm/policies/GleamNoPathDepsInPublished.js +43 -0
  92. package/esm/policies/GleamNoPathDepsInPublished.js.map +1 -0
  93. package/esm/policies/GleamPathDepsValid.d.ts +9 -0
  94. package/esm/policies/GleamPathDepsValid.d.ts.map +1 -0
  95. package/esm/policies/GleamPathDepsValid.js +59 -0
  96. package/esm/policies/GleamPathDepsValid.js.map +1 -0
  97. package/esm/policies/GleamProjectStructure.d.ts +25 -0
  98. package/esm/policies/GleamProjectStructure.d.ts.map +1 -0
  99. package/esm/policies/GleamProjectStructure.js +33 -0
  100. package/esm/policies/GleamProjectStructure.js.map +1 -0
  101. package/esm/policies/GleamSharedDependencyVersions.d.ts +29 -0
  102. package/esm/policies/GleamSharedDependencyVersions.d.ts.map +1 -0
  103. package/esm/policies/GleamSharedDependencyVersions.js +101 -0
  104. package/esm/policies/GleamSharedDependencyVersions.js.map +1 -0
  105. package/esm/policies/GleamTomlRequired.d.ts +30 -0
  106. package/esm/policies/GleamTomlRequired.d.ts.map +1 -0
  107. package/esm/policies/GleamTomlRequired.js +37 -0
  108. package/esm/policies/GleamTomlRequired.js.map +1 -0
  109. package/esm/policies/GleamTomlSorted.d.ts +20 -0
  110. package/esm/policies/GleamTomlSorted.d.ts.map +1 -0
  111. package/esm/policies/GleamTomlSorted.js +153 -0
  112. package/esm/policies/GleamTomlSorted.js.map +1 -0
  113. package/esm/policies/GleamVersionConstraint.d.ts +20 -0
  114. package/esm/policies/GleamVersionConstraint.d.ts.map +1 -0
  115. package/esm/policies/GleamVersionConstraint.js +64 -0
  116. package/esm/policies/GleamVersionConstraint.js.map +1 -0
  117. package/esm/policies/HtmlFileHeaders.js +1 -1
  118. package/esm/policies/HtmlFileHeaders.js.map +1 -1
  119. package/esm/policies/NoBuildDirectory.d.ts +9 -0
  120. package/esm/policies/NoBuildDirectory.d.ts.map +1 -0
  121. package/esm/policies/NoBuildDirectory.js +29 -0
  122. package/esm/policies/NoBuildDirectory.js.map +1 -0
  123. package/esm/policies/NoTargetArtifacts.d.ts +8 -0
  124. package/esm/policies/NoTargetArtifacts.d.ts.map +1 -0
  125. package/esm/policies/NoTargetArtifacts.js +42 -0
  126. package/esm/policies/NoTargetArtifacts.js.map +1 -0
  127. package/esm/policies/NoUnsafeWithoutJustification.d.ts +24 -0
  128. package/esm/policies/NoUnsafeWithoutJustification.d.ts.map +1 -0
  129. package/esm/policies/NoUnsafeWithoutJustification.js +80 -0
  130. package/esm/policies/NoUnsafeWithoutJustification.js.map +1 -0
  131. package/esm/policies/NoWildcardDependencies.d.ts +30 -0
  132. package/esm/policies/NoWildcardDependencies.d.ts.map +1 -0
  133. package/esm/policies/NoWildcardDependencies.js +83 -0
  134. package/esm/policies/NoWildcardDependencies.js.map +1 -0
  135. package/esm/policies/PackageEsmType.d.ts.map +1 -1
  136. package/esm/policies/PackageEsmType.js +7 -5
  137. package/esm/policies/PackageEsmType.js.map +1 -1
  138. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  139. package/esm/policies/PackageJsonProperties.js +12 -4
  140. package/esm/policies/PackageJsonProperties.js.map +1 -1
  141. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  142. package/esm/policies/PackageJsonRepoDirectoryProperty.js +3 -2
  143. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  144. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  145. package/esm/policies/PackageJsonSorted.js +42 -7
  146. package/esm/policies/PackageJsonSorted.js.map +1 -1
  147. package/esm/policies/PackageLicense.d.ts.map +1 -1
  148. package/esm/policies/PackageLicense.js +1 -2
  149. package/esm/policies/PackageLicense.js.map +1 -1
  150. package/esm/policies/PackagePrivateField.d.ts.map +1 -1
  151. package/esm/policies/PackagePrivateField.js +3 -2
  152. package/esm/policies/PackagePrivateField.js.map +1 -1
  153. package/esm/policies/PackageReadme.js +2 -2
  154. package/esm/policies/PackageReadme.js.map +1 -1
  155. package/esm/policies/PackageScripts.d.ts.map +1 -1
  156. package/esm/policies/PackageScripts.js +3 -2
  157. package/esm/policies/PackageScripts.js.map +1 -1
  158. package/esm/policies/PackageTestScripts.js +3 -3
  159. package/esm/policies/PackageTestScripts.js.map +1 -1
  160. package/esm/policies/PublicApiDocumented.d.ts +21 -0
  161. package/esm/policies/PublicApiDocumented.d.ts.map +1 -0
  162. package/esm/policies/PublicApiDocumented.js +36 -0
  163. package/esm/policies/PublicApiDocumented.js.map +1 -0
  164. package/esm/policies/RequiredScripts.d.ts.map +1 -1
  165. package/esm/policies/RequiredScripts.js +3 -2
  166. package/esm/policies/RequiredScripts.js.map +1 -1
  167. package/esm/policies/RustDocExists.d.ts +24 -0
  168. package/esm/policies/RustDocExists.d.ts.map +1 -0
  169. package/esm/policies/RustDocExists.js +49 -0
  170. package/esm/policies/RustDocExists.js.map +1 -0
  171. package/esm/policies/RustToolchainExists.d.ts +20 -0
  172. package/esm/policies/RustToolchainExists.d.ts.map +1 -0
  173. package/esm/policies/RustToolchainExists.js +27 -0
  174. package/esm/policies/RustToolchainExists.js.map +1 -0
  175. package/esm/policies/RustfmtConfigExists.d.ts +20 -0
  176. package/esm/policies/RustfmtConfigExists.d.ts.map +1 -0
  177. package/esm/policies/RustfmtConfigExists.js +27 -0
  178. package/esm/policies/RustfmtConfigExists.js.map +1 -0
  179. package/esm/policies/SharedDependencyVersions.d.ts +24 -0
  180. package/esm/policies/SharedDependencyVersions.d.ts.map +1 -0
  181. package/esm/policies/SharedDependencyVersions.js +135 -0
  182. package/esm/policies/SharedDependencyVersions.js.map +1 -0
  183. package/esm/policies/WorkspaceInheritance.d.ts +24 -0
  184. package/esm/policies/WorkspaceInheritance.d.ts.map +1 -0
  185. package/esm/policies/WorkspaceInheritance.js +94 -0
  186. package/esm/policies/WorkspaceInheritance.js.map +1 -0
  187. package/esm/policies/WorkspaceMembersValid.d.ts +20 -0
  188. package/esm/policies/WorkspaceMembersValid.d.ts.map +1 -0
  189. package/esm/policies/WorkspaceMembersValid.js +88 -0
  190. package/esm/policies/WorkspaceMembersValid.js.map +1 -0
  191. package/esm/policies/constants.d.ts +8 -0
  192. package/esm/policies/constants.d.ts.map +1 -1
  193. package/esm/policies/constants.js +8 -0
  194. package/esm/policies/constants.js.map +1 -1
  195. package/esm/policies.d.ts +30 -0
  196. package/esm/policies.d.ts.map +1 -1
  197. package/esm/policies.js +51 -1
  198. package/esm/policies.js.map +1 -1
  199. package/esm/policy.d.ts +24 -0
  200. package/esm/policy.d.ts.map +1 -1
  201. package/esm/policy.js.map +1 -1
  202. package/esm/policyDefiners/defineCargoPolicy.d.ts +66 -0
  203. package/esm/policyDefiners/defineCargoPolicy.d.ts.map +1 -0
  204. package/esm/policyDefiners/defineCargoPolicy.js +60 -0
  205. package/esm/policyDefiners/defineCargoPolicy.js.map +1 -0
  206. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  207. package/esm/policyDefiners/defineFileHeaderPolicy.js +6 -7
  208. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  209. package/esm/policyDefiners/defineGleamPolicy.d.ts +65 -0
  210. package/esm/policyDefiners/defineGleamPolicy.d.ts.map +1 -0
  211. package/esm/policyDefiners/defineGleamPolicy.js +59 -0
  212. package/esm/policyDefiners/defineGleamPolicy.js.map +1 -0
  213. package/esm/policyDefiners/spdxValidator.d.ts +74 -0
  214. package/esm/policyDefiners/spdxValidator.d.ts.map +1 -0
  215. package/esm/policyDefiners/spdxValidator.js +127 -0
  216. package/esm/policyDefiners/spdxValidator.js.map +1 -0
  217. package/esm/policyDefiners/tomlParser.d.ts +8 -0
  218. package/esm/policyDefiners/tomlParser.d.ts.map +1 -0
  219. package/esm/policyDefiners/tomlParser.js +17 -0
  220. package/esm/policyDefiners/tomlParser.js.map +1 -0
  221. package/esm/runner.d.ts +60 -0
  222. package/esm/runner.d.ts.map +1 -0
  223. package/esm/runner.js +174 -0
  224. package/esm/runner.js.map +1 -0
  225. package/esm/tsdoc-metadata.json +1 -1
  226. package/oclif.manifest.json +100 -3
  227. package/package.json +46 -31
  228. package/scripts/postinstall.mjs +154 -0
  229. package/sidecar/sidecar.mjs +468 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repopo",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Enforce policies on all or some of the files in a git repository.",
5
5
  "keywords": [
6
6
  "policy",
@@ -39,6 +39,8 @@
39
39
  "/bin",
40
40
  "/oclif.manifest.json",
41
41
  "/esm",
42
+ "/scripts",
43
+ "/sidecar",
42
44
  "/THIRD-PARTY-LICENSES.txt"
43
45
  ],
44
46
  "oclif": {
@@ -61,54 +63,64 @@
61
63
  "repositoryPrefix": "<%- repo %>/blob/main/packages/repopo/<%- commandPath %>"
62
64
  },
63
65
  "dependencies": {
64
- "@fluid-tools/build-infrastructure": "~0.63.0",
65
- "@oclif/core": "^4.8.0",
66
- "@oclif/plugin-help": "^6.2.36",
67
- "@rushstack/node-core-library": "^5.19.0",
68
- "consola": "^3.4.0",
69
- "defu": "^6.1.4",
70
- "effection": "^4.0.0",
71
- "jsonfile": "^6.2.0",
66
+ "@fluid-tools/build-infrastructure": "~0.65.0",
67
+ "@oclif/core": "^4.11.14",
68
+ "@oclif/plugin-help": "^6.2.53",
69
+ "@rushstack/node-core-library": "^5.23.1",
70
+ "consola": "^3.4.2",
71
+ "defu": "^6.1.7",
72
+ "effection": "^4.0.3",
73
+ "jsonfile": "^6.2.1",
74
+ "lilconfig": "^3.1.3",
72
75
  "microdiff": "^1.5.0",
73
76
  "pathe": "^2.0.3",
74
77
  "picocolors": "^1.1.1",
75
- "picomatch": "^4.0.3",
76
- "resolve-workspace-root": "^2.0.0",
77
- "simple-git": "^3.30.0",
78
- "tinyglobby": "^0.2.15",
79
- "@tylerbu/cli-api": "^0.10.1",
80
- "@tylerbu/fundamentals": "^0.3.0"
78
+ "picomatch": "^4.0.5",
79
+ "resolve-workspace-root": "^2.0.1",
80
+ "simple-git": "^3.36.0",
81
+ "tinyglobby": "^0.2.17",
82
+ "@tylerbu/cli-api": "^0.11.0",
83
+ "@tylerbu/fundamentals": "^0.3.0",
84
+ "@tylerbu/lilconfig-loader-ts": "^0.1.3"
81
85
  },
82
86
  "devDependencies": {
83
87
  "@biomejs/biome": "2.3.8",
84
- "@microsoft/api-extractor": "^7.56.0",
85
- "@oclif/plugin-command-snapshot": "^5.3.8",
86
- "@oclif/test": "^4.1.15",
88
+ "@microsoft/api-extractor": "^7.58.9",
89
+ "@oclif/plugin-command-snapshot": "^5.3.30",
90
+ "@oclif/test": "^4.1.20",
87
91
  "@types/jsonfile": "^6.1.4",
88
- "@types/node": "^20.19.25",
89
- "@types/picomatch": "^4.0.2",
92
+ "@types/node": "^22.20.1",
93
+ "@types/picomatch": "^4.0.3",
94
+ "@types/spdx-correct": "^3.1.3",
90
95
  "@types/tmp": "^0.2.6",
91
- "@vitest/coverage-v8": "^4.0.17",
92
- "generate-license-file": "^4.1.1",
93
- "oclif": "^4.22.50",
94
- "rimraf": "^6.1.2",
96
+ "@vitest/coverage-v8": "^4.1.10",
97
+ "generate-license-file": "^4.2.1",
98
+ "oclif": "^4.23.27",
99
+ "rimraf": "^6.1.3",
100
+ "smol-toml": "^1.7.0",
101
+ "spdx-correct": "^3.2.0",
95
102
  "tmp-promise": "^3.0.3",
96
103
  "ts-node": "^10.9.2",
97
- "type-fest": "^5.4.1",
98
- "typescript": "~5.9.3",
99
- "vitest": "^4.0.17"
104
+ "type-fest": "^5.8.0",
105
+ "typescript": "~6.0.3",
106
+ "vitest": "^4.1.10"
100
107
  },
101
108
  "peerDependencies": {
102
- "sort-package-json": "*"
109
+ "smol-toml": "^1.0.0",
110
+ "sort-package-json": "*",
111
+ "spdx-correct": "^3.0.0"
103
112
  },
104
113
  "peerDependenciesMeta": {
114
+ "smol-toml": {
115
+ "optional": true
116
+ },
105
117
  "sort-package-json": {
106
118
  "optional": true
119
+ },
120
+ "spdx-correct": {
121
+ "optional": true
107
122
  }
108
123
  },
109
- "engines": {
110
- "node": ">=18.0.0"
111
- },
112
124
  "nx": {
113
125
  "targets": {
114
126
  "build": {
@@ -123,12 +135,15 @@
123
135
  "build:compile": "tsc --project ./tsconfig.json",
124
136
  "build:manifest": "oclif manifest",
125
137
  "build:readme": "concurrently npm:readme:*",
138
+ "build:rust": "cargo build --manifest-path crates/core/Cargo.toml",
139
+ "build:rust:release": "cargo build --release --manifest-path crates/core/Cargo.toml",
126
140
  "check": "concurrently npm:check:format",
127
141
  "check:format": "biome check . --linter-enabled=false",
128
142
  "clean": "concurrently npm:clean:build npm:clean:manifest",
129
143
  "clean:build": "rimraf esm *.tsbuildinfo *.done.build.log",
130
144
  "clean:manifest": "rimraf oclif.manifest.json",
131
145
  "format": "biome check . --linter-enabled=false --write",
146
+ "postinstall": "node scripts/postinstall.mjs",
132
147
  "lint": "biome lint .",
133
148
  "lint:fix": "biome lint . --write",
134
149
  "readme:docs": "oclif readme --no-aliases --readme-path=../repopo-docs/src/content/docs/cli-reference.md",
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Postinstall script for repopo - downloads pre-built repopo-core binary.
3
+ *
4
+ * This script uses only Node.js built-ins (no workspace dependencies).
5
+ * It gracefully handles all errors — a missing binary is not fatal since
6
+ * the Rust engine is optional.
7
+ */
8
+
9
+ import { execFileSync } from "node:child_process";
10
+ import { createHash } from "node:crypto";
11
+ import {
12
+ chmodSync,
13
+ createWriteStream,
14
+ existsSync,
15
+ mkdirSync,
16
+ unlinkSync,
17
+ } from "node:fs";
18
+ import { readFile } from "node:fs/promises";
19
+ import { dirname, join } from "node:path";
20
+ import { pipeline } from "node:stream/promises";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
+ const packageRoot = join(__dirname, "..");
25
+
26
+ // Skip in source checkout (development)
27
+ if (existsSync(join(packageRoot, "crates", "core", "Cargo.toml"))) {
28
+ console.log("repopo: source checkout detected, skipping binary download.");
29
+ process.exit(0);
30
+ }
31
+
32
+ // Skip if explicitly disabled
33
+ // biome-ignore lint/style/noProcessEnv: Postinstall environment check
34
+ if (process.env.REPOPO_SKIP_POSTINSTALL === "1") {
35
+ console.log("repopo: REPOPO_SKIP_POSTINSTALL=1, skipping binary download.");
36
+ process.exit(0);
37
+ }
38
+
39
+ const PLATFORM_MAP = {
40
+ "linux-x64": "repopo-core-linux-x64",
41
+ "linux-arm64": "repopo-core-linux-arm64",
42
+ "darwin-x64": "repopo-core-darwin-x64",
43
+ "darwin-arm64": "repopo-core-darwin-arm64",
44
+ "win32-x64": "repopo-core-win32-x64",
45
+ };
46
+
47
+ const platformKey = `${process.platform}-${process.arch}`;
48
+ const assetName = PLATFORM_MAP[platformKey];
49
+
50
+ if (!assetName) {
51
+ console.warn(
52
+ `repopo: unsupported platform ${platformKey}, skipping binary download.`,
53
+ );
54
+ process.exit(0);
55
+ }
56
+
57
+ try {
58
+ const pkg = JSON.parse(
59
+ await readFile(join(packageRoot, "package.json"), "utf-8"),
60
+ );
61
+ const version = pkg.version;
62
+ const isWindows = process.platform === "win32";
63
+ const ext = isWindows ? ".zip" : ".tar.gz";
64
+ const binaryName = isWindows ? "repopo-core.exe" : "repopo-core";
65
+ const baseUrl = `https://github.com/tylerbutler/tools-monorepo/releases/download/repopo-core-v${version}`;
66
+ const archiveUrl = `${baseUrl}/${assetName}${ext}`;
67
+ const checksumUrl = `${archiveUrl}.sha256`;
68
+
69
+ const nativeDir = join(packageRoot, "native");
70
+ const binaryDest = join(nativeDir, binaryName);
71
+
72
+ // Skip if binary already exists
73
+ if (existsSync(binaryDest)) {
74
+ console.log("repopo: binary already exists, skipping download.");
75
+ process.exit(0);
76
+ }
77
+
78
+ mkdirSync(nativeDir, { recursive: true });
79
+
80
+ console.log(`repopo: downloading ${assetName}${ext}...`);
81
+
82
+ // Download checksum
83
+ const checksumText = await fetchText(checksumUrl);
84
+ const expectedHash = checksumText.trim().split(/\s+/)[0];
85
+
86
+ // Download archive
87
+ const archivePath = join(nativeDir, `${assetName}${ext}`);
88
+ await downloadFile(archiveUrl, archivePath);
89
+
90
+ // Verify checksum
91
+ const fileBuffer = await readFile(archivePath);
92
+ const actualHash = createHash("sha256").update(fileBuffer).digest("hex");
93
+
94
+ if (actualHash !== expectedHash) {
95
+ unlinkSync(archivePath);
96
+ throw new Error(
97
+ `Checksum mismatch: expected ${expectedHash}, got ${actualHash}`,
98
+ );
99
+ }
100
+
101
+ // Extract — uses execFileSync with explicit argument arrays to avoid shell injection
102
+ if (isWindows) {
103
+ execFileSync(
104
+ "powershell",
105
+ [
106
+ "-Command",
107
+ `Expand-Archive -Path '${archivePath}' -DestinationPath '${nativeDir}' -Force`,
108
+ ],
109
+ { stdio: "pipe" },
110
+ );
111
+ } else {
112
+ execFileSync("tar", ["xzf", archivePath, "-C", nativeDir], {
113
+ stdio: "pipe",
114
+ });
115
+ }
116
+
117
+ // Clean up archive
118
+ unlinkSync(archivePath);
119
+
120
+ // Set executable permission (non-Windows)
121
+ if (!isWindows) {
122
+ chmodSync(binaryDest, 0o755);
123
+ }
124
+
125
+ console.log("repopo: binary installed successfully.");
126
+ } catch (err) {
127
+ console.warn(
128
+ `repopo: failed to download binary (${err.message}). The Rust engine will be unavailable.`,
129
+ );
130
+ process.exit(0);
131
+ }
132
+
133
+ /**
134
+ * Fetch text content from a URL using Node.js built-in fetch.
135
+ */
136
+ async function fetchText(url) {
137
+ const res = await fetch(url);
138
+ if (!res.ok) {
139
+ throw new Error(`HTTP ${res.status} fetching ${url}`);
140
+ }
141
+ return res.text();
142
+ }
143
+
144
+ /**
145
+ * Download a file from a URL to a local path using Node.js built-in fetch.
146
+ */
147
+ async function downloadFile(url, dest) {
148
+ const res = await fetch(url);
149
+ if (!res.ok) {
150
+ throw new Error(`HTTP ${res.status} fetching ${url}`);
151
+ }
152
+ const fileStream = createWriteStream(dest);
153
+ await pipeline(res.body, fileStream);
154
+ }