Skip to content

Commit ecc6f80

Browse files
committedMar 11, 2025
api-key generate: Add required argument expiryDate
Change-type: major
1 parent c0fd1e3 commit ecc6f80

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed
 

‎docs/balena-cli.md

+6
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,19 @@ or to authenticate requests to the API with an 'Authorization: Bearer <key>' hea
326326
Examples:
327327

328328
$ balena api-key generate "Jenkins Key"
329+
$ balena api-key generate "Jenkins Key" 2025-10-30
330+
$ balena api-key generate "Jenkins Key" never
329331

330332
### Arguments
331333

332334
#### NAME
333335

334336
the API key name
335337

338+
#### EXPIRYDATE
339+
340+
the expiry date of the API key as an ISO date string, or "never" for no expiry
341+
336342
## api-key list
337343

338344
### Aliases

‎src/commands/api-key/generate.ts

+72-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@
1717

1818
import { Args, Command } from '@oclif/core';
1919
import { ExpectedError } from '../../errors';
20-
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
20+
import { getBalenaSdk, getCliForm, stripIndent } from '../../utils/lazy';
21+
import {
22+
formatDuration,
23+
intervalToDuration,
24+
isValid,
25+
parseISO,
26+
} from 'date-fns';
27+
28+
// In days
29+
const durations = [1, 7, 30, 90];
2130

2231
async function isLoggedInWithJwt() {
2332
const balena = getBalenaSdk();
@@ -41,23 +50,83 @@ export default class GenerateCmd extends Command {
4150
This key can be used to log into the CLI using 'balena login --token <key>',
4251
or to authenticate requests to the API with an 'Authorization: Bearer <key>' header.
4352
`;
44-
public static examples = ['$ balena api-key generate "Jenkins Key"'];
53+
public static examples = [
54+
'$ balena api-key generate "Jenkins Key"',
55+
'$ balena api-key generate "Jenkins Key" 2025-10-30',
56+
'$ balena api-key generate "Jenkins Key" never',
57+
];
4558

4659
public static args = {
4760
name: Args.string({
4861
description: 'the API key name',
4962
required: true,
5063
}),
64+
expiryDate: Args.string({
65+
description:
66+
'the expiry date of the API key as an ISO date string, or "never" for no expiry',
67+
}),
5168
};
5269

5370
public static authenticated = true;
5471

5572
public async run() {
5673
const { args: params } = await this.parse(GenerateCmd);
5774

75+
let expiryDateResponse: string | number | undefined = params.expiryDate;
5876
let key;
5977
try {
60-
key = await getBalenaSdk().models.apiKey.create(params.name);
78+
if (!expiryDateResponse) {
79+
expiryDateResponse = await getCliForm().ask({
80+
message: 'Please pick an expiry date for the API key',
81+
type: 'list',
82+
choices: [...durations, 'custom', 'never'].map((duration) => ({
83+
name:
84+
duration === 'never'
85+
? 'No expiration'
86+
: typeof duration === 'number'
87+
? formatDuration(
88+
intervalToDuration({
89+
start: 0,
90+
end: duration * 24 * 60 * 60 * 1000,
91+
}),
92+
)
93+
: 'Custom expiration',
94+
value: duration,
95+
})),
96+
});
97+
}
98+
let expiryDate: Date | null;
99+
if (expiryDateResponse === 'never') {
100+
expiryDate = null;
101+
} else if (expiryDateResponse === 'custom') {
102+
do {
103+
expiryDate = parseISO(
104+
await getCliForm().ask({
105+
message:
106+
'Please enter an expiry date for the API key as an ISO date string',
107+
type: 'input',
108+
}),
109+
);
110+
if (!isValid(expiryDate)) {
111+
console.error('Invalid date format');
112+
}
113+
} while (!isValid(expiryDate));
114+
} else if (typeof expiryDateResponse === 'string') {
115+
expiryDate = parseISO(expiryDateResponse);
116+
if (!isValid(expiryDate)) {
117+
throw new Error(
118+
'Invalid date format, please use a valid ISO date string',
119+
);
120+
}
121+
} else {
122+
expiryDate = new Date(
123+
Date.now() + expiryDateResponse * 24 * 60 * 60 * 1000,
124+
);
125+
}
126+
key = await getBalenaSdk().models.apiKey.create({
127+
name: params.name,
128+
expiryDate: expiryDate === null ? null : expiryDate.toISOString(),
129+
});
61130
} catch (e) {
62131
if (e.name === 'BalenaNotLoggedIn') {
63132
if (await isLoggedInWithJwt()) {

0 commit comments

Comments
 (0)
Please sign in to comment.