Skip to content
Open
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
12 changes: 10 additions & 2 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -760,8 +760,8 @@ DESCRIPTION

USAGE
$ apify actors push [actorId] [--allow-missing-secrets]
[-b <value>] [--dir <value>] [-f] [--json] [--open]
[-v <value>] [-w <value>]
[--apply-env-vars-to-build] [-b <value>] [--dir <value>]
[-f] [--json] [--open] [-v <value>] [-w <value>]

ARGUMENTS
actorId Name or ID of the Actor to push (e.g. "apify/hello-world" or
Expand All @@ -772,6 +772,14 @@ FLAGS
--allow-missing-secrets Allow the command to
continue even when secret values are not found in
the local secrets storage.
--apply-env-vars-to-build Make the environment
variables also available to the Actor build
process. To turn the setting off, use
--no-apply-env-vars-to-build. Overrides the value
of the 'applyEnvVarsToBuild' field in the
'.actor/actor.json' file. When both the field and
the flag are omitted, the setting currently stored
on the platform is kept.
-b, --build-tag=<value> Build tag to be
applied to the successful Actor build. By default,
it is taken from the '.actor/actor.json' file.
Expand Down
19 changes: 19 additions & 0 deletions docs/vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,22 @@ You can use the CLI to manage secrets environment variables:
...
}
```

### Apply environment variables to the build

By default, custom environment variables are available only at runtime. To make them available also to the Actor build process, for example, as Docker build arguments, set `applyEnvVarsToBuild` in `.actor/actor.json`:

```json
{
"actorSpecification": 1,
"name": "dataset-to-mysql",
"version": "0.1",
"buildTag": "latest",
"applyEnvVarsToBuild": true,
"environmentVariables": {
"MYSQL_PASSWORD": "@mySecretPassword"
}
}
```

To apply the environment variables to a single push, add the `--apply-env-vars-to-build` flag to the `apify push` command. To turn off the setting for a single push, add the `--no-apply-env-vars-to-build` flag. The flag overrides the value of the `applyEnvVarsToBuild` field. If you use neither the field nor a flag, the Apify platform keeps the stored setting.
10 changes: 9 additions & 1 deletion src/commands/actors/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ export class ActorsPushCommand extends ApifyCommand<typeof ActorsPushCommand> {
required: false,
default: false,
}),
'apply-env-vars-to-build': Flags.boolean({
description: `Make the environment variables also available to the Actor build process. To turn the setting off, use --no-apply-env-vars-to-build. Overrides the value of the 'applyEnvVarsToBuild' field in the '${LOCAL_CONFIG_PATH}' file. When both the field and the flag are omitted, the setting currently stored on the platform is kept.`,
required: false,
}),
};

static override args = {
Expand Down Expand Up @@ -406,9 +410,12 @@ Skipping push. Use --force to override.`,
allowMissing: this.flags.allowMissingSecrets,
})
: undefined;
// undefined when neither the flag nor the actor.json field is set, so the value stored on the platform is preserved
const applyEnvVarsToBuild =
this.flags.applyEnvVarsToBuild ?? (actorConfig!.applyEnvVarsToBuild as boolean | undefined);

