This commit is contained in:
Otavio Jacobi 2024-02-21 16:38:14 -03:00
parent 9e4dd3fce2
commit eed4a385a9
9 changed files with 92 additions and 26 deletions

View File

@ -882,6 +882,7 @@ Examples:
$ balena build --fleet 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 --docker /var/run/docker.sock --fleet myFleet # Linux, Mac
$ 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
#### --docker-compose DOCKER-COMPOSE
Alternative compose yml file, relative to the source folder
#### --nologs
Hide the image build log output (produce less verbose output)
@ -1087,6 +1092,7 @@ Examples:
$ balena deploy myFleet
$ 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 myRepo/myImage
$ 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
#### --docker-compose DOCKER-COMPOSE
Alternative compose yml file, relative to the source folder
#### --nologs
Hide the image build log output (produce less verbose output)
@ -3281,6 +3291,7 @@ Examples:
$ 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 --docker-compose my-custom-compose.yml
$ balena push myorg/myfleet
$ 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
#### --docker-compose DOCKER-COMPOSE
Alternative compose yml file, relative to the source folder
#### -c, --nocache
Don't use cached layers of previously built images for this project. This

View File

@ -67,6 +67,7 @@ ${dockerignoreHelp}
public static examples = [
'$ balena build --fleet 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 --docker /var/run/docker.sock --fleet myFleet # Linux, Mac',
'$ balena build --docker //./pipe/docker_engine --fleet myFleet # Windows',

View File

@ -98,6 +98,7 @@ ${dockerignoreHelp}
public static examples = [
'$ balena deploy myFleet',
'$ 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 myRepo/myImage',
'$ 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 --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 --docker-compose my-custom-compose.yml',
'$ balena push myorg/myfleet',
'',
'$ balena push 10.0.0.1',
@ -121,6 +122,10 @@ export default class PushCmd extends Command {
description:
'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({
description: stripIndent`
Don't use cached layers of previously built images for this project. This
@ -365,6 +370,7 @@ export default class PushCmd extends Command {
dockerfilePath,
registrySecrets,
multiDockerignore: options['multi-dockerignore'],
composefileName: options['docker-compose'],
nocache: options.nocache,
pull: options.pull,
noParentCheck: options['noparent-check'],

View File

@ -47,6 +47,12 @@ export class NoPortsDefinedError 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`
* typescript operator, for use with TypedError errors when the error

View File

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

View File

@ -40,6 +40,7 @@ export function generateOpts(options: {
dockerfile?: string;
'multi-dockerignore': boolean;
'noparent-check': boolean;
'docker-compose'?: string;
}): Promise<ComposeOpts> {
const { promises: fs } = require('fs') as typeof import('fs');
return fs.realpath(options.source || '.').then((projectPath) => ({
@ -49,6 +50,7 @@ export function generateOpts(options: {
convertEol: !options['noconvert-eol'],
dockerfilePath: options.dockerfile,
multiDockerignore: !!options['multi-dockerignore'],
composefileName: options['docker-compose'],
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 type { Duplex, Readable } from 'stream';
import type { Pack } from 'tar-stream';
import { ExpectedError } from '../errors';
import { CompositionFileNotFoundError, ExpectedError } from '../errors';
import {
BuiltImage,
ComposeOpts,
@ -128,7 +128,11 @@ export async function loadProject(
composeStr = compose.defaultComposition(image);
} else {
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 (opts.dockerfilePath) {
@ -198,33 +202,55 @@ async function mergeDevComposeOverlay(
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
* the contents of the first one found.
*/
async function resolveProject(
logger: Logger,
projectRoot: string,
async function resolveProject({
logger,
projectRoot,
quiet = false,
): Promise<[string, string]> {
let composeFileName = '';
compositionFile,
}: ResolveProjectParameters): Promise<[string, string]> {
logger.logError(`Iam on resolve project and have ${compositionFile}`);
const composeFileName =
compositionFile ?? (await getDefaultCompositionFileName(projectRoot));
let composeFileContents = '';
for (const fname of compositionFileNames) {
const fpath = path.join(projectRoot, fname);
if (await exists(fpath)) {
logger.logDebug(`${fname} file found at "${projectRoot}"`);
composeFileName = fname;
try {
composeFileContents = await fs.readFile(fpath, 'utf8');
} catch (err) {
logger.logError(`Error reading composition file "${fpath}":\n${err}`);
throw err;
}
break;
}
if (composeFileName == null) {
throw new CompositionFileNotFoundError(projectRoot);
}
const fpath = path.join(projectRoot, composeFileName);
if (!(await exists(fpath))) {
throw new CompositionFileNotFoundError(fpath);
}
logger.logDebug(`Using composition file at "${fpath}"`);
try {
composeFileContents = await fs.readFile(fpath, 'utf8');
} catch (err) {
logger.logError(`Error reading composition file "${fpath}":\n${err}`);
throw err;
}
if (!quiet && !composeFileName) {
logger.logInfo(`No "docker-compose.yml" file found at "${projectRoot}"`);
logger.logInfo(`No composition file found at "${projectRoot}"`);
}
return [composeFileName, composeFileContents];
@ -680,15 +706,17 @@ async function loadBuildMetatada(
export async function getServiceDirsFromComposition(
sourceDir: string,
composition?: Composition,
compositionFile?: string,
): Promise<Dictionary<string>> {
const { createProject } = await import('./compose');
const serviceDirs: Dictionary<string> = {};
if (!composition) {
const [, composeStr] = await resolveProject(
Logger.getLogger(),
sourceDir,
true,
);
const [, composeStr] = await resolveProject({
logger: Logger.getLogger(),
projectRoot: sourceDir,
quiet: true,
compositionFile,
});
if (composeStr) {
composition = createProject(sourceDir, composeStr).composition;
}
@ -1652,6 +1680,9 @@ export const composeCliFlags = {
description:
'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({
description:
'Hide the image build log output (produce less verbose output)',

View File

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