Skip to content

Commit

Permalink
Merge pull request #324 from leoafarias/fix/channel-unknown
Browse files Browse the repository at this point in the history
New checkout workflow for release versions
  • Loading branch information
leoafarias authored Jun 17, 2021
2 parents 6822f97 + 7ae44dd commit e6c58d8
Show file tree
Hide file tree
Showing 16 changed files with 250 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ packages/cli/test/.test_coverage.dart
packages/cli/.env
.env
node_modules
test/.test_cov.dart
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.2.0

- Resolves channel unknown when pulling release version
- Allows for release install of different channels [Read more](https://fvm.app/docs/advanced/release_multiple_channels)

## 2.1.1

- Removed Flutter version validation check
Expand Down
40 changes: 36 additions & 4 deletions lib/src/commands/flutter_command.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:args/args.dart';
import 'package:fvm/exceptions.dart';

import '../models/valid_version_model.dart';
import '../services/cache_service.dart';
import '../services/project_service.dart';
import '../utils/commands.dart';
import '../utils/logger.dart';
Expand All @@ -22,20 +24,50 @@ class FlutterCommand extends BaseCommand {
@override
Future<int> run() async {
final version = await ProjectService.findVersion();
final args = argResults!.arguments;
final args = [...argResults!.arguments];

if (version != null) {
final validVersion = ValidVersion(version);
// Will install version if not already instaled
final cacheVersion = await ensureCacheWorkflow(validVersion);

logger.trace('fvm: running version "$version"\n');

// If its not a channel silence version check
if (!validVersion.isChannel) {
args.add('--no-version-check');
_checkIfUpgradeCommand(args);
}
// Runs flutter command with pinned version
return await flutterCmd(cacheVersion, args);
} else {
// Running null will default to flutter version on paths
return await flutterGlobalCmd(args);
// Try to get fvm global version
final cacheVersion = await CacheService.getGlobal();

// Get exec path for flutter
if (cacheVersion != null) {
logger.trace(
'FVM: Running global configured version "${cacheVersion.name}"',
);
final validVersion = ValidVersion(cacheVersion.name);
// If its not a channel silence version check
if (!validVersion.isChannel) {
args.add('--no-version-check');
_checkIfUpgradeCommand(args);
}
return await flutterCmd(cacheVersion, args);
} else {
// Running null will default to flutter version on paths
return await flutterGlobalCmd(args);
}
}
}
}

void _checkIfUpgradeCommand(List<String> args) {
if (args.first == 'upgrade') {
throw FvmUsageException(
'You should not upgrade a release version. '
'Please install a channel instead to upgrade it. ',
);
}
}
46 changes: 46 additions & 0 deletions lib/src/models/valid_version_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '../../constants.dart';
import '../utils/helpers.dart';

/// Model for valid Flutter versions.
/// User for type safety across FVM
Expand All @@ -15,11 +16,56 @@ class ValidVersion {
this.custom = false,
});

/// Version number
String get version {
if (forceChannel == null) {
// Return name if channel is not forced
return name;
} else {
// Return version number without channel
final channel = '@$forceChannel';
return name.substring(0, name.length - channel.length);
}
}

/// Is [name] a release
bool get isRelease {
// Is a release if its not a channel or hash
return !isGitHash && !isChannel;
}

/// Checks if [name] is a git hash
bool get isGitHash {
return checkIsGitHash(name);
}

/// Checks if need to reset after clone
bool get needReset {
return isGitHash || isRelease;
}

/// Is valid version a channel
bool get isChannel {
return kFlutterChannels.contains(name);
}

/// Forces a valid version within a channel
String? get forceChannel {
if (checkIsChannel(name)) {
return null;
}

// Check if last part is channel
// i.e. 2.2.2@beta
final parts = name.split('@');

if (checkIsChannel(parts.last)) {
return parts.last;
} else {
return null;
}
}

