@hardlydifficult/ci-scripts 1.0.2 → 1.0.4
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 +75 -0
- package/dist/auto-commit-fixes.d.ts +3 -0
- package/dist/auto-commit-fixes.d.ts.map +1 -0
- package/dist/auto-commit-fixes.js +100 -0
- package/dist/auto-commit-fixes.js.map +1 -0
- package/dist/log-local-skills.d.ts +3 -0
- package/dist/log-local-skills.d.ts.map +1 -0
- package/dist/log-local-skills.js +88 -0
- package/dist/log-local-skills.js.map +1 -0
- package/dist/sync-skills.d.ts +3 -0
- package/dist/sync-skills.d.ts.map +1 -0
- package/dist/sync-skills.js +103 -0
- package/dist/sync-skills.js.map +1 -0
- package/package.json +5 -2
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# @hardlydifficult/ci-scripts
|
|
2
|
+
|
|
3
|
+
Reusable CI scripts exposed as CLI commands.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D @hardlydifficult/ci-scripts
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
### `check-pinned-deps`
|
|
14
|
+
|
|
15
|
+
Validates that all dependencies in all `package.json` files use exact versions (no `^` or `~` prefixes). Exits with a non-zero code if any unpinned dependencies are found.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx check-pinned-deps
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### `monorepo-publish`
|
|
22
|
+
|
|
23
|
+
Smart monorepo publisher. Auto-increments patch versions, publishes packages in dependency order, and transforms `file:` references to real versions before publishing.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx monorepo-publish
|
|
27
|
+
npx monorepo-publish --packages-dir libs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The `--packages-dir` option specifies the directory containing packages. Defaults to `packages`.
|
|
31
|
+
|
|
32
|
+
### `auto-commit-fixes`
|
|
33
|
+
|
|
34
|
+
Checks for uncommitted changes, commits them, and pushes with exponential backoff retry. Exits with code 1 after a successful commit to trigger a CI re-run so branch protection sees a clean pass. Does nothing if the working tree is clean.
|
|
35
|
+
|
|
36
|
+
Requires the `BRANCH` environment variable.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
BRANCH=my-feature npx auto-commit-fixes
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `sync-skills`
|
|
43
|
+
|
|
44
|
+
Pulls `.claude/` skills from GitHub repos listed in `packages/shared-config/skill-repos.json` using the GitHub REST API. Run from the monorepo root.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx sync-skills
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Set the `GITHUB_TOKEN` environment variable for private repos.
|
|
51
|
+
|
|
52
|
+
### `log-local-skills`
|
|
53
|
+
|
|
54
|
+
Reports `.claude/` files that exist locally but are not in the shared-config package. Informational only, never fails.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx log-local-skills
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## CI Workflow Integration
|
|
61
|
+
|
|
62
|
+
Typical GitHub Actions usage for auto-fix workflows:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
- name: Auto-fix lint and format
|
|
66
|
+
run: npm run fix
|
|
67
|
+
|
|
68
|
+
- name: Log local .claude skills
|
|
69
|
+
run: npx log-local-skills
|
|
70
|
+
|
|
71
|
+
- name: Commit and push fixes
|
|
72
|
+
env:
|
|
73
|
+
BRANCH: ${{ github.head_ref || github.ref_name }}
|
|
74
|
+
run: npx auto-commit-fixes
|
|
75
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-commit-fixes.d.ts","sourceRoot":"","sources":["../src/auto-commit-fixes.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
/**
|
|
6
|
+
* Auto-commits and pushes any uncommitted changes (e.g., from lint/format auto-fix or shared-config sync).
|
|
7
|
+
* Exits 0 if no changes. Exits 1 after successfully committing and pushing (to trigger CI re-run).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx auto-commit-fixes
|
|
11
|
+
*
|
|
12
|
+
* Environment:
|
|
13
|
+
* BRANCH - Required. The branch to push to (e.g., from github.head_ref || github.ref_name).
|
|
14
|
+
* GH_PAT - Optional. A GitHub PAT used for push so the commit triggers a new CI run.
|
|
15
|
+
* (Pushes with the default GITHUB_TOKEN do not trigger workflows.)
|
|
16
|
+
*/
|
|
17
|
+
const child_process_1 = require("child_process");
|
|
18
|
+
function exec(command, ignoreError = false) {
|
|
19
|
+
console.log(`$ ${command}`);
|
|
20
|
+
try {
|
|
21
|
+
return (0, child_process_1.execSync)(command, { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (ignoreError) {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function hasChanges() {
|
|
31
|
+
try {
|
|
32
|
+
(0, child_process_1.execSync)("git diff --exit-code", { stdio: "pipe" });
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function sleep(ms) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
setTimeout(resolve, ms);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function pushWithRetry(branch) {
|
|
45
|
+
for (let attempt = 1; attempt <= 4; attempt++) {
|
|
46
|
+
exec(`git pull --rebase origin ${branch}`, true);
|
|
47
|
+
try {
|
|
48
|
+
exec(`git push origin HEAD:${branch}`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
if (attempt === 4) {
|
|
53
|
+
throw new Error("Failed to push after 4 attempts");
|
|
54
|
+
}
|
|
55
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
56
|
+
console.log(`Push failed, retrying in ${String(delay / 1000)}s...`);
|
|
57
|
+
await sleep(delay);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function main() {
|
|
62
|
+
const branch = process.env.BRANCH;
|
|
63
|
+
if (branch === undefined || branch === "") {
|
|
64
|
+
console.error("Error: BRANCH environment variable is required.");
|
|
65
|
+
/* eslint-disable no-template-curly-in-string */
|
|
66
|
+
console.error("Set it in your CI workflow: BRANCH: ${{ github.head_ref || github.ref_name }}");
|
|
67
|
+
/* eslint-enable no-template-curly-in-string */
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
if (!hasChanges()) {
|
|
71
|
+
console.log("No changes detected. Nothing to commit.");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
console.log("Changes detected. Committing auto-fixes...");
|
|
75
|
+
// Use PAT for push so the commit triggers a new CI run
|
|
76
|
+
// (pushes with the default GITHUB_TOKEN do not trigger workflows)
|
|
77
|
+
const ghPat = process.env.GH_PAT;
|
|
78
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
79
|
+
if (ghPat !== undefined && ghPat !== "" && repo !== undefined) {
|
|
80
|
+
exec(`git remote set-url origin https://x-access-token:${ghPat}@github.com/${repo}.git`);
|
|
81
|
+
}
|
|
82
|
+
exec('git config --local user.email "github-actions[bot]@users.noreply.github.com"');
|
|
83
|
+
exec('git config --local user.name "github-actions[bot]"');
|
|
84
|
+
// Stash, pull latest, reapply
|
|
85
|
+
exec("git stash");
|
|
86
|
+
exec(`git pull --rebase origin ${branch}`, true);
|
|
87
|
+
exec("git stash pop", true);
|
|
88
|
+
exec("git add -A");
|
|
89
|
+
exec('git commit -m "style: auto-fix linting issues"');
|
|
90
|
+
await pushWithRetry(branch);
|
|
91
|
+
console.log("");
|
|
92
|
+
console.log("Auto-fix commit pushed successfully.");
|
|
93
|
+
console.log("This build will fail so the next CI run validates the fixes.");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
main().catch((err) => {
|
|
97
|
+
console.error("auto-commit-fixes failed:", err);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=auto-commit-fixes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-commit-fixes.js","sourceRoot":"","sources":["../src/auto-commit-fixes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-local-skills.d.ts","sourceRoot":"","sources":["../src/log-local-skills.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
/**
|
|
6
|
+
* Logs .claude/ files that exist locally but not in the shared-config package.
|
|
7
|
+
* Informational only - never fails. Helps identify skills that could be promoted to shared.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx log-local-skills
|
|
11
|
+
*/
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
function listFilesRecursive(dir) {
|
|
15
|
+
const results = [];
|
|
16
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
17
|
+
return results;
|
|
18
|
+
}
|
|
19
|
+
for (const entry of (0, fs_1.readdirSync)(dir)) {
|
|
20
|
+
if (entry === ".gitkeep") {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const fullPath = (0, path_1.join)(dir, entry);
|
|
24
|
+
const stat = (0, fs_1.statSync)(fullPath);
|
|
25
|
+
if (stat.isDirectory()) {
|
|
26
|
+
results.push(...listFilesRecursive(fullPath));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
results.push(fullPath);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
function findSharedConfigFiles() {
|
|
35
|
+
// Try to find shared-config package in node_modules
|
|
36
|
+
try {
|
|
37
|
+
const sharedConfigBase = (0, path_1.join)(process.cwd(), "node_modules", "@hardlydifficult", "shared-config", "files", ".claude");
|
|
38
|
+
if ((0, fs_1.existsSync)(sharedConfigBase)) {
|
|
39
|
+
return sharedConfigBase;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Ignore
|
|
44
|
+
}
|
|
45
|
+
// Try monorepo path
|
|
46
|
+
const monoRepoPath = (0, path_1.join)(process.cwd(), "packages", "shared-config", "files", ".claude");
|
|
47
|
+
if ((0, fs_1.existsSync)(monoRepoPath)) {
|
|
48
|
+
return monoRepoPath;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function main() {
|
|
53
|
+
const localClaudeDir = (0, path_1.join)(process.cwd(), ".claude");
|
|
54
|
+
if (!(0, fs_1.existsSync)(localClaudeDir)) {
|
|
55
|
+
console.log("No .claude/ directory found in repo root.");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const sharedClaudeDir = findSharedConfigFiles();
|
|
59
|
+
if (sharedClaudeDir === null) {
|
|
60
|
+
console.log("Could not find shared-config package. Listing all .claude/ files as local:");
|
|
61
|
+
const allFiles = listFilesRecursive(localClaudeDir);
|
|
62
|
+
for (const file of allFiles) {
|
|
63
|
+
console.log(` LOCAL: ${(0, path_1.relative)(process.cwd(), file)}`);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const localFiles = listFilesRecursive(localClaudeDir);
|
|
68
|
+
const sharedFiles = new Set(listFilesRecursive(sharedClaudeDir).map((f) => (0, path_1.relative)(sharedClaudeDir, f)));
|
|
69
|
+
const localOnly = [];
|
|
70
|
+
for (const file of localFiles) {
|
|
71
|
+
const relPath = (0, path_1.relative)(localClaudeDir, file);
|
|
72
|
+
if (!sharedFiles.has(relPath)) {
|
|
73
|
+
localOnly.push(relPath);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (localOnly.length === 0) {
|
|
77
|
+
console.log("All .claude/ files are from shared-config. No local-only files.");
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log(`Found ${String(localOnly.length)} local-only .claude/ file(s):`);
|
|
81
|
+
for (const file of localOnly) {
|
|
82
|
+
console.log(` LOCAL: .claude/${file}`);
|
|
83
|
+
}
|
|
84
|
+
console.log("\nConsider promoting these to shared-config if they should be shared across repos.");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
main();
|
|
88
|
+
//# sourceMappingURL=log-local-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-local-skills.js","sourceRoot":"","sources":["../src/log-local-skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-skills.d.ts","sourceRoot":"","sources":["../src/sync-skills.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
/**
|
|
6
|
+
* Syncs .claude/ skills from external GitHub repositories into the shared-config package.
|
|
7
|
+
* Reads skill-repos.json for the list of repos (owner/repo format).
|
|
8
|
+
* Uses the GitHub REST API with native fetch().
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx sync-skills
|
|
12
|
+
*
|
|
13
|
+
* Environment:
|
|
14
|
+
* GITHUB_TOKEN - Optional. Required for private repos. Auto-available in GitHub Actions.
|
|
15
|
+
*/
|
|
16
|
+
const fs_1 = require("fs");
|
|
17
|
+
const path_1 = require("path");
|
|
18
|
+
const GITHUB_API = "https://api.github.com";
|
|
19
|
+
function getHeaders() {
|
|
20
|
+
const headers = {
|
|
21
|
+
Accept: "application/vnd.github.v3+json",
|
|
22
|
+
"User-Agent": "hardlydifficult-sync-skills",
|
|
23
|
+
};
|
|
24
|
+
const token = process.env.GITHUB_TOKEN;
|
|
25
|
+
if (token !== undefined && token !== "") {
|
|
26
|
+
headers.Authorization = `token ${token}`;
|
|
27
|
+
}
|
|
28
|
+
return headers;
|
|
29
|
+
}
|
|
30
|
+
async function fetchContents(repo, path) {
|
|
31
|
+
const url = `${GITHUB_API}/repos/${repo}/contents/${path}`;
|
|
32
|
+
const response = await fetch(url, { headers: getHeaders() });
|
|
33
|
+
if (response.status === 404) {
|
|
34
|
+
console.log(` No ${path} directory found in ${repo}, skipping`);
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`GitHub API error ${String(response.status)}: ${await response.text()}`);
|
|
39
|
+
}
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
if (!Array.isArray(data)) {
|
|
42
|
+
// Single file, not a directory
|
|
43
|
+
return [data];
|
|
44
|
+
}
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
async function downloadFile(downloadUrl) {
|
|
48
|
+
const response = await fetch(downloadUrl, { headers: getHeaders() });
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`Failed to download ${downloadUrl}: ${String(response.status)}`);
|
|
51
|
+
}
|
|
52
|
+
return response.text();
|
|
53
|
+
}
|
|
54
|
+
async function syncDirectory(repo, remotePath, localDir) {
|
|
55
|
+
const contents = await fetchContents(repo, remotePath);
|
|
56
|
+
for (const item of contents) {
|
|
57
|
+
const localPath = (0, path_1.join)(localDir, item.name);
|
|
58
|
+
if (item.type === "dir") {
|
|
59
|
+
(0, fs_1.mkdirSync)(localPath, { recursive: true });
|
|
60
|
+
await syncDirectory(repo, item.path, localPath);
|
|
61
|
+
}
|
|
62
|
+
else if (item.download_url !== null) {
|
|
63
|
+
const content = await downloadFile(item.download_url);
|
|
64
|
+
const parentDir = (0, path_1.dirname)(localPath);
|
|
65
|
+
if (!(0, fs_1.existsSync)(parentDir)) {
|
|
66
|
+
(0, fs_1.mkdirSync)(parentDir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
(0, fs_1.writeFileSync)(localPath, content);
|
|
69
|
+
console.log(` Synced: ${item.path}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function main() {
|
|
74
|
+
// Find skill-repos.json relative to this monorepo
|
|
75
|
+
const sharedConfigDir = (0, path_1.join)(process.cwd(), "packages", "shared-config");
|
|
76
|
+
const skillReposPath = (0, path_1.join)(sharedConfigDir, "skill-repos.json");
|
|
77
|
+
if (!(0, fs_1.existsSync)(skillReposPath)) {
|
|
78
|
+
console.error("Error: packages/shared-config/skill-repos.json not found");
|
|
79
|
+
console.error("Run this command from the monorepo root.");
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const skillRepos = JSON.parse((0, fs_1.readFileSync)(skillReposPath, "utf-8"));
|
|
83
|
+
if (skillRepos.length === 0) {
|
|
84
|
+
console.log("No skill repos configured in skill-repos.json");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const targetDir = (0, path_1.join)(sharedConfigDir, "files", ".claude");
|
|
88
|
+
// Clean and recreate the target .claude directory
|
|
89
|
+
if ((0, fs_1.existsSync)(targetDir)) {
|
|
90
|
+
(0, fs_1.rmSync)(targetDir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
93
|
+
for (const repo of skillRepos) {
|
|
94
|
+
console.log(`Syncing skills from ${repo}...`);
|
|
95
|
+
await syncDirectory(repo, ".claude", targetDir);
|
|
96
|
+
}
|
|
97
|
+
console.log("Skill sync complete.");
|
|
98
|
+
}
|
|
99
|
+
main().catch((err) => {
|
|
100
|
+
console.error("Skill sync failed:", err);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=sync-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-skills.js","sourceRoot":"","sources":["../src/sync-skills.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardlydifficult/ci-scripts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Reusable CI scripts",
|
|
5
5
|
"bin": {
|
|
6
6
|
"check-pinned-deps": "./dist/check-pinned-deps.js",
|
|
7
|
-
"monorepo-publish": "./dist/publish.js"
|
|
7
|
+
"monorepo-publish": "./dist/publish.js",
|
|
8
|
+
"sync-skills": "./dist/sync-skills.js",
|
|
9
|
+
"log-local-skills": "./dist/log-local-skills.js",
|
|
10
|
+
"auto-commit-fixes": "./dist/auto-commit-fixes.js"
|
|
8
11
|
},
|
|
9
12
|
"files": [
|
|
10
13
|
"dist"
|