mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-18 13:26:24 +00:00
Release export/import code enhancements/fixes.
Change-type: patch Signed-off-by: Carlo Miguel F. Cruz <carloc@balena.io>
This commit is contained in:
parent
8a588706bb
commit
7cc0d8b155
@ -282,9 +282,9 @@ are encouraged to regularly update the balena CLI to the latest version.
|
||||
|
||||
- Releases
|
||||
|
||||
- [release export <commitorfleet>](#release-export-commitorfleet)
|
||||
- [release export <commitorapplication>](#release-export-commitorapplication)
|
||||
- [release finalize <commitorid>](#release-finalize-commitorid)
|
||||
- [release import <file> <fleet>](#release-import-file-fleet)
|
||||
- [release import <file> <applicapplication>](#release-import-file-applicapplication)
|
||||
- [release <commitorid>](#release-commitorid)
|
||||
- [release invalidate <commitorid>](#release-invalidate-commitorid)
|
||||
- [release validate <commitorid>](#release-validate-commitorid)
|
||||
@ -3347,27 +3347,29 @@ The notes for this release
|
||||
|
||||
# Releases
|
||||
|
||||
## release export <commitOrFleet>
|
||||
## 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.
|
||||
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
|
||||
|
||||
@ -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 <commitOrId>
|
||||
|
||||
@ -3405,22 +3407,22 @@ the commit or ID of the release to finalize
|
||||
|
||||
### Options
|
||||
|
||||
## release import <file> <fleet>
|
||||
## release import <file> <applicapplication>
|
||||
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
|
@ -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({
|
||||
@ -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,
|
||||
}),
|
||||
};
|
||||
@ -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}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -26,26 +26,26 @@ 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',
|
||||
];
|
||||
|
||||
public static usage = 'release import <file> <fleet>';
|
||||
public static usage = 'release import <file> <applicapplication>';
|
||||
|
||||
public static flags = {
|
||||
'override-version': Flags.string({
|
||||
@ -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"',
|
||||
}),
|
||||
};
|
||||
|
||||
@ -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,
|
||||
@ -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}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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'),
|
||||
);
|
||||
@ -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));
|
||||
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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}`;
|
||||
|
||||
@ -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(
|
||||
|
@ -11,13 +11,13 @@ const itSS = process.env.BALENA_CLI_TEST_TYPE === 'standalone' ? it.skip : it;
|
||||
describe('balena release import', function () {
|
||||
const appCommit = '4c8becf0780ca69d33b638ea8fa163d7';
|
||||
const appSlug = 'myOrg/myFleet';
|
||||
// const appVersion = '1.2.3+rev1';
|
||||
const releasePath = path.join('tests', 'test-data', 'release.tar');
|
||||
let api: BalenaAPIMock;
|
||||
const releaseBundleApplyStub = sinon.stub();
|
||||
|
||||
this.beforeEach(async function () {
|
||||
api = new BalenaAPIMock();
|
||||
api.expectGetWhoAmI();
|
||||
mock('@balena/release-bundle', {
|
||||
apply: releaseBundleApplyStub,
|
||||
});
|
||||
@ -30,7 +30,6 @@ describe('balena release import', function () {
|
||||
});
|
||||
|
||||
itSS('should import a release to an app', async () => {
|
||||
api.expectGetWhoAmI();
|
||||
api.expectGetApplication();
|
||||
releaseBundleApplyStub.resolves(123);
|
||||
|
||||
@ -48,7 +47,6 @@ describe('balena release import', function () {
|
||||
itSS(
|
||||
'should import a release to an app with a version override',
|
||||
async () => {
|
||||
api.expectGetWhoAmI();
|
||||
api.expectGetApplication();
|
||||
releaseBundleApplyStub.resolves(123);
|
||||
|
||||
@ -65,7 +63,6 @@ describe('balena release import', function () {
|
||||
);
|
||||
|
||||
it('should fail if release file does not exist', async () => {
|
||||
api.expectGetWhoAmI();
|
||||
const nonExistentFile = path.join(
|
||||
'tests',
|
||||
'test-data',
|
||||
@ -83,7 +80,6 @@ describe('balena release import', function () {
|
||||
});
|
||||
|
||||
itSS('should fail if overriding version is not a valid semver', async () => {
|
||||
api.expectGetWhoAmI();
|
||||
api.expectGetApplication();
|
||||
const expectedError = `Manifest is malformed: Expected version to be a valid semantic version but found '${appCommit}'`;
|
||||
releaseBundleApplyStub.rejects(new Error(expectedError));
|
||||
@ -100,7 +96,6 @@ describe('balena release import', function () {
|
||||
itSS(
|
||||
'should fail if a successful release with the same commit already exists',
|
||||
async () => {
|
||||
api.expectGetWhoAmI();
|
||||
api.expectGetApplication();
|
||||
const expectedError = `A successful release with commit ${appCommit} (1.2.3) already exists; nothing to do`;
|
||||
releaseBundleApplyStub.rejects(new Error(expectedError));
|
||||
|
Loading…
Reference in New Issue
Block a user