paginate app operations and refine runtime settings layout
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -11,6 +11,8 @@ GITEA_APP_ORG=platform
|
|||||||
GITEA_ADMIN_TOKEN=
|
GITEA_ADMIN_TOKEN=
|
||||||
GITEA_RELEASE_PUSH_WHITELIST=
|
GITEA_RELEASE_PUSH_WHITELIST=
|
||||||
GITEA_RELEASE_MERGE_WHITELIST=
|
GITEA_RELEASE_MERGE_WHITELIST=
|
||||||
|
GITEA_RELEASE_PUSH_WHITELIST_TEAMS=
|
||||||
|
GITEA_RELEASE_MERGE_WHITELIST_TEAMS=
|
||||||
BUILD_REGISTRY=localhost:5000
|
BUILD_REGISTRY=localhost:5000
|
||||||
BUILD_ARTIFACT_INSTALL_COMMAND=npm ci
|
BUILD_ARTIFACT_INSTALL_COMMAND=npm ci
|
||||||
LAUNCHER_BUILD_COMMAND="npm ci && npm run build:linux"
|
LAUNCHER_BUILD_COMMAND="npm ci && npm run build:linux"
|
||||||
|
|||||||
+38
-16
@@ -640,6 +640,11 @@ export class AppsService {
|
|||||||
|
|
||||||
if (byRepository.size === 0) return;
|
if (byRepository.size === 0) return;
|
||||||
|
|
||||||
|
const gitBin = this.configService.get<string>('GIT_BIN', 'git');
|
||||||
|
const isUnrelatedHistoryError = (message: string) => {
|
||||||
|
const normalized = message.toLowerCase();
|
||||||
|
return normalized.includes('refusing to merge unrelated histories') || normalized.includes('not possible to fast-forward');
|
||||||
|
};
|
||||||
const workRoot = path.join(os.tmpdir(), 'studio-admin-release-promotions');
|
const workRoot = path.join(os.tmpdir(), 'studio-admin-release-promotions');
|
||||||
await fs.mkdir(workRoot, { recursive: true });
|
await fs.mkdir(workRoot, { recursive: true });
|
||||||
|
|
||||||
@@ -647,38 +652,59 @@ export class AppsService {
|
|||||||
const workDir = path.join(workRoot, `${sanitizeImageName(repository)}-${Date.now()}`);
|
const workDir = path.join(workRoot, `${sanitizeImageName(repository)}-${Date.now()}`);
|
||||||
try {
|
try {
|
||||||
await fs.rm(workDir, { recursive: true, force: true });
|
await fs.rm(workDir, { recursive: true, force: true });
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
await execFileAsync(gitBin, [
|
||||||
'clone',
|
'clone',
|
||||||
'--no-checkout',
|
'--no-checkout',
|
||||||
await this.getAuthenticatedHttpCloneUrl(repository),
|
await this.getAuthenticatedHttpCloneUrl(repository),
|
||||||
workDir,
|
workDir,
|
||||||
]);
|
]);
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
await execFileAsync(gitBin, [
|
||||||
'fetch',
|
'fetch',
|
||||||
'origin',
|
'origin',
|
||||||
sourceBranch,
|
sourceBranch,
|
||||||
], { cwd: workDir });
|
], { cwd: workDir });
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
await execFileAsync(gitBin, [
|
||||||
'fetch',
|
'fetch',
|
||||||
'origin',
|
'origin',
|
||||||
targetBranch,
|
targetBranch,
|
||||||
], { cwd: workDir }).catch(() => undefined);
|
], { cwd: workDir }).catch(() => undefined);
|
||||||
|
|
||||||
const targetExists = await gitRefExists(workDir, `refs/remotes/origin/${targetBranch}`, this.configService.get<string>('GIT_BIN', 'git'));
|
const pushArgs: string[] = ['push', 'origin', `HEAD:refs/heads/${targetBranch}`];
|
||||||
|
|
||||||
|
const targetExists = await gitRefExists(workDir, `refs/remotes/origin/${targetBranch}`, gitBin);
|
||||||
if (targetExists) {
|
if (targetExists) {
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
await execFileAsync(gitBin, [
|
||||||
'checkout',
|
'checkout',
|
||||||
'-B',
|
'-B',
|
||||||
targetBranch,
|
targetBranch,
|
||||||
`origin/${targetBranch}`,
|
`origin/${targetBranch}`,
|
||||||
], { cwd: workDir });
|
], { cwd: workDir });
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
let forcePush = false;
|
||||||
'merge',
|
try {
|
||||||
'--ff-only',
|
await execFileAsync(gitBin, [
|
||||||
commitSha,
|
'merge',
|
||||||
], { cwd: workDir });
|
'--ff-only',
|
||||||
|
commitSha,
|
||||||
|
], { cwd: workDir });
|
||||||
|
} catch (error) {
|
||||||
|
const message = String(error instanceof Error ? error.message : error);
|
||||||
|
if (!isUnrelatedHistoryError(message)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
forcePush = true;
|
||||||
|
await execFileAsync(gitBin, [
|
||||||
|
'checkout',
|
||||||
|
'-B',
|
||||||
|
targetBranch,
|
||||||
|
commitSha,
|
||||||
|
], { cwd: workDir });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forcePush) {
|
||||||
|
pushArgs.splice(0, pushArgs.length, 'push', '--force-with-lease', 'origin', `HEAD:refs/heads/${targetBranch}`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
await execFileAsync(gitBin, [
|
||||||
'checkout',
|
'checkout',
|
||||||
'-B',
|
'-B',
|
||||||
targetBranch,
|
targetBranch,
|
||||||
@@ -686,11 +712,7 @@ export class AppsService {
|
|||||||
], { cwd: workDir });
|
], { cwd: workDir });
|
||||||
}
|
}
|
||||||
|
|
||||||
await execFileAsync(this.configService.get<string>('GIT_BIN', 'git'), [
|
await execFileAsync(gitBin, pushArgs, { cwd: workDir });
|
||||||
'push',
|
|
||||||
'origin',
|
|
||||||
`HEAD:refs/heads/${targetBranch}`,
|
|
||||||
], { cwd: workDir });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : 'Unknown git promotion failure';
|
const message = error instanceof Error ? error.message : 'Unknown git promotion failure';
|
||||||
throw new BadRequestException(`${repository} release 브랜치 승격 실패: ${message}`);
|
throw new BadRequestException(`${repository} release 브랜치 승격 실패: ${message}`);
|
||||||
|
|||||||
+19
-20
@@ -36,6 +36,8 @@ export class GiteaService {
|
|||||||
private readonly token?: string;
|
private readonly token?: string;
|
||||||
private readonly releaseBranchPushUsers: string[];
|
private readonly releaseBranchPushUsers: string[];
|
||||||
private readonly releaseBranchMergeUsers: string[];
|
private readonly releaseBranchMergeUsers: string[];
|
||||||
|
private readonly releaseBranchPushTeams: string[];
|
||||||
|
private readonly releaseBranchMergeTeams: string[];
|
||||||
|
|
||||||
constructor(configService: ConfigService) {
|
constructor(configService: ConfigService) {
|
||||||
this.baseUrl = configService
|
this.baseUrl = configService
|
||||||
@@ -49,6 +51,12 @@ export class GiteaService {
|
|||||||
this.releaseBranchMergeUsers = parseWhiteList(
|
this.releaseBranchMergeUsers = parseWhiteList(
|
||||||
configService.get<string>('GITEA_RELEASE_MERGE_WHITELIST'),
|
configService.get<string>('GITEA_RELEASE_MERGE_WHITELIST'),
|
||||||
);
|
);
|
||||||
|
this.releaseBranchPushTeams = parseWhiteList(
|
||||||
|
configService.get<string>('GITEA_RELEASE_PUSH_WHITELIST_TEAMS'),
|
||||||
|
);
|
||||||
|
this.releaseBranchMergeTeams = parseWhiteList(
|
||||||
|
configService.get<string>('GITEA_RELEASE_MERGE_WHITELIST_TEAMS'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getVersion() {
|
async getVersion() {
|
||||||
@@ -258,23 +266,22 @@ export class GiteaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async protectReleaseBranch(owner: string, repo: string) {
|
private async protectReleaseBranch(owner: string, repo: string) {
|
||||||
const me = await this.getAuthenticatedUser();
|
const pushUsernames = uniqueUsers(this.releaseBranchPushUsers);
|
||||||
const pushUsernames = uniqueUsers([
|
const mergeUsernames = uniqueUsers(this.releaseBranchMergeUsers);
|
||||||
...this.releaseBranchPushUsers,
|
const pushTeams = uniqueUsers(this.releaseBranchPushTeams);
|
||||||
...(me?.login ? [me.login] : []),
|
const mergeTeams = uniqueUsers(this.releaseBranchMergeTeams);
|
||||||
]);
|
const hasPushWhitelist = pushUsernames.length > 0 || pushTeams.length > 0;
|
||||||
const mergeUsernames = uniqueUsers([
|
const hasMergeWhitelist = mergeUsernames.length > 0 || mergeTeams.length > 0;
|
||||||
...this.releaseBranchMergeUsers,
|
|
||||||
...(me?.login ? [me.login] : []),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
branch_name: 'release',
|
branch_name: 'release',
|
||||||
enable_push: true,
|
enable_push: true,
|
||||||
enable_push_whitelist: true,
|
enable_push_whitelist: hasPushWhitelist,
|
||||||
push_whitelist_usernames: pushUsernames,
|
push_whitelist_usernames: pushUsernames,
|
||||||
enable_merge_whitelist: true,
|
push_whitelist_teams: pushTeams,
|
||||||
merge_whitelist_usernames: mergeUsernames,
|
enable_merge_whitelist: hasMergeWhitelist,
|
||||||
|
merge_whitelist_usernames: hasMergeWhitelist ? mergeUsernames : [],
|
||||||
|
merge_whitelist_teams: hasMergeWhitelist ? mergeTeams : [],
|
||||||
enable_force_push: false,
|
enable_force_push: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -285,14 +292,6 @@ export class GiteaService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getAuthenticatedUser() {
|
|
||||||
try {
|
|
||||||
return await this.request<{ login: string }>('/api/v1/user');
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async request<T>(
|
private async request<T>(
|
||||||
path: string,
|
path: string,
|
||||||
init?: RequestInit & { allowedStatuses?: number[] },
|
init?: RequestInit & { allowedStatuses?: number[] },
|
||||||
|
|||||||
Reference in New Issue
Block a user