diff --git a/.github/workflows/build-util-images.yml b/.github/workflows/build-util-images.yml index 4110918c0..40d315f5d 100644 --- a/.github/workflows/build-util-images.yml +++ b/.github/workflows/build-util-images.yml @@ -3,9 +3,9 @@ name: Build Utility Images on: # Uncomment below for testing purposes pull_request: - push: - branches: - - main + #push: + # branches: + # - main jobs: buildx: @@ -38,6 +38,7 @@ jobs: run: echo "tag-suffix=-edge" >> $GITHUB_OUTPUT - name: Login to DockerHub uses: docker/login-action@v2 + if: ${{ github.event_name != 'pull_request' }} with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -47,7 +48,7 @@ jobs: context: ${{ matrix.context }} file: ${{ matrix.dockerfile }} platforms: linux/amd64,linux/arm64 - push: true + push: ${{ github.event_name != 'pull_request' }} tags: devwithlando/${{ matrix.image }}:${{ matrix.tag }}${{ steps.pr.outputs.tag-suffix }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 603e1ddb6..c0909fc33 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -1,10 +1,7 @@ name: Create Dev Releases on: - push: - branches: - - main - - 'preview-**' + workflow_call: jobs: package: diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 4d0f3edae..405221f95 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -14,6 +14,7 @@ jobs: leia-test: - badname - build + - c,o,m,m,a - cache - certs - config @@ -93,7 +94,7 @@ jobs: - name: Install pkg dependencies run: npm clean-install --prefer-offline --frozen-lockfile --production - name: Package into node binary - uses: lando/pkg-action@v5 + uses: lando/pkg-action@v6 id: pkg-action with: entrypoint: bin/lando diff --git a/.github/workflows/pr-docs-tests.yml b/.github/workflows/pr-docs-tests.yml index 78afe9586..e3dce4b1e 100644 --- a/.github/workflows/pr-docs-tests.yml +++ b/.github/workflows/pr-docs-tests.yml @@ -66,7 +66,7 @@ jobs: - name: Install pkg dependencies run: npm clean-install --prefer-offline --frozen-lockfile --production - name: Package into node binary - uses: lando/pkg-action@v5 + uses: lando/pkg-action@v6 id: pkg-action with: entrypoint: bin/lando diff --git a/.github/workflows/pr-setup-linux-tests.yml b/.github/workflows/pr-setup-linux-tests.yml index cec2665f7..fa9ec363a 100644 --- a/.github/workflows/pr-setup-linux-tests.yml +++ b/.github/workflows/pr-setup-linux-tests.yml @@ -36,7 +36,7 @@ jobs: - name: Install pkg dependencies run: npm clean-install --prefer-offline --frozen-lockfile --production - name: Package into node binary - uses: lando/pkg-action@v5 + uses: lando/pkg-action@v6 id: pkg-action with: entrypoint: bin/lando diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml index 4f2f2c4cc..5ddbe500a 100644 --- a/.github/workflows/pr-setup-macos-tests.yml +++ b/.github/workflows/pr-setup-macos-tests.yml @@ -37,7 +37,7 @@ jobs: - name: Install pkg dependencies run: npm clean-install --prefer-offline --frozen-lockfile --production - name: Package into node binary - uses: lando/pkg-action@v5 + uses: lando/pkg-action@v6 id: pkg-action with: entrypoint: bin/lando diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index fa076c06c..822c89381 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -35,7 +35,7 @@ jobs: - name: Install pkg dependencies run: npm clean-install --prefer-offline --frozen-lockfile --production - name: Package into node binary - uses: lando/pkg-action@v5 + uses: lando/pkg-action@v6 id: pkg-action with: entrypoint: bin/lando diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e3105458a..5d058801f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,214 +6,55 @@ on: - created jobs: - package: - uses: ./.github/workflows/pkg-binary.yml - strategy: - fail-fast: false - matrix: - arch: - - x64 - - arm64 - os: - - linux - - macos - - win - fatcore: - - false - - true - with: - arch: ${{ matrix.arch }} - edge: ${{ github.event.release.prerelease }} - fatcore: ${{ matrix.fatcore }} - filename: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}${{ matrix.fatcore == false && '-slim' || '' }} - node-version: "20" - os: ${{ matrix.os }} - version: ${{ github.event.release.tag_name }} - - sign: - uses: ./.github/workflows/sign-binary.yml - needs: - - package - strategy: - fail-fast: false - matrix: - file: - - lando-linux-arm64-${{ github.ref_name }} - - lando-macos-arm64-${{ github.ref_name }} - - lando-win-arm64-${{ github.ref_name }} - - - lando-linux-x64-${{ github.ref_name }} - - lando-macos-x64-${{ github.ref_name }} - - lando-win-x64-${{ github.ref_name }} - - - lando-linux-arm64-${{ github.ref_name }}-slim - - lando-macos-arm64-${{ github.ref_name }}-slim - - lando-win-arm64-${{ github.ref_name }}-slim - - - lando-linux-x64-${{ github.ref_name }}-slim - - lando-macos-x64-${{ github.ref_name }}-slim - - lando-win-x64-${{ github.ref_name }}-slim - with: - download-pattern: packaged-lando-* - file: ${{ matrix.file }} - secrets: - apple-notary-user: ${{ secrets.APPLE_NOTARY_USER }} - apple-notary-password: ${{ secrets.APPLE_NOTARY_PASSWORD }} - certificate-data: ${{ contains(matrix.file, 'macos') && secrets.APPLE_CERT_DATA || secrets.KEYLOCKER_CLIENT_CERT }} - certificate-password: ${{ contains(matrix.file, 'macos') && secrets.APPLE_CERT_PASSWORD || secrets.KEYLOCKER_CLIENT_CERT_PASSWORD }} - keylocker-api-key: ${{ secrets.KEYLOCKER_API_KEY }} - keylocker-cert-sha1-hash: ${{ secrets.KEYLOCKER_CERT_SHA1_HASH }} - keylocker-keypair-alias: ${{ secrets.KEYLOCKER_KEYPAIR_ALIAS }} - - build-release-binary-alias: - uses: ./.github/workflows/release-rename-binary.yml - needs: - - sign - strategy: - fail-fast: false - matrix: - arch: - - x64 - - arm64 - os: - - linux - - macos - - win - type: - - -slim - - - alias: ${{ github.event.release.prerelease == true && fromJson('["edge"]') || fromJson('["stable", "edge"]') }} - - with: - source: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}${{ matrix.type }} - destination: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.alias }}${{ matrix.type }} - download-pattern: signed-lando-* - build-release-binary-tag: - uses: ./.github/workflows/release-rename-binary.yml - needs: - - sign + bump-and-npm: + runs-on: ${{ matrix.os }} + env: + TERM: xterm strategy: - fail-fast: false matrix: - arch: - - x64 - - arm64 os: - - linux - - macos - - win - type: - - -slim - - - with: - source: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}${{ matrix.type }} - destination: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}${{ matrix.type }} - download-pattern: signed-lando-* - - checksum: - uses: ./.github/workflows/generate-checksums.yml - needs: - - build-release-binary-alias - - build-release-binary-tag - with: - download-pattern: release-*-${{ github.ref_name }}* - flatten: true - show: true - upload-name: release-checksums-${{ github.ref_name }} - checksum-s3-aliases: - uses: ./.github/workflows/generate-checksums.yml - strategy: - fail-fast: false - matrix: - alias: - - edge - - ${{ github.event.release.prerelease == false && 'stable' || 'prerelease' }} - - ${{ github.ref_name }} - needs: - - build-release-binary-alias - - build-release-binary-tag - with: - download-pattern: release-*-${{ github.ref_name }}* - output: sha256sum-${{ matrix.alias }}.txt - flatten: true - show: true - upload-name: release-checksums${{ matrix.alias }} - - deploy-releases-s3: - uses: ./.github/workflows/deploy-s3.yml - needs: - - checksum - - checksum-s3-aliases - with: - download-pattern: release-* - secrets: - aws-secret-access-key: ${{ secrets.S3_SECRET_ACCESS_KEY }} - aws-access-key-id: ${{ secrets.S3_ACCESS_KEY_ID }} - aws-region: us-east-1 - deploy-releases-artifacts: - uses: ./.github/workflows/deploy-artifacts.yml - needs: - - checksum - with: - download-pattern: release-* - deploy-releases-releases: - uses: ./.github/workflows/deploy-releases.yml - needs: - - checksum - with: - download-pattern: release-*-${{ github.ref_name }}* - deploy-npm: - uses: ./.github/workflows/deploy-npm.yml - needs: - - checksum - secrets: - github-token: ${{ secrets.RTFM47_COAXIUM_INJECTOR }} - npm-token: ${{ secrets.NPM_DEPLOY_TOKEN }} - deploy-legacy-notifications: - runs-on: ubuntu-24.04 - needs: - - checksum - env: - TERM: xterm + - ubuntu-24.04 + node-version: + - '20' steps: - - name: Push release to lando/lando - uses: softprops/action-gh-release@v2 + - name: Checkout code + uses: actions/checkout@v4 + - name: Install node ${{ matrix.node-version }} + uses: actions/setup-node@v4 with: - repository: lando/lando - name: ${{ github.event.release.tag_name }} - draft: ${{ github.event.release.draft }} - prerelease: ${{ github.event.release.prerelease }} - tag_name: ${{ github.event.release.tag_name }} - token: ${{ secrets.RTFM47_COAXIUM_INJECTOR }} - body: | - **Starting with v3.21.0-beta.18, Lando is no longer distributed via package installers in here in this releases page!** - - To install Lando please visit the [official install docs](https://docs.lando.dev/install). - - ## Changelogs - - Lando now runs as a distributed plugin-based ecosystem so you will want to check the releases/changelogs in - the various [plugins](https://docs.lando.dev/plugins.html) for relevant notes. - - [Click Here](https://github.com/lando/core/releases/tag/${{ github.event.release.tag_name }}) to check out the notes for `@lando/core@${{ github.event.release.tag_name }}`. - - ## Notes - - * We will continue to push releases here for backwards compatibility, posterity, etc - * [Extended release notes](https://lando.dev/blog/2024/01/16/v321-extended.html) - - - name: Push release to lando/cli - uses: softprops/action-gh-release@v2 + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org + cache: npm + - name: Install dependencies + run: npm clean-install --prefer-offline --frozen-lockfile + - name: Lint code + run: npm run lint + - name: Run unit tests + run: npm run test:unit + - name: Prepare Release + uses: lando/prepare-release-action@v3 with: - repository: lando/legacy-cli - name: ${{ github.event.release.tag_name }} - draft: ${{ github.event.release.draft }} - prerelease: ${{ github.event.release.prerelease }} - tag_name: ${{ github.event.release.tag_name }} - token: ${{ secrets.RTFM47_COAXIUM_INJECTOR }} - body: | - **Starting with v3.23.0, Lando CLI binaries are no longer distributed here in these releases!** - - They are now available in the `@lando/core` [releases page](https://github.com/lando/core/releases) including [this ${{ github.event.release.tag_name }} release](https://github.com/lando/core/releases/tag/${{ github.event.release.tag_name }}). - - All that said we don't recommned you use these binaries directly. Instead, to install Lando please visit the [official install docs](https://docs.lando.dev/install). + lando-plugin: true + sync: false + - name: Publish to npm + run: | + VERSION=$(node -p "require('./package.json').version") + PACKAGE=$(node -p "require('./package.json').name") + + if [ "${{ github.event.release.prerelease }}" == "false" ]; then + npm publish --access public --dry-run + npm publish --access public + npm dist-tag add "$PACKAGE@$VERSION" edge + + echo "::notice title=Published $VERSION to $PACKAGE::This is a stable release published to the default 'latest' npm tag" + echo "::notice title=Updated latest tag to $VERSION::The stable tag now points to $VERSION" + echo "::notice title=Updated edge tag to $VERSION::The edge tag now points to $VERSION" + else + npm publish --access public --tag edge --dry-run + npm publish --access public --tag edge + + echo "::notice title=Published $VERSION to $PACKAGE::This is a prerelease published to the 'edge' npm tag" + echo "::notice title=Updated edge tag to $VERSION::The edge tag now points to $VERSION" + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_DEPLOY_TOKEN }} diff --git a/.gitignore b/.gitignore index d9a3c39f6..77c875a5d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ coverage/ # lando .lando/id -FATCORE +checksums.txt plugins/* !plugins/healthcheck !plugins/networking @@ -45,6 +45,7 @@ plugins/* !plugins/scanner !plugins/sharing !plugins/test +FATCORE # docs .temp diff --git a/.npmignore b/.npmignore index 233074207..4697cbc1a 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,7 @@ .github .nyc_output coverage +dist docs examples guides diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e6f5ec94..cd184c391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ ## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }}) +## v3.23.26 - [January 24, 2025](https://github.com/lando/core/releases/tag/v3.23.26) + +* Fixed bug where an app’s services were inadvertently reaped if the app’s path included a comma [#322](https://github.com/lando/core/issues/322) + +## v3.23.25 - [January 18, 2025](https://github.com/lando/core/releases/tag/v3.23.25) + +* Fixed bug causing `--accept-license` flag to not work when installing Docker Desktop on macOS +* Updated default Docker Desktop version to `4.37.1|2` +* Updated default Docker Engine version to `27.5.0` +* Updated default Docker Compose version to `2.31.0` +* Updated recommended Docker Desktop range to `>=4.37.0` +* Updated tested Docker Desktop range to `<=4.37` +* Updated tested Docker Compose range to `<=2.32` + +## v3.23.24 - [January 14, 2025](https://github.com/lando/core/releases/tag/v3.23.24) + +* Fixed bug causing service script moving to fail when receiving non-stringy inputs + +## v3.23.23 - [January 14, 2025](https://github.com/lando/core/releases/tag/v3.23.23) + +* Fixed bug causing service script loading collisions + ## v3.23.22 - [December 17, 2024](https://github.com/lando/core/releases/tag/v3.23.22) * Added ability to customize `networkLimit` [#245](https://github.com/lando/core/pull/245) diff --git a/README.md b/README.md index a98d5d602..05587cfc0 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,5 @@ -# Lando Core +# Flos Lando Core -These are the core libraries that power Lando. They are implemented in [`@lando/cli`] and things like [Pantheon LocalDev](https://pantheon.io/product/localdev) and [WordPress VIP CLI](https://github.com/Automattic/vip-cli/blob/develop/package.json). +These are the core libraries that power flos version of Lando with seamless compose integration. -On a high level they serve as: - -**An abstraction layer** Lando vastly reduces the complexity of spinning up containers by exposing only the most relevant config for a given "service" and setting "sane defaults". Lando also provides "recipes" which are common combinations of services and their tooling that satisfy a given development use case - e.g. Drupal, Python, Laravel, Dotnet, etc. - -**A superset** Lando provides ways for developers to run complex commands, build steps and automation on their services without the hassle of custom Dockerfiles or long "docker exec" commands. Think `lando yarn add express`. Think clear my applications cache after I import a database. Think install this core-extension before my appserver starts and then `composer install` after it does. - -**A utility** Lando handles some of the more arduous configuration required for a good Docker Compose setup - e.g. proxying, nice urls, cross-application networking (think Vue.js frontend talking to a separate Laravel backend), host-container file permission handling, file sharing, per-container SSL certificate handling, ssh-key handling, etc. - -## Basic Usage - -```js -const Lando = require('@lando/core'); -const lando = new Lando(config); - -// bootstrap and go -return lando.bootstrap(bsLevel).then(lando => { - lando.getApp().init().then(() => cli.run(getTasks(config, cli.argv()), config)); -}); -const -``` - -For more info you should check out the [docs](https://docs.lando.dev/core/v3): - -## Issues, Questions and Support - -If you have a question or would like some community support we recommend you [join us on Slack](https://launchpass.com/devwithlando). - -If you'd like to report a bug or submit a feature request then please [use the issue queue](https://github.com/lando/core/issues/new/choose) in this repo. - -## Changelog - -We try to log all changes big and small in both [THE CHANGELOG](https://github.com/lando/core/blob/main/CHANGELOG.md) and the [release notes](https://github.com/lando/core/releases). - -## Contributors - - - - - -Made with [contributors-img](https://contrib.rocks).` - -## Other Selected Resources - -* [LICENSE](https://github.com/lando/core/blob/main/LICENSE.md) -* [The best professional advice ever](https://www.youtube.com/watch?v=tkBVDh7my9Q) +Thanks to the upstream [lando-core](https://github.com/lando/core)! diff --git a/app.js b/app.js index 77965eece..bd3bebc17 100644 --- a/app.js +++ b/app.js @@ -113,6 +113,9 @@ module.exports = async (app, lando) => { // Add tooling if applicable app.events.on('post-init', async () => await require('./hooks/app-add-tooling')(app, lando)); + // Add _init tooling for bootstrap reference + app.events.on('pre-bootstrap', async () => await require('./hooks/app-add-init-tooling')(app, lando)); + // Collect info so we can inject LANDO_INFO // @NOTE: this is not currently the full lando info because a lot of it requires the app to be on app.events.on('post-init', 10, async () => await require('./hooks/app-set-lando-info')(app, lando)); @@ -128,7 +131,7 @@ module.exports = async (app, lando) => { app.events.on('ready', 1, async () => await require('./hooks/app-override-tooling-defaults')(app, lando)); // set tooling compose cache - app.events.on('ready', async () => await require('./hooks/app-set-compose-cache')(app, lando)); + app.events.on('ready-engine', async () => await require('./hooks/app-set-compose-cache')(app, lando)); // v4 parts of the app are ready app.events.on('ready', 6, async () => await require('./hooks/app-v4-ready')(app, lando)); @@ -141,7 +144,7 @@ module.exports = async (app, lando) => { // Save a compose cache every time the app is ready, we have to duplicate this for v4 because we modify the // composeData after the v3 app.ready event - app.events.on('ready-v4', async () => await require('./hooks/app-set-v4-compose-cache')(app, lando)); + app.events.on('ready-engine', async () => await require('./hooks/app-set-v4-compose-cache')(app, lando)); // Otherwise set on rebuilds // NOTE: We set this pre-rebuild because post-rebuild runs after post-start because you would need to @@ -240,6 +243,8 @@ module.exports = async (app, lando) => { BITNAMI_DEBUG: 'true', }, labels: { + 'io.lando.landofiles': app.configFiles.map(file => path.basename(file)).join(','), + 'io.lando.root': app.root, 'io.lando.src': app.configFiles.join(','), 'io.lando.http-ports': '80,443', }, diff --git a/bin/lando b/bin/lando index 693186d77..3d0a30728 100755 --- a/bin/lando +++ b/bin/lando @@ -108,6 +108,10 @@ const cores = [ path.resolve(__dirname, '..'), ]; +if (typeof _.get(config, 'plugins.@lando/core') === 'string') { + cores.unshift(path.resolve(appConfig.root, config.plugins['@lando/core'])); +} + // if appConfig points to a different core lets set that here if (typeof _.get(appConfig, 'plugins.@lando/core') === 'string') { cores.unshift(path.resolve(appConfig.root, appConfig.plugins['@lando/core'])); diff --git a/builders/_init.js b/builders/_init.js index ff553c716..09e3131ca 100644 --- a/builders/_init.js +++ b/builders/_init.js @@ -13,9 +13,11 @@ module.exports = { version: 'custom', type: 'init', name: 'init', + data: null, + dataHome: null, }, builder: (parent, config) => class LandoInit extends parent { - constructor(userConfRoot, home, app, env = {}, labels = {}, image = 'devwithlando/util:4') { + constructor(userConfRoot, home, app, _app, env = {}, labels = {}, image = 'devwithlando/util:4') { // Basic Init service const initService = { services: { @@ -34,7 +36,7 @@ module.exports = { initService.services.init.environment.LANDO_SERVICE_TYPE = 'init'; initService.services.init.labels['io.lando.service-container'] = 'TRUE'; initService.services.init.labels['io.lando.init-container'] = 'TRUE'; - super('init', _.merge({}, config, {env, home, labels, userConfRoot}), initService); + super('init', _.merge({}, config, {env, home, labels, userConfRoot, _app}), initService); }; }, }; diff --git a/builders/_lando-compose.js b/builders/_lando-compose.js new file mode 100644 index 000000000..00279d8db --- /dev/null +++ b/builders/_lando-compose.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = { + name: '_lando-compose', + parent: '_lando', + builder: (parent, config) => class LandoComposeServiceV3 extends parent { + constructor(id, options = {}) { + super(id, options); + }; + }, +}; diff --git a/builders/_lando.js b/builders/_lando.js index 8556de70e..2d678922d 100644 --- a/builders/_lando.js +++ b/builders/_lando.js @@ -41,6 +41,7 @@ module.exports = { refreshCerts = false, remoteFiles = {}, scripts = [], + scriptsDir = false, sport = '443', ssl = false, sslExpose = true, @@ -48,6 +49,8 @@ module.exports = { supportedIgnore = false, root = '', webroot = '/app', + _app = null, + appMount = '/app', } = {}, ...sources ) { @@ -66,21 +69,34 @@ module.exports = { console.error(color.yellow(`${type} version ${version} is a legacy version! We recommend upgrading.`)); } + // normalize scripts dir if needed + if (typeof scriptsDir === 'string' && !path.isAbsolute(scriptsDir)) scriptsDir = path.resolve(root, scriptsDir); + + // Get some basic locations + const globalScriptsDir = path.join(userConfRoot, 'scripts'); + const serviceScriptsDir = path.join(userConfRoot, 'helpers', project, type, name); + const entrypointScript = path.join(globalScriptsDir, 'lando-entrypoint.sh'); + const addCertsScript = path.join(globalScriptsDir, 'add-cert.sh'); + const refreshCertsScript = path.join(globalScriptsDir, 'refresh-certs.sh'); + // Move our config into the userconfroot if we have some // NOTE: we need to do this because on macOS and Windows not all host files // are shared into the docker vm if (fs.existsSync(confSrc)) require('../utils/move-config')(confSrc, confDest); - // Get some basic locations - const scriptsDir = path.join(userConfRoot, 'scripts'); - const entrypointScript = path.join(scriptsDir, 'lando-entrypoint.sh'); - const addCertsScript = path.join(scriptsDir, 'add-cert.sh'); - const refreshCertsScript = path.join(scriptsDir, 'refresh-certs.sh'); + // ditto for service helpers + if (!require('../utils/is-disabled')(scriptsDir) && typeof scriptsDir === 'string' && fs.existsSync(scriptsDir)) { + require('../utils/move-config')(scriptsDir, serviceScriptsDir); + } // Handle Environment const environment = { LANDO_SERVICE_NAME: name, LANDO_SERVICE_TYPE: type, + LANDO_WEBROOT_USER: meUser, + LANDO_WEBROOT_GROUP: meUser, + LANDO_MOUNT: appMount, + LANDO_SERVICE_API: 3, }; // Handle labels @@ -94,11 +110,13 @@ module.exports = { // Handle volumes const volumes = [ `${userConfRoot}:/lando:cached`, - `${scriptsDir}:/helpers`, + `${globalScriptsDir}:/helpers`, `${entrypointScript}:/lando-entrypoint.sh`, - `${dataHome}:/var/www`, ]; + // add in service helpers if we have them + if (fs.existsSync(serviceScriptsDir)) volumes.push(`${serviceScriptsDir}:/etc/lando/service/helpers`); + // Handle ssl if (ssl) { // also expose the sport @@ -115,7 +133,8 @@ module.exports = { } // Add in some more dirz if it makes sense - if (home) volumes.push(`${home}:/user:cached`); + if (home && _.get(_app, '_config.homeMount', true)) volumes.push(`${home}:/user:cached`); + else if (home && _.get(_app, 'config.keys', true)) volumes.push(`${path.join(home, '.ssh')}:/user/.ssh:cached`); // Handle cert refresh // @TODO: this might only be relevant to the proxy, if so let's move it there @@ -139,9 +158,16 @@ module.exports = { // Add named volumes and other thingz into our primary service const namedVols = {}; - _.set(namedVols, data, {}); - _.set(namedVols, dataHome, {}); - + if (null !== data) { + _.set(namedVols, data, {}); + } + if (null !== dataHome) { + _.set(namedVols, dataHome, {}); + volumes.push(`${dataHome}:/var/www`); + } + if (null === entrypoint) { + entrypoint = undefined; + } sources.push({ services: _.set({}, name, { entrypoint, @@ -171,6 +197,7 @@ module.exports = { info.meUser = meUser; info.hasCerts = ssl; info.api = 3; + info.appMount = appMount; // Add the healthcheck if it exists if (healthcheck) info.healthcheck = healthcheck; diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 80e9c03fc..6f5b42412 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -443,7 +443,10 @@ module.exports = { const labels = merge({}, app.labels, { 'dev.lando.container': 'TRUE', 'dev.lando.id': lando.config.id, + 'dev.lando.landofiles': app.configFiles.map(file => path.basename(file)).join(','), + 'dev.lando.root': app.root, 'dev.lando.src': app.root, + 'io.lando.http-ports': '80,443', }, config.labels); // add it all 2getha diff --git a/checksums.txt b/checksums.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/config.yml b/config.yml index abbb1be3b..9d25a6bac 100644 --- a/config.yml +++ b/config.yml @@ -13,21 +13,21 @@ dockerSupportedVersions: compose: satisfies: "1.x.x || 2.x.x" recommendUpdate: "<=2.24.6" - tested: "<=2.30.99" + tested: "<=2.32.99" link: linux: https://docs.docker.com/compose/install/#install-compose-on-linux-systems darwin: https://docs.docker.com/desktop/install/mac-install/ win32: https://docs.docker.com/desktop/install/windows-install/ desktop: satisfies: ">=4.0.0 <5" - tested: "<=4.36.99" - recommendUpdate: "<=4.34.0" + tested: "<=4.37.99" + recommendUpdate: "<=4.36" link: darwin: https://docs.docker.com/desktop/install/mac-install/ win32: https://docs.docker.com/desktop/install/windows-install/ wsl: https://docs.docker.com/desktop/install/windows-install/ engine: satisfies: ">=18 <28" - tested: "<=27.3.1" + tested: "<=27.5.99" link: linux: https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script diff --git a/examples/build/.lando.yml b/examples/build/.lando.yml index 6916f11c4..7a41302bf 100644 --- a/examples/build/.lando.yml +++ b/examples/build/.lando.yml @@ -14,7 +14,7 @@ events: - cat /var/www/run_internal.txt | grep www-data - cat /run_as_root.txt | grep root - cat /var/www/run.txt | grep www-data - - /bin/sh -c 'echo "$LANDO_APP_PROJECT" | grep landobuild' + - /bin/sh -c 'echo "$LANDO_APP_PROJECT" | grep lando-build' # uncomment below to test out https://github.com/lando/core/issues/70 # this is commented out by default because of https://github.com/actions/runner/issues/241 # - bash /app/post-start.bash diff --git a/examples/build/README.md b/examples/build/README.md index dd8911c55..3801b7ca8 100644 --- a/examples/build/README.md +++ b/examples/build/README.md @@ -36,8 +36,8 @@ lando rebuild -y # Should rerun build steps even if containers are manually removed and stuff lando destroy -y lando start -y -docker rm -f landobuild_nginx_1 -docker rm -f landobuild_appserver_1 +docker rm -f lando-build_nginx_1 +docker rm -f lando-build_appserver_1 lando start -y lando exec appserver -- vim --version lando exec appserver -- cat /var/www/build.txt diff --git a/examples/c,o,m,m,a/.lando.yml b/examples/c,o,m,m,a/.lando.yml new file mode 100644 index 000000000..90da2ee06 --- /dev/null +++ b/examples/c,o,m,m,a/.lando.yml @@ -0,0 +1,23 @@ +name: commapp +services: + defaults: + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html:ro + + defaults-v4: + api: 4 + type: l337 + image: nginx:1.22.1 + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html:ro + +plugins: + "@lando/core": "../.." diff --git a/examples/c,o,m,m,a/README.md b/examples/c,o,m,m,a/README.md new file mode 100644 index 000000000..8f037e087 --- /dev/null +++ b/examples/c,o,m,m,a/README.md @@ -0,0 +1,35 @@ +# C,O,M,M,A Example + +This example exists primarily to test the following: + +* [Issue #322](https://github.com/lando/core/issues/322) + +## Start up tests + +Run the following commands to get up and running with this example. + +```bash +# Should start up successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to validate things are rolling as they should. + +```bash +# Should serve from the app root by default +lando exec defaults -- curl http://localhost | grep ROOTDIR +lando exec defaults-v4 -- curl http://localhost | grep ROOTDIR +``` + +## Destroy tests + +Run the following commands to trash this app like nothing ever happened. + +```bash +# Should be destroyed with success +lando destroy -y +lando poweroff +``` diff --git a/examples/c,o,m,m,a/index.html b/examples/c,o,m,m,a/index.html new file mode 100644 index 000000000..83213b29b --- /dev/null +++ b/examples/c,o,m,m,a/index.html @@ -0,0 +1 @@ +ROOTDIR diff --git a/examples/cache/README.md b/examples/cache/README.md index 9e2ee5e37..4cd235bba 100644 --- a/examples/cache/README.md +++ b/examples/cache/README.md @@ -34,7 +34,8 @@ cat ~/.lando/cache/lando-cache.compose.cache # Should regenerate the caches on any --help before the help is displayed lando --clear -lando exec --help | grep service | grep choices | grep web | grep web2 | grep web3 | grep web4 +# NOTE(flo): Web is not here as the task bootstrapping runs before the app init, which gets compose services :/ +lando exec --help | grep service | grep choices | grep web2 | grep web3 | grep web4 cat ~/.lando/cache/_.tasks.cache cat ~/.lando/cache/lando-cache.compose.cache diff --git a/examples/certs/README.md b/examples/certs/README.md index 8dea4a3ab..78c6784f1 100644 --- a/examples/certs/README.md +++ b/examples/certs/README.md @@ -20,8 +20,8 @@ Run the following commands to verify things work as expected ```bash # Should set the environment variables correctly -lando exec web -- env | grep LANDO_SERVICE_CERT | grep /lando/certs/web.landocerts.crt -lando exec web -- env | grep LANDO_SERVICE_KEY | grep /lando/certs/web.landocerts.key +lando exec web -- env | grep LANDO_SERVICE_CERT | grep /lando/certs/web.lando-certs.crt +lando exec web -- env | grep LANDO_SERVICE_KEY | grep /lando/certs/web.lando-certs.key lando exec web2 -- env | grep LANDO_SERVICE_CERT | grep /etc/lando/certs/cert.crt lando exec web2 -- env | grep LANDO_SERVICE_KEY | grep /etc/lando/certs/cert.key lando exec web3 -- env | grep LANDO_SERVICE_CERT | grep /certs/cert.crt @@ -70,21 +70,21 @@ lando certinfo --service web4 | grep Issuer | grep "Lando Development CA" # Should have the correct cert SANS lando certinfo | grep DNS | grep -w localhost -lando certinfo | grep DNS | grep -w web.landocerts.internal +lando certinfo | grep DNS | grep -w web.lando-certs.internal lando certinfo | grep DNS | grep -w web lando certinfo | grep "IP Address" | grep 127.0.0.1 lando certinfo --service web2 | grep DNS | grep -w localhost lando certinfo --service web2 | grep DNS | grep -w web2.lndo.site -lando certinfo --service web2 | grep DNS | grep -w web2.landocerts.internal +lando certinfo --service web2 | grep DNS | grep -w web2.lando-certs.internal lando certinfo --service web2 | grep DNS | grep -w web2 lando certinfo --service web2 | grep "IP Address" | grep 127.0.0.1 lando certinfo --service web3 | grep DNS | grep -w vibes.rising lando certinfo --service web3 | grep DNS | grep -w localhost -lando certinfo --service web3 | grep DNS | grep -w web3.landocerts.internal +lando certinfo --service web3 | grep DNS | grep -w web3.lando-certs.internal lando certinfo --service web3 | grep DNS | grep -w web3 lando certinfo --service web3 | grep "IP Address" | grep 127.0.0.1 lando certinfo --service web4 | grep DNS | grep -w localhost -lando certinfo --service web4 | grep DNS | grep -w web4.landocerts.internal +lando certinfo --service web4 | grep DNS | grep -w web4.lando-certs.internal lando certinfo --service web4 | grep DNS | grep -w web4 lando certinfo --service web4 | grep "IP Address" | grep 127.0.0.1 diff --git a/examples/info/README.md b/examples/info/README.md index a9b8b1338..b17d0fe7b 100644 --- a/examples/info/README.md +++ b/examples/info/README.md @@ -38,7 +38,7 @@ lando info --deep --format json | grep "^\[{\"" # Should output tabular data with --format table lando info --format table | grep Key | grep Value -lando info --deep --format table | grep NetworkSettings.Networks.lando_bridge_network.Aliases | grep web4.landoinfo.internal +lando info --deep --format table | grep NetworkSettings.Networks.lando_bridge_network.Aliases | grep web4.lando-info.internal # Should return a specified --path when given with lando info lando info --path "[0]" | grep service | wc -l | grep 1 @@ -59,7 +59,7 @@ lando destroy -y lando info --service web --path urls | grep "\[\]" lando info --service web --path type | grep docker-compose lando info --service web --path healthy | grep unknown -lando info --service web --path hostnames | grep web.landoinfo.internal +lando info --service web --path hostnames | grep web.lando-info.internal lando info --service web2 --path urls | grep "\[\]" lando info --service web2 --path type | grep lando lando info --service web2 --path healthy | grep unknown @@ -67,7 +67,7 @@ lando info --service web2 --path version | grep custom lando info --service web2 --path meUser | grep www-data lando info --service web2 --path hasCerts | grep "false" lando info --service web2 --path api | grep 3 -lando info --service web2 --path hostnames | grep web2.landoinfo.internal +lando info --service web2 --path hostnames | grep web2.lando-info.internal lando info --service web3 --path urls | grep "\[\]" lando info --service web3 --path type | grep l337 lando info --service web3 --path healthy | grep unknown @@ -77,7 +77,7 @@ lando info --service web3 --path primary | grep "true" lando info --service web3 --path image | grep nginx lando info --service web3 --path user | grep root lando info --service web3 --path appMount | grep /usr/share/nginx/html -lando info --service web3 --path hostnames | grep web3.landoinfo.internal +lando info --service web3 --path hostnames | grep web3.lando-info.internal lando info --service web4 --path urls | grep "\[\]" lando info --service web4 --path type | grep lando lando info --service web4 --path healthy | grep unknown @@ -88,14 +88,14 @@ lando info --service web4 --path primary | grep "false" lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 lando info --service web4 --path user | grep nginx lando info --service web4 --path appMount | grep /usr/share/nginx/html -lando info --service web4 --path hostnames | grep web4.landoinfo.internal +lando info --service web4 --path hostnames | grep web4.lando-info.internal # Should have the correct info after a start and/or rebuild lando start lando info --service web --path urls | grep "\[\]" lando info --service web --path type | grep docker-compose lando info --service web --path healthy | grep unknown -lando info --service web --path hostnames | grep web.landoinfo.internal +lando info --service web --path hostnames | grep web.lando-info.internal lando info --service web2 --path urls | grep "http://localhost" lando info --service web2 --path type | grep lando lando info --service web2 --path healthy | grep unknown @@ -103,7 +103,7 @@ lando info --service web2 --path version | grep custom lando info --service web2 --path meUser | grep www-data lando info --service web2 --path hasCerts | grep "false" lando info --service web2 --path api | grep 3 -lando info --service web2 --path hostnames | grep web2.landoinfo.internal +lando info --service web2 --path hostnames | grep web2.lando-info.internal lando info --service web3 --path urls | grep "http://localhost" lando info --service web3 --path type | grep l337 lando info --service web3 --path healthy | grep unknown @@ -114,7 +114,7 @@ lando info --service web3 --path primary | grep "true" lando info --service web3 --path image | grep nginx lando info --service web3 --path user | grep root lando info --service web3 --path appMount | grep /usr/share/nginx/html -lando info --service web3 --path hostnames | grep web3.landoinfo.internal +lando info --service web3 --path hostnames | grep web3.lando-info.internal lando info --service web4 --path urls | grep "http://localhost" lando info --service web4 --path type | grep lando lando info --service web4 --path healthy | grep unknown @@ -126,12 +126,12 @@ lando info --service web4 --path primary | grep "false" lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 lando info --service web4 --path user | grep nginx lando info --service web4 --path appMount | grep /usr/share/nginx/html -lando info --service web4 --path hostnames | grep web4.landoinfo.internal +lando info --service web4 --path hostnames | grep web4.lando-info.internal lando rebuild -y lando info --service web --path urls | grep "\[\]" lando info --service web --path type | grep docker-compose lando info --service web --path healthy | grep unknown -lando info --service web --path hostnames | grep web.landoinfo.internal +lando info --service web --path hostnames | grep web.lando-info.internal lando info --service web2 --path urls | grep "http://localhost" lando info --service web2 --path type | grep lando lando info --service web2 --path healthy | grep unknown @@ -139,7 +139,7 @@ lando info --service web2 --path version | grep custom lando info --service web2 --path meUser | grep www-data lando info --service web2 --path hasCerts | grep "false" lando info --service web2 --path api | grep 3 -lando info --service web2 --path hostnames | grep web2.landoinfo.internal +lando info --service web2 --path hostnames | grep web2.lando-info.internal lando info --service web3 --path urls | grep "http://localhost" lando info --service web3 --path type | grep l337 lando info --service web3 --path healthy | grep unknown @@ -150,7 +150,7 @@ lando info --service web3 --path primary | grep "true" lando info --service web3 --path image | grep nginx lando info --service web3 --path user | grep root lando info --service web3 --path appMount | grep /usr/share/nginx/html -lando info --service web3 --path hostnames | grep web3.landoinfo.internal +lando info --service web3 --path hostnames | grep web3.lando-info.internal lando info --service web4 --path urls | grep "http://localhost" lando info --service web4 --path type | grep lando lando info --service web4 --path healthy | grep unknown @@ -162,7 +162,7 @@ lando info --service web4 --path primary | grep "false" lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 lando info --service web4 --path user | grep nginx lando info --service web4 --path appMount | grep /usr/share/nginx/html -lando info --service web4 --path hostnames | grep web4.landoinfo.internal +lando info --service web4 --path hostnames | grep web4.lando-info.internal ``` ## Destroy tests diff --git a/examples/lando-v4/.lando.yml b/examples/lando-v4/.lando.yml index 1aaf725dc..b28213e79 100644 --- a/examples/lando-v4/.lando.yml +++ b/examples/lando-v4/.lando.yml @@ -42,6 +42,8 @@ services: # build # files + # check labels + # appmount # support appMount but prefer app-mount # project-mount: /project how does this work? diff --git a/examples/landofile/README.md b/examples/landofile/README.md index 15f1bc0cf..13edd2536 100644 --- a/examples/landofile/README.md +++ b/examples/landofile/README.md @@ -20,17 +20,17 @@ Run the following commands to verify things work as expected ```bash # Should merge in all Landofiles correctly -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_log_1 -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web_1 -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web2_1 -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web3_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_log_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_web_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_web2_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_web3_1 # Should merge in all Landofiles correctly even if we are down a directory cd docker-compose -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_log_1 -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web_1 -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web2_1 -docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web3_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_log_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_web_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_web2_1 +docker ps --filter label=com.docker.compose.project=lando-landofile | grep lando-landofile_web3_1 cd .. ``` diff --git a/examples/list/README.md b/examples/list/README.md index 5e178f9bf..2c0d74606 100644 --- a/examples/list/README.md +++ b/examples/list/README.md @@ -27,20 +27,20 @@ cd .. lando list # Should list this apps containers -lando list --app landolist | grep landolist_web_1 -lando list --app landolist | grep landolist_web2_1 -lando list --app landolist | grep landolist_web3_1 -lando list --app landolist | grep landolist_web4_1 +lando list --app lando-list | grep lando-list_web_1 +lando list --app lando-list | grep lando-list_web2_1 +lando list --app lando-list | grep lando-list_web3_1 +lando list --app lando-list | grep lando-list_web4_1 # Should list no containers if we spin down the app lando stop lando list | grep "\[\]" # Should list even stopped containers with --all -lando list --all | grep landolist_web_1 -lando list --all | grep landolist_web2_1 -lando list --all | grep landolist_web3_1 -lando list --all | grep landolist_web4_1 +lando list --all | grep lando-list_web_1 +lando list --all | grep lando-list_web2_1 +lando list --all | grep lando-list_web3_1 +lando list --all | grep lando-list_web4_1 # Should output JSON with --format json lando list --all --format json | grep "^\[{\"" @@ -54,11 +54,11 @@ skip # Should return --path without preceding index if array has size 1 lando start -lando list --filter app=landolist --filter service=web4 --path service | grep web4 +lando list --filter app=lando-list --filter service=web4 --path service | grep web4 # Should allow data to be filtered -docker stop landolist_web4_1 -lando list --all --filter running=false --filter app=landolist --path service | grep web4 +docker stop lando-list_web4_1 +lando list --all --filter running=false --filter app=lando-list --path service | grep web4 ``` ## Destroy tests diff --git a/examples/networking/README.md b/examples/networking/README.md index 5b639135f..86b99c767 100644 --- a/examples/networking/README.md +++ b/examples/networking/README.md @@ -35,10 +35,10 @@ Run the following commands to verify things work as expected ```bash # Should have the correct internal hostname info cd lamp -lando info -s appserver | grep hostnames: | grep appserver.landolamp.internal +lando info -s appserver | grep hostnames: | grep appserver.lando-lamp.internal cd .. && cd lemp -lando info -s appserver | grep hostnames: | grep appserver.landolemp.internal -lando info -s appserver_nginx | grep hostnames: | grep appserver_nginx.landolemp.internal +lando info -s appserver | grep hostnames: | grep appserver.lando-lemp.internal +lando info -s appserver_nginx | grep hostnames: | grep appserver_nginx.lando-lemp.internal # Should be able to self connect from lamp cd lamp @@ -53,20 +53,20 @@ lando exec appserver_nginx -- curl https://localhost:8443 # Should be able to curl lemp from lamp at proxy addresses and internal hostnames cd lamp lando exec appserver -- curl http://lando-lemp.lndo.site -lando exec appserver -- curl http://appserver_nginx.landolemp.internal:8080 +lando exec appserver -- curl http://appserver_nginx.lando-lemp.internal:8080 lando exec appserver -- curl https://lando-lemp.lndo.site -lando exec appserver -- curl https://appserver_nginx.landolemp.internal:8443 +lando exec appserver -- curl https://appserver_nginx.lando-lemp.internal:8443 # Should be able to curl lamp from lemp at proxy addresses and internal hostname cd lemp lando exec appserver_nginx -- curl http://lando-lamp.lndo.site -lando exec appserver_nginx -- curl http://appserver.landolamp.internal +lando exec appserver_nginx -- curl http://appserver.lando-lamp.internal lando exec appserver_nginx -- curl https://lando-lamp.lndo.site -lando exec appserver_nginx -- curl https://appserver.landolamp.internal +lando exec appserver_nginx -- curl https://appserver.lando-lamp.internal # Should even be able to connect to a database in a different app cd lamp -lando exec database -- mysql -uroot -h database.landolemp.internal -e "quit" +lando exec database -- mysql -uroot -h database.lando-lemp.internal -e "quit" # Should see the correct network limit lando config | grep "networkLimit: 64" diff --git a/examples/proxy/README.md b/examples/proxy/README.md index e67352a7e..785ea1935 100644 --- a/examples/proxy/README.md +++ b/examples/proxy/README.md @@ -34,16 +34,16 @@ curl -s -o /dev/null -I -w "%{http_code}" http://l337.lndo.site | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://lando4.lndo.site | grep 200 # Should only work over http unless service has certs to use -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' landoproxy_web_1 | grep -w http -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' landoproxy_web_1 | grep -w "true" || echo $? | grep 1 -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' landoproxy_web3_1 | grep -w "http,https" -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' landoproxy_web3_1 | grep -w "true" -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' landoproxy_l337_1 | grep -w http -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' landoproxy_l337_1 | grep -w "true" || echo $? | grep 1 -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' landoproxy_web5_1 | grep -w http -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' landoproxy_web5_1 | grep -w "true" || echo $? | grep 1 -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' landoproxy_web4_1 | grep -w "http,https" -docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' landoproxy_web4_1 | grep -w "true" +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' lando-proxy_web_1 | grep -w http +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' lando-proxy_web_1 | grep -w "true" || echo $? | grep 1 +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' lando-proxy_web3_1 | grep -w "http,https" +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' lando-proxy_web3_1 | grep -w "true" +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' lando-proxy_l337_1 | grep -w http +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' lando-proxy_l337_1 | grep -w "true" || echo $? | grep 1 +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' lando-proxy_web5_1 | grep -w http +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' lando-proxy_web5_1 | grep -w "true" || echo $? | grep 1 +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.protocols" }}' lando-proxy_web4_1 | grep -w "http,https" +docker inspect --format='{{ index .Config.Labels "dev.lando.proxy.has-certs" }}' lando-proxy_web4_1 | grep -w "true" # Should also work over https if ssl is true and we have certs curl -s -o /dev/null -Ik -w "%{http_code}" https://web3.lndo.site | grep 200 @@ -98,8 +98,8 @@ docker exec landoproxyhyperion5000gandalfedition_proxy_1 cat /proxy_config/defau docker exec landoproxyhyperion5000gandalfedition_proxy_1 cat /proxy_config/default-certs.yaml | grep keyFile | grep /certs/cert.key # Should generate proxy cert files and move them into the right location as needed -docker exec landoproxy_web3_1 cat /proxy_config/web3.landoproxy.yaml| grep certFile | grep "/lando/certs/web3.landoproxy.crt" -docker exec landoproxy_web3_1 cat /proxy_config/web3.landoproxy.yaml| grep keyFile | grep "/lando/certs/web3.landoproxy.key" +docker exec lando-proxy_web3_1 cat /proxy_config/web3.lando-proxy.yaml| grep certFile | grep "/lando/certs/web3.lando-proxy.crt" +docker exec lando-proxy_web3_1 cat /proxy_config/web3.lando-proxy.yaml| grep keyFile | grep "/lando/certs/web3.lando-proxy.key" lando exec web4 -- cat \$LANDO_SERVICE_CERT lando exec web4 -- env | grep LANDO_SERVICE_CERT | grep /certs/cert.crt lando exec web4 -- cat \$LANDO_SERVICE_KEY diff --git a/examples/rebuild/README.md b/examples/rebuild/README.md index a64418429..01ad0648b 100644 --- a/examples/rebuild/README.md +++ b/examples/rebuild/README.md @@ -21,26 +21,26 @@ Run the following commands to verify things work as expected ```bash # Should rebuild the services without errors lando rebuild -y -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web3_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web4_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web2_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web3_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web4_1 # Should only rebuild the specified services lando rebuild -y --service web2 lando rebuild -y -s web2 -docker ps --latest | grep landorebuild_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web3_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web4_1 +docker ps --latest | grep lando-rebuild_web2_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web2_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web3_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web4_1 lando rebuild -y --service web3 lando rebuild -y -s web3 -docker ps --latest | grep landorebuild_web3_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web3_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web4_1 +docker ps --latest | grep lando-rebuild_web3_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web2_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web3_1 +docker ps --filter label=com.docker.compose.project=lando-rebuild | grep lando-rebuild_web4_1 # Should persist tooling cache between rebuilds lando do-i-exist | grep icachethereforeiam diff --git a/examples/restart/README.md b/examples/restart/README.md index eb7c5c2a7..faa7108a9 100644 --- a/examples/restart/README.md +++ b/examples/restart/README.md @@ -23,7 +23,7 @@ Run the following commands to verify things work as expected ```bash # Should stop the apps containers lando stop -docker ps --filter label=com.docker.compose.project=landorestart -q | wc -l | grep 0 +docker ps --filter label=com.docker.compose.project=lando-restart -q | wc -l | grep 0 # Should stop ALL running lando containers lando start @@ -33,10 +33,10 @@ docker ps --filter label=io.lando.container=TRUE -q | wc -l | grep 0 # Should restart the services without errors lando restart -docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web_1 -docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web2_1 -docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web3_1 -docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web4_1 +docker ps --filter label=com.docker.compose.project=lando-restart | grep lando-restart_web_1 +docker ps --filter label=com.docker.compose.project=lando-restart | grep lando-restart_web2_1 +docker ps --filter label=com.docker.compose.project=lando-restart | grep lando-restart_web3_1 +docker ps --filter label=com.docker.compose.project=lando-restart | grep lando-restart_web4_1 ``` ## Destroy tests diff --git a/examples/scanner/README.md b/examples/scanner/README.md index cdb8d19f0..26d123850 100755 --- a/examples/scanner/README.md +++ b/examples/scanner/README.md @@ -20,16 +20,16 @@ Run the following commands to validate things are rolling as they should. ```bash # Should set 80,443 in io.lando.http-ports label by default -docker inspect landoscanner_scanme_1 | grep io.lando.http-ports | grep "80,443" +docker inspect lando-scanner_scanme_1 | grep io.lando.http-ports | grep "80,443" # Should add an extra port to io.lando.http-ports if specified -docker inspect landoscanner_moreports_1 | grep io.lando.http-ports | grep "80,443,8888" -docker inspect landoscanner_l337_1 | grep dev.lando.http-ports | grep "8888" +docker inspect lando-scanner_moreports_1 | grep io.lando.http-ports | grep "80,443,8888" +docker inspect lando-scanner_l337_1 | grep dev.lando.http-ports | grep "8888" # Should set correct labels in Lando 4 services -docker inspect landoscanner_web4_1 | grep io.lando.http-ports | grep "80,443" -docker inspect landoscanner_web4_1 | grep dev.lando.http-ports | grep "8080" -docker inspect landoscanner_web4_1 | grep dev.lando.https-ports | grep "8443" +docker inspect lando-scanner_web4_1 | grep io.lando.http-ports | grep "80,443" +docker inspect lando-scanner_web4_1 | grep dev.lando.http-ports | grep "8080" +docker inspect lando-scanner_web4_1 | grep dev.lando.https-ports | grep "8443" ``` ## Destroy tests diff --git a/examples/storage/.lando.yml b/examples/storage/.lando.yml index 7e88b4cdc..bf557850e 100644 --- a/examples/storage/.lando.yml +++ b/examples/storage/.lando.yml @@ -49,7 +49,7 @@ services: type: image # remount for fun? - - source: landostorage-db-var-lib-mysql + - source: lando-storage-db-var-lib-mysql target: /var/lib/mysql-backup alpine: api: 4 @@ -68,7 +68,7 @@ services: type: bind # or if you are clever and know the storage volume names you can remount them elsewhere - - source: landostorage-stuff + - source: lando-storage-stuff target: /things - source: lando-everywhere target: /universal diff --git a/examples/storage/README.md b/examples/storage/README.md index 54f4e998c..60e4e9e83 100644 --- a/examples/storage/README.md +++ b/examples/storage/README.md @@ -22,13 +22,13 @@ Run the following commands to verify things work as expected ```bash # Should create storage volumes with names that imply the scope docker volume inspect lando-everywhere -docker volume inspect landostorage-stuff -docker volume inspect landostorage-alpine-some-cache-directory -docker volume inspect landostorage-db-some-other-dir -docker volume inspect landostorage-db-var-lib-mysql -docker volume inspect landostorage-owners-someplace -docker volume inspect landostorage-owners-someplace-free -docker volume inspect landostorage-owners-someplace-secret +docker volume inspect lando-storage-stuff +docker volume inspect lando-storage-alpine-some-cache-directory +docker volume inspect lando-storage-db-some-other-dir +docker volume inspect lando-storage-db-var-lib-mysql +docker volume inspect lando-storage-owners-someplace +docker volume inspect lando-storage-owners-someplace-free +docker volume inspect lando-storage-owners-someplace-secret docker volume list --filter "label=dev.lando.storage-volume=TRUE" | wc -l | grep 9 # Should create storage bind mounts @@ -39,22 +39,22 @@ docker volume inspect lando-everywhere | grep "dev.lando.storage-volume" | grep docker volume inspect lando-everywhere | grep "dev.lando.storage-scope" | grep global docker volume inspect lando-everywhere | grep "dev.lando.storage-project" || echo "$?" | grep 1 docker volume inspect lando-everywhere | grep "dev.lando.storage-service" || echo "$?" | grep 1 -docker volume inspect landostorage-stuff | grep "dev.lando.storage-volume" | grep TRUE -docker volume inspect landostorage-stuff | grep "dev.lando.storage-scope" | grep app -docker volume inspect landostorage-stuff | grep "dev.lando.storage-project" | grep landostorage -docker volume inspect landostorage-stuff | grep "dev.lando.storage-service" | grep db -docker volume inspect landostorage-alpine-some-cache-directory | grep "dev.lando.storage-volume" | grep TRUE -docker volume inspect landostorage-alpine-some-cache-directory | grep "dev.lando.storage-scope" | grep service -docker volume inspect landostorage-alpine-some-cache-directory | grep "dev.lando.storage-project" | grep landostorage -docker volume inspect landostorage-alpine-some-cache-directory | grep "dev.lando.storage-service" | grep alpine -docker volume inspect landostorage-db-some-other-dir | grep "dev.lando.storage-volume" | grep TRUE -docker volume inspect landostorage-db-some-other-dir | grep "dev.lando.storage-scope" | grep service -docker volume inspect landostorage-db-some-other-dir | grep "dev.lando.storage-project" | grep landostorage -docker volume inspect landostorage-db-some-other-dir | grep "dev.lando.storage-service" | grep db -docker volume inspect landostorage-db-var-lib-mysql | grep "dev.lando.storage-volume" | grep TRUE -docker volume inspect landostorage-db-var-lib-mysql | grep "dev.lando.storage-scope" | grep service -docker volume inspect landostorage-db-var-lib-mysql | grep "dev.lando.storage-project" | grep landostorage -docker volume inspect landostorage-db-var-lib-mysql | grep "dev.lando.storage-service" | grep db +docker volume inspect lando-storage-stuff | grep "dev.lando.storage-volume" | grep TRUE +docker volume inspect lando-storage-stuff | grep "dev.lando.storage-scope" | grep app +docker volume inspect lando-storage-stuff | grep "dev.lando.storage-project" | grep lando-storage +docker volume inspect lando-storage-stuff | grep "dev.lando.storage-service" | grep db +docker volume inspect lando-storage-alpine-some-cache-directory | grep "dev.lando.storage-volume" | grep TRUE +docker volume inspect lando-storage-alpine-some-cache-directory | grep "dev.lando.storage-scope" | grep service +docker volume inspect lando-storage-alpine-some-cache-directory | grep "dev.lando.storage-project" | grep lando-storage +docker volume inspect lando-storage-alpine-some-cache-directory | grep "dev.lando.storage-service" | grep alpine +docker volume inspect lando-storage-db-some-other-dir | grep "dev.lando.storage-volume" | grep TRUE +docker volume inspect lando-storage-db-some-other-dir | grep "dev.lando.storage-scope" | grep service +docker volume inspect lando-storage-db-some-other-dir | grep "dev.lando.storage-project" | grep lando-storage +docker volume inspect lando-storage-db-some-other-dir | grep "dev.lando.storage-service" | grep db +docker volume inspect lando-storage-db-var-lib-mysql | grep "dev.lando.storage-volume" | grep TRUE +docker volume inspect lando-storage-db-var-lib-mysql | grep "dev.lando.storage-scope" | grep service +docker volume inspect lando-storage-db-var-lib-mysql | grep "dev.lando.storage-project" | grep lando-storage +docker volume inspect lando-storage-db-var-lib-mysql | grep "dev.lando.storage-service" | grep db # Should share app scoped storage volumes across all app services lando exec db -- touch /stuff/test1 @@ -177,10 +177,10 @@ lando exec alpine -- stat /universal/test1 # Should not persist non-global storage volumes across rebuilds lando destroy -y docker volume inspect lando-everywhere -docker volume inspect landostorage-stuff || echo "$?" | grep 1 -docker volume inspect landostorage-alpine-some-cache-directory || echo "$?" | grep 1 -docker volume inspect landostorage-db-some-other-dir || echo "$?" | grep 1 -docker volume inspect landostorage-db-var-lib-mysql || echo "$?" | grep 1 +docker volume inspect lando-storage-stuff || echo "$?" | grep 1 +docker volume inspect lando-storage-alpine-some-cache-directory || echo "$?" | grep 1 +docker volume inspect lando-storage-db-some-other-dir || echo "$?" | grep 1 +docker volume inspect lando-storage-db-var-lib-mysql || echo "$?" | grep 1 docker volume list --filter "label=dev.lando.storage-volume=TRUE" | wc -l | grep 2 lando start lando exec db -- mysql -u root -e "SHOW DATABASES;" | grep vibes || echo "$?" | grep 1 @@ -190,10 +190,10 @@ lando exec alpine -- stat /things/test1 || echo "$?" | grep 1 # Should persist global storage across destroys lando destroy -y docker volume inspect lando-everywhere -docker volume inspect landostorage-stuff || echo "$?" | grep 1 -docker volume inspect landostorage-alpine-some-cache-directory || echo "$?" | grep 1 -docker volume inspect landostorage-db-some-other-dir || echo "$?" | grep 1 -docker volume inspect landostorage-db-var-lib-mysql || echo "$?" | grep 1 +docker volume inspect lando-storage-stuff || echo "$?" | grep 1 +docker volume inspect lando-storage-alpine-some-cache-directory || echo "$?" | grep 1 +docker volume inspect lando-storage-db-some-other-dir || echo "$?" | grep 1 +docker volume inspect lando-storage-db-var-lib-mysql || echo "$?" | grep 1 docker volume list --filter "label=dev.lando.storage-volume=TRUE" | wc -l | grep 2 lando start lando exec alpine -- stat /universal/test1 @@ -227,9 +227,9 @@ lando exec owners -- stat /someplace-free/me # Should allow for top level volumes to still be used with overrides.volumes docker volume inspect my-data || echo "$?" | grep 1 -docker volume inspect landostorage_my-data -docker volume inspect landostorage_my-data | grep com.docker.compose.project | grep landostorage -docker volume inspect landostorage_my-data | grep com.docker.compose.volume | grep my-data +docker volume inspect lando-storage_my-data +docker volume inspect lando-storage_my-data | grep com.docker.compose.project | grep lando-storage +docker volume inspect lando-storage_my-data | grep com.docker.compose.volume | grep my-data lando exec --user root db -- touch /my-data/thing ``` diff --git a/examples/tooling/.lando.yml b/examples/tooling/.lando.yml index 66d5d8c74..399f16275 100644 --- a/examples/tooling/.lando.yml +++ b/examples/tooling/.lando.yml @@ -10,6 +10,7 @@ services: api: 3 type: lando meUser: node + scriptsDir: scripts services: image: node:16 command: docker-entrypoint.sh tail -f /dev/null @@ -261,6 +262,19 @@ tooling: arg2: describe: Uses arg2 type: string + sdargs: + cmd: /etc/lando/service/helpers/args.sh + service: node + positionals: + arg1: + describe: Uses arg1 + type: string + choices: + - thing + - stuff + arg2: + describe: Uses arg2 + type: string plugins: "@lando/core": "../.." diff --git a/examples/tooling/README.md b/examples/tooling/README.md index e8991e604..44de5792c 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -159,6 +159,10 @@ lando everything --help | grep "lando everything \[arg1\] \[arg2\] MORETHINGS" # Should allow for example pasthru in task definition lando everything --help | grep "lando this is just for testing" +# Should be able to access scriptsDir from the landofile +lando exec node -- stat /etc/lando/service/helpers/args.sh +lando sdargs hello there | grep "hello there" + # Should be able to run even if options are empty lando emptyopter diff --git a/examples/tooling/scripts/args.sh b/examples/tooling/scripts/args.sh new file mode 100755 index 000000000..b00e31206 --- /dev/null +++ b/examples/tooling/scripts/args.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "$1 $2" diff --git a/hooks/app-add-init-tooling.js b/hooks/app-add-init-tooling.js new file mode 100644 index 000000000..f2801b54e --- /dev/null +++ b/hooks/app-add-init-tooling.js @@ -0,0 +1,21 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = async (app, lando) => { + if (!_.isEmpty(_.get(app, 'config.tooling', {}))) { + app.log.verbose('additional tooling detected'); + + // Add the _init tasks for the bootstrap event! + // TODO(flo): They are duplicated through "app-add-tooling" but I do not care for now! + _.forEach(require('../utils/get-tooling-tasks')(app.config.tooling, app), task => { + if (task.service !== '_init') { + return; + } + + app.log.debug('adding app cli task %s', task.name); + const injectable = _.has(app, 'engine') ? app : lando; + app.tasks.push(require('../utils/build-tooling-task')(task, injectable)); + }); + } +}; diff --git a/hooks/app-add-v3-services.js b/hooks/app-add-v3-services.js index fe3659406..b68e21fc5 100644 --- a/hooks/app-add-v3-services.js +++ b/hooks/app-add-v3-services.js @@ -6,6 +6,13 @@ module.exports = async (app, lando) => { // add parsed services to app object so we can use them downstream app.cachedInfo = _.get(lando.cache.get(app.composeCache), 'info', []); app.parsedServices = require('../utils/parse-v3-services')(_.get(app, 'config.services', {}), app); + app.parsedServices = app.parsedServices.concat( + require('../utils/parse-compose-services')( + _.get(app, 'config.services', {}), + _.keys(_.get(app, 'composeData[0].data[0].services', {})), + app, + ), + ); app.parsedV3Services = _(app.parsedServices).filter(service => service.api === 3).value(); app.servicesList = app.parsedV3Services.map(service => service.name); diff --git a/hooks/app-run-events.js b/hooks/app-run-events.js index 921086139..59c14f459 100644 --- a/hooks/app-run-events.js +++ b/hooks/app-run-events.js @@ -1,9 +1,12 @@ 'use strict'; const _ = require('lodash'); +const remove = require('../utils/remove'); +const path = require('path'); +const formatters = require('../lib/formatters'); module.exports = async (app, lando, cmds, data, event) => { - const eventCommands = require('./../utils/parse-events-config')(cmds, app, data); + const eventCommands = require('./../utils/parse-events-config')(cmds, app, data, lando); // add perm sweeping to all v3 services if (!_.isEmpty(eventCommands)) { const permsweepers = _(eventCommands) @@ -27,7 +30,28 @@ module.exports = async (app, lando, cmds, data, event) => { }); } const injectable = _.has(app, 'engine') ? app : lando; - return injectable.engine.run(eventCommands).catch(err => { + + const splitEventCommands = []; + while (!_.isEmpty(eventCommands)) { + splitEventCommands.push( + _.takeWhile(eventCommands, + (eventCommand, index) => index === 0 || (!!eventCommand.toolingTask === !!eventCommands[index - 1].toolingTask), + ), + ); + eventCommands.splice(0, _.last(splitEventCommands).length); + } + + return lando.Promise.mapSeries(splitEventCommands, eventCommands => { + return lando.Promise.mapSeries(eventCommands, eventCommand => { + if (undefined !== eventCommand.toolingTask) { + const inquiry = formatters.getInteractive(eventCommand.toolingTask.options, eventCommand.answers); + return formatters.handleInteractive(inquiry, eventCommand.answers, eventCommand.toolingTask.command, lando) + .then(answers => eventCommand.toolingTask.run(_.merge(eventCommand.answers, answers))); + } else { + return injectable.engine.run(eventCommands); + } + }); + }).catch(err => { const command = _.tail(event.split('-')).join('-'); if (app.addMessage) { const message = _.trim(_.get(err, 'message')) || 'UNKNOWN ERROR'; @@ -44,5 +68,16 @@ module.exports = async (app, lando, cmds, data, event) => { } else { lando.exitCode = 12; } + }).finally(() => { + const initToolingRunners = _.filter(eventCommands, eventCommand => true === eventCommand.isInitEventCommand); + if (_.isEmpty(initToolingRunners)) { + return; + } + const run = _.first(initToolingRunners); + + run.opts = {purge: true, mode: 'attach'}; + return injectable.engine.stop(run) + .then(() => injectable.engine.destroy(run)) + .then(() => remove(path.dirname(run.compose[0]))); }); }; diff --git a/hooks/lando-run-setup.js b/hooks/lando-run-setup.js index 331ee65aa..cf43d5e0c 100644 --- a/hooks/lando-run-setup.js +++ b/hooks/lando-run-setup.js @@ -32,7 +32,7 @@ module.exports = async lando => { // reload plugins await lando.reloadPlugins(); // reload needed config - const {orchestratorBin, orchestratorVersion, dockerBin, engineConfig} = require('../utils/build-config')(); + const {orchestratorBin, orchestratorVersion, dockerBin, engineConfig} = require('../utils/build-config')(lando.config); // reset needed config lando.config = {...lando.config, orchestratorBin, orchestratorVersion, dockerBin, engineConfig}; // we need to explicitly reset this for some reason diff --git a/hooks/lando-setup-build-engine-darwin.js b/hooks/lando-setup-build-engine-darwin.js index c291b7ba7..337cf629a 100644 --- a/hooks/lando-setup-build-engine-darwin.js +++ b/hooks/lando-setup-build-engine-darwin.js @@ -10,6 +10,9 @@ const semver = require('semver'); const {color} = require('listr2'); const buildIds = { + '4.37.2': '179585', + '4.37.1': '178610', + '4.37.0': '178034', '4.36.0': '175267', '4.35.1': '173168', '4.35.0': '172550', diff --git a/hooks/lando-setup-build-engine-win32.js b/hooks/lando-setup-build-engine-win32.js index bfedd2ce8..b21a1b311 100644 --- a/hooks/lando-setup-build-engine-win32.js +++ b/hooks/lando-setup-build-engine-win32.js @@ -10,6 +10,8 @@ const {color} = require('listr2'); const {nanoid} = require('nanoid'); const buildIds = { + '4.37.1': '178610', + '4.37.0': '178034', '4.36.0': '175267', '4.35.1': '173168', '4.35.0': '172550', diff --git a/hooks/lando-setup-build-engine-wsl.js b/hooks/lando-setup-build-engine-wsl.js index 2877cbf6a..b2e2a8bc6 100644 --- a/hooks/lando-setup-build-engine-wsl.js +++ b/hooks/lando-setup-build-engine-wsl.js @@ -12,6 +12,8 @@ const {color} = require('listr2'); const {nanoid} = require('nanoid'); const buildIds = { + '4.37.1': '178610', + '4.37.0': '178034', '4.36.0': '175267', '4.35.1': '173168', '4.35.0': '172550', diff --git a/hooks/lando-setup-orchestrator.js b/hooks/lando-setup-orchestrator.js index 7d6144780..d01dc7735 100644 --- a/hooks/lando-setup-orchestrator.js +++ b/hooks/lando-setup-orchestrator.js @@ -7,7 +7,7 @@ const path = require('path'); /* * Helper to get docker compose v2 download url */ -const getComposeDownloadUrl = (version = '2.30.3') => { +const getComposeDownloadUrl = (version = '2.31.0') => { const mv = version.split('.')[0] > 1 ? '2' : '1'; const arch = process.arch === 'arm64' ? 'aarch64' : 'x86_64'; const toggle = `${process.platform}-${mv}`; @@ -31,7 +31,7 @@ const getComposeDownloadUrl = (version = '2.30.3') => { /* * Helper to get docker compose v2 download destination */ -const getComposeDownloadDest = (base, version = '2.30.3') => { +const getComposeDownloadDest = (base, version = '2.31.0') => { switch (process.platform) { case 'linux': case 'darwin': diff --git a/lib/app.js b/lib/app.js index 18f3f6326..712498c41 100644 --- a/lib/app.js +++ b/lib/app.js @@ -6,12 +6,13 @@ const hasher = require('object-hash'); const path = require('path'); const Promise = require('./promise'); const utils = require('./utils'); +const fs = require('node:fs'); /* * Helper to init and then report */ -const initAndReport = (app, method = 'start') => { - return app.init().then(() => { +const initAndReport = (app, method, shouldBootstrap = false) => { + return app.init({shouldBootstrap}).then(() => { app.metrics.report(method, utils.metricsParse(app)); return Promise.resolve(true); }); @@ -56,7 +57,7 @@ module.exports = class App { * @alias app.name */ this.name = require('../utils/slugify')(name); - this.project = require('../utils/docker-composify')(this.name); + this.project = this.name; this._serviceApi = 3; this._config = lando.config; this._defaultService = 'appserver'; @@ -256,6 +257,8 @@ module.exports = class App { .then(() => this.log.info('destroyed app.')); }; + static isBootstrapCommand = undefined; + /** * Initializes the app * @@ -269,18 +272,45 @@ module.exports = class App { * @fires ready * @return {Promise} A Promise. */ - init({noEngine = false} = {}) { + init({noEngine = false, shouldBootstrap = false} = {}) { // We should only need to initialize once, if we have just go right to app ready if (this.initialized) return this.events.emit('ready', this); - // Get compose data if we have any, otherwise set to [] - const composeFiles = require('../utils/load-compose-files')(_.get(this, 'config.compose', []), this.root); - this.composeData = [new this.ComposeService('compose', {}, ...composeFiles)]; - // Validate and set env files - this.envFiles = require('../utils/normalize-files')(_.get(this, 'config.env_file', []), this.root); - // Log some things - this.log.verbose('initiatilizing app at %s...', this.root); - this.log.silly('app has config', this.config); + if (undefined === App.isBootstrapCommand) { + App.isBootstrapCommand = !fs.existsSync(this._dir); + } + const bootstrapping = App.isBootstrapCommand && shouldBootstrap && !noEngine; + if (bootstrapping) { + console.log(require('yargonaut').chalk().cyan('Looks like this is the first time to start the app. Lets bootstrap it...')); + } + + const composeEnvFiles = require('../utils/normalize-files')(_.get(this, 'config.compose_env_file', []), this.root); + return loadPlugins(this, this._lando) + /** + * Event that only gets triggered if the app never started before (or was destroyed) + * + * @since 3.23.25 + * @alias app.events:pre-bootstrap + * @event pre-bootstrap + * @property {App} app The app instance. + */ + .then(() => bootstrapping ? this.events.emit('pre-bootstrap', this) : undefined) + // Get compose data if we have any, otherwise set to [] + .then(() => noEngine === true ? [] : require('../utils/load-compose-files')( + _.get(this, 'config.compose', []), + this.root, + this._dir, + (composeFiles, outputFilePath) => + this.engine.getComposeConfig({compose: composeFiles, project: this.project, outputFilePath, opts: {envFiles: composeEnvFiles}}), + )) + .then(composeFileData => { + this.composeData = [new this.ComposeService('compose', {}, ...composeFileData)]; + // Validate and set env files + this.envFiles = require('../utils/normalize-files')(_.get(this, 'config.env_file', []), this.root); + // Log some things + this.log.verbose('initiatilizing app at %s...', this.root); + this.log.silly('app has config', this.config); + }) /** * Event that allows altering of the app object right before it is * initialized. @@ -293,8 +323,7 @@ module.exports = class App { * @event pre_init * @property {App} app The app instance. */ - return loadPlugins(this, this._lando).then(() => this.events.emit('pre-init', this)) - + .then(() => this.events.emit('pre-init', this)) // Actually assemble this thing so its ready for that engine .then(() => { // Get all the services @@ -490,13 +519,15 @@ module.exports = class App { * @alias app.start * @fires pre_start * @fires post_start + * @fires post_bootstrap * @return {Promise} A Promise. * */ start() { // Log this.log.info('starting app...'); - return initAndReport(this) + + return initAndReport(this, 'start', true) /** * Event that runs before an app starts up. @@ -524,6 +555,17 @@ module.exports = class App { * @event post_start */ .then(() => this.events.emit('post-start')) + + /** + * Event that only gets triggered if the app never started before (or was destroyed) + * + * @since 3.23.25 + * @alias app.events:post-bootstrap + * @event post-bootstrap + * @property {App} app The app instance. + */ + .then(() => App.isBootstrapCommand ? this.events.emit('post-bootstrap', this) : undefined) + .then(() => this.log.info('started app.')); }; diff --git a/lib/cli.js b/lib/cli.js index 60098cc6a..6ba89dd07 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -317,6 +317,7 @@ module.exports = class Cli { .option('help', globalOptions.help) .option('verbose', globalOptions.verbose) .version(false) + .env('lando_cli_') .middleware([(argv => { argv._app = config; argv._yargs = yargs; diff --git a/lib/compose.js b/lib/compose.js index 34cc67c18..a5c21de19 100644 --- a/lib/compose.js +++ b/lib/compose.js @@ -20,6 +20,20 @@ const composeFlags = { rm: '--rm', timestamps: '--timestamps', volumes: '-v', + outputFilePath: '-o', +}; + +const composeFlagOptionMapping = { + build: ['noCache', 'pull', 'q'], + down: ['removeOrphans', 'volumes'], + exec: ['background', 'detach', 'noTTY'], + kill: ['removeOrphans'], + logs: ['follow', 'timestamps'], + ps: ['q'], + pull: ['q'], + rm: ['force', 'volumes'], + up: ['background', 'detach', 'noRecreate', 'noDeps', 'pull', 'q', 'recreate', 'removeOrphans', 'timestamps'], + config: ['outputFilePath', 'q'], }; // Default options nad things @@ -33,12 +47,20 @@ const defaultOptions = { pull: {}, rm: {force: true, volumes: true}, up: {background: true, noRecreate: true, recreate: false, removeOrphans: true}, + config: {}, }; /* * Helper to merge options with default */ -const mergeOpts = (run, opts = {}) => _.merge({}, defaultOptions[run], opts); +const mergeOpts = (run, opts = {}) => _.merge( + {}, + defaultOptions[run], + _.pickBy( + opts, + (value, index) => (!_.includes(_.keys(composeFlags), index)) || _.includes(composeFlagOptionMapping[run], index), + ), +); /* * Parse docker-compose options @@ -55,21 +77,22 @@ const parseOptions = (opts = {}) => { /* * Helper to standardize construction of docker commands */ -const buildCmd = (run, name, compose, {services, cmd}, opts = {}) => { +const buildCmd = (run, name, compose, {services, cmd, envFiles}, opts = {}) => { if (!name) throw new Error('Need to give this composition a project name!'); // @TODO: we need to strip out opts.user on start/stop because we often get it as part of run const project = ['--project-name', name]; const files = _.flatten(_.map(compose, unit => ['--file', unit])); + const envFile = _.flatten(_.map(envFiles, unit => ['--env-file', unit])); const options = parseOptions(opts); const argz = _.flatten(_.compact([services, cmd])); - return _.flatten([project, files, run, options, argz]); + return _.flatten([project, files, envFile, run, options, argz]); }; /* * Helper to build build object needed by lando.shell.sh */ const buildShell = (run, name, compose, opts = {}) => ({ - cmd: buildCmd(run, name, compose, {services: opts.services, cmd: opts.cmd}, mergeOpts(run, opts)), + cmd: buildCmd(run, name, compose, {services: opts.services, cmd: opts.cmd, envFiles: opts.envFiles ?? []}, mergeOpts(run, opts)), opts: {mode: 'spawn', cstdio: opts.cstdio, silent: opts.silent}, }); @@ -155,3 +178,8 @@ exports.start = (compose, project, opts = {}) => buildShell('up', project, compo * Run docker compose stop */ exports.stop = (compose, project, opts = {}) => buildShell('stop', project, compose, opts); + +/* + * Run docker compose config + */ +exports.config = (compose, project, opts = {}) => buildShell('config', project, compose, opts); diff --git a/lib/daemon.js b/lib/daemon.js index d98da204e..df5b31baa 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -40,7 +40,7 @@ const buildDockerCmd = (cmd, scriptsDir, dockerBin) => { */ const getMacProp = prop => shell.sh(['defaults', 'read', `${MACOS_BASE}/Contents/Info.plist`, prop]) .then(data => _.trim(data)) - .catch(() => null); + .catch(() => 'skip'); /* * Creates a new Daemon instance. @@ -53,7 +53,7 @@ module.exports = class LandoDaemon { log = new Log(), context = 'node', compose = require('../utils/get-compose-x')(), - orchestratorVersion = '2.30.3', + orchestratorVersion = '2.31.0', userConfRoot = path.join(os.homedir(), '.lando'), ) { this.cache = cache; diff --git a/lib/docker.js b/lib/docker.js index 125594068..52e8d2daf 100644 --- a/lib/docker.js +++ b/lib/docker.js @@ -92,7 +92,7 @@ module.exports = class Landerode extends Dockerode { // Filter by app name if an app name was given. .then(containers => { if (options.project) return _.filter(containers, c => c.app === options.project); - else if (options.app) return _.filter(containers, c => c.app === require('../utils/docker-composify')(options.app)); // eslint-disable-line max-len + else if (options.app) return _.filter(containers, c => c.app === options.app); return containers; }) // Then finally filter by everything else diff --git a/lib/engine.js b/lib/engine.js index ea90ec07a..db344264f 100644 --- a/lib/engine.js +++ b/lib/engine.js @@ -172,7 +172,7 @@ module.exports = class Engine { * return lando.engine.exists(compose); */ exists(data) { - return this.engineCmd('exists', data); + return this.engineCmd('exists', _.merge({}, {separator: this.separator}, data)); }; /* @@ -495,5 +495,26 @@ module.exports = class Engine { // stop return this.engineCmd('stop', data); }; + + /** + * Get dumped docker compose config for compose files from project + * using a `compose` object with `{compose: compose, project: project, opts: opts}` + * + * @since 3.0.0 + * @param {Object} data Config needs a service within a compose context + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {String} [data.outputFilePath='/path/to/file.yml'] String to output path + * @param {Object} [data.opts] Options + * @param {Array} [data.opts.envFiles] An Array of paths to env files + * @return {Promise} A Promise. + * @example + * return lando.engine.stop(app); + */ + getComposeConfig(data) { + data.opts = {cmd: ['-o', data.outputFilePath]}; + delete data.outputFilePath; + return this.engineCmd('config', data); + }; }; diff --git a/lib/formatters.js b/lib/formatters.js index c86e63695..5af6e6662 100644 --- a/lib/formatters.js +++ b/lib/formatters.js @@ -135,7 +135,7 @@ exports.handleInteractive = (inquiry, argv, command, lando) => lando.Promise.try // NOTE: We need to clone deep here otherwise any apps with interactive options get 2x all their events // NOTE: Not exactly clear on why app here gets conflated with the app returned from lando.getApp const app = _.cloneDeep(lando.getApp(argv._app.root)); - return app.init().then(() => { + return app.init({noEngine: true}).then(() => { inquiry = exports.getInteractive(_.find(app.tasks.concat(lando.tasks), {command: command}).options, argv); return inquirer.prompt(_.sortBy(inquiry, 'weight')); }); diff --git a/lib/router.js b/lib/router.js index ca844fbf9..70bb57cdd 100644 --- a/lib/router.js +++ b/lib/router.js @@ -55,7 +55,7 @@ exports.destroy = (data, compose, docker) => retryEach(data, datum => { exports.exists = (data, compose, docker, ids = []) => { if (data.compose) return compose('getId', data).then(id => !_.isEmpty(id)); else { - return docker.list() + return docker.list({}, data.separator) .each(container => { ids.push(container.id); ids.push(container.name); @@ -85,6 +85,19 @@ exports.run = (data, compose, docker, started = true) => Promise.mapSeries(norma // if this is a prestart build step and its not the last one make sure we set started = true // this prevents us from having to stop and then restart the container during builds started = _.get(datum, 'opts.prestart', false) && !_.get(datum, 'opts.last', false); + + const cmd = [ + '/bin/sh', + '-c', + // eslint-disable-next-line max-len + 'if [ "$LANDO_SERVICE_API" = "3" ]; then if [ -f /helpers/check-entrypoint-ran.sh ]; then /helpers/check-entrypoint-ran.sh; fi fi', + ]; + return compose('run', _.merge( + {}, + datum, + {opts: {cmd, id: datum.id, user: 'root', mode: 'attach'}}, + ), + ); }); } }) @@ -133,3 +146,5 @@ exports.start = (data, compose) => retryEach(data, datum => compose('start', dat exports.stop = (data, compose, docker) => retryEach(data, datum => { return (datum.compose) ? compose(data.kill ? 'kill' : 'stop', datum) : docker.stop(getContainerId(datum)); }); + +exports.config = (data, compose) => retryEach(data, datum => compose('config', datum)); diff --git a/lib/updates.js b/lib/updates.js index 0791b6f35..49091e2b6 100644 --- a/lib/updates.js +++ b/lib/updates.js @@ -127,13 +127,26 @@ module.exports = class UpdateManager { const {data, status, url} = await octokit.rest.repos.listReleases({owner: 'lando', repo: 'core'}); this.debug('retrieved cli information from %o [%o]', url, status); + const corePlugin = await this.plugins.find(plugin => plugin.core); + if (undefined === corePlugin) { + throw new Error('We should find a core!'); + } + let newestCoreVersion = corePlugin.check4Update()?.update?.version ?? corePlugin.version; + if (undefined === newestCoreVersion) { + throw new Error('Could not obtain the next lando core version!'); + } + newestCoreVersion = semver.clean(newestCoreVersion); + const versions = data .map(release => ({...release, version: semver.clean(release.tag_name)})) .filter(release => semver.valid(release.version) !== null) .filter(release => semver.satisfies(release.version, '>=3.0.0 <4', {loose: true, includePrerelease: true})) .filter(release => release.draft === false) .filter(release => this.channel === 'edge' ? true : release.prerelease === false) - .map(release => release.version); + .map(release => release.version) + .filter(version => null === newestCoreVersion ? + true : require('../utils/is-lte-version')(version, newestCoreVersion), + ); // get highest version const hv = semver.rsort(versions)[0]; diff --git a/netlify.toml b/netlify.toml index 554d1debf..1c79b4c45 100644 --- a/netlify.toml +++ b/netlify.toml @@ -20,6 +20,7 @@ "https://docs.google.com/document", "https://docs.google.com/forms", "https://github.com", + "https://www.drupal.org/community/events", "/v/" ] skipPatterns = [ diff --git a/package-lock.json b/package-lock.json index 5a5bfa0a1..05b3420ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "@lando/core", - "version": "3.23.22", + "name": "@florianpat/lando-core", + "version": "3.23.27-4florianPat.2", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@lando/core", - "version": "3.23.20", + "name": "@florianpat/lando-core", + "version": "3.23.27-4florianPat.2", "license": "GPL-3.0", "dependencies": { "@lando/argv": "^1.1.2", @@ -76,7 +76,7 @@ "devDependencies": { "@babel/eslint-parser": "^7.16.0", "@lando/leia": "^1.0.0-beta.4", - "@lando/vitepress-theme-default-plus": "github:lando/vitepress-theme-default-plus#main", + "@lando/vitepress-theme-default-plus": "^1.1.1", "@yao-pkg/pkg": "^5.16.1", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", @@ -1840,8 +1840,9 @@ } }, "node_modules/@lando/vitepress-theme-default-plus": { - "version": "1.1.0-beta.24", - "resolved": "git+ssh://git@github.com/lando/vitepress-theme-default-plus.git#480fb59734af4ff45c06fb48ff60289355c6a93f", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lando/vitepress-theme-default-plus/-/vitepress-theme-default-plus-1.1.1.tgz", + "integrity": "sha512-DcwDvsUHJ8n9ofKRgclbkEXGl5DglzUSQdXNEY1MvoSMKGDTP/iZUXR5acZk6gsOmaUyQ3/vBRrIjXUvCsC1vQ==", "dev": true, "license": "GPL-3.0", "dependencies": { @@ -14169,9 +14170,9 @@ } }, "node_modules/undici": { - "version": "6.20.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.1.tgz", - "integrity": "sha512-AjQF1QsmqfJys+LXfGTNum+qw4S88CojRInG/6t31W/1fk6G59s92bnAvGz5Cmur+kQv2SURXEvvudLmbrE8QA==", + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", "license": "MIT", "engines": { "node": ">=18.17" @@ -14423,9 +14424,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 58a0e4b69..6ae4aa90f 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "@lando/core", - "description": "The libraries that power all of Lando.", - "version": "3.23.22", - "author": "Mike Pirog @pirog", + "name": "@florianpat/lando-core", + "description": "The libraries that power all of Lando. Fork by flo for compose integration", + "version": "3.23.27-4florianPat.2", + "author": "Florian Patruck @florianPat", "license": "GPL-3.0", - "repository": "lando/core", - "bugs": "https://github.com/lando/core/issues/new/choose", - "homepage": "https://github.com/lando/core", + "repository": "florianPat/lando-core", + "bugs": "https://github.com/florianPat/lando-core/issues/new/choose", + "homepage": "https://github.com/florianPat/lando-core", "keywords": [ "lando", "lando-plugin" @@ -168,7 +168,7 @@ "devDependencies": { "@babel/eslint-parser": "^7.16.0", "@lando/leia": "^1.0.0-beta.4", - "@lando/vitepress-theme-default-plus": "github:lando/vitepress-theme-default-plus#main", + "@lando/vitepress-theme-default-plus": "^1.1.1", "@yao-pkg/pkg": "^5.16.1", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", diff --git a/plugins/networking/app.js b/plugins/networking/app.js index 5f8731402..e01e3fb27 100644 --- a/plugins/networking/app.js +++ b/plugins/networking/app.js @@ -22,7 +22,8 @@ module.exports = (app, lando) => { return landonet.disconnect({Container: container.id, Force: true}) // Only throw non not connected errors .catch(error => { - if (!_.includes(error.message, 'is not connected to network')) throw error; + if (!_.includes(error.message, 'is not connected to network') && + !_.includes(error.message, 'is not connected to the network')) throw error; }) // Connect .then(() => { @@ -67,7 +68,8 @@ module.exports = (app, lando) => { return bridgeNet.disconnect({Container: proxyContainer, Force: true}) // Only throw non not connected errors .catch(error => { - if (!_.includes(error.message, 'is not connected to network')) throw error; + if (!_.includes(error.message, 'is not connected to network') && + !_.includes(error.message, 'is not connected to the network')) throw error; }) // Connect .then(() => { diff --git a/plugins/proxy/app.js b/plugins/proxy/app.js index f5070d2d2..4cf866799 100644 --- a/plugins/proxy/app.js +++ b/plugins/proxy/app.js @@ -182,19 +182,17 @@ module.exports = (app, lando) => { service.labels['traefik.enable'] = true; service.labels['traefik.docker.network'] = lando.config.proxyNet; service.environment.LANDO_PROXY_PASSTHRU = _.toString(lando.config.proxyPassThru); - const proxyVolume = `${lando.config.proxyName}_proxy_config`; return { services: _.set({}, service.name, { networks: {'lando_proxyedge': {}}, labels: service.labels, environment: service.environment, volumes: [ - `${proxyVolume}:/proxy_config`, + `${lando.config.proxyConfigDir}:/proxy_config`, `${lando.config.userConfRoot}/scripts/proxy-certs.sh:/scripts/100-proxy-certs`, ], }), networks: {'lando_proxyedge': {name: lando.config.proxyNet, external: true}}, - volumes: _.set({}, proxyVolume, {external: true}), }; }) diff --git a/plugins/proxy/builders/_proxy.js b/plugins/proxy/builders/_proxy.js index 83a623f95..1b5c3b8d8 100644 --- a/plugins/proxy/builders/_proxy.js +++ b/plugins/proxy/builders/_proxy.js @@ -6,11 +6,18 @@ const _ = require('lodash'); /* * Helper to get core proxy service */ -const getProxy = ({proxyCommand, proxyPassThru, proxyDomain, userConfRoot, version = 'unknown'} = {}) => { +const getProxy = ({ + proxyCommand, + proxyPassThru, + proxyDomain, + userConfRoot, + proxyConfigDir, + version = 'unknown', +} = {}) => { return { services: { proxy: { - image: 'traefik:2.2.0', + image: 'traefik:2.11.16', command: proxyCommand.join(' '), environment: { LANDO_APP_PROJECT: '_lando_', @@ -23,7 +30,7 @@ const getProxy = ({proxyCommand, proxyPassThru, proxyDomain, userConfRoot, versi volumes: [ '/var/run/docker.sock:/var/run/docker.sock', `${userConfRoot}/scripts/proxy-certs.sh:/scripts/100-proxy-certs`, - 'proxy_config:/proxy_config', + `${proxyConfigDir}:/proxy_config`, ], }, }, @@ -32,9 +39,6 @@ const getProxy = ({proxyCommand, proxyPassThru, proxyDomain, userConfRoot, versi driver: 'bridge', }, }, - volumes: { - proxy_config: {}, - }, }; }; diff --git a/plugins/proxy/index.js b/plugins/proxy/index.js index 9ff6485cb..ca13fb606 100644 --- a/plugins/proxy/index.js +++ b/plugins/proxy/index.js @@ -14,7 +14,7 @@ const defaultConfig = { '/entrypoint.sh', '--log.level=DEBUG', '--api.insecure=true', - '--api.dashboard=false', + '--api.dashboard=true', '--providers.docker=true', '--entrypoints.https.address=:443', '--entrypoints.http.address=:80', diff --git a/plugins/proxy/scripts/proxy-certs.sh b/plugins/proxy/scripts/proxy-certs.sh index 66ae3b38f..946275fa0 100644 --- a/plugins/proxy/scripts/proxy-certs.sh +++ b/plugins/proxy/scripts/proxy-certs.sh @@ -28,11 +28,6 @@ fi : ${LANDO_PROXY_KEY:="/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.key"} : ${LANDO_PROXY_CONFIG_FILE:="/proxy_config/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.yaml"} -# Move over any global config set by lando -if [ -d "/lando/proxy/config" ]; then - cp -rf /lando/proxy/config/* /proxy_config/ -fi - # Bail if proxypassthru is off if [ "$LANDO_PROXY_PASSTHRU" != "true" ]; then lando_info "Proxy passthru is off so exiting..." diff --git a/release-aliases/3-EDGE b/release-aliases/3-EDGE index 2b4687a7e..fd8a32599 100644 --- a/release-aliases/3-EDGE +++ b/release-aliases/3-EDGE @@ -1 +1 @@ -v3.24.0-beta.8 +v3.24.0-beta.12 diff --git a/release-aliases/3-STABLE b/release-aliases/3-STABLE index 71f6f0f88..7a1681c35 100644 --- a/release-aliases/3-STABLE +++ b/release-aliases/3-STABLE @@ -1 +1 @@ -v3.23.22 +v3.23.26 diff --git a/scripts/check-entrypoint-ran.sh b/scripts/check-entrypoint-ran.sh new file mode 100755 index 000000000..b03071e69 --- /dev/null +++ b/scripts/check-entrypoint-ran.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# retry settings +attempt=0 +delay=1 +retry=16 + +until [ "$attempt" -ge "$retry" ] +do + test -f "/tmp/lando-entrypoint-ran" && break + attempt=$((attempt+1)) + sleep "$delay" +done diff --git a/scripts/install-docker-desktop.sh b/scripts/install-docker-desktop.sh index 50fcb2dd7..80440abbf 100755 --- a/scripts/install-docker-desktop.sh +++ b/scripts/install-docker-desktop.sh @@ -59,7 +59,7 @@ debug "INSTALLER: $INSTALLER" debug "USER: $USER" # add accept license if set -if [ "${DEBUG}" == 1 ]; then OPTS="$OPTS --accept-license"; fi +if [ "${ACCEPT_LICENSE}" == 1 ]; then OPTS="$OPTS --accept-license"; fi # run hdiutil attach "$INSTALLER" diff --git a/scripts/install-docker-engine.sh b/scripts/install-docker-engine.sh index f94aaad47..3aa19f69b 100755 --- a/scripts/install-docker-engine.sh +++ b/scripts/install-docker-engine.sh @@ -3,7 +3,7 @@ set -eo pipefail DEBUG=0 INSTALLER="get-docker.sh" -VERSION="27.3.1" +VERSION="27.5.0" OPTS= debug() { diff --git a/scripts/lando-entrypoint.sh b/scripts/lando-entrypoint.sh index 5f37a4bad..96f1a062a 100755 --- a/scripts/lando-entrypoint.sh +++ b/scripts/lando-entrypoint.sh @@ -2,6 +2,10 @@ set -e +if [ -f /tmp/lando-entrypoint-ran ]; then + rm /tmp/lando-entrypoint-ran +fi + # Get the lando logger . /helpers/log.sh @@ -58,7 +62,7 @@ if [ -d "/scripts" ] && [ -z ${LANDO_NO_SCRIPTS+x} ]; then # Keep this for backwards compat and fallback opts chmod +x /scripts/* || true - find /scripts/ -type f -name "*.sh" -exec {} \; + find /scripts/ -type f \( -name "*.sh" -o ! -name "*.*" \) -exec {} \; fi; # Run any bash scripts that we've loaded into the mix for autorun unless we've @@ -73,6 +77,8 @@ fi # @TODO: We should def figure out whether we can get away with running everything through exec at some point lando_info "Lando handing off to: $@" +touch /tmp/lando-entrypoint-ran + # Try to DROP DOWN to another user if we can if [ ! -z ${LANDO_DROP_USER+x} ]; then lando_debug "Running command as ${LANDO_DROP_USER}..." diff --git a/scripts/user-perm-helpers.sh b/scripts/user-perm-helpers.sh index 28db38732..f09d8fbaa 100755 --- a/scripts/user-perm-helpers.sh +++ b/scripts/user-perm-helpers.sh @@ -10,31 +10,25 @@ LANDO_MODULE="userperms" add_user() { local USER=$1 local GROUP=$2 - local UID=$3 - local GID=$4 - local DISTRO=$5 - local EXTRAS="$6" - if [ "$DISTRO" = "alpine" ]; then - if ! groups | grep "$GROUP" > /dev/null 2>&1; then addgroup -g "$GID" "$GROUP" 2>/dev/null; fi - if ! id -u "$GROUP" > /dev/null 2>&1; then adduser -H -D -G "$GROUP" -u "$UID" "$USER" "$GROUP" 2>/dev/null; fi - else - if ! groups | grep "$GROUP" > /dev/null 2>&1; then groupadd --force --gid "$GID" "$GROUP" 2>/dev/null; fi - if ! id -u "$GROUP" > /dev/null 2>&1; then useradd --gid "$GID" --uid "$UID" $EXTRAS "$USER" 2>/dev/null; fi - fi; + local WEBROOT_UID=$3 + local WEBROOT_GID=$4 + if ! getent group | cut -d: -f1 | grep "$GROUP" > /dev/null 2>&1; then addgroup -g "$WEBROOT_GID" "$GROUP" 2>/dev/null; fi + if ! id -u "$USER" > /dev/null 2>&1; then adduser -H -D -G "$GROUP" -u "$WEBROOT_UID" "$USER" "$GROUP" 2>/dev/null; fi } # Verify user verify_user() { local USER=$1 local GROUP=$2 - local DISTRO=$3 id -u "$USER" > /dev/null 2>&1 - groups | grep "$GROUP" > /dev/null 2>&1 - if [ "$DISTRO" = "alpine" ]; then + groups "$USER" | grep "$GROUP" > /dev/null 2>&1 + if command -v chsh > /dev/null 2>&1 ; then + if command -v /bin/bash > /dev/null 2>&1 ; then + chsh -s /bin/bash $USER || true + fi; + else true # is there a chsh we can use? do we need to? - else - chsh -s /bin/bash $USER || true fi; } @@ -59,11 +53,10 @@ reset_user() { if [ "$(id -u $USER)" != "$HOST_UID" ]; then usermod -o -u "$HOST_UID" "$USER" 2>/dev/null fi - groupmod -g "$HOST_GID" "$GROUP" 2>/dev/null || true - if [ "$(id -u $USER)" != "$HOST_UID" ]; then + groupmod -o -g "$HOST_GID" "$GROUP" 2>/dev/null || true + if [ "$(id -g $USER)" != "$HOST_GID" ]; then usermod -g "$HOST_GID" "$USER" 2>/dev/null || true fi - usermod -a -G "$GROUP" "$USER" 2>/dev/null || true fi; # If this mapping is incorrect lets abort here if [ "$(id -u $USER)" != "$HOST_UID" ]; then @@ -84,6 +77,7 @@ perm_sweep() { chown -R $USER:$GROUP /usr/local/bin chown $USER:$GROUP /var/www chown $USER:$GROUP /app + chown $USER:$GROUP /tmp chmod 755 /var/www # Do other dirs first if we have them @@ -97,7 +91,6 @@ perm_sweep() { nohup find /user/.ssh -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err & nohup find /var/www -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err & nohup find /usr/local/bin -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err & - nohup chmod -R 755 /var/www >/dev/null 2>&1 & # Lets also make some /usr/locals chowned nohup find /usr/local/lib -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err & diff --git a/tasks/exec.js b/tasks/exec.js index 89385aaae..2eb036acf 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -1,11 +1,11 @@ 'use strict'; // Modules -const fs = require('fs'); -const path = require('path'); const _ = require('lodash'); const {color} = require('listr2'); +const fs = require('fs'); +const path = require('path'); // @TODO: when we have a file for recipes/compose we can set choices on service @@ -14,7 +14,7 @@ module.exports = (lando, config = lando.appConfig) => ({ describe: 'Runs command(s) on a service', usage: '$0 exec [--user ] -- ', override: true, - level: 'engine', + level: 'app', examples: [ '$0 exec appserver -- lash bash', '$0 exec nginx --user root -- whoami', @@ -40,7 +40,8 @@ module.exports = (lando, config = lando.appConfig) => ({ // if no app then we need to throw if (!fs.existsSync(minapp.composeCache)) { - throw new Error('Could not detect a built app. Rebuild or move into the correct location!'); + const app = lando.getApp(options._app.root); + await app.init(); } // Build a minimal app @@ -59,7 +60,7 @@ module.exports = (lando, config = lando.appConfig) => ({ } // nice things - const aservices = app?.config?.allServices ?? app?.allServices ?? []; + const aservices = app.allServices; const choices = `[${color.green('choices:')} ${aservices.map(service => `"${service}"`).join(', ')}]`; // gather our options @@ -127,6 +128,8 @@ module.exports = (lando, config = lando.appConfig) => ({ ropts.push(sconf?.overrides?.working_dir ?? sconf?.working_dir); // mix in mount if applicable ropts.push(app?.mounts[options.service]); + ropts.push(!options.deps ?? false); + ropts.push(options.autoRemove ?? true); // emit pre-exec await app.events.emit('pre-exec', config); @@ -137,18 +140,12 @@ module.exports = (lando, config = lando.appConfig) => ({ // try to run it try { lando.log.debug('running exec command %o on %o', runner.cmd, runner.id); - await require('../utils/build-docker-exec')(lando, 'inherit', runner); + await lando.engine.run(runner); // error } catch (error) { - return lando.engine.isRunning(runner.id).then(isRunning => { - if (!isRunning) { - throw new Error(`Looks like your app is stopped! ${color.bold('lando start')} it up to exec your heart out.`); - } else { - error.hide = true; - throw error; - } - }); + error.hide = true; + throw error; // finally } finally { diff --git a/tasks/info.js b/tasks/info.js index 0a77a5018..94d68f191 100644 --- a/tasks/info.js +++ b/tasks/info.js @@ -34,11 +34,12 @@ module.exports = lando => ({ const getData = async () => { // go deep if (options.deep) { + const separator = _.get(app, '_config.orchestratorSeparator', '_'); return await lando.engine.list({project: app.project}) .map(async container => await lando.engine.scan(container)) .filter(container => { if (!options.service) return true; - return options.service.map(service => `/${app.project}_${service}_1`).includes(container.Name); + return options.service.map(service => `/${app.project}${separator}${service}${separator}1`).includes(container.Name); }); // normal info diff --git a/tasks/ssh.js b/tasks/ssh.js index ac325fc57..c4f545f95 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -40,6 +40,8 @@ module.exports = (lando, app) => ({ const api = _.get(_.find(app.info, {service}), 'api', 3); // set additional opt defaults if possible const opts = [undefined, api === 4 ? undefined : '/app']; + opts[2] = !app._config.command.deps ?? false; + opts[3] = app._config.command.autoRemove ?? true; // mix any v4 service info on top of app.config.services const services = _(_.get(app, 'config.services', {})) .map((service, id) => _.merge({}, {id}, service)) diff --git a/test/compose.spec.js b/test/compose.spec.js index d282600b0..0b599cdc9 100644 --- a/test/compose.spec.js +++ b/test/compose.spec.js @@ -202,4 +202,16 @@ describe('compose', () => { expect(stopResult).to.be.an('object'); }); }); + + describe('#config', () => { + it('should return the correct default options when not specified'); + it('#config should return an object.', () => { + const configResult = compose.config( + ['string1', 'string2'], + 'my_project', + myOpts, + ); + expect(configResult).to.be.an('object'); + }); + }); }); diff --git a/test/get-docker-bin-path.spec.js b/test/get-docker-bin-path.spec.js index cf6065141..7aa37b0a1 100644 --- a/test/get-docker-bin-path.spec.js +++ b/test/get-docker-bin-path.spec.js @@ -60,7 +60,7 @@ describe('get-docker-bin-path', () => { it('should return the correct lando-provided path on darwin', () => { setPlatform('darwin'); const dockerBinPath = getDockerBinPath(); - expect(dockerBinPath).to.equal('/Applications/Docker.app/Contents/Resources/bin'); + expect(dockerBinPath).to.equal('/usr/bin'); resetPlatform(); }); }); diff --git a/test/get-docker-x.spec.js b/test/get-docker-x.spec.js index d1952aebe..da8fdfadb 100644 --- a/test/get-docker-x.spec.js +++ b/test/get-docker-x.spec.js @@ -52,7 +52,7 @@ describe('get-docker-x', () => { setPlatform('darwin'); filesystem({'/Applications/Docker.app/Contents/Resources/bin/docker': 'CODEZ'}); const dockerExecutable = getDockerExecutable(); - expect(dockerExecutable).to.equal('/Applications/Docker.app/Contents/Resources/bin/docker'); + expect(dockerExecutable).to.equal('.'); filesystem.restore(); resetPlatform(); }); diff --git a/test/get-user.spec.js b/test/get-user.spec.js index d756b0b17..abf5c4216 100644 --- a/test/get-user.spec.js +++ b/test/get-user.spec.js @@ -29,6 +29,11 @@ describe('get-user', function() { expect(getUser('test-service', info)).to.equal('www-data'); }); + it('should return specified user if service is a "no-api" docker-compose service and user is specified', function() { + const info = [{service: 'test-service', type: 'docker-compose', meUser: 'custom-user'}]; + expect(getUser('test-service', info)).to.equal('custom-user'); + }); + it('should return "www-data" if service.api is 4 but no user is specified', function() { const info = [{service: 'test-service', api: 4}]; expect(getUser('test-service', info)).to.equal('www-data'); diff --git a/utils/build-init-runner.js b/utils/build-init-runner.js index 421a5a164..fb6c410d3 100644 --- a/utils/build-init-runner.js +++ b/utils/build-init-runner.js @@ -6,9 +6,12 @@ module.exports = config => ({ project: config.project, cmd: config.cmd, opts: { + environment: require('./get-cli-env')(config.env), mode: 'attach', user: config.user, services: ['init'], autoRemove: config.remove, + workdir: config.workdir, + prestart: config.prestart, }, }); diff --git a/utils/build-tooling-runner.js b/utils/build-tooling-runner.js index 6436548c7..15c1313f9 100644 --- a/utils/build-tooling-runner.js +++ b/utils/build-tooling-runner.js @@ -4,7 +4,8 @@ const _ = require('lodash'); const path = require('path'); const getContainer = (app, service) => { - return app?.containers?.[service] ?? `${app.project}_${service}_1`; + const separator = _.get(app, '_config.orchestratorSeparator', '_'); + return app?.containers?.[service] ?? `${app.project}${separator}${service}${separator}1`; }; const getContainerPath = (appRoot, appMount = undefined) => { @@ -20,7 +21,17 @@ const getContainerPath = (appRoot, appMount = undefined) => { return dir.join('/'); }; -module.exports = (app, command, service, user, env = {}, dir = undefined, appMount = undefined) => ({ +module.exports = ( + app, + command, + service, + user, + env = {}, + dir = undefined, + appMount = undefined, + noDeps = false, + autoRemove = true, +) => ({ id: getContainer(app, service), compose: app.compose, project: app.project, @@ -32,6 +43,8 @@ module.exports = (app, command, service, user, env = {}, dir = undefined, appMou user: (user === null) ? require('./get-user')(service, app.info) : user, services: _.compact([service]), hijack: false, - autoRemove: true, + autoRemove, + noDeps, + prestart: !autoRemove, }, _.identity), }); diff --git a/utils/build-tooling-task.js b/utils/build-tooling-task.js index bf808dcf3..d0d55732f 100644 --- a/utils/build-tooling-task.js +++ b/utils/build-tooling-task.js @@ -1,11 +1,13 @@ 'use strict'; const _ = require('lodash'); +const remove = require('./remove'); +const path = require('path'); module.exports = (config, injected) => { // Get our defaults and such const getToolingDefaults = require('./get-tooling-defaults'); - const {name, app, appMount, cmd, describe, dir, env, options, service, stdio, user} = getToolingDefaults(config); + const {name, app, appMount, cmd, describe, dir, env, options, service, user} = getToolingDefaults(config); // add debug stuff if debuggy env.DEBUG = injected.debuggy ? '1' : ''; @@ -18,29 +20,53 @@ module.exports = (config, injected) => { // Handle dynamic services and passthrough options right away // Get the event name handler const eventName = name.split(' ')[0]; - const run = answers => injected.Promise.try(() => (_.isEmpty(app.compose)) ? app.init() : true) - // Kick off the pre event wrappers - .then(() => app.events.emit(`pre-${eventName}`, config, answers)) - // Get an interable of our commandz - .then(() => _.map(require('./parse-tooling-config')(cmd, service, options, answers, canExec))) - // Build run objects - .map(({command, service}) => require('./build-tooling-runner')(app, command, service, user, env, dir, appMount)) - // Try to run the task quickly first and then fallback to compose launch - .each(runner => require('./build-docker-exec')(injected, stdio, runner).catch(execError => { - return injected.engine.isRunning(runner.id).then(isRunning => { - if (!isRunning) { - return injected.engine.run(runner).catch(composeError => { - composeError.hide = true; - throw composeError; - }); - } else { - execError.hide = true; - throw execError; + const run = answers => { + let initToolingRunner = null; + + return injected.Promise.try(() => (_.isEmpty(app.compose) && '_init' !== service) ? app.init() : true) + // Kick off the pre event wrappers + .then(() => app.events.emit(`pre-${eventName}`, config, answers)) + // Get an interable of our commandz + .then(() => _.map(require('./parse-tooling-config')(cmd, service, name, options, answers, canExec))) + // Build run objects + .map( + ({command, service}) => { + if ('_init' === service) { + initToolingRunner = _.merge( + {}, + require('./build-init-runner')(_.merge( + {}, + require('./get-init-runner-defaults')(app._lando, {destination: app.root, name: app.project, _app: app}), + {cmd: command, workdir: '/app', env}, + )), + ); + + return initToolingRunner; + } + + return require('./build-tooling-runner')( + app, command, service, user, env, dir, appMount, !answers?.deps ?? false, answers?.autoRemove ?? true, + ); + }) + // Try to run the task quickly first and then fallback to compose launch + .each(runner => injected.engine.run(runner).catch(composeError => { + composeError.hide = true; + throw composeError; + }), + ) + // Post event + .then(() => app.events.emit(`post-${eventName}`, config, answers)) + .finally(() => { + if (null === initToolingRunner) { + return; } + + initToolingRunner.opts = {purge: true, mode: 'attach'}; + return injected.engine.stop(initToolingRunner) + .then(() => injected.engine.destroy(initToolingRunner)) + .then(() => remove(path.dirname(initToolingRunner.compose[0]))); }); - })) - // Post event - .then(() => app.events.emit(`post-${eventName}`, config, answers)); + }; // Return our tasks return { @@ -48,5 +74,6 @@ module.exports = (config, injected) => { describe, run, options, + service, }; }; diff --git a/utils/fetch-plugin.js b/utils/fetch-plugin.js index 1f76f075a..6fe521920 100644 --- a/utils/fetch-plugin.js +++ b/utils/fetch-plugin.js @@ -12,6 +12,23 @@ module.exports = async (spec, opts, Plugin = require('../components/plugin')) => // attempt to fetch the plugin const plugin = await Plugin.fetch(spec, opts); + if (plugin.core && plugin.scope && plugin.scope !== '@lando') { + const src = plugin.location; + const dest = path.resolve(plugin.location, '..', '..', plugin.name); + const orgDir = path.resolve(plugin.location, '..'); + + // move and log + fs.moveSync(src, dest, {overwrite: true}); + plugin.debug('lando core detected, moved to %s for cli loading to work', dest); + // remove and log + remove(path.resolve(src, '..')); + plugin.debug('removed dangling and presumably/hopefully empty org scope dir %s', orgDir); + + // get the plugin info again to confirm we moved it to the correct place + // @NOTE: we use new Plugin() here instead of Plugin.info to ensure plugin remains consistent + return new Plugin(dest); + } + // lando 3 plugin loading is at odds with lando 4 plugin installing so we need to move up a directory // if the plugin has non-lando org scope also clean up dangling org dir if (plugin.scope && plugin.scope !== '@lando') { diff --git a/utils/filter-v3-build-steps.js b/utils/filter-v3-build-steps.js index b722a5bb7..30a70b7fd 100644 --- a/utils/filter-v3-build-steps.js +++ b/utils/filter-v3-build-steps.js @@ -33,26 +33,8 @@ module.exports = (services, app, rootSteps = [], buildSteps= [], prestart = fals } }); }); - // Let's silent run user-perm stuff and add a "last" flag + // Let's add a "last" flag if (!_.isEmpty(build)) { - const permsweepers = _(build) - .map(command => ({id: command.id, services: _.get(command, 'opts.services', [])})) - .uniqBy('id') - .value(); - _.forEach(permsweepers, ({id, services}) => { - build.unshift({ - id, - cmd: '/helpers/user-perms.sh --silent', - compose: app.compose, - project: app.project, - opts: { - mode: 'attach', - prestart, - user: 'root', - services, - }, - }); - }); // Denote the last step in the build if its happening before start const last = _.last(build); last.opts.last = prestart; diff --git a/utils/get-app-mounts.js b/utils/get-app-mounts.js index 694ffedb3..521b4af70 100644 --- a/utils/get-app-mounts.js +++ b/utils/get-app-mounts.js @@ -6,7 +6,7 @@ module.exports = app => _(app.services) // Objectify .map(service => _.merge({name: service}, _.get(app, `config.services.${service}`, {}))) // Set the default - .map(config => _.merge({}, config, {app_mount: _.get(config, 'app_mount', 'cached')})) + .map(config => _.merge({}, config, {app_mount: _.get(config, 'app_mount', app.config.app_mount || 'cached')})) // Filter out disabled mountes .filter(config => config.app_mount !== false && config.app_mount !== 'disabled') // Combine together diff --git a/utils/get-app.js b/utils/get-app.js index 7997e9ec9..b555a4399 100644 --- a/utils/get-app.js +++ b/utils/get-app.js @@ -23,8 +23,7 @@ module.exports = (files, userConfRoot) => { if (!config.name) return {}; // cast the name to a string...just to make sure. config.name = require('../utils/slugify')(config.name); - // slugify project - config.project = require('../utils/docker-composify')(config.name); + config.project = config.name; return _.merge({}, config, { configFiles: files, diff --git a/utils/get-compose-x.js b/utils/get-compose-x.js index 5a37f2756..ace77eda6 100644 --- a/utils/get-compose-x.js +++ b/utils/get-compose-x.js @@ -27,7 +27,7 @@ const getDockerBin = (bin, base, pathFallback = true) => { } }; -module.exports = ({orchestratorVersion = '2.30.3', userConfRoot = os.tmpdir()} = {}) => { +module.exports = ({orchestratorVersion = '2.31.0', userConfRoot = os.tmpdir()} = {}) => { const orchestratorBin = `docker-compose-v${orchestratorVersion}`; switch (process.platform) { case 'darwin': diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index cc9b3803b..faa617bcf 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -8,20 +8,20 @@ const os = require('os'); const getBuildEngineVersion = (platform = process.landoPlatform ?? process.platform) => { switch (platform) { case 'darwin': - return '4.36.0'; + return '4.37.2'; case 'linux': - return '27.3.1'; + return '27.5.0'; case 'win32': - return '4.36.0'; + return '4.37.1'; case 'wsl': - return '4.36.0'; + return '4.37.1'; } }; // Default config const defaultConfig = options => ({ orchestratorSeparator: '_', - orchestratorVersion: '2.30.3', + orchestratorVersion: '2.31.0', configSources: [], coreBase: path.resolve(__dirname, '..'), disablePlugins: [], diff --git a/utils/get-docker-bin-path.js b/utils/get-docker-bin-path.js index 273f2690d..a6af527a1 100644 --- a/utils/get-docker-bin-path.js +++ b/utils/get-docker-bin-path.js @@ -5,8 +5,6 @@ const path = require('path'); module.exports = (platform = process.landoPlatform ?? process.platform) => { switch (platform) { - case 'darwin': - return '/Applications/Docker.app/Contents/Resources/bin'; case 'linux': return '/usr/share/lando/bin'; case 'win32': diff --git a/utils/get-init-runner-defaults.js b/utils/get-init-runner-defaults.js index d63666c68..1918ce3c0 100644 --- a/utils/get-init-runner-defaults.js +++ b/utils/get-init-runner-defaults.js @@ -10,6 +10,7 @@ module.exports = (lando, options) => { lando.config.userConfRoot, lando.config.home, options.destination, + _.get(options, '_app', {}), _.cloneDeep(lando.config.appEnv), _.cloneDeep(lando.config.appLabels), _.get(options, 'initImage', 'devwithlando/util:4'), @@ -17,7 +18,7 @@ module.exports = (lando, options) => { const initDir = path.join(lando.config.userConfRoot, 'init', options.name); const initFiles = require('./dump-compose-data')(initData, initDir); // Start to build out some propz and shiz - const project = `${lando.config.product}init` + require('./docker-composify')(options.name); + const project = `${lando.config.product}init` + options.name; const separator = lando.config.orchestratorSeparator; // Return return { @@ -26,5 +27,8 @@ module.exports = (lando, options) => { user: 'www-data', compose: initFiles, remove: false, + workdir: '/', + prestart: true, + env: {}, }; }; diff --git a/utils/get-tasks.js b/utils/get-tasks.js index a7668c804..78777ce4d 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const fs = require('fs'); const path = require('path'); +const App = require('../lib/app'); /* * Paths to / @@ -40,9 +41,10 @@ const loadCacheFile = file => { */ const appRunner = command => (argv, lando) => { const app = lando.getApp(argv._app.root); + const service = _.get(app.config, `tooling.${command}.service`, ''); return lando.events.emit('pre-app-runner', app) .then(() => lando.events.emit('pre-command-runner', app)) - .then(() => app.init().then(() => _.find(app.tasks, {command}).run(argv))); + .then(() => app.init({noEngine: '_init' === service}).then(() => _.find(app.tasks, {command}).run(argv))); }; /* @@ -128,7 +130,7 @@ module.exports = (config = {}, argv = {}, tasks = []) => { // If the tooling command is being called lets assess whether we can get away with engine bootstrap level const ids = _(config.tooling).map(task => task.id).filter(_.identity).value(); - const level = (_.includes(ids, argv._[0])) ? getBsLevel(config, argv._[0]) : 'app'; + const level = !App.isBootstrapCommand && (_.includes(ids, argv._[0])) ? getBsLevel(config, argv._[0]) : 'app'; // Load all the tasks, remember we need to remove "disabled" tasks (eg non-object tasks) here _.forEach(_.get(config, 'tooling', {}), (task, command) => { diff --git a/utils/get-tooling-defaults.js b/utils/get-tooling-defaults.js index 73d24fe0f..d4cf67649 100644 --- a/utils/get-tooling-defaults.js +++ b/utils/get-tooling-defaults.js @@ -12,7 +12,6 @@ module.exports = ({ env = {}, options = {}, service = '', - stdio = 'inherit', user = null, } = {}) => ({ @@ -25,6 +24,5 @@ module.exports = ({ describe: description, options: options, service: service, - stdio: stdio, user, }); diff --git a/utils/get-user.js b/utils/get-user.js index f5b8b2094..ed42f7b87 100644 --- a/utils/get-user.js +++ b/utils/get-user.js @@ -7,8 +7,8 @@ module.exports = (name, info = []) => { if (!_.find(info, {service: name})) return 'www-data'; // otherwise get the service const service = _.find(info, {service: name}); - // if this is a "no-api" service eg type "docker-compose" also return www-data - if (!service.api && service.type === 'docker-compose') return 'www-data'; + // if this is a "no-api" service eg type "docker-compose" return meUser or www-data as default + if (!service.api && service.type === 'docker-compose') return service.meUser || 'www-data'; // otherwise return different things based on the api return service.api === 4 ? service.user || 'www-data' : service.meUser || 'www-data'; }; diff --git a/utils/load-compose-files.js b/utils/load-compose-files.js index f13bf4cec..73a23fcee 100644 --- a/utils/load-compose-files.js +++ b/utils/load-compose-files.js @@ -2,8 +2,32 @@ const _ = require('lodash'); const Yaml = require('./../lib/yaml'); +const path = require('path'); const yaml = new Yaml(); +const fs = require('fs'); +const remove = require('./remove'); -module.exports = (files, dir) => _(require('./normalize-files')(files, dir)) - .map(file => yaml.load(file)) - .value(); +// This just runs `docker compose --project-directory ${dir} config -f ${files} --output ${outputPaths}` to +// make all paths relative to the lando config root +module.exports = async (files, dir, landoComposeConfigDir = undefined, outputConfigFunction = undefined) => { + const composeFilePaths = _(require('./normalize-files')(files, dir)).value(); + if (_.isEmpty(composeFilePaths)) { + return []; + } + + if (undefined === outputConfigFunction) { + return _(composeFilePaths) + .map(file => yaml.load(file)) + .value(); + } + + const outputFile = path.join(landoComposeConfigDir, 'resolved-compose-config.yml'); + + fs.mkdirSync(path.dirname(outputFile), {recursive: true}); + await outputConfigFunction(composeFilePaths, outputFile); + const result = yaml.load(outputFile); + fs.unlinkSync(outputFile); + remove(path.dirname(outputFile)); + + return [result]; +}; diff --git a/utils/parse-compose-services.js b/utils/parse-compose-services.js new file mode 100644 index 000000000..5753166a2 --- /dev/null +++ b/utils/parse-compose-services.js @@ -0,0 +1,27 @@ +'use strict'; + +// Modules +const _ = require('lodash'); + +// adds required methods to ensure the lando v3 debugger can be injected into v4 things +module.exports = (config, composeServiceIds, app) => _(config) + // Arrayify + .map((service, name) => _.merge({}, service, {name})) + // Filter out any services which are not defined in the docker compose services + .filter(service => _.includes(composeServiceIds, service.name)) + // Build the config and ensure api is set to 3 + .map(service => _.merge({}, service, { + _app: app, + app: app.name, + home: app.config.home || app._config.home, + project: app.project, + root: app.root, + type: '_lando-compose', + userConfRoot: app._config.userConfRoot, + version: 'custom', + api: 3, + entrypoint: null, // NOTE: Do not overwrite the entrypoint from docker compose. Or should we? + data: null, // NOTE: Do not create the data volume + dataHome: null, // NOTE: Do not create the dataHome volume + })) + .value(); diff --git a/utils/parse-events-config.js b/utils/parse-events-config.js index 412d182e4..9db97dac3 100644 --- a/utils/parse-events-config.js +++ b/utils/parse-events-config.js @@ -34,10 +34,30 @@ const getService = (cmd, data = {}, defaultService = 'appserver') => { }; // adds required methods to ensure the lando v3 debugger can be injected into v4 things -module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { +module.exports = (cmds, app, data, lando) => _.map(cmds, cmd => { // Discover the service const command = getCommand(cmd); const service = getService(cmd, data, app._defaultService); + + if ('lando' === service) { + const yargs = require('yargs'); + const argv = yargs(_.isArray(command) ? command.join(' ') : command).parse(); + const $0 = _.pullAt(argv._, [0])[0]; + const toolingTask = _.find(app.tasks, task => $0 === task.command); + argv._eventArgs = argv._; + argv.$0 = undefined; + argv._ = undefined; + argv._app = app; + + if (undefined === toolingTask) { + throw new Error('Could not find tooling command: ' + $0); + } + return { + toolingTask, + answers: argv, + }; + } + // compute stdio based on compose major version const cstdio = _.get(app, '_config.orchestratorMV', 2) ? 'inherit' : ['inherit', 'pipe', 'pipe']; @@ -62,6 +82,18 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { cmd = ['/bin/sh', '-c', _.isArray(command) ? command.join(' ') : command]; } + if ('_init' === service) { + return _.merge( + {}, + require('./build-init-runner')(_.merge( + {}, + require('./get-init-runner-defaults')(lando, {destination: app.root, name: app.project, _app: app}), + {cmd, workdir: '/app'}, + )), + {isInitEventCommand: true}, + ); + } + // Validate the service if we can // @NOTE fast engine runs might not have this data yet if ( diff --git a/utils/parse-tooling-config.js b/utils/parse-tooling-config.js index 489fcd9fa..fe365d57a 100644 --- a/utils/parse-tooling-config.js +++ b/utils/parse-tooling-config.js @@ -19,11 +19,11 @@ const getDynamicKeys = (answer, answers = {}) => _(answers) * Set SERVICE from answers and strip out that noise from the rest of * stuff, check answers/argv for --service or -s, validate and then remove */ -const handleDynamic = (config, options = {}, answers = {}, execs = {}) => { +const handleDynamic = (config, argv, options = {}, answers = {}, execs = {}) => { if (_.startsWith(config.service, ':')) { const answer = answers[config.service.split(':')[1]]; // Remove dynamic service option from argv - _.remove(process.argv, arg => _.includes(getDynamicKeys(answer, answers).concat(answer), arg)); + _.remove(argv, arg => _.includes(getDynamicKeys(answer, answers).concat(answer), arg)); // get the service const service = answers[config.service.split(':')[1]]; // Return updated config @@ -40,9 +40,9 @@ const handleDynamic = (config, options = {}, answers = {}, execs = {}) => { * the first three assuming they are [node, lando.js, options.name]' * Check to see if we have global lando opts and remove them if we do */ -const handleOpts = (config, argopts = []) => { +const handleOpts = (config, name, argv, argopts = []) => { // Append any user specificed opts - argopts = argopts.concat(process.argv.slice(3)); + argopts = argopts.concat(argv.slice(argv.findIndex(value => value === name.split(' ')[0]) + 1)); // If we have no args then just return right away if (_.isEmpty(argopts)) return config; // Return @@ -68,13 +68,13 @@ const parseCommand = (cmd, service, execs) => ({ }); // adds required methods to ensure the lando v3 debugger can be injected into v4 things -module.exports = (cmd, service, options = {}, answers = {}, execs = {}) => _(cmd) +module.exports = (cmd, service, name, options = {}, answers = {}, execs = {}) => _(cmd) // Put into an object so we can handle "multi-service" tooling .map(cmd => parseCommand(cmd, service, execs)) // Handle dynamic services - .map(config => handleDynamic(config, options, answers, execs)) + .map(config => handleDynamic(config, answers._eventArgs ?? process.argv, options, answers, execs)) // Add in any argv extras if they've been passed in - .map(config => handleOpts(config, handlePassthruOpts(options, answers))) + .map(config => handleOpts(config, name, answers._eventArgs ?? process.argv, handlePassthruOpts(options, answers))) // Wrap the command in /bin/sh if that makes sense .map(config => _.merge({}, config, {command: require('./shell-escape')(config.command, true, config.args, config.exec)})) // eslint-disable-line max-len // Add any args to the command and compact to remove undefined diff --git a/utils/parse-v3-services.js b/utils/parse-v3-services.js index cfed7f138..5707a322e 100644 --- a/utils/parse-v3-services.js +++ b/utils/parse-v3-services.js @@ -20,7 +20,7 @@ module.exports = (config, app) => _(config) app: app.name, confDest: path.join(app._config.userConfRoot, 'config', service.type.split(':')[0]), data: `data_${service.name}`, - home: app._config.home, + home: app.config.home || app._config.home, project: app.project, root: app.root, type: service.type.split(':')[0], diff --git a/utils/run-init.js b/utils/run-init.js index 9d432d16b..372775d21 100644 --- a/utils/run-init.js +++ b/utils/run-init.js @@ -1,5 +1,8 @@ 'use strict'; +const remove = require('../utils/remove'); +const path = require('path'); + // Helper to kill a run const killRun = config => ({ id: config.id, @@ -13,7 +16,9 @@ const killRun = config => ({ // adds required methods to ensure the lando v3 debugger can be injected into v4 things module.exports = (lando, run) => lando.engine.run(run).catch(err => { + return lando.Promise.reject(err); +}).finally(() => { return lando.engine.stop(killRun(run)) .then(() => lando.engine.destroy(killRun(run))) - .then(() => lando.Promise.reject(err)); + .then(() => remove(path.dirname(run.compose[0]))); }); diff --git a/utils/to-lando-container.js b/utils/to-lando-container.js index 50f95d97d..0c7b54855 100644 --- a/utils/to-lando-container.js +++ b/utils/to-lando-container.js @@ -1,22 +1,36 @@ 'use strict'; -module.exports = ({Labels, Id, Status}, separator = '_') => { +const path = require('path'); + +module.exports = ({Labels, Id, Status}, separator = '_', src = []) => { // Get name of docker container. const app = Labels['com.docker.compose.project']; const service = Labels['com.docker.compose.service']; const num = Labels['com.docker.compose.container-number']; const lando = Labels['io.lando.container']; const special = Labels['io.lando.service-container']; + + // if we have io.lando.root and io.lando. + if (Labels['io.lando.root'] && Labels['io.lando.landofiles']) { + src = Labels['io.lando.landofiles'].split(',').map(landofile => path.join(Labels['io.lando.root'], landofile)); + + // or legacy support for Labels['io.lando.src'] + } else if (Labels['io.lando.src']) { + src = Labels['io.lando.src'].split(','); + + // or its just unknown + } else src = 'unknown'; + // Build generic container. return { id: Id, service: service, name: [app, service, num].join(separator), app: (special !== 'TRUE') ? app : '_global_', - src: (Labels['io.lando.src']) ? Labels['io.lando.src'].split(',') : 'unknown', kind: (special !== 'TRUE') ? 'app' : 'service', lando: (lando === 'TRUE') ? true : false, instance: Labels['io.lando.id'] || 'unknown', status: Status, + src, }; };