Skip to content

chore(ci): prevent commit including generated files #379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3e75735
chore(ci): add pre-commit hook to unstage generated codes
eunjae-lee Apr 12, 2022
227edcc
chore: test commit
eunjae-lee Apr 12, 2022
9167fc9
chore: implement pre-commit script
eunjae-lee Apr 14, 2022
b9d1057
chore: fix lint error
eunjae-lee Apr 14, 2022
ff0b1b8
Merge branch 'main' into chore/githook
eunjae-lee Apr 14, 2022
054c031
chore: fix lint error
eunjae-lee Apr 14, 2022
3fe4723
chore: call js file directly
eunjae-lee Apr 15, 2022
40cbd5e
chore: update .cache_version
eunjae-lee Apr 15, 2022
c5d7781
Merge branch 'main' into chore/githook
eunjae-lee Apr 15, 2022
fbfba2b
Merge branch 'main' into chore/githook
eunjae-lee Apr 19, 2022
c8c3c6e
Update scripts/ci/husky/pre-commit.js
eunjae-lee Apr 19, 2022
0b07721
Update scripts/ci/husky/pre-commit.js
eunjae-lee Apr 19, 2022
4dc5a01
chore: use postinstall instead of prepare
eunjae-lee Apr 19, 2022
202169c
chore: better error message
eunjae-lee Apr 19, 2022
6d34e74
chore: fix patterns
eunjae-lee Apr 19, 2022
62c75fe
chore: unstage files automatically and exit with 0
eunjae-lee Apr 19, 2022
541a167
Merge branch 'main' into chore/githook
eunjae-lee Apr 19, 2022
a3d66a6
Merge branch 'main' into chore/githook
eunjae-lee Apr 20, 2022
fc73866
chore: change loop
eunjae-lee Apr 21, 2022
958ff64
Merge branch 'main' into chore/githook
eunjae-lee Apr 21, 2022
d5fa011
chore: skip deleted files
eunjae-lee Apr 21, 2022
958c1fa
Merge branch 'chore/githook' of github.com:algolia/api-clients-automa…
eunjae-lee Apr 21, 2022
613fca9
Merge branch 'main' into chore/githook
eunjae-lee Apr 22, 2022
73528a0
chore: split config to a separate file
eunjae-lee Apr 22, 2022
b7e752e
chore: update docs
eunjae-lee Apr 22, 2022
bb700fd
Merge branch 'main' into chore/githook
eunjae-lee Apr 22, 2022
32dd38e
chore: update cache version
eunjae-lee Apr 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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 */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test can still be in TS 😇

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(joking)

// 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);
});
});
96 changes: 96 additions & 0 deletions scripts/ci/husky/pre-commit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env node
/* eslint-disable no-process-exit */
/* eslint-disable no-console */
/* eslint-disable import/no-commonjs */
/* eslint-disable @typescript-eslint/no-var-requires */
const execa = require('execa');
const micromatch = require('micromatch');

const GENERATED_FILE_PATTERNS = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list is meant to be updated whenever we create a file by hand, maybe it would be nice to have it easily accessible in the config folder, wdyt ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^!!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and maybe documented somewhere on how to update this list)

Copy link
Contributor Author

@eunjae-lee eunjae-lee Apr 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created config/generation.config.js. What do you think?

I created a json first, but that way we cannot add any comment. This config particularly will require us to write some contexts as comment. So I made it a js file. Let me know what you think. 73528a0

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's perfect like that !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@millotp great! and I applied the changes to the patterns in that js file.

// 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/com/algolia/exceptions/*',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update those path pls ? They now use algoliasearch-core/src/com/algolia...

'!clients/algoliasearch-client-java-2/algoliasearch-core/com/algolia/utils/*',
'clients/algoliasearch-client-java-2/algoliasearch-core/com/algolia/utils/echo/EchoResponse.java',
Copy link
Collaborator

@millotp millotp Apr 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The echo response is a bit more complicated now, its:

Suggested change
'clients/algoliasearch-client-java-2/algoliasearch-core/com/algolia/utils/echo/EchoResponse.java',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/com/algolia/utils/echo/EchoResponse*.java',
'!clients/algoliasearch-client-java-2/algoliasearch-core/src/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',
];

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('!')) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this simplification of splitting negative and positive will work for every case, for example if we have a sequence of [positive, negative, positive, negative], it will loose the pattern, but I don't have a better solution 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm not too sure either. But I couldn't think of any edge case so far. Once we face it in the future, it will reveal itself and still won't ruin anything. Then we can adjust the logic to cover the case.

// 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 matcher = createMemoizedMicromatchMatcher(GENERATED_FILE_PATTERNS);
const generatedFiles = stagedFiles.filter((file) => matcher(file));

if (generatedFiles.length > 0) {
console.log(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really nice output 😃

'\x1b[41m%s\x1b[0m',
'[ERROR]',
`You cannot include generated files in the commit. Please unstage the following:\n\n${generatedFiles
.map((file) => ` - ${file}`)
.join('\n')}`
);
process.exit(1);
}
}

if (require.main === module) {
preCommit();
}

module.exports = {
createMemoizedMicromatchMatcher,
};
3 changes: 3 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,6 +20,7 @@
"@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",
Expand All @@ -31,6 +33,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
48 changes: 38 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,6 +19058,7 @@ __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
Expand All @@ -19044,6 +19071,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