mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-24 07:46:39 +00:00
Feat: Add ability to select a docker-compose file
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>
This commit is contained in:
parent
10ca5b4f59
commit
258beb09a4
@ -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
|
||||
|
@ -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
|
||||
@ -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'],
|
||||
@ -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,
|
||||
@ -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,
|
||||
|
1
lib/utils/compose-types.d.ts
vendored
1
lib/utils/compose-types.d.ts
vendored
@ -53,6 +53,7 @@ export interface TaggedImage {
|
||||
export interface ComposeOpts {
|
||||
convertEol: boolean;
|
||||
dockerfilePath?: string;
|
||||
composefile?: string;
|
||||
inlineLogs?: boolean;
|
||||
multiDockerignore: boolean;
|
||||
noParentCheck: boolean;
|
||||
|
@ -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> {
|
||||
@ -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'],
|
||||
}));
|
||||
|
@ -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) {
|
||||
@ -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,
|
||||
@ -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}"`);
|
||||
@ -1149,6 +1167,7 @@ export async function validateProjectDirectory(
|
||||
sdk: BalenaSDK,
|
||||
opts: {
|
||||
dockerfilePath?: string;
|
||||
composefile?: string;
|
||||
noParentCheck: boolean;
|
||||
projectPath: string;
|
||||
registrySecretsPath?: string;
|
||||
@ -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)),
|
||||
),
|
||||
),
|
||||
|
@ -57,6 +57,7 @@ export interface DeviceDeployOptions {
|
||||
deviceHost: string;
|
||||
devicePort?: number;
|
||||
dockerfilePath?: string;
|
||||
composefile?: string;
|
||||
registrySecrets: RegistrySecrets;
|
||||
multiDockerignore: boolean;
|
||||
nocache: boolean;
|
||||
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user