diff --git a/doc/cli.markdown b/doc/cli.markdown index c2f1ea46..fd2817de 100644 --- a/doc/cli.markdown +++ b/doc/cli.markdown @@ -2926,16 +2926,16 @@ Don't convert line endings from CRLF (Windows format) to LF (Unix format). Have each service use its own .dockerignore file. See "balena help push". -#### -G, --nogitignore - -No-op (default behavior) since balena CLI v12.0.0. See "balena help push". - #### -g, --gitignore Consider .gitignore files in addition to the .dockerignore file. This reverts to the CLI v11 behavior/implementation (deprecated) if compatibility is required until your project can be adapted. +#### -G, --nogitignore + +No-op (default behavior) since balena CLI v12.0.0. See "balena help push". + #### --release-tag RELEASE-TAG Set release tags if the image build is successful (balenaCloud only). Multiple @@ -3159,14 +3159,14 @@ Consider .gitignore files in addition to the .dockerignore file. This reverts to the CLI v11 behavior/implementation (deprecated) if compatibility is required until your project can be adapted. -#### -m, --multi-dockerignore - -Have each service use its own .dockerignore file. See "balena help build". - #### -G, --nogitignore No-op (default behavior) since balena CLI v12.0.0. See "balena help build". +#### -m, --multi-dockerignore + +Have each service use its own .dockerignore file. See "balena help build". + #### --noparent-check Disable project validation check of 'docker-compose.yml' file in parent folder @@ -3398,14 +3398,14 @@ Consider .gitignore files in addition to the .dockerignore file. This reverts to the CLI v11 behavior/implementation (deprecated) if compatibility is required until your project can be adapted. -#### -m, --multi-dockerignore - -Have each service use its own .dockerignore file. See "balena help build". - #### -G, --nogitignore No-op (default behavior) since balena CLI v12.0.0. See "balena help build". +#### -m, --multi-dockerignore + +Have each service use its own .dockerignore file. See "balena help build". + #### --noparent-check Disable project validation check of 'docker-compose.yml' file in parent folder diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 2a2dd365..2b528118 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -266,7 +266,7 @@ ${dockerignoreHelp} inlineLogs: composeOpts.inlineLogs, convertEol: composeOpts.convertEol, dockerfilePath: composeOpts.dockerfilePath, - nogitignore: composeOpts.nogitignore, + nogitignore: composeOpts.nogitignore, // v13: delete this line multiDockerignore: composeOpts.multiDockerignore, }); } diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 2eb9ec5a..75e26185 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -314,7 +314,7 @@ ${dockerignoreHelp} inlineLogs: composeOpts.inlineLogs, convertEol: composeOpts.convertEol, dockerfilePath: composeOpts.dockerfilePath, - nogitignore: composeOpts.nogitignore, + nogitignore: composeOpts.nogitignore, // v13: delete this line multiDockerignore: composeOpts.multiDockerignore, }); builtImagesByService = _.keyBy(builtImages, 'serviceName'); diff --git a/lib/commands/push.ts b/lib/commands/push.ts index 3f8022cb..d9b7622c 100644 --- a/lib/commands/push.ts +++ b/lib/commands/push.ts @@ -47,8 +47,8 @@ interface FlagsDef { pull: boolean; 'noparent-check': boolean; 'registry-secrets'?: string; - gitignore?: boolean; - nogitignore?: boolean; + gitignore?: boolean; // v13: delete this flag + nogitignore?: boolean; // v13: delete this flag nolive: boolean; detached: boolean; service?: string[]; @@ -237,11 +237,20 @@ export default class PushCmd extends Command { 'Have each service use its own .dockerignore file. See "balena help push".', char: 'm', default: false, - exclusive: ['gitignore'], + exclusive: ['gitignore'], // v13: delete this line }), ...(isV13() ? {} : { + gitignore: flags.boolean({ + description: stripIndent` + Consider .gitignore files in addition to the .dockerignore file. This reverts + to the CLI v11 behavior/implementation (deprecated) if compatibility is + required until your project can be adapted.`, + char: 'g', + default: false, + exclusive: ['multi-dockerignore'], + }), nogitignore: flags.boolean({ description: 'No-op (default behavior) since balena CLI v12.0.0. See "balena help push".', @@ -250,15 +259,6 @@ export default class PushCmd extends Command { default: false, }), }), - gitignore: flags.boolean({ - description: stripIndent` - Consider .gitignore files in addition to the .dockerignore file. This reverts - to the CLI v11 behavior/implementation (deprecated) if compatibility is - required until your project can be adapted.`, - char: 'g', - default: false, - exclusive: ['multi-dockerignore'], - }), 'release-tag': flags.string({ description: stripIndent` Set release tags if the image build is successful (balenaCloud only). Multiple @@ -378,7 +378,7 @@ export default class PushCmd extends Command { source: options.source, auth: token, baseUrl, - nogitignore: !options.gitignore, + nogitignore: !options.gitignore, // v13: delete this line sdk, opts, }; @@ -422,7 +422,7 @@ export default class PushCmd extends Command { multiDockerignore: options['multi-dockerignore'], nocache: options.nocache, pull: options.pull, - nogitignore: !options.gitignore, + nogitignore: !options.gitignore, // v13: delete this line noParentCheck: options['noparent-check'], nolive: options.nolive, detached: options.detached, diff --git a/lib/utils/compose-types.d.ts b/lib/utils/compose-types.d.ts index aa6425ed..8ed3c225 100644 --- a/lib/utils/compose-types.d.ts +++ b/lib/utils/compose-types.d.ts @@ -51,7 +51,7 @@ export interface ComposeOpts { dockerfilePath?: string; inlineLogs?: boolean; multiDockerignore: boolean; - nogitignore: boolean; + nogitignore: boolean; // v13: delete this line noParentCheck: boolean; projectName: string; projectPath: string; @@ -63,9 +63,9 @@ export interface ComposeCliFlags { dockerfile?: string; logs: boolean; nologs: boolean; - gitignore: boolean; + gitignore?: boolean; // v13: delete this line + nogitignore?: boolean; // v13: delete this line 'multi-dockerignore': boolean; - nogitignore: boolean; 'noparent-check': boolean; 'registry-secrets'?: RegistrySecrets; 'convert-eol': boolean; @@ -102,6 +102,6 @@ interface TarDirectoryOptions { composition?: Composition; convertEol?: boolean; multiDockerignore?: boolean; - nogitignore: boolean; + nogitignore: boolean; // v13: delete this line preFinalizeCallback?: (pack: Pack) => void | Promise; } diff --git a/lib/utils/compose.js b/lib/utils/compose.js index 615aaa28..2ebffff1 100644 --- a/lib/utils/compose.js +++ b/lib/utils/compose.js @@ -18,6 +18,7 @@ import * as path from 'path'; import { ExpectedError } from '../errors'; import { getChalk } from './lazy'; +import { isV13 } from './version'; /** * @returns Promise<{import('./compose-types').ComposeOpts}> @@ -25,7 +26,7 @@ import { getChalk } from './lazy'; export function generateOpts(options) { const { promises: fs } = require('fs'); - if (options.gitignore && options['multi-dockerignore']) { + if (!isV13() && options.gitignore && options['multi-dockerignore']) { throw new ExpectedError( 'The --gitignore and --multi-dockerignore options cannot be used together', ); @@ -37,7 +38,7 @@ export function generateOpts(options) { convertEol: !options['noconvert-eol'], dockerfilePath: options.dockerfile, multiDockerignore: !!options['multi-dockerignore'], - nogitignore: !options.gitignore, + nogitignore: !options.gitignore, // v13: delete this line noParentCheck: options['noparent-check'], })); } @@ -92,6 +93,8 @@ export function createProject(composePath, composeStr, projectName = null) { * @param {string} dir Source directory * @param {import('./compose-types').TarDirectoryOptions} param * @returns {Promise} + * + * v13: delete this function */ export async function originalTarDirectory(dir, param) { let { diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index 3eb1eb8c..8658e710 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -18,8 +18,9 @@ import { flags } from '@oclif/command'; import { BalenaSDK } from 'balena-sdk'; import type { TransposeOptions } from 'docker-qemu-transpose'; import type * as Dockerode from 'dockerode'; -import * as _ from 'lodash'; import { promises as fs } from 'fs'; +import jsyaml = require('js-yaml'); +import * as _ from 'lodash'; import * as path from 'path'; import type { BuildConfig, @@ -42,8 +43,8 @@ import { import type { DeviceInfo } from './device/api'; import { getBalenaSdk, getChalk, stripIndent } from './lazy'; import Logger = require('./logger'); +import { isV13 } from './version'; import { exists } from './which'; -import jsyaml = require('js-yaml'); const allowedContractTypes = ['sw.application', 'sw.block']; @@ -250,7 +251,7 @@ export interface BuildProjectOpts { inlineLogs?: boolean; convertEol: boolean; dockerfilePath?: string; - nogitignore: boolean; + nogitignore: boolean; // v13: delete this line multiDockerignore: boolean; } @@ -737,8 +738,8 @@ export async function tarDirectory( dir: string, param: TarDirectoryOptions, ): Promise { - const { nogitignore = false } = param; - if (nogitignore) { + const { nogitignore = false } = param; // v13: delete this line + if (isV13() || nogitignore) { return newTarDirectory(dir, param); } else { return (await import('./compose')).originalTarDirectory(dir, param); @@ -759,11 +760,13 @@ async function newTarDirectory( composition, convertEol = false, multiDockerignore = false, - nogitignore = false, + nogitignore = false, // v13: delete this line preFinalizeCallback, }: TarDirectoryOptions, ): Promise { - require('assert').strict.equal(nogitignore, true); + if (!isV13()) { + require('assert').strict.equal(nogitignore, true); + } const { filterFilesWithDockerignore } = await import('./ignore'); const { toPosixPath } = (await import('resin-multibuild')).PathUtils; @@ -879,7 +882,8 @@ function printDockerignoreWarn( } } if (msg.length) { - logFunc.call(logger, [' ', hr, ...msg, hr].join('\n')); + const { warnify } = require('./messages') as typeof import('./messages'); + logFunc.call(logger, ' \n' + warnify(msg.join('\n'), '')); } } @@ -888,11 +892,16 @@ function printDockerignoreWarn( * found and the --gitignore (-g) option has been provided (v11 compatibility). * @param dockerignoreFile Absolute path to a .dockerignore file * @param gitignoreFiles Array of absolute paths to .gitginore files + * + * v13: delete this function */ export function printGitignoreWarn( dockerignoreFile: string, gitignoreFiles: string[], ) { + if (isV13()) { + return; + } const ignoreFiles = [dockerignoreFile, ...gitignoreFiles].filter((e) => e); if (ignoreFiles.length === 0) { return; @@ -1619,22 +1628,26 @@ export const composeCliFlags: flags.Input = { description: 'Hide the image build log output (produce less verbose output)', }), - gitignore: flags.boolean({ - description: stripIndent` - Consider .gitignore files in addition to the .dockerignore file. This reverts - to the CLI v11 behavior/implementation (deprecated) if compatibility is required - until your project can be adapted.`, - char: 'g', - }), + ...(isV13() + ? {} + : { + gitignore: flags.boolean({ + description: stripIndent` + Consider .gitignore files in addition to the .dockerignore file. This reverts + to the CLI v11 behavior/implementation (deprecated) if compatibility is required + until your project can be adapted.`, + char: 'g', + }), + nogitignore: flags.boolean({ + description: `No-op (default behavior) since balena CLI v12.0.0. See "balena help build".`, + char: 'G', + }), + }), 'multi-dockerignore': flags.boolean({ description: 'Have each service use its own .dockerignore file. See "balena help build".', char: 'm', }), - nogitignore: flags.boolean({ - description: `No-op (default behavior) since balena CLI v12.0.0. See "balena help build".`, - char: 'G', - }), 'noparent-check': flags.boolean({ description: "Disable project validation check of 'docker-compose.yml' file in parent folder", diff --git a/lib/utils/device/deploy.ts b/lib/utils/device/deploy.ts index 57b7225b..0f85d7d7 100644 --- a/lib/utils/device/deploy.ts +++ b/lib/utils/device/deploy.ts @@ -57,7 +57,7 @@ export interface DeviceDeployOptions { registrySecrets: RegistrySecrets; multiDockerignore: boolean; nocache: boolean; - nogitignore: boolean; + nogitignore: boolean; // v13: delete this line noParentCheck: boolean; nolive: boolean; pull: boolean; @@ -182,7 +182,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { convertEol: opts.convertEol, dockerfilePath: opts.dockerfilePath, multiDockerignore: opts.multiDockerignore, - nogitignore: opts.nogitignore, + nogitignore: opts.nogitignore, // v13: delete this line noParentCheck: opts.noParentCheck, projectName: 'local', projectPath: opts.source, @@ -201,7 +201,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { composition: project.composition, convertEol: opts.convertEol, multiDockerignore: opts.multiDockerignore, - nogitignore: opts.nogitignore, + nogitignore: opts.nogitignore, // v13: delete this line }); // Try to detect the device information @@ -426,7 +426,7 @@ export async function rebuildSingleTask( composition, convertEol: opts.convertEol, multiDockerignore: opts.multiDockerignore, - nogitignore: opts.nogitignore, + nogitignore: opts.nogitignore, // v13: delete this line }); const task = _.find( diff --git a/lib/utils/ignore.ts b/lib/utils/ignore.ts index 7823a3a8..69dcbdad 100644 --- a/lib/utils/ignore.ts +++ b/lib/utils/ignore.ts @@ -27,6 +27,7 @@ import { ExpectedError } from '../errors'; const { toPosixPath } = MultiBuild.PathUtils; +// v13: delete this enum export enum IgnoreFileType { DockerIgnore, GitIgnore, @@ -42,6 +43,8 @@ interface IgnoreEntry { * This class is used by the CLI v10 / v11 "original" tarDirectory function * in `compose.js`. It is still around for the benefit of the `--gitignore` * option, but is expected to be deleted in CLI v13. + * + * v13: delete this class */ export class FileIgnorer { private dockerIgnoreEntries: IgnoreEntry[]; diff --git a/lib/utils/messages.ts b/lib/utils/messages.ts index 979a14ba..36821262 100644 --- a/lib/utils/messages.ts +++ b/lib/utils/messages.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { isV13 } from './version'; + export const reachingOut = `\ For further help or support, visit: https://www.balena.io/docs/reference/balena-cli/#support-faq-and-troubleshooting @@ -48,7 +50,8 @@ used. Find out more at: https://git.io/JRHUW#deprecation-policy * where the length of the dash rows matches the length of the longest line. */ export function warnify(msg: string, prefix = '[Warn] ') { - const lines = msg.split('\n').map((l) => `${prefix}${l}`); + let lines = msg.split('\n'); + lines = prefix ? lines.map((l) => `${prefix}${l}`) : lines; const maxLength = Math.max(...lines.map((l) => l.length)); const hr = '-'.repeat(maxLength); return [hr, ...lines, hr].join('\n'); @@ -85,7 +88,7 @@ If the --registry-secrets option is not specified, and a secrets.yml or secrets.json file exists in the balena directory (usually $HOME/.balena), this file will be used instead.`; -export const dockerignoreHelp = +const dockerignoreHelpV12 = 'DOCKERIGNORE AND GITIGNORE FILES \n' + `By default, the balena CLI will use a single ".dockerignore" file (if any) at the project root (--source directory) in order to decide which source files to @@ -138,6 +141,60 @@ adding counter patterns to the applicable .dockerignore file(s), for example - https://docs.docker.com/engine/reference/builder/#dockerignore-file - https://www.npmjs.com/package/@balena/dockerignore`; +const dockerignoreHelpV13 = + 'DOCKERIGNORE AND GITIGNORE FILES \n' + + `By default, the balena CLI will use a single ".dockerignore" file (if any) at +the project root (--source directory) in order to decide which source files to +exclude from the "build context" (tar stream) sent to balenaCloud, Docker +daemon or balenaEngine. In a microservices (multicontainer) fleet, the +source directory is the directory that contains the "docker-compose.yml" file. + +The --multi-dockerignore (-m) option may be used with microservices +(multicontainer) fleets that define a docker-compose.yml file. When this +option is used, each service subdirectory (defined by the \`build\` or +\`build.context\` service properties in the docker-compose.yml file) is +filtered separately according to a .dockerignore file defined in the service +subdirectory. If no .dockerignore file exists in a service subdirectory, then +only the default .dockerignore patterns (see below) apply for that service +subdirectory. + +When the --multi-dockerignore (-m) option is used, the .dockerignore file (if +any) defined at the overall project root will be used to filter files and +subdirectories other than service subdirectories. It will not have any effect +on service subdirectories, whether or not a service subdirectory defines its +own .dockerignore file. Multiple .dockerignore files are not merged or added +together, and cannot override or extend other files. This behavior maximizes +compatibility with the standard docker-compose tool, while still allowing a +root .dockerignore file (at the overall project root) to filter files and +folders that are outside service subdirectories. + +balena CLI v11 also took .gitignore files into account. This behavior was +deprecated in CLI v12 and removed in CLI v13. Please use .dockerignore files +instead. + +Default .dockerignore patterns \n` + + `A few default/hardcoded dockerignore patterns are "merged" (in memory) with the +patterns found in the applicable .dockerignore files, in the following order: +\`\`\` + **/.git + < user's patterns from the applicable '.dockerignore' file, if any > + !**/.balena + !**/.resin + !**/Dockerfile + !**/Dockerfile.* + !**/docker-compose.yml +\`\`\` +These patterns always apply, whether or not .dockerignore files exist in the +project. If necessary, the effect of the \`**/.git\` pattern may be modified by +adding exception patterns to the applicable .dockerignore file(s), for example +\`!mysubmodule/.git\`. For documentation on pattern format, see: +- https://docs.docker.com/engine/reference/builder/#dockerignore-file +- https://www.npmjs.com/package/@balena/dockerignore`; + +export const dockerignoreHelp = isV13() + ? dockerignoreHelpV13 + : dockerignoreHelpV12; + export const applicationIdInfo = `\ Fleets may be specified by fleet name, slug, or numeric ID. Fleet slugs are the recommended option, as they are unique and unambiguous. Slugs can be diff --git a/lib/utils/remote-build.ts b/lib/utils/remote-build.ts index 5853f774..3d699d52 100644 --- a/lib/utils/remote-build.ts +++ b/lib/utils/remote-build.ts @@ -50,7 +50,7 @@ export interface RemoteBuild { source: string; auth: string; baseUrl: string; - nogitignore: boolean; + nogitignore: boolean; // v13: delete this line opts: BuildOpts; sdk: BalenaSDK; // For internal use @@ -321,7 +321,7 @@ async function getTarStream(build: RemoteBuild): Promise { preFinalizeCallback: preFinalizeCb, convertEol: build.opts.convertEol, multiDockerignore: build.opts.multiDockerignore, - nogitignore: build.nogitignore, + nogitignore: build.nogitignore, // v13: delete this line }); } finally { tarSpinner.stop(); diff --git a/tests/commands/build.spec.ts b/tests/commands/build.spec.ts index 87f0065e..75ac7df5 100644 --- a/tests/commands/build.spec.ts +++ b/tests/commands/build.spec.ts @@ -21,7 +21,8 @@ import mock = require('mock-require'); import { promises as fs } from 'fs'; import * as path from 'path'; -import { stripIndent } from '../../lib/utils/lazy'; +import { stripIndent } from '../../build/utils/lazy'; +import { isV13 } from '../../build/utils/version'; import { BalenaAPIMock } from '../nock/balena-api-mock'; import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build'; import { DockerMock, dockerResponsePath } from '../nock/docker-mock'; @@ -61,9 +62,6 @@ const commonComposeQueryParams = { labels: '', }; -const hr = - '----------------------------------------------------------------------'; - // "itSS" means "it() Skip Standalone" const itSS = process.env.BALENA_CLI_TEST_TYPE === 'standalone' ? it.skip : it; @@ -126,7 +124,9 @@ describe('balena build', function () { } docker.expectGetInfo({}); await testDockerBuildStream({ - commandLine: `build ${projectPath} --deviceType nuc --arch amd64 -g`, + commandLine: `build ${projectPath} --deviceType nuc --arch amd64 ${ + isV13() ? '' : '-g' + }`, dockerMock: docker, expectedFilesByService: { main: expectedFiles }, expectedQueryParamsByService: { main: Object.entries(commonQueryParams) }, @@ -272,7 +272,9 @@ describe('balena build', function () { mock.reRequire('../../build/utils/qemu'); docker.expectGetInfo({ OperatingSystem: 'balenaOS 2.44.0+rev1' }); await testDockerBuildStream({ - commandLine: `build ${projectPath} --emulated --deviceType ${deviceType} --arch ${arch} --nogitignore`, + commandLine: `build ${projectPath} --emulated --deviceType ${deviceType} --arch ${arch} ${ + isV13() ? '' : '--nogitignore' + }`, dockerMock: docker, expectedFilesByService: { main: expectedFiles }, expectedQueryParamsByService: { @@ -416,7 +418,9 @@ describe('balena build', function () { } docker.expectGetInfo({}); await testDockerBuildStream({ - commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol -G -B COMPOSE_ARG=A -B barg=b --cache-from my/img1,my/img2`, + commandLine: `build ${projectPath} --deviceType nuc --arch amd64 --convert-eol ${ + isV13() ? '' : '-G' + } -B COMPOSE_ARG=A -B barg=b --cache-from my/img1,my/img2`, dockerMock: docker, expectedFilesByService, expectedQueryParamsByService, @@ -484,11 +488,11 @@ describe('balena build', function () { '[Build] service2 Step 1/4 : FROM busybox', ], ...[ - `[Info] ${hr}`, + `[Info] ---------------------------------------------------------------------------`, '[Info] The --multi-dockerignore option is being used, and a .dockerignore file was', '[Info] found at the project source (root) directory. Note that this file will not', '[Info] be used to filter service subdirectories. See "balena help build".', - `[Info] ${hr}`, + `[Info] ---------------------------------------------------------------------------`, ], ]; if (isWindows) { diff --git a/tests/commands/deploy.spec.ts b/tests/commands/deploy.spec.ts index 63a1d10b..a378090f 100644 --- a/tests/commands/deploy.spec.ts +++ b/tests/commands/deploy.spec.ts @@ -23,6 +23,7 @@ import * as nock from 'nock'; import * as path from 'path'; import * as sinon from 'sinon'; +import { isV13 } from '../../build/utils/version'; import { BalenaAPIMock } from '../nock/balena-api-mock'; import { expectStreamNoCRLF, testDockerBuildStream } from '../docker-build'; import { DockerMock, dockerResponsePath } from '../nock/docker-mock'; @@ -66,9 +67,6 @@ const commonComposeQueryParams = { labels: '', }; -const hr = - '----------------------------------------------------------------------'; - describe('balena deploy', function () { let api: BalenaAPIMock; let docker: DockerMock; @@ -143,7 +141,9 @@ describe('balena deploy', function () { api.expectPostImageLabel(); await testDockerBuildStream({ - commandLine: `deploy testApp --build --source ${projectPath} -G`, + commandLine: `deploy testApp --build --source ${projectPath} ${ + isV13() ? '' : '-G' + }`, dockerMock: docker, expectedFilesByService: { main: expectedFiles }, expectedQueryParamsByService: { main: commonQueryParams }, @@ -304,7 +304,9 @@ describe('balena deploy', function () { sinon.stub(process, 'exit'); await testDockerBuildStream({ - commandLine: `deploy testApp --build --source ${projectPath} --noconvert-eol -G`, + commandLine: `deploy testApp --build --source ${projectPath} --noconvert-eol ${ + isV13() ? '' : '-G' + }`, dockerMock: docker, expectedFilesByService: { main: expectedFiles }, expectedQueryParamsByService: { main: commonQueryParams }, @@ -385,11 +387,11 @@ describe('balena deploy', function () { '[Build] service2 Step 1/4 : FROM busybox', ], ...[ - `[Info] ${hr}`, + `[Info] ---------------------------------------------------------------------------`, '[Info] The --multi-dockerignore option is being used, and a .dockerignore file was', '[Info] found at the project source (root) directory. Note that this file will not', '[Info] be used to filter service subdirectories. See "balena help deploy".', - `[Info] ${hr}`, + `[Info] ---------------------------------------------------------------------------`, ], ]; if (isWindows) { diff --git a/tests/commands/push.spec.ts b/tests/commands/push.spec.ts index 467f8ed6..74fca89f 100644 --- a/tests/commands/push.spec.ts +++ b/tests/commands/push.spec.ts @@ -19,6 +19,7 @@ import { expect } from 'chai'; import { promises as fs } from 'fs'; import * as path from 'path'; +import { isV13 } from '../../build/utils/version'; import { BalenaAPIMock } from '../nock/balena-api-mock'; import { BuilderMock, builderResponsePath } from '../nock/builder-mock'; import { expectStreamNoCRLF, testPushBuildStream } from '../docker-build'; @@ -34,6 +35,8 @@ import { const repoPath = path.normalize(path.join(__dirname, '..', '..')); const projectsPath = path.join(repoPath, 'tests', 'test-data', 'projects'); +const itNoV13 = isV13() ? it.skip : it; + const commonResponseLines = { 'build-POST-v3.json': [ '[Info] Starting build for testApp, user gh_user', @@ -234,71 +237,74 @@ describe('balena push', function () { }); }); - it('should create the expected tar stream (single container, --gitignore)', async () => { - const projectPath = path.join( - projectsPath, - 'no-docker-compose', - 'dockerignore1', - ); - const expectedFiles: ExpectedTarStreamFiles = { - '.balena/balena.yml': { fileSize: 12, type: 'file' }, - '.dockerignore': { fileSize: 438, type: 'file' }, - '.gitignore': { fileSize: 20, type: 'file' }, - '.git/bar.txt': { fileSize: 4, type: 'file' }, - '.git/foo.txt': { fileSize: 4, type: 'file' }, - 'c.txt': { fileSize: 1, type: 'file' }, - Dockerfile: { fileSize: 13, type: 'file' }, - 'src/.balena/balena.yml': { fileSize: 16, type: 'file' }, - 'src/.gitignore': { fileSize: 10, type: 'file' }, - 'vendor/.git/vendor-git-contents': { fileSize: 20, type: 'file' }, - // When --gitignore (-g) is provided for v11 compatibility, the old - // `zeit/dockerignore` npm package is still used but it is broken on - // Windows (reason why we created `@balena/dockerignore`). - ...(isWindows - ? { - 'src/src-b.txt': { fileSize: 5, type: 'file' }, - 'dot.git/bar.txt': { fileSize: 4, type: 'file' }, - 'dot.git/foo.txt': { fileSize: 4, type: 'file' }, - 'vendor/dot.git/vendor-git-contents': { - fileSize: 20, - type: 'file', - }, - } - : {}), - }; + itNoV13( + 'should create the expected tar stream (single container, --gitignore)', + async () => { + const projectPath = path.join( + projectsPath, + 'no-docker-compose', + 'dockerignore1', + ); + const expectedFiles: ExpectedTarStreamFiles = { + '.balena/balena.yml': { fileSize: 12, type: 'file' }, + '.dockerignore': { fileSize: 438, type: 'file' }, + '.gitignore': { fileSize: 20, type: 'file' }, + '.git/bar.txt': { fileSize: 4, type: 'file' }, + '.git/foo.txt': { fileSize: 4, type: 'file' }, + 'c.txt': { fileSize: 1, type: 'file' }, + Dockerfile: { fileSize: 13, type: 'file' }, + 'src/.balena/balena.yml': { fileSize: 16, type: 'file' }, + 'src/.gitignore': { fileSize: 10, type: 'file' }, + 'vendor/.git/vendor-git-contents': { fileSize: 20, type: 'file' }, + // When --gitignore (-g) is provided for v11 compatibility, the old + // `zeit/dockerignore` npm package is still used but it is broken on + // Windows (reason why we created `@balena/dockerignore`). + ...(isWindows + ? { + 'src/src-b.txt': { fileSize: 5, type: 'file' }, + 'dot.git/bar.txt': { fileSize: 4, type: 'file' }, + 'dot.git/foo.txt': { fileSize: 4, type: 'file' }, + 'vendor/dot.git/vendor-git-contents': { + fileSize: 20, + type: 'file', + }, + } + : {}), + }; - const regSecretsPath = await addRegSecretsEntries(expectedFiles); - const responseFilename = 'build-POST-v3.json'; - const responseBody = await fs.readFile( - path.join(builderResponsePath, responseFilename), - 'utf8', - ); - const expectedResponseLines = [ - ...[ - `[Warn] ${hr}`, - '[Warn] Using file ignore patterns from:', - `[Warn] * ${path.join(projectPath, '.dockerignore')}`, - `[Warn] * ${path.join(projectPath, '.gitignore')}`, - `[Warn] * ${path.join(projectPath, 'src', '.gitignore')}`, - '[Warn] .gitignore files are being considered because the --gitignore option was used.', - '[Warn] This option is deprecated and will be removed in the next major version release.', - "[Warn] For more information, see 'balena help push'.", - `[Warn] ${hr}`, - ], - ...commonResponseLines[responseFilename], - ]; + const regSecretsPath = await addRegSecretsEntries(expectedFiles); + const responseFilename = 'build-POST-v3.json'; + const responseBody = await fs.readFile( + path.join(builderResponsePath, responseFilename), + 'utf8', + ); + const expectedResponseLines = [ + ...[ + `[Warn] ${hr}`, + '[Warn] Using file ignore patterns from:', + `[Warn] * ${path.join(projectPath, '.dockerignore')}`, + `[Warn] * ${path.join(projectPath, '.gitignore')}`, + `[Warn] * ${path.join(projectPath, 'src', '.gitignore')}`, + '[Warn] .gitignore files are being considered because the --gitignore option was used.', + '[Warn] This option is deprecated and will be removed in the next major version release.', + "[Warn] For more information, see 'balena help push'.", + `[Warn] ${hr}`, + ], + ...commonResponseLines[responseFilename], + ]; - await testPushBuildStream({ - builderMock: builder, - commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} -g`, - expectedFiles, - expectedQueryParams: commonQueryParams, - expectedResponseLines, - projectPath, - responseBody, - responseCode: 200, - }); - }); + await testPushBuildStream({ + builderMock: builder, + commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} -g`, + expectedFiles, + expectedQueryParams: commonQueryParams, + expectedResponseLines, + projectPath, + responseBody, + responseCode: 200, + }); + }, + ); it('should create the expected tar stream (single container, --nogitignore)', async () => { const projectPath = path.join( @@ -370,24 +376,27 @@ describe('balena push', function () { path.join(builderResponsePath, responseFilename), 'utf8', ); - const expectedResponseLines = isWindows - ? [ - `[Warn] ${hr}`, - '[Warn] Using file ignore patterns from:', - `[Warn] * ${path.join(projectPath, '.dockerignore')}`, - '[Warn] The --gitignore option was used, but no .gitignore files were found.', - '[Warn] The --gitignore option is deprecated and will be removed in the next major', - '[Warn] version release. It prevents the use of a better dockerignore parser and', - '[Warn] filter library that fixes several issues on Windows and improves compatibility', - "[Warn] with 'docker build'. For more information, see 'balena help push'.", - `[Warn] ${hr}`, - ...commonResponseLines[responseFilename], - ] - : commonResponseLines[responseFilename]; + const expectedResponseLines = + !isV13() && isWindows + ? [ + `[Warn] ${hr}`, + '[Warn] Using file ignore patterns from:', + `[Warn] * ${path.join(projectPath, '.dockerignore')}`, + '[Warn] The --gitignore option was used, but no .gitignore files were found.', + '[Warn] The --gitignore option is deprecated and will be removed in the next major', + '[Warn] version release. It prevents the use of a better dockerignore parser and', + '[Warn] filter library that fixes several issues on Windows and improves compatibility', + "[Warn] with 'docker build'. For more information, see 'balena help push'.", + `[Warn] ${hr}`, + ...commonResponseLines[responseFilename], + ] + : commonResponseLines[responseFilename]; await testPushBuildStream({ builderMock: builder, - commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} --gitignore`, + commandLine: `push testApp -s ${projectPath} -R ${regSecretsPath} ${ + isV13() ? '' : '--gitignore' + }`, expectedFiles, expectedQueryParams: commonQueryParams, expectedResponseLines, @@ -516,11 +525,11 @@ describe('balena push', function () { const expectedResponseLines: string[] = [ ...commonResponseLines[responseFilename], ...[ - `[Info] ${hr}`, + `[Info] ---------------------------------------------------------------------------`, '[Info] The --multi-dockerignore option is being used, and a .dockerignore file was', '[Info] found at the project source (root) directory. Note that this file will not', '[Info] be used to filter service subdirectories. See "balena help push".', - `[Info] ${hr}`, + `[Info] ---------------------------------------------------------------------------`, ], ]; if (isWindows) { @@ -571,7 +580,7 @@ describe('balena push: project validation', function () { ]; const { out, err } = await runCommand( - `push testApp --source ${projectPath} --gitignore`, + `push testApp --source ${projectPath}`, ); expect(cleanOutput(err, true)).to.include.members(expectedErrorLines); expect(out).to.be.empty; diff --git a/tests/helpers.ts b/tests/helpers.ts index 3beb64b4..3c2075d7 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -105,7 +105,7 @@ async function runCommandInProcess(cmd: string): Promise { const unhookIntercept = intercept(stdoutHook, stderrHook); try { - await balenaCLI.run(preArgs.concat(cmd.split(' ')), { + await balenaCLI.run(preArgs.concat(cmd.split(' ').filter((c) => c)), { noFlush: true, }); } finally { @@ -160,7 +160,7 @@ async function runCommandInSubprocess( await new Promise((resolve) => { const child = execFile( standalonePath, - cmd.split(' '), + cmd.split(' ').filter((c) => c), { env: { ...process.env, ...addedEnvs } }, ($error, $stdout, $stderr) => { stderr = $stderr || ''; diff --git a/tests/projects.ts b/tests/projects.ts index 80639b36..ef9f0c73 100644 --- a/tests/projects.ts +++ b/tests/projects.ts @@ -86,7 +86,6 @@ export async function addRegSecretsEntries( export function getDockerignoreWarn1(paths: string[], cmd: string) { const lines = [ - '[Warn] ----------------------------------------------------------------------', '[Warn] The following .dockerignore file(s) will not be used:', ]; lines.push(...paths.map((p) => `[Warn] * ${p}`)); @@ -96,7 +95,6 @@ export function getDockerignoreWarn1(paths: string[], cmd: string) { '[Warn] root) is used. Microservices (multicontainer) fleets may use a separate', '[Warn] .dockerignore file for each service with the --multi-dockerignore (-m)', `[Warn] option. See "balena help ${cmd}" for more details.`, - '[Warn] ----------------------------------------------------------------------', ], ); return lines; @@ -104,7 +102,6 @@ export function getDockerignoreWarn1(paths: string[], cmd: string) { export function getDockerignoreWarn2(paths: string[], cmd: string) { const lines = [ - '[Warn] ----------------------------------------------------------------------', '[Warn] The following .dockerignore file(s) will not be used:', ]; lines.push(...paths.map((p) => `[Warn] * ${p}`)); @@ -114,7 +111,6 @@ export function getDockerignoreWarn2(paths: string[], cmd: string) { "[Warn] root of each service's build context (in a microservices/multicontainer", '[Warn] fleet), plus a .dockerignore file at the overall project root, are used.', `[Warn] See "balena help ${cmd}" for more details.`, - '[Warn] ----------------------------------------------------------------------', ], ); return lines; diff --git a/tests/utils/ignore.spec.ts b/tests/utils/ignore.spec.ts index 76dc733a..6c035249 100644 --- a/tests/utils/ignore.spec.ts +++ b/tests/utils/ignore.spec.ts @@ -7,6 +7,9 @@ import { FileIgnorer, IgnoreFileType } from '../../build/utils/ignore'; // of the FileIgnorer class to prevent a Typescript compilation error (this // behavior is by design: see // https://github.com/microsoft/TypeScript/issues/19335 ) +// +// v13: delete this file +// describe('File ignorer', function () { it('should detect ignore files', function () { const f = new FileIgnorer(`.${path.sep}`);