Skip to content

Commit

Permalink
Feat: Add ability to select a docker-compose file
Browse files Browse the repository at this point in the history
Allow using the --composefile argument with the push command to specify the compose file

Change-type: minor

Signed-off-by: Quentin Guillemot <quentin.guillemot@cyclair.fr>
  • Loading branch information
cyclair-tech committed Apr 28, 2024
1 parent 10ca5b4 commit 258beb0
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 14 deletions.
5 changes: 5 additions & 0 deletions docs/balena-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,11 @@ suspected issues with the balenaCloud backend.

Alternative Dockerfile name/path, relative to the source folder

#### --composefile COMPOSEFILE

Alternative compose file name/path, relative to the source folder.
Only works for local devices

#### -c, --nocache

Don't use cached layers of previously built images for this project. This
Expand Down
12 changes: 12 additions & 0 deletions lib/commands/push/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ export default class PushCmd extends Command {
description:
'Alternative Dockerfile name/path, relative to the source folder',
}),
composefile: Flags.string({
description: stripIndent`
Alternative compose file name/path, relative to the source folder.
Only works for local devices`,
}),
nocache: Flags.boolean({
description: stripIndent`
Don't use cached layers of previously built images for this project. This
Expand Down Expand Up @@ -238,6 +243,7 @@ export default class PushCmd extends Command {
sdk,
{
dockerfilePath: options.dockerfile,
composefile: options.composefile,
noParentCheck: options['noparent-check'],
projectPath: options.source,
registrySecretsPath: options['registry-secrets'],
Expand All @@ -248,6 +254,11 @@ export default class PushCmd extends Command {
case BuildTarget.Cloud:
logger.logDebug(`Pushing to cloud for fleet: ${params.fleetOrDevice}`);

if (options.composefile) {
throw new Error(stripIndent`
The use of a compose file is not permitted for cloud builds.`);
}

await this.pushToCloud(
params.fleetOrDevice,
options,
Expand Down Expand Up @@ -363,6 +374,7 @@ export default class PushCmd extends Command {
source: options.source,
deviceHost: localDeviceAddress,
dockerfilePath,
composefile: options.composefile,
registrySecrets,
multiDockerignore: options['multi-dockerignore'],
nocache: options.nocache,
Expand Down
1 change: 1 addition & 0 deletions lib/utils/compose-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface TaggedImage {
export interface ComposeOpts {
convertEol: boolean;
dockerfilePath?: string;
composefile?: string;
inlineLogs?: boolean;
multiDockerignore: boolean;
noParentCheck: boolean;
Expand Down
2 changes: 2 additions & 0 deletions lib/utils/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function generateOpts(options: {
nologs: boolean;
'noconvert-eol': boolean;
dockerfile?: string;
composefile?: string;
'multi-dockerignore': boolean;
'noparent-check': boolean;
}): Promise<ComposeOpts> {
Expand All @@ -48,6 +49,7 @@ export function generateOpts(options: {
inlineLogs: !options.nologs,
convertEol: !options['noconvert-eol'],
dockerfilePath: options.dockerfile,
composefile: options.composefile,
multiDockerignore: !!options['multi-dockerignore'],
noParentCheck: options['noparent-check'],
}));
Expand Down
63 changes: 49 additions & 14 deletions lib/utils/compose_ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ 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,
opts.projectPath,
false,
opts.composefile,
);

if (composeName) {
if (opts.dockerfilePath) {
Expand All @@ -143,8 +148,9 @@ export async function loadProject(
composeStr = compose.defaultComposition(undefined, opts.dockerfilePath);
}

// If local push, merge dev compose overlay
if (opts.isLocal) {
// If local push and no specific compose file has been provided,
// merge dev compose overlay
if (opts.isLocal && !opts.composefile) {
composeStr = await mergeDevComposeOverlay(
logger,
composeStr,
Expand Down Expand Up @@ -206,10 +212,22 @@ async function resolveProject(
logger: Logger,
projectRoot: string,
quiet = false,
specificComposeName?: string,
): Promise<[string, string]> {
let composeFileName = '';
let composeFileContents = '';
for (const fname of compositionFileNames) {

let compositionFileNamesLocal: string[] = [];
if (specificComposeName) {
compositionFileNamesLocal = [specificComposeName];
logger.logInfo(
`Using specified "${specificComposeName}" file in "${projectRoot}"`,
);
} else {
compositionFileNamesLocal = compositionFileNames;
}

for (const fname of compositionFileNamesLocal) {
const fpath = path.join(projectRoot, fname);
if (await exists(fpath)) {
logger.logDebug(`${fname} file found at "${projectRoot}"`);
Expand Down Expand Up @@ -1149,6 +1167,7 @@ export async function validateProjectDirectory(
sdk: BalenaSDK,
opts: {
dockerfilePath?: string;
composefile?: string;
noParentCheck: boolean;
projectPath: string;
registrySecretsPath?: string;
Expand All @@ -1175,21 +1194,37 @@ export async function validateProjectDirectory(
);
} else {
const files = await fs.readdir(opts.projectPath);
const projectMatch = (file: string) =>
/^(Dockerfile|Dockerfile\.\S+|docker-compose.ya?ml|package.json)$/.test(
file,
);
if (!_.some(files, projectMatch)) {
throw new ExpectedError(stripIndent`
Error: no "Dockerfile[.*]", "docker-compose.yml" or "package.json" file
found in source folder "${opts.projectPath}"
`);
const projectMatch = (file: string, composefile?: string) => {
let regexPattern =
/^(Dockerfile|Dockerfile\.\S+|docker-compose.ya?ml|package(\.json)?)$/;
if (composefile) {
regexPattern = new RegExp(
`^(Dockerfile|Dockerfile\\.\\S+|docker-compose.ya?ml|${composefile}|package(\\.json)?)$`,
);
}
return regexPattern.test(file);
};
if (!_.some(files, (file) => projectMatch(file, opts.composefile))) {
if (opts.composefile) {
throw new ExpectedError(stripIndent`
Error: no "${opts.composefile}" file
found in source folder "${opts.projectPath}"
`);
} else {
throw new ExpectedError(stripIndent`
Error: no "Dockerfile[.*]", "docker-compose.yml" or "package.json" file
found in source folder "${opts.projectPath}"
`);
}
}
if (!opts.noParentCheck) {
const checkCompose = async (folder: string) => {
const compositionFileNamesLocal = opts.composefile
? [opts.composefile]
: compositionFileNames;
return _.some(
await Promise.all(
compositionFileNames.map((filename) =>
compositionFileNamesLocal.map((filename) =>
exists(path.join(folder, filename)),
),
),
Expand Down
2 changes: 2 additions & 0 deletions lib/utils/device/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface DeviceDeployOptions {
deviceHost: string;
devicePort?: number;
dockerfilePath?: string;
composefile?: string;
registrySecrets: RegistrySecrets;
multiDockerignore: boolean;
nocache: boolean;
Expand Down Expand Up @@ -183,6 +184,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
const project = await loadProject(globalLogger, {
convertEol: opts.convertEol,
dockerfilePath: opts.dockerfilePath,
composefile: opts.composefile,
multiDockerignore: opts.multiDockerignore,
noParentCheck: opts.noParentCheck,
projectName: 'local',
Expand Down

0 comments on commit 258beb0

Please sign in to comment.