Compare commits

...

1 Commits

Author SHA1 Message Date
eed4a385a9 wip 2024-02-21 16:38:14 -03:00
9 changed files with 92 additions and 26 deletions

View File

@ -882,6 +882,7 @@ Examples:
$ balena build --fleet myFleet $ balena build --fleet myFleet
$ balena build ./source/ --fleet myorg/myfleet $ balena build ./source/ --fleet myorg/myfleet
$ balena build ./source/ --fleet myorg/myfleet --docker-compose my-custom-compose.yml
$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated $ balena build --deviceType raspberrypi3 --arch armv7hf --emulated
$ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac $ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac
$ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows $ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows
@ -915,6 +916,10 @@ Use QEMU for ARM architecture emulation during the image build
Alternative Dockerfile name/path, relative to the source folder Alternative Dockerfile name/path, relative to the source folder
#### --docker-compose DOCKER-COMPOSE
Alternative compose yml file, relative to the source folder
#### --nologs #### --nologs
Hide the image build log output (produce less verbose output) Hide the image build log output (produce less verbose output)
@ -1087,6 +1092,7 @@ Examples:
$ balena deploy myFleet $ balena deploy myFleet
$ balena deploy myorg/myfleet --build --source myBuildDir/ $ balena deploy myorg/myfleet --build --source myBuildDir/
$ balena deploy myorg/myfleet --builld --source myBuildDir/ --docker-compose my-custom-compose.yml
$ balena deploy myorg/myfleet --build --source myBuildDir/ --note "this is the note for this release" $ balena deploy myorg/myfleet --build --source myBuildDir/ --note "this is the note for this release"
$ balena deploy myorg/myfleet myRepo/myImage $ balena deploy myorg/myfleet myRepo/myImage
$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces" $ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"
@ -1140,6 +1146,10 @@ Use QEMU for ARM architecture emulation during the image build
Alternative Dockerfile name/path, relative to the source folder Alternative Dockerfile name/path, relative to the source folder
#### --docker-compose DOCKER-COMPOSE
Alternative compose yml file, relative to the source folder
#### --nologs #### --nologs
Hide the image build log output (produce less verbose output) Hide the image build log output (produce less verbose output)
@ -3281,6 +3291,7 @@ Examples:
$ balena push myFleet -s <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 --source <source directory> --note "this is the note for this release"
$ balena push myFleet --release-tag key1 "" key2 "value2 with spaces" $ balena push myFleet --release-tag key1 "" key2 "value2 with spaces"
$ balena push myorg/myfleet --docker-compose my-custom-compose.yml
$ balena push myorg/myfleet $ balena push myorg/myfleet
$ balena push 10.0.0.1 $ balena push 10.0.0.1
@ -3315,6 +3326,10 @@ suspected issues with the balenaCloud backend.
Alternative Dockerfile name/path, relative to the source folder Alternative Dockerfile name/path, relative to the source folder
#### --docker-compose DOCKER-COMPOSE
Alternative compose yml file, relative to the source folder
#### -c, --nocache #### -c, --nocache
Don't use cached layers of previously built images for this project. This Don't use cached layers of previously built images for this project. This

View File

