Skip to content

Commit a407792

Browse files
authored
Exclude repos by glob (#70)
1 parent 276086d commit a407792

File tree

4 files changed

+78
-17
lines changed

4 files changed

+78
-17
lines changed

configs/filter.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
"forks": true,
1616
"repos": [
1717
"my-org/repo1",
18-
"my-org/repo2"
18+
"my-org/repo2",
19+
"my-org/sub-org-1/**",
20+
"my-org/sub-org-*/**"
1921
]
2022
}
2123
},
@@ -34,7 +36,9 @@
3436
"forks": true,
3537
"projects": [
3638
"my-group/project1",
37-
"my-group/project2"
39+
"my-group/project2",
40+
"my-org/sub-org-1/**",
41+
"my-org/sub-org-*/**"
3842
]
3943
}
4044
},
@@ -53,7 +57,9 @@
5357
"forks": true,
5458
"repos": [
5559
"my-org/repo1",
56-
"my-org/repo2"
60+
"my-org/repo2",
61+
"my-org/sub-org-1/**",
62+
"my-org/sub-org-*/**"
5763
]
5864
}
5965
},

packages/backend/src/utils.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,62 @@
11
import { expect, test } from 'vitest';
2-
import { arraysEqualShallow, isRemotePath } from './utils';
2+
import { arraysEqualShallow, isRemotePath, excludeReposByName } from './utils';
3+
import { Repository } from './types';
4+
5+
const testNames: string[] = [
6+
"abcdefg/zfmno/ioiwerj/fawdf",
7+
"abcdefg/zfmno/ioiwerj/werw",
8+
"abcdefg/zfmno/ioiwerj/terne",
9+
"abcdefg/zfmno/ioiwerj/asdf45e4r",
10+
"abcdefg/zfmno/ioiwerj/ddee",
11+
"abcdefg/zfmno/ioiwerj/ccdfeee",
12+
"abcdefg/zfmno/sadfaw",
13+
"abcdefg/zfmno/ioiwerj/wwe",
14+
"abcdefg/ieieiowowieu8383/ieckup-e",
15+
"abcdefg/ieieiowowieu8383/fvas-eer-wwwer3"
16+
];
17+
18+
const createRepository = (name: string) => (<Repository>{
19+
vcs: 'git',
20+
id: name,
21+
name: name,
22+
path: name,
23+
isStale: false,
24+
cloneUrl: name,
25+
branches: [name],
26+
tags: [name]
27+
});
28+
29+
test('should filter repos by micromatch pattern', () => {
30+
// bad glob patterns
31+
const unfilteredRepos = excludeReposByName(testNames.map(n => (createRepository(n))), ['/zfmno/']);
32+
expect(unfilteredRepos.length).toBe(10);
33+
expect(unfilteredRepos.map(r => r.name)).toEqual(testNames);
34+
const unfilteredRepos1 = excludeReposByName(testNames.map(n => (createRepository(n))), ['**zfmno**']);
35+
expect(unfilteredRepos1.length).toBe(10);
36+
expect(unfilteredRepos1.map(r => r.name)).toEqual(testNames);
37+
38+
// good glob patterns
39+
const filteredRepos = excludeReposByName(testNames.map(n => (createRepository(n))), ['**/zfmno/**']);
40+
expect(filteredRepos.length).toBe(2);
41+
expect(filteredRepos.map(r => r.name)).toEqual(["abcdefg/ieieiowowieu8383/ieckup-e", "abcdefg/ieieiowowieu8383/fvas-eer-wwwer3"]);
42+
const filteredRepos1 = excludeReposByName(testNames.map(n => (createRepository(n))), ['**/*fmn*/**']);
43+
expect(filteredRepos1.length).toBe(2);
44+
expect(filteredRepos1.map(r => r.name)).toEqual(["abcdefg/ieieiowowieu8383/ieckup-e", "abcdefg/ieieiowowieu8383/fvas-eer-wwwer3"]);
45+
});
46+
47+
test('should filter repos by name exact match', () => {
48+
const filteredRepos = excludeReposByName(testNames.map(n => (createRepository(n))), testNames.slice(1, 9));
49+
expect(filteredRepos.length).toBe(2);
50+
expect(filteredRepos.map(r => r.name)).toEqual([testNames[0], testNames[9]]);
51+
52+
const filteredRepos1 = excludeReposByName(testNames.map(n => (createRepository(n))), testNames.slice(3, 5));
53+
expect(filteredRepos1.length).toBe(8);
54+
expect(filteredRepos1.map(r => r.name)).toEqual([testNames[0], testNames[1], testNames[2], testNames[5], testNames[6], testNames[7], testNames[8], testNames[9]]);
55+
56+
const filteredRepos2 = excludeReposByName(testNames.map(n => (createRepository(n))), [testNames[0], testNames[7], testNames[9]]);
57+
expect(filteredRepos2.length).toBe(7);
58+
expect(filteredRepos2.map(r => r.name)).toEqual([...testNames.slice(1, 7), testNames[8]]);
59+
});
360

