Skip to content

Commit

Permalink
Release export/import code enhancements.
Browse files Browse the repository at this point in the history
Change-type: patch
Signed-off-by: Carlo Miguel F. Cruz <[email protected]>
  • Loading branch information
cmfcruz committed Sep 17, 2024
1 parent dd18000 commit a0cd1f1
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 76 deletions.
38 changes: 20 additions & 18 deletions docs/balena-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ are encouraged to regularly update the balena CLI to the latest version.

- Releases

- [release export &#60;commitorfleet&#62;](#release-export-commitorfleet)
- [release export &#60;commitorapplication&#62;](#release-export-commitorapplication)
- [release finalize &#60;commitorid&#62;](#release-finalize-commitorid)
- [release import &#60;file&#62; &#60;fleet&#62;](#release-import-file-fleet)
- [release &#60;commitorid&#62;](#release-commitorid)
Expand Down Expand Up @@ -3347,27 +3347,29 @@ The notes for this release

# Releases

## release export &#60;commitOrFleet&#62;
## release export &#60;commitOrApplication&#62;

Exports a release to a file that you can use to import an exact
copy of the original release into another app.
copy of the original release into another app, block, or fleet.

If the SemVer of a release is provided using the --version option,
the first argument is assumed to be the fleet's slug.
the first argument is assumed to be the app's slug.

Only successful releases can be exported.

To import a release to an app, block, or fleet, use 'balena release import'.

Examples:

$ balena release export a777f7345fe3d655c1c981aa642e5555 -o ../path/to/release.tar
$ balena release export myOrg/myFleet --version 1.2.3 -o ../path/to/release.tar
$ balena release export myFleet --version 1.2.3 -o ../path/to/release.tar
$ balena release export myApp --version 1.2.3 -o ../path/to/release.tar

### Arguments

#### COMMITORFLEET
#### COMMITORAPPLICATION

release commit or fleet if used in conjunction with the --version option
release commit or app if used in conjunction with the --version option

### Options

Expand All @@ -3377,7 +3379,7 @@ output path

#### --version VERSION

version of the release to export from the specified fleet
version of the release to export from the specified app, block, or fleet

## release finalize &#60;commitOrId&#62;

Expand Down Expand Up @@ -3407,20 +3409,20 @@ the commit or ID of the release to finalize

## release import &#60;file&#62; &#60;fleet&#62;

Imports a release from a file to an app or fleet. The revision field of the release
is automatically omitted when importing a release. The backend will auto-increment
the revision field of the imported release if a release exists with the same semver.
A release will not be imported if a successful release with the same commit already
exists.

To export a release to a file, use 'balena release export'.
Imports a release from a file to an app, block, or fleet. The revision field of the
release is automatically omitted when importing a release. The balena API will
auto-increment the revision field of the imported release if a release exists with
the same semver. A release will not be imported if a successful release with the
same commit already exists.

Use the --override-version option to specify the version
of the imported release, overriding the one saved in the file.

To export a release to a file, use 'balena release export'.

Examples:

$ balena release import ../path/to/release.tar myFleet
$ balena release import ../path/to/release.tar myApp
$ balena release import ../path/to/release.tar myOrg/myFleet
$ balena release import ../path/to/release.tar myOrg/myFleet --override-version 1.2.3

Expand All @@ -3430,9 +3432,9 @@ Examples:

path to a file, e.g. "./release.tar"

#### FLEET
#### APPLICATION

fleet that the release will be imported to, e.g. "myOrg/myFleet"
app, block, or fleet that the release will be imported to, e.g. "myOrg/myFleet"

### Options

Expand Down
57 changes: 31 additions & 26 deletions src/commands/release/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,22 @@ export default class ReleaseExportCmd extends Command {
Exports a release into a file.
Exports a release to a file that you can use to import an exact
copy of the original release into another app.
copy of the original release into another app, block, or fleet.
If the SemVer of a release is provided using the --version option,
the first argument is assumed to be the fleet's slug.
the first argument is assumed to be the app's slug.
Only successful releases can be exported.
To import a release to an app, block, or fleet, use 'balena release import'.
`;
public static examples = [
'$ balena release export a777f7345fe3d655c1c981aa642e5555 -o ../path/to/release.tar',
'$ balena release export myOrg/myFleet --version 1.2.3 -o ../path/to/release.tar',
'$ balena release export myFleet --version 1.2.3 -o ../path/to/release.tar',
'$ balena release export myApp --version 1.2.3 -o ../path/to/release.tar',
];

public static usage = 'release export <commitOrFleet>';
public static usage = 'release export <commitOrApplication>';

public static flags = {
output: Flags.string({
Expand All @@ -51,15 +53,16 @@ export default class ReleaseExportCmd extends Command {
required: true,
}),
version: Flags.string({
description: 'version of the release to export from the specified fleet',
description:
'version of the release to export from the specified app, block, or fleet',
}),
help: cf.help,
};

public static args = {
commitOrFleet: Args.string({
commitOrApplication: Args.string({
description:
'release commit or fleet if used in conjunction with the --version option',
'release commit or app if used in conjunction with the --version option',
required: true,
}),
};
Expand All @@ -69,44 +72,46 @@ export default class ReleaseExportCmd extends Command {
public async run() {
const { args: params, flags: options } = await this.parse(ReleaseExportCmd);

const balena = getBalenaSdk();

let versionInfo = '';

try {
let releaseDetails:
| string
| number
| { application: string | number; rawVersion: string }; // ReleaseRawVersionApplicationPair
const balena = getBalenaSdk();

let releaseDetails: string | { application: number; rawVersion: string }; // ReleaseRawVersionApplicationPair

if (options.version != null) {
versionInfo = ` version ${options.version}`;
const parsedVersion = semver.parse(options.version);
if (parsedVersion == null) {
throw new ExpectedError(`version must be valid SemVer`);
} else {
const { getApplication } = await import('../../utils/sdk');
const application = (
await getApplication(balena, params.commitOrFleet)
).id;
releaseDetails = { application, rawVersion: parsedVersion.raw };
}
const { getApplication } = await import('../../utils/sdk');
const { id: application } = await getApplication(
balena,
params.commitOrApplication,
);
releaseDetails = { application, rawVersion: parsedVersion.raw };
} else {
releaseDetails = params.commitOrFleet;
releaseDetails = params.commitOrApplication;
}

const release = await balena.models.release.get(releaseDetails, {
$select: ['id'],
});
const { id: releaseId } = await balena.models.release.get(
releaseDetails,
{
$select: ['id'],
},
);
const releaseBundle = await create({
sdk: balena,
releaseId: release.id,
releaseId,
});
await fs.writeFile(options.output, releaseBundle);
console.log(
`Release ${params.commitOrFleet}${versionInfo} has been exported to ${options.output}.`,
`Release ${params.commitOrApplication}${versionInfo} has been exported to ${options.output}.`,
);
} catch (error) {
throw new ExpectedError(
`Release ${params.commitOrFleet}${versionInfo} could not be exported: ${error.message}`,
`Release ${params.commitOrApplication}${versionInfo} could not be exported: ${error.message}`,
);
}
}
Expand Down
43 changes: 22 additions & 21 deletions src/commands/release/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@ import { ExpectedError } from '../../errors';

export default class ReleaseImportCmd extends Command {
public static description = stripIndent`
Imports a release from a file to an app or fleet.
Imports a release from a file to an app, block, or fleet.
Imports a release from a file to an app or fleet. The revision field of the release
is automatically omitted when importing a release. The backend will auto-increment
the revision field of the imported release if a release exists with the same semver.
A release will not be imported if a successful release with the same commit already
exists.
To export a release to a file, use 'balena release export'.
Imports a release from a file to an app, block, or fleet. The revision field of the
release is automatically omitted when importing a release. The balena API will
auto-increment the revision field of the imported release if a release exists with
the same semver. A release will not be imported if a successful release with the
same commit already exists.
Use the --override-version option to specify the version
of the imported release, overriding the one saved in the file.
To export a release to a file, use 'balena release export'.
`;
public static examples = [
'$ balena release import ../path/to/release.tar myFleet',
'$ balena release import ../path/to/release.tar myApp',
'$ balena release import ../path/to/release.tar myOrg/myFleet',
'$ balena release import ../path/to/release.tar myOrg/myFleet --override-version 1.2.3',
];
Expand All @@ -61,10 +61,10 @@ export default class ReleaseImportCmd extends Command {
required: true,
description: 'path to a file, e.g. "./release.tar"',
}),
fleet: Args.string({
application: Args.string({
required: true,
description:
'fleet that the release will be imported to, e.g. "myOrg/myFleet"',
'app, block, or fleet that the release will be imported to, e.g. "myOrg/myFleet"',
}),
};

Expand All @@ -73,23 +73,24 @@ export default class ReleaseImportCmd extends Command {
public async run() {
const { args: params, flags: options } = await this.parse(ReleaseImportCmd);

const balena = getBalenaSdk();

let bundle: ReadStream;
try {
const balena = getBalenaSdk();

let bundle: ReadStream;
try {
const fileHandle = await fs.open(params.bundle);
bundle = fileHandle.createReadStream();
} catch (error) {
throw new ExpectedError(
`${params.bundle} does not exist or is not accessible`,
);
throw new Error(`${params.bundle} does not exist or is not accessible`);
}

const { getApplication } = await import('../../utils/sdk');
const application = (await getApplication(balena, params.fleet)).id;
const { id: application } = await getApplication(
balena,
params.application,
);
if (application == null) {
throw new ExpectedError(`Fleet ${params.fleet} not found`);
throw new ExpectedError(`Fleet ${params.application} not found`);
}
await apply({
sdk: balena,
Expand All @@ -98,11 +99,11 @@ export default class ReleaseImportCmd extends Command {
version: options['override-version'],
});
console.log(
`Release bundle ${params.bundle} has been imported to ${params.fleet}.`,
`Release bundle ${params.bundle} has been imported to ${params.application}.`,
);
} catch (error) {
throw new ExpectedError(
`Could not import release bundle ${params.bundle} to ${params.fleet}: ${error.message}`,
`Could not import release bundle ${params.bundle} to ${params.application}: ${error.message}`,
);
}
}
Expand Down
6 changes: 1 addition & 5 deletions tests/commands/release/export.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('balena release export', function () {

this.beforeEach(async function () {
api = new BalenaAPIMock();
api.expectGetWhoAmI();
releaseFileBuffer = await fs.readFile(
path.join('tests', 'test-data', 'release.tar'),
);
Expand All @@ -38,7 +39,6 @@ describe('balena release export', function () {
});

itSS('should export a release to a file', async () => {
api.expectGetWhoAmI();
api.expectGetRelease();
releaseBundleCreateStub.resolves(stream.Readable.from(releaseFileBuffer));

Expand All @@ -54,7 +54,6 @@ describe('balena release export', function () {
});

itSS('should fail if the create throws an error', async () => {
api.expectGetWhoAmI();
api.expectGetRelease();
const expectedError = `BalenaReleaseNotFound: Release not found: ${appCommit}`;
releaseBundleCreateStub.rejects(new Error(expectedError));
Expand All @@ -69,7 +68,6 @@ describe('balena release export', function () {
});

itSS('should parse with application slug and version', async () => {
api.expectGetWhoAmI();
api.expectGetRelease();
api.expectGetApplication({ times: 2 });
releaseBundleCreateStub.resolves(stream.Readable.from(releaseFileBuffer));
Expand All @@ -86,7 +84,6 @@ describe('balena release export', function () {
});

it('should fail if the app slug is provided without the release version', async () => {
api.expectGetWhoAmI();
api.expectGetRelease({ notFound: true });
const expectedError = `Release not found: ${appSlug}`;

Expand All @@ -100,7 +97,6 @@ describe('balena release export', function () {
});

it('should fail if the semver is invalid', async () => {
api.expectGetWhoAmI();
const expectedError = 'version must be valid SemVer';

const { err } = await runCommand(
Expand Down
Loading

0 comments on commit a0cd1f1

Please sign in to comment.