@ -67,6 +67,7 @@ ${dockerignoreHelp}
public static examples = [ public static examples = [
'$ balena build --fleet myFleet', '$ balena build --fleet myFleet',
'$ balena build ./source/ --fleet myorg/myfleet', '$ balena build ./source/ --fleet myorg/myfleet',
'$ balena build ./source/ --fleet myorg/myfleet --docker-compose my-custom-compose.yml',
'$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated', '$ balena build --deviceType raspberrypi3 --arch armv7hf --emulated',
'$ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac', '$ balena build --docker /var/run/docker.sock --fleet myFleet # Linux, Mac',
'$ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows', '$ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows',

View File

@ -98,6 +98,7 @@ ${dockerignoreHelp}
public static examples = [ public static examples = [
'$ balena deploy myFleet', '$ balena deploy myFleet',
'$ balena deploy myorg/myfleet --build --source myBuildDir/', '$ balena deploy myorg/myfleet --build --source myBuildDir/',
'$ balena deploy myorg/myfleet --builld --source myBuildDir/ --docker-compose my-custom-compose.yml',
'$ balena deploy myorg/myfleet --build --source myBuildDir/ --note "this is the note for this release"', '$ balena deploy myorg/myfleet --build --source myBuildDir/ --note "this is the note for this release"',
'$ balena deploy myorg/myfleet myRepo/myImage', '$ balena deploy myorg/myfleet myRepo/myImage',
'$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"', '$ balena deploy myFleet myRepo/myImage --release-tag key1 "" key2 "value2 with spaces"',

View File

@ -78,6 +78,7 @@ export default class PushCmd extends Command {
'$ balena push myFleet -s <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 --source <source directory> --note "this is the note for this release"',
'$ balena push myFleet --release-tag key1 "" key2 "value2 with spaces"', '$ balena push myFleet --release-tag key1 "" key2 "value2 with spaces"',
'$ balena push myorg/myfleet --docker-compose my-custom-compose.yml',
'$ balena push myorg/myfleet', '$ balena push myorg/myfleet',
'', '',
'$ balena push 10.0.0.1', '$ balena push 10.0.0.1',
@ -121,6 +122,10 @@ export default class PushCmd extends Command {
description: description:
'Alternative Dockerfile name/path, relative to the source folder', 'Alternative Dockerfile name/path, relative to the source folder',
}), }),
'docker-compose': Flags.string({
description:
'Alternative compose yml file, relative to the source folder',
}),
nocache: Flags.boolean({ nocache: Flags.boolean({
description: stripIndent` description: stripIndent`
Don't use cached layers of previously built images for this project. This Don't use cached layers of previously built images for this project. This
@ -365,6 +370,7 @@ export default class PushCmd extends Command {
dockerfilePath, dockerfilePath,
registrySecrets, registrySecrets,
multiDockerignore: options['multi-dockerignore'], multiDockerignore: options['multi-dockerignore'],
composefileName: options['docker-compose'],
nocache: options.nocache, nocache: options.nocache,
pull: options.pull, pull: options.pull,
noParentCheck: options['noparent-check'], noParentCheck: options['noparent-check'],

View File

@ -47,6 +47,12 @@ export class NoPortsDefinedError extends ExpectedError {
export class SIGINTError extends ExpectedError {} export class SIGINTError extends ExpectedError {}
export class CompositionFileNotFoundError extends ExpectedError {
constructor(filePath: string) {
super(`Composition file not found at: "${filePath}"`);
}
}
/** /**
* instanceOf is a more reliable implementation of the plain `instanceof` * instanceOf is a more reliable implementation of the plain `instanceof`
* typescript operator, for use with TypedError errors when the error * typescript operator, for use with TypedError errors when the error

View File

@ -53,6 +53,7 @@ export interface TaggedImage {
export interface ComposeOpts { export interface ComposeOpts {
convertEol: boolean; convertEol: boolean;
dockerfilePath?: string; dockerfilePath?: string;
composefileName?: string;
inlineLogs?: boolean; inlineLogs?: boolean;
multiDockerignore: boolean; multiDockerignore: boolean;
noParentCheck: boolean; noParentCheck: boolean;
@ -65,6 +66,7 @@ export interface ComposeCliFlags {
emulated: boolean; emulated: boolean;
dockerfile?: string; dockerfile?: string;
nologs: boolean; nologs: boolean;
'docker-compose'?: string;
'multi-dockerignore': boolean; 'multi-dockerignore': boolean;
'noparent-check': boolean; 'noparent-check': boolean;
'registry-secrets'?: RegistrySecrets; 'registry-secrets'?: RegistrySecrets;

View File

@ -40,6 +40,7 @@ export function generateOpts(options: {
dockerfile?: string; dockerfile?: string;
'multi-dockerignore': boolean; 'multi-dockerignore': boolean;
'noparent-check': boolean; 'noparent-check': boolean;
'docker-compose'?: string;
}): Promise<ComposeOpts> { }): Promise<ComposeOpts> {
const { promises: fs } = require('fs') as typeof import('fs'); const { promises: fs } = require('fs') as typeof import('fs');
return fs.realpath(options.source || '.').then((projectPath) => ({ return fs.realpath(options.source || '.').then((projectPath) => ({
@ -49,6 +50,7 @@ export function generateOpts(options: {
convertEol: !options['noconvert-eol'], convertEol: !options['noconvert-eol'],
dockerfilePath: options.dockerfile, dockerfilePath: options.dockerfile,
multiDockerignore: !!options['multi-dockerignore'], multiDockerignore: !!options['multi-dockerignore'],
composefileName: options['docker-compose'],
noParentCheck: options['noparent-check'], noParentCheck: options['noparent-check'],
})); }));
} }

View File

@ -31,7 +31,7 @@ import type * as MultiBuild from '@balena/compose/dist/multibuild';
import * as semver from 'semver'; import * as semver from 'semver';
import type { Duplex, Readable } from 'stream'; import type { Duplex, Readable } from 'stream';
import type { Pack } from 'tar-stream'; import type { Pack } from 'tar-stream';
import { ExpectedError } from '../errors'; import { CompositionFileNotFoundError, ExpectedError } from '../errors';
import { import {
BuiltImage, BuiltImage,
ComposeOpts, ComposeOpts,
@ -128,7 +128,11 @@ export async function loadProject(
composeStr = compose.defaultComposition(image); composeStr = compose.defaultComposition(image);
} else { } else {
logger.logDebug('Resolving project...'); logger.logDebug('Resolving project...');
[composeName, composeStr] = await resolveProject(logger, opts.projectPath); [composeName, composeStr] = await resolveProject({
logger,
projectRoot: opts.projectPath,
compositionFile: opts.composefileName,
});
if (composeName) { if (composeName) {
if (opts.dockerfilePath) { if (opts.dockerfilePath) {
@ -198,33 +202,55 @@ async function mergeDevComposeOverlay(
return composeStr; return composeStr;
} }
async function getDefaultCompositionFileName(
projectRoot: string,
): Promise<string | undefined> {
for (const fname of compositionFileNames) {
if (await exists(path.join(projectRoot, fname))) {
return fname;
}
}
}
interface ResolveProjectParameters {
logger: Logger;
projectRoot: string;
quiet?: boolean;
compositionFile?: string;
}
/** /**
* Look into the given directory for valid compose files and return * Look into the given directory for valid compose files and return
* the contents of the first one found. * the contents of the first one found.
*/ */
async function resolveProject( async function resolveProject({
logger: Logger, logger,
projectRoot: string, projectRoot,
quiet = false, quiet = false,
): Promise<[string, string]> { compositionFile,
let composeFileName = ''; }: ResolveProjectParameters): Promise<[string, string]> {
logger.logError(`Iam on resolve project and have ${compositionFile}`);
const composeFileName =
compositionFile ?? (await getDefaultCompositionFileName(projectRoot));
let composeFileContents = ''; let composeFileContents = '';
for (const fname of compositionFileNames) {
const fpath = path.join(projectRoot, fname); if (composeFileName == null) {
if (await exists(fpath)) { throw new CompositionFileNotFoundError(projectRoot);
logger.logDebug(`${fname} file found at "${projectRoot}"`); }
composeFileName = fname;
try { const fpath = path.join(projectRoot, composeFileName);
composeFileContents = await fs.readFile(fpath, 'utf8'); if (!(await exists(fpath))) {
} catch (err) { throw new CompositionFileNotFoundError(fpath);
logger.logError(`Error reading composition file "${fpath}":\n${err}`); }
throw err;
} logger.logDebug(`Using composition file at "${fpath}"`);
break; try {
} composeFileContents = await fs.readFile(fpath, 'utf8');
} catch (err) {
logger.logError(`Error reading composition file "${fpath}":\n${err}`);
throw err;
} }
if (!quiet && !composeFileName) { if (!quiet && !composeFileName) {
logger.logInfo(`No "docker-compose.yml" file found at "${projectRoot}"`); logger.logInfo(`No composition file found at "${projectRoot}"`);
} }
return [composeFileName, composeFileContents]; return [composeFileName, composeFileContents];
@ -680,15 +706,17 @@ async function loadBuildMetatada(
export async function getServiceDirsFromComposition( export async function getServiceDirsFromComposition(
sourceDir: string, sourceDir: string,
composition?: Composition, composition?: Composition,
compositionFile?: string,
): Promise<Dictionary<string>> { ): Promise<Dictionary<string>> {
const { createProject } = await import('./compose'); const { createProject } = await import('./compose');
const serviceDirs: Dictionary<string> = {}; const serviceDirs: Dictionary<string> = {};
if (!composition) { if (!composition) {
const [, composeStr] = await resolveProject( const [, composeStr] = await resolveProject({
Logger.getLogger(), logger: Logger.getLogger(),
sourceDir, projectRoot: sourceDir,
true, quiet: true,
); compositionFile,
});
if (composeStr) { if (composeStr) {
composition = createProject(sourceDir, composeStr).composition; composition = createProject(sourceDir, composeStr).composition;
} }
@ -1652,6 +1680,9 @@ export const composeCliFlags = {
description: description:
'Alternative Dockerfile name/path, relative to the source folder', 'Alternative Dockerfile name/path, relative to the source folder',
}), }),
'docker-compose': Flags.string({
description: 'Alternative compose yml file, relative to the source folder',
}),
nologs: Flags.boolean({ nologs: Flags.boolean({
description: description:
'Hide the image build log output (produce less verbose output)', 'Hide the image build log output (produce less verbose output)',

View File

@ -56,6 +56,7 @@ export interface DeviceDeployOptions {
deviceHost: string; deviceHost: string;
devicePort?: number; devicePort?: number;
dockerfilePath?: string; dockerfilePath?: string;
composefileName?: string;
registrySecrets: RegistrySecrets; registrySecrets: RegistrySecrets;
multiDockerignore: boolean; multiDockerignore: boolean;
nocache: boolean; nocache: boolean;
@ -182,6 +183,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
const project = await loadProject(globalLogger, { const project = await loadProject(globalLogger, {
convertEol: opts.convertEol, convertEol: opts.convertEol,
dockerfilePath: opts.dockerfilePath, dockerfilePath: opts.dockerfilePath,
composefileName: opts.composefileName,
multiDockerignore: opts.multiDockerignore, multiDockerignore: opts.multiDockerignore,
noParentCheck: opts.noParentCheck, noParentCheck: opts.noParentCheck,
projectName: 'local', projectName: 'local',