From 1639d2d8de0a3c3d150946d4e87ba889a336ec39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E9=AD=8F=E6=B4=AA?= Date: Thu, 21 May 2026 15:03:21 +0800 Subject: [PATCH] feat: resolve ${env('...')} variables in cloud build yaml before parsing Co-Authored-By: Claude Opus 4.7 --- __tests__/ut/commands/build_test.ts | 41 ++++++++++++++++++++++++++++- src/subCommands/build/index.ts | 14 +++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/__tests__/ut/commands/build_test.ts b/__tests__/ut/commands/build_test.ts index 9ce1b780..6d862ef9 100644 --- a/__tests__/ut/commands/build_test.ts +++ b/__tests__/ut/commands/build_test.ts @@ -1,4 +1,4 @@ -import BuilderFactory, { BuildType } from '../../../src/subCommands/build'; +import BuilderFactory, { BuildType, resolveEnvVariables } from '../../../src/subCommands/build'; import { ImageBuiltKitBuilder } from '../../../src/subCommands/build/impl/imageBuiltKitBuilder'; import { ImageDockerBuilder } from '../../../src/subCommands/build/impl/imageDockerBuilder'; import { ImageKanikoBuilder } from '../../../src/subCommands/build/impl/imageKanikoBuilder'; @@ -302,4 +302,43 @@ describe('BuilderFactory', () => { expect(builder2).toBe(mockBuilder2); }); }); + + describe('resolveEnvVariables', () => { + it('should replace env variables with single quotes', () => { + process.env.MY_VAR = 'hello'; + const result = resolveEnvVariables("value: ${env('MY_VAR')}"); + expect(result).toBe('value: hello'); + delete process.env.MY_VAR; + }); + + it('should replace env variables with double quotes', () => { + process.env.MY_VAR = 'world'; + const result = resolveEnvVariables('value: ${env("MY_VAR")}'); + expect(result).toBe('value: world'); + delete process.env.MY_VAR; + }); + + it('should replace multiple env variables', () => { + process.env.USERNAME = 'admin'; + process.env.PASSWORD = 'secret'; + const result = resolveEnvVariables( + "username: ${env('USERNAME')}\npassword: ${env('PASSWORD')}", + ); + expect(result).toBe('username: admin\npassword: secret'); + delete process.env.USERNAME; + delete process.env.PASSWORD; + }); + + it('should throw error when env variable is not found', () => { + delete process.env.NOT_EXIST; + expect(() => resolveEnvVariables("value: ${env('NOT_EXIST')}")).toThrow( + 'Environment variable not found: NOT_EXIST', + ); + }); + + it('should not modify content without env variables', () => { + const content = 'region: cn-hangzhou\nruntime: nodejs18'; + expect(resolveEnvVariables(content)).toBe(content); + }); + }); }); diff --git a/src/subCommands/build/index.ts b/src/subCommands/build/index.ts index 35913cb5..a27856fd 100644 --- a/src/subCommands/build/index.ts +++ b/src/subCommands/build/index.ts @@ -22,6 +22,17 @@ export enum BuildType { Default = 'DEFAULT', } +export function resolveEnvVariables(content: string): string { + return content.replace(/\$\{env\(['"](.+?)['"]\)\}/g, (match, envName: string) => { + const value = process.env[envName]; + if (value === undefined) { + logger.error(`Environment variable not found: ${envName}`); + throw new Error(`Environment variable not found: ${envName}`); + } + return value; + }); +} + export default class BuilderFactory { private inputs: IInputs; private debugInstance: boolean; @@ -73,7 +84,8 @@ export default class BuilderFactory { try { const buildYamlContent = fs.readFileSync(buildYamlPath, 'utf8'); - const buildConfig = yaml.load(buildYamlContent) as any; + const resolvedContent = resolveEnvVariables(buildYamlContent); + const buildConfig = yaml.load(resolvedContent) as any; // 从build.yaml中提取配置,支持多种格式 let config = buildConfig;