Skip to content

Commit 357eb9b

Browse files
author
Vins
committed
chore: add cosmokeeper🚦
1 parent 13edfea commit 357eb9b

File tree

9 files changed

+6621
-6696
lines changed

9 files changed

+6621
-6696
lines changed

Diff for: .cosmokeeper.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"lint": {
3+
"eslint": true,
4+
"prettier": true,
5+
"matches": "(.ts)$"
6+
},
7+
"patterns": {
8+
"branch": "/(w+/w+|main|master)/g"
9+
}
10+
}

Diff for: .cosmokeeper/bin/commit-msg.ts

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import * as fs from "fs";
2+
import { ParamsOf } from "../../src/utils/types";
3+
import { spawnSync } from "child_process";
4+
import * as rl from "readline";
5+
6+
const question = (q: string): Promise<string> => {
7+
return new Promise<string>((resolve, reject) => {
8+
readline.question(q, res => {
9+
resolve(res);
10+
});
11+
});
12+
};
13+
14+
// read interface
15+
const readline = rl.createInterface({
16+
input: process.stdin,
17+
output: process.stdout
18+
});
19+
20+
const normalizePath = (path: string): string => {
21+
return path.replace(/^\.\//g, "");
22+
};
23+
24+
export const controlledSpawn = (...params: ParamsOf<typeof spawnSync>) => {
25+
params[2] && !params[2].encoding && (params[2].encoding = "utf8");
26+
const output = spawnSync(...params);
27+
if (output.status !== 0) {
28+
console.error(output.stderr?.toString() ?? "");
29+
process.exit(1);
30+
}
31+
32+
return output.stdout.toString();
33+
};
34+
35+
// RUN
36+
const run = async () => {
37+
const GIT_ROOT = controlledSpawn("git", [
38+
"rev-parse",
39+
"--show-toplevel"
40+
]).trim();
41+
42+
console.log("reading", `${GIT_ROOT}/.cosmokeeper.json`);
43+
// read the configuration
44+
if (!fs.existsSync(`${GIT_ROOT}/.cosmokeeper.json`)) {
45+
console.error(
46+
"missing .cosmokeeper configuration file, please run 'npx @futura-dev/cosmokeeper init' to create it."
47+
);
48+
process.exit(1);
49+
}
50+
const config = JSON.parse(
51+
fs.readFileSync(`${GIT_ROOT}/.cosmokeeper.json`, { encoding: "utf8" })
52+
);
53+
const package_json = JSON.parse(
54+
fs.readFileSync(`${GIT_ROOT}/package.json`, { encoding: "utf8" })
55+
);
56+
57+
// TODO: validate package.json
58+
// TODO: validate config
59+
60+
const IS_MONO_REPO = !!package_json.workspaces;
61+
const STAGED_FILES = controlledSpawn("git", [
62+
"diff",
63+
"--cached",
64+
"--name-only"
65+
]).split(/(\s|\n|\r|\r\n)/);
66+
67+
// MONOREPO
68+
if (IS_MONO_REPO) {
69+
const packages: string[] = package_json.workspaces;
70+
// find all the package slugs
71+
const visited_slugs = new Set<string>();
72+
const slugs = packages
73+
.map((pkg: string) => `${pkg}/package.json`)
74+
.map((pkg_json: string) => {
75+
const json = JSON.parse(
76+
fs.readFileSync(pkg_json, { encoding: "utf8" })
77+
);
78+
if (json.slug === undefined) {
79+
console.error(`missing slug in ${pkg_json}.`);
80+
process.exit(1);
81+
}
82+
if (visited_slugs.has(json.slug)) {
83+
console.error(`slug ${json.slug} must be unique in your packages.`);
84+
process.exit(1);
85+
}
86+
visited_slugs.add(json.slug);
87+
return json.slug;
88+
});
89+
90+
// Check if the branch name contains any slug
91+
const current_branch = controlledSpawn("git", [
92+
"symbolic-ref",
93+
"--short",
94+
"HEAD"
95+
]).trim();
96+
const branch_name_contains_a_slug = slugs.reduce((acc, slug) => {
97+
return (
98+
acc ||
99+
new RegExp(`${slug}@${config.patterns.branch}`).test(current_branch) ||
100+
["main", "master"].includes(current_branch)
101+
);
102+
}, false);
103+
if (!branch_name_contains_a_slug) {
104+
console.error(
105+
`branch name \'${current_branch}\' does not respect the pattern '[slug]@${config.patterns.branch}|main|master'`
106+
);
107+
process.exit(1);
108+
}
109+
110+
/**
111+
* - take staged files
112+
* - foreach staged file take the corresponding package
113+
* - save the package ( path or name )
114+
* - check if is unique
115+
*/
116+
const visited_packages = new Set<string>();
117+
for (const file of STAGED_FILES) {
118+
// take the package
119+
const pkg =
120+
packages.find(pkg =>
121+
new RegExp(`${normalizePath(pkg)}`).test(normalizePath(file))
122+
) ?? "root";
123+
visited_packages.add(pkg);
124+
}
125+
126+
if (visited_packages.size > 1) {
127+
// ask
128+
console.log("Warning: Modified files found in multiple packages.");
129+
const input = await question("Do you want to allow this commit? (y/N):");
130+
if (input.toLowerCase() !== "y") {
131+
console.error("execution stopped");
132+
process.exit(1);
133+
}
134+
}
135+
} else {
136+
// Check if the branch name matches the configured patters
137+
const current_branch = controlledSpawn("git", [
138+
"symbolic-ref",
139+
"--short",
140+
"HEAD"
141+
]);
142+
if (new RegExp(`${config.patterns.branch}`).test(current_branch)) {
143+
console.error(
144+
`branch name \'${current_branch}\' does not respect the pattern '${config.patterns.branch}'`
145+
);
146+
process.exit(1);
147+
}
148+
}
149+
150+
// COMMON
151+
// ( lint, prettier )
152+
const TO_LINT = STAGED_FILES.filter(file =>
153+
new RegExp(`${config.lint.matches}`).test(file)
154+
);
155+
156+
if (config.lint.eslintb && TO_LINT.length > 0)
157+
controlledSpawn("npx", ["eslint", ...TO_LINT, "--fix"]);
158+
159+
if (config.lint.prettier && TO_LINT.length > 0)
160+
controlledSpawn("npx", ["prettier", ...TO_LINT, "--write"]);
161+
162+
process.exit(0);
163+
};
164+
run();

Diff for: .cosmokeeper/commit-msg

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#! /bin/sh
2+
3+
# TODO: insert here an explanation
4+
# TODO: insert here the checks ( node installed, correct node version, ...)
5+
6+
GIT_ROOT=$(git rev-parse --show-toplevel)
7+
8+
exec < /dev/tty
9+
10+
if ! npx --no tsx $GIT_ROOT/.cosmokeeper/bin/commit-msg.ts;
11+
then
12+
exit 1
13+
fi
14+
15+
# Check conventional commit
16+
if ! npx --no -- commitlint --edit "$1";
17+
then
18+
exit 1
19+
fi
20+
21+
exit 0

Diff for: commitlint.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ["@commitlint/config-conventional"],
3+
rules: {
4+
"body-max-length": [0]
5+
}
6+
};

0 commit comments

Comments
 (0)