Skip to content

Commit 4e68fd9

Browse files
authored
Workspaces glob support (#4680)
1 parent acf54b8 commit 4e68fd9

File tree

5 files changed

+161
-36
lines changed

5 files changed

+161
-36
lines changed

lib/src/language_version.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class LanguageVersion implements Comparable<LanguageVersion> {
6565

6666
bool get supportsWorkspaces => this >= firstVersionWithWorkspaces;
6767

68+
bool get supportsWorkspaceGlobs => this >= firstVersionWithWorkspaceGlobs;
69+
6870
bool get supportsTagPattern => this >= firstVersionWithTagPattern;
6971

7072
bool get forbidsUnknownDescriptionKeys =>
@@ -110,6 +112,7 @@ class LanguageVersion implements Comparable<LanguageVersion> {
110112
static const firstVersionWithNullSafety = LanguageVersion(2, 12);
111113
static const firstVersionWithShorterHostedSyntax = LanguageVersion(2, 15);
112114
static const firstVersionWithWorkspaces = LanguageVersion(3, 5);
115+
static const firstVersionWithWorkspaceGlobs = LanguageVersion(3, 11);
113116
static const firstVersionWithTagPattern = LanguageVersion(3, 9);
114117
static const firstVersionForbidingUnknownDescriptionKeys = LanguageVersion(
115118
3,

lib/src/package.dart

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
import 'dart:io';
66

7+
import 'package:glob/glob.dart';
8+
import 'package:glob/list_local_fs.dart';
79
import 'package:path/path.dart' as p;
810
import 'package:pub_semver/pub_semver.dart';
911

1012
import 'exceptions.dart';
1113
import 'git.dart' as git;
1214
import 'ignore.dart';
1315
import 'io.dart';
16+
import 'language_version.dart';
1417
import 'log.dart' as log;
1518
import 'package_name.dart';
1619
import 'pubspec.dart';
@@ -174,21 +177,44 @@ class Package {
174177
);
175178

176179
final workspacePackages =
177-
pubspec.workspace.map((workspacePath) {
180+
pubspec.workspace.expand((workspacePath) {
181+
final Glob glob;
178182
try {
179-
return Package.load(
180-
p.join(dir, workspacePath),
181-
loadPubspec: loadPubspec,
182-
withPubspecOverrides: withPubspecOverrides,
183+
glob = Glob(
184+
pubspec.languageVersion.supportsWorkspaceGlobs
185+
? workspacePath
186+
: Glob.quote(workspacePath),
183187
);
184-
} on FileException catch (e) {
185-
final pubspecPath = p.join(dir, 'pubspec.yaml');
186-
throw FileException(
187-
'${e.message}\n'
188-
'That was included in the workspace of $pubspecPath.',
189-
e.path,
188+
} on FormatException catch (e) {
189+
fail('Failed to parse glob `$workspacePath`. $e');
190+
}
191+
final packages = <Package>[];
192+
for (final globResult in glob.listSync(root: dir)) {
193+
final pubspecPath = p.join(globResult.path, 'pubspec.yaml');
194+
if (!fileExists(pubspecPath)) continue;
195+
packages.add(
196+
Package.load(
197+
globResult.path,
198+
loadPubspec: loadPubspec,
199+
withPubspecOverrides: withPubspecOverrides,
200+
),
190201
);
191202
}
203+
if (packages.isEmpty) {
204+
final globHint =
205+
!pubspec.languageVersion.supportsWorkspaceGlobs &&
206+
_looksLikeGlob(workspacePath)
207+
? '''
208+
\n\nGlob syntax is only supported from language version ${LanguageVersion.firstVersionWithWorkspaceGlobs}.
209+
Consider changing the language version of ${p.join(dir, 'pubspec.yaml')} to ${LanguageVersion.firstVersionWithWorkspaceGlobs}.
210+
'''
211+
: '';
212+
fail('''
213+
No workspace packages matching `$workspacePath`.
214+
That was included in the workspace of `${p.join(dir, 'pubspec.yaml')}`.$globHint
215+
''');
216+
}
217+
return packages;
192218
}).toList();
193219
for (final package in workspacePackages) {
194220
if (package.pubspec.resolution != Resolution.workspace) {
@@ -546,3 +572,5 @@ See https://dart.dev/go/workspaces-stray-files for details.
546572
}
547573
}
548574
}
575+
576+
bool _looksLikeGlob(String s) => Glob.quote(s) != s;

pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ packages:
122122
source: hosted
123123
version: "4.0.0"
124124
glob:
125-
dependency: transitive
125+
dependency: "direct main"
126126
description:
127127
name: glob
128128
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies:
1212
convert: ^3.1.2
1313
crypto: ^3.0.6
1414
frontend_server_client: ^4.0.0
15+
glob: ^2.1.3
1516
graphs: ^2.3.2
1617
http: ^1.5.0
1718
http_multi_server: ^3.2.2

test/workspace_test.dart

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -392,17 +392,14 @@ void main() {
392392
),
393393
]),
394394
]).create();
395-
final appABPath = p.join(sandbox, appPath, 'a', 'b');
396395
final aPubspecPath = p.join('.', 'a', 'pubspec.yaml');
397-
final pubspecPath = p.join('.', 'pubspec.yaml');
398396
await pubGet(
399397
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
400398
error: contains(
401-
'Could not find a file named "pubspec.yaml" in "$appABPath".\n'
402-
'That was included in the workspace of $aPubspecPath.\n'
403-
'That was included in the workspace of $pubspecPath.',
399+
'No workspace packages matching `b`.\n'
400+
'That was included in the workspace of `$aPubspecPath`',
404401
),
405-
exitCode: NO_INPUT,
402+
exitCode: 1,
406403
);
407404
});
408405

@@ -866,11 +863,11 @@ foo:foomain''',
866863
await pubGet(
867864
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
868865
warning: allOf(
869-
contains('Deleting old lock-file: `.${s}pkgs/a${s}pubspec.lock'),
870-
isNot(contains('.${s}pkgs/b${s}pubspec.lock')),
866+
contains('Deleting old lock-file: `.${s}pkgs${s}a${s}pubspec.lock'),
867+
isNot(contains('.${s}pkgs${s}b${s}pubspec.lock')),
871868
contains(
872869
'Deleting old package config: '
873-
'`.${s}pkgs/a$s.dart_tool${s}package_config.json`',
870+
'`.${s}pkgs${s}a$s.dart_tool${s}package_config.json`',
874871
),
875872
contains('Deleting old lock-file: `.${s}pkgs${s}pubspec.lock'),
876873
contains(
@@ -954,7 +951,7 @@ Packages can only be included in the workspace once.
954951
error: '''
955952
Packages can only be included in the workspace once.
956953
957-
`.${s}pkgs/a/pubspec.yaml` is included twice into the workspace of `.${s}pubspec.yaml`''',
954+
`.${s}pkgs${s}a${s}pubspec.yaml` is included twice into the workspace of `.${s}pubspec.yaml`''',
958955
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
959956
);
960957
});
@@ -1596,7 +1593,7 @@ Consider removing one of the overrides.''',
15961593
error: allOf(
15971594
contains('Cannot override workspace packages'),
15981595
contains(
1599-
'Package `a` at `.${s}pkgs/a` is overridden in `pubspec.yaml`.',
1596+
'Package `a` at `.${s}pkgs${s}a` is overridden in `pubspec.yaml`.',
16001597
),
16011598
),
16021599
);
@@ -1765,41 +1762,45 @@ b a${s}b$s
17651762
args: ['get', '--example'],
17661763
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
17671764
output: allOf(
1768-
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
1769-
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
1765+
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
1766+
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.`.')),
17701767
),
17711768
);
17721769

17731770
await runPub(
17741771
args: ['upgrade', '--example'],
17751772
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
17761773
output: allOf(
1777-
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
1778-
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
1774+
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
1775+
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.`.')),
17791776
),
17801777
);
17811778

17821779
await runPub(
17831780
args: ['upgrade', '--example', '--tighten'],
17841781
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
17851782
output: allOf(
1786-
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
1783+
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
17871784
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
17881785
),
17891786
error: contains(
1790-
'Running `upgrade --tighten` only in `.`. Run `dart pub upgrade --tighten --directory .${s}pkgs/a${s}example` separately.',
1787+
'Running `upgrade --tighten` only in `.`. '
1788+
'Run `dart pub upgrade --tighten '
1789+
'--directory .${s}pkgs${s}a${s}example` separately.',
17911790
),
17921791
);
17931792

17941793
await runPub(
17951794
args: ['upgrade', '--example', '--major-versions'],
17961795
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
17971796
output: allOf(
1798-
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
1799-
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.')),
1797+
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
1798+
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.')),
18001799
),
18011800
error: contains(
1802-
'Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory .${s}pkgs/a${s}example` separately.',
1801+
'Running `upgrade --major-versions` only in `.`. '
1802+
'Run `dart pub upgrade --major-versions '
1803+
'--directory .${s}pkgs${s}a${s}example` separately.',
18031804
),
18041805
);
18051806

@@ -1808,8 +1809,8 @@ b a${s}b$s
18081809
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
18091810
output: allOf(
18101811
contains('+ foo 1.5.0'),
1811-
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
1812-
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.')),
1812+
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
1813+
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.')),
18131814
),
18141815
);
18151816

@@ -1818,11 +1819,103 @@ b a${s}b$s
18181819
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
18191820
output: allOf(
18201821
contains('< foo 1.0.0'),
1821-
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
1822-
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
1822+
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
1823+
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.`.')),
18231824
),
18241825
);
18251826
});
1827+
1828+
test('bad globs are handled gracefully', () async {
1829+
await dir(appPath, [
1830+
libPubspec(
1831+
'myapp',
1832+
'1.2.3',
1833+
extras: {
1834+
'workspace': ['pkgs/{*'],
1835+
},
1836+
sdk: '^3.5.0',
1837+
),
1838+
]).create();
1839+
await pubGet(
1840+
environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'},
1841+
error: contains('''
1842+
No workspace packages matching `pkgs/{*`.
1843+
That was included in the workspace of `.${s}pubspec.yaml`.
1844+
1845+
Glob syntax is only supported from language version 3.11.
1846+
Consider changing the language version of .${s}pubspec.yaml to 3.11.'''),
1847+
);
1848+
await dir(appPath, [
1849+
libPubspec(
1850+
'myapp',
1851+
'1.2.3',
1852+
extras: {
1853+
'workspace': ['pkgs/{*'],
1854+
},
1855+
sdk: '^3.11.0',
1856+
),
1857+
]).create();
1858+
await pubGet(
1859+
environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'},
1860+
error: contains(
1861+
'Failed to parse glob `pkgs/{*`. '
1862+
'Error on line 1, column 8: expected ",".',
1863+
),
1864+
);
1865+
});
1866+
1867+
test(
1868+
'globs are not resolved in older language versions',
1869+
() async {
1870+
await dir(appPath, [
1871+
libPubspec(
1872+
'myapp',
1873+
'1.2.3',
1874+
extras: {
1875+
'workspace': ['pkgs/*'],
1876+
},
1877+
sdk: '^3.6.0',
1878+
),
1879+
dir('pkgs', [
1880+
dir('*', [libPubspec('a', '1.1.1', resolutionWorkspace: true)]),
1881+
dir('b', [libPubspec('b', '1.1.1', resolutionWorkspace: true)]),
1882+
]),
1883+
]).create();
1884+
await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'});
1885+
await dir(appPath, [
1886+
packageConfigFile([
1887+
packageConfigEntry(name: 'myapp', path: '.'),
1888+
packageConfigEntry(name: 'a', path: 'pkgs/*'),
1889+
], generatorVersion: '3.11.0'),
1890+
]).validate();
1891+
},
1892+
skip: Platform.isWindows, // Cannot create directory named "*" on Windows.
1893+
);
1894+
1895+
test('globs are resolved with newer language versions', () async {
1896+
await dir(appPath, [
1897+
libPubspec(
1898+
'myapp',
1899+
'1.2.3',
1900+
extras: {
1901+
'workspace': ['pkgs/*'],
1902+
},
1903+
sdk: '^3.11.0',
1904+
),
1905+
dir('pkgs', [
1906+
dir('a', [libPubspec('a', '1.1.1', resolutionWorkspace: true)]),
1907+
dir('b', [libPubspec('b', '1.1.1', resolutionWorkspace: true)]),
1908+
]),
1909+
]).create();
1910+
await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'});
1911+
await dir(appPath, [
1912+
packageConfigFile([
1913+
packageConfigEntry(name: 'myapp', path: '.'),
1914+
packageConfigEntry(name: 'a', path: 'pkgs/a'),
1915+
packageConfigEntry(name: 'b', path: 'pkgs/b'),
1916+
], generatorVersion: '3.11.0'),
1917+
]).validate();
1918+
});
18261919
}
18271920

18281921
final s = p.separator;

0 commit comments

Comments
 (0)