Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commands for exporting and importing app/fleet releases. #2779

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion completion/_balena
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ _balena() {
key_cmds=( add rm )
local_cmds=( configure flash )
os_cmds=( build-config configure download initialize versions )
release_cmds=( finalize invalidate validate )
release_cmds=( export finalize import invalidate validate )
tag_cmds=( rm set )


Expand Down
2 changes: 1 addition & 1 deletion completion/balena-completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ _balena_complete()
key_cmds="add rm"
local_cmds="configure flash"
os_cmds="build-config configure download initialize versions"
release_cmds="finalize invalidate validate"
release_cmds="export finalize import invalidate validate"
tag_cmds="rm set"


Expand Down
71 changes: 71 additions & 0 deletions docs/balena-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ are encouraged to regularly update the balena CLI to the latest version.

- Releases

- [release export <commitorapplication>](#release-export-commitorapplication)
- [release finalize <commitorid>](#release-finalize-commitorid)
- [release import <file> <applicapplication>](#release-import-file-applicapplication)
- [release <commitorid>](#release-commitorid)
- [release invalidate <commitorid>](#release-invalidate-commitorid)
- [release validate <commitorid>](#release-validate-commitorid)
Expand Down Expand Up @@ -3345,6 +3347,40 @@ The notes for this release

# Releases

## release export <commitOrApplication>

Exports a release to a file that you can use to import an exact
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 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 myApp --version 1.2.3 -o ../path/to/release.tar

### Arguments

#### COMMITORAPPLICATION

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

### Options

#### -o, --output OUTPUT

output path

#### --version VERSION

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

## release finalize <commitOrId>

Finalize a release. Releases can be "draft" or "final", and this command
Expand All @@ -3371,6 +3407,41 @@ the commit or ID of the release to finalize

### Options

## release import <file> <applicapplication>

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 myApp
$ balena release import ../path/to/release.tar myOrg/myFleet
$ balena release import ../path/to/release.tar myOrg/myFleet --override-version 1.2.3

### Arguments

#### BUNDLE

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

#### APPLICATION

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

### Options

#### --override-version OVERRIDE-VERSION

Imports this release with the specified version overriding the version in the file.

## release <commitOrId>

The --json option is recommended when scripting the output of this command,
Expand Down
40 changes: 40 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
"@balena/dockerignore": "^1.0.2",
"@balena/env-parsing": "^1.1.8",
"@balena/es-version": "^1.0.1",
"@balena/release-bundle": "^0.5.2",
"@oclif/core": "^4.0.8",
"@sentry/node": "^6.16.1",
"balena-config-json": "^4.2.0",
Expand Down
118 changes: 118 additions & 0 deletions src/commands/release/export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* @license
* Copyright 2016-2024 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Flags, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { create } from '@balena/release-bundle';
import * as fs from 'fs/promises';
import * as semver from 'balena-semver';
import { ExpectedError } from '../../errors';

export default class ReleaseExportCmd extends Command {
public static description = stripIndent`
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, block, or fleet.

If the SemVer of a release is provided using the --version option,
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 myApp --version 1.2.3 -o ../path/to/release.tar',
];

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

public static flags = {
output: Flags.string({
description: 'output path',
char: 'o',
required: true,
}),
version: Flags.string({
description:
'version of the release to export from the specified app, block, or fleet',
}),
help: cf.help,
};

public static args = {
commitOrApplication: Args.string({
description:
'release commit or app if used in conjunction with the --version option',
required: true,
}),
};

public static authenticated = true;

public async run() {
const { args: params, flags: options } = await this.parse(ReleaseExportCmd);

let versionInfo = '';

try {
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`);
}
const { getApplication } = await import('../../utils/sdk');
const { id: application } = await getApplication(
balena,
params.commitOrApplication,
);
releaseDetails = { application, rawVersion: parsedVersion.raw };
} else {
releaseDetails = params.commitOrApplication;
}

const { id: releaseId } = await balena.models.release.get(
releaseDetails,
{
$select: ['id'],
},
);
const releaseBundle = await create({
sdk: balena,
releaseId,
});
await fs.writeFile(options.output, releaseBundle);
console.log(
`Release ${params.commitOrApplication}${versionInfo} has been exported to ${options.output}.`,
);
} catch (error) {
throw new ExpectedError(
`Release ${params.commitOrApplication}${versionInfo} could not be exported: ${error.message}`,
);
}
}
}
Loading
Loading