Skip to content
Open
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
18 changes: 8 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,15 @@ jobs:
- name: Setup Dart
uses: dart-lang/setup-dart@v1

- name: Create archive (macOS, Linux)
if: matrix.os == 'macos-latest' || matrix.os == 'ubuntu-latest'
- name: Create archive
run: |
bash ./scripts/build_release.sh
shell: bash

- name: Create archive (Windows)
if: matrix.os == 'windows-latest'
run: |
./scripts/build_release.ps1
shell: pwsh
cd scripts
dart pub get
cd ..
dart run scripts/bin/build_release.dart
env:
GITHUB_MATRIX_OS: ${{ matrix.os }}
RUNNER_ARCH: ${{ runner.arch }}

- name: Upload artifact
uses: actions/upload-artifact@v4
Expand Down
32 changes: 32 additions & 0 deletions scripts/bin/build_release.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2025 The Flutter Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:args/args.dart';
import 'package:release_scripts/src/build_release_command.dart';
import 'package:release_scripts/src/utils.dart';

Future<void> main(List<String> args) async {
await runScript((context) async {
final parser = ArgParser();
parser.addFlag(
'help',
abbr: 'h',
negatable: false,
help: 'Print this usage information.',
);
final argResults = parser.parse(args);

if (argResults.flag('help')) {
print('Usage: build_release');
print(parser.usage);
return;
} else if (argResults.rest.isNotEmpty) {
throw ExitException(
'Unexpected arguments: ${argResults.rest.join(' ')}\nUsage: build_release',
);
}

await BuildReleaseCommand(context).run();
});
}
30 changes: 30 additions & 0 deletions scripts/bin/bump_version.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 The Flutter Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:args/args.dart';
import 'package:release_scripts/src/bump_version_command.dart';
import 'package:release_scripts/src/utils.dart';

Future<void> main(List<String> args) async {
await runScript((context) async {
final parser = ArgParser();
parser.addFlag(
'help',
abbr: 'h',
negatable: false,
help: 'Print this usage information.',
);
final argResults = parser.parse(args);

if (argResults.flag('help') || argResults.rest.isEmpty) {
print('Usage: bump_version <new_version>');
print(parser.usage);
if (argResults.rest.isEmpty && !argResults.flag('help')) {
throw ExitException('No version provided.');
}
return;
}
await BumpVersionCommand(context, argResults.rest.first).run();
});
}
32 changes: 32 additions & 0 deletions scripts/bin/update_local.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2025 The Flutter Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:args/args.dart';
import 'package:release_scripts/src/update_local_command.dart';
import 'package:release_scripts/src/utils.dart';

Future<void> main(List<String> args) async {
await runScript((context) async {
final parser = ArgParser();
parser.addFlag(
'help',
abbr: 'h',
negatable: false,
help: 'Print this usage information.',
);
final argResults = parser.parse(args);

if (argResults.flag('help')) {
print('Usage: update_local');
print(parser.usage);
return;
} else if (argResults.rest.isNotEmpty) {
throw ExitException(
'Unexpected arguments: ${argResults.rest.join(' ')}\nUsage: update_local',
);
}

await UpdateLocalCommand(context).run();
});
}
7 changes: 0 additions & 7 deletions scripts/build_release.ps1

This file was deleted.

43 changes: 0 additions & 43 deletions scripts/build_release.sh

This file was deleted.

36 changes: 0 additions & 36 deletions scripts/bump_version.sh

This file was deleted.

103 changes: 103 additions & 0 deletions scripts/lib/src/build_release_command.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'package:file/file.dart';

import 'utils.dart';

/// A command that builds a release archive for the Flutter extension.
///
/// This command:
/// 1. Detects the current OS and architecture (or uses GITHUB_MATRIX_OS if set).
/// 2. Identifies the repository root.
/// 3. Creates a `.tar.gz` (Linux/macOS) or `.zip` (Windows) archive of the
/// extension source.
/// 4. Sets the `ARCHIVE_NAME` environment variable for GitHub Actions.
class BuildReleaseCommand {
/// The script execution context.
final ScriptContext context;

/// Creates a [BuildReleaseCommand] with the given [context].
BuildReleaseCommand(this.context);

/// Executes the build release process.
///
/// Returns the path to the created archive.
Future<String> run() async {
final fs = context.fs;
final platform = context.platform;

final platformInfo = await getPlatformInfo(context);
final os = platformInfo.os;
final arch = platformInfo.arch;
final ext = platformInfo.ext;

final archiveName = '$os.$arch.flutter.$ext';

// Find the repository root by looking for 'gemini-extension.json'.
final repoRoot = findRepoRoot(context);
final repoPath = repoRoot.path;
print('Repository root: $repoPath');

String tagName = (platform.environment['GITHUB_REF'] ?? 'refs/tags/HEAD');
if (tagName.startsWith('refs/tags/')) {
tagName = tagName.substring('refs/tags/'.length);
}
if (tagName.isEmpty) tagName = 'HEAD';

if (fs.isFileSync(fs.path.join(repoPath, archiveName))) {
fs.file(fs.path.join(repoPath, archiveName)).deleteSync();
}

print('Creating archive $archiveName from $tagName...');

final filesToArchive = [
'gemini-extension.json',
'commands/',
'LICENSE',
'README.md',
'flutter.md',
];

if (os == 'windows') {
await runProcess(context,
[
'git',
'archive',
'--format=zip',
'-o',
archiveName,
tagName,
...filesToArchive
],
workingDirectory: repoPath);
} else {
final tarName = '$os.$arch.flutter.tar';
await runProcess(context,
[
'git',
'archive',
'--format=tar',
'-o',
tarName,
tagName,
...filesToArchive
],
workingDirectory: repoPath);

await runProcess(context, [
'gzip',
'--force',
tarName,
], workingDirectory: repoPath);
}

// Set output env var for GitHub Actions.
final githubEnv = platform.environment['GITHUB_ENV'];
if (githubEnv != null && fs.isFileSync(githubEnv)) {
fs.file(githubEnv).writeAsStringSync('ARCHIVE_NAME=$archiveName\n',
mode: FileMode.append);
} else {
context.stdout.writeln('Archive written to $archiveName');
}

return fs.path.join(repoPath, archiveName);
}
}
Loading