Skip to content

Commit

Permalink
chore(ci): prevent commit including generated files (#379)
Browse files Browse the repository at this point in the history
* chore(ci): add pre-commit hook to unstage generated codes

* chore: test commit

* chore: implement pre-commit script

* chore: fix lint error

* chore: fix lint error

* chore: call js file directly

* chore: update .cache_version

* Update scripts/ci/husky/pre-commit.js

Co-authored-by: Pierre Millot <[email protected]>

* Update scripts/ci/husky/pre-commit.js

Co-authored-by: Pierre Millot <[email protected]>

* chore: use postinstall instead of prepare

* chore: better error message

* chore: fix patterns

* chore: unstage files automatically and exit with 0

* chore: change loop

* chore: skip deleted files

* chore: split config to a separate file

* chore: update docs

* chore: update cache version

Co-authored-by: Pierre Millot <[email protected]>
  • Loading branch information
eunjae-lee and millotp authored Apr 22, 2022
1 parent 04c1122 commit 9fbba0e
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .github/.cache_version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.0.5.1
8.0.5.2
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

./scripts/ci/husky/pre-commit.js
30 changes: 30 additions & 0 deletions config/generation.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// eslint-disable-next-line import/no-commonjs
module.exports = {
patterns: [
// Ignore the roots and go down the tree by negating hand written files
'clients/**',
'!clients/README.md',
'!clients/**/.openapi-generator-ignore',

// Java
'!clients/algoliasearch-client-java-2/algoliasearch-core/src/com/algolia/exceptions/*',
'!clients/algoliasearch-client-java-2/algoliasearch-core/src/com/algolia/utils/*',
'clients/algoliasearch-client-java-2/algoliasearch-core/com/algolia/utils/echo/EchoResponse*.java',
'!clients/algoliasearch-client-java-2/algoliasearch-core/com/algolia/utils/echo/EchoResponseInterface.java',

// JavaScript
'!clients/algoliasearch-client-javascript/*',
'!clients/algoliasearch-client-javascript/.github/**',
'!clients/algoliasearch-client-javascript/.yarn/**',
'!clients/algoliasearch-client-javascript/scripts/**',
'!clients/algoliasearch-client-javascript/packages/algoliasearch/**',
'!clients/algoliasearch-client-javascript/packages/requester-*/**',
'!clients/algoliasearch-client-javascript/packages/client-common/**',

// PHP
'!clients/algoliasearch-client-php/lib/Configuration/*',
'clients/algoliasearch-client-php/lib/*.php',
'clients/algoliasearch-client-php/lib/Api/*',
'clients/algoliasearch-client-php/lib/Configuration/Configuration.php',
],
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"docker:setup": "yarn docker:clean && yarn docker:build && yarn docker:mount",
"fix:json": "eslint --ext=json . --fix",
"github-actions:lint": "eslint --ext=yml .github/",
"postinstall": "yarn workspace eslint-plugin-automation-custom build",
"postinstall": "husky install && yarn workspace eslint-plugin-automation-custom build",
"playground:browser": "yarn workspace javascript-browser-playground start",
"release": "yarn workspace scripts createReleaseIssue",
"scripts:lint": "eslint --ext=ts scripts/",
Expand Down Expand Up @@ -50,6 +50,7 @@
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-unused-imports": "2.0.0",
"eslint-plugin-yml": "0.14.0",
"husky": "7.0.4",
"json": "11.0.0",
"mustache": "4.2.0",
"prettier": "2.6.2",
Expand Down
24 changes: 24 additions & 0 deletions scripts/ci/husky/__tests__/pre-commit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-disable import/no-commonjs */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { createMemoizedMicromatchMatcher } = require('../pre-commit');

describe('createMemoizedMicromatchMatcher', () => {
it('matches correctly', () => {
const matcher = createMemoizedMicromatchMatcher([
'clients/**',
'!clients/README.md',
]);

expect(matcher('clients/README.md')).toEqual(false);
expect(matcher('clients/CONTRIBUTING.md')).toEqual(true);
});

it('prioritizes the exact match when two patterns conflict', () => {
const matcher = createMemoizedMicromatchMatcher([
'!lib/Configuration/*',
'lib/Configuration/Configuration.php',
]);

expect(matcher('lib/Configuration/Configuration.php')).toEqual(true);
});
});
81 changes: 81 additions & 0 deletions scripts/ci/husky/pre-commit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env node
/* eslint-disable no-console */
/* eslint-disable import/no-commonjs */
/* eslint-disable @typescript-eslint/no-var-requires */
const chalk = require('chalk');
const execa = require('execa');
const micromatch = require('micromatch');

const GENERATED_FILE_PATTERNS =
require('../../../config/generation.config').patterns;

const run = async (command, { cwd } = {}) => {
return (
(await execa.command(command, { shell: 'bash', all: true, cwd })).all ?? ''
);
};

function createMemoizedMicromatchMatcher(patterns = []) {
const exactMatchers = [];
const positiveMatchers = [];
const negativeMatchers = [];

patterns.forEach((pattern) => {
if (pattern.startsWith('!')) {
// Patterns starting with `!` are negated
negativeMatchers.push(micromatch.matcher(pattern.slice(1)));
} else if (!pattern.includes('*')) {
exactMatchers.push(micromatch.matcher(pattern));
} else {
positiveMatchers.push(micromatch.matcher(pattern));
}
});

return function matcher(str) {
if (exactMatchers.some((match) => match(str))) {
return true;
}

// As `some` returns false on empty array, test will always fail if we only
// provide `negativeMatchers`. We fallback to `true` is it's the case.
const hasPositiveMatchers =
Boolean(positiveMatchers.length === 0 && negativeMatchers.length) ||
positiveMatchers.some((match) => match(str));

return hasPositiveMatchers && !negativeMatchers.some((match) => match(str));
};
}

async function preCommit() {
const stagedFiles = (await run(`git diff --name-only --cached`)).split('\n');
const deletedFiles = new Set(
(await run(`git ls-files --deleted`)).split('\n')
);
const matcher = createMemoizedMicromatchMatcher(GENERATED_FILE_PATTERNS);

for (const stagedFile of stagedFiles) {
// keep the deleted files staged even if they were generated before.
if (deletedFiles.has(stagedFile)) {
continue;
}

if (!matcher(stagedFile)) {
continue;
}

console.log(
chalk.bgYellow('[INFO]'),
`Generated file found, unstaging: ${stagedFile}`
);

await run(`git restore --staged ${stagedFile}`);
}
}

if (require.main === module && !process.env.CI) {
preCommit();
}

module.exports = {
createMemoizedMicromatchMatcher,
};
4 changes: 4 additions & 0 deletions scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"cleanGeneratedBranch": "ts-node ci/codegen/cleanGeneratedBranch.ts",
"createMatrix": "ts-node ci/createMatrix.ts",
"createReleaseIssue": "ts-node release/create-release-issue.ts",
"pre-commit": "./ci/husky/pre-commit.js",
"processRelease": "ts-node release/process-release.ts",
"pushGeneratedCode": "ts-node ci/codegen/pushGeneratedCode.ts",
"setRunVariables": "ts-node ci/setRunVariables.ts",
Expand All @@ -19,9 +20,11 @@
"@types/inquirer": "8.2.1",
"@types/jest": "27.4.1",
"@types/js-yaml": "4.0.5",
"@types/micromatch": "4.0.2",
"@types/mustache": "4.1.2",
"@types/node": "16.11.26",
"@types/semver": "7.3.9",
"chalk": "4.1.2",
"commander": "9.1.0",
"dotenv": "16.0.0",
"eslint": "8.12.0",
Expand All @@ -31,6 +34,7 @@
"inquirer": "8.2.2",
"jest": "27.5.1",
"js-yaml": "4.1.0",
"micromatch": "4.0.5",
"mustache": "4.2.0",
"openapi-types": "10.0.0",
"ora-classic": "5.4.2",
Expand Down
17 changes: 17 additions & 0 deletions website/docs/commitAndPullRequest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Commit and Pull-request
---

# Commit and Pull-request

## Commit

If you accidentally include generated files in your commit, the `pre-commit` hook will automatically unstage them.

We create commits on the CI as well, and in that case, we skip this unstaging behavior with the environment variable `CI=true` given.

If you want to change the patterns of generated file paths, see [config/generation.config.js](https://github.com/algolia/api-clients-automation/blob/main/config/generation.config.js).

## Pull-request

Semantic title is required. It's validated by [GitHub Action](https://github.com/deepakputhraya/action-pr-title). See [pr-title.yml](https://github.com/algolia/api-clients-automation/blob/main/.github/workflows/pr-title.yml) for the complete regular expressions.
2 changes: 1 addition & 1 deletion website/docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ To contribute to the repository, make sure to take a look at our guidelines and
- [Setup the repository tooling](/docs/setupRepository): to install our tooling.
- [Add a new client](/docs/addNewClient): to add a new client spec to generate.
- [Support a new language](/docs/addNewLanguage): to add a new supported language to the API clients.
- [Pull-request](/docs/pullRequest): to see how to send pull-requests.
- [Commit and Pull-request](/docs/commitAndPullRequest): to see how to commit and send pull-requests.
- [Release process](/docs/releaseProcess): to see how to release API clients.

CLI commands can be found at [CLI > specs commands](/docs/specsCommands) and [CLI > generation commands](/docs/generationCommands)
Expand Down
7 changes: 0 additions & 7 deletions website/docs/pullRequest.md

This file was deleted.

2 changes: 1 addition & 1 deletion website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const sidebars = {
},
'addNewClient',
'addNewLanguage',
'pullRequest',
'commitAndPullRequest',
'releaseProcess',
],
},
Expand Down
49 changes: 39 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ __metadata:
eslint-plugin-prettier: 4.0.0
eslint-plugin-unused-imports: 2.0.0
eslint-plugin-yml: 0.14.0
husky: 7.0.4
json: 11.0.0
mustache: 4.2.0
prettier: 2.6.2
Expand Down Expand Up @@ -5528,6 +5529,13 @@ __metadata:
languageName: node
linkType: hard

"@types/braces@npm:*":
version: 3.0.1
resolution: "@types/braces@npm:3.0.1"
checksum: 3749f7673a03d498ddb2f199b222bb403e53e78ac05a599c757c2049703ece802602c78640af0ff826be0fd2ea8b03daff04ce18806ed739592302195b7a569b
languageName: node
linkType: hard

"@types/connect-history-api-fallback@npm:^1.3.5":
version: 1.3.5
resolution: "@types/connect-history-api-fallback@npm:1.3.5"
Expand Down Expand Up @@ -5746,6 +5754,15 @@ __metadata:
languageName: node
linkType: hard

"@types/micromatch@npm:4.0.2":
version: 4.0.2
resolution: "@types/micromatch@npm:4.0.2"
dependencies:
"@types/braces": "*"
checksum: 6c678e9c625d5b422c6d2c1001da1c502ecc4457248343bbd324b79fd798a6563e336a4d79630d80e8202312013dd7cf8b4440afa644d04477abd26fde6fba24
languageName: node
linkType: hard

"@types/mime@npm:^1":
version: 1.3.2
resolution: "@types/mime@npm:1.3.2"
Expand Down Expand Up @@ -12248,6 +12265,15 @@ __metadata:
languageName: node
linkType: hard

"husky@npm:7.0.4":
version: 7.0.4
resolution: "husky@npm:7.0.4"
bin:
husky: lib/bin.js
checksum: c6ec4af63da2c9522da8674a20ad9b48362cc92704896cc8a58c6a2a39d797feb2b806f93fbd83a6d653fbdceb2c3b6e0b602c6b2e8565206ffc2882ef7db9e9
languageName: node
linkType: hard

"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"
Expand Down Expand Up @@ -14731,6 +14757,16 @@ __metadata:
languageName: node
linkType: hard

"micromatch@npm:4.0.5, micromatch@npm:^4.0.5":
version: 4.0.5
resolution: "micromatch@npm:4.0.5"
dependencies:
braces: ^3.0.2
picomatch: ^2.3.1
checksum: 02a17b671c06e8fefeeb6ef996119c1e597c942e632a21ef589154f23898c9c6a9858526246abb14f8bca6e77734aa9dcf65476fca47cedfb80d9577d52843fc
languageName: node
linkType: hard

"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4":
version: 4.0.4
resolution: "micromatch@npm:4.0.4"
Expand All @@ -14741,16 +14777,6 @@ __metadata:
languageName: node
linkType: hard

"micromatch@npm:^4.0.5":
version: 4.0.5
resolution: "micromatch@npm:4.0.5"
dependencies:
braces: ^3.0.2
picomatch: ^2.3.1
checksum: 02a17b671c06e8fefeeb6ef996119c1e597c942e632a21ef589154f23898c9c6a9858526246abb14f8bca6e77734aa9dcf65476fca47cedfb80d9577d52843fc
languageName: node
linkType: hard

"miller-rabin@npm:^4.0.0":
version: 4.0.1
resolution: "miller-rabin@npm:4.0.1"
Expand Down Expand Up @@ -19032,9 +19058,11 @@ __metadata:
"@types/inquirer": 8.2.1
"@types/jest": 27.4.1
"@types/js-yaml": 4.0.5
"@types/micromatch": 4.0.2
"@types/mustache": 4.1.2
"@types/node": 16.11.26
"@types/semver": 7.3.9
chalk: 4.1.2
commander: 9.1.0
dotenv: 16.0.0
eslint: 8.12.0
Expand All @@ -19044,6 +19072,7 @@ __metadata:
inquirer: 8.2.2
jest: 27.5.1
js-yaml: 4.1.0
micromatch: 4.0.5
mustache: 4.2.0
openapi-types: 10.0.0
ora-classic: 5.4.2
Expand Down

0 comments on commit 9fbba0e

Please sign in to comment.