Skip to content

Revert deletion of deploy command to fix dev build #8155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 6, 2025
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
84 changes: 84 additions & 0 deletions tool/plugin/lib/plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Future<int> main(List<String> args) async {
runner.addCommand(LintCommand(runner));
runner.addCommand(GradleBuildCommand(runner));
runner.addCommand(TestCommand(runner));
runner.addCommand(DeployCommand(runner));
runner.addCommand(GenerateCommand(runner));
runner.addCommand(VerifyCommand(runner));
runner.addCommand(RunIdeCommand(runner));
Expand Down Expand Up @@ -475,6 +476,89 @@ class GradleBuildCommand extends ProductCommand {
}
}

/// Either the --release or --channel options must be provided.
/// The permanent token is read from the file specified by Kokoro.
class DeployCommand extends ProductCommand {
@override
final BuildCommandRunner runner;

DeployCommand(this.runner) : super('deploy');

@override
String get description => 'Upload the Flutter plugin to the JetBrains site.';

@override
Future<int> doit() async {
if (isReleaseMode) {
if (!await performReleaseChecks(this)) {
return 1;
}
} else if (!isDevChannel) {
log('Deploy must have a --release or --channel=dev argument');
return 1;
}

var token = readTokenFromKeystore('FLUTTER_KEYSTORE_NAME');
var value = 0;
var originalDir = Directory.current;
for (var spec in specs) {
if (spec.channel != channel) continue;
var filePath = releasesFilePath(spec);
log("uploading $filePath");
var file = File(filePath);
changeDirectory(file.parent);
var pluginNumber = pluginRegistryIds[spec.pluginId];
value = await upload(
p.basename(file.path),
pluginNumber!,
token,
spec.channel,
);
if (value != 0) {
return value;
}
}
changeDirectory(originalDir);
return value;
}

void changeDirectory(Directory dir) {
Directory.current = dir.path;
}

Future<int> upload(
String filePath,
String pluginNumber,
String token,
String channel,
) async {
if (!File(filePath).existsSync()) {
throw 'File not found: $filePath';
}
// See https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html#PluginUploadAPI-POST
// Trying to run curl directly doesn't work; something odd happens to the quotes.
var cmd = '''
curl
-i
--header "Authorization: Bearer $token"
-F pluginId=$pluginNumber
-F file=@$filePath
-F channel=$channel
https://plugins.jetbrains.com/plugin/uploadPlugin
''';

var args = ['-c', cmd.split('\n').join(' ')];
final processResult = await Process.run('sh', args);
if (processResult.exitCode != 0) {
log('Upload failed: ${processResult.stderr} for file: $filePath');
}
final out = processResult.stdout as String;
var message = out.trim().split('\n').last.trim();
log(message);
return processResult.exitCode;
}
}

