inject launcher client build env from settings
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -237,6 +237,26 @@ export class UpdateLauncherUserAppPermissionsDto {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class LauncherBuildEnvVariableDto {
|
||||
@IsString()
|
||||
key!: string;
|
||||
|
||||
@IsString()
|
||||
value!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isSecret?: boolean;
|
||||
}
|
||||
|
||||
export class UpdateLauncherBuildEnvDto {
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => LauncherBuildEnvVariableDto)
|
||||
variables!: LauncherBuildEnvVariableDto[];
|
||||
}
|
||||
|
||||
export class CreateLauncherCommonSettingDto {
|
||||
@IsString()
|
||||
key!: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common';
|
||||
|
||||
import { CreateLauncherAppLinkDto, CreateLauncherBuildJobDto, CreateLauncherCommonSettingDto, CreateLauncherVersionDto, UpdateLauncherSourceDto, LinkLauncherVersionSourceDto, MoveLauncherAppLinkDto, UpdateLauncherAppLinkDto, UpdateLauncherCommonSettingDto, UpdateLauncherUserAppPermissionsDto } from './dto';
|
||||
import { CreateLauncherAppLinkDto, CreateLauncherBuildJobDto, CreateLauncherCommonSettingDto, CreateLauncherVersionDto, UpdateLauncherSourceDto, LinkLauncherVersionSourceDto, MoveLauncherAppLinkDto, UpdateLauncherAppLinkDto, UpdateLauncherBuildEnvDto, UpdateLauncherCommonSettingDto, UpdateLauncherUserAppPermissionsDto } from './dto';
|
||||
import { LauncherService } from './launcher.service';
|
||||
|
||||
@Controller('launcher')
|
||||
@@ -27,6 +27,17 @@ export class LauncherController {
|
||||
return this.launcherService.updateSource(body);
|
||||
}
|
||||
|
||||
|
||||
@Get('build-env')
|
||||
listBuildEnv() {
|
||||
return this.launcherService.listBuildEnv();
|
||||
}
|
||||
|
||||
@Put('build-env')
|
||||
updateBuildEnv(@Body() body: UpdateLauncherBuildEnvDto) {
|
||||
return this.launcherService.updateBuildEnv(body);
|
||||
}
|
||||
|
||||
@Get('builds')
|
||||
listBuildJobs() {
|
||||
return this.launcherService.listBuildJobs();
|
||||
|
||||
@@ -18,11 +18,28 @@ import {
|
||||
UpdateLauncherSourceDto,
|
||||
LinkLauncherVersionSourceDto,
|
||||
UpdateLauncherAppLinkDto,
|
||||
UpdateLauncherBuildEnvDto,
|
||||
UpdateLauncherCommonSettingDto,
|
||||
UpdateLauncherUserAppPermissionsDto,
|
||||
} from './dto';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
const LAUNCHER_BUILD_ENV_PREFIX = 'launcher.client.build.env.';
|
||||
const DEFAULT_LAUNCHER_CLIENT_BUILD_ENV = [
|
||||
{
|
||||
key: 'VITE_ATNMY_STDO_API_BASE_URL',
|
||||
name: '런처 API 주소',
|
||||
value: 'https://portal-api.autonomystudio.net',
|
||||
description: '런처 클라이언트가 호출할 portal-api 주소',
|
||||
},
|
||||
{
|
||||
key: 'VITE_ATNMY_AUTH_API_BASE_URL',
|
||||
name: '공통 인증 API 주소',
|
||||
value: 'https://auth.autonomystudio.net',
|
||||
description: '회원가입/비밀번호 재설정에서 호출할 공통 인증 서버 주소',
|
||||
},
|
||||
] as const;
|
||||
|
||||
const LAUNCHER_SOURCE_SETTING_KEYS = {
|
||||
repository: 'launcher.client.source.repository',
|
||||
branch: 'launcher.client.source.branch',
|
||||
@@ -234,12 +251,42 @@ export class LauncherService {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private async getLauncherClientBuildEnv() {
|
||||
const rows = await this.launcherPrisma.launcherCommonSetting.findMany({
|
||||
where: {
|
||||
key: {
|
||||
startsWith: LAUNCHER_BUILD_ENV_PREFIX,
|
||||
},
|
||||
isActive: true,
|
||||
},
|
||||
orderBy: { key: 'asc' },
|
||||
});
|
||||
|
||||
if (!rows.length) {
|
||||
return DEFAULT_LAUNCHER_CLIENT_BUILD_ENV.map((variable) => ({
|
||||
key: variable.key,
|
||||
value: this.configService.get<string>(variable.key, variable.value),
|
||||
isSecret: false,
|
||||
}));
|
||||
}
|
||||
|
||||
return rows
|
||||
.map((row) => ({
|
||||
key: row.key.slice(LAUNCHER_BUILD_ENV_PREFIX.length),
|
||||
value: row.value,
|
||||
isSecret: row.isSecret,
|
||||
}))
|
||||
.filter((row) => row.key);
|
||||
}
|
||||
|
||||
private upsertCommonSettingByKey(
|
||||
key: string,
|
||||
name: string,
|
||||
type: string,
|
||||
value: string,
|
||||
description: string,
|
||||
isSecret = false,
|
||||
) {
|
||||
return this.launcherPrisma.launcherCommonSetting.upsert({
|
||||
where: { key },
|
||||
@@ -250,7 +297,7 @@ export class LauncherService {
|
||||
value,
|
||||
description,
|
||||
isActive: true,
|
||||
isSecret: false,
|
||||
isSecret,
|
||||
},
|
||||
update: {
|
||||
name,
|
||||
@@ -258,11 +305,45 @@ export class LauncherService {
|
||||
value,
|
||||
description,
|
||||
isActive: true,
|
||||
isSecret: false,
|
||||
isSecret,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async listBuildEnv() {
|
||||
const variables = await this.getLauncherClientBuildEnv();
|
||||
return { variables };
|
||||
}
|
||||
|
||||
async updateBuildEnv(body: UpdateLauncherBuildEnvDto) {
|
||||
const variables = normalizeBuildEnvVariables(body.variables ?? []);
|
||||
const nextKeys = new Set(variables.map((variable) => variable.key));
|
||||
const existing = await this.launcherPrisma.launcherCommonSetting.findMany({
|
||||
where: {
|
||||
key: {
|
||||
startsWith: LAUNCHER_BUILD_ENV_PREFIX,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await this.launcherPrisma.$transaction([
|
||||
...existing
|
||||
.filter((setting) => !nextKeys.has(setting.key.slice(LAUNCHER_BUILD_ENV_PREFIX.length)))
|
||||
.map((setting) => this.launcherPrisma.launcherCommonSetting.delete({ where: { id: setting.id } })),
|
||||
...variables.map((variable) => this.upsertCommonSettingByKey(
|
||||
`${LAUNCHER_BUILD_ENV_PREFIX}${variable.key}`,
|
||||
variable.key,
|
||||
'launcher-build-env',
|
||||
variable.value,
|
||||
`런처 클라이언트 빌드 환경변수: ${variable.key}`,
|
||||
variable.isSecret,
|
||||
)),
|
||||
]);
|
||||
|
||||
return this.listBuildEnv();
|
||||
}
|
||||
|
||||
listBuildJobs() {
|
||||
return this.prisma.launcherBuildJob.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
@@ -908,7 +989,11 @@ export class LauncherService {
|
||||
}
|
||||
|
||||
const buildCommand = process.env.LAUNCHER_BUILD_COMMAND || 'npm ci && npm run build:linux';
|
||||
await runCommand(buildCommand, workDir, append);
|
||||
const buildEnvVariables = await this.getLauncherClientBuildEnv();
|
||||
if (buildEnvVariables.length) {
|
||||
await append(`[build-env] ${buildEnvVariables.map((variable) => `${variable.key}=${variable.isSecret ? '********' : variable.value}`).join(' ')}`);
|
||||
}
|
||||
await runCommand(buildCommand, workDir, append, buildCommand, mergeBuildEnv(process.env, buildEnvVariables));
|
||||
|
||||
const { stdout } = await runCommand(`find ${quote(workDir)} -type f -name '*.deb' | head -n 1`, workDir, append);
|
||||
const debPath = stdout.trim().split('\n').filter(Boolean)[0];
|
||||
@@ -1228,11 +1313,12 @@ async function runCommand(
|
||||
cwd: string,
|
||||
append: (...lines: string[]) => Promise<void>,
|
||||
safeCommand = command,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
) {
|
||||
await append(`[run] ${safeCommand}`);
|
||||
const result = await execAsync(command, {
|
||||
cwd,
|
||||
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
||||
env: { ...env, GIT_TERMINAL_PROMPT: '0' },
|
||||
maxBuffer: 1024 * 1024 * 20,
|
||||
shell: '/bin/bash',
|
||||
});
|
||||
@@ -1243,6 +1329,38 @@ async function runCommand(
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function normalizeBuildEnvVariables(
|
||||
variables: Array<{ key?: string; value?: string; isSecret?: boolean }>,
|
||||
) {
|
||||
const byKey = new Map<string, { key: string; value: string; isSecret: boolean }>();
|
||||
for (const variable of variables) {
|
||||
const key = variable.key?.trim() ?? '';
|
||||
if (!key) continue;
|
||||
if (!/^VITE_[A-Za-z0-9_]+$/.test(key)) {
|
||||
throw new BadRequestException(`런처 클라이언트 빌드 환경변수는 VITE_로 시작해야 합니다: ${key}`);
|
||||
}
|
||||
byKey.set(key, {
|
||||
key,
|
||||
value: variable.value ?? '',
|
||||
isSecret: variable.isSecret ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
return [...byKey.values()].sort((left, right) => left.key.localeCompare(right.key));
|
||||
}
|
||||
|
||||
function mergeBuildEnv(
|
||||
env: NodeJS.ProcessEnv,
|
||||
variables: Array<{ key: string; value: string }>,
|
||||
) {
|
||||
const nextEnv: NodeJS.ProcessEnv = { ...env };
|
||||
for (const variable of variables) {
|
||||
nextEnv[variable.key] = variable.value;
|
||||
}
|
||||
return nextEnv;
|
||||
}
|
||||
|
||||
function trimOptional(value?: string) {
|
||||
return value === undefined ? null : value.trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user