Add exclude.readOnly and exclude.hidden to gerrit connection config (#280)

This commit is contained in:
Brendan Kellam 2025-04-29 12:05:19 -07:00 committed by GitHub
parent 15073644f9
commit 09894a5d7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 150 additions and 16 deletions

View file

@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- Added `exclude.readOnly` and `exclude.hidden` options to Gerrit connection config. [#280](https://github.com/sourcebot-dev/sourcebot/pull/280)
## [3.1.1] - 2025-04-28 ## [3.1.1] - 2025-04-28
### Changed ### Changed

View file

@ -51,7 +51,13 @@ To connect to a gerrit instance, provide the `url` property to your config:
"projects": [ "projects": [
"project1/foo-project", "project1/foo-project",
"project2/sub-project/some-sub-folder/**" "project2/sub-project/some-sub-folder/**"
] ],
// projects that have state READ_ONLY
"readOnly": true,
// projects that have state HIDDEN
"hidden": true
} }
} }
``` ```
@ -110,6 +116,16 @@ To connect to a gerrit instance, provide the `url` property to your config:
] ]
], ],
"description": "List of specific projects to exclude from syncing." "description": "List of specific projects to exclude from syncing."
},
"readOnly": {
"type": "boolean",
"default": false,
"description": "Exclude read-only projects from syncing."
},
"hidden": {
"type": "boolean",
"default": false,
"description": "Exclude hidden projects from syncing."
} }
}, },
"additionalProperties": false "additionalProperties": false

View file

@ -1,5 +1,5 @@
import fetch from 'cross-fetch'; import fetch from 'cross-fetch';
import { GerritConfig } from "@sourcebot/schemas/v2/index.type" import { GerritConnectionConfig } from "@sourcebot/schemas/v3/index.type"
import { createLogger } from './logger.js'; import { createLogger } from './logger.js';
import micromatch from "micromatch"; import micromatch from "micromatch";
import { measure, fetchWithRetry } from './utils.js'; import { measure, fetchWithRetry } from './utils.js';
@ -12,16 +12,19 @@ interface GerritProjects {
[projectName: string]: GerritProjectInfo; [projectName: string]: GerritProjectInfo;
} }
// https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#:~:text=date%20upon%20submit.-,state,-optional
type GerritProjectState = 'ACTIVE' | 'READ_ONLY' | 'HIDDEN';
interface GerritProjectInfo { interface GerritProjectInfo {
id: string; id: string;
state?: string; state?: GerritProjectState;
web_links?: GerritWebLink[]; web_links?: GerritWebLink[];
} }
interface GerritProject { interface GerritProject {
name: string; name: string;
id: string; id: string;
state?: string; state?: GerritProjectState;
web_links?: GerritWebLink[]; web_links?: GerritWebLink[];
} }
@ -32,7 +35,7 @@ interface GerritWebLink {
const logger = createLogger('Gerrit'); const logger = createLogger('Gerrit');
export const getGerritReposFromConfig = async (config: GerritConfig): Promise<GerritProject[]> => { export const getGerritReposFromConfig = async (config: GerritConnectionConfig): Promise<GerritProject[]> => {
const url = config.url.endsWith('/') ? config.url : `${config.url}/`; const url = config.url.endsWith('/') ? config.url : `${config.url}/`;
const hostname = new URL(config.url).hostname; const hostname = new URL(config.url).hostname;
@ -57,24 +60,24 @@ export const getGerritReposFromConfig = async (config: GerritConfig): Promise<Ge
throw e; throw e;
} }
// exclude "All-Projects" and "All-Users" projects
const excludedProjects = ['All-Projects', 'All-Users', 'All-Avatars', 'All-Archived-Projects'];
projects = projects.filter(project => !excludedProjects.includes(project.name));
// include repos by glob if specified in config // include repos by glob if specified in config
if (config.projects) { if (config.projects) {
projects = projects.filter((project) => { projects = projects.filter((project) => {
return micromatch.isMatch(project.name, config.projects!); return micromatch.isMatch(project.name, config.projects!);
}); });
} }
if (config.exclude && config.exclude.projects) {
projects = projects.filter((project) => {
return !micromatch.isMatch(project.name, config.exclude!.projects!);
});
}
logger.debug(`Fetched ${Object.keys(projects).length} projects in ${durationMs}ms.`); projects = projects
.filter((project) => {
const isExcluded = shouldExcludeProject({
project,
exclude: config.exclude,
});
return !isExcluded;
});
logger.debug(`Fetched ${projects.length} projects in ${durationMs}ms.`);
return projects; return projects;
}; };
@ -137,3 +140,51 @@ const fetchAllProjects = async (url: string): Promise<GerritProject[]> => {
return allProjects; return allProjects;
}; };
const shouldExcludeProject = ({
project,
exclude,
}: {
project: GerritProject,
exclude?: GerritConnectionConfig['exclude'],
}) => {
let reason = '';
const shouldExclude = (() => {
if ([
'All-Projects',
'All-Users',
'All-Avatars',
'All-Archived-Projects'
].includes(project.name)) {
reason = `Project is a special project.`;
return true;
}
if (!!exclude?.readOnly && project.state === 'READ_ONLY') {
reason = `\`exclude.readOnly\` is true`;
return true;
}
if (!!exclude?.hidden && project.state === 'HIDDEN') {
reason = `\`exclude.hidden\` is true`;
return true;
}
if (exclude?.projects) {
if (micromatch.isMatch(project.name, exclude.projects)) {
reason = `\`exclude.projects\` contains ${project.name}`;
return true;
}
}
return false;
})();
if (shouldExclude) {
logger.debug(`Excluding project ${project.name}. Reason: ${reason}`);
return true;
}
return false;
}