/// Generate the plugin.xml from the plugin.xml.template file. If the --release
/// argument is given, create a git branch and commit the new file to it,
/// assuming the release checks pass.
Expand Down
7 changes: 3 additions & 4 deletions tool/plugin/lib/runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class BuildCommandRunner extends CommandRunner<int> {
BuildCommandRunner()
: super(
'plugin',
'A script to build and test the Flutter IntelliJ plugin.',
'A script to build, test, and deploy the Flutter IntelliJ plugin.',
) {
argParser.addOption(
'release',
Expand Down Expand Up @@ -79,9 +79,8 @@ org.gradle.parallel=true
org.gradle.jvmargs=-Xms1024m -Xmx4048m
''';
final propertiesFile = File("$rootPath/gradle.properties");
final source = propertiesFile.existsSync()
? propertiesFile.readAsStringSync()
: null;
final source =
propertiesFile.existsSync() ? propertiesFile.readAsStringSync() : null;
propertiesFile.writeAsStringSync(contents);
int result;
// Using the Gradle daemon causes a strange problem.
Expand Down
110 changes: 110 additions & 0 deletions tool/plugin/test/plugin_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ void main() {
expect(TestCommand(BuildCommandRunner()).name, "test");
});

test('deploy', () {
expect(DeployCommand(BuildCommandRunner()).name, "deploy");
});

test('generate', () {
expect(GenerateCommand(BuildCommandRunner()).name, "generate");
});
Expand Down Expand Up @@ -83,6 +87,13 @@ void main() {
});
});

test('deploy', () async {
var runner = makeTestRunner();
await runner.run(["-r19", "-d../..", "deploy"]).whenComplete(() {
buildSpecAssertions(runner, "deploy");
});
});

test('verify', () async {
var runner = makeTestRunner();
await runner.run(["-r19", "-d../..", "verify"]).whenComplete(() {
Expand All @@ -91,6 +102,75 @@ void main() {
});
});

group('release', () {
test('simple', () async {
var runner = makeTestRunner();
late TestDeployCommand cmd;
await runner.run(["-r19", "-d../..", "deploy"]).whenComplete(() {
cmd = (runner.commands['deploy'] as TestDeployCommand);
});
expect(cmd.isReleaseValid, true);
});

test('minor', () async {
var runner = makeTestRunner();
late TestDeployCommand cmd;
await runner.run(["-r19.2", "-d../..", "deploy"]).whenComplete(() {
cmd = (runner.commands['deploy'] as TestDeployCommand);
});
expect(cmd.isReleaseValid, true);
});

test('patch invalid', () async {
var runner = makeTestRunner();
late TestDeployCommand cmd;
await runner.run(["-r19.2.1", "-d../..", "deploy"]).whenComplete(() {
cmd = (runner.commands['deploy'] as TestDeployCommand);
});
expect(cmd.isReleaseValid, false);
});

test('non-numeric', () async {
var runner = makeTestRunner();
late TestDeployCommand cmd;
await runner.run(["-rx19.2", "-d../..", "deploy"]).whenComplete(() {
cmd = (runner.commands['deploy'] as TestDeployCommand);
});
expect(cmd.isReleaseValid, false);
});
});

group('deploy', () {
test('clean', () async {
var dir = Directory.current;
var runner = makeTestRunner();
await runner
.run(["-r=19", "-d../..", "deploy", "--no-as", "--no-ij"])
.whenComplete(() {
expect(Directory.current.path, equals(dir.path));
});
});

test('without --release', () async {
var runner = makeTestRunner();
late TestDeployCommand cmd;
await runner.run(["-d../..", "deploy"]).whenComplete(() {
cmd = (runner.commands['deploy'] as TestDeployCommand);
});
expect(cmd.paths, orderedEquals([]));
});

test('release paths', () async {
var runner = makeTestRunner();
late TestDeployCommand cmd;
await runner.run(["--release=19", "-d../..", "deploy"]).whenComplete(() {
cmd = (runner.commands['deploy'] as TestDeployCommand);
});
var specs = cmd.specs.where((s) => s.isStableChannel).toList();
expect(cmd.paths.length, specs.length);
});
});

group('build', () {
test('plugin.xml', () async {
var runner = makeTestRunner();
Expand Down Expand Up @@ -175,11 +255,41 @@ BuildCommandRunner makeTestRunner() {
var runner = BuildCommandRunner();
runner.addCommand(TestMakeCommand(runner));
runner.addCommand(TestTestCommand(runner));
runner.addCommand(TestDeployCommand(runner));
runner.addCommand(TestGenCommand(runner));
runner.addCommand(TestVerifyCommand(runner));
return runner;
}

class TestDeployCommand extends DeployCommand {
List<String> paths = <String>[];
List<String> plugins = <String>[];

TestDeployCommand(super.runner);

@override
bool get isTesting => true;

@override
void changeDirectory(Directory dir) {}

String readTokenFile() {
return "token";
}

@override
Future<int> upload(
String filePath,
String pluginNumber,
String token,
String channel,
) {
paths.add(filePath);
plugins.add(pluginNumber);
return Future(() => 0);
}
}

class TestGenCommand extends GenerateCommand {
TestGenCommand(super.runner);

Expand Down