Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/src/language_version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class LanguageVersion implements Comparable<LanguageVersion> {

bool get supportsWorkspaces => this >= firstVersionWithWorkspaces;

bool get supportsWorkspaceGlobs => this >= firstVersionWithWorkspaceGlobs;

bool get supportsTagPattern => this >= firstVersionWithTagPattern;

bool get forbidsUnknownDescriptionKeys =>
Expand Down Expand Up @@ -110,6 +112,7 @@ class LanguageVersion implements Comparable<LanguageVersion> {
static const firstVersionWithNullSafety = LanguageVersion(2, 12);
static const firstVersionWithShorterHostedSyntax = LanguageVersion(2, 15);
static const firstVersionWithWorkspaces = LanguageVersion(3, 5);
static const firstVersionWithWorkspaceGlobs = LanguageVersion(3, 11);
static const firstVersionWithTagPattern = LanguageVersion(3, 9);
static const firstVersionForbidingUnknownDescriptionKeys = LanguageVersion(
3,
Expand Down
50 changes: 39 additions & 11 deletions lib/src/package.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

import 'dart:io';

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';

import 'exceptions.dart';
import 'git.dart' as git;
import 'ignore.dart';
import 'io.dart';
import 'language_version.dart';
import 'log.dart' as log;
import 'package_name.dart';
import 'pubspec.dart';
Expand Down Expand Up @@ -174,21 +177,44 @@ class Package {
);

final workspacePackages =
pubspec.workspace.map((workspacePath) {
pubspec.workspace.expand((workspacePath) {
final Glob glob;
try {
return Package.load(
p.join(dir, workspacePath),
loadPubspec: loadPubspec,
withPubspecOverrides: withPubspecOverrides,
glob = Glob(
pubspec.languageVersion.supportsWorkspaceGlobs
? workspacePath
: Glob.quote(workspacePath),
);
} on FileException catch (e) {
final pubspecPath = p.join(dir, 'pubspec.yaml');
throw FileException(
'${e.message}\n'
'That was included in the workspace of $pubspecPath.',
e.path,
} on FormatException catch (e) {
fail('Failed to parse glob `$workspacePath`. $e');
}
final packages = <Package>[];
for (final globResult in glob.listSync(root: dir)) {
final pubspecPath = p.join(globResult.path, 'pubspec.yaml');
if (!fileExists(pubspecPath)) continue;
packages.add(
Package.load(
globResult.path,
loadPubspec: loadPubspec,
withPubspecOverrides: withPubspecOverrides,
),
);
}
if (packages.isEmpty) {
final globHint =
!pubspec.languageVersion.supportsWorkspaceGlobs &&
_looksLikeGlob(workspacePath)
? '''
\n\nGlob syntax is only supported from language version ${LanguageVersion.firstVersionWithWorkspaceGlobs}.
Consider changing the language version of ${p.join(dir, 'pubspec.yaml')} to ${LanguageVersion.firstVersionWithWorkspaceGlobs}.
'''
: '';
fail('''
No workspace packages matching `$workspacePath`.
That was included in the workspace of `${p.join(dir, 'pubspec.yaml')}`.$globHint
''');
}
return packages;
}).toList();
for (final package in workspacePackages) {
if (package.pubspec.resolution != Resolution.workspace) {
Expand Down Expand Up @@ -546,3 +572,5 @@ See https://dart.dev/go/workspaces-stray-files for details.
}
}
}

bool _looksLikeGlob(String s) => Glob.quote(s) != s;
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ packages:
source: hosted
version: "4.0.0"
glob:
dependency: transitive
dependency: "direct main"
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies:
convert: ^3.1.2
crypto: ^3.0.6
frontend_server_client: ^4.0.0
glob: ^2.1.3
graphs: ^2.3.2
http: ^1.5.0
http_multi_server: ^3.2.2
Expand Down
141 changes: 117 additions & 24 deletions test/workspace_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,14 @@ void main() {
),
]),
]).create();
final appABPath = p.join(sandbox, appPath, 'a', 'b');
final aPubspecPath = p.join('.', 'a', 'pubspec.yaml');
final pubspecPath = p.join('.', 'pubspec.yaml');
await pubGet(
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
error: contains(
'Could not find a file named "pubspec.yaml" in "$appABPath".\n'
'That was included in the workspace of $aPubspecPath.\n'
'That was included in the workspace of $pubspecPath.',
'No workspace packages matching `b`.\n'
'That was included in the workspace of `$aPubspecPath`',
),
exitCode: NO_INPUT,
exitCode: 1,
);
});

Expand Down Expand Up @@ -866,11 +863,11 @@ foo:foomain''',
await pubGet(
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
warning: allOf(
contains('Deleting old lock-file: `.${s}pkgs/a${s}pubspec.lock'),
isNot(contains('.${s}pkgs/b${s}pubspec.lock')),
contains('Deleting old lock-file: `.${s}pkgs${s}a${s}pubspec.lock'),
isNot(contains('.${s}pkgs${s}b${s}pubspec.lock')),
contains(
'Deleting old package config: '
'`.${s}pkgs/a$s.dart_tool${s}package_config.json`',
'`.${s}pkgs${s}a$s.dart_tool${s}package_config.json`',
),
contains('Deleting old lock-file: `.${s}pkgs${s}pubspec.lock'),
contains(
Expand Down Expand Up @@ -954,7 +951,7 @@ Packages can only be included in the workspace once.
error: '''
Packages can only be included in the workspace once.