View file

@ -494,6 +494,16 @@ const schema = {
] ]
], ],
"description": "List of specific projects to exclude from syncing." "description": "List of specific projects to exclude from syncing."
},
"readOnly": {
"type": "boolean",
"default": false,
"description": "Exclude read-only projects from syncing."
},
"hidden": {
"type": "boolean",
"default": false,
"description": "Exclude hidden projects from syncing."
} }
}, },
"additionalProperties": false "additionalProperties": false

View file

@ -234,6 +234,14 @@ export interface GerritConnectionConfig {
* List of specific projects to exclude from syncing. * List of specific projects to exclude from syncing.
*/ */
projects?: string[]; projects?: string[];
/**
* Exclude read-only projects from syncing.
*/
readOnly?: boolean;
/**
* Exclude hidden projects from syncing.
*/
hidden?: boolean;
}; };
} }
export interface BitbucketConnectionConfig { export interface BitbucketConnectionConfig {

View file

@ -45,6 +45,16 @@ const schema = {
] ]
], ],
"description": "List of specific projects to exclude from syncing." "description": "List of specific projects to exclude from syncing."
},
"readOnly": {
"type": "boolean",
"default": false,
"description": "Exclude read-only projects from syncing."
},
"hidden": {
"type": "boolean",
"default": false,
"description": "Exclude hidden projects from syncing."
} }
}, },
"additionalProperties": false "additionalProperties": false

View file

@ -18,5 +18,13 @@ export interface GerritConnectionConfig {
* List of specific projects to exclude from syncing. * List of specific projects to exclude from syncing.
*/ */
projects?: string[]; projects?: string[];
/**
* Exclude read-only projects from syncing.
*/
readOnly?: boolean;
/**
* Exclude hidden projects from syncing.
*/
hidden?: boolean;
}; };
} }

View file

@ -623,6 +623,16 @@ const schema = {
] ]
], ],
"description": "List of specific projects to exclude from syncing." "description": "List of specific projects to exclude from syncing."
},
"readOnly": {
"type": "boolean",
"default": false,
"description": "Exclude read-only projects from syncing."
},
"hidden": {
"type": "boolean",
"default": false,
"description": "Exclude hidden projects from syncing."
} }
}, },
"additionalProperties": false "additionalProperties": false

View file

@ -329,6 +329,14 @@ export interface GerritConnectionConfig {
* List of specific projects to exclude from syncing. * List of specific projects to exclude from syncing.
*/ */
projects?: string[]; projects?: string[];
/**
* Exclude read-only projects from syncing.
*/
readOnly?: boolean;
/**
* Exclude hidden projects from syncing.
*/
hidden?: boolean;
}; };
} }
export interface BitbucketConnectionConfig { export interface BitbucketConnectionConfig {

View file

@ -44,6 +44,16 @@
] ]
], ],
"description": "List of specific projects to exclude from syncing." "description": "List of specific projects to exclude from syncing."
},
"readOnly": {
"type": "boolean",
"default": false,
"description": "Exclude read-only projects from syncing."
},
"hidden": {
"type": "boolean",
"default": false,
"description": "Exclude hidden projects from syncing."
} }
}, },
"additionalProperties": false "additionalProperties": false