Skip to content

Commit 5824c1b

Browse files
fix(backend): match GitLab topics case-insensitively (#1393)
* Match GitLab topic filters case-insensitively Normalize GitLab project topics before include/exclude matching so mixed-case GitLab topics obey the lowercased filter patterns.\n\nConstraint: GitLab connection configs already normalize configured topic filters to lowercase.\nRejected: Preserve project-side case sensitivity | it makes mixed-case GitLab topics fail otherwise matching filters.\nConfidence: high\nScope-risk: narrow\nDirective: Keep GitLab topic include and exclude matching symmetric.\nTested: node .yarn/releases/yarn-4.7.0.cjs workspace @sourcebot/backend test src/gitlab.test.ts; node .yarn/releases/yarn-4.7.0.cjs workspace @sourcebot/backend build\nNot-tested: Live GitLab API sync against mixed-case project topics * Record GitLab topic matching fix Document the GitLab topic case-insensitive matching fix in the Unreleased changelog after the PR number was assigned.\n\nConstraint: Sourcebot requires every PR to include a changelog entry with the PR link.\nConfidence: high\nScope-risk: narrow\nDirective: Keep changelog entries as follow-up commits once PR numbers exist.\nTested: not run; changelog-only change\nNot-tested: Runtime behavior * Cover GitLab topic glob case folding Add a regression for mixed-case GitLab project topics matching lowercase include-topic glob filters.\n\nConstraint: CodeRabbit requested explicit glob coverage for the new case-insensitive topic matching behavior.\nConfidence: high\nScope-risk: narrow\nDirective: Keep topic filter behavior covered for exact and glob patterns.\nTested: node .yarn/releases/yarn-4.7.0.cjs workspace @sourcebot/backend test src/gitlab.test.ts; node .yarn/releases/yarn-4.7.0.cjs workspace @sourcebot/backend build\nNot-tested: Live GitLab API sync against mixed-case project topics * Update CHANGELOG.md --------- Co-authored-by: Brendan Kellam <brendan@sourcebot.dev>
1 parent 23a5c02 commit 5824c1b

3 files changed

Lines changed: 29 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111
- Upgraded `brace-expansion` to `^1.1.17`/`^2.1.3`/`^5.0.8`. [#1527](https://github.com/sourcebot-dev/sourcebot/pull/1527)
1212
- Upgraded `tar` to `^7.5.22`. [#1472](https://github.com/sourcebot-dev/sourcebot/pull/1472)
13+
- Fixed GitLab topic filters being incorrectly case-sensitive. [#1393](https://github.com/sourcebot-dev/sourcebot/pull/1393)
1314

1415
## [5.1.5] - 2026-07-31
1516

packages/backend/src/gitlab.test.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ test('shouldExcludeProject returns false when include.topics matches via glob pa
117117
})).toBe(false);
118118
});
119119

120+
test('shouldExcludeProject matches include.topics glob patterns case-insensitively.', () => {
121+
const project = {
122+
path_with_namespace: 'test/project',
123+
topics: ['Core-API'],
124+
} as unknown as ProjectSchema;
125+
126+
expect(shouldExcludeProject({
127+
project,
128+
include: { topics: ['core-*'] },
129+
})).toBe(false);
130+
});
131+
120132
test('shouldExcludeProject returns true when exclude.topics matches a project topic.', () => {
121133
const project = {
122134
path_with_namespace: 'test/project',
@@ -141,16 +153,26 @@ test('shouldExcludeProject returns false when exclude.topics does not match any
141153
})).toBe(false);
142154
});
143155

144-
test('shouldExcludeProject include.topics matching is case-sensitive on the project side.', () => {
156+
test('shouldExcludeProject matches include.topics case-insensitively.', () => {
145157
const project = {
146158
path_with_namespace: 'test/project',
147159
topics: ['Backend'],
148160
} as unknown as ProjectSchema;
149161

150-
// The function lowercases config topics but not project topics,
151-
// so 'Backend' does not match the lowercased pattern 'backend'.
152162
expect(shouldExcludeProject({
153163
project,
154164
include: { topics: ['backend'] },
165+
})).toBe(false);
166+
});
167+
168+
test('shouldExcludeProject matches exclude.topics case-insensitively.', () => {
169+
const project = {
170+
path_with_namespace: 'test/project',
171+
topics: ['Deprecated'],
172+
} as unknown as ProjectSchema;
173+
174+
expect(shouldExcludeProject({
175+
project,
176+
exclude: { topics: ['deprecated'] },
155177
})).toBe(true);
156178
});

packages/backend/src/gitlab.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export const shouldExcludeProject = ({
249249

250250
if (include?.topics) {
251251
const configTopics = include.topics.map(topic => topic.toLowerCase());
252-
const projectTopics = project.topics ?? [];
252+
const projectTopics = (project.topics ?? []).map(topic => topic.toLowerCase());
253253

254254
const matchingTopics = projectTopics.filter((topic) => micromatch.isMatch(topic, configTopics));
255255
if (matchingTopics.length === 0) {
@@ -260,7 +260,7 @@ export const shouldExcludeProject = ({
260260

261261
if (exclude?.topics) {
262262
const configTopics = exclude.topics.map(topic => topic.toLowerCase());
263-
const projectTopics = project.topics ?? [];
263+
const projectTopics = (project.topics ?? []).map(topic => topic.toLowerCase());
264264

265265
const matchingTopics = projectTopics.filter((topic) => micromatch.isMatch(topic, configTopics));
266266
if (matchingTopics.length > 0) {
@@ -333,4 +333,4 @@ export const getOAuthScopesForAuthenticatedUser = async (api: InstanceType<typeo
333333
logger.error('Failed to fetch OAuth scopes for authenticated user.', error);
334334
throw error;
335335
}
336-
}
336+
}

0 commit comments

Comments
 (0)