diff --git a/.github/workflows/deploy-npm.yml b/.github/workflows/deploy-npm.yml
index 02fb6d1d4..472a69684 100644
--- a/.github/workflows/deploy-npm.yml
+++ b/.github/workflows/deploy-npm.yml
@@ -12,9 +12,6 @@ on:
github-token:
description: "The github token"
required: true
- npm-token:
- description: "The npm deploy token"
- required: true
jobs:
deploy-npm:
@@ -28,7 +25,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- registry-url: https://registry.npmjs.org
cache: npm
- name: Install dependencies
run: npm clean-install --prefer-offline --frozen-lockfile
@@ -36,26 +32,24 @@ jobs:
run: npm run lint
- name: Run unit tests
run: npm run test:unit
- - name: Update edge release alias
- shell: bash
- run: |
- if ./scripts/semcompare.sh "${{ github.event.release.tag_name }}" "$(cat ./release-aliases/3-EDGE)"; then
- echo "${{ github.event.release.tag_name }}" > ./release-aliases/3-EDGE
- fi
- - name: Update stable release alias
- shell: bash
- if: github.event.release.prerelease == false
- run: |
- if ./scripts/semcompare.sh "${{ github.event.release.tag_name }}" "$(cat ./release-aliases/3-STABLE)"; then
- echo "${{ github.event.release.tag_name }}" > ./release-aliases/3-STABLE
- fi
+ #- name: Update edge release alias
+ # shell: bash
+ # run: |
+ # if ./scripts/semcompare.sh "${{ github.event.release.tag_name }}" "$(cat ./release-aliases/3-EDGE)"; then
+ # echo "${{ github.event.release.tag_name }}" > ./release-aliases/3-EDGE
+ # fi
+ #- name: Update stable release alias
+ # shell: bash
+ # if: github.event.release.prerelease == false
+ # run: |
+ # if ./scripts/semcompare.sh "${{ github.event.release.tag_name }}" "$(cat ./release-aliases/3-STABLE)"; then
+ # echo "${{ github.event.release.tag_name }}" > ./release-aliases/3-STABLE
+ # fi
- name: Prepare Release
uses: lando/prepare-release-action@v3
with:
lando-plugin: true
- sync-token: ${{ secrets.github-token }}
- sync-email: rtfm47@lando.dev
- sync-username: rtfm-47
+ sync: false
- name: Upgrade npm for trusted publishing
run: npm install -g "npm@^11.5.1"
- name: Publish to npm
@@ -64,9 +58,9 @@ jobs:
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
+ npm publish --access public --tag latest --dry-run
+ npm publish --access public --tag latest
+ # npm dist-tag add "$PACKAGE@$VERSION" edge # not supported with trusted publishing
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"
@@ -78,16 +72,14 @@ jobs:
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-token }}
- - name: Update edge release alias on main
- if: github.event.release.target_commitish == 'edge'
- run: |
- git clone https://github.com/lando/core.git core
- cd core
- git config user.name "rtfm-47"
- git config user.email "rtfm47@lando.dev"
- echo "${{ github.event.release.tag_name }}" > ./release-aliases/3-EDGE
- git add .
- git commit -m "Update edge release alias to ${{ github.event.release.tag_name }} triggered by @rtfm-47"
- git push https://x-access-token:${{ secrets.github-token }}@github.com/lando/core.git main
+ #- name: Update edge release alias on main
+ # if: github.event.release.target_commitish == 'edge'
+ # run: |
+ # git clone https://github.com/lando/core.git core
+ # cd core
+ # git config user.name "rtfm-47"
+ # git config user.email "rtfm47@lando.dev"
+ # echo "${{ github.event.release.tag_name }}" > ./release-aliases/3-EDGE
+ # git add .
+ # git commit -m "Update edge release alias to ${{ github.event.release.tag_name }} triggered by @rtfm-47"
+ # git push https://x-access-token:${{ secrets.github-token }}@github.com/lando/core.git main
diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml
index 5d0313e00..018dd4f93 100644
--- a/.github/workflows/dev-release.yml
+++ b/.github/workflows/dev-release.yml
@@ -29,38 +29,38 @@ jobs:
os: ${{ matrix.os }}
version: dev
- sign:
- uses: ./.github/workflows/sign-binary.yml
- needs:
- - package
- strategy:
- fail-fast: false
- matrix:
- file:
- - lando-linux-arm64-${{ github.sha }}
- - lando-macos-arm64-${{ github.sha }}
- - lando-win-arm64-${{ github.sha }}
+ #sign:
+ # uses: ./.github/workflows/sign-binary.yml
+ # needs:
+ # - package
+ # strategy:
+ # fail-fast: false
+ # matrix:
+ # file:
+ # - lando-linux-arm64-${{ github.sha }}
+ # - lando-macos-arm64-${{ github.sha }}
+ # - lando-win-arm64-${{ github.sha }}
- - lando-linux-x64-${{ github.sha }}
- - lando-macos-x64-${{ github.sha }}
- - lando-win-x64-${{ github.sha }}
+ # - lando-linux-x64-${{ github.sha }}
+ # - lando-macos-x64-${{ github.sha }}
+ # - lando-win-x64-${{ github.sha }}
- 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 }}
+ # 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
+ - package
strategy:
fail-fast: false
matrix:
@@ -77,11 +77,11 @@ jobs:
with:
source: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
destination: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.alias }}
- download-pattern: signed-lando-*
+ download-pattern: packaged-lando-*
build-release-binary-branch:
uses: ./.github/workflows/release-rename-binary.yml
needs:
- - sign
+ - package
strategy:
fail-fast: false
matrix:
@@ -96,7 +96,7 @@ jobs:
with:
source: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
destination: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.head_ref || github.ref_name }}
- download-pattern: signed-lando-*
+ download-pattern: packaged-lando-*
checksum:
uses: ./.github/workflows/generate-checksums.yml
@@ -116,16 +116,16 @@ jobs:
show: true
upload-name: release-checksums-${{ matrix.alias }}
- deploy-releases-s3:
- uses: ./.github/workflows/deploy-s3.yml
- needs:
- - checksum
- 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-s3:
+ # uses: ./.github/workflows/deploy-s3.yml
+ # needs:
+ # - checksum
+ # 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:
diff --git a/.github/workflows/pkg-binary.yml b/.github/workflows/pkg-binary.yml
index 13a9e0994..9ff4df142 100644
--- a/.github/workflows/pkg-binary.yml
+++ b/.github/workflows/pkg-binary.yml
@@ -51,8 +51,8 @@ jobs:
cache: npm
- name: Install dependencies
run: npm clean-install --prefer-offline --frozen-lockfile --production
- - name: Install plugins
- run: scripts/install-plugins.sh --lando bin/lando ${{ inputs.edge == true && '--edge' || '' }}
+ #- name: Install plugins
+ # run: scripts/install-plugins.sh --lando bin/lando ${{ inputs.edge == true && '--edge' || '' }}
- name: Switch to edge channel
if: inputs.edge == true
run: |
@@ -71,7 +71,7 @@ jobs:
node-version: ${{ inputs.node-version }}
os: ${{ inputs.os }}
options: --options dns-result-order=ipv4first
- pkg: "@yao-pkg/pkg@5.16.1"
+ pkg: "@yao-pkg/pkg@6.4.0"
upload-key: "packaged-${{ inputs.filename }}-${{ inputs.os }}-${{ inputs.arch }}-${{ github.sha }}"
- name: Ensure version
if: (inputs.os == 'linux' && runner.os == 'Linux') || (inputs.os == 'macos' && runner.os == 'macOS')
@@ -82,8 +82,8 @@ jobs:
- name: Ensure channel
if: (inputs.os == 'linux' && runner.os == 'Linux') || (inputs.os == 'macos' && runner.os == 'macOS')
run: ./dist/${{ inputs.filename }} config --path channel | grep ${{ inputs.edge == true && 'edge' || 'stable' }}
- - name: Ensure plugin install
- if: ((inputs.os == 'linux' && runner.os == 'Linux') || (inputs.os == 'macos' && runner.os == 'macOS'))
- run: |
- ./dist/${{ inputs.filename }} config --path fatcore | grep true
- ./dist/${{ inputs.filename }} config | grep -q "/snapshot/core/plugins/wordpress"
+ #- name: Ensure plugin install
+ # if: ((inputs.os == 'linux' && runner.os == 'Linux') || (inputs.os == 'macos' && runner.os == 'macOS'))
+ # run: |
+ # ./dist/${{ inputs.filename }} config --path fatcore | grep true
+ # ./dist/${{ inputs.filename }} config | grep -q "/snapshot/core/plugins/wordpress"
diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml
index 0bd6950d8..7ca9a2770 100644
--- a/.github/workflows/pr-core-tests.yml
+++ b/.github/workflows/pr-core-tests.yml
@@ -103,7 +103,7 @@ jobs:
node-version: ${{ matrix.node-version }}
options: --options dns-result-order=ipv4first
upload: false
- pkg: "@yao-pkg/pkg@5.16.1"
+ pkg: "@yao-pkg/pkg@6.4.0"
- name: Install full deps
run: npm clean-install --prefer-offline --frozen-lockfile
- name: Setup lando ${{ steps.pkg-action.outputs.file }}
diff --git a/.github/workflows/pr-docs-tests.yml b/.github/workflows/pr-docs-tests.yml
index 9e0cb17ae..e146987f4 100644
--- a/.github/workflows/pr-docs-tests.yml
+++ b/.github/workflows/pr-docs-tests.yml
@@ -74,7 +74,7 @@ jobs:
node-version: ${{ matrix.node-version }}
options: --options dns-result-order=ipv4first
upload: false
- pkg: "@yao-pkg/pkg@5.16.1"
+ pkg: "@yao-pkg/pkg@6.4.0"
- name: Install full deps
run: npm clean-install --prefer-offline --frozen-lockfile
- name: Setup lando ${{ steps.pkg-action.outputs.file }}
diff --git a/.github/workflows/pr-setup-linux-tests.yml b/.github/workflows/pr-setup-linux-tests.yml
index 682ab7d98..6984bcb31 100644
--- a/.github/workflows/pr-setup-linux-tests.yml
+++ b/.github/workflows/pr-setup-linux-tests.yml
@@ -44,7 +44,7 @@ jobs:
node-version: ${{ matrix.node-version }}
options: --options dns-result-order=ipv4first
upload: false
- pkg: "@yao-pkg/pkg@5.16.1"
+ pkg: "@yao-pkg/pkg@6.4.0"
- name: Install full deps
run: npm clean-install --prefer-offline --frozen-lockfile
- name: Setup lando ${{ steps.pkg-action.outputs.file }}
diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml
index 61ed8b539..a1fa5c8b6 100644
--- a/.github/workflows/pr-setup-macos-tests.yml
+++ b/.github/workflows/pr-setup-macos-tests.yml
@@ -47,7 +47,7 @@ jobs:
node-version: ${{ matrix.node-version }}
options: --options dns-result-order=ipv4first
upload: false
- pkg: "@yao-pkg/pkg@5.16.1"
+ pkg: "@yao-pkg/pkg@6.4.0"
- name: Install full deps
run: npm clean-install --prefer-offline --frozen-lockfile
- name: Setup lando ${{ steps.pkg-action.outputs.file }}
diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml
index 208fd5b6c..02dc67f34 100644
--- a/.github/workflows/pr-setup-windows-tests.yml
+++ b/.github/workflows/pr-setup-windows-tests.yml
@@ -43,7 +43,7 @@ jobs:
node-version: ${{ matrix.node-version }}
options: --options dns-result-order=ipv4first
upload: false
- pkg: "@yao-pkg/pkg@5.16.1"
+ pkg: "@yao-pkg/pkg@6.4.0"
- name: Install full deps
run: npm clean-install --prefer-offline --frozen-lockfile
- name: Setup lando ${{ steps.pkg-action.outputs.file }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index df1d18807..06c2fa13d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -31,38 +31,38 @@ jobs:
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 }}
-
- 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 }}
+# 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 }}
+
+# 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
+ - package
strategy:
fail-fast: false
matrix:
@@ -78,11 +78,11 @@ jobs:
with:
source: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}
destination: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.alias }}
- download-pattern: signed-lando-*
+ download-pattern: packaged-lando-*
build-release-binary-tag:
uses: ./.github/workflows/release-rename-binary.yml
needs:
- - sign
+ - package
strategy:
fail-fast: false
matrix:
@@ -96,7 +96,7 @@ jobs:
with:
source: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}
destination: lando-${{ matrix.os }}-${{ matrix.arch }}-${{ github.ref_name }}
- download-pattern: signed-lando-*
+ download-pattern: packaged-lando-*
checksum:
uses: ./.github/workflows/generate-checksums.yml
@@ -127,17 +127,17 @@ jobs:
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-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:
@@ -156,52 +156,51 @@ jobs:
- 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
- steps:
- - name: Push release to lando/lando
- uses: softprops/action-gh-release@v2
- 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
- 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).
+# deploy-legacy-notifications:
+# runs-on: ubuntu-24.04
+# needs:
+# - checksum
+# env:
+# TERM: xterm
+# steps:
+# - name: Push release to lando/lando
+# uses: softprops/action-gh-release@v2
+# 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
+# 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).
diff --git a/README.md b/README.md
index 7fdd3fd5c..05587cfc0 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +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).
-
-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](/LICENSE)
-* [TERMS OF USE](https://docs.lando.dev/terms)
-* [PRIVACY POLICY](https://docs.lando.dev/privacy)
-* [CODE OF CONDUCT](https://docs.lando.dev/coc)
+These are the core libraries that power flos version of Lando with seamless compose integration.
+Thanks to the upstream [lando-core](https://github.com/lando/core)!
diff --git a/app.js b/app.js
index 920f230b3..c8b8baa5f 100644
--- a/app.js
+++ b/app.js
@@ -64,7 +64,6 @@ module.exports = async (app, lando) => {
overrides: {
tooling: app._coreToolingOverrides,
},
-
}, {persist: true});
};
@@ -124,6 +123,9 @@ module.exports = async (app, lando) => {
// add proxy info as needed
app.events.on('post-init', async () => await require('./hooks/app-add-proxy-info')(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));
@@ -144,9 +146,6 @@ module.exports = async (app, lando) => {
// v4 parts of the app are ready
app.events.on('ready', 6, async () => await require('./hooks/app-v4-ready')(app, lando));
- // this is a gross hack we need to do to reset the engine because the lando 3 runtime has no idea
- app.events.on('ready-engine', 1, async () => await require('./hooks/app-reset-orchestrator')(app, lando));
-
// Discover portforward true info
app.events.on('ready-engine', async () => await require('./hooks/app-set-portforwards')(app, lando));
@@ -199,7 +198,7 @@ module.exports = async (app, lando) => {
app.events.on('post-start', async () => await require('./hooks/app-add-proxy-info')(app, lando));
// Add update tip if needed
- app.events.on('post-start', async () => await require('./hooks/app-add-path-info')(app, lando));
+ // app.events.on('post-start', async () => await require('./hooks/app-add-path-info')(app, lando));
// If we don't have a builtAgainst already then we must be spinning up for the first time and its safe to set this
app.events.on('post-start', async () => await require('./hooks/app-update-built-against-post')(app, lando));
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 a86cf3b04..5b240f560 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..f46ef8d95
--- /dev/null
+++ b/builders/_lando-compose.js
@@ -0,0 +1,11 @@
+'use strict';
+
+module.exports = {
+ name: '_lando-compose',
+ parent: '_lando',
+ builder: parent => class LandoComposeServiceV3 extends parent {
+ constructor(id, options = {}) {
+ super(id, options);
+ }
+ },
+};
diff --git a/builders/_lando.js b/builders/_lando.js
index 3ef39dc51..637312193 100644
--- a/builders/_lando.js
+++ b/builders/_lando.js
@@ -53,6 +53,8 @@ module.exports = {
supportedIgnore = false,
root = '',
// webroot = '/app',
+ _app = null,
+ appMount = '/app',
} = {},
...sources
) {
@@ -98,6 +100,10 @@ module.exports = {
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
@@ -110,10 +116,9 @@ module.exports = {
// Handle volumes
const volumes = [
- `${userConfRoot}:/lando:cached`,
+ `${userConfRoot}/keys:/lando/keys:cached`,
`${globalScriptsDir}:/helpers`,
`${entrypointScript}:/lando-entrypoint.sh`,
- `${dataHome}:/var/www`,
];
// add in service helpers if we have them
@@ -132,10 +137,15 @@ module.exports = {
volumes.push(`${addCertsScript}:/scripts/000-add-cert`);
volumes.push(`${path.join(userConfRoot, 'certs', certname)}:/certs/cert.crt`);
volumes.push(`${path.join(userConfRoot, 'certs', keyname)}:/certs/cert.key`);
+ volumes.push(`${path.join(userConfRoot, 'certs', certname)}:/lando/certs/${certname}`);
+ volumes.push(`${path.join(userConfRoot, 'certs', keyname)}:/lando/certs/${keyname}`);
+ volumes.push(`${userConfRoot}/certs/LandoCA.crt:/lando/certs/LandoCA.crt`);
+ volumes.push(`${userConfRoot}/certs/LandoCA.key:/lando/certs/LandoCA.key`);
}
// 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
@@ -178,9 +188,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,
@@ -210,6 +227,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/_proxy.js b/builders/_proxy.js
index 1f31be61c..a42dd0808 100644
--- a/builders/_proxy.js
+++ b/builders/_proxy.js
@@ -6,7 +6,14 @@ 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: {
@@ -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/config.yml b/config.yml
index 9d25a6bac..3f8e55692 100644
--- a/config.yml
+++ b/config.yml
@@ -12,22 +12,22 @@ stats:
dockerSupportedVersions:
compose:
satisfies: "1.x.x || 2.x.x"
- recommendUpdate: "<=2.24.6"
- tested: "<=2.32.99"
+ recommendUpdate: "<=2.40.2"
+ tested: "<=2.40.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.37.99"
- recommendUpdate: "<=4.36"
+ tested: "<=4.54.99"
+ recommendUpdate: "<=4.53"
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.5.99"
+ satisfies: ">=18 <30"
+ tested: "<=29.1.99"
link:
linux: https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script
diff --git a/examples/cache/README.md b/examples/cache/README.md
index 9e2ee5e37..4ed4dabd8 100644
--- a/examples/cache/README.md
+++ b/examples/cache/README.md
@@ -30,10 +30,15 @@ cat ~/.lando/cache/lando-cache.compose.cache || echo $? | grep 1
lando --clear
lando || true
cat ~/.lando/cache/_.tasks.cache
-cat ~/.lando/cache/lando-cache.compose.cache
+cat ~/.lando/cache/lando-cache.compose.cache || true
# Should regenerate the caches on any --help before the help is displayed
lando --clear
+# 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 || true
+lando exec web -- echo 'Hello World' | grep 'Hello World'
lando exec --help | grep service | grep choices | grep web | grep web2 | grep web3 | grep web4
cat ~/.lando/cache/_.tasks.cache
cat ~/.lando/cache/lando-cache.compose.cache
diff --git a/examples/events/README.md b/examples/events/README.md
index 40a2ae4cf..5ad6d8784 100644
--- a/examples/events/README.md
+++ b/examples/events/README.md
@@ -42,7 +42,7 @@ lando exec web2 -- "cat /app/test/web2-post-stuff.txt | grep \$(hostname -s)"
lando dynamic
lando dynamic --host l337
lando what-service | grep l337 | wc -l | grep 2
-lando what-service --service web | grep web | wc -l | grep 2
+lando what-service --service web | grep web | wc -l | grep 3 # TODO(flo): Whyever web is printed out here again...
lando what-service --service web2 | grep web | wc -l | grep 2
# Should use the app default service as the default in multi-service tooling
diff --git a/hooks/app-add-2-landonet.js b/hooks/app-add-2-landonet.js
index af8edd685..f61693b9a 100644
--- a/hooks/app-add-2-landonet.js
+++ b/hooks/app-add-2-landonet.js
@@ -15,7 +15,8 @@ module.exports = async (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(() => {
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-proxy-2-landonet.js b/hooks/app-add-proxy-2-landonet.js
index 831b251d9..690f2496b 100644
--- a/hooks/app-add-proxy-2-landonet.js
+++ b/hooks/app-add-proxy-2-landonet.js
@@ -34,7 +34,8 @@ module.exports = async (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/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-reset-orchestrator.js b/hooks/app-reset-orchestrator.js
deleted file mode 100644
index 0591c1361..000000000
--- a/hooks/app-reset-orchestrator.js
+++ /dev/null
@@ -1,22 +0,0 @@
-'use strict';
-
-module.exports = async (app, lando) => {
- // if we dont have an orchestrator bin yet then discover it
- if (!lando.config.orchestratorBin) lando.config.orchestratorBin = require('../utils/get-compose-x')(lando.config);
-
- // because the entire lando 3 runtime was made in a bygone era when we never dreamed of doing stuff like this
- // we need this workaround
- if (lando._bootstrapLevel >= 3 && !app.engine.composeInstalled) {
- app.engine = require('../utils/setup-engine')(
- lando.config,
- lando.cache,
- lando.events,
- app.log,
- app.shell,
- lando.config.instance,
- );
- }
-
- // log our sitch
- app.log.debug('using docker-compose %s', lando.config.orchestratorBin);
-};
diff --git a/hooks/app-run-events.js b/hooks/app-run-events.js
index 921086139..bcd71679d 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(_.flatten(splitEventCommands), 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/app-start-proxy.js b/hooks/app-start-proxy.js
index 3db065cfb..01043f43a 100644
--- a/hooks/app-start-proxy.js
+++ b/hooks/app-start-proxy.js
@@ -190,7 +190,7 @@ const parseRoutes = (service, urls = [], sslReady, labels = {}) => {
rule.middlewares.push({name: 'lando', key: 'headers.customrequestheaders.X-Lando', value: 'on'});
// Add in any path stripping middleware we need it
- if (rule.pathname.length > 1) {
+ if (rule.pathname.length > 1 && _.get(rule, 'stripPrefix', true)) {
rule.middlewares.push({name: 'stripprefix', key: 'stripprefix.prefixes', value: rule.pathname});
}
@@ -323,19 +323,17 @@ module.exports = async (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/hooks/lando-copy-v3-scripts.js b/hooks/lando-copy-v3-scripts.js
index e378e352c..4c0293766 100644
--- a/hooks/lando-copy-v3-scripts.js
+++ b/hooks/lando-copy-v3-scripts.js
@@ -7,8 +7,7 @@ module.exports = async lando => {
return lando.Promise.map(lando.config.plugins, plugin => {
if (fs.existsSync(plugin.scripts)) {
const confDir = path.join(lando.config.userConfRoot, 'scripts');
- const dest = require('../utils/move-config')(plugin.scripts, confDir);
- require('../utils/make-executable')(fs.readdirSync(dest), dest);
+ require('../utils/move-config')(plugin.scripts, confDir);
lando.log.debug('automoved scripts from %s to %s and set to mode 755', plugin.scripts, confDir);
}
});
diff --git a/hooks/lando-get-compat.js b/hooks/lando-get-compat.js
index 7acadcf39..2108513cd 100644
--- a/hooks/lando-get-compat.js
+++ b/hooks/lando-get-compat.js
@@ -5,11 +5,20 @@ const _ = require('lodash');
module.exports = async lando => {
// only run if engine bootstrap or above and if engine/orchestrator have been installed
if (lando._bootstrapLevel >= 3) {
- if (lando.engine.composeInstalled && lando.engine.dockerInstalled ) {
+ if (lando.engine.composeInstalled && lando.engine.dockerInstalled) {
+ if (!await lando.cache.isOlderThanMinutes('versions')) {
+ return;
+ }
+
lando.engine.getCompatibility().then(results => {
lando.log.verbose('checking docker version compatibility...');
lando.log.debug('compatibility results', _.keyBy(results, 'name'));
- lando.cache.set('versions', _.assign(lando.versions, _.keyBy(results, 'name')), {persist: true});
+ const lastLandoVersions = _.cloneDeep(lando.versions);
+ _.assign(lando.versions, _.keyBy(results, 'name'));
+ if (_.isEqual(lando.versions, lastLandoVersions)) {
+ return;
+ }
+ lando.cache.set('versions', lando.versions, {persist: true});
lando.versions = lando.cache.get('versions');
});
}
diff --git a/hooks/lando-run-setup.js b/hooks/lando-run-setup.js
index 5392fc401..e799f9b4c 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-landonet.js b/hooks/lando-setup-landonet.js
index b882e0fc4..751526918 100644
--- a/hooks/lando-setup-landonet.js
+++ b/hooks/lando-setup-landonet.js
@@ -39,7 +39,7 @@ module.exports = async (lando, options) => {
if (lando.engine.dockerInstalled === false) return false;
// we also want to do an additional check on docker-destkop
- if (lando.config.os.landoPlatform !== 'linux' && !fs.existsSync(getDockerDesktopBin())) return false;
+ if (!['linux', 'wsl'].includes(lando.config.os.landoPlatform) && !fs.existsSync(getDockerDesktopBin())) return false;
// otherwise attempt to sus things out
try {
diff --git a/hooks/lando-setup-orchestrator.js b/hooks/lando-setup-orchestrator.js
index d01dc7735..60b975dc4 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.31.0') => {
+const getComposeDownloadUrl = (version = '2.40.3') => {
const mv = version.split('.')[0] > 1 ? '2' : '1';
const arch = process.arch === 'arm64' ? 'aarch64' : 'x86_64';
const toggle = `${process.platform}-${mv}`;
diff --git a/hooks/plugin-auth-from-npmrc.js b/hooks/plugin-auth-from-npmrc.js
new file mode 100644
index 000000000..30b380287
--- /dev/null
+++ b/hooks/plugin-auth-from-npmrc.js
@@ -0,0 +1,25 @@
+'use strict';
+
+const write = require('../utils/write-file');
+const fs = require('fs');
+const path = require('path');
+const os = require('os');
+const {parse} = require('ini');
+
+module.exports = async lando => {
+ if (!lando.config.loadNpmrcForPluginAuth) {
+ return;
+ }
+
+ const npmrcPath = path.resolve(os.homedir(), '.npmrc');
+ if (!fs.existsSync(npmrcPath)) {
+ return;
+ }
+ lando.log.debug('Reading home .npmrc for plugin-auth.json...');
+ const content = fs.readFileSync(npmrcPath, {
+ encoding: 'utf-8',
+ });
+ const data = parse(content);
+ write(lando.config.pluginConfigFile, data);
+ lando.plugins.updates = data;
+};
diff --git a/index.js b/index.js
index 090bed015..4873339cb 100644
--- a/index.js
+++ b/index.js
@@ -24,7 +24,7 @@ const defaults = {
'/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',
@@ -81,7 +81,7 @@ module.exports = async lando => {
lando.events.once('pre-install-plugins', async options => await require('./hooks/lando-setup-common-plugins')(lando, options));
// move v3 scripts directories as needed
- lando.events.on('pre-setup', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando));
+ lando.events.on('post-install-plugins', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando));
// ensure we setup docker if needed
lando.events.once('pre-setup', async options => await require(`./hooks/lando-setup-build-engine-${platform}`)(lando, options));
@@ -100,12 +100,6 @@ module.exports = async lando => {
// ensure we setup landonet
lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-landonet')(lando, options));
- // also move scripts for init considerations
- lando.events.on('pre-init', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando));
-
- // move v3 scripts directories as needed
- lando.events.on('pre-init', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando));
-
// set proxy config
lando.events.on('post-bootstrap-config', async () => await require('./hooks/lando-set-proxy-config')(lando));
@@ -131,15 +125,14 @@ module.exports = async lando => {
// autostart docker if we need to
lando.events.once('engine-autostart', async () => await require('./hooks/lando-autostart-engine')(lando));
- // move v3 scripts directories as needed
- lando.events.on('pre-engine-start', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando));
-
// clean networks
lando.events.on('pre-engine-start', 1, async () => await require('./hooks/lando-clean-networks')(lando));
// regen task cache
lando.events.on('before-end', 9999, async () => await require('./hooks/lando-generate-tasks-cache')(lando));
+ lando.events.on('post-bootstrap-config', async () => await require('./hooks/plugin-auth-from-npmrc')(lando));
+
// return some default things
return _.merge({}, defaults, uc(), {config: {
appEnv: {
diff --git a/lib/app.js b/lib/app.js
index f1dc77a76..136ad290f 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -6,11 +6,12 @@ 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') => {
+const initAndReport = (app, method) => {
return app.init().then(() => {
app.metrics.report(method, utils.metricsParse(app));
return Promise.resolve(true);
@@ -56,7 +57,11 @@ module.exports = class App {
* @alias app.name
*/
this.name = require('../utils/slugify')(name);
- this.project = require('../utils/docker-composify')(this.name);
+ if (lando.config.shouldDockerComposifyProjectName ?? true) {
+ this.project = require('../utils/docker-composify')(this.name);
+ } else {
+ this.project = name;
+ }
this._serviceApi = 3;
this._config = lando.config;
this._defaultService = 'appserver';
@@ -256,6 +261,8 @@ module.exports = class App {
.then(() => this.log.info('destroyed app.'));
}
+ static isBootstrapCommand = undefined;
+
/**
* Initializes the app
*
@@ -272,14 +279,38 @@ module.exports = class App {
init({noEngine = 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);
+ if (App.isBootstrapCommand) {
+ 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(() => App.isBootstrapCommand ? 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.
@@ -292,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
@@ -360,7 +390,7 @@ module.exports = class App {
}
// Log
- this.initialized = true;
+ this.initialized = !!noEngine;
this.log.verbose('app is ready!');
})
/**
@@ -374,7 +404,8 @@ module.exports = class App {
.then(() => this.events.emit('ready', this))
// @NOTE: dont ask, just continuing to work around v3-wasnt-intended-to-do-this problems
- .then(() => noEngine === true ? undefined : this.events.emit('ready-engine', this));
+ .then(() => noEngine === true ? undefined : this.events.emit('ready-engine', this))
+ .then(() => noEngine === true ? require('../hooks/app-purge-compose-cache')(this, this._lando) : undefined);
}
/**
@@ -489,13 +520,19 @@ 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)
+
+ if (undefined === App.isBootstrapCommand) {
+ App.isBootstrapCommand = !fs.existsSync(this._dir);
+ }
+
+ return initAndReport(this, 'start')
/**
* Event that runs before an app starts up.
@@ -523,6 +560,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/cache.js b/lib/cache.js
index a03dead72..81711dbd2 100644
--- a/lib/cache.js
+++ b/lib/cache.js
@@ -118,6 +118,18 @@ class Cache extends NodeCache {
this.log.debug('No file cache with key %s', key);
}
}
+
+ async isOlderThanMinutes(key, minutes = 5) {
+ try {
+ const stats = await fs.stat(path.join(this.cacheDir, key));
+ const modifiedTime = stats.mtime;
+ const minutesAgo = Date.now() - minutes * 60 * 1000;
+
+ return modifiedTime.getTime() < minutesAgo;
+ } catch (err) {
+ return false;
+ }
+ }
}
/*
diff --git a/lib/cli.js b/lib/cli.js
index 91af82162..85111d5af 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -320,6 +320,8 @@ module.exports = class Cli {
.option('help', globalOptions.help)
.option('verbose', globalOptions.verbose)
.version(false)
+ .env('lando_cli_')
+ .locale('en')
.middleware([(argv => {
argv._app = config;
argv._yargs = yargs;
diff --git a/lib/compose.js b/lib/compose.js
index 34cc67c18..dccca225e 100644
--- a/lib/compose.js
+++ b/lib/compose.js
@@ -20,6 +20,21 @@ const composeFlags = {
rm: '--rm',
timestamps: '--timestamps',
volumes: '-v',
+ outputFilePath: '-o',
+ ignoreBuildable: '--ignore-buildable',
+};
+
+const composeFlagOptionMapping = {
+ build: ['noCache', 'pull', 'q'],
+ down: ['removeOrphans', 'volumes'],
+ exec: ['background', 'detach', 'noTTY'],
+ kill: ['removeOrphans'],
+ logs: ['follow', 'timestamps'],
+ ps: ['q'],
+ pull: ['q', 'ignoreBuildable'],
+ rm: ['force', 'volumes'],
+ up: ['background', 'detach', 'noRecreate', 'noDeps', 'pull', 'q', 'recreate', 'removeOrphans', 'timestamps'],
+ config: ['outputFilePath', 'q'],
};
// Default options nad things
@@ -30,21 +45,30 @@ const defaultOptions = {
kill: {},
logs: {follow: false, timestamps: false},
ps: {q: true},
- pull: {},
+ pull: {ignoreBuildable: true},
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(composeFlagOptionMapping[run], index),
+ ),
+);
/*
* Parse docker-compose options
*/
-const parseOptions = (opts = {}) => {
- const flags = _.map(composeFlags, (value, key) => _.get(opts, key, false) ? value : '');
+const parseOptions = (run, opts = {}) => {
+ const composeOpts = mergeOpts(run, _.merge({}, opts, require('yargs').argv));
+ const flags = _.map(composeFlags, (value, key) => _.get(composeOpts, key, false) ? value : '');
const environment = _.flatMap(opts.environment, (value, key) => ['--env', `${key}=${value}`]);
const user = (_.has(opts, 'user')) ? ['--user', opts.user] : [];
const workdir = (_.has(opts, 'workdir')) ? ['--workdir', opts.workdir] : [];
@@ -55,21 +79,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 options = parseOptions(opts);
+ const envFile = _.flatten(_.map(envFiles, unit => ['--env-file', unit]));
+ const options = parseOptions(run, 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 ?? []}, opts),
opts: {mode: 'spawn', cstdio: opts.cstdio, silent: opts.silent},
});
@@ -155,3 +180,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 938beff03..fda2ff13b 100644
--- a/lib/daemon.js
+++ b/lib/daemon.js
@@ -41,7 +41,7 @@ const buildDockerCmd = (cmd, scriptsDir) => {
*/
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.
@@ -54,7 +54,7 @@ module.exports = class LandoDaemon {
log = new Log(),
context = 'node',
compose = require('../utils/get-compose-x')(),
- orchestratorVersion = '2.31.0',
+ orchestratorVersion = '2.40.3',
userConfRoot = path.join(os.homedir(), '.lando'),
) {
this.cache = cache;
@@ -223,7 +223,7 @@ module.exports = class LandoDaemon {
// Return true if we get a zero response and cache the result
try {
- await require('../utils/run-command')(docker, ['ps'], {debug: this.debug});
+ await require('../utils/run-command')(docker, ['ps', '-q'], {debug: this.debug});
this.debug('engine is up.');
cache.set('engineup', true, {ttl: 5});
this.isRunning = true;
diff --git a/lib/docker.js b/lib/docker.js
index c38325471..ca784562c 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 b86c919b4..c5be551a4 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 b9ae15c0e..0d41ae191 100644
--- a/lib/formatters.js
+++ b/lib/formatters.js
@@ -147,7 +147,7 @@ exports.handleInteractive = (inquiry, argv, command, lando, file) => lando.Promi
// 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 c8c314868..6d715397d 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);
@@ -87,6 +87,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'}},
+ ),
+ );
});
}
})
@@ -135,3 +148,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 4871b8218..5de9bcda4 100644
--- a/lib/updates.js
+++ b/lib/updates.js
@@ -128,7 +128,8 @@ module.exports = class UpdateManager {
const ext = process.platform === 'win32' ? '.exe' : '';
const os = getOS();
const version = `v${lando.update.version}`;
- const url = `https://github.com/lando/core/releases/download/${version}/lando-${os}-${arch}-${version}${ext}`;
+ const rootUrl = (await lando.info()).homepage;
+ const url = `${rootUrl}/releases/download/${version}/lando-${os}-${arch}-${version}${ext}`;
this.debug(`${color.dim('lando')} update resolved cli download url to %o`, url);
// now see whether that link is good
@@ -208,58 +209,21 @@ module.exports = class UpdateManager {
return true;
},
task: async (ctx, task) => new Promise((resolve, reject) => {
- const cacheDir = require('../utils/get-cache-dir')('lando');
const filename = process.platform === 'win32' ? 'lando.exe' : 'lando';
- const dest = path.join(cacheDir, `v${version}`, 'bin', filename);
+ const dest = path.join(this.cli.installPath, filename);
// @TODO: restore test when we cut 3.22?
const download = require('../utils/download-x')(url, {debug: this.debug, dest}); // test: ['version']});
// success
download.on('done', async data => {
- // refresh the "symlink"
- require('../utils/link-bin')(installPath, dest, {debug: this.debug});
-
// set a good default update messag
task.title = `Updated lando to ${version}`;
- // if lando.exe exists on windows in the install path then remove it so the link has primacy
- // in PATHEXT hierarchy
- if (process.platform === 'win32' && fs.existsSync(path.join(installPath, filename))) {
- remove(path.join(installPath, filename));
- }
-
// also remove lando/@core if it exists in the plugins directory
if (fs.existsSync(path.join(this.dir, '@lando', 'core'))) {
remove(path.join(this.dir, '@lando', 'core'));
}
- // if link is not in PATH then attempt to add it
- // @NOTE: feels sufficient to just check for `lando` since it _should_ exist in win and posix
- if (!require('../utils/is-in-path')(path.join(installPath, 'lando'))) {
- const binPaths = require('../utils/get-bin-paths')(this.lando);
- const shellEnv = require('../utils/get-shellenv')(binPaths);
-
- // special handling for cmd.exe
- if (require('../utils/get-user-shell')() === 'cmd.exe') {
- const args = require('string-argv')(shellEnv.map(line => line[0]).join(' && '));
- const opts = {debug: this.debug, ignoreReturnCode: true};
- const result = require('is-root')()
- ? await require('../utils/run-elevated')(args, opts)
- : await require('../utils/run-command')(args[0], args.slice(1), opts);
- this.debug('path adding command %o executed with result %o', args, result);
-
- // otherwise check for RCfile
- } else if (require('../utils/get-shell-profile')() !== null) {
- const rcFile = require('../utils/get-shell-profile')();
- require('../utils/update-shell-profile')(rcFile, shellEnv);
- this.debug('added %o to %o', shellEnv, rcFile);
- task.title = `${task.title}. Start a new terminal session to use the updated ${color.bold(`lando`)}`;
-
- // otherwis i guess do something else?
- // @TODO: throw a warning?
- } else this.debug('could not add %o to PATH!', binPaths);
- }
-
// finish
resolve(data);
});
diff --git a/package-lock.json b/package-lock.json
index c0f04a618..c819fa291 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
- "name": "@lando/core",
- "version": "3.26.2",
+ "name": "@florianpat/lando-core",
+ "version": "3.26.3-1florianPat.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "@lando/core",
- "version": "3.26.0",
+ "name": "@florianpat/lando-core",
+ "version": "3.26.3-1florianPat.4",
"license": "MIT",
"dependencies": {
"@lando/argv": "^1.2.0",
@@ -32,6 +32,7 @@
"figures": "^3.2.0",
"fs-extra": "^11.1.1",
"glob": "^7.1.3",
+ "ini": "^5.0.0",
"inquirer": "^6.5.2",
"inquirer-autocomplete-prompt": "^1.4.0",
"is-class": "^0.0.9",
@@ -77,7 +78,7 @@
"@babel/eslint-parser": "^7.16.0",
"@lando/leia": "^1.0.0-beta.4",
"@lando/vitepress-theme-default-plus": "^1.1.1",
- "@yao-pkg/pkg": "^5.16.1",
+ "@yao-pkg/pkg": "^6.11.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"chai-events": "^0.0.1",
@@ -241,7 +242,6 @@
"integrity": "sha512-m9tK4IqJmn+flEPRtuxuHgiHmrKV0su5fuVwVpq8/es4DMjWMgX1a7Lg1PktvO8AbKaTp9kTtBAPnwXpuCwmEg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
@@ -384,7 +384,6 @@
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
@@ -1432,6 +1431,19 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@isaacs/string-locale-compare": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz",
@@ -2548,7 +2560,6 @@
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz",
"integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@octokit/auth-token": "^3.0.0",
"@octokit/graphql": "^5.0.0",
@@ -4043,34 +4054,39 @@
}
},
"node_modules/@yao-pkg/pkg": {
- "version": "5.16.1",
- "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-5.16.1.tgz",
- "integrity": "sha512-crUlnNFSReFNFuXDc4f3X2ignkFlc9kmEG7Bp/mJMA1jYyqR0lqjZGLgrSDYTYiNsYud8AzgA3RY1DrMdcUZWg==",
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.11.0.tgz",
+ "integrity": "sha512-cofhWpH8ifhastwvbSe0Xnh1leq9oT0VmGbxa8fqH1hc4PtrO1dz6B3M5uQ4xTjOeGd9R9Gx8rT6bB2/ZERaTA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/generator": "^7.23.0",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
- "@yao-pkg/pkg-fetch": "3.5.16",
+ "@yao-pkg/pkg-fetch": "3.5.31",
"into-stream": "^6.0.0",
"minimist": "^1.2.6",
"multistream": "^4.1.0",
"picocolors": "^1.1.0",
"picomatch": "^4.0.2",
"prebuild-install": "^7.1.1",
- "resolve": "^1.22.0",
+ "resolve": "^1.22.10",
"stream-meter": "^1.0.4",
- "tinyglobby": "^0.2.9"
+ "tar": "^7.4.3",
+ "tinyglobby": "^0.2.11",
+ "unzipper": "^0.12.3"
},
"bin": {
"pkg": "lib-es5/bin.js"
+ },
+ "engines": {
+ "node": ">=18.0.0"
}
},
"node_modules/@yao-pkg/pkg-fetch": {
- "version": "3.5.16",
- "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.16.tgz",
- "integrity": "sha512-mCnZvZz0/Ylpk4TGyt34pqWJyBGYJM8c3dPoMRV8Knodv2QhcYS4iXb5kB/JNWkrRtCKukGZIKkMLXZ3TQlzPg==",
+ "version": "3.5.31",
+ "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.31.tgz",
+ "integrity": "sha512-qBLFfCXJECsxMlvwamhdWR65LWI7Cnb40dmI+1NIr1Nfk8Ddc8luZIJsRRZER9UrY13X1NJZSRORsqIPYDsJbw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4079,7 +4095,7 @@
"picocolors": "^1.1.0",
"progress": "^2.0.3",
"semver": "^7.3.5",
- "tar-fs": "^2.1.1",
+ "tar-fs": "^3.1.1",
"yargs": "^16.2.0"
},
"bin": {
@@ -4125,6 +4141,33 @@
"node": ">= 6"
}
},
+ "node_modules/@yao-pkg/pkg-fetch/node_modules/tar-fs": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
+ "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0",
+ "tar-stream": "^3.1.5"
+ },
+ "optionalDependencies": {
+ "bare-fs": "^4.0.1",
+ "bare-path": "^3.0.0"
+ }
+ },
+ "node_modules/@yao-pkg/pkg-fetch/node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
"node_modules/@yao-pkg/pkg-fetch/node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -4164,6 +4207,56 @@
"node": ">=10"
}
},
+ "node_modules/@yao-pkg/pkg/node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@yao-pkg/pkg/node_modules/minizlib": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@yao-pkg/pkg/node_modules/tar": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz",
+ "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@yao-pkg/pkg/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/abbrev": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
@@ -4179,7 +4272,6 @@
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4263,7 +4355,6 @@
"integrity": "sha512-wioVnf/8uuG8Bmywhk5qKIQ3wzCCtmdvicPRb0fa3kKYGGoewfgDqLEaET1MV2NbTc3WGpPv+AgauLVBp1nB9A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@algolia/client-abtesting": "5.34.0",
"@algolia/client-analytics": "5.34.0",
@@ -4499,12 +4590,124 @@
"proxy-from-env": "^1.1.0"
}
},
+ "node_modules/b4a": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
+ "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
+ "node_modules/bare-events": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
+ "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-fs": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz",
+ "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "bare-events": "^2.5.4",
+ "bare-path": "^3.0.0",
+ "bare-stream": "^2.6.4",
+ "bare-url": "^2.2.2",
+ "fast-fifo": "^1.3.2"
+ },
+ "engines": {
+ "bare": ">=1.16.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-os": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
+ "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "bare": ">=1.14.0"
+ }
+ },
+ "node_modules/bare-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
+ "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "bare-os": "^3.0.1"
+ }
+ },
+ "node_modules/bare-stream": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
+ "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "streamx": "^2.21.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*",
+ "bare-events": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-events": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-url": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
+ "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "bare-path": "^3.0.0"
+ }
+ },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -4675,7 +4878,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001726",
"electron-to-chromium": "^1.5.173",
@@ -4981,7 +5183,6 @@
"integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.3",
@@ -6109,6 +6310,16 @@
"node": ">= 0.4"
}
},
+ "node_modules/duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "readable-stream": "^2.0.2"
+ }
+ },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -6187,7 +6398,6 @@
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
"integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"ansi-colors": "^4.1.1",
"strip-ansi": "^6.0.1"
@@ -6364,7 +6574,6 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.4.3",
@@ -6702,6 +6911,16 @@
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
@@ -6760,6 +6979,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -7023,7 +7249,6 @@
"integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"tabbable": "^6.2.0"
}
@@ -7979,11 +8204,13 @@
"license": "ISC"
},
"node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true,
- "license": "ISC"
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz",
+ "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==",
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
},
"node_modules/inquirer": {
"version": "6.5.2",
@@ -10995,6 +11222,13 @@
"he": "1.2.0"
}
},
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/node-preload": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
@@ -12061,7 +12295,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -12415,6 +12648,13 @@
"rc": "cli.js"
}
},
+ "node_modules/rc/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/rc/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
@@ -13759,6 +13999,18 @@
"readable-stream": "^2.1.4"
}
},
+ "node_modules/streamx": {
+ "version": "2.23.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
+ "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "events-universal": "^1.0.0",
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ }
+ },
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -14265,6 +14517,16 @@
"node": "*"
}
},
+ "node_modules/text-decoder": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
+ "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -14778,6 +15040,20 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/unzipper": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.3.tgz",
+ "integrity": "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bluebird": "~3.7.2",
+ "duplexer2": "~0.1.4",
+ "fs-extra": "^11.2.0",
+ "graceful-fs": "^4.2.2",
+ "node-int64": "^0.4.0"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -14911,7 +15187,6 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -14972,7 +15247,6 @@
"integrity": "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@docsearch/css": "3.8.2",
"@docsearch/js": "3.8.2",
@@ -15138,7 +15412,6 @@
"integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.17",
"@vue/compiler-sfc": "3.5.17",
diff --git a/package.json b/package.json
index 1c56d611d..b4604159d 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,15 @@
{
- "name": "@lando/core",
+ "name": "@florianpat/lando-core",
"description": "The libraries that power all of Lando.",
- "version": "3.26.2",
- "author": "Mike Pirog @pirog",
+ "version": "3.26.3-1florianPat.4",
+ "author": "Florian Patruck @florianPat",
"license": "MIT",
- "repository": "lando/core",
- "bugs": "https://github.com/lando/core/issues/new/choose",
- "homepage": "https://github.com/lando/core",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/HDNET/lando-core.git"
+ },
+ "bugs": "https://github.com/HDNET/lando-core/issues/new/choose",
+ "homepage": "https://github.com/HDNET/lando-core",
"keywords": [
"lando",
"lando-plugin"
@@ -106,6 +109,7 @@
"figures": "^3.2.0",
"fs-extra": "^11.1.1",
"glob": "^7.1.3",
+ "ini": "^5.0.0",
"inquirer": "^6.5.2",
"inquirer-autocomplete-prompt": "^1.4.0",
"is-class": "^0.0.9",
@@ -148,7 +152,7 @@
"@babel/eslint-parser": "^7.16.0",
"@lando/leia": "^1.0.0-beta.4",
"@lando/vitepress-theme-default-plus": "^1.1.1",
- "@yao-pkg/pkg": "^5.16.1",
+ "@yao-pkg/pkg": "^6.11.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"chai-events": "^0.0.1",
diff --git a/scripts/check-entrypoint-ran.sh b/scripts/check-entrypoint-ran.sh
new file mode 100755
index 000000000..07e9c677a
--- /dev/null
+++ b/scripts/check-entrypoint-ran.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# retry settings
+attempt=0
+delay=1
+retry=32
+
+until [ "$attempt" -ge "$retry" ]
+do
+ test -f "/tmp/lando-entrypoint-ran" && break
+ attempt=$((attempt+1))
+ sleep "$delay"
+done
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/proxy-certs.sh b/scripts/proxy-certs.sh
index 66ae3b38f..946275fa0 100644
--- a/scripts/proxy-certs.sh
+++ b/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/scripts/user-perm-helpers.sh b/scripts/user-perm-helpers.sh
index 28db38732..917ea9f50 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;
}
@@ -45,6 +39,7 @@ reset_user() {
local HOST_UID=$3
local HOST_GID=$4
local DISTRO=$5
+ local USER_HOME=$6
local HOST_GROUP=$GROUP
if getent group "$HOST_GID" 1>/dev/null 2>/dev/null; then
HOST_GROUP=$(getent group "$HOST_GID" | cut -d: -f1)
@@ -53,17 +48,16 @@ reset_user() {
deluser "$USER" 2>/dev/null
addgroup -g "$HOST_GID" "$GROUP" 2>/dev/null | addgroup "$GROUP" 2>/dev/null
addgroup -g "$HOST_GID" "$HOST_GROUP" 2>/dev/null
- adduser -u "$HOST_UID" -G "$HOST_GROUP" -h /var/www -D "$USER" 2>/dev/null
+ adduser -u "$HOST_UID" -G "$HOST_GROUP" -h "$USER_HOME" -D "$USER" 2>/dev/null
adduser "$USER" "$GROUP" 2>/dev/null
else
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
@@ -78,33 +72,31 @@ reset_user() {
perm_sweep() {
local USER=$1
local GROUP=$2
- local OTHER_DIR=$3
-
- # Start with the directories that are likely blockers
- chown -R $USER:$GROUP /usr/local/bin
- chown $USER:$GROUP /var/www
- chown $USER:$GROUP /app
- chmod 755 /var/www
+ local USER_HOME=$3
+ local OTHER_DIR=$4
# Do other dirs first if we have them
if [ ! -z "$OTHER_DIR" ]; then
- chown -R $USER:$GROUP $OTHER_DIR >/dev/null 2>&1 &
+ chown -R $USER:$GROUP $OTHER_DIR > /tmp/perms.out 2> /tmp/perms.err || true
fi
- # Do a background sweep
- nohup find /app -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err &
- nohup find /var/www/.ssh -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err &
- 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 &
+ # Do permission sweep and wait for completion
+ chown -R $USER:$GROUP /app > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned /app"
+ chown -R $USER:$GROUP /tmp > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned /tmp"
+ [ -d /user ] && chown -R $USER:$GROUP /user > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned /user"
+ chown -R $USER:$GROUP /var/www > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned /var/www"
+ chmod 755 /var/www
- # 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 &
- nohup find /usr/local/share -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err &
- nohup find /usr/local -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err &
+ chown -R $USER:$GROUP /usr/local > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned /usr/local"
# Make sure we chown the $USER home directory
- nohup find $(getent passwd $USER | cut -d : -f 6) -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err &
- nohup find /lando -not -user $USER -execdir chown $USER:$GROUP {} \+ > /tmp/perms.out 2> /tmp/perms.err &
+ [ -d "$USER_HOME" ] && chown -R $USER:$GROUP "$USER_HOME" > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned $USER_HOME"
+ [ -d /lando/keys ] && chown -R $USER:$GROUP /lando/keys > /tmp/perms.out 2> /tmp/perms.err || true
+ lando_info "chowned /lando"
}
diff --git a/scripts/user-perms.sh b/scripts/user-perms.sh
index 215573859..f12bae898 100755
--- a/scripts/user-perms.sh
+++ b/scripts/user-perms.sh
@@ -52,17 +52,25 @@ mkdir -p /var/www/.ssh
mkdir -p /user/.ssh
mkdir -p /app
+# Get the webroot user's home directory
+WEBROOT_HOME=$(getent passwd "$LANDO_WEBROOT_USER" | cut -d : -f 6)
+if [ -z "$WEBROOT_HOME" ]; then
+ WEBROOT_HOME="/var/www"
+fi
+
+lando_info "meUsers home directory: $WEBROOT_HOME"
+
# Symlink the gitconfig
-if [ -f "/user/.gitconfig" ]; then
- rm -f /var/www/.gitconfig
- ln -sf /user/.gitconfig /var/www/.gitconfig
+if [ -f "/user/.gitconfig" ] && [ ! -f "$WEBROOT_HOME/.gitconfig" ]; then
+ mkdir -p "$WEBROOT_HOME"
+ ln -sf /user/.gitconfig "$WEBROOT_HOME/.gitconfig"
lando_info "Symlinked users .gitconfig."
fi
# Symlink the known_hosts
-if [ -f "/user/.ssh/known_hosts" ]; then
- rm -f /var/www/.ssh/known_hosts
- ln -sf /user/.ssh/known_hosts /var/www/.ssh/known_hosts
+if [ -f "/user/.ssh/known_hosts" ] && [ ! -f "$WEBROOT_HOME/.ssh/known_hosts" ]; then
+ mkdir -p "$WEBROOT_HOME/.ssh"
+ ln -sf /user/.ssh/known_hosts "$WEBROOT_HOME/.ssh/known_hosts"
lando_info "Symlinked users known_hosts"
fi
@@ -95,10 +103,10 @@ verify_user $LANDO_WEBROOT_USER $LANDO_WEBROOT_GROUP $FLAVOR
# Lets do this regardless of OS now
lando_info "Remapping ownership to handle docker volume sharing..."
lando_info "Resetting $LANDO_WEBROOT_USER:$LANDO_WEBROOT_GROUP from $LANDO_WEBROOT_UID:$LANDO_WEBROOT_GID to $LANDO_HOST_UID:$LANDO_HOST_GID"
-reset_user $LANDO_WEBROOT_USER $LANDO_WEBROOT_GROUP $LANDO_HOST_UID $LANDO_HOST_GID $FLAVOR
+reset_user $LANDO_WEBROOT_USER $LANDO_WEBROOT_GROUP $LANDO_HOST_UID $LANDO_HOST_GID $FLAVOR $WEBROOT_HOME
lando_info "$LANDO_WEBROOT_USER:$LANDO_WEBROOT_GROUP is now running as $(id $LANDO_WEBROOT_USER)!"
# Make sure we set the ownership of the mount and HOME when we start a service
lando_info "And here. we. go."
lando_info "Doing the permission sweep."
-perm_sweep $LANDO_WEBROOT_USER $(getent group "$LANDO_HOST_GID" | cut -d: -f1) $LANDO_RESET_DIR
+perm_sweep $LANDO_WEBROOT_USER $(getent group "$LANDO_HOST_GID" | cut -d: -f1) $WEBROOT_HOME $LANDO_RESET_DIR
diff --git a/tasks/exec.js b/tasks/exec.js
index 4844b5344..63c140df1 100644
--- a/tasks/exec.js
+++ b/tasks/exec.js
@@ -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',
@@ -25,7 +25,7 @@ module.exports = (lando, config = lando.appConfig) => ({
service: {
describe: 'Runs on this service',
type: 'string',
- choices: config?.allServices ?? [],
+ choices: config?.allServices ?? _.keys(lando.appConfig.services) ?? [],
},
},
options: {
@@ -38,9 +38,10 @@ module.exports = (lando, config = lando.appConfig) => ({
// construct a minapp from various places
const minapp = !_.isEmpty(config) ? config : lando.appConfig;
- // if no app then we need to throw
+ // if no app then we need to create one
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
@@ -49,6 +50,8 @@ module.exports = (lando, config = lando.appConfig) => ({
// augment
app.config = minapp;
+ app._lando = lando;
+ app._config = lando.config;
app.events = new AsyncEvents(lando.log);
// Load only what we need so we don't pay the appinit penalty
@@ -127,6 +130,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 +142,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 {
@@ -156,3 +155,4 @@ module.exports = (lando, config = lando.appConfig) => ({
}
},
});
+
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/plugin-add.js b/tasks/plugin-add.js
index 33a7cf653..f77ffea37 100644
--- a/tasks/plugin-add.js
+++ b/tasks/plugin-add.js
@@ -84,6 +84,8 @@ module.exports = lando => {
},
});
+ await lando.events.emit('post-install-plugins', {errors, results, total});
+
// status
console.log();
console.log('added %s of %s plugins with %s errors', results.length, total, errors.length); // eslint-disable-line max-len
diff --git a/tasks/plugin-remove.js b/tasks/plugin-remove.js
index 9a7f55df8..9cb633339 100644
--- a/tasks/plugin-remove.js
+++ b/tasks/plugin-remove.js
@@ -39,6 +39,8 @@ module.exports = lando => {
},
});
+ await lando.events.emit('post-install-plugins', {errors, results, total});
+
// otherwise we good!
console.log();
console.log('removed %s of %s plugins with %s errors', results.length, total, errors.length); // eslint-disable-line max-len
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/tasks/update.js b/tasks/update.js
index 69938c6e3..a277d2513 100644
--- a/tasks/update.js
+++ b/tasks/update.js
@@ -157,6 +157,7 @@ module.exports = lando => {
},
});
+ await lando.events.emit('post-install-plugins', {errors, results});
// flush relevant caches
lando.cli.clearTaskCaches();
lando.cache.remove('updates-2');
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-config.js b/utils/build-config.js
index 39cf1db1f..b38afded9 100644
--- a/utils/build-config.js
+++ b/utils/build-config.js
@@ -66,7 +66,7 @@ module.exports = options => {
// Set up the default engine config if needed
config.engineConfig = getEngineConfig(config);
// Strip all COMPOSE_ envvars
- config.env = stripEnv('COMPOSE_');
+ // config.env = stripEnv('COMPOSE_');
// Disable docker CLI_HINTS
config.env.DOCKER_CLI_HINTS = false;
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 089a07067..0bfee3ce1 100644
--- a/utils/build-tooling-task.js
+++ b/utils/build-tooling-task.js
@@ -1,6 +1,8 @@
'use strict';
const _ = require('lodash');
+const remove = require('./remove');
+const path = require('path');
module.exports = (config, injected) => {
// Get our defaults and such
@@ -17,29 +19,61 @@ 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, sapis)))
- // 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, sapis)))
+ // 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 => 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;
+ }
+ });
+ }))
+ // 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 {
@@ -47,5 +81,6 @@ module.exports = (config, injected) => {
describe,
run,
options,
+ service,
};
};
diff --git a/utils/filter-v3-build-steps.js b/utils/filter-v3-build-steps.js
index fc3a2ca7e..2190f9c97 100644
--- a/utils/filter-v3-build-steps.js
+++ b/utils/filter-v3-build-steps.js
@@ -4,6 +4,7 @@ const _ = require('lodash');
module.exports = (services, app, rootSteps = [], buildSteps= [], prestart = false) => {
const getUser = require('../utils/get-user');
+ const getAppMount = require('../utils/get-app-mount');
// compute stdid based on compose major version
const cstdio = _.get(app, '_config.orchestratorMV', 2) ? 'inherit' : ['inherit', 'pipe', 'pipe'];
// Start collecting them
@@ -29,6 +30,7 @@ module.exports = (services, app, rootSteps = [], buildSteps= [], prestart = fals
mode: 'attach',
cstdio,
prestart,
+ workdir: getAppMount(service, app.info),
user: (_.includes(rootSteps, section)) ? 'root' : getUser(service, app.info),
services: [service],
},
@@ -37,26 +39,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-mount.js b/utils/get-app-mount.js
new file mode 100644
index 000000000..c8d0634c2
--- /dev/null
+++ b/utils/get-app-mount.js
@@ -0,0 +1,11 @@
+'use strict';
+
+const _ = require('lodash');
+
+module.exports = (name, info = []) => {
+ // if no matching service return /app
+ if (!_.find(info, {service: name})) return '/app';
+ // otherwise get the service
+ const service = _.find(info, {service: name});
+ return service.appMount || '/app';
+};
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 b86883bd2..a50121cda 100644
--- a/utils/get-app.js
+++ b/utils/get-app.js
@@ -24,8 +24,6 @@ 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);
return _.merge({}, config, {
configFiles: files,
diff --git a/utils/get-cli-env.js b/utils/get-cli-env.js
index ebaf3938f..212051bdc 100644
--- a/utils/get-cli-env.js
+++ b/utils/get-cli-env.js
@@ -2,6 +2,13 @@
const _ = require('lodash');
-module.exports = (more = {}) => _.merge({}, {
- PHP_MEMORY_LIMIT: '-1',
-}, more);
+module.exports = function(more = {}) {
+ let githubEnvVars = {};
+ if (process.env.LANDO_CLI_ENV_JSON) {
+ githubEnvVars = JSON.parse(process.env.LANDO_CLI_ENV_JSON);
+ }
+
+ return _.merge({}, {
+ PHP_MEMORY_LIMIT: '-1',
+ }, githubEnvVars, more);
+};
diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js
index ead6181e8..17c37a947 100644
--- a/utils/get-config-defaults.js
+++ b/utils/get-config-defaults.js
@@ -21,7 +21,7 @@ const getBuildEngineVersion = (platform = process.landoPlatform ?? process.platf
// Default config
const defaultConfig = options => ({
orchestratorSeparator: '_',
- orchestratorVersion: '2.31.0',
+ orchestratorVersion: '2.40.3',
configSources: [],
coreBase: path.resolve(__dirname, '..'),
disablePlugins: [],
diff --git a/utils/get-docker-bin-path.js b/utils/get-docker-bin-path.js
index ec4a00f42..3b3ecf904 100644
--- a/utils/get-docker-bin-path.js
+++ b/utils/get-docker-bin-path.js
@@ -5,9 +5,8 @@ const path = require('path');
module.exports = (platform = process.landoPlatform ?? process.platform) => {
switch (platform) {
- case 'darwin':
- return '/Applications/Docker.app/Contents/Resources/bin';
case 'linux':
+ case 'wsl':
return '/usr/share/lando/bin';
case 'win32': {
const programFiles = process.env.ProgramW6432 || process.env.ProgramFiles;
@@ -20,8 +19,6 @@ module.exports = (platform = process.landoPlatform ?? process.platform) => {
return path.win32.join(programFiles + '\\Docker\\Docker\\resources\\bin');
}
}
- case 'wsl':
- return '/mnt/wsl/docker-desktop/cli-tools/usr/bin';
default:
return '/usr/bin';
}
diff --git a/utils/get-init-runner-defaults.js b/utils/get-init-runner-defaults.js
index d63666c68..f046e1c2f 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'),
@@ -21,10 +22,13 @@ module.exports = (lando, options) => {
const separator = lando.config.orchestratorSeparator;
// Return
return {
- id: [`${project}${separator}init${separator}1`],
+ id: `${project}${separator}init${separator}1`,
project,
user: 'www-data',
compose: initFiles,
remove: false,
+ workdir: '/',
+ prestart: true,
+ env: {},
};
};
diff --git a/utils/get-tasks.js b/utils/get-tasks.js
index 3ae593808..9e34aed29 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 /
@@ -41,9 +42,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)));
};
/*
@@ -53,6 +55,8 @@ const engineRunner = (config, command) => (argv, lando) => {
const AsyncEvents = require('./../lib/events');
// Build a minimal app
const app = lando.cache.get(path.basename(config.composeCache));
+ app._lando = lando;
+ app._config = lando.config;
app.config = config;
app.events = new AsyncEvents(lando.log);
@@ -129,7 +133,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..1a0eafe31
--- /dev/null
+++ b/utils/parse-compose-services.js
@@ -0,0 +1,31 @@
+'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))
+ .filter(service => 4 !== service.api)
+ // Build the config and ensure api is set to 3
+ .map(service => _.merge({}, {
+ _app: app,
+ app: app.name,
+ home: app.config.home || app._config.home,
+ project: app.project,
+ root: app.root,
+ type: '_lando-compose',
+ version: 'custom',
+ userConfRoot: app._config.userConfRoot,
+ 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
+ meUser: 'root',
+ appMount: '/',
+ sslExpose: false,
+ }, service))
+ .value();
diff --git a/utils/parse-events-config.js b/utils/parse-events-config.js
index 017fd3b62..8dc1fa07c 100644
--- a/utils/parse-events-config.js
+++ b/utils/parse-events-config.js
@@ -36,9 +36,10 @@ 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 service = getService(cmd, data, app._defaultService);
+
// compute stdio based on compose major version
const cstdio = _.get(app, '_config.orchestratorMV', 2) ? 'inherit' : ['inherit', 'pipe', 'pipe'];
@@ -53,6 +54,25 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => {
// if array then just join it together
if (_.isArray(cmd)) cmd = cmd.join(' ');
+ if ('lando' === service) {
+ const yargs = require('yargs');
+ const argv = yargs(cmd).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,
+ };
+ }
+
// lando 4 services
// @NOTE: lando 4 service events will change once we have a complete hook system
if (sapi === 4) {
@@ -76,6 +96,19 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => {
_.get(app, 'v4.servicesList', []),
]).flatten().compact().uniq().value();
+
+ 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 (
@@ -95,6 +128,7 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => {
opts: {
cstdio,
mode: 'attach',
+ workdir: require('./get-app-mount')(service, app.info),
user: require('./get-user')(service, app.info),
services: [service],
environment: {
diff --git a/utils/parse-tooling-config.js b/utils/parse-tooling-config.js
index e3e21cddd..1f7150355 100644
--- a/utils/parse-tooling-config.js
+++ b/utils/parse-tooling-config.js
@@ -20,11 +20,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 = {}, sapis = {}) => {
+const handleDynamic = (config, argv, answers = {}, sapis = {}) => {
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
@@ -41,9 +41,9 @@ const handleDynamic = (config, options, answers = {}, sapis = {}) => {
* 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
@@ -74,13 +74,13 @@ const parseCommand = (cmd, service, sapis) => {
};
// adds required methods to ensure the lando v3 debugger can be injected into v4 things
-module.exports = (cmd, service, options = {}, answers = {}, sapis = {}) => _(cmd)
+module.exports = (cmd, service, name, options = {}, answers = {}, sapis = {}) => _(cmd)
// Put into an object so we can handle "multi-service" tooling
.map(cmd => parseCommand(cmd, service, sapis))
// Handle dynamic services
- .map(config => handleDynamic(config, options, answers, sapis))
+ .map(config => handleDynamic(config, answers._eventArgs ?? process.argv, answers, sapis))
// 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 => ({...config, command: require('./shell-escape')(config.command, true, config.args, config.sapi)}))
// 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/parse-v4-services.js b/utils/parse-v4-services.js
index 3d5dc2fd1..567be08b1 100644
--- a/utils/parse-v4-services.js
+++ b/utils/parse-v4-services.js
@@ -5,6 +5,7 @@ const _ = require('lodash');
// adds required methods to ensure the lando v3 debugger can be injected into v4 things
module.exports = services => _(services)
+ .pickBy(service => null !== service)
.map((service, name) => {
const type = service.type ?? 'lando';
return _.merge({}, {
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])));
});