mirror of
https://github.com/flutter/packages.git
synced 2025-06-23 16:40:00 +08:00
[tool] Add initial update-dependency
command (#3632)
[tool] Add initial `update-dependency` command
This commit is contained in:
@ -135,6 +135,26 @@ the branch point from `upstream/main`. For more complex use cases where you want
|
||||
a different diff point, you can pass a different `--base-branch`, or use
|
||||
`--base-sha` to pick the exact diff point.
|
||||
|
||||
### Update a dependency
|
||||
|
||||
`update-dependency` will update a pub dependency to a new version.
|
||||
|
||||
For instance, to updated to version 3.0.0 of `some_package` in every package
|
||||
that depends on it:
|
||||
|
||||
```sh
|
||||
cd <repository root>
|
||||
dart run script/tool/bin/flutter_plugin_tools.dart update-dependency \
|
||||
--pub-package=some_package \
|
||||
--version=3.0.0 \
|
||||
```
|
||||
|
||||
If a `--version` is not provided, the latest version from pub will be used.
|
||||
|
||||
Currently this only updates the dependency itself in pubspec.yaml, but in the
|
||||
future this will also update any generated code for packages that use code
|
||||
generation (e.g., regenerating mocks when updating `mockito`).
|
||||
|
||||
### Publish a Release
|
||||
|
||||
**Releases are automated for `flutter/packages`.**
|
||||
|
@ -31,6 +31,7 @@ import 'pubspec_check_command.dart';
|
||||
import 'readme_check_command.dart';
|
||||
import 'remove_dev_dependencies_command.dart';
|
||||
import 'test_command.dart';
|
||||
import 'update_dependency_command.dart';
|
||||
import 'update_excerpts_command.dart';
|
||||
import 'update_min_sdk_command.dart';
|
||||
import 'update_release_info_command.dart';
|
||||
@ -77,6 +78,7 @@ void main(List<String> args) {
|
||||
..addCommand(ReadmeCheckCommand(packagesDir))
|
||||
..addCommand(RemoveDevDependenciesCommand(packagesDir))
|
||||
..addCommand(TestCommand(packagesDir))
|
||||
..addCommand(UpdateDependencyCommand(packagesDir))
|
||||
..addCommand(UpdateExcerptsCommand(packagesDir))
|
||||
..addCommand(UpdateMinSdkCommand(packagesDir))
|
||||
..addCommand(UpdateReleaseInfoCommand(packagesDir))
|
||||
|
207
script/tool/lib/src/update_dependency_command.dart
Normal file
207
script/tool/lib/src/update_dependency_command.dart
Normal file
@ -0,0 +1,207 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:pubspec_parse/pubspec_parse.dart';
|
||||
import 'package:yaml_edit/yaml_edit.dart';
|
||||
|
||||
import 'common/core.dart';
|
||||
import 'common/package_looping_command.dart';
|
||||
import 'common/pub_version_finder.dart';
|
||||
import 'common/repository_package.dart';
|
||||
|
||||
const int _exitIncorrectTargetDependency = 3;
|
||||
const int _exitNoTargetVersion = 4;
|
||||
|
||||
/// A command to update a dependency in packages.
|
||||
///
|
||||
/// This is intended to expand over time to support any sort of dependency that
|
||||
/// packages use, including pub packages and native dependencies, and should
|
||||
/// include any tasks related to the dependency (e.g., regenerating files when
|
||||
/// updating a dependency that is responsible for code generation).
|
||||
class UpdateDependencyCommand extends PackageLoopingCommand {
|
||||
/// Creates an instance of the version check command.
|
||||
UpdateDependencyCommand(
|
||||
Directory packagesDir, {
|
||||
http.Client? httpClient,
|
||||
}) : _pubVersionFinder =
|
||||
PubVersionFinder(httpClient: httpClient ?? http.Client()),
|
||||
super(packagesDir) {
|
||||
argParser.addOption(
|
||||
_pubPackageFlag,
|
||||
help: 'A pub package to update.',
|
||||
);
|
||||
argParser.addOption(
|
||||
_versionFlag,
|
||||
help: 'The version to update to.\n\n'
|
||||
'- For pub, defaults to the latest published version if not '
|
||||
'provided. This can be any constraint that pubspec.yaml allows; a '
|
||||
'specific version will be treated as the exact version for '
|
||||
'dependencies that are alread pinned, or a ^ range for those that '
|
||||
'are unpinned.',
|
||||
);
|
||||
}
|
||||
|
||||
static const String _pubPackageFlag = 'pub-package';
|
||||
static const String _versionFlag = 'version';
|
||||
|
||||
final PubVersionFinder _pubVersionFinder;
|
||||
|
||||
late final String? _targetPubPackage;
|
||||
late final String _targetVersion;
|
||||
|
||||
@override
|
||||
final String name = 'update-dependency';
|
||||
|
||||
@override
|
||||
final String description = 'Updates a dependency in a package.';
|
||||
|
||||
@override
|
||||
bool get hasLongOutput => false;
|
||||
|
||||
@override
|
||||
PackageLoopingType get packageLoopingType =>
|
||||
PackageLoopingType.includeAllSubpackages;
|
||||
|
||||
@override
|
||||
Future<void> initializeRun() async {
|
||||
const Set<String> targetFlags = <String>{_pubPackageFlag};
|
||||
final Set<String> passedTargetFlags =
|
||||
targetFlags.where((String flag) => argResults![flag] != null).toSet();
|
||||
if (passedTargetFlags.length != 1) {
|
||||
printError(
|
||||
'Exactly one of the target flags must be provided: (${targetFlags.join(', ')})');
|
||||
throw ToolExit(_exitIncorrectTargetDependency);
|
||||
}
|
||||
_targetPubPackage = getNullableStringArg(_pubPackageFlag);
|
||||
if (_targetPubPackage != null) {
|
||||
final String? version = getNullableStringArg(_versionFlag);
|
||||
if (version == null) {
|
||||
final PubVersionFinderResponse response = await _pubVersionFinder
|
||||
.getPackageVersion(packageName: _targetPubPackage!);
|
||||
switch (response.result) {
|
||||
case PubVersionFinderResult.success:
|
||||
_targetVersion = response.versions.first.toString();
|
||||
break;
|
||||
case PubVersionFinderResult.fail:
|
||||
printError('''
|
||||
Error fetching $_targetPubPackage version from pub: ${response.httpResponse.statusCode}:
|
||||
${response.httpResponse.body}
|
||||
''');
|
||||
throw ToolExit(_exitNoTargetVersion);
|
||||
case PubVersionFinderResult.noPackageFound:
|
||||
printError('$_targetPubPackage does not exist on pub');
|
||||
throw ToolExit(_exitNoTargetVersion);
|
||||
}
|
||||
} else {
|
||||
_targetVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> completeRun() async {
|
||||
_pubVersionFinder.httpClient.close();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PackageResult> runForPackage(RepositoryPackage package) async {
|
||||
if (_targetPubPackage != null) {
|
||||
return _runForPubDependency(package, _targetPubPackage!);
|
||||
}
|
||||
// TODO(stuartmorgan): Add othe dependency types here (e.g., maven).
|
||||
|
||||
return PackageResult.fail();
|
||||
}
|
||||
|
||||
/// Handles all of the updates for [package] when the target dependency is
|
||||
/// a pub dependency.
|
||||
Future<PackageResult> _runForPubDependency(
|
||||
RepositoryPackage package, String dependency) async {
|
||||
final _PubDependencyInfo? dependencyInfo =
|
||||
_getPubDependencyInfo(package, dependency);
|
||||
if (dependencyInfo == null) {
|
||||
return PackageResult.skip('Does not depend on $dependency');
|
||||
} else if (!dependencyInfo.hosted) {
|
||||
return PackageResult.skip('$dependency in not a hosted dependency');
|
||||
}
|
||||
|
||||
final String sectionKey = dependencyInfo.type == _PubDependencyType.dev
|
||||
? 'dev_dependencies'
|
||||
: 'dependencies';
|
||||
final String versionString;
|
||||
final VersionConstraint parsedConstraint =
|
||||
VersionConstraint.parse(_targetVersion);
|
||||
// If the provided string was a constraint, or if it's a specific
|
||||
// version but the package has a pinned dependency, use it as-is.
|
||||
if (dependencyInfo.pinned ||
|
||||
parsedConstraint is! VersionRange ||
|
||||
parsedConstraint.min != parsedConstraint.max) {
|
||||
versionString = _targetVersion;
|
||||
} else {
|
||||
// Otherwise, it's a specific version; treat it as '^version'.
|
||||
final Version minVersion = parsedConstraint.min!;
|
||||
versionString = '^$minVersion';
|
||||
}
|
||||
|
||||
print('${indentation}Updating to "$versionString"');
|
||||
if (versionString == dependencyInfo.constraintString) {
|
||||
return PackageResult.skip('Already depends on $versionString');
|
||||
}
|
||||
final YamlEditor editablePubspec =
|
||||
YamlEditor(package.pubspecFile.readAsStringSync());
|
||||
editablePubspec.update(
|
||||
<String>[sectionKey, dependency],
|
||||
versionString,
|
||||
);
|
||||
package.pubspecFile.writeAsStringSync(editablePubspec.toString());
|
||||
|
||||
// TODO(stuartmorgan): Add additionally handling of known packages that
|
||||
// do file generation (mockito, pigeon, etc.).
|
||||
|
||||
return PackageResult.success();
|
||||
}
|
||||
|
||||
/// Returns information about the current dependency of [package] on
|
||||
/// the package named [dependencyName], or null if there is no dependency.
|
||||
_PubDependencyInfo? _getPubDependencyInfo(
|
||||
RepositoryPackage package, String dependencyName) {
|
||||
final Pubspec pubspec = package.parsePubspec();
|
||||
|
||||
Dependency? dependency;
|
||||
final _PubDependencyType type;
|
||||
if (pubspec.dependencies.containsKey(dependencyName)) {
|
||||
dependency = pubspec.dependencies[dependencyName];
|
||||
type = _PubDependencyType.normal;
|
||||
} else if (pubspec.devDependencies.containsKey(dependencyName)) {
|
||||
dependency = pubspec.devDependencies[dependencyName];
|
||||
type = _PubDependencyType.dev;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (dependency != null && dependency is HostedDependency) {
|
||||
final VersionConstraint version = dependency.version;
|
||||
return _PubDependencyInfo(
|
||||
type,
|
||||
pinned: version is VersionRange && version.min == version.max,
|
||||
hosted: true,
|
||||
constraintString: version.toString(),
|
||||
);
|
||||
}
|
||||
return _PubDependencyInfo(type, pinned: false, hosted: false);
|
||||
}
|
||||
}
|
||||
|
||||
class _PubDependencyInfo {
|
||||
const _PubDependencyInfo(this.type,
|
||||
{required this.pinned, required this.hosted, this.constraintString});
|
||||
final _PubDependencyType type;
|
||||
final bool pinned;
|
||||
final bool hosted;
|
||||
final String? constraintString;
|
||||
}
|
||||
|
||||
enum _PubDependencyType { normal, dev }
|
343
script/tool/test/update_dependency_command_test.dart
Normal file
343
script/tool/test/update_dependency_command_test.dart
Normal file
@ -0,0 +1,343 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_plugin_tools/src/common/core.dart';
|
||||
import 'package:flutter_plugin_tools/src/update_dependency_command.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/testing.dart';
|
||||
import 'package:pubspec_parse/pubspec_parse.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
void main() {
|
||||
FileSystem fileSystem;
|
||||
late Directory packagesDir;
|
||||
late CommandRunner<void> runner;
|
||||
Future<http.Response> Function(http.Request request)? mockHttpResponse;
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem();
|
||||
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
|
||||
final UpdateDependencyCommand command = UpdateDependencyCommand(
|
||||
packagesDir,
|
||||
httpClient:
|
||||
MockClient((http.Request request) => mockHttpResponse!(request)),
|
||||
);
|
||||
|
||||
runner = CommandRunner<void>(
|
||||
'update_dependency_command', 'Test for update-dependency command.');
|
||||
runner.addCommand(command);
|
||||
});
|
||||
|
||||
/// Adds a dummy 'dependencies:' entries for [dependency] to [package].
|
||||
void addDependency(RepositoryPackage package, String dependency,
|
||||
{String version = '^1.0.0'}) {
|
||||
final List<String> lines = package.pubspecFile.readAsLinesSync();
|
||||
final int dependenciesStartIndex = lines.indexOf('dependencies:');
|
||||
assert(dependenciesStartIndex != -1);
|
||||
lines.insert(dependenciesStartIndex + 1, ' $dependency: $version');
|
||||
package.pubspecFile.writeAsStringSync(lines.join('\n'));
|
||||
}
|
||||
|
||||
/// Adds a 'dev_dependencies:' section with an entry for [dependency] to
|
||||
/// [package].
|
||||
void addDevDependency(RepositoryPackage package, String dependency,
|
||||
{String version = '^1.0.0'}) {
|
||||
final String originalContent = package.pubspecFile.readAsStringSync();
|
||||
package.pubspecFile.writeAsStringSync('''
|
||||
$originalContent
|
||||
|
||||
dev_dependencies:
|
||||
$dependency: $version
|
||||
''');
|
||||
}
|
||||
|
||||
test('throws if no target is provided', () async {
|
||||
Error? commandError;
|
||||
final List<String> output = await runCapturingPrint(
|
||||
runner, <String>['update-dependency'], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Exactly one of the target flags must be provided:'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
group('pub dependencies', () {
|
||||
test('throws if no version is given for an unpublished target', () async {
|
||||
mockHttpResponse = (http.Request request) async {
|
||||
return http.Response('', 404);
|
||||
};
|
||||
|
||||
Error? commandError;
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package'
|
||||
], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('target_package does not exist on pub'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('skips if there is no dependency', () async {
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('SKIPPING: Does not depend on target_package'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('skips if the dependency is already the target version', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package', version: '^1.5.0');
|
||||
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('SKIPPING: Already depends on ^1.5.0'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('logs updates', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package');
|
||||
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Updating to "^1.5.0"'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('updates normal dependency', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package');
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().dependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '^1.5.0');
|
||||
});
|
||||
|
||||
test('updates dev dependency', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDevDependency(package, 'target_package');
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().devDependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '^1.5.0');
|
||||
});
|
||||
|
||||
test('updates dependency in example', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir);
|
||||
final RepositoryPackage example = package.getExamples().first;
|
||||
addDevDependency(example, 'target_package');
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
example.parsePubspec().devDependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '^1.5.0');
|
||||
});
|
||||
|
||||
test('uses provided constraint as-is', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package');
|
||||
|
||||
const String providedConstraint = '>=1.6.0 <3.0.0';
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
providedConstraint
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().dependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), providedConstraint);
|
||||
});
|
||||
|
||||
test('uses provided version as lower bound for unpinned', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package');
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().dependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '^1.5.0');
|
||||
});
|
||||
|
||||
test('uses provided version as exact version for pinned', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package', version: '1.0.0');
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
'--version',
|
||||
'1.5.0'
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().dependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '1.5.0');
|
||||
});
|
||||
|
||||
test('uses latest pub version as lower bound for unpinned', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package');
|
||||
|
||||
const Map<String, dynamic> targetPackagePubResponse = <String, dynamic>{
|
||||
'name': 'a',
|
||||
'versions': <String>[
|
||||
'0.0.1',
|
||||
'1.0.0',
|
||||
'1.5.0',
|
||||
],
|
||||
};
|
||||
mockHttpResponse = (http.Request request) async {
|
||||
if (request.url.pathSegments.last == 'target_package.json') {
|
||||
return http.Response(json.encode(targetPackagePubResponse), 200);
|
||||
}
|
||||
return http.Response('', 500);
|
||||
};
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().dependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '^1.5.0');
|
||||
});
|
||||
|
||||
test('uses latest pub version as exact version for pinned', () async {
|
||||
final RepositoryPackage package =
|
||||
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
||||
addDependency(package, 'target_package', version: '1.0.0');
|
||||
|
||||
const Map<String, dynamic> targetPackagePubResponse = <String, dynamic>{
|
||||
'name': 'a',
|
||||
'versions': <String>[
|
||||
'0.0.1',
|
||||
'1.0.0',
|
||||
'1.5.0',
|
||||
],
|
||||
};
|
||||
mockHttpResponse = (http.Request request) async {
|
||||
if (request.url.pathSegments.last == 'target_package.json') {
|
||||
return http.Response(json.encode(targetPackagePubResponse), 200);
|
||||
}
|
||||
return http.Response('', 500);
|
||||
};
|
||||
|
||||
await runCapturingPrint(runner, <String>[
|
||||
'update-dependency',
|
||||
'--pub-package',
|
||||
'target_package',
|
||||
]);
|
||||
|
||||
final Dependency? dep =
|
||||
package.parsePubspec().dependencies['target_package'];
|
||||
expect(dep, isA<HostedDependency>());
|
||||
expect((dep! as HostedDependency).version.toString(), '1.5.0');
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user