diff --git a/.github/workflows/prlint.yml b/.github/workflows/prlint.yml new file mode 100644 index 0000000..6ae38ed --- /dev/null +++ b/.github/workflows/prlint.yml @@ -0,0 +1,35 @@ +name: 📝 Lint PR title +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: 🔖Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: 📦Setup PNPM + uses: pnpm/action-setup@v2 + with: + version: 8 + - name: 🌳Setup Node + uses: actions/setup-node@v3 + with: + node-version: 20 + cache: pnpm + + - name: 🛠️Install dependencies from lockfile + run: pnpm install --frozen-lockfile + + - name: 🧾Print versions + run: | + git --version + node --version + pnpm --version + pnpm commitlint --version + + - name: 📝Validate PR title with commitlint + uses: ./packages/commitlint diff --git a/.gitignore b/.gitignore index 397bb7e..4b14332 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # dist dist +# commited dist files +!packages/commitlint/dist + # dependencies node_modules diff --git a/eslint.config.js b/eslint.config.js index 83155ac..31c549d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -14,6 +14,7 @@ export default antfu( '**/dist', '.idea', '**/__fixtures__', + 'eslint.config.js', 'packages/conventionalsets-action', ], overrides: { diff --git a/package.json b/package.json index 990f772..5b42848 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@antfu/eslint-config": "^1.1.0", "@changesets/cli": "^2.26.2", "@commitlint/cli": "^18.2.0", + "@commitlint/config-conventional": "^18.1.0", "@vitest/coverage-v8": "^0.34.6", "eslint": "^8.53.0", "husky": "^8.0.3", diff --git a/packages/commitlint/action.yml b/packages/commitlint/action.yml new file mode 100644 index 0000000..0f74941 --- /dev/null +++ b/packages/commitlint/action.yml @@ -0,0 +1,9 @@ +name: Commitlint (prlint) +author: Kevin Taeyoon Jin +description: Ensure PR title match commitlint config +runs: + using: node20 + main: dist/index.cjs +branding: + icon: git-pull-request + color: green diff --git a/packages/commitlint/dist/index.cjs b/packages/commitlint/dist/index.cjs new file mode 100644 index 0000000..cd68e9f --- /dev/null +++ b/packages/commitlint/dist/index.cjs @@ -0,0 +1,96 @@ +'use strict'; + +var github = require('@actions/github'); +var core = require('@actions/core'); +var process = require('process'); +var fs = require('fs'); +var load = require('@commitlint/load'); +var lint = require('@commitlint/lint'); + +function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } + +function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n.default = e; + return Object.freeze(n); +} + +var github__namespace = /*#__PURE__*/_interopNamespace(github); +var process__default = /*#__PURE__*/_interopDefault(process); +var fs__namespace = /*#__PURE__*/_interopNamespace(fs); +var load__default = /*#__PURE__*/_interopDefault(load); +var lint__default = /*#__PURE__*/_interopDefault(lint); + +// src/index.ts +function handleError(err, fail = true) { + if (err instanceof Error) { + core.error(err); + fail && core.setFailed(err.message); + } else { + const message = typeof err == "string" ? err : "Unknown error has occurred!"; + core.error(message); + fail && core.setFailed(message); + } +} +var errHandle_default = handleError; + +// src/log.ts +var SEPARATOR = "====================="; +function logWithTile(title, content) { + return `${title} +${SEPARATOR} +${content}`; +} +var log_default = logWithTile; + +// src/lint.ts +var defaultConfig = { + extends: "@commitlint/config-conventional" +}; +function getLintOptions(configuration) { + var _a; + return { + defaultIgnores: configuration.defaultIgnores ? configuration.defaultIgnores : true, + ignores: configuration.ignores ? configuration.ignores : void 0, + parserOpts: typeof ((_a = configuration.parserPreset) == null ? void 0 : _a.parserOpts) == "object" ? configuration.parserPreset.parserOpts : void 0, + plugins: configuration.plugins ? configuration.plugins : void 0, + helpUrl: configuration.helpUrl ? configuration.helpUrl : void 0 + }; +} +async function verifyTitle(title, configPath = "") { + const commitlintConfig = fs__namespace.existsSync(configPath) ? await load__default.default({}, { file: configPath, cwd: process__default.default.cwd() }) : await load__default.default(defaultConfig); + const linterResult = await lint__default.default(title, commitlintConfig.rules, getLintOptions(commitlintConfig)); + if (linterResult.valid) { + return true; + } else { + const errors = linterResult.errors.map((error2) => { + return `${error2.name}: ${error2.message}`; + }).join("\n"); + throw new Error(log_default("Commitlint check failed!", errors)); + } +} + +// src/index.ts +async function run() { + const pullRequestPayload = github__namespace.context.payload.pull_request; + if (!(pullRequestPayload == null ? void 0 : pullRequestPayload.title)) + throw new Error("Pull Request or Title not found!"); + const pullRequestObject = { + title: pullRequestPayload.title, + number: pullRequestPayload.number + }; + await verifyTitle(pullRequestObject.title, "commitlint.config.js"); +} +run().catch(errHandle_default); diff --git a/packages/commitlint/dist/index.js b/packages/commitlint/dist/index.js new file mode 100644 index 0000000..fa3cb8f --- /dev/null +++ b/packages/commitlint/dist/index.js @@ -0,0 +1,68 @@ +import * as github from '@actions/github'; +import { error, setFailed } from '@actions/core'; +import process from 'process'; +import * as fs from 'fs'; +import load from '@commitlint/load'; +import lint from '@commitlint/lint'; + +// src/index.ts +function handleError(err, fail = true) { + if (err instanceof Error) { + error(err); + fail && setFailed(err.message); + } else { + const message = typeof err == "string" ? err : "Unknown error has occurred!"; + error(message); + fail && setFailed(message); + } +} +var errHandle_default = handleError; + +// src/log.ts +var SEPARATOR = "====================="; +function logWithTile(title, content) { + return `${title} +${SEPARATOR} +${content}`; +} +var log_default = logWithTile; + +// src/lint.ts +var defaultConfig = { + extends: "@commitlint/config-conventional" +}; +function getLintOptions(configuration) { + var _a; + return { + defaultIgnores: configuration.defaultIgnores ? configuration.defaultIgnores : true, + ignores: configuration.ignores ? configuration.ignores : void 0, + parserOpts: typeof ((_a = configuration.parserPreset) == null ? void 0 : _a.parserOpts) == "object" ? configuration.parserPreset.parserOpts : void 0, + plugins: configuration.plugins ? configuration.plugins : void 0, + helpUrl: configuration.helpUrl ? configuration.helpUrl : void 0 + }; +} +async function verifyTitle(title, configPath = "") { + const commitlintConfig = fs.existsSync(configPath) ? await load({}, { file: configPath, cwd: process.cwd() }) : await load(defaultConfig); + const linterResult = await lint(title, commitlintConfig.rules, getLintOptions(commitlintConfig)); + if (linterResult.valid) { + return true; + } else { + const errors = linterResult.errors.map((error2) => { + return `${error2.name}: ${error2.message}`; + }).join("\n"); + throw new Error(log_default("Commitlint check failed!", errors)); + } +} + +// src/index.ts +async function run() { + const pullRequestPayload = github.context.payload.pull_request; + if (!(pullRequestPayload == null ? void 0 : pullRequestPayload.title)) + throw new Error("Pull Request or Title not found!"); + const pullRequestObject = { + title: pullRequestPayload.title, + number: pullRequestPayload.number + }; + await verifyTitle(pullRequestObject.title, "commitlint.config.js"); +} +run().catch(errHandle_default); diff --git a/packages/commitlint/src/lint.ts b/packages/commitlint/src/lint.ts index df0bbfd..a251dd7 100644 --- a/packages/commitlint/src/lint.ts +++ b/packages/commitlint/src/lint.ts @@ -3,6 +3,7 @@ import * as fs from 'node:fs'; import type { LintOptions, ParserOptions, QualifiedConfig } from '@commitlint/types'; import load from '@commitlint/load'; import lint from '@commitlint/lint'; +import { setOutput } from '@actions/core'; import logWithTile from './log'; const defaultConfig = { @@ -44,6 +45,7 @@ export async function verifyTitle(title: string, configPath: string = ''): Promi const linterResult = await lint(title, commitlintConfig.rules, getLintOptions(commitlintConfig)); if (linterResult.valid) { + setOutput('✅ Commitlint tests passed!\n', linterResult); return true; } else { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5f313a..6473978 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@commitlint/cli': specifier: ^18.2.0 version: 18.2.0(typescript@5.2.2) + '@commitlint/config-conventional': + specifier: ^18.1.0 + version: 18.1.0 '@vitest/coverage-v8': specifier: ^0.34.6 version: 0.34.6(vitest@0.34.6) @@ -1731,7 +1734,6 @@ packages: engines: {node: '>=v18'} dependencies: conventional-changelog-conventionalcommits: 7.0.2 - dev: false /@commitlint/config-validator@18.1.0: resolution: {integrity: sha512-kbHkIuItXn93o2NmTdwi5Mk1ujyuSIysRE/XHtrcps/27GuUKEIqBJp6TdJ4Sq+ze59RlzYSHMKuDKZbfg9+uQ==} @@ -3749,7 +3751,6 @@ packages: engines: {node: '>=16'} dependencies: compare-func: 2.0.0 - dev: false /conventional-commits-parser@5.0.0: resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} diff --git a/tsconfig.json b/tsconfig.json index a205987..a4a4a51 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,5 @@ "noUnusedParameters": true, "forceConsistentCasingInFileNames": true }, - "include": ["src"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "**/dist"] }