Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 5 additions & 18 deletions config/generation.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,20 @@ module.exports = {
'specs/bundled/*.yml',
Copy link
Member

Choose a reason for hiding this comment

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

Let's closely look at the next PRs pushed code so we can make sure we did not broke anything here


'clients/**',
'clients/**/.*', // hidden files are not ignored by default
'!clients/README.md',
'!clients/**/.openapi-generator-ignore',

// Java
'!clients/algoliasearch-client-java-2/*.gradle',
'!clients/algoliasearch-client-java-2/gradlew',
'!clients/algoliasearch-client-java-2/.gitignore',
'!clients/algoliasearch-client-java-2/gradle/wrapper/**',
'!clients/algoliasearch-client-java-2/algoliasearch-core/build.gradle',
'!clients/algoliasearch-client-java-2/algoliasearch-core/gradle.properties',
'!clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/exceptions/**',
'!clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/utils/**',
'!clients/algoliasearch-client-java-2/**',
'clients/algoliasearch-client-java-2/gradle.properties',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/ApiClient.java',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/api/**',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/model/**',

'tests/output/java/build.gradle',
'tests/output/java/src/test/java/com/algolia/methods/**', // this could be added automatically by the script, but with overhead
'tests/output/java/src/test/java/com/algolia/client/**',

// JavaScript
'!clients/algoliasearch-client-javascript/*',
'!clients/algoliasearch-client-javascript/.*',
'!clients/algoliasearch-client-javascript/.github/**',
'!clients/algoliasearch-client-javascript/.yarn/**',
'!clients/algoliasearch-client-javascript/scripts/**',
Expand All @@ -34,12 +27,9 @@ module.exports = {
'!clients/algoliasearch-client-javascript/packages/client-common/**',

'tests/output/javascript/package.json',
'tests/output/javascript/src/methods/**',
'tests/output/javascript/src/client/**',

// PHP
'!clients/algoliasearch-client-php/*',
'!clients/algoliasearch-client-php/.*',
'!clients/algoliasearch-client-php/lib/*',
'!clients/algoliasearch-client-php/lib/Cache/**',
'!clients/algoliasearch-client-php/lib/Exceptions/**',
Expand All @@ -50,8 +40,5 @@ module.exports = {
'!clients/algoliasearch-client-php/lib/Support/**',
'!clients/algoliasearch-client-php/lib/Configuration/**',
'clients/algoliasearch-client-php/lib/Configuration/Configuration.php',

'tests/output/php/src/methods/**',
'tests/output/php/src/client/**',
],
};
71 changes: 55 additions & 16 deletions scripts/ci/husky/__tests__/pre-commit.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,63 @@
/* eslint-disable @typescript-eslint/no-var-requires */
Copy link
Member

Choose a reason for hiding this comment

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

It seems good for now! We can add more tests later :D

/* eslint-disable import/no-commonjs */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { createMemoizedMicromatchMatcher } = require('../pre-commit');
const micromatch = require('micromatch');

describe('createMemoizedMicromatchMatcher', () => {
const { getPatterns } = require('../pre-commit');

describe('micromatch', () => {
it('matches correctly', () => {
const matcher = createMemoizedMicromatchMatcher([
'clients/**',
'!clients/README.md',
]);
expect(
micromatch
.match(
[
'clients/algoliasearch-client-java-2/build.gradle',
'clients/algoliasearch-client-java-2/.gitignore',
'clients/algoliasearch-client-java-2/gradle.properties',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/api/SearchClient.java',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/model/search/Test.java',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/utils/AlgoliaAgent.java',

expect(matcher('clients/README.md')).toEqual(false);
expect(matcher('clients/CONTRIBUTING.md')).toEqual(true);
});
'clients/algoliasearch-client-javascript/.prettierrc',
'clients/algoliasearch-client-javascript/lerna.json',
'clients/algoliasearch-client-javascript/packages/client-common/whatever.test',
'clients/algoliasearch-client-javascript/packages/client-search/ignore.txt',

'clients/algoliasearch-client-php/.gitignore',
'clients/algoliasearch-client-php/lib/Api/SearchClient.php',
'clients/algoliasearch-client-php/lib/Cache/FileCacheDriver.php',

'tests/output/java/build.gradle',
'tests/output/java/settings.gradle',
'tests/output/java/src/test/java/com/algolia/EchoResponse.java',
'tests/output/java/src/test/java/com/algolia/client/test.java',

'tests/output/javascript/jest.config.ts',
'tests/output/javascript/package.json',
'tests/output/javascript/src/client/test.ts',

'tests/output/php/src/methods/requests/test.php',
],
getPatterns()
)
.sort()
).toEqual(
[
'clients/algoliasearch-client-java-2/gradle.properties',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/api/SearchClient.java',
'clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/model/search/Test.java',

'clients/algoliasearch-client-javascript/packages/client-search/ignore.txt',

'clients/algoliasearch-client-php/lib/Api/SearchClient.php',

'tests/output/java/build.gradle',
'tests/output/java/src/test/java/com/algolia/client/test.java',

it('prioritizes the exact match when two patterns conflict', () => {
const matcher = createMemoizedMicromatchMatcher([
'!lib/Configuration/*',
'lib/Configuration/Configuration.php',
]);
'tests/output/javascript/package.json',
'tests/output/javascript/src/client/test.ts',

expect(matcher('lib/Configuration/Configuration.php')).toEqual(true);
'tests/output/php/src/methods/requests/test.php',
].sort()
);
});
});
69 changes: 21 additions & 48 deletions scripts/ci/husky/pre-commit.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/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 clientConfig = require('../../../config/clients.config.json');
const GENERATED_FILE_PATTERNS =
require('../../../config/generation.config').patterns;

Expand All @@ -15,67 +15,40 @@ const run = async (command, { cwd } = {}) => {
);
};

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));
};
function getPatterns() {
const patterns = GENERATED_FILE_PATTERNS;
for (const [language, { tests }] of Object.entries(clientConfig)) {
patterns.push(`tests/output/${language}/${tests.outputFolder}/client/**`);
patterns.push(`tests/output/${language}/${tests.outputFolder}/methods/**`);
}
return patterns;
}

async function preCommit() {
const stagedFiles = (await run('git diff --name-only --cached')).split('\n');
const deletedFiles = new Set(
(await run('git diff --name-only --staged --diff-filter=D')).split('\n')
);
const matcher = createMemoizedMicromatchMatcher(GENERATED_FILE_PATTERNS);
// when merging, we want to stage all the files
if ((await run('git merge HEAD')) !== 'Already up to date.') {
return;
}

for (const stagedFile of stagedFiles) {
// keep the deleted files staged even if they were generated before.
if (deletedFiles.has(stagedFile)) {
continue;
}
const stagedFiles = (
await run('git diff --name-only --cached --diff-filter=d')
).split('\n');

if (!matcher(stagedFile)) {
continue;
}
const toUnstage = micromatch.match(stagedFiles, getPatterns());

for (const file of toUnstage) {
// eslint-disable-next-line no-console
console.log(
chalk.black.bgYellow('[INFO]'),
`Generated file found, unstaging: ${stagedFile}`
`Generated file found, unstaging: ${file}`
);

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

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

module.exports = {
createMemoizedMicromatchMatcher,
};
module.exports = { getPatterns };