Compare commits
4 commits
main
...
repo/chore
| Author | SHA1 | Date | |
|---|---|---|---|
| 057fc31c59 | |||
| 35273f525d | |||
| a941565528 | |||
| 99a7717410 |
4 changed files with 97 additions and 49 deletions
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
name: Continuous Integration (CI)
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node: [22, 24]
|
|
||||||
name: Node ${{ matrix.node }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
cache: "npm"
|
|
||||||
cache-dependency-path: "package-lock.json"
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run test --workspaces --if-present
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -36,7 +36,7 @@
|
||||||
!/flake.lock
|
!/flake.lock
|
||||||
|
|
||||||
### Repository configuration ###
|
### Repository configuration ###
|
||||||
!/.github/workflows/*.yml
|
!/.gitlab-ci.yml
|
||||||
!/.husky/commit-msg
|
!/.husky/commit-msg
|
||||||
!/conventional.config.js
|
!/conventional.config.js
|
||||||
|
|
||||||
|
|
|
||||||
18
.gitlab-ci.yml
Normal file
18
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
stages: [build, test]
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
cache: &cached-deps
|
||||||
|
key:
|
||||||
|
files: [package-lock.json]
|
||||||
|
paths: [node_modules/]
|
||||||
|
script:
|
||||||
|
- npm ci
|
||||||
|
- npm run build --workspaces --if-present
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
cache:
|
||||||
|
<<: &cached-deps
|
||||||
|
script:
|
||||||
|
- npm run test --workspaces --if-present
|
||||||
|
|
@ -15,21 +15,14 @@ const validateRepoScopedCommit = (message) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const packagePrefix = "@websnacksjs/";
|
|
||||||
const packages = await fs.readdir(new URL("./packages", import.meta.url));
|
const packages = await fs.readdir(new URL("./packages", import.meta.url));
|
||||||
|
const validScopes = ["repo", ...packages];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("@websnacksjs/conventional").CommitMessage} message
|
* @param {import("@websnacksjs/conventional").CommitMessage} message
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
const validatePackageScopedCommit = (message) => {
|
const validatePackageScopedCommit = (message) => {
|
||||||
const pkg = message.scope?.slice(packagePrefix.length) ?? "";
|
|
||||||
if (!packages.includes(pkg)) {
|
|
||||||
throw new Error(
|
|
||||||
`unknown package ${JSON.stringify(pkg)} referenced in commit scope`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const supportedTypes = ["feat", "fix", "docs", "test", "chore"];
|
const supportedTypes = ["feat", "fix", "docs", "test", "chore"];
|
||||||
if (!supportedTypes.includes(message.type)) {
|
if (!supportedTypes.includes(message.type)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -39,6 +32,73 @@ const validatePackageScopedCommit = (message) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} value
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const validateUrl = (value) => {
|
||||||
|
try {
|
||||||
|
new URL(value);
|
||||||
|
} catch {
|
||||||
|
const error = new Error(
|
||||||
|
`expected valid URL but got ${JSON.stringify(value)}`,
|
||||||
|
);
|
||||||
|
Error.captureStackTrace(error, validateUrl);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("@websnacksjs/conventional").Footer[]} footers
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
const validateFooters = (footers) => {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const unsupportedFooters = [];
|
||||||
|
/** @type {{footer: string, reason: Error}[]} */
|
||||||
|
const invalidFooters = [];
|
||||||
|
for (const { key, value } of footers) {
|
||||||
|
try {
|
||||||
|
switch (key) {
|
||||||
|
case "Merge-request": {
|
||||||
|
validateUrl(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
unsupportedFooters.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof Error)) {
|
||||||
|
throw new Error(
|
||||||
|
`caught unexpected non-error value ${JSON.stringify(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
invalidFooters.push({ footer: key, reason: error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMessageParts = [];
|
||||||
|
if (unsupportedFooters.length > 0) {
|
||||||
|
let message = `unspported footers in message:`;
|
||||||
|
for (const footer of unsupportedFooters) {
|
||||||
|
message += `\n\t- ${footer}`;
|
||||||
|
}
|
||||||
|
errorMessageParts.push(message);
|
||||||
|
}
|
||||||
|
if (invalidFooters.length > 0) {
|
||||||
|
let message = `invalid footers in message:`;
|
||||||
|
for (const { footer, reason } of invalidFooters) {
|
||||||
|
message += `\n\t- ${footer}: ${reason.message}`;
|
||||||
|
}
|
||||||
|
errorMessageParts.push(message);
|
||||||
|
}
|
||||||
|
if (errorMessageParts.length > 0) {
|
||||||
|
const message = errorMessageParts.join("\n\n");
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
validateCommitMessage(message) {
|
validateCommitMessage(message) {
|
||||||
if (!message.scope) {
|
if (!message.scope) {
|
||||||
|
|
@ -47,25 +107,19 @@ export default defineConfig({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.footers.length > 0) {
|
if (message.scope === "repo") {
|
||||||
|
validateRepoScopedCommit(message);
|
||||||
|
} else if (packages.includes(message.scope)) {
|
||||||
|
validatePackageScopedCommit(message);
|
||||||
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`commit message footers are currently unsupported ` +
|
[
|
||||||
`(try removing them from your commit message)`,
|
`scope ${JSON.stringify(message.scope)} is unsupported`,
|
||||||
|
`(try one of ${JSON.stringify(validScopes).replace(",", ", ")})`,
|
||||||
|
].join(" "),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.scope === "repo") {
|
validateFooters(message.footers);
|
||||||
validateRepoScopedCommit(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.scope.startsWith(packagePrefix)) {
|
|
||||||
validatePackageScopedCommit(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`scope ${JSON.stringify(message.scope)} is unsupported (try one of ["repo", "@websnacksjs/:package"])`,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue