UNPKG

balena-cli

Version:

The official balena Command Line Interface

315 lines (308 loc) • 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const lazy_1 = require("../../utils/lazy"); const messages_1 = require("../../utils/messages"); const errors_1 = require("../../errors"); const normalization_1 = require("../../utils/normalization"); const compose_ts_1 = require("../../utils/compose_ts"); var BuildTarget; (function (BuildTarget) { BuildTarget[BuildTarget["Cloud"] = 0] = "Cloud"; BuildTarget[BuildTarget["Device"] = 1] = "Device"; })(BuildTarget || (BuildTarget = {})); class PushCmd extends core_1.Command { async run() { const { args: params, flags: options } = await this.parse(PushCmd); const Logger = await Promise.resolve().then(() => require('../../utils/logger')); const logger = Logger.getLogger(); logger.logDebug(`Using build source directory: ${options.source} `); const sdk = (0, lazy_1.getBalenaSdk)(); const { validateProjectDirectory } = await Promise.resolve().then(() => require('../../utils/compose_ts')); const { dockerfilePath, registrySecrets } = await validateProjectDirectory(sdk, { dockerfilePath: options.dockerfile, noParentCheck: options['noparent-check'], projectPath: options.source, registrySecretsPath: options['registry-secrets'], }); switch (await this.getBuildTarget(params.fleetOrDevice)) { case BuildTarget.Cloud: logger.logDebug(`Pushing to cloud for fleet: ${params.fleetOrDevice}`); await this.pushToCloud(params.fleetOrDevice, options, sdk, dockerfilePath, registrySecrets); break; case BuildTarget.Device: logger.logDebug(`Pushing to local device: ${params.fleetOrDevice}`); await this.pushToDevice(params.fleetOrDevice, options, dockerfilePath, registrySecrets); break; } } async pushToCloud(appNameOrSlug, options, sdk, dockerfilePath, registrySecrets) { var _a; const remote = await Promise.resolve().then(() => require('../../utils/remote-build')); const { getApplication } = await Promise.resolve().then(() => require('../../utils/sdk')); const localOnlyOptions = [ 'nolive', 'service', 'system', 'env', ]; this.checkInvalidOptions(localOnlyOptions, options, 'is only valid when pushing to a local mode device'); const { releaseTagKeys, releaseTagValues } = (0, compose_ts_1.parseReleaseTagKeysAndValues)((_a = options['release-tag']) !== null && _a !== void 0 ? _a : []); const { checkLoggedIn } = await Promise.resolve().then(() => require('../../utils/patterns')); await checkLoggedIn(); const [token, baseUrl] = await Promise.all([ sdk.auth.getToken(), sdk.settings.get('balenaUrl'), ]); const application = await getApplication(sdk, appNameOrSlug, { $select: 'slug', }); const opts = { dockerfilePath, emulated: options.emulated, multiDockerignore: options['multi-dockerignore'], nocache: options.nocache, registrySecrets, headless: options.detached, convertEol: !options['noconvert-eol'], isDraft: options.draft, }; const args = { appSlug: application.slug, source: options.source, auth: token, baseUrl, sdk, opts, }; const releaseId = await remote.startRemoteBuild(args); if (releaseId) { await (0, compose_ts_1.applyReleaseTagKeysAndValues)(sdk, releaseId, releaseTagKeys, releaseTagValues); if (options.note) { await sdk.models.release.setNote(releaseId, options.note); } } else if (releaseTagKeys.length > 0) { throw new Error((0, lazy_1.stripIndent) ` A release ID could not be parsed out of the builder's output. As a result, the release tags have not been set.`); } } async pushToDevice(localDeviceAddress, options, dockerfilePath, registrySecrets) { const remoteOnlyOptions = ['release-tag', 'draft']; this.checkInvalidOptions(remoteOnlyOptions, options, 'is only valid when pushing to a fleet'); const deviceDeploy = await Promise.resolve().then(() => require('../../utils/device/deploy')); try { await deviceDeploy.deployToDevice({ source: options.source, deviceHost: localDeviceAddress, dockerfilePath, registrySecrets, multiDockerignore: options['multi-dockerignore'], nocache: options.nocache, pull: options.pull, noParentCheck: options['noparent-check'], nolive: options.nolive, detached: options.detached, services: options.service, system: options.system, env: options.env || [], convertEol: !options['noconvert-eol'], }); } catch (e) { const { BuildError } = await Promise.resolve().then(() => require('../../utils/device/errors')); if ((0, errors_1.instanceOf)(e, BuildError)) { throw new errors_1.ExpectedError(e.toString()); } else { throw e; } } } async getBuildTarget(appOrDevice) { const { validateLocalHostnameOrIp } = await Promise.resolve().then(() => require('../../utils/validation')); return validateLocalHostnameOrIp(appOrDevice) ? BuildTarget.Device : BuildTarget.Cloud; } checkInvalidOptions(invalidOptions, options, errorMessage) { invalidOptions.forEach((opt) => { if (options[opt]) { throw new errors_1.ExpectedError(`The --${opt} flag ${errorMessage}`); } }); } } PushCmd.description = (0, lazy_1.stripIndent) ` Build release images on balenaCloud servers or on a local mode device. Build release images on balenaCloud servers or on a local mode device. When building on the balenaCloud servers, the given source directory will be sent to the remote server. This can be used as a drop-in replacement for the "git push" deployment method. When building on a local mode device, the given source directory will be built on the device, and the resulting containers will be run on the device. Logs will be streamed back from the device as part of the same invocation. The web dashboard can be used to switch a device to local mode: https://www.balena.io/docs/learn/develop/local-mode/ Note that local mode requires a supervisor version of at least v7.21.0. The logs from only a single service can be shown with the --service flag, and showing only the system logs can be achieved with --system. Note that these flags can be used together. When pushing to a local device a live session will be started. The project source folder is watched for filesystem events, and changes to files and folders are automatically synchronized to the running containers. The synchronization is only in one direction, from this machine to the device, and changes made on the device itself may be overwritten. This feature requires a device running supervisor version v9.7.0 or greater. ${messages_1.registrySecretsHelp.split('\n').join('\n\t\t')} ${messages_1.dockerignoreHelp.split('\n').join('\n\t\t')} Note: the --service and --env flags must come after the fleetOrDevice parameter, as per examples. `; PushCmd.examples = [ '$ balena push myFleet', '$ balena push myFleet --source <source directory>', '$ balena push myFleet -s <source directory>', '$ balena push myFleet --source <source directory> --note "this is the note for this release"', '$ balena push myFleet --release-tag key1 "" key2 "value2 with spaces"', '$ balena push myorg/myfleet', '', '$ balena push 10.0.0.1', '$ balena push 10.0.0.1 --source <source directory>', '$ balena push 10.0.0.1 --service my-service', '$ balena push 10.0.0.1 --env MY_ENV_VAR=value --env my-service:SERVICE_VAR=value', '$ balena push 10.0.0.1 --nolive', '', '$ balena push 23c73a1.local --system', '$ balena push 23c73a1.local --system --service my-service', ]; PushCmd.args = { fleetOrDevice: core_1.Args.string({ description: 'fleet name or slug, or local device IP address or ".local" hostname', required: true, parse: normalization_1.lowercaseIfSlug, }), }; PushCmd.flags = { source: core_1.Flags.string({ description: (0, lazy_1.stripIndent) ` Source directory to be sent to balenaCloud or balenaOS device (default: current working dir)`, char: 's', default: '.', }), emulated: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` Don't use the faster, native balenaCloud ARM builders; force slower QEMU ARM emulation on Intel x86-64 builders. This flag is sometimes used to investigate suspected issues with the balenaCloud backend.`, char: 'e', default: false, }), dockerfile: core_1.Flags.string({ description: 'Alternative Dockerfile name/path, relative to the source folder', }), nocache: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` Don't use cached layers of previously built images for this project. This ensures that the latest base image and packages are pulled. Note that build logs may still display the message _"Pulling previous images for caching purposes" (as the cloud builder needs previous images to compute delta updates), but the logs will not display the "Using cache" lines for each build step of a Dockerfile.`, char: 'c', default: false, }), pull: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` When pushing to a local device, force the base images to be pulled again. Currently this option is ignored when pushing to the balenaCloud builders.`, default: false, }), 'noparent-check': core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` Disable project validation check of 'docker-compose.yml' file in parent folder`, default: false, }), 'registry-secrets': core_1.Flags.string({ description: (0, lazy_1.stripIndent) ` Path to a local YAML or JSON file containing Docker registry passwords used to pull base images. Note that if registry-secrets are not provided on the command line, a secrets configuration file from the balena directory will be used (usually $HOME/.balena/secrets.yml|.json)`, char: 'R', }), nolive: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` Don't run a live session on this push. The filesystem will not be monitored, and changes will not be synchronized to any running containers. Note that both this flag and --detached are required to cause the process to end once the initial build has completed.`, default: false, }), detached: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` When pushing to the cloud, this option will cause the build to start, then return execution back to the shell, with the status and release ID (if applicable). When pushing to a local mode device, this option will cause the command to not tail logs when the build has completed.`, char: 'd', default: false, }), service: core_1.Flags.string({ description: (0, lazy_1.stripIndent) ` Reject logs not originating from this service. This can be used in combination with --system and other --service flags. Only valid when pushing to a local mode device.`, multiple: true, }), system: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` Only show system logs. This can be used in combination with --service. Only valid when pushing to a local mode device.`, default: false, }), env: core_1.Flags.string({ description: (0, lazy_1.stripIndent) ` When performing a push to device, run the built containers with environment variables provided with this argument. Environment variables can be applied to individual services by adding their service name before the argument, separated by a colon, e.g: --env main:MY_ENV=value Note that if the service name cannot be found in the composition, the entire left hand side of the = character will be treated as the variable name. `, multiple: true, }), 'noconvert-eol': core_1.Flags.boolean({ description: `Don't convert line endings from CRLF (Windows format) to LF (Unix format).`, default: false, }), 'multi-dockerignore': core_1.Flags.boolean({ description: 'Have each service use its own .dockerignore file. See "balena help push".', char: 'm', default: false, }), 'release-tag': core_1.Flags.string({ description: (0, lazy_1.stripIndent) ` Set release tags if the image build is successful (balenaCloud only). Multiple arguments may be provided, alternating tag keys and values (see examples). Hint: Empty values may be specified with "" (bash, cmd.exe) or '""' (PowerShell). `, multiple: true, exclusive: ['detached'], }), draft: core_1.Flags.boolean({ description: (0, lazy_1.stripIndent) ` Instruct the builder to create the release as a draft. Draft releases are ignored by the 'track latest' release policy but can be used through release pinning. Draft releases can be marked as final through the API. Releases are created as final by default unless this option is given.`, default: false, }), note: core_1.Flags.string({ description: 'The notes for this release' }), }; PushCmd.primary = true; exports.default = PushCmd; //# sourceMappingURL=index.js.map