@florianpat/lando-core 3.23.22 → 3.23.27-1florianPat.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/app.js +2 -0
  3. package/builders/_lando.js +19 -6
  4. package/builders/lando-v4.js +3 -0
  5. package/config.yml +4 -4
  6. package/hooks/app-run-events.js +22 -0
  7. package/hooks/lando-setup-build-engine-darwin.js +3 -0
  8. package/hooks/lando-setup-build-engine-win32.js +2 -0
  9. package/hooks/lando-setup-build-engine-wsl.js +2 -0
  10. package/hooks/lando-setup-orchestrator.js +2 -2
  11. package/lib/daemon.js +1 -1
  12. package/lib/updates.js +9 -1
  13. package/netlify.toml +1 -0
  14. package/node_modules/undici/docs/docs/api/Dispatcher.md +51 -0
  15. package/node_modules/undici/index.js +2 -1
  16. package/node_modules/undici/lib/api/api-request.js +1 -1
  17. package/node_modules/undici/lib/core/connect.js +5 -0
  18. package/node_modules/undici/lib/dispatcher/client-h2.js +20 -6
  19. package/node_modules/undici/lib/handler/retry-handler.js +3 -3
  20. package/node_modules/undici/lib/interceptor/dns.js +375 -0
  21. package/node_modules/undici/lib/web/cache/cache.js +1 -0
  22. package/node_modules/undici/lib/web/cache/cachestorage.js +2 -0
  23. package/node_modules/undici/lib/web/eventsource/eventsource.js +2 -0
  24. package/node_modules/undici/lib/web/fetch/body.js +9 -1
  25. package/node_modules/undici/lib/web/fetch/formdata.js +2 -0
  26. package/node_modules/undici/lib/web/fetch/headers.js +2 -0
  27. package/node_modules/undici/lib/web/fetch/index.js +1 -1
  28. package/node_modules/undici/lib/web/fetch/request.js +1 -0
  29. package/node_modules/undici/lib/web/fetch/response.js +1 -0
  30. package/node_modules/undici/lib/web/fetch/webidl.js +2 -0
  31. package/node_modules/undici/lib/web/websocket/events.js +4 -0
  32. package/node_modules/undici/lib/web/websocket/websocket.js +2 -0
  33. package/node_modules/undici/package.json +1 -1
  34. package/node_modules/undici/types/interceptors.d.ts +14 -0
  35. package/node_modules/undici/types/retry-handler.d.ts +1 -1
  36. package/node_modules/undici/types/webidl.d.ts +6 -0
  37. package/package.json +6 -6
  38. package/release-aliases/3-EDGE +1 -1
  39. package/release-aliases/3-STABLE +1 -1
  40. package/scripts/install-docker-desktop.sh +1 -1
  41. package/scripts/install-docker-engine.sh +1 -1
  42. package/scripts/lando-entrypoint.sh +1 -1
  43. package/utils/get-compose-x.js +1 -1
  44. package/utils/get-config-defaults.js +5 -5
  45. package/utils/to-lando-container.js +16 -2
  46. package/checksums.txt +0 -0
package/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
1
  ## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }})
2
2
 
