chore: enable public_api_docs enforcement. (#2229)
Co-authored-by: Felix Angelov <felix@shorebird.dev>
This commit is contained in:
+6
-2
@@ -1,5 +1,9 @@
|
||||
# Release Notes
|
||||
|
||||
<!--
|
||||
cspell:words pubspec erickzanardo xcframeworks Cupertino codesign codecov rkishan appbundle proto tlsv
|
||||
-->
|
||||
|
||||
This section contains past updates we've sent to customers.
|
||||
|
||||
## 1.1.11 (June 11, 2024)
|
||||
@@ -58,7 +62,7 @@ https://docs.shorebird.dev/guides/patch-signing/
|
||||
## 1.1.5 (May 15, 2024)
|
||||
|
||||
- 🐦 Upgrade to Flutter 3.22.0 and Dart 3.4.0
|
||||
- 💾 Precache Flutter assets when switching revisions
|
||||
- 💾 Pre-cache Flutter assets when switching revisions
|
||||
- 🪵 Improvements to exception logs
|
||||
|
||||
## 1.1.4 (May 14, 2024)
|
||||
@@ -754,7 +758,7 @@ We've just released Shorebird v0.14.9 🎉
|
||||
|
||||
- 🍎 `shorebird release ios-alpha` supports `--no-codesign`
|
||||
- 🍧 automatically add new flavors if detected by `shorebird init`
|
||||
- 👀 only list previewable releases when running `shorebird preview`
|
||||
- 👀 only list preview-able releases when running `shorebird preview`
|
||||
|
||||
📚 Release notes can be found at https://github.com/shorebirdtech/shorebird/releases/tag/v0.14.9
|
||||
|
||||
|
||||
@@ -2,6 +2,4 @@ include: package:very_good_analysis/analysis_options.5.1.0.yaml
|
||||
analyzer:
|
||||
exclude:
|
||||
- lib/**.g.dart
|
||||
linter:
|
||||
rules:
|
||||
public_member_api_docs: false
|
||||
- lib/src/version.dart
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// cspell:words aapt USERPROFILE cmdline
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
@@ -7,7 +9,10 @@ import 'package:shorebird_cli/src/os/operating_system_interface.dart';
|
||||
import 'package:shorebird_cli/src/platform.dart';
|
||||
|
||||
// https://developer.android.com/studio/command-line/variables.html#envar
|
||||
/// The environment variable name for the Android SDK home directory.
|
||||
const kAndroidHome = 'ANDROID_HOME';
|
||||
|
||||
/// The environment variable name for the Android SDK root directory.
|
||||
const kAndroidSdkRoot = 'ANDROID_SDK_ROOT';
|
||||
|
||||
/// A reference to a [AndroidSdk] instance.
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:archive/archive_io.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart';
|
||||
|
||||
/// A wrapper around a directory that can be zipped.
|
||||
extension DirectoryArchive on Directory {
|
||||
/// Copies this directory to a temporary directory and zips it.
|
||||
Future<File> zipToTempFile() async {
|
||||
|
||||
@@ -85,6 +85,7 @@ abstract class ArchiveDiffer {
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a map of file paths to their respective checksums.
|
||||
Future<PathHashes> fileHashes(File archive) async {
|
||||
return Isolate.run(() {
|
||||
final zipDirectory = ZipDirectory.read(InputFileStream(archive.path));
|
||||
|
||||
@@ -5,6 +5,7 @@ typedef PathHashes = Map<String, String>;
|
||||
|
||||
/// Sets of [PathHashes] that represent changes between two sets of files.
|
||||
class FileSetDiff {
|
||||
/// Creates a [FileSetDiff] showing added, changed, and removed file sets.
|
||||
FileSetDiff({
|
||||
required this.addedPaths,
|
||||
required this.removedPaths,
|
||||
@@ -28,6 +29,7 @@ class FileSetDiff {
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates an empty FileSetDiff.
|
||||
FileSetDiff.empty()
|
||||
: addedPaths = {},
|
||||
removedPaths = {},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// cspell:words xcframeworks xcasset unsign codesign assetutil pubspec xcassets
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:typed_data';
|
||||
@@ -23,16 +24,18 @@ import 'package:shorebird_cli/src/archive_analysis/macho.dart';
|
||||
class IosArchiveDiffer extends ArchiveDiffer {
|
||||
String _hash(List<int> bytes) => sha256.convert(bytes).toString();
|
||||
|
||||
static final binaryFilePatterns = {
|
||||
static final _binaryFilePatterns = {
|
||||
RegExp(r'App.framework/App$'),
|
||||
RegExp(r'Flutter.framework/Flutter$'),
|
||||
};
|
||||
static RegExp appRegex = RegExp(
|
||||
|
||||
/// The regex pattern for identifying app files within an archive.
|
||||
static final RegExp appRegex = RegExp(
|
||||
r'^Products/Applications/[\w\-. ]+.app/[\w\- ]+$',
|
||||
);
|
||||
|
||||
/// Files that have been added, removed, or that have changed between the
|
||||
/// archives at the two provided paths. This method will also unisgn mach-o
|
||||
/// archives at the two provided paths. This method will also unsign mach-o
|
||||
/// binaries in the archives before computing the diff.
|
||||
@override
|
||||
Future<FileSetDiff> changedFiles(
|
||||
@@ -85,7 +88,7 @@ class IosArchiveDiffer extends ArchiveDiffer {
|
||||
.where((file) => file.isFile)
|
||||
.where(
|
||||
(file) =>
|
||||
binaryFilePatterns
|
||||
_binaryFilePatterns
|
||||
.any((pattern) => pattern.hasMatch(file.name)) ||
|
||||
appRegex.hasMatch(file.name),
|
||||
)
|
||||
@@ -125,7 +128,7 @@ class IosArchiveDiffer extends ArchiveDiffer {
|
||||
}
|
||||
|
||||
/// Uses assetutil to write a json description of a .car file to disk and
|
||||
/// diffs the contents of that file, less a timestamp line that chnages based
|
||||
/// diffs the contents of that file, less a timestamp line that changes based
|
||||
/// on when the .car file was created.
|
||||
Future<String> _carFileHash(ArchiveFile file) async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
const machOHeaderSize = 32;
|
||||
const uuidLoadCommandType = 0x1b;
|
||||
const _machOHeaderSize = 32;
|
||||
const _uuidLoadCommandType = 0x1b;
|
||||
|
||||
/// Utilities for interacting with Mach-O files.
|
||||
/// See https://en.wikipedia.org/wiki/Mach-O.
|
||||
@@ -38,12 +38,12 @@ class MachO {
|
||||
final numberOfLoadCommands = _readInt32(bytes, 16);
|
||||
|
||||
// The load commands are immediately after the header.
|
||||
var offset = machOHeaderSize;
|
||||
var offset = _machOHeaderSize;
|
||||
for (var i = 0; i < numberOfLoadCommands; i++) {
|
||||
final commandType = _readInt32(bytes, offset);
|
||||
final commandLength = _readInt32(bytes, offset + 4);
|
||||
|
||||
if (commandType == uuidLoadCommandType) {
|
||||
if (commandType == _uuidLoadCommandType) {
|
||||
// Zero out the UUID bytes.
|
||||
final loadCommandStart = offset + 8;
|
||||
for (var j = loadCommandStart; j < offset + commandLength; j++) {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// cspell:words propertylistserialization xcarchives
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:propertylistserialization/propertylistserialization.dart';
|
||||
|
||||
/// A representation of an Info.plist file.
|
||||
class Plist {
|
||||
/// Creates a new [Plist] from the contents of the provided [file].
|
||||
Plist({required File file}) {
|
||||
properties = PropertyListSerialization.propertyListWithString(
|
||||
file.readAsStringSync(),
|
||||
@@ -29,8 +33,10 @@ class Plist {
|
||||
/// },
|
||||
static const applicationPropertiesKey = 'ApplicationProperties';
|
||||
|
||||
/// The properties contained in the Info.plist file.
|
||||
late final Map<String, Object> properties;
|
||||
|
||||
/// The version number of the application.
|
||||
String get versionNumber {
|
||||
final applicationProperties =
|
||||
properties[applicationPropertiesKey]! as Map<String, Object>;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// cspell:words endtemplate aabs ipas appbundle bryanoltman codesign xcarchive
|
||||
// cspell:words xcframework
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
@@ -130,6 +132,9 @@ class ArtifactBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an APK using `flutter build apk`. Runs `flutter pub get` with the
|
||||
/// system installation of Flutter to reset `.dart_tool/package_config.json`
|
||||
/// after the build completes or fails.
|
||||
Future<File> buildApk({
|
||||
String? flavor,
|
||||
String? target,
|
||||
@@ -188,6 +193,9 @@ class ArtifactBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an AAR using `flutter build aar`. Runs `flutter pub get` with the
|
||||
/// system installation of Flutter to reset `.dart_tool/package_config.json`
|
||||
/// after the build completes or fails.
|
||||
Future<void> buildAar({
|
||||
required String buildNumber,
|
||||
Iterable<Arch>? targetPlatforms,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// cspell:words archs xcarchive xcframework
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
@@ -17,6 +18,7 @@ final artifactManagerRef = create(ArtifactManager.new);
|
||||
/// The [ArtifactManager] instance available in the current zone.
|
||||
ArtifactManager get artifactManager => read(artifactManagerRef);
|
||||
|
||||
/// Manages artifacts for the Shorebird CLI.
|
||||
class ArtifactManager {
|
||||
/// Generates a binary diff between two files and returns the path to the
|
||||
/// output diff file.
|
||||
@@ -246,6 +248,7 @@ class ArtifactManager {
|
||||
return ipaFiles.single;
|
||||
}
|
||||
|
||||
/// Name of the App.xcframework generated by `shorebird release ios-framework`
|
||||
static const String appXcframeworkName = 'App.xcframework';
|
||||
|
||||
/// Returns the path to the App.xcframework generated by
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// cspell:words googleapis bryanoltman endtemplate CLI tgvek orctktiabrek
|
||||
// cspell:words GOCSPX googleusercontent Pkkwp Entra
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@@ -19,10 +22,10 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
export 'ci_token.dart';
|
||||
|
||||
// A reference to a [Auth] instance.
|
||||
/// A reference to an [Auth] instance.
|
||||
final authRef = create(Auth.new);
|
||||
|
||||
// The [Auth] instance available in the current zone.
|
||||
/// The [Auth] instance available in the current zone.
|
||||
Auth get auth => read(authRef);
|
||||
|
||||
/// The JWT issuer field for Google-issued JWTs.
|
||||
@@ -36,6 +39,7 @@ const microsoftJwtIssuerPrefix = 'https://login.microsoftonline.com/';
|
||||
/// The environment variable that holds the Shorebird CI token.
|
||||
const shorebirdTokenEnvVar = 'SHOREBIRD_TOKEN';
|
||||
|
||||
/// Callback for obtaining access credentials.
|
||||
typedef ObtainAccessCredentials = Future<oauth2.AccessCredentials> Function(
|
||||
oauth2.ClientId clientId,
|
||||
List<String> scopes,
|
||||
@@ -44,6 +48,7 @@ typedef ObtainAccessCredentials = Future<oauth2.AccessCredentials> Function(
|
||||
oauth2.AuthEndpoints authEndpoints,
|
||||
});
|
||||
|
||||
/// Callback for refreshing access credentials.
|
||||
typedef RefreshCredentials = Future<oauth2.AccessCredentials> Function(
|
||||
oauth2.ClientId clientId,
|
||||
oauth2.AccessCredentials credentials,
|
||||
@@ -51,11 +56,15 @@ typedef RefreshCredentials = Future<oauth2.AccessCredentials> Function(
|
||||
oauth2.AuthEndpoints authEndpoints,
|
||||
});
|
||||
|
||||
/// Callback when credentials are refreshed.
|
||||
typedef OnRefreshCredentials = void Function(
|
||||
oauth2.AccessCredentials credentials,
|
||||
);
|
||||
|
||||
/// A client that automatically refreshes OAuth 2.0 credentials.
|
||||
class AuthenticatedClient extends http.BaseClient {
|
||||
/// Creates a new [AuthenticatedClient] with the given [httpClient] and
|
||||
/// [credentials].
|
||||
AuthenticatedClient.credentials({
|
||||
required http.Client httpClient,
|
||||
required oauth2.AccessCredentials credentials,
|
||||
@@ -68,6 +77,8 @@ class AuthenticatedClient extends http.BaseClient {
|
||||
refreshCredentials: refreshCredentials,
|
||||
);
|
||||
|
||||
/// Creates a new [AuthenticatedClient] with the given [httpClient] and
|
||||
/// [token].
|
||||
AuthenticatedClient.token({
|
||||
required http.Client httpClient,
|
||||
required CiToken token,
|
||||
@@ -137,7 +148,9 @@ class AuthenticatedClient extends http.BaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// An OAuth 2.0 authentication provider.
|
||||
class Auth {
|
||||
/// Creates a new [Auth] instance.
|
||||
Auth({
|
||||
http.Client? httpClient,
|
||||
String? credentialsDir,
|
||||
@@ -160,10 +173,12 @@ class Auth {
|
||||
final CodePushClientBuilder _buildCodePushClient;
|
||||
CiToken? _token;
|
||||
|
||||
/// The path to the credentials file.
|
||||
String get credentialsFilePath {
|
||||
return p.join(_credentialsDir, 'credentials.json');
|
||||
}
|
||||
|
||||
/// The underlying HTTP client.
|
||||
http.Client get client {
|
||||
if (_credentials == null && _token == null) {
|
||||
return _httpClient;
|
||||
@@ -183,6 +198,7 @@ class Auth {
|
||||
);
|
||||
}
|
||||
|
||||
/// Gets a CI token for the current user.
|
||||
Future<CiToken> loginCI(
|
||||
AuthProvider authProvider, {
|
||||
required void Function(String) prompt,
|
||||
@@ -220,6 +236,7 @@ class Auth {
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs in the user.
|
||||
Future<void> login(
|
||||
AuthProvider authProvider, {
|
||||
required void Function(String) prompt,
|
||||
@@ -255,14 +272,17 @@ class Auth {
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs out the user.
|
||||
void logout() => _clearCredentials();
|
||||
|
||||
oauth2.AccessCredentials? _credentials;
|
||||
|
||||
String? _email;
|
||||
|
||||
/// The current user's email.
|
||||
String? get email => _email;
|
||||
|
||||
/// Whether the user is authenticated.
|
||||
bool get isAuthenticated => _email != null || _token != null;
|
||||
|
||||
void _loadCredentials() {
|
||||
@@ -312,12 +332,15 @@ Run `shorebird login:ci` to obtain a new token.''');
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes the underlying HTTP client.
|
||||
void close() {
|
||||
_httpClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions on [oauth2.AccessCredentials] for working with JWT claims.
|
||||
extension JwtClaims on oauth2.AccessCredentials {
|
||||
/// Get the email from the JWT claims.
|
||||
String? get email {
|
||||
final token = idToken;
|
||||
|
||||
@@ -356,7 +379,9 @@ class UserNotFoundException implements Exception {
|
||||
final String email;
|
||||
}
|
||||
|
||||
/// Extensions on Jwt for working with OAuth 2.0 providers.
|
||||
extension OauthAuthProvider on Jwt {
|
||||
/// Get the [AuthProvider] from the JWT issuer.
|
||||
AuthProvider get authProvider {
|
||||
if (payload.iss == googleJwtIssuer) {
|
||||
return AuthProvider.google;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io' hide Platform;
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// cspell:words endtemplate pubspec sideloadable bryanoltman archs sideload
|
||||
// cspell:words xcarchive codesigned xcframework
|
||||
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart'
|
||||
/// Login as a new Shorebird user.
|
||||
/// {@endtemplate}
|
||||
class LoginCommand extends ShorebirdCommand {
|
||||
/// {@macro login_command}
|
||||
LoginCommand() {
|
||||
argParser.addOption(
|
||||
'provider',
|
||||
@@ -74,6 +75,7 @@ We could not find a Shorebird account for ${error.email}.''',
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
/// Prompt the user to log in.
|
||||
void prompt(String url) {
|
||||
logger.info('''
|
||||
The Shorebird CLI needs your authorization to manage apps, releases, and patches on your behalf.
|
||||
|
||||
@@ -54,6 +54,7 @@ class IosFrameworkPatcher extends Patcher {
|
||||
@override
|
||||
double? get linkPercentage => lastBuildLinkPercentage;
|
||||
|
||||
/// The last build link percentage.
|
||||
@visibleForTesting
|
||||
double? lastBuildLinkPercentage;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// cspell:words devicectl endtemplate bryanoltman sideloadable previewable apks
|
||||
// cspell:words bundletool
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
@@ -97,11 +100,11 @@ class PreviewCommand extends ShorebirdCommand {
|
||||
appId = shorebirdYaml.appId;
|
||||
} else if (shorebirdYaml != null && flavors != null) {
|
||||
final flavorOptions = flavors.keys.toList();
|
||||
final choosenFlavor = logger.chooseOne<String>(
|
||||
final chosenFlavor = logger.chooseOne<String>(
|
||||
'Which app flavor?',
|
||||
choices: flavorOptions,
|
||||
);
|
||||
appId = flavors[choosenFlavor];
|
||||
appId = flavors[chosenFlavor];
|
||||
} else {
|
||||
appId = await promptForApp();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class AarReleaser extends Releaser {
|
||||
/// argument of the flutter build aar command.
|
||||
String get buildNumber => argResults['build-number'] as String;
|
||||
|
||||
/// The architectures to build the aar for.
|
||||
Set<Arch> get architectures => (argResults['target-platform'] as List<String>)
|
||||
.map(
|
||||
(platform) => AndroidArch.availableAndroidArchs
|
||||
|
||||
@@ -30,6 +30,7 @@ class IosFrameworkReleaser extends Releaser {
|
||||
required super.target,
|
||||
});
|
||||
|
||||
/// The directory where the release artifacts are stored.
|
||||
Directory get releaseDirectory => Directory(
|
||||
p.join(shorebirdEnv.getShorebirdProjectRoot()!.path, 'release'),
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.da
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
|
||||
/// A function that resolves a [Releaser] for a given [ReleaseType].
|
||||
typedef ResolveReleaser = Releaser Function(ReleaseType releaseType);
|
||||
|
||||
/// {@template release_command}
|
||||
@@ -150,6 +151,7 @@ of the iOS app that is using this module.''',
|
||||
return ExitCode.success.code;
|
||||
}
|
||||
|
||||
/// Returns a [Releaser] for the given [ReleaseType].
|
||||
@visibleForTesting
|
||||
Releaser getReleaser(ReleaseType releaseType) {
|
||||
switch (releaseType) {
|
||||
|
||||
@@ -16,6 +16,7 @@ class UpgradeCommand extends ShorebirdCommand {
|
||||
@override
|
||||
String get description => 'Upgrade your copy of Shorebird.';
|
||||
|
||||
/// Name of the command, exposed for the [CommandRunner].
|
||||
static const String commandName = 'upgrade';
|
||||
|
||||
@override
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:shorebird_cli/src/platform/ios.dart';
|
||||
/// A class that describes an argument from a command/sub command.
|
||||
/// {@endtemplate}
|
||||
class ArgumentDescriber {
|
||||
/// {@macro argument_describer}
|
||||
const ArgumentDescriber({
|
||||
required this.name,
|
||||
required this.description,
|
||||
@@ -53,6 +54,8 @@ Entries from "--dart-define" with identical keys take precedence over entries fr
|
||||
'''Export an IPA with these options. See "xcodebuild -h" for available exportOptionsPlist keys (iOS only).''',
|
||||
);
|
||||
|
||||
/// An argument that allows the user to specify a public key file that will be
|
||||
/// used to validate patch signatures.
|
||||
static const publicKeyArg = ArgumentDescriber(
|
||||
name: 'public-key-path',
|
||||
description: '''
|
||||
@@ -60,6 +63,8 @@ The path for a public key .pem file that will be used to validate patch signatur
|
||||
''',
|
||||
);
|
||||
|
||||
/// An argument that allows the user to specify a private key file that will
|
||||
/// be used to sign the patch artifact.
|
||||
static const privateKeyArg = ArgumentDescriber(
|
||||
name: 'private-key-path',
|
||||
description: '''
|
||||
|
||||
@@ -19,6 +19,7 @@ class ShorebirdYaml {
|
||||
this.autoUpdate,
|
||||
});
|
||||
|
||||
/// Creates a [ShorebirdYaml] from a JSON map.
|
||||
factory ShorebirdYaml.fromJson(Map<dynamic, dynamic> json) =>
|
||||
_$ShorebirdYamlFromJson(json);
|
||||
|
||||
@@ -48,7 +49,9 @@ class ShorebirdYaml {
|
||||
final bool? autoUpdate;
|
||||
}
|
||||
|
||||
/// Extension on [ShorebirdYaml] to get the app id for a specific flavor.
|
||||
extension AppIdExtension on ShorebirdYaml {
|
||||
/// Returns the app id for the given flavor.
|
||||
String getAppId({String? flavor}) {
|
||||
if (flavor == null || flavors == null) return appId;
|
||||
return flavors![flavor] ?? appId;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
|
||||
// A reference to a [EngineConfig] instance.
|
||||
/// A reference to an [EngineConfig] instance.
|
||||
final engineConfigRef = create(() => const EngineConfig.empty());
|
||||
|
||||
// The [EngineConfig] instance available in the current zone.
|
||||
/// The [EngineConfig] instance available in the current zone.
|
||||
EngineConfig get engineConfig => read(engineConfigRef);
|
||||
|
||||
class EngineConfig {
|
||||
|
||||
@@ -60,6 +60,7 @@ class Adb {
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs `adb logcat`.
|
||||
Future<Process> logcat({
|
||||
String? filter,
|
||||
String? deviceId,
|
||||
|
||||
@@ -131,6 +131,8 @@ class AotTools {
|
||||
return tryParseVersion(version) ?? noVersion;
|
||||
}
|
||||
|
||||
/// Whether the current analyze_snapshot executable supports the
|
||||
/// `--dump-debug-info` flag.
|
||||
Future<bool> isLinkDebugInfoSupported() async {
|
||||
final result = await _exec(['link', '--help']);
|
||||
return result.stdout.toString().contains('dump-debug-info');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// cspell:words bundletool
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
import 'package:shorebird_cli/src/android_sdk.dart';
|
||||
@@ -12,7 +13,9 @@ final bundletoolRef = create(Bundletool.new);
|
||||
/// The [Bundletool] instance available in the current zone.
|
||||
Bundletool get bundletool => read(bundletoolRef);
|
||||
|
||||
/// A wrapper around the `bundletool` command.
|
||||
class Bundletool {
|
||||
/// The name of the bundletool jar.
|
||||
static const jar = 'bundletool.jar';
|
||||
|
||||
Future<ShorebirdProcessResult> _exec(List<String> command) async {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:shorebird_cli/src/extensions/version.dart';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// cspell:words bundletool devicectl gradlew idevicesyslog xcodebuild
|
||||
|
||||
export 'adb.dart';
|
||||
export 'aot_tools.dart';
|
||||
export 'bundletool.dart';
|
||||
|
||||
@@ -11,6 +11,7 @@ Git get git => read(gitRef);
|
||||
|
||||
/// A wrapper around all git related functionality.
|
||||
class Git {
|
||||
/// Name of the git executable.
|
||||
static const executable = 'git';
|
||||
|
||||
/// Execute a git command with the provided [arguments].
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
@@ -16,6 +18,7 @@ class MissingAndroidProjectException implements Exception {
|
||||
/// {@macro missing_android_project_exception}
|
||||
const MissingAndroidProjectException(this.projectPath);
|
||||
|
||||
/// Expected path for the Android project.
|
||||
final String projectPath;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@@ -17,6 +17,7 @@ class PatchFailedException implements Exception {
|
||||
/// {@macro patch_failed_exception}
|
||||
PatchFailedException(this.message);
|
||||
|
||||
/// The error message.
|
||||
final String message;
|
||||
|
||||
@override
|
||||
@@ -29,6 +30,7 @@ class PatchFailedException implements Exception {
|
||||
///
|
||||
/// Throws [PatchFailedException] if the patch command exits with non-zero code.
|
||||
class PatchExecutable {
|
||||
/// Runs the `patch` executable.
|
||||
Future<void> run({
|
||||
required String releaseArtifactPath,
|
||||
required String patchArtifactPath,
|
||||
|
||||
@@ -19,6 +19,7 @@ class PackageFailedException implements Exception {
|
||||
/// {@macro package_failed_exception}
|
||||
PackageFailedException(this.message);
|
||||
|
||||
/// The error message.
|
||||
final String message;
|
||||
|
||||
@override
|
||||
@@ -37,6 +38,7 @@ class ShorebirdTools {
|
||||
return shorebirdToolsDirectory.existsSync();
|
||||
}
|
||||
|
||||
/// The directory containing the `shorebird_tools` package.
|
||||
Directory get shorebirdToolsDirectory {
|
||||
final dir = Directory(
|
||||
p.join(
|
||||
|
||||
@@ -13,6 +13,7 @@ class MissingIOSProjectException implements Exception {
|
||||
/// {@macro missing_ios_project_exception}
|
||||
const MissingIOSProjectException(this.projectPath);
|
||||
|
||||
/// Expected path of the XCode project.
|
||||
final String projectPath;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
import 'package:args/args.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
@@ -6,7 +7,6 @@ import 'package:shorebird_cli/src/common_arguments.dart';
|
||||
import 'package:shorebird_cli/src/extensions/file.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/release_type.dart';
|
||||
|
||||
import 'package:shorebird_cli/src/third_party/flutter_tools/lib/src/base/io.dart';
|
||||
|
||||
extension OptionFinder on ArgResults {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
|
||||
/// Extensions for finding `app.dill` in a [ShorebirdProcessResult].
|
||||
extension FindAppDill on ShorebirdProcessResult {
|
||||
/// Finds a line in stdout that invokes gen_snapshot with app.dill as an
|
||||
/// argument. The path to the app.dill file is the last argument in the line.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
extension NullOrEmtpy on String? {
|
||||
/// Returns `true` if this string is null or empty.
|
||||
bool get isNullOrEmpty => this == null || this!.isEmpty;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
/// Formats the given [bytes] into a human-readable string.
|
||||
String formatBytes(int bytes, {int decimals = 2}) {
|
||||
if (bytes <= 0) return '0 B';
|
||||
const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:shorebird_cli/src/logger.dart';
|
||||
|
||||
/// An http client that logs request at the verbose level.
|
||||
class LoggingClient extends http.BaseClient {
|
||||
/// Creates a new [LoggingClient] wrapping [httpClient].
|
||||
LoggingClient({required http.Client httpClient}) : _baseClient = httpClient;
|
||||
|
||||
final http.Client _baseClient;
|
||||
|
||||
@@ -10,6 +10,7 @@ http.Client retryingHttpClient(http.Client client) => RetryClient(
|
||||
whenError: isRetryableException,
|
||||
);
|
||||
|
||||
/// Returns `true` if the [exception] is a retryable exception.
|
||||
bool isRetryableException(Object exception, StackTrace _) {
|
||||
return switch (exception.runtimeType) {
|
||||
http.ClientException => true,
|
||||
@@ -21,5 +22,6 @@ bool isRetryableException(Object exception, StackTrace _) {
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns `true` if the [response] is a retryable response.
|
||||
bool isRetryableResponse(http.BaseResponse response) =>
|
||||
response.statusCode >= 500;
|
||||
|
||||
@@ -19,7 +19,7 @@ const _logFileName = 'shorebird.log';
|
||||
/// be created for every run of the Shorebird CLI, and will have the name
|
||||
/// `timestamp_shorebird.log`.
|
||||
final File currentRunLogFile = (() {
|
||||
// TODO(bryanoltman): use package:clock to test that we use the correct timestamp
|
||||
// TODO(bryanoltman): use package:clock to test for the correct timestamp
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
final file = File(
|
||||
p.join(
|
||||
|
||||
@@ -34,7 +34,7 @@ class UserCancelledException implements Exception {}
|
||||
/// A reference to a [PatchDiffChecker] instance.
|
||||
ScopedRef<PatchDiffChecker> patchDiffCheckerRef = create(PatchDiffChecker.new);
|
||||
|
||||
// The [PatchVerifier] instance available in the current zone.
|
||||
/// The [PatchDiffChecker] instance available in the current zone.
|
||||
PatchDiffChecker get patchDiffChecker => read(patchDiffCheckerRef);
|
||||
|
||||
/// {@template patch_verifier}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
|
||||
// A reference to a [Platform] instance.
|
||||
/// A reference to a [Platform] instance.
|
||||
ScopedRef<Platform> platformRef = create(() => const LocalPlatform());
|
||||
|
||||
// The [Platform] instance available in the current zone.
|
||||
/// The [Platform] instance available in the current zone.
|
||||
Platform get platform => read(platformRef, orElse: () => const LocalPlatform());
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
|
||||
import 'package:shorebird_cli/src/engine_config.dart';
|
||||
import 'package:shorebird_cli/src/platform/platform.dart';
|
||||
|
||||
/// Extensions for working with Android architectures.
|
||||
extension AndroidArch on Arch {
|
||||
/// The name of the architecture as expected by the --target-platforms flag.
|
||||
String get targetPlatformCliArg {
|
||||
@@ -40,6 +41,7 @@ extension AndroidArch on Arch {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the available Android architectures.
|
||||
static Iterable<Arch> get availableAndroidArchs {
|
||||
if (engineConfig.localEngine != null) {
|
||||
final localEngineOutName = engineConfig.localEngine!;
|
||||
@@ -61,6 +63,7 @@ extension AndroidArch on Arch {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions for working with Android target platforms.
|
||||
extension TargetPlatformArgs on Iterable<Arch> {
|
||||
/// The value to pass to the --target-platforms flag.
|
||||
String get targetPlatformArg => map((e) => e.targetPlatformCliArg).join(',');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
|
||||
@@ -7,8 +7,13 @@ export 'ios.dart';
|
||||
/// Note: in addition to these values, the Flutter tooling also supports x86.
|
||||
/// {@endtemplate}
|
||||
enum Arch {
|
||||
/// 32-bit ARM architecture.
|
||||
arm32(arch: 'arm'),
|
||||
|
||||
/// 64-bit ARM architecture.
|
||||
arm64(arch: 'aarch64'),
|
||||
|
||||
/// 64-bit x86 architecture.
|
||||
x86_64(arch: 'x86_64');
|
||||
|
||||
/// {@macro arch}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
@@ -11,12 +13,16 @@ import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
|
||||
/// Thrown when multiple artifacts are found in the build directory.
|
||||
class MultipleArtifactsFoundException implements Exception {
|
||||
/// Creates a [MultipleArtifactsFoundException].
|
||||
MultipleArtifactsFoundException({
|
||||
required this.buildDir,
|
||||
required this.foundArtifacts,
|
||||
});
|
||||
|
||||
/// The build directory where the artifacts were found.
|
||||
final String buildDir;
|
||||
|
||||
/// The list of found artifacts.
|
||||
final List<FileSystemEntity> foundArtifacts;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:io' hide Platform;
|
||||
|
||||
import 'package:checked_yaml/checked_yaml.dart';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import 'package:shorebird_cli/src/engine_config.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
|
||||
// A reference to a [ShorebirdProcess] instance.
|
||||
/// A reference to a [ShorebirdProcess] instance.
|
||||
final processRef = create(ShorebirdProcess.new);
|
||||
|
||||
// The [ShorebirdProcess] instance available in the current zone.
|
||||
/// The [ShorebirdProcess] instance available in the current zone.
|
||||
ShorebirdProcess get process => read(processRef);
|
||||
|
||||
/// A wrapper around [Process] that replaces executables to Shorebird-vended
|
||||
@@ -17,12 +17,15 @@ ShorebirdProcess get process => read(processRef);
|
||||
// This may need a better name, since it returns "Process" it's more a
|
||||
// "ProcessFactory" than a "Process".
|
||||
class ShorebirdProcess {
|
||||
/// Creates a ShorebirdProcess.
|
||||
ShorebirdProcess({
|
||||
ProcessWrapper? processWrapper, // For mocking ShorebirdProcess.
|
||||
}) : processWrapper = processWrapper ?? ProcessWrapper();
|
||||
|
||||
/// The underlying process wrapper.
|
||||
final ProcessWrapper processWrapper;
|
||||
|
||||
/// Runs the process and returns the result.
|
||||
Future<ShorebirdProcessResult> run(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
@@ -62,6 +65,7 @@ class ShorebirdProcess {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Runs the process synchronously and returns the result.
|
||||
ShorebirdProcessResult runSync(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
@@ -101,6 +105,7 @@ class ShorebirdProcess {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Starts a new process running the executable with the specified arguments.
|
||||
Future<Process> start(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
@@ -222,21 +227,30 @@ $stderr''');
|
||||
}
|
||||
}
|
||||
|
||||
/// Result from running a process.
|
||||
class ShorebirdProcessResult {
|
||||
/// Creates a new [ShorebirdProcessResult].
|
||||
const ShorebirdProcessResult({
|
||||
required this.exitCode,
|
||||
required this.stdout,
|
||||
required this.stderr,
|
||||
});
|
||||
|
||||
/// The exit code of the process.
|
||||
final int exitCode;
|
||||
|
||||
/// The standard output of the process.
|
||||
final dynamic stdout;
|
||||
|
||||
/// The standard error of the process.
|
||||
final dynamic stderr;
|
||||
}
|
||||
|
||||
/// A wrapper around [Process] that can be mocked for testing.
|
||||
// coverage:ignore-start
|
||||
@visibleForTesting
|
||||
class ProcessWrapper {
|
||||
/// Runs the process and returns the result.
|
||||
Future<ShorebirdProcessResult> run(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
@@ -258,6 +272,7 @@ class ProcessWrapper {
|
||||
);
|
||||
}
|
||||
|
||||
/// Runs the process synchronously and returns the result.
|
||||
ShorebirdProcessResult runSync(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
@@ -279,6 +294,7 @@ class ProcessWrapper {
|
||||
);
|
||||
}
|
||||
|
||||
/// Starts a new process running the executable with the specified arguments.
|
||||
Future<Process> start(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/// Shorebird Web Console URLs.
|
||||
class ShorebirdWebConsole {
|
||||
/// Returns a [Uri] for the Shorebird Web Console.
|
||||
static Uri uri(String path) {
|
||||
return Uri.parse('https://console.shorebird.dev/$path');
|
||||
}
|
||||
|
||||
/// Returns a [Uri] for the Shorebird Web Console login page.
|
||||
static Uri appReleaseUri(
|
||||
String appId,
|
||||
int releaseId,
|
||||
|
||||
+6
@@ -1,9 +1,15 @@
|
||||
/// An exception thrown when a process fails.
|
||||
class ProcessExit implements Exception {
|
||||
/// Creates a new [ProcessExit] with the given [exitCode].
|
||||
ProcessExit(this.exitCode, {this.immediate = false});
|
||||
|
||||
/// Whether the process exited immediately.
|
||||
final bool immediate;
|
||||
|
||||
/// The exit code of the process.
|
||||
final int exitCode;
|
||||
|
||||
/// The message associated with the exception.
|
||||
String get message => 'ProcessExit: $exitCode';
|
||||
|
||||
@override
|
||||
|
||||
+3
-2
@@ -10,7 +10,8 @@ import 'package:xml/xml.dart';
|
||||
///
|
||||
/// See https://github.com/shorebirdtech/shorebird/issues/160.
|
||||
class AndroidInternetPermissionValidator extends Validator {
|
||||
final String mainAndroidManifestPath = p.join(
|
||||
/// Path to the main AndroidManifest.xml file.
|
||||
final String _mainAndroidManifestPath = p.join(
|
||||
'android',
|
||||
'app',
|
||||
'src',
|
||||
@@ -55,7 +56,7 @@ The command you are running must be run within a Flutter app project that suppor
|
||||
ValidationIssue(
|
||||
severity: ValidationIssueSeverity.error,
|
||||
message:
|
||||
'$mainAndroidManifestPath is missing the INTERNET permission.',
|
||||
'$_mainAndroidManifestPath is missing the INTERNET permission.',
|
||||
fix: () => _addInternetPermissionToFile(manifestFilePath),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -5,7 +5,9 @@ import 'package:shorebird_cli/src/shorebird_flutter.dart';
|
||||
import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart';
|
||||
import 'package:shorebird_cli/src/validators/validators.dart';
|
||||
|
||||
/// An exception thrown when a validation issue is found.
|
||||
class FlutterValidationException implements Exception {
|
||||
/// Creates a new [FlutterValidationException] with the provided [message].
|
||||
const FlutterValidationException(this.message);
|
||||
|
||||
/// The message associated with the exception.
|
||||
@@ -15,6 +17,7 @@ class FlutterValidationException implements Exception {
|
||||
String toString() => 'FlutterValidationException: $message';
|
||||
}
|
||||
|
||||
/// An exception thrown when a command is not found.
|
||||
class CommandNotFoundException implements Exception {}
|
||||
|
||||
/// {@template shorebird_flutter_validator}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:shorebird_cli/src/validators/validators.dart';
|
||||
|
||||
/// Verifies that the currently installed version of Shorebird is the latest.
|
||||
class ShorebirdVersionValidator extends Validator {
|
||||
/// Creates a new [ShorebirdVersionValidator].
|
||||
ShorebirdVersionValidator();
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// cspell:words googleapis
|
||||
import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart';
|
||||
import 'package:shorebird_cli/src/validators/validators.dart';
|
||||
|
||||
/// Verifies that the user has access to storage.googleapis.com.
|
||||
class StorageAccessValidator extends Validator {
|
||||
@override
|
||||
String get description => 'Has access to storage.googleapis.com';
|
||||
|
||||
@@ -11,17 +11,19 @@ export 'shorebird_yaml_asset_validator.dart';
|
||||
export 'storage_access_validator.dart';
|
||||
|
||||
/// Severity level of a [ValidationIssue].
|
||||
///
|
||||
/// [error]s will prevent code push from working and block releases, builds,
|
||||
/// patches, etc.
|
||||
/// [warning]s should be fixed before releasing your app, but are not as urgent.
|
||||
enum ValidationIssueSeverity {
|
||||
/// [error]s will prevent code push from working and block releases, builds,
|
||||
/// patches, etc.
|
||||
error,
|
||||
|
||||
/// [warning]s should be fixed before releasing your app, but are not as
|
||||
/// urgent.
|
||||
warning,
|
||||
}
|
||||
|
||||
/// Display helpers for printing [ValidationIssue]s.
|
||||
extension Display on ValidationIssueSeverity {
|
||||
/// The raw string representation of this severity.
|
||||
String get rawLeading {
|
||||
switch (this) {
|
||||
case ValidationIssueSeverity.error:
|
||||
@@ -31,6 +33,7 @@ extension Display on ValidationIssueSeverity {
|
||||
}
|
||||
}
|
||||
|
||||
/// The colorized string representation of this severity.
|
||||
String get displayLeading {
|
||||
switch (this) {
|
||||
case ValidationIssueSeverity.error:
|
||||
@@ -44,6 +47,7 @@ extension Display on ValidationIssueSeverity {
|
||||
/// A (potential) problem with the current Shorebird installation or project.
|
||||
@immutable
|
||||
class ValidationIssue {
|
||||
/// Creates a new [ValidationIssue].
|
||||
const ValidationIssue({
|
||||
required this.severity,
|
||||
required this.message,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// cspell:words xcarchive xcarchives xcframeworks xcframework
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
|
||||
import 'package:shorebird_cli/src/platform.dart';
|
||||
|
||||
Reference in New Issue
Block a user