Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/commands/actors/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ export class ActorsPushCommand extends ApifyCommand<typeof ActorsPushCommand> {
description: 'Directory where the Actor is located',
required: false,
}),
'ignore-missing-secrets': Flags.boolean({
description: 'Ignore missing secrets and show warnings instead of failing. Environment variables referencing missing secrets will be omitted.',
default: false,
required: false,
}),
};

static override args = {
Expand Down Expand Up @@ -280,7 +285,11 @@ Skipping push. Use --force to override.`,
// Update Actor version
const actorCurrentVersion = await actorClient.version(version).get();
const envVars = actorConfig!.environmentVariables
? transformEnvToEnvVars(actorConfig!.environmentVariables as Record<string, string>)
? transformEnvToEnvVars(
actorConfig!.environmentVariables as Record<string, string>,
undefined,
this.flags.ignoreMissingSecrets
)
: undefined;

if (actorCurrentVersion) {
Expand Down
11 changes: 10 additions & 1 deletion src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ export class RunCommand extends ApifyCommand<typeof RunCommand> {
stdin: StdinMode.Stringified,
exclusive: ['input'],
}),
'ignore-missing-secrets': Flags.boolean({
description: 'Ignore missing secrets and show warnings instead of failing. Environment variables referencing missing secrets will be omitted.',
default: false,
required: false,
}),
};

async run() {
Expand Down Expand Up @@ -259,7 +264,11 @@ export class RunCommand extends ApifyCommand<typeof RunCommand> {
if (userId) localEnvVars[APIFY_ENV_VARS.USER_ID] = userId;
if (token) localEnvVars[APIFY_ENV_VARS.TOKEN] = token;
if (localConfig!.environmentVariables) {
const updatedEnv = replaceSecretsValue(localConfig!.environmentVariables as Record<string, string>);
const updatedEnv = replaceSecretsValue(
localConfig!.environmentVariables as Record<string, string>,
undefined,
this.flags.ignoreMissingSecrets
);
Object.assign(localEnvVars, updatedEnv);
}

Expand Down
53 changes: 45 additions & 8 deletions src/lib/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,44 @@ const isSecretKey = (envValue: string) => {
* Replaces secure values in env with proper values from local secrets file.
* @param env
* @param secrets - Object with secrets, if not set, will be load from secrets file.
* @param ignoreMissingSecrets - If true, emit warnings for missing secrets instead of throwing errors
*/
export const replaceSecretsValue = (env: Record<string, string>, secrets?: Record<string, string>) => {
export const replaceSecretsValue = (env: Record<string, string>, secrets?: Record<string, string>, ignoreMissingSecrets?: boolean) => {
secrets = secrets || getSecretsFile();
const updatedEnv = {};
const missingSecrets: string[] = [];

Object.keys(env).forEach((key) => {
if (isSecretKey(env[key])) {
const secretKey = env[key].replace(new RegExp(`^${SECRET_KEY_PREFIX}`), '');
if (secrets![secretKey]) {
// @ts-expect-error - we are replacing the value
updatedEnv[key] = secrets[secretKey];
} else {
warning({
message: `Value for ${secretKey} not found in local secrets. Set it by calling "apify secrets add ${secretKey} [SECRET_VALUE]"`,
});
missingSecrets.push(secretKey);
}
} else {
// @ts-expect-error - we are replacing the value
updatedEnv[key] = env[key];
}
});

if (missingSecrets.length > 0) {
if (ignoreMissingSecrets) {
// Emit warnings for each missing secret, keeping original behavior
missingSecrets.forEach(secretKey => {
warning({
message: `Value for ${secretKey} not found in local secrets. Set it by calling "apify secrets add ${secretKey} [SECRET_VALUE]"`,
});
});
} else {
throw new Error(
`Missing secrets: ${missingSecrets.join(', ')}. ` +
`Set them by calling "apify secrets add <SECRET_NAME> <SECRET_VALUE>".`
);
}
}

return updatedEnv;
};

Expand All @@ -86,11 +104,15 @@ interface EnvVar {
/**
* Transform env to envVars format attribute, which uses Apify API
* It replaces secrets to values from secrets file.
* @param env
* @param secrets - Object with secrets, if not set, will be load from secrets file.
* @param ignoreMissingSecrets - If true, emit warnings for missing secrets instead of throwing errors
*/
export const transformEnvToEnvVars = (env: Record<string, string>, secrets?: Record<string, string>) => {
export const transformEnvToEnvVars = (env: Record<string, string>, secrets?: Record<string, string>, ignoreMissingSecrets?: boolean) => {
secrets = secrets || getSecretsFile();
const envVars: EnvVar[] = [];
const missingSecrets: string[] = [];

Object.keys(env).forEach((key) => {
if (isSecretKey(env[key])) {
const secretKey = env[key].replace(new RegExp(`^${SECRET_KEY_PREFIX}`), '');
Expand All @@ -101,9 +123,7 @@ export const transformEnvToEnvVars = (env: Record<string, string>, secrets?: Rec
isSecret: true,
});
} else {
warning({
message: `Value for ${secretKey} not found in local secrets. Set it by calling "apify secrets add ${secretKey} [SECRET_VALUE]"`,
});
missingSecrets.push(secretKey);
}
} else {
envVars.push({
Expand All @@ -112,5 +132,22 @@ export const transformEnvToEnvVars = (env: Record<string, string>, secrets?: Rec
});
}
});

if (missingSecrets.length > 0) {
if (ignoreMissingSecrets) {
// Emit warnings for each missing secret, keeping original behavior
missingSecrets.forEach(secretKey => {
warning({
message: `Value for ${secretKey} not found in local secrets. Set it by calling "apify secrets add ${secretKey} [SECRET_VALUE]"`,
});
});
} else {
throw new Error(
`Missing secrets: ${missingSecrets.join(', ')}. ` +
`Set them by calling "apify secrets add <SECRET_NAME> <SECRET_VALUE>".`
);
}
}

return envVars;
};
Loading
Loading