Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add ability to select a docker-compose file #2760

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading