Skip to content

Commit

Permalink
feat: Add @commitlint/load as external dependency (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevintyj authored Jun 26, 2024
1 parent 841643e commit a99014b
Show file tree
Hide file tree
Showing 1,057 changed files with 120,782 additions and 106 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-students-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kevintyj/prlint": minor
---

Fixes unresolved behavior on remote action run
21 changes: 2 additions & 19 deletions .github/workflows/prlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,8 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦Setup PNPM
uses: pnpm/action-setup@v4
with:
version: 9
- name: 🌳Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
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: ./
with:
download-dependencies: node
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
lib

# dependencies
node_modules
node_modules/
!dist/**/node_modules
!dist/**/node_modules/**

# IDEs and editors
/.idea
Expand Down
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
npx lint-staged --config .lintstagedrc.json
4 changes: 4 additions & 0 deletions .lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"src/*": "eslint --fix",
"__tests__/*": "eslint --fix"
}
86 changes: 78 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,53 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📝Validate PR title with commitlint
uses: kevintyj/prlint@v2
# Optional
with:
download-dependencies: ignore
```
The above action only check's out the current repository to fetch the commitlint configuration file.
PNPM and node is used to install necessary dependencies, then config-conventional is used as a default config.
When using the above configuration, `pnpm-lock.yaml` is required. Please use npm and node if `package-lock.json` is used.

## v2 release

> [!CAUTION]
> v2.0.0 release is an unstable breaking build. v2.0.0 is known to have issues with
> **remote action run**. You will still be able to run v2.0.0 as a local action.

----

**This new major version of prlint updates commitlint package to the new v19.
This update removes support for CJS and only exports the app as a ESM package
(this should not affect the way you use this plugin in any way as the Github
node runner handles ESM just fine).**

### New configuration dependency option
Now users can make the dependency install step **optional**. The action will automatically install the dependency listed in your configuration file.
This means that if the user's config does not have any dependencies, the entire node and npm setup process can be omitted.

**See Inputs section for more details on how to set this option**

However, if you wish to still download dependencies as a part of your workflow (recommended with cache enabled), resulting in a file as follows:

`prlint.yml`
```yaml
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@v4
with:
fetch-depth: 0
- name: 📦Setup PNPM
uses: pnpm/action-setup@v4
with:
Expand All @@ -39,22 +86,45 @@ jobs:
with:
node-version: 22
cache: pnpm
- name: 🗂️ Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: 🚀 Cache pnpm packages
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: 🛠️Install dependencies for prlint
run: pnpm install @commitlint/config-conventional
- name: 📝Validate PR title with commitlint
uses: kevintyj/prlint@v2
```
The above action only check's out the current repository to fetch the commitlint configuration file.
PNPM and node is used to install necessary dependencies, then config-conventional is used as a default config.
When using the above configuration, `pnpm-lock.yaml` is required. Please use npm and node if `package-lock.json` is used.

## v2 release
**This new major version of prlint updates commitlint package to the new v19.
This update removes support for CJS and only exports the app as a ESM package
(this should not affect the way you use this plugin in any way as the Github
node runner handles ESM just fine).**
**When to use different options**

| Project environment and nature of commitlint config | Recommended method |
|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| Does not use "extends" or outside dependencies **Recommended** | 'ignore' |
| Does not use "extends" but also contains other outside dependencies | 'ignore' and install dependencies using actions |
| Does use "extends" but no outside dependencies | 'node' when no caching is used 'ignore' and install dependencies using actions when caching is enabled |
| Does use "extends" but also contains other outside dependencies | 'ignore' and install dependencies using actions |

### Inputs

#### `download-dependencies`
**Optional** Experimental - use node to download commitlint config dependency (eg. @commitlint/config-conventional) Default : `'ignore'`

Options:
- `'node'` uses node's exec to run a child process to automatically detect the dependency and install from action (may be slower if caching is not enabled in actions) - currently only limited to `@commitlint/config-conventional`
- `'ignore'` expects all dependencies to be added with actions script using a package manager such as npm or pnpm

> Only available in v2
>
#### `cl-config`
**Optional** Path to commit lint config. Default : `'commitlint.config.js'`

Expand Down
20 changes: 16 additions & 4 deletions __tests__/errHandle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,48 @@ describe('error handler', () => {
beforeEach(() => {
vi.clearAllMocks();
});

it('simple string error calls error and fail once', () => {
handleError('error');
expect(debugErr).toBeCalledWith('error');
expect(debugErr).toBeCalledTimes(1);
expect(debugFail).toBeCalledWith('error');
expect(debugFail).toBeCalledTimes(1);
});

it('simple string warning calls error once but does not block', () => {
handleError('error', false);
expect(debugErr).toBeCalledWith('error');
expect(debugErr).toBeCalledTimes(1);
expect(debugFail).toBeCalledTimes(0);
});

it('class error calls error and fail once', () => {
handleError(new Error('error'));
expect(debugErr).toBeCalledWith(new Error('error'));
const errorInstance = new Error('error');
// eslint-disable-next-line ts/no-unsafe-return
expect(() => handleError(errorInstance)).not.toThrow();
expect(debugErr).toBeCalledWith(errorInstance);
expect(debugErr).toBeCalledTimes(1);
expect(debugFail).toBeCalledWith(`Error Name: ${errorInstance.name} \nMessage: ${errorInstance.message} \nStack: ${errorInstance.stack}`);
expect(debugFail).toBeCalledTimes(1);
});

it('class error warning calls error once but does not block', () => {
handleError(new Error('error'), false);
expect(debugErr).toBeCalledWith(new Error('error'));
const errorInstance = new Error('error');
handleError(errorInstance, false);
expect(debugErr).toBeCalledWith(errorInstance);
expect(debugErr).toBeCalledTimes(1);
expect(debugFail).toBeCalledTimes(0);
});

it('unknown error calls error and fail once', () => {
handleError(false);
expect(debugErr).toBeCalledWith('Unknown error has occurred!');
expect(debugErr).toBeCalledTimes(1);
expect(debugFail).toBeCalledWith('Unknown error has occurred!');
expect(debugFail).toBeCalledTimes(1);
});

it('unknown error warning calls error once but does not block', () => {
handleError(false, false);
expect(debugErr).toBeCalledWith('Unknown error has occurred!');
Expand Down
37 changes: 29 additions & 8 deletions __tests__/lint.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import process from 'node:process';
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import load from '@commitlint/load';
import { testLintOptions, verifyTitle } from '../src/lint';

const { getLintOptions } = testLintOptions;
const { getLintOptions, extractPackageNameFromError, loadCommitLintConfig } = testLintOptions;

Check warning on line 6 in __tests__/lint.test.ts

View workflow job for this annotation

GitHub Actions / build

Unsafe assignment of an error typed value

/* eslint-disable regexp/no-super-linear-backtracking */
const emptyConfigOption = {
Expand Down Expand Up @@ -61,14 +61,35 @@ describe('commitlint', async () => {
});

it('throw error on incorrect title', async () => {
await expect(verifyTitle('foo: bar')).rejects.toThrowError(/check failed/);
await expect(verifyTitle('foo: bar', 'something.config.js')).rejects.toThrowError(/subject-case/);
await expect(verifyTitle('test: add tests', 'commitlint.config.js')).rejects.toThrowError(/sentence-case/);
await expect(verifyTitle('foo: bar.', { downloadOptions: 'node' })).rejects.toThrow();
await expect(verifyTitle('foo: bar.', { downloadOptions: 'ignore' })).rejects.toThrow();
await expect(verifyTitle('test: add tests', { downloadOptions: 'test' })).rejects.toThrowError(/sentence-case/);
});

it('return true if title is valid', async () => {
await expect(verifyTitle('fix: Add new commets')).resolves.toEqual(true);
await expect(verifyTitle('feat: Title is short and nice!', 'something.config.js')).resolves.toEqual(true);
await expect(verifyTitle('test: Add test suites', 'commitlint.config.js')).resolves.toEqual(true);
await expect(verifyTitle('fix: Add new comments', { downloadOptions: 'test' })).resolves.toEqual(true);
await expect(verifyTitle('feat: Title is short and nice!', { downloadOptions: 'test' })).resolves.toEqual(true);
await expect(verifyTitle('test: Add test suites', { downloadOptions: 'test' })).resolves.toEqual(true);
});
});

describe('handler', async () => {
it('misc errors should return empty', () => {
expect(extractPackageNameFromError('Error: You forgot a semicolon')).toBeNull;
});

it('valid errors should return package', () => {
expect(extractPackageNameFromError('Cannot find module "semicolon"')).toBe('semicolon');
});

it('test valid config', () => {
expect(loadCommitLintConfig()).resolves.not.toThrow;
expect(loadCommitLintConfig('node')).resolves.not.toThrow;
});

it('test failing config', () => {
vi.spyOn(process, 'cwd').mockReturnValue('/text-tmp');
expect(loadCommitLintConfig('node')).toThrow;
expect(loadCommitLintConfig('ignore')).toThrow;
});
});
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ description: Ensure PR title match commitlint config
runs:
using: node20
main: dist/index.js
inputs:
download-dependencies:
description: 'Download commitlint dependencies'
required: false
default: 'ignore'
outputs:
lint-status:
description: The status of the PR lint
Expand Down
Loading

0 comments on commit a99014b

Please sign in to comment.