3
- ## v3.23.22 - [January 6, 2025](https://github.com/florianPat/lando-core/releases/tag/v3.23.22)
3
+ ## v3.23.27-1florianPat.0 - [February 17, 2025](https://github.com/florianPat/lando-core/releases/tag/v3.23.27-1florianPat.0)
4
+
5
+ ## v3.23.26 - [January 24, 2025](https://github.com/lando/core/releases/tag/v3.23.26)
6
+
7
+ * Fixed bug where an app’s services were inadvertently reaped if the app’s path included a comma [#322](https://github.com/lando/core/issues/322)
8
+
9
+ ## v3.23.25 - [January 18, 2025](https://github.com/lando/core/releases/tag/v3.23.25)
10
+
11
+ * Fixed bug causing `--accept-license` flag to not work when installing Docker Desktop on macOS
12
+ * Updated default Docker Desktop version to `4.37.1|2`
13
+ * Updated default Docker Engine version to `27.5.0`
14
+ * Updated default Docker Compose version to `2.31.0`
15
+ * Updated recommended Docker Desktop range to `>=4.37.0`
16
+ * Updated tested Docker Desktop range to `<=4.37`
17
+ * Updated tested Docker Compose range to `<=2.32`
18
+
19
+ ## v3.23.24 - [January 14, 2025](https://github.com/lando/core/releases/tag/v3.23.24)
20
+
21
+ * Fixed bug causing service script moving to fail when receiving non-stringy inputs
22
+
23
+ ## v3.23.23 - [January 14, 2025](https://github.com/lando/core/releases/tag/v3.23.23)
24
+
25
+ * Fixed bug causing service script loading collisions
4
26
 
5
27
  ## v3.23.22 - [December 17, 2024](https://github.com/lando/core/releases/tag/v3.23.22)
6
28
 
package/app.js CHANGED
@@ -240,6 +240,8 @@ module.exports = async (app, lando) => {
240
240
  BITNAMI_DEBUG: 'true',
241
241
  },
242
242
  labels: {
243
+ 'io.lando.landofiles': app.configFiles.map(file => path.basename(file)).join(','),
244
+ 'io.lando.root': app.root,
243
245
  'io.lando.src': app.configFiles.join(','),
244
246
  'io.lando.http-ports': '80,443',
245
247
  },
@@ -41,6 +41,7 @@ module.exports = {
41
41
  refreshCerts = false,
42
42
  remoteFiles = {},
43
43
  scripts = [],
44
+ scriptsDir = false,
44
45
  sport = '443',
45
46
  ssl = false,
46
47
  sslExpose = true,
@@ -68,16 +69,25 @@ module.exports = {
68
69
  console.error(color.yellow(`${type} version ${version} is a legacy version! We recommend upgrading.`));
69
70
  }
70
71
 
72
+ // normalize scripts dir if needed
73
+ if (typeof scriptsDir === 'string' && !path.isAbsolute(scriptsDir)) scriptsDir = path.resolve(root, scriptsDir);
74
+
75
+ // Get some basic locations
76
+ const globalScriptsDir = path.join(userConfRoot, 'scripts');
77
+ const serviceScriptsDir = path.join(userConfRoot, 'helpers', project, type, name);
78
+ const entrypointScript = path.join(globalScriptsDir, 'lando-entrypoint.sh');
79
+ const addCertsScript = path.join(globalScriptsDir, 'add-cert.sh');
80
+ const refreshCertsScript = path.join(globalScriptsDir, 'refresh-certs.sh');
81
+
71
82
  // Move our config into the userconfroot if we have some
72
83
  // NOTE: we need to do this because on macOS and Windows not all host files
73
84
  // are shared into the docker vm
74
85
  if (fs.existsSync(confSrc)) require('../utils/move-config')(confSrc, confDest);
75
86
 
76
- // Get some basic locations
77
- const scriptsDir = path.join(userConfRoot, 'scripts');
78
- const entrypointScript = path.join(scriptsDir, 'lando-entrypoint.sh');
79
- const addCertsScript = path.join(scriptsDir, 'add-cert.sh');
80
- const refreshCertsScript = path.join(scriptsDir, 'refresh-certs.sh');
87
+ // ditto for service helpers
88
+ if (!require('../utils/is-disabled')(scriptsDir) && typeof scriptsDir === 'string' && fs.existsSync(scriptsDir)) {
89
+ require('../utils/move-config')(scriptsDir, serviceScriptsDir);
90
+ }
81
91
 
82
92
  // Handle Environment
83
93
  const environment = {
@@ -100,10 +110,13 @@ module.exports = {
100
110
  // Handle volumes
101
111
  const volumes = [
102
112
  `${userConfRoot}:/lando:cached`,
103
- `${scriptsDir}:/helpers`,
113
+ `${globalScriptsDir}:/helpers`,
104
114
  `${entrypointScript}:/lando-entrypoint.sh`,
105
115
  ];
106
116
 
117
+ // add in service helpers if we have them
118
+ if (fs.existsSync(serviceScriptsDir)) volumes.push(`${serviceScriptsDir}:/etc/lando/service/helpers`);
119
+
107
120
  // Handle ssl
108
121
  if (ssl) {
109
122
  // also expose the sport
@@ -443,7 +443,10 @@ module.exports = {
443
443
  const labels = merge({}, app.labels, {
444
444
  'dev.lando.container': 'TRUE',
445
445
  'dev.lando.id': lando.config.id,
446
+ 'dev.lando.landofiles': app.configFiles.map(file => path.basename(file)).join(','),
447
+ 'dev.lando.root': app.root,
446
448
  'dev.lando.src': app.root,
449
+ 'io.lando.http-ports': '80,443',
447
450
  }, config.labels);
448
451
 
449
452
  // add it all 2getha
package/config.yml CHANGED
@@ -13,21 +13,21 @@ dockerSupportedVersions:
13
13
  compose:
14
14
  satisfies: "1.x.x || 2.x.x"
15
15
  recommendUpdate: "<=2.24.6"
16
- tested: "<=2.30.99"
16
+ tested: "<=2.32.99"
17
17
  link:
18
18
  linux: https://docs.docker.com/compose/install/#install-compose-on-linux-systems
19
19
  darwin: https://docs.docker.com/desktop/install/mac-install/
20
20
  win32: https://docs.docker.com/desktop/install/windows-install/
21
21
  desktop:
22
22
  satisfies: ">=4.0.0 <5"
23
- tested: "<=4.36.99"
24
- recommendUpdate: "<=4.34.0"
23
+ tested: "<=4.37.99"
24
+ recommendUpdate: "<=4.36"
25
25
  link:
26
26
  darwin: https://docs.docker.com/desktop/install/mac-install/
27
27
  win32: https://docs.docker.com/desktop/install/windows-install/
28
28
  wsl: https://docs.docker.com/desktop/install/windows-install/
29
29
  engine:
30
30
  satisfies: ">=18 <28"
31
- tested: "<=27.3.1"
31
+ tested: "<=27.5.99"
32
32
  link:
33
33
  linux: https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script
@@ -7,6 +7,28 @@ const formatters = require('../lib/formatters');
7
7
 
8
8
  module.exports = async (app, lando, cmds, data, event) => {
9
9
  const eventCommands = require('./../utils/parse-events-config')(cmds, app, data, lando);
10
+ // add perm sweeping to all v3 services
11
+ if (!_.isEmpty(eventCommands)) {
12
+ const permsweepers = _(eventCommands)
13
+ .filter(command => command.api === 3)
14
+ .map(command => ({id: command.id, services: _.get(command, 'opts.services', [])}))
15
+ .uniqBy('id')
16
+ .value();
17
+ lando.log.debug('added preemptive perm sweeping to evented v3 services %j', permsweepers.map(s => s.id));
18
+ _.forEach(permsweepers, ({id, services}) => {
19
+ eventCommands.unshift({
20
+ id,
21
+ cmd: '/helpers/user-perms.sh --silent',
22
+ compose: app.compose,
23
+ project: app.project,
24
+ opts: {
25
+ mode: 'attach',
26
+ user: 'root',
27
+ services,
28
+ },
29
+ });
30
+ });
31
+ }
10
32
  const injectable = _.has(app, 'engine') ? app : lando;
11
33
 
12
34
  const splitEventCommands = [];
@@ -10,6 +10,9 @@ const semver = require('semver');
10
10
  const {color} = require('listr2');
11
11
 
12
12
  const buildIds = {
13
+ '4.37.2': '179585',
14
+ '4.37.1': '178610',
15
+ '4.37.0': '178034',
13
16
  '4.36.0': '175267',
14
17
  '4.35.1': '173168',
15
18
  '4.35.0': '172550',
@@ -10,6 +10,8 @@ const {color} = require('listr2');
10
10
  const {nanoid} = require('nanoid');
11
11
 
12
12
  const buildIds = {
13
+ '4.37.1': '178610',
14
+ '4.37.0': '178034',
13
15
  '4.36.0': '175267',
14
16
  '4.35.1': '173168',
15
17
  '4.35.0': '172550',
@@ -12,6 +12,8 @@ const {color} = require('listr2');
12
12
  const {nanoid} = require('nanoid');
13
13
 
14
14
  const buildIds = {
15
+ '4.37.1': '178610',
16
+ '4.37.0': '178034',
15
17
  '4.36.0': '175267',
16
18
  '4.35.1': '173168',
17
19
  '4.35.0': '172550',
@@ -7,7 +7,7 @@ const path = require('path');
7
7
  /*
8
8
  * Helper to get docker compose v2 download url
9
9
  */
10
- const getComposeDownloadUrl = (version = '2.30.3') => {
10
+ const getComposeDownloadUrl = (version = '2.31.0') => {
11
11
  const mv = version.split('.')[0] > 1 ? '2' : '1';
12
12
  const arch = process.arch === 'arm64' ? 'aarch64' : 'x86_64';
13
13
  const toggle = `${process.platform}-${mv}`;
@@ -31,7 +31,7 @@ const getComposeDownloadUrl = (version = '2.30.3') => {
31
31
  /*
32
32
  * Helper to get docker compose v2 download destination
33
33
  */
34
- const getComposeDownloadDest = (base, version = '2.30.3') => {
34
+ const getComposeDownloadDest = (base, version = '2.31.0') => {
35
35
  switch (process.platform) {
36
36
  case 'linux':
37
37
  case 'darwin':
package/lib/daemon.js CHANGED
@@ -53,7 +53,7 @@ module.exports = class LandoDaemon {
53
53
  log = new Log(),
54
54
  context = 'node',
55
55
  compose = require('../utils/get-compose-x')(),
56
- orchestratorVersion = '2.30.3',
56
+ orchestratorVersion = '2.31.0',
57
57
  userConfRoot = path.join(os.homedir(), '.lando'),
58
58
  ) {
59
59
  this.cache = cache;
package/lib/updates.js CHANGED
@@ -127,7 +127,15 @@ module.exports = class UpdateManager {
127
127
  const {data, status, url} = await octokit.rest.repos.listReleases({owner: 'lando', repo: 'core'});
128
128
  this.debug('retrieved cli information from %o [%o]', url, status);
129
129
 
130
- const newestCoreVersion = semver.clean((await this.plugins.find(plugin => plugin.core)?.check4Update())?.update?.version ?? '');
130
+ const corePlugin = await this.plugins.find(plugin => plugin.core);
131
+ if (undefined === corePlugin) {
132
+ throw new Error('We should find a core!');
133
+ }
134
+ let newestCoreVersion = corePlugin.check4Update()?.update?.version ?? corePlugin.version;
135
+ if (undefined === newestCoreVersion) {
136
+ throw new Error('Could not obtain the next lando core version!');
137
+ }
138
+ newestCoreVersion = semver.clean(newestCoreVersion);
131
139
 
132
140
  const versions = data
133
141
  .map(release => ({...release, version: semver.clean(release.tag_name)}))
package/netlify.toml CHANGED
@@ -20,6 +20,7 @@
20
20
  "https://docs.google.com/document",
21
21
  "https://docs.google.com/forms",
22
22
  "https://github.com",
23
+ "https://www.drupal.org/community/events",
23
24
  "/v/"
24
25
  ]
25
26
  skipPatterns = [
@@ -986,6 +986,57 @@ client.dispatch(
986
986
  );
987
987
  ```
988
988
 
989
+ ##### `dns`
990
+
991
+ The `dns` interceptor enables you to cache DNS lookups for a given duration, per origin.
992
+
993
+ >It is well suited for scenarios where you want to cache DNS lookups to avoid the overhead of resolving the same domain multiple times
994
+
995
+ **Options**
996
+ - `maxTTL` - The maximum time-to-live (in milliseconds) of the DNS cache. It should be a positive integer. Default: `10000`.
997
+ - Set `0` to disable TTL.
998
+ - `maxItems` - The maximum number of items to cache. It should be a positive integer. Default: `Infinity`.
999
+ - `dualStack` - Whether to resolve both IPv4 and IPv6 addresses. Default: `true`.
1000
+ - It will also attempt a happy-eyeballs-like approach to connect to the available addresses in case of a connection failure.
1001
+ - `affinity` - Whether to use IPv4 or IPv6 addresses. Default: `4`.
1002
+ - It can be either `'4` or `6`.
1003
+ - It will only take effect if `dualStack` is `false`.
1004
+ - `lookup: (hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void` - Custom lookup function. Default: `dns.lookup`.
1005
+ - For more info see [dns.lookup](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback).
1006
+ - `pick: (origin: URL, records: DNSInterceptorRecords, affinity: 4 | 6) => DNSInterceptorRecord` - Custom pick function. Default: `RoundRobin`.
1007
+ - The function should return a single record from the records array.
1008
+ - By default a simplified version of Round Robin is used.
1009
+ - The `records` property can be mutated to store the state of the balancing algorithm.
1010
+
1011
+ > The `Dispatcher#options` also gets extended with the options `dns.affinity`, `dns.dualStack`, `dns.lookup` and `dns.pick` which can be used to configure the interceptor at a request-per-request basis.
1012
+
1013
+
1014
+ **DNSInterceptorRecord**
1015
+ It represents a DNS record.
1016
+ - `family` - (`number`) The IP family of the address. It can be either `4` or `6`.
1017
+ - `address` - (`string`) The IP address.
1018
+
1019
+ **DNSInterceptorOriginRecords**
1020
+ It represents a map of DNS IP addresses records for a single origin.
1021
+ - `4.ips` - (`DNSInterceptorRecord[] | null`) The IPv4 addresses.
1022
+ - `6.ips` - (`DNSInterceptorRecord[] | null`) The IPv6 addresses.
1023
+
1024
+ **Example - Basic DNS Interceptor**
1025
+
1026
+ ```js
1027
+ const { Client, interceptors } = require("undici");
1028
+ const { dns } = interceptors;
1029
+
1030
+ const client = new Agent().compose([
1031
+ dns({ ...opts })
1032
+ ])
1033
+
1034
+ const response = await client.request({
1035
+ origin: `http://localhost:3030`,
1036
+ ...requestOpts
1037
+ })
1038
+ ```
1039
+
989
1040
  ##### `Response Error Interceptor`
990
1041
 
991
1042
  **Introduction**
@@ -41,7 +41,8 @@ module.exports.createRedirectInterceptor = createRedirectInterceptor
41
41
  module.exports.interceptors = {
42
42
  redirect: require('./lib/interceptor/redirect'),
43
43
  retry: require('./lib/interceptor/retry'),
44
- dump: require('./lib/interceptor/dump')
44
+ dump: require('./lib/interceptor/dump'),
45
+ dns: require('./lib/interceptor/dns')
45
46
  }
46
47
 
47
48
  module.exports.buildConnector = buildConnector
@@ -73,7 +73,7 @@ class RequestHandler extends AsyncResource {
73
73
  this.removeAbortListener = util.addAbortListener(this.signal, () => {
74
74
  this.reason = this.signal.reason ?? new RequestAbortedError()
75
75
  if (this.res) {
76
- util.destroy(this.res, this.reason)
76
+ util.destroy(this.res.on('error', util.nop), this.reason)
77
77
  } else if (this.abort) {
78
78
  this.abort(this.reason)
79
79
  }
@@ -220,6 +220,11 @@ const setupConnectTimeout = process.platform === 'win32'
220
220
  * @param {number} opts.port
221
221
  */
222
222
  function onConnectTimeout (socket, opts) {
223
+ // The socket could be already garbage collected
224
+ if (socket == null) {
225
+ return
226
+ }
227
+
223
228
  let message = 'Connect Timeout Error'
224
229
  if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
225
230
  message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
@@ -31,6 +31,8 @@ const {
31
31
 
32
32
  const kOpenStreams = Symbol('open streams')
33
33
 
34
+ let extractBody
35
+
34
36
  // Experimental
35
37
  let h2ExperimentalWarned = false
36
38
 
@@ -240,11 +242,12 @@ function onHTTP2GoAway (code) {
240
242
  util.destroy(this[kSocket], err)
241
243
 
242
244
  // Fail head of pipeline.
243
- const request = client[kQueue][client[kRunningIdx]]
244
- client[kQueue][client[kRunningIdx]++] = null
245
- util.errorRequest(client, request, err)
246
-
247
- client[kPendingIdx] = client[kRunningIdx]
245
+ if (client[kRunningIdx] < client[kQueue].length) {
246
+ const request = client[kQueue][client[kRunningIdx]]
247
+ client[kQueue][client[kRunningIdx]++] = null
248
+ util.errorRequest(client, request, err)
249
+ client[kPendingIdx] = client[kRunningIdx]
250
+ }
248
251
 
249
252
  assert(client[kRunning] === 0)
250
253
 
@@ -260,7 +263,8 @@ function shouldSendContentLength (method) {
260
263
 
261
264
  function writeH2 (client, request) {
262
265
  const session = client[kHTTP2Session]
263
- const { body, method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
266
+ const { method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
267
+ let { body } = request
264
268
 
265
269
  if (upgrade) {
266
270
  util.errorRequest(client, request, new Error('Upgrade not supported for H2'))
@@ -381,6 +385,16 @@ function writeH2 (client, request) {
381
385
 
382
386
  let contentLength = util.bodyLength(body)
383
387
 
388
+ if (util.isFormDataLike(body)) {
389
+ extractBody ??= require('../web/fetch/body.js').extractBody
390
+
391
+ const [bodyStream, contentType] = extractBody(body)
392
+ headers['content-type'] = contentType
393
+
394
+ body = bodyStream.stream
395
+ contentLength = bodyStream.length
396
+ }
397
+
384
398
  if (contentLength == null) {
385
399
  contentLength = request.contentLength
386
400
  }
@@ -229,7 +229,7 @@ class RetryHandler {
229
229
  return false
230
230
  }
231
231
 
232
- const { start, size, end = size } = contentRange
232
+ const { start, size, end = size - 1 } = contentRange
233
233
 
234
234
  assert(this.start === start, 'content-range mismatch')
235
235
  assert(this.end == null || this.end === end, 'content-range mismatch')
@@ -252,7 +252,7 @@ class RetryHandler {
252
252
  )
253
253
  }
254
254
 
255
- const { start, size, end = size } = range
255
+ const { start, size, end = size - 1 } = range
256
256
  assert(
257
257
  start != null && Number.isFinite(start),
258
258
  'content-range mismatch'
@@ -266,7 +266,7 @@ class RetryHandler {
266
266
  // We make our best to checkpoint the body for further range headers
267
267
  if (this.end == null) {
268
268
  const contentLength = headers['content-length']
269
- this.end = contentLength != null ? Number(contentLength) : null
269
+ this.end = contentLength != null ? Number(contentLength) - 1 : null
270
270
  }
271
271
 
272
272
  assert(Number.isFinite(this.start))
@@ -0,0 +1,375 @@
1
+ 'use strict'
2
+ const { isIP } = require('node:net')
3
+ const { lookup } = require('node:dns')
4
+ const DecoratorHandler = require('../handler/decorator-handler')
5
+ const { InvalidArgumentError, InformationalError } = require('../core/errors')
6
+ const maxInt = Math.pow(2, 31) - 1
7
+
8
+ class DNSInstance {
9
+ #maxTTL = 0
10
+ #maxItems = 0
11
+ #records = new Map()
12
+ dualStack = true
13
+ affinity = null
14
+ lookup = null
15
+ pick = null
16
+
17
+ constructor (opts) {
18
+ this.#maxTTL = opts.maxTTL
19
+ this.#maxItems = opts.maxItems
20
+ this.dualStack = opts.dualStack
21
+ this.affinity = opts.affinity
22
+ this.lookup = opts.lookup ?? this.#defaultLookup
23
+ this.pick = opts.pick ?? this.#defaultPick
24
+ }
25
+
26
+ get full () {
27
+ return this.#records.size === this.#maxItems
28
+ }
29
+
30
+ runLookup (origin, opts, cb) {
31
+ const ips = this.#records.get(origin.hostname)
32
+
33
+ // If full, we just return the origin
34
+ if (ips == null && this.full) {
35
+ cb(null, origin.origin)
36
+ return
37
+ }
38
+
39
+ const newOpts = {
40
+ affinity: this.affinity,
41
+ dualStack: this.dualStack,
42
+ lookup: this.lookup,
43
+ pick: this.pick,
44
+ ...opts.dns,
45
+ maxTTL: this.#maxTTL,
46
+ maxItems: this.#maxItems
47
+ }
48
+
49
+ // If no IPs we lookup
50
+ if (ips == null) {
51
+ this.lookup(origin, newOpts, (err, addresses) => {
52
+ if (err || addresses == null || addresses.length === 0) {
53
+ cb(err ?? new InformationalError('No DNS entries found'))
54
+ return
55
+ }
56
+
57
+ this.setRecords(origin, addresses)
58
+ const records = this.#records.get(origin.hostname)
59
+
60
+ const ip = this.pick(
61
+ origin,
62
+ records,
63
+ newOpts.affinity
64
+ )
65
+
66
+ let port
67
+ if (typeof ip.port === 'number') {
68
+ port = `:${ip.port}`
69
+ } else if (origin.port !== '') {
70
+ port = `:${origin.port}`
71
+ } else {
72
+ port = ''
73
+ }
74
+
75
+ cb(
76
+ null,
77
+ `${origin.protocol}//${
78
+ ip.family === 6 ? `[${ip.address}]` : ip.address
79
+ }${port}`
80
+ )
81
+ })
82
+ } else {
83
+ // If there's IPs we pick
84
+ const ip = this.pick(
85
+ origin,
86
+ ips,
87
+ newOpts.affinity
88
+ )
89
+
90
+ // If no IPs we lookup - deleting old records
91
+ if (ip == null) {
92
+ this.#records.delete(origin.hostname)
93
+ this.runLookup(origin, opts, cb)
94
+ return
95
+ }
96
+
97
+ let port
98
+ if (typeof ip.port === 'number') {
99
+ port = `:${ip.port}`
100
+ } else if (origin.port !== '') {
101
+ port = `:${origin.port}`
102
+ } else {
103
+ port = ''
104
+ }
105
+
106
+ cb(
107
+ null,
108
+ `${origin.protocol}//${
109
+ ip.family === 6 ? `[${ip.address}]` : ip.address
110
+ }${port}`
111
+ )
112
+ }
113
+ }
114
+
115
+ #defaultLookup (origin, opts, cb) {
116
+ lookup(
117
+ origin.hostname,
118
+ {
119
+ all: true,
120
+ family: this.dualStack === false ? this.affinity : 0,
121
+ order: 'ipv4first'
122
+ },
123
+ (err, addresses) => {
124
+ if (err) {
125
+ return cb(err)
126
+ }
127
+
128
+ const results = new Map()
129
+
130
+ for (const addr of addresses) {
131
+ // On linux we found duplicates, we attempt to remove them with
132
+ // the latest record
133
+ results.set(`${addr.address}:${addr.family}`, addr)
134
+ }
135
+
136
+ cb(null, results.values())
137
+ }
138
+ )
139
+ }
140
+
141
+ #defaultPick (origin, hostnameRecords, affinity) {
142
+ let ip = null
143
+ const { records, offset } = hostnameRecords
144
+
145
+ let family
146
+ if (this.dualStack) {
147
+ if (affinity == null) {
148
+ // Balance between ip families
149
+ if (offset == null || offset === maxInt) {
150
+ hostnameRecords.offset = 0
151
+ affinity = 4
152
+ } else {
153
+ hostnameRecords.offset++
154
+ affinity = (hostnameRecords.offset & 1) === 1 ? 6 : 4
155
+ }
156
+ }
157
+
158
+ if (records[affinity] != null && records[affinity].ips.length > 0) {
159
+ family = records[affinity]
160
+ } else {
161
+ family = records[affinity === 4 ? 6 : 4]
162
+ }
163
+ } else {
164
+ family = records[affinity]
165
+ }
166
+
167
+ // If no IPs we return null
168
+ if (family == null || family.ips.length === 0) {
169
+ return ip
170
+ }
171
+
172
+ if (family.offset == null || family.offset === maxInt) {
173
+ family.offset = 0
174
+ } else {
175
+ family.offset++
176
+ }
177
+
178
+ const position = family.offset % family.ips.length
179
+ ip = family.ips[position] ?? null
180
+
181
+ if (ip == null) {
182
+ return ip
183
+ }
184
+
185
+ if (Date.now() - ip.timestamp > ip.ttl) { // record TTL is already in ms
186
+ // We delete expired records
187
+ // It is possible that they have different TTL, so we manage them individually
188
+ family.ips.splice(position, 1)
189
+ return this.pick(origin, hostnameRecords, affinity)
190
+ }
191
+
192
+ return ip
193
+ }
194
+
195
+ setRecords (origin, addresses) {
196
+ const timestamp = Date.now()
197
+ const records = { records: { 4: null, 6: null } }
198
+ for (const record of addresses) {
199
+ record.timestamp = timestamp
200
+ if (typeof record.ttl === 'number') {
201
+ // The record TTL is expected to be in ms
202
+ record.ttl = Math.min(record.ttl, this.#maxTTL)
203
+ } else {
204
+ record.ttl = this.#maxTTL
205
+ }
206
+
207
+ const familyRecords = records.records[record.family] ?? { ips: [] }
208
+
209
+ familyRecords.ips.push(record)
210
+ records.records[record.family] = familyRecords
211
+ }
212
+
213
+ this.#records.set(origin.hostname, records)
214
+ }
215
+
216
+ getHandler (meta, opts) {
217
+ return new DNSDispatchHandler(this, meta, opts)
218
+ }
219
+ }
220
+
221
+ class DNSDispatchHandler extends DecoratorHandler {
222
+ #state = null
223
+ #opts = null
224
+ #dispatch = null
225
+ #handler = null
226
+ #origin = null
227
+
228
+ constructor (state, { origin, handler, dispatch }, opts) {
229
+ super(handler)
230
+ this.#origin = origin
231
+ this.#handler = handler
232
+ this.#opts = { ...opts }
233
+ this.#state = state
234
+ this.#dispatch = dispatch
235
+ }
236
+
237
+ onError (err) {
238
+ switch (err.code) {
239
+ case 'ETIMEDOUT':
240
+ case 'ECONNREFUSED': {
241
+ if (this.#state.dualStack) {
242
+ // We delete the record and retry
243
+ this.#state.runLookup(this.#origin, this.#opts, (err, newOrigin) => {
244
+ if (err) {
245
+ return this.#handler.onError(err)
246
+ }
247
+
248
+ const dispatchOpts = {
249
+ ...this.#opts,
250
+ origin: newOrigin
251
+ }
252
+
253
+ this.#dispatch(dispatchOpts, this)
254
+ })
255
+
256
+ // if dual-stack disabled, we error out
257
+ return
258
+ }
259
+
260
+ this.#handler.onError(err)
261
+ return
262
+ }
263
+ case 'ENOTFOUND':
264
+ this.#state.deleteRecord(this.#origin)
265
+ // eslint-disable-next-line no-fallthrough
266
+ default:
267
+ this.#handler.onError(err)
268
+ break
269
+ }
270
+ }
271
+ }
272
+
273
+ module.exports = interceptorOpts => {
274
+ if (
275
+ interceptorOpts?.maxTTL != null &&
276
+ (typeof interceptorOpts?.maxTTL !== 'number' || interceptorOpts?.maxTTL < 0)
277
+ ) {
278
+ throw new InvalidArgumentError('Invalid maxTTL. Must be a positive number')
279
+ }
280
+
281
+ if (
282
+ interceptorOpts?.maxItems != null &&
283
+ (typeof interceptorOpts?.maxItems !== 'number' ||
284
+ interceptorOpts?.maxItems < 1)
285
+ ) {
286
+ throw new InvalidArgumentError(
287
+ 'Invalid maxItems. Must be a positive number and greater than zero'
288
+ )
289
+ }
290
+
291
+ if (
292
+ interceptorOpts?.affinity != null &&
293
+ interceptorOpts?.affinity !== 4 &&
294
+ interceptorOpts?.affinity !== 6
295
+ ) {
296
+ throw new InvalidArgumentError('Invalid affinity. Must be either 4 or 6')
297
+ }
298
+
299
+ if (
300
+ interceptorOpts?.dualStack != null &&
301
+ typeof interceptorOpts?.dualStack !== 'boolean'
302
+ ) {
303
+ throw new InvalidArgumentError('Invalid dualStack. Must be a boolean')
304
+ }
305
+
306
+ if (
307
+ interceptorOpts?.lookup != null &&
308
+ typeof interceptorOpts?.lookup !== 'function'
309
+ ) {
310
+ throw new InvalidArgumentError('Invalid lookup. Must be a function')
311
+ }
312
+
313
+ if (
314
+ interceptorOpts?.pick != null &&
315
+ typeof interceptorOpts?.pick !== 'function'
316
+ ) {
317
+ throw new InvalidArgumentError('Invalid pick. Must be a function')
318
+ }
319
+
320
+ const dualStack = interceptorOpts?.dualStack ?? true
321
+ let affinity
322
+ if (dualStack) {
323
+ affinity = interceptorOpts?.affinity ?? null
324
+ } else {
325
+ affinity = interceptorOpts?.affinity ?? 4
326
+ }
327
+
328
+ const opts = {
329
+ maxTTL: interceptorOpts?.maxTTL ?? 10e3, // Expressed in ms
330
+ lookup: interceptorOpts?.lookup ?? null,
331
+ pick: interceptorOpts?.pick ?? null,
332
+ dualStack,
333
+ affinity,
334
+ maxItems: interceptorOpts?.maxItems ?? Infinity
335
+ }
336
+
337
+ const instance = new DNSInstance(opts)
338
+
339
+ return dispatch => {
340
+ return function dnsInterceptor (origDispatchOpts, handler) {
341
+ const origin =
342
+ origDispatchOpts.origin.constructor === URL
343
+ ? origDispatchOpts.origin
344
+ : new URL(origDispatchOpts.origin)
345
+
346
+ if (isIP(origin.hostname) !== 0) {
347
+ return dispatch(origDispatchOpts, handler)
348
+ }
349
+
350
+ instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => {
351
+ if (err) {
352
+ return handler.onError(err)
353
+ }
354
+
355
+ let dispatchOpts = null
356
+ dispatchOpts = {
357
+ ...origDispatchOpts,
358
+ servername: origin.hostname, // For SNI on TLS
359
+ origin: newOrigin,
360
+ headers: {
361
+ host: origin.hostname,
362
+ ...origDispatchOpts.headers
363
+ }
364
+ }
365
+
366
+ dispatch(
367
+ dispatchOpts,
368
+ instance.getHandler({ origin, dispatch, handler }, origDispatchOpts)
369
+ )
370
+ })
371
+
372
+ return true
373
+ }
374
+ }
375
+ }
@@ -37,6 +37,7 @@ class Cache {
37
37
  webidl.illegalConstructor()
38
38
  }
39
39
 
40
+ webidl.util.markAsUncloneable(this)
40
41
  this.#relevantRequestResponseList = arguments[1]
41
42
  }
42
43
 
@@ -16,6 +16,8 @@ class CacheStorage {
16
16
  if (arguments[0] !== kConstruct) {
17
17
  webidl.illegalConstructor()
18
18
  }
19
+
20
+ webidl.util.markAsUncloneable(this)
19
21
  }
20
22
 
21
23
  async match (request, options = {}) {
@@ -105,6 +105,8 @@ class EventSource extends EventTarget {
105
105
  // 1. Let ev be a new EventSource object.
106
106
  super()
107
107
 
108
+ webidl.util.markAsUncloneable(this)
109
+
108
110
  const prefix = 'EventSource constructor'
109
111
  webidl.argumentLengthCheck(arguments, 1, prefix)
110
112
 
@@ -20,6 +20,14 @@ const { isErrored, isDisturbed } = require('node:stream')
20
20
  const { isArrayBuffer } = require('node:util/types')
21
21
  const { serializeAMimeType } = require('./data-url')
22
22
  const { multipartFormDataParser } = require('./formdata-parser')
23
+ let random
24
+
25
+ try {
26
+ const crypto = require('node:crypto')
27
+ random = (max) => crypto.randomInt(0, max)
28
+ } catch {
29
+ random = (max) => Math.floor(Math.random(max))
30
+ }
23
31
 
24
32
  const textEncoder = new TextEncoder()
25
33
  function noop () {}
@@ -113,7 +121,7 @@ function extractBody (object, keepalive = false) {
113
121
  // Set source to a copy of the bytes held by object.
114
122
  source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength))
115
123
  } else if (util.isFormDataLike(object)) {
116
- const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}`
124
+ const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, '0')}`
117
125
  const prefix = `--${boundary}\r\nContent-Disposition: form-data`
118
126
 
119
127
  /*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
@@ -14,6 +14,8 @@ const File = globalThis.File ?? NativeFile
14
14
  // https://xhr.spec.whatwg.org/#formdata
15
15
  class FormData {
16
16
  constructor (form) {
17
+ webidl.util.markAsUncloneable(this)
18
+
17
19
  if (form !== undefined) {
18
20
  throw webidl.errors.conversionFailed({
19
21
  prefix: 'FormData constructor',
@@ -359,6 +359,8 @@ class Headers {
359
359
  #headersList
360
360
 
361
361
  constructor (init = undefined) {
362
+ webidl.util.markAsUncloneable(this)
363
+
362
364
  if (init === kConstruct) {
363
365
  return
364
366
  }
@@ -2137,7 +2137,7 @@ async function httpNetworkFetch (
2137
2137
 
2138
2138
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
2139
2139
  if (codings.length !== 0 && request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
2140
- for (let i = 0; i < codings.length; ++i) {
2140
+ for (let i = codings.length - 1; i >= 0; --i) {
2141
2141
  const coding = codings[i]
2142
2142
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
2143
2143
  if (coding === 'x-gzip' || coding === 'gzip') {
@@ -82,6 +82,7 @@ let patchMethodWarning = false
82
82
  class Request {
83
83
  // https://fetch.spec.whatwg.org/#dom-request
84
84
  constructor (input, init = {}) {
85
+ webidl.util.markAsUncloneable(this)
85
86
  if (input === kConstruct) {
86
87
  return
87
88
  }
@@ -110,6 +110,7 @@ class Response {
110
110
 
111
111
  // https://fetch.spec.whatwg.org/#dom-response
112
112
  constructor (body = null, init = {}) {
113
+ webidl.util.markAsUncloneable(this)
113
114
  if (body === kConstruct) {
114
115
  return
115
116
  }
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { types, inspect } = require('node:util')
4
+ const { markAsUncloneable } = require('node:worker_threads')
4
5
  const { toUSVString } = require('../../core/util')
5
6
 
6
7
  /** @type {import('../../../types/webidl').Webidl} */
@@ -86,6 +87,7 @@ webidl.util.Type = function (V) {
86
87
  }
87
88
  }
88
89
 
90
+ webidl.util.markAsUncloneable = markAsUncloneable || (() => {})
89
91
  // https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
90
92
  webidl.util.ConvertToInt = function (V, bitLength, signedness, opts) {
91
93
  let upperBound
@@ -14,6 +14,7 @@ class MessageEvent extends Event {
14
14
  constructor (type, eventInitDict = {}) {
15
15
  if (type === kConstruct) {
16
16
  super(arguments[1], arguments[2])
17
+ webidl.util.markAsUncloneable(this)
17
18
  return
18
19
  }
19
20
 
@@ -26,6 +27,7 @@ class MessageEvent extends Event {
26
27
  super(type, eventInitDict)
27
28
 
28
29
  this.#eventInit = eventInitDict
30
+ webidl.util.markAsUncloneable(this)
29
31
  }
30
32
 
31
33
  get data () {
@@ -112,6 +114,7 @@ class CloseEvent extends Event {
112
114
  super(type, eventInitDict)
113
115
 
114
116
  this.#eventInit = eventInitDict
117
+ webidl.util.markAsUncloneable(this)
115
118
  }
116
119
 
117
120
  get wasClean () {
@@ -142,6 +145,7 @@ class ErrorEvent extends Event {
142
145
  webidl.argumentLengthCheck(arguments, 1, prefix)
143
146
 
144
147
  super(type, eventInitDict)
148
+ webidl.util.markAsUncloneable(this)
145
149
 
146
150
  type = webidl.converters.DOMString(type, prefix, 'type')
147
151
  eventInitDict = webidl.converters.ErrorEventInit(eventInitDict ?? {})
@@ -51,6 +51,8 @@ class WebSocket extends EventTarget {
51
51
  constructor (url, protocols = []) {
52
52
  super()
53
53
 
54
+ webidl.util.markAsUncloneable(this)
55
+
54
56
  const prefix = 'WebSocket constructor'
55
57
  webidl.argumentLengthCheck(arguments, 1, prefix)
56
58
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "6.20.1",
3
+ "version": "6.21.1",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
@@ -1,3 +1,5 @@
1
+ import { LookupOptions } from 'node:dns'
2
+
1
3
  import Dispatcher from "./dispatcher";
2
4
  import RetryHandler from "./retry-handler";
3
5
 
@@ -9,6 +11,18 @@ declare namespace Interceptors {
9
11
  export type RedirectInterceptorOpts = { maxRedirections?: number }
10
12
  export type ResponseErrorInterceptorOpts = { throwOnError: boolean }
11
13
 
14
+ // DNS interceptor
15
+ export type DNSInterceptorRecord = { address: string, ttl: number, family: 4 | 6 }
16
+ export type DNSInterceptorOriginRecords = { 4: { ips: DNSInterceptorRecord[] } | null, 6: { ips: DNSInterceptorRecord[] } | null }
17
+ export type DNSInterceptorOpts = {
18
+ maxTTL?: number
19
+ maxItems?: number
20
+ lookup?: (hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void
21
+ pick?: (origin: URL, records: DNSInterceptorOriginRecords, affinity: 4 | 6) => DNSInterceptorRecord
22
+ dualStack?: boolean
23
+ affinity?: 4 | 6
24
+ }
25
+
12
26
  export function createRedirectInterceptor(opts: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
13
27
  export function dump(opts?: DumpInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
14
28
  export function retry(opts?: RetryInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
@@ -32,7 +32,7 @@ declare namespace RetryHandler {
32
32
  };
33
33
  },
34
34
  callback: OnRetryCallback
35
- ) => number | null;
35
+ ) => void
36
36
 
37
37
  export interface RetryOptions {
38
38
  /**
@@ -67,6 +67,12 @@ interface WebidlUtil {
67
67
  * Stringifies {@param V}
68
68
  */
69
69
  Stringify (V: any): string
70
+
71
+ /**
72
+ * Mark a value as uncloneable for Node.js.
73
+ * This is only effective in some newer Node.js versions.
74
+ */
75
+ markAsUncloneable (V: any): void
70
76
  }
71
77
 
72
78
  interface WebidlConverters {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@florianpat/lando-core",
3
3
  "description": "The libraries that power all of Lando. Fork by flo for compose integration",
4
- "version": "3.23.22",
4
+ "version": "3.23.27-1florianPat.0",
5
5
  "author": "Florian Patruck @florianPat",
6
6
  "license": "GPL-3.0",
7
7
  "repository": "florianPat/lando-core",
@@ -168,7 +168,7 @@
168
168
  "devDependencies": {
169
169
  "@babel/eslint-parser": "^7.16.0",
170
170
  "@lando/leia": "^1.0.0-beta.4",
171
- "@lando/vitepress-theme-default-plus": "github:lando/vitepress-theme-default-plus#main",
171
+ "@lando/vitepress-theme-default-plus": "^1.1.1",
172
172
  "@yao-pkg/pkg": "^5.16.1",
173
173
  "chai": "^4.3.4",
174
174
  "chai-as-promised": "^7.1.1",
@@ -247,9 +247,9 @@
247
247
  "yargs-parser"
248
248
  ],
249
249
  "dist": {
250
- "integrity": "sha512-7Ak4ffLOApUfus6mu06+SGYIHKtpDFx45ACv5gQhuNtY8JgE/xyPL5EwCabkQXkZhwHKkN8nQXRUdFk9d7Tq/w==",
251
- "shasum": "1f59804c71e8e5ddad072d8fa102ad50d6f3d956",
252
- "filename": "florianpat-lando-core-3.23.22.tgz",
253
- "unpackedSize": 60948228
250
+ "integrity": "sha512-EF8oq87DSgrav9p/64DkDyDuudJ/hW3soeFWiiuZzhjIIZ52UV98g6DGiIjVVyowcVtuAL6sAf/cidZkv7s+tQ==",
251
+ "shasum": "7fa58d190ddcacde2bb53ede9fffc5d92c0bb42c",
252
+ "filename": "florianpat-lando-core-3.23.27-1florianPat.0.tgz",
253
+ "unpackedSize": 60965868
254
254
  }
255
255
  }
@@ -1 +1 @@
1
- v3.24.0-beta.8
1
+ v3.24.0-beta.12
@@ -1 +1 @@
1
- v3.23.22
1
+ v3.23.26
@@ -59,7 +59,7 @@ debug "INSTALLER: $INSTALLER"
59
59
  debug "USER: $USER"
60
60
 
61
61
  # add accept license if set
62
- if [ "${DEBUG}" == 1 ]; then OPTS="$OPTS --accept-license"; fi
62
+ if [ "${ACCEPT_LICENSE}" == 1 ]; then OPTS="$OPTS --accept-license"; fi
63
63
 
64
64
  # run
65
65
  hdiutil attach "$INSTALLER"
@@ -3,7 +3,7 @@ set -eo pipefail
3
3
 
4
4
  DEBUG=0
5
5
  INSTALLER="get-docker.sh"
6
- VERSION="27.3.1"
6
+ VERSION="27.5.0"
7
7
  OPTS=
8
8
 
9
9
  debug() {
@@ -62,7 +62,7 @@ if [ -d "/scripts" ] && [ -z ${LANDO_NO_SCRIPTS+x} ]; then
62
62
 
63
63
  # Keep this for backwards compat and fallback opts
64
64
  chmod +x /scripts/* || true
65
- find /scripts/ -type f -name "*.sh" -exec {} \;
65
+ find /scripts/ -type f \( -name "*.sh" -o ! -name "*.*" \) -exec {} \;
66
66
  fi;
67
67
 
68
68
  # Run any bash scripts that we've loaded into the mix for autorun unless we've
@@ -27,7 +27,7 @@ const getDockerBin = (bin, base, pathFallback = true) => {
27
27
  }
28
28
  };
29
29
 
30
- module.exports = ({orchestratorVersion = '2.30.3', userConfRoot = os.tmpdir()} = {}) => {
30
+ module.exports = ({orchestratorVersion = '2.31.0', userConfRoot = os.tmpdir()} = {}) => {
31
31
  const orchestratorBin = `docker-compose-v${orchestratorVersion}`;
32
32
  switch (process.platform) {
33
33
  case 'darwin':
@@ -8,20 +8,20 @@ const os = require('os');
8
8
  const getBuildEngineVersion = (platform = process.landoPlatform ?? process.platform) => {
9
9
  switch (platform) {
10
10
  case 'darwin':
11
- return '4.36.0';
11
+ return '4.37.2';
12
12
  case 'linux':
13
- return '27.3.1';
13
+ return '27.5.0';
14
14
  case 'win32':
15
- return '4.36.0';
15
+ return '4.37.1';
16
16
  case 'wsl':
17
- return '4.36.0';
17
+ return '4.37.1';
18
18
  }
19
19
  };
20
20
 
21
21
  // Default config
22
22
  const defaultConfig = options => ({
23
23
  orchestratorSeparator: '_',
24
- orchestratorVersion: '2.30.3',
24
+ orchestratorVersion: '2.31.0',
25
25
  configSources: [],
26
26
  coreBase: path.resolve(__dirname, '..'),
27
27
  disablePlugins: [],
@@ -1,22 +1,36 @@
1
1
  'use strict';
2
2
 
3
- module.exports = ({Labels, Id, Status}, separator = '_') => {
3
+ const path = require('path');
4
+
5
+ module.exports = ({Labels, Id, Status}, separator = '_', src = []) => {
4
6
  // Get name of docker container.
5
7
  const app = Labels['com.docker.compose.project'];
6
8
  const service = Labels['com.docker.compose.service'];
7
9
  const num = Labels['com.docker.compose.container-number'];
8
10
  const lando = Labels['io.lando.container'];
9
11
  const special = Labels['io.lando.service-container'];
12
+
13
+ // if we have io.lando.root and io.lando.
14
+ if (Labels['io.lando.root'] && Labels['io.lando.landofiles']) {
15
+ src = Labels['io.lando.landofiles'].split(',').map(landofile => path.join(Labels['io.lando.root'], landofile));
16
+
17
+ // or legacy support for Labels['io.lando.src']
18
+ } else if (Labels['io.lando.src']) {
19
+ src = Labels['io.lando.src'].split(',');
20
+
21
+ // or its just unknown
22
+ } else src = 'unknown';
23
+
10
24
  // Build generic container.
11
25
  return {
12
26
  id: Id,
13
27
  service: service,
14
28
  name: [app, service, num].join(separator),
15
29
  app: (special !== 'TRUE') ? app : '_global_',
16
- src: (Labels['io.lando.src']) ? Labels['io.lando.src'].split(',') : 'unknown',
17
30
  kind: (special !== 'TRUE') ? 'app' : 'service',
18
31
  lando: (lando === 'TRUE') ? true : false,
19
32
  instance: Labels['io.lando.id'] || 'unknown',
20
33
  status: Status,
34
+ src,
21
35
  };
22
36
  };
package/checksums.txt DELETED
File without changes