[flutter_conductor] allow --force to override validations on start sub-command (#93514)
This commit is contained in:
parent
e95d669e3a
commit
764577f89a
@ -9,6 +9,7 @@ import 'proto/conductor_state.pb.dart' as pb;
|
|||||||
const String gsutilBinary = 'gsutil.py';
|
const String gsutilBinary = 'gsutil.py';
|
||||||
|
|
||||||
const String kFrameworkDefaultBranch = 'master';
|
const String kFrameworkDefaultBranch = 'master';
|
||||||
|
const String kForceFlag = 'force';
|
||||||
|
|
||||||
const List<String> kReleaseChannels = <String>[
|
const List<String> kReleaseChannels = <String>[
|
||||||
'stable',
|
'stable',
|
||||||
@ -81,6 +82,18 @@ String? getValueFromEnvOrArgs(
|
|||||||
'to be provided!');
|
'to be provided!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getBoolFromEnvOrArgs(
|
||||||
|
String name,
|
||||||
|
ArgResults argResults,
|
||||||
|
Map<String, String> env,
|
||||||
|
) {
|
||||||
|
final String envName = fromArgToEnvName(name);
|
||||||
|
if (env[envName] != null) {
|
||||||
|
return (env[envName]?.toUpperCase()) == 'TRUE';
|
||||||
|
}
|
||||||
|
return argResults[name] as bool;
|
||||||
|
}
|
||||||
|
|
||||||
/// Return multiple values from the environment or fall back to [argResults].
|
/// Return multiple values from the environment or fall back to [argResults].
|
||||||
///
|
///
|
||||||
/// Values read from an environment variable are assumed to be comma-delimited.
|
/// Values read from an environment variable are assumed to be comma-delimited.
|
||||||
|
@ -14,7 +14,6 @@ import './stdio.dart';
|
|||||||
|
|
||||||
const String kStateOption = 'state-file';
|
const String kStateOption = 'state-file';
|
||||||
const String kYesFlag = 'yes';
|
const String kYesFlag = 'yes';
|
||||||
const String kForceFlag = 'force';
|
|
||||||
|
|
||||||
/// Command to proceed from one [pb.ReleasePhase] to the next.
|
/// Command to proceed from one [pb.ReleasePhase] to the next.
|
||||||
class NextCommand extends Command<void> {
|
class NextCommand extends Command<void> {
|
||||||
|
@ -102,6 +102,11 @@ class StartCommand extends Command<void> {
|
|||||||
'n': 'Indicates a hotfix to a dev or beta release.',
|
'n': 'Indicates a hotfix to a dev or beta release.',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
argParser.addFlag(
|
||||||
|
kForceFlag,
|
||||||
|
abbr: 'f',
|
||||||
|
help: 'Override all validations of the command line inputs.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Checkouts checkouts;
|
final Checkouts checkouts;
|
||||||
@ -178,6 +183,11 @@ class StartCommand extends Command<void> {
|
|||||||
argumentResults,
|
argumentResults,
|
||||||
platform.environment,
|
platform.environment,
|
||||||
)!;
|
)!;
|
||||||
|
final bool force = getBoolFromEnvOrArgs(
|
||||||
|
kForceFlag,
|
||||||
|
argumentResults,
|
||||||
|
platform.environment,
|
||||||
|
);
|
||||||
final File stateFile = checkouts.fileSystem.file(
|
final File stateFile = checkouts.fileSystem.file(
|
||||||
getValueFromEnvOrArgs(kStateOption, argumentResults, platform.environment),
|
getValueFromEnvOrArgs(kStateOption, argumentResults, platform.environment),
|
||||||
);
|
);
|
||||||
@ -198,6 +208,7 @@ class StartCommand extends Command<void> {
|
|||||||
releaseChannel: releaseChannel,
|
releaseChannel: releaseChannel,
|
||||||
stateFile: stateFile,
|
stateFile: stateFile,
|
||||||
stdio: stdio,
|
stdio: stdio,
|
||||||
|
force: force,
|
||||||
);
|
);
|
||||||
return context.run();
|
return context.run();
|
||||||
}
|
}
|
||||||
@ -223,6 +234,7 @@ class StartContext {
|
|||||||
required this.releaseChannel,
|
required this.releaseChannel,
|
||||||
required this.stateFile,
|
required this.stateFile,
|
||||||
required this.stdio,
|
required this.stdio,
|
||||||
|
this.force = false,
|
||||||
}) : git = Git(processManager);
|
}) : git = Git(processManager);
|
||||||
|
|
||||||
final String candidateBranch;
|
final String candidateBranch;
|
||||||
@ -242,6 +254,9 @@ class StartContext {
|
|||||||
final File stateFile;
|
final File stateFile;
|
||||||
final Stdio stdio;
|
final Stdio stdio;
|
||||||
|
|
||||||
|
/// If validations should be overridden.
|
||||||
|
final bool force;
|
||||||
|
|
||||||
Future<void> run() async {
|
Future<void> run() async {
|
||||||
if (stateFile.existsSync()) {
|
if (stateFile.existsSync()) {
|
||||||
throw ConductorException(
|
throw ConductorException(
|
||||||
@ -361,7 +376,12 @@ class StartContext {
|
|||||||
final Version lastVersion = Version.fromString(await framework.getFullTag(
|
final Version lastVersion = Version.fromString(await framework.getFullTag(
|
||||||
framework.upstreamRemote.name, candidateBranch,
|
framework.upstreamRemote.name, candidateBranch,
|
||||||
exact: false,
|
exact: false,
|
||||||
))..ensureValid(candidateBranch, incrementLetter);
|
));
|
||||||
|
// [force] means we know this would fail but need to publish anyway
|
||||||
|
if (!force) {
|
||||||
|
lastVersion.ensureValid(candidateBranch, incrementLetter);
|
||||||
|
}
|
||||||
|
|
||||||
Version nextVersion = calculateNextVersion(lastVersion);
|
Version nextVersion = calculateNextVersion(lastVersion);
|
||||||
nextVersion = await ensureBranchPointTagged(nextVersion, framework);
|
nextVersion = await ensureBranchPointTagged(nextVersion, framework);
|
||||||
|
|
||||||
|
@ -238,12 +238,18 @@ class Version {
|
|||||||
final int y;
|
final int y;
|
||||||
|
|
||||||
/// Number of hotfix releases after a stable release.
|
/// Number of hotfix releases after a stable release.
|
||||||
|
///
|
||||||
|
/// For non-stable releases, this will be 0.
|
||||||
final int z;
|
final int z;
|
||||||
|
|
||||||
/// Zero-indexed count of dev releases after a beta release.
|
/// Zero-indexed count of dev releases after a beta release.
|
||||||
|
///
|
||||||
|
/// For stable releases, this will be null.
|
||||||
final int? m;
|
final int? m;
|
||||||
|
|
||||||
/// Number of hotfixes required to make a dev release.
|
/// Number of hotfixes required to make a dev release.
|
||||||
|
///
|
||||||
|
/// For stable releases, this will be null.
|
||||||
final int? n;
|
final int? n;
|
||||||
|
|
||||||
/// Number of commits past last tagged dev release.
|
/// Number of commits past last tagged dev release.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:args/args.dart';
|
||||||
import 'package:conductor_core/src/globals.dart';
|
import 'package:conductor_core/src/globals.dart';
|
||||||
import 'package:conductor_core/src/proto/conductor_state.pb.dart' as pb;
|
import 'package:conductor_core/src/proto/conductor_state.pb.dart' as pb;
|
||||||
|
|
||||||
@ -129,4 +130,69 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('getBoolFromEnvOrArgs', () {
|
||||||
|
const String flagName = 'a-cli-flag';
|
||||||
|
|
||||||
|
test('prefers env over argResults', () {
|
||||||
|
final ArgResults argResults = FakeArgs(results: <String, Object>{
|
||||||
|
flagName: false,
|
||||||
|
});
|
||||||
|
final Map<String, String> env = <String, String>{'A_CLI_FLAG': 'TRUE'};
|
||||||
|
final bool result = getBoolFromEnvOrArgs(
|
||||||
|
flagName,
|
||||||
|
argResults,
|
||||||
|
env,
|
||||||
|
);
|
||||||
|
expect(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('falls back to argResults if env is empty', () {
|
||||||
|
final ArgResults argResults = FakeArgs(results: <String, Object>{
|
||||||
|
flagName: false,
|
||||||
|
});
|
||||||
|
final Map<String, String> env = <String, String>{};
|
||||||
|
final bool result = getBoolFromEnvOrArgs(
|
||||||
|
flagName,
|
||||||
|
argResults,
|
||||||
|
env,
|
||||||
|
);
|
||||||
|
expect(result, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeArgs implements ArgResults {
|
||||||
|
FakeArgs({
|
||||||
|
this.arguments = const <String>[],
|
||||||
|
this.name = 'fake-command',
|
||||||
|
this.results = const <String, Object>{},
|
||||||
|
});
|
||||||
|
|
||||||
|
final Map<String, Object> results;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final List<String> arguments;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ArgResults? get command => throw Exception('Unimplemented');
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get rest => throw Exception('Unimplemented');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<String> get options => throw Exception('Unimplemented');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool wasParsed(String name) {
|
||||||
|
return results[name] != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? operator[](String name) {
|
||||||
|
return results[name];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:convert' show jsonDecode;
|
import 'dart:convert' show jsonDecode;
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:conductor_core/src/globals.dart';
|
||||||
import 'package:conductor_core/src/proto/conductor_state.pb.dart' as pb;
|
import 'package:conductor_core/src/proto/conductor_state.pb.dart' as pb;
|
||||||
import 'package:conductor_core/src/proto/conductor_state.pbenum.dart' show ReleasePhase;
|
import 'package:conductor_core/src/proto/conductor_state.pbenum.dart' show ReleasePhase;
|
||||||
import 'package:conductor_core/src/repository.dart';
|
import 'package:conductor_core/src/repository.dart';
|
||||||
@ -122,6 +123,188 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('does not fail if version wrong but --force provided', () async {
|
||||||
|
const String revision2 = 'def789';
|
||||||
|
const String revision3 = '123abc';
|
||||||
|
const String previousDartRevision = '171876a4e6cf56ee6da1f97d203926bd7afda7ef';
|
||||||
|
const String nextDartRevision = 'f6c91128be6b77aef8351e1e3a9d07c85bc2e46e';
|
||||||
|
const String previousVersion = '1.2.0-1.0.pre';
|
||||||
|
// This is what this release will be
|
||||||
|
const String nextVersion = '1.2.0-1.1.pre';
|
||||||
|
const String incrementLevel = 'n';
|
||||||
|
|
||||||
|
final Directory engine = fileSystem.directory(checkoutsParentDirectory)
|
||||||
|
.childDirectory('flutter_conductor_checkouts')
|
||||||
|
.childDirectory('engine');
|
||||||
|
|
||||||
|
final File depsFile = engine.childFile('DEPS');
|
||||||
|
|
||||||
|
final List<FakeCommand> engineCommands = <FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--origin',
|
||||||
|
'upstream',
|
||||||
|
'--',
|
||||||
|
EngineRepository.defaultUpstream,
|
||||||
|
engine.path,
|
||||||
|
],
|
||||||
|
onRun: () {
|
||||||
|
// Create the DEPS file which the tool will update
|
||||||
|
engine.createSync(recursive: true);
|
||||||
|
depsFile.writeAsStringSync(generateMockDeps(previousDartRevision));
|
||||||
|
}
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'remote', 'add', 'mirror', engineMirror],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'fetch', 'mirror'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision2,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'checkout',
|
||||||
|
'-b',
|
||||||
|
'cherrypicks-$candidateBranch',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'status', '--porcelain'],
|
||||||
|
stdout: 'MM path/to/DEPS',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'add', '--all'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'commit', "--message='Update Dart SDK to $nextDartRevision'"],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision2,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision2,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
final List<FakeCommand> frameworkCommands = <FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--origin',
|
||||||
|
'upstream',
|
||||||
|
'--',
|
||||||
|
FrameworkRepository.defaultUpstream,
|
||||||
|
fileSystem.path.join(
|
||||||
|
checkoutsParentDirectory,
|
||||||
|
'flutter_conductor_checkouts',
|
||||||
|
'framework',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'remote', 'add', 'mirror', frameworkMirror],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'fetch', 'mirror'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'checkout', 'upstream/$candidateBranch'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision3,
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'checkout',
|
||||||
|
'-b',
|
||||||
|
'cherrypicks-$candidateBranch',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'git',
|
||||||
|
'describe',
|
||||||
|
'--match',
|
||||||
|
'*.*.*',
|
||||||
|
'--tags',
|
||||||
|
'refs/remotes/upstream/$candidateBranch',
|
||||||
|
],
|
||||||
|
stdout: '$previousVersion-42-gabc123',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', 'HEAD'],
|
||||||
|
stdout: revision3,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
final CommandRunner<void> runner = createRunner(
|
||||||
|
commands: <FakeCommand>[
|
||||||
|
...engineCommands,
|
||||||
|
...frameworkCommands,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final String stateFilePath = fileSystem.path.join(
|
||||||
|
platform.environment['HOME']!,
|
||||||
|
kStateFileName,
|
||||||
|
);
|
||||||
|
|
||||||
|
await runner.run(<String>[
|
||||||
|
'start',
|
||||||
|
'--$kFrameworkMirrorOption',
|
||||||
|
frameworkMirror,
|
||||||
|
'--$kEngineMirrorOption',
|
||||||
|
engineMirror,
|
||||||
|
'--$kCandidateOption',
|
||||||
|
candidateBranch,
|
||||||
|
'--$kReleaseOption',
|
||||||
|
releaseChannel,
|
||||||
|
'--$kStateOption',
|
||||||
|
stateFilePath,
|
||||||
|
'--$kDartRevisionOption',
|
||||||
|
nextDartRevision,
|
||||||
|
'--$kIncrementOption',
|
||||||
|
incrementLevel,
|
||||||
|
'--$kForceFlag',
|
||||||
|
]);
|
||||||
|
|
||||||
|
final File stateFile = fileSystem.file(stateFilePath);
|
||||||
|
|
||||||
|
final pb.ConductorState state = pb.ConductorState();
|
||||||
|
state.mergeFromProto3Json(
|
||||||
|
jsonDecode(stateFile.readAsStringSync()),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
|
expect(state.isInitialized(), true);
|
||||||
|
expect(state.releaseChannel, releaseChannel);
|
||||||
|
expect(state.releaseVersion, nextVersion);
|
||||||
|
expect(state.engine.candidateBranch, candidateBranch);
|
||||||
|
expect(state.engine.startingGitHead, revision2);
|
||||||
|
expect(state.engine.dartRevision, nextDartRevision);
|
||||||
|
expect(state.engine.upstream.url, 'git@github.com:flutter/engine.git');
|
||||||
|
expect(state.framework.candidateBranch, candidateBranch);
|
||||||
|
expect(state.framework.startingGitHead, revision3);
|
||||||
|
expect(state.framework.upstream.url, 'git@github.com:flutter/flutter.git');
|
||||||
|
expect(state.currentPhase, ReleasePhase.APPLY_ENGINE_CHERRYPICKS);
|
||||||
|
expect(state.conductorVersion, conductorVersion);
|
||||||
|
expect(state.incrementLevel, incrementLevel);
|
||||||
|
});
|
||||||
|
|
||||||
test('creates state file if provided correct inputs', () async {
|
test('creates state file if provided correct inputs', () async {
|
||||||
const String revision2 = 'def789';
|
const String revision2 = 'def789';
|
||||||
const String revision3 = '123abc';
|
const String revision3 = '123abc';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user