461
test('should return true for identical arrays', () => {
562
expect(arraysEqualShallow([1, 2, 3], [1, 2, 3])).toBe(true);

packages/backend/src/utils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Logger } from "winston";
22
import { AppContext, Repository } from "./types.js";
33
import path from 'path';
4+
import micromatch from "micromatch";
45

56
export const measure = async <T>(cb : () => Promise<T>) => {
67
const start = Date.now();
@@ -36,10 +37,10 @@ export const excludeArchivedRepos = <T extends Repository>(repos: T[], logger?:
3637
});
3738
}
3839

40+
3941
export const excludeReposByName = <T extends Repository>(repos: T[], excludedRepoNames: string[], logger?: Logger) => {
40-
const excludedRepos = new Set(excludedRepoNames);
4142
return repos.filter((repo) => {
42-
if (excludedRepos.has(repo.name)) {
43+
if (micromatch.isMatch(repo.name, excludedRepoNames)) {
4344
logger?.debug(`Excluding repo ${repo.id}. Reason: exclude.repos contains ${repo.name}`);
4445
return false;
4546
}
@@ -90,4 +91,4 @@ export const arraysEqualShallow = <T>(a?: readonly T[], b?: readonly T[]) => {
9091
}
9192

9293
return true;
93-
}
94+
}

schemas/v2/index.json

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,10 @@
146146
"repos": {
147147
"type": "array",
148148
"items": {
149-
"type": "string",
150-
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
149+
"type": "string"
151150
},
152151
"default": [],
153-
"description": "List of individual repositories to exclude from syncing. Expected to be formatted as '{orgName}/{repoName}' or '{userName}/{repoName}'."
152+
"description": "List of individual repositories to exclude from syncing. Glob patterns are supported."
154153
}
155154
},
156155
"additionalProperties": false
@@ -238,16 +237,15 @@
238237
"projects": {
239238
"type": "array",
240239
"items": {
241-
"type": "string",
242-
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
240+
"type": "string"
243241
},
244242
"default": [],
245243
"examples": [
246244
[
247245
"my-group/my-project"
248246
]
249247
],
250-
"description": "List of individual projects to exclude from syncing. The project's namespace must be specified. See: https://docs.gitlab.com/ee/user/namespace/"
248+
"description": "List of projects to exclude from syncing. Glob patterns are supported. The project's namespace must be specified, see: https://docs.gitlab.com/ee/user/namespace/"
251249
}
252250
},
253251
"additionalProperties": false
@@ -336,11 +334,10 @@
336334
"repos": {
337335
"type": "array",
338336
"items": {
339-
"type": "string",
340-
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
337+
"type": "string"
341338
},
342339
"default": [],
343-
"description": "List of individual repositories to exclude from syncing. Expected to be formatted as '{orgName}/{repoName}' or '{userName}/{repoName}'."
340+
"description": "List of individual repositories to exclude from syncing. Glob patterns are supported."
344341
}
345342
},
346343
"additionalProperties": false
@@ -432,4 +429,4 @@
432429
}
433430
},
434431
"additionalProperties": false
435-
}
432+
}

0 commit comments

Comments
 (0)