if (actorCurrentVersion) {
const actorVersionModifier = { tarballUrl, sourceFiles, buildTag, sourceType, envVars };
const actorVersionModifier = { tarballUrl, sourceFiles, buildTag, sourceType, envVars, applyEnvVarsToBuild };
// TODO: fix this type too -.-
await actorClient.version(version).update(actorVersionModifier as never);
run({ message: `Updated version ${version} for Actor ${actor.name}.` });
Expand All @@ -420,6 +427,7 @@ Skipping push. Use --force to override.`,
buildTag,
sourceType,
envVars,
applyEnvVarsToBuild,
};

await actorClient.versions().create({
Expand Down
6 changes: 4 additions & 2 deletions src/lib/command-framework/apify-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,9 @@ export abstract class ApifyCommand<T extends typeof BuiltApifyCommand = typeof B
}

// If you have a flag a, with alias b, and you pass --a and --b, it's not allowed
const matchingFlags = allMatchers.filter((matcher) => rawFlags[matcher]);
// Check for presence, not truthiness: the real CLI path always yields arrays (`multiple: true`), but
// internalRunCommand/testRunCommand inject scalar values, where an explicit `false` must match too
const matchingFlags = allMatchers.filter((matcher) => typeof rawFlags[matcher] !== 'undefined');

if (matchingFlags.length > 1) {
throw new CommandError({
Expand All @@ -514,7 +516,7 @@ export abstract class ApifyCommand<T extends typeof BuiltApifyCommand = typeof B

let rawFlag = rawFlags[matchingFlags[0]];

if (!rawFlag && builderData.required) {
if (typeof rawFlag === 'undefined' && builderData.required) {
throw new CommandError({
code: CommandErrorCode.APIFY_MISSING_FLAG,
command: this.ctor,
Expand Down
116 changes: 116 additions & 0 deletions test/api/commands/push.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,122 @@ describe('[api] apify push', () => {
TEST_TIMEOUT,
);

it(
'should set applyEnvVarsToBuild when the flag is passed and keep it when omitted',
async () => {
const testActor = await testUserClient.actors().create(TEST_ACTOR);
actorsForCleanup.add(testActor.id);
const testActorClient = testUserClient.actor(testActor.id);
const actorJson = JSON.parse(readFileSync(joinPath(LOCAL_CONFIG_PATH), 'utf8'));

await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
flags_applyEnvVarsToBuild: true,
});

const versionWithFlag = await testActorClient.version(actorJson.version).get();

await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
});

const versionWithoutFlag = await testActorClient.version(actorJson.version).get();

// false is what --no-apply-env-vars-to-build parses to
await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
flags_applyEnvVarsToBuild: false,
});

const versionWithNegatedFlag = await testActorClient.version(actorJson.version).get();

await testActorClient.delete();

expect(versionWithFlag!.applyEnvVarsToBuild).to.be.eql(true);
// omitting the flag must preserve the value stored on the platform
expect(versionWithoutFlag!.applyEnvVarsToBuild).to.be.eql(true);
// the negated flag must actively turn the setting off
expect(versionWithNegatedFlag!.applyEnvVarsToBuild).to.be.eql(false);
},
TEST_TIMEOUT,
);

it(
'should read applyEnvVarsToBuild from actor.json, with the flag taking precedence',
async () => {
const testActor = await testUserClient.actors().create(TEST_ACTOR);
actorsForCleanup.add(testActor.id);
const testActorClient = testUserClient.actor(testActor.id);
const actorJson = JSON.parse(readFileSync(joinPath(LOCAL_CONFIG_PATH), 'utf8'));

try {
actorJson.applyEnvVarsToBuild = true;
writeFileSync(joinPath(LOCAL_CONFIG_PATH), JSON.stringify(actorJson, null, '\t'), { flag: 'w' });

await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
});

const versionWithFieldTrue = await testActorClient.version(actorJson.version).get();

actorJson.applyEnvVarsToBuild = false;
writeFileSync(joinPath(LOCAL_CONFIG_PATH), JSON.stringify(actorJson, null, '\t'), { flag: 'w' });
// the actor config is cached per cwd, so mid-test rewrites need a reset
resetCwdCaches();

await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
});

const versionWithFieldFalse = await testActorClient.version(actorJson.version).get();

// the file still says false, but the flag must win
await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
flags_applyEnvVarsToBuild: true,
});

const versionWithFlagOverride = await testActorClient.version(actorJson.version).get();

// and the negated flag must also win over a true in the file
actorJson.applyEnvVarsToBuild = true;
writeFileSync(joinPath(LOCAL_CONFIG_PATH), JSON.stringify(actorJson, null, '\t'), { flag: 'w' });
resetCwdCaches();

await testRunCommand(ActorsPushCommand, {
args_actorId: testActor.id,
flags_noPrompt: true,
flags_force: true,
flags_applyEnvVarsToBuild: false,
});

const versionWithNegatedFlagOverride = await testActorClient.version(actorJson.version).get();

expect(versionWithFieldTrue!.applyEnvVarsToBuild).to.be.eql(true);
expect(versionWithFieldFalse!.applyEnvVarsToBuild).to.be.eql(false);
expect(versionWithFlagOverride!.applyEnvVarsToBuild).to.be.eql(true);
expect(versionWithNegatedFlagOverride!.applyEnvVarsToBuild).to.be.eql(false);
} finally {
delete actorJson.applyEnvVarsToBuild;
writeFileSync(joinPath(LOCAL_CONFIG_PATH), JSON.stringify(actorJson, null, '\t'), { flag: 'w' });
await testActorClient.delete();
}
},
TEST_TIMEOUT,
);

it(
'should upload zip for source files larger that 3MB',
async () => {
Expand Down