Skip to content

Commit 8256cf5

Browse files
authored
feat: Add secrets:ls command to list all secrets (#990)
1 parent fbdf676 commit 8256cf5

5 files changed

Lines changed: 137 additions & 1 deletion

File tree

docs/reference.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ DESCRIPTION
151151
SUBCOMMANDS
152152
secrets add Adds a new secret to '~/.apify' for use in Actor
153153
environment variables.
154+
secrets ls Lists all secret keys stored in your local
155+
configuration.
154156
secrets rm Permanently deletes a secret from your stored
155157
credentials.
156158
```
@@ -169,6 +171,19 @@ ARGUMENTS
169171
value Value of the secret
170172
```
171173
174+
##### `apify secrets ls`
175+
176+
```sh
177+
DESCRIPTION
178+
Lists all secret keys stored in your local configuration.
179+
180+
USAGE
181+
$ apify secrets ls [--json]
182+
183+
FLAGS
184+
--json Format the command output as JSON
185+
```
186+
172187
##### `apify secrets rm`
173188
174189
```sh

scripts/generate-cli-docs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const categories: Record<string, CommandsInCategory[]> = {
1313
{ command: Commands.info },
1414
{ command: Commands.secrets },
1515
{ command: Commands.secretsAdd },
16+
{ command: Commands.secretsLs },
1617
{ command: Commands.secretsRm },
1718
],
1819
'actor-dev': [

src/commands/secrets/_index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ApifyCommand } from '../../lib/command-framework/apify-command.js';
22
import { LOCAL_CONFIG_PATH } from '../../lib/consts.js';
33
import { SecretsAddCommand } from './add.js';
4+
import { SecretsLsCommand } from './ls.js';
45
import { SecretsRmCommand } from './rm.js';
56

67
export class SecretsIndexCommand extends ApifyCommand<typeof SecretsIndexCommand> {
@@ -19,7 +20,7 @@ export class SecretsIndexCommand extends ApifyCommand<typeof SecretsIndexCommand
1920
`}\n\n` +
2021
`When the Actor is pushed to Apify cloud, the "SECRET_ENV_VAR" and its value is stored as a secret environment variable of the Actor.`;
2122

22-
static override subcommands = [SecretsAddCommand, SecretsRmCommand];
23+
static override subcommands = [SecretsAddCommand, SecretsLsCommand, SecretsRmCommand];
2324

2425
async run() {
2526
this.printHelp();

src/commands/secrets/ls.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import chalk from 'chalk';
2+
3+
import { ApifyCommand } from '../../lib/command-framework/apify-command.js';
4+
import { CompactMode, ResponsiveTable } from '../../lib/commands/responsive-table.js';
5+
import { info, simpleLog } from '../../lib/outputs.js';
6+
import { getSecretsFile } from '../../lib/secrets.js';
7+
import { printJsonToStdout } from '../../lib/utils.js';
8+
9+
const table = new ResponsiveTable({
10+
allColumns: ['Secret Name'],
11+
mandatoryColumns: ['Secret Name'],
12+
columnAlignments: {
13+
'Secret Name': 'left',
14+
},
15+
});
16+
17+
export class SecretsLsCommand extends ApifyCommand<typeof SecretsLsCommand> {
18+
static override name = 'ls' as const;
19+
20+
static override description = 'Lists all secret keys stored in your local configuration.';
21+
22+
static override enableJsonFlag = true;
23+
24+
async run() {
25+
const { json } = this.flags;
26+
27+
const secrets = getSecretsFile();
28+
const secretKeys = Object.keys(secrets);
29+
30+
if (json) {
31+
printJsonToStdout({ keys: secretKeys });
32+
return;
33+
}
34+
35+
if (secretKeys.length === 0) {
36+
info({
37+
message: "You don't have any secrets stored locally. Use 'apify secrets add' to add a secret.",
38+
stdout: true,
39+
});
40+
41+
return;
42+
}
43+
44+
for (const key of secretKeys) {
45+
table.pushRow({
46+
'Secret Name': chalk.cyan(key),
47+
});
48+
}
49+
50+
simpleLog({
51+
message: table.render(CompactMode.WebLikeCompact),
52+
stdout: true,
53+
});
54+
}
55+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { SecretsAddCommand } from '../../../../src/commands/secrets/add.js';
2+
import { SecretsLsCommand } from '../../../../src/commands/secrets/ls.js';
3+
import { SecretsRmCommand } from '../../../../src/commands/secrets/rm.js';
4+
import { testRunCommand } from '../../../../src/lib/command-framework/apify-command.js';
5+
import { getSecretsFile } from '../../../../src/lib/secrets.js';
6+
7+
const SECRET_KEY_1 = 'testSecret1';
8+
const SECRET_KEY_2 = 'testSecret2';
9+
const SECRET_VALUE = 'testSecretValue';
10+
11+
describe('apify secrets ls', () => {
12+
beforeAll(async () => {
13+
// Clean up any existing test secrets
14+
const secrets = getSecretsFile();
15+
if (secrets[SECRET_KEY_1]) {
16+
await testRunCommand(SecretsRmCommand, {
17+
args_name: SECRET_KEY_1,
18+
});
19+
}
20+
if (secrets[SECRET_KEY_2]) {
21+
await testRunCommand(SecretsRmCommand, {
22+
args_name: SECRET_KEY_2,
23+
});
24+
}
25+
26+
// Add test secrets
27+
await testRunCommand(SecretsAddCommand, {
28+
args_name: SECRET_KEY_1,
29+
args_value: SECRET_VALUE,
30+
});
31+
await testRunCommand(SecretsAddCommand, {
32+
args_name: SECRET_KEY_2,
33+
args_value: SECRET_VALUE,
34+
});
35+
});
36+
37+
it('should list all secrets', async () => {
38+
const spy = vitest.spyOn(console, 'log');
39+
40+
await testRunCommand(SecretsLsCommand, {});
41+
42+
// Verify the command outputs our test secrets
43+
const output = spy.mock.calls.map((call) => call.join(' ')).join('\n');
44+
expect(output).to.include(SECRET_KEY_1);
45+
expect(output).to.include(SECRET_KEY_2);
46+
47+
spy.mockRestore();
48+
});
49+
50+
afterAll(async () => {
51+
// Clean up test secrets
52+
const secrets = getSecretsFile();
53+
if (secrets[SECRET_KEY_1]) {
54+
await testRunCommand(SecretsRmCommand, {
55+
args_name: SECRET_KEY_1,
56+
});
57+
}
58+
if (secrets[SECRET_KEY_2]) {
59+
await testRunCommand(SecretsRmCommand, {
60+
args_name: SECRET_KEY_2,
61+
});
62+
}
63+
});
64+
});

0 commit comments

Comments
 (0)