`.${s}pkgs/a/pubspec.yaml` is included twice into the workspace of `.${s}pubspec.yaml`''',
`.${s}pkgs${s}a${s}pubspec.yaml` is included twice into the workspace of `.${s}pubspec.yaml`''',
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
);
});
Expand Down Expand Up @@ -1596,7 +1593,7 @@ Consider removing one of the overrides.''',
error: allOf(
contains('Cannot override workspace packages'),
contains(
'Package `a` at `.${s}pkgs/a` is overridden in `pubspec.yaml`.',
'Package `a` at `.${s}pkgs${s}a` is overridden in `pubspec.yaml`.',
),
),
);
Expand Down Expand Up @@ -1765,41 +1762,45 @@ b a${s}b$s
args: ['get', '--example'],
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
output: allOf(
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.`.')),
),
);

await runPub(
args: ['upgrade', '--example'],
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
output: allOf(
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.`.')),
),
);

await runPub(
args: ['upgrade', '--example', '--tighten'],
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
output: allOf(
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
),
error: contains(
'Running `upgrade --tighten` only in `.`. Run `dart pub upgrade --tighten --directory .${s}pkgs/a${s}example` separately.',
'Running `upgrade --tighten` only in `.`. '
'Run `dart pub upgrade --tighten '
'--directory .${s}pkgs${s}a${s}example` separately.',
),
);

await runPub(
args: ['upgrade', '--example', '--major-versions'],
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
output: allOf(
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.')),
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.')),
),
error: contains(
'Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory .${s}pkgs/a${s}example` separately.',
'Running `upgrade --major-versions` only in `.`. '
'Run `dart pub upgrade --major-versions '
'--directory .${s}pkgs${s}a${s}example` separately.',
),
);

Expand All @@ -1808,8 +1809,8 @@ b a${s}b$s
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
output: allOf(
contains('+ foo 1.5.0'),
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.')),
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.')),
),
);

Expand All @@ -1818,11 +1819,103 @@ b a${s}b$s
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
output: allOf(
contains('< foo 1.0.0'),
contains('Got dependencies in `.${s}pkgs/a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')),
contains('Got dependencies in `.${s}pkgs${s}a${s}example`.'),
isNot(contains('Got dependencies in `.${s}pkgs${s}b${s}example`.`.')),
),
);
});

test('bad globs are handled gracefully', () async {
await dir(appPath, [
libPubspec(
'myapp',
'1.2.3',
extras: {
'workspace': ['pkgs/{*'],
},
sdk: '^3.5.0',
),
]).create();
await pubGet(
environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'},
error: contains('''
No workspace packages matching `pkgs/{*`.
That was included in the workspace of `.${s}pubspec.yaml`.

Glob syntax is only supported from language version 3.11.
Consider changing the language version of .${s}pubspec.yaml to 3.11.'''),
);
await dir(appPath, [
libPubspec(
'myapp',
'1.2.3',
extras: {
'workspace': ['pkgs/{*'],
},
sdk: '^3.11.0',
),
]).create();
await pubGet(
environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'},
error: contains(
'Failed to parse glob `pkgs/{*`. '
'Error on line 1, column 8: expected ",".',
),
);
});

test(
'globs are not resolved in older language versions',
() async {
await dir(appPath, [
libPubspec(
'myapp',
'1.2.3',
extras: {
'workspace': ['pkgs/*'],
},
sdk: '^3.6.0',
),
dir('pkgs', [
dir('*', [libPubspec('a', '1.1.1', resolutionWorkspace: true)]),
dir('b', [libPubspec('b', '1.1.1', resolutionWorkspace: true)]),
]),
]).create();
await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'});
await dir(appPath, [
packageConfigFile([
packageConfigEntry(name: 'myapp', path: '.'),
packageConfigEntry(name: 'a', path: 'pkgs/*'),
], generatorVersion: '3.11.0'),
]).validate();
},
skip: Platform.isWindows, // Cannot create directory named "*" on Windows.
);

test('globs are resolved with newer language versions', () async {
await dir(appPath, [
libPubspec(
'myapp',
'1.2.3',
extras: {
'workspace': ['pkgs/*'],
},
sdk: '^3.11.0',
),
dir('pkgs', [
dir('a', [libPubspec('a', '1.1.1', resolutionWorkspace: true)]),
dir('b', [libPubspec('b', '1.1.1', resolutionWorkspace: true)]),
]),
]).create();
await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '3.11.0'});
await dir(appPath, [
packageConfigFile([
packageConfigEntry(name: 'myapp', path: '.'),
packageConfigEntry(name: 'a', path: 'pkgs/a'),
packageConfigEntry(name: 'b', path: 'pkgs/b'),
], generatorVersion: '3.11.0'),
]).validate();
});
}

final s = p.separator;