From 3d15bdf6bbfc2ab34df4c0503529a07f785ab68b Mon Sep 17 00:00:00 2001 From: chyoo Date: Thu, 2 Jul 2026 13:51:21 +0900 Subject: [PATCH] use app version in R2 artifact path --- src/apps/build-runner.service.ts | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/apps/build-runner.service.ts b/src/apps/build-runner.service.ts index 56a39e5..f1d0b9d 100644 --- a/src/apps/build-runner.service.ts +++ b/src/apps/build-runner.service.ts @@ -385,6 +385,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy { if (!artifact) { throw new Error(`산출물을 찾을 수 없습니다: ${outputDir}`); } + const artifactVersion = await readPackageVersion(workDir); await fs.mkdir(artifactDir, { recursive: true }); const destinationName = job.artifact || path.basename(artifact); @@ -395,6 +396,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy { job, destination, destinationName, + artifactVersion, append, ); const publicBaseUrl = this.configService.get('BUILD_ARTIFACT_BASE_URL'); @@ -417,6 +419,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy { job: BuildJobWithComponent, artifactPath: string, artifactName: string, + artifactVersion: string | null, append: (line: string) => Promise, ) { const bucket = this.configService.get('R2_BUCKET'); @@ -428,7 +431,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy { return null; } - const key = this.createArtifactObjectKey(job, artifactName); + const key = this.createArtifactObjectKey(job, artifactName, artifactVersion); const stat = await fs.stat(artifactPath); const sha256 = await sha256File(artifactPath); const client = this.getR2Client(accessKeyId, secretAccessKey); @@ -447,6 +450,7 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy { component: safeMetadataValue(job.component.name), branch: safeMetadataValue(job.branch), commit: safeMetadataValue(job.commitSha ?? ''), + version: safeMetadataValue(artifactVersion ?? ''), }, })); @@ -483,16 +487,21 @@ export class BuildRunnerService implements OnModuleInit, OnModuleDestroy { return this.r2Client; } - private createArtifactObjectKey(job: BuildJobWithComponent, artifactName: string) { + private createArtifactObjectKey( + job: BuildJobWithComponent, + artifactName: string, + artifactVersion: string | null, + ) { const prefix = this.configService.get('R2_ARTIFACT_PREFIX', 'studio-artifacts'); - const revision = job.commitSha?.trim().slice(0, 7) || job.id; + const versionPath = artifactVersion + ? [artifactVersion] + : [job.branch, 'latest']; return [ prefix, 'apps', job.component.app.key, job.component.name, - job.branch, - revision, + ...versionPath, artifactName, ].map(sanitizeObjectKeyPart).join('/'); } @@ -679,6 +688,18 @@ async function findNewestArtifact(dir: string) { return candidates[0]?.path ?? null; } +async function readPackageVersion(workDir: string) { + try { + const packageJson = await fs.readFile(path.join(workDir, 'package.json'), 'utf8'); + const parsed = JSON.parse(packageJson) as { version?: unknown }; + return typeof parsed.version === 'string' && parsed.version.trim() + ? parsed.version.trim() + : null; + } catch { + return null; + } +} + async function findArtifactCandidates(dir: string): Promise> { const entries = await fs.readdir(dir, { withFileTypes: true }); const results: Array<{ path: string; mtimeMs: number }> = [];