/// Is valid version is master channel
bool get isMaster {
return name == 'master';
Expand Down
2 changes: 1 addition & 1 deletion lib/src/services/cache_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class CacheService {

/// Caches version a [validVersion] and returns [CacheVersion]
static Future<void> cacheVersion(ValidVersion validVersion) async {
await GitTools.cloneVersion(validVersion.name);
await GitTools.cloneVersion(validVersion);
}

/// Gets Flutter SDK version from CacheVersion sync
Expand Down
97 changes: 51 additions & 46 deletions lib/src/services/git_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,54 @@ import 'package:process_run/cmd_run.dart';

import '../../constants.dart';
import '../../exceptions.dart';
import '../models/valid_version_model.dart';
import '../utils/helpers.dart';
import '../utils/logger.dart';
import 'context.dart';
import 'releases_service/releases_client.dart';

/// Tools and helpers used for interacting with git
class GitTools {
GitTools._();

/// Clones Flutter SDK from Version Number or Channel
/// Returns exists:true if comes from cache or false if its new fetch.
static Future<void> cloneVersion(String version) async {
final versionDir = versionCacheDir(version);
static Future<void> cloneVersion(ValidVersion version) async {
final versionDir = versionCacheDir(version.name);
await versionDir.create(recursive: true);

final isCommit = checkIsGitHash(version);
// Check if its git commit

String? channel;

if (version.isChannel) {
channel = version.name;
// If its not a commit hash
} else if (version.isRelease) {
if (version.forceChannel != null) {
// Version name forces channel version
channel = version.forceChannel;
} else {
// Fetches the channel of version by priority
final flutterReleases = await fetchFlutterReleases();
channel = flutterReleases.getChannelFromVersion(version.name);
}
}

final args = [
'clone',
'--progress',
if (!isCommit) ...[
if (!version.isGitHash) ...[
'-c',
'advice.detachedHead=false',
'-b',
version,
channel ?? version.name,
],
kFlutterRepo,
versionDir.path
];

final process = await runExecutableArguments(
var process = await runExecutableArguments(
'git',
args,
stdout: consoleController.stdoutSink,
Expand All @@ -44,17 +62,16 @@ class GitTools {
if (process.exitCode != 0) {
// Did not cleanly exit clean up directory
await _cleanupVersionDir(versionDir);

logger.trace(process.stderr.toString());
throw FvmInternalError('Could not git clone $version');
}

if (isCommit) {
/// If version has a channel reset
if (version.needReset) {
try {
await _resetRepository(versionDir, commitHash: version);
} on FvmInternalError catch (_) {
await _resetRepository(versionDir, version: version.version);
} on FvmInternalError {
await _cleanupVersionDir(versionDir);

rethrow;
}
}
Expand All @@ -64,7 +81,7 @@ class GitTools {

static Future<void> _cleanupVersionDir(Directory versionDir) async {
if (await versionDir.exists()) {
await versionDir.delete();
await versionDir.delete(recursive: true);
}
}

Expand Down Expand Up @@ -94,54 +111,42 @@ class GitTools {
}

/// Returns the [name] of a branch or tag for a [version]
static Future<String?> getBranchOrTag(String version) async {
static Future<String?> getBranch(String version) async {
final versionDir = Directory(join(ctx.cacheDir.path, version));
return _getCurrentGitBranch(versionDir);
final result = await runExecutableArguments(
'git',
['rev-parse', '--abbrev-ref', 'HEAD'],
workingDirectory: versionDir.path,
);
return result.stdout.trim() as String;
}

static Future<String?> _getCurrentGitBranch(Directory dir) async {
try {
if (!await dir.exists()) {
throw Exception(
'Could not get GIT version from ${dir.path} that does not exist');
}
var result = await runExecutableArguments(
'git', ['rev-parse', '--abbrev-ref', 'HEAD'],
workingDirectory: dir.path);

if (result.stdout.trim() == 'HEAD') {
result = await runExecutableArguments(
'git', ['tag', '--points-at', 'HEAD'],
workingDirectory: dir.path);
}

if (result.exitCode != 0) {
return null;
}

return result.stdout.trim() as String;
} on Exception catch (err) {
FvmLogger.error(err.toString());
return null;
}
/// Returns the [name] of a tag [version]
static Future<String?> getTag(String version) async {
final versionDir = Directory(join(ctx.cacheDir.path, version));
final result = await runExecutableArguments(
'git',
['describe', '--tags', '--exact-match'],
workingDirectory: versionDir.path,
);
return result.stdout.trim() as String;
}

/// Resets the repository at [directory] to [commitHash] using `git reset`
/// Resets the repository at [directory] to [version] using `git reset`
///
/// Throws [FvmInternalError] if `git`'s exit code is not 0.
static Future<void> _resetRepository(
Directory directory, {
required String commitHash,
required String version,
}) async {
final reset = await runExecutableArguments(
'git',
[
'-C',
directory.path,
'reset',
'--hard',
commitHash,
version,
],
workingDirectory: directory.path,
stdout: consoleController.stdoutSink,
stderr: consoleController.stderrSink,
);
Expand All @@ -150,7 +155,7 @@ class GitTools {
logger.trace(reset.stderr.toString());

throw FvmInternalError(
'Could not git reset $commitHash: ${reset.exitCode}',
'Could not git reset $version: ${reset.exitCode}',
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,26 @@ class FlutterReleases {
return channels[version];
}

final foundIdx = releases.indexWhere((v) => v.version == version);
int findReleaseIdx(Channel channel) {
return releases.indexWhere(
(v) => v.version == version && v.channel == channel,
);
}

// Versions can be in multiple versions
// Prioritize by order of maturity
// TODO: could be optimized and avoid multiple loops
final stableIndex = findReleaseIdx(Channel.stable);
final betaIndex = findReleaseIdx(Channel.beta);
final devIndex = findReleaseIdx(Channel.dev);

Release? release;
if (foundIdx >= 0) {
release = releases[foundIdx];
if (stableIndex >= 0) {
release = releases[stableIndex];
} else if (betaIndex >= 0) {
release = releases[betaIndex];
} else if (devIndex >= 0) {
release = releases[devIndex];
}

return release;
Expand Down
21 changes: 4 additions & 17 deletions lib/src/utils/commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:process_run/shell.dart';

import '../../constants.dart';
import '../../fvm.dart';
import '../services/cache_service.dart';
import 'console_utils.dart';
import 'guards.dart';
import 'helpers.dart';
Expand Down Expand Up @@ -55,22 +54,10 @@ Future<int> dartGlobalCmd(List<String> args) async {

/// Runs flutter from global version
Future<int> flutterGlobalCmd(List<String> args) async {
String execPath;
// Try to get fvm global version
final cacheVersion = await CacheService.getGlobal();
// Get exec path for flutter
if (cacheVersion != null) {
execPath = cacheVersion.flutterExec;
logger.trace(
'FVM: Running global configured version "${cacheVersion.name}"',
);
} else {
execPath = whichSync('flutter') ?? '';
logger.trace(
'FVM: Running Flutter SDK configured on environment PATH. $execPath',
);
}
FvmLogger.spacer();
final execPath = whichSync('flutter') ?? '';
logger.trace(
'FVM: Running Flutter SDK configured on environment PATH. $execPath',
);

// Run command
return await _runCmd(
Expand Down
Loading

0 comments on commit e6c58d8

Please sign in to comment.