Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
353add83
Unverified
Commit
353add83
authored
Jan 25, 2021
by
Christopher Fujino
Committed by
GitHub
Jan 25, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Re-land codesign test improvement (#73997)
parent
35000147
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1186 additions
and
385 deletions
+1186
-385
codesign.dart
dev/bots/codesign.dart
+17
-177
test.dart
dev/bots/test.dart
+1
-1
conductor.dart
dev/tools/bin/conductor.dart
+16
-23
codesign.dart
dev/tools/lib/codesign.dart
+369
-0
git.dart
dev/tools/lib/git.dart
+1
-0
globals.dart
dev/tools/lib/globals.dart
+62
-0
repository.dart
dev/tools/lib/repository.dart
+151
-118
roll_dev.dart
dev/tools/lib/roll_dev.dart
+9
-13
stdio.dart
dev/tools/lib/stdio.dart
+10
-4
codesign_integration_test.dart
dev/tools/test/codesign_integration_test.dart
+72
-0
codesign_test.dart
dev/tools/test/codesign_test.dart
+416
-0
common.dart
dev/tools/test/common.dart
+1
-0
roll_dev_integration_test.dart
dev/tools/test/roll_dev_integration_test.dart
+11
-16
roll_dev_test.dart
dev/tools/test/roll_dev_test.dart
+50
-33
No files found.
dev/bots/codesign.dart
View file @
353add83
...
...
@@ -2,184 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:io'
;
import
'dart:io'
as
io
;
import
'package:path/path.dart'
as
path
;
String
get
repoRoot
=>
path
.
normalize
(
path
.
join
(
path
.
dirname
(
Platform
.
script
.
toFilePath
()),
'..'
,
'..'
));
String
get
cacheDirectory
=>
path
.
normalize
(
path
.
join
(
repoRoot
,
'bin'
,
'cache'
));
/// Check mime-type of file at [filePath] to determine if it is binary
bool
isBinary
(
String
filePath
)
{
final
ProcessResult
result
=
Process
.
runSync
(
'file'
,
<
String
>[
'--mime-type'
,
'-b'
,
// is binary
filePath
,
],
);
return
(
result
.
stdout
as
String
).
contains
(
'application/x-mach-binary'
);
}
/// Find every binary file in the given [rootDirectory]
List
<
String
>
findBinaryPaths
([
String
rootDirectory
])
{
rootDirectory
??=
cacheDirectory
;
final
ProcessResult
result
=
Process
.
runSync
(
'find'
,
<
String
>[
rootDirectory
,
'-type'
,
'f'
,
'-perm'
,
'+111'
,
// is executable
],
// TODO(fujino): delete this script once PR #71244 lands on stable.
void
main
(
List
<
String
>
args
)
{
final
String
scriptPath
=
io
.
Platform
.
script
.
toFilePath
();
final
String
scriptDir
=
path
.
dirname
(
scriptPath
);
final
String
repoRoot
=
path
.
normalize
(
path
.
join
(
scriptDir
,
'..'
,
'..'
));
final
io
.
ProcessResult
result
=
io
.
Process
.
runSync
(
path
.
join
(
repoRoot
,
'dev'
,
'tools'
,
'bin'
,
'conductor'
),
<
String
>[
'codesign'
,
'--verify'
],
);
final
List
<
String
>
allFiles
=
(
result
.
stdout
as
String
).
split
(
'
\n
'
).
where
((
String
s
)
=>
s
.
isNotEmpty
).
toList
();
return
allFiles
.
where
(
isBinary
).
toList
();
}
/// Given the path to a stamp file, read the contents.
///
/// Will throw if the file doesn't exist.
String
readStamp
(
String
filePath
)
{
final
File
file
=
File
(
filePath
);
if
(!
file
.
existsSync
())
{
throw
'Error! Stamp file
$filePath
does not exist!'
;
}
return
file
.
readAsStringSync
().
trim
();
}
/// Return whether or not the flutter cache is up to date.
bool
checkCacheIsCurrent
(
)
{
try
{
final
String
dartSdkStamp
=
readStamp
(
path
.
join
(
cacheDirectory
,
'engine-dart-sdk.stamp'
));
final
String
engineVersion
=
readStamp
(
path
.
join
(
repoRoot
,
'bin'
,
'internal'
,
'engine.version'
));
return
dartSdkStamp
==
engineVersion
;
}
catch
(
e
)
{
print
(
e
);
return
false
;
}
}
List
<
String
>
get
binariesWithEntitlements
=>
List
<
String
>.
unmodifiable
(<
String
>[
'ideviceinfo'
,
'idevicename'
,
'idevicescreenshot'
,
'idevicesyslog'
,
'libimobiledevice.6.dylib'
,
'libplist.3.dylib'
,
'iproxy'
,
'libusbmuxd.4.dylib'
,
'libssl.1.0.0.dylib'
,
'libcrypto.1.0.0.dylib'
,
'libzip.5.0.dylib'
,
'libzip.5.dylib'
,
'gen_snapshot'
,
'dart'
,
'flutter_tester'
,
'gen_snapshot_arm64'
,
'gen_snapshot_armv7'
,
]);
List
<
String
>
get
expectedEntitlements
=>
List
<
String
>.
unmodifiable
(<
String
>[
'com.apple.security.cs.allow-jit'
,
'com.apple.security.cs.allow-unsigned-executable-memory'
,
'com.apple.security.cs.allow-dyld-environment-variables'
,
'com.apple.security.network.client'
,
'com.apple.security.network.server'
,
'com.apple.security.cs.disable-library-validation'
,
]);
/// Check if the binary has the expected entitlements.
bool
hasExpectedEntitlements
(
String
binaryPath
)
{
try
{
final
ProcessResult
entitlementResult
=
Process
.
runSync
(
'codesign'
,
<
String
>[
'--display'
,
'--entitlements'
,
':-'
,
binaryPath
,
],
);
if
(
entitlementResult
.
exitCode
!=
0
)
{
print
(
'The `codesign --entitlements` command failed with exit code
${entitlementResult.exitCode}
:
\n
'
'
${entitlementResult.stderr}
\n
'
);
return
false
;
}
bool
passes
=
true
;
final
String
output
=
entitlementResult
.
stdout
as
String
;
for
(
final
String
entitlement
in
expectedEntitlements
)
{
final
bool
entitlementExpected
=
binariesWithEntitlements
.
contains
(
path
.
basename
(
binaryPath
));
if
(
output
.
contains
(
entitlement
)
!=
entitlementExpected
)
{
print
(
'File "
$binaryPath
"
${entitlementExpected ? 'does not have expected' : 'has unexpected'}
entitlement
$entitlement
.'
);
passes
=
false
;
}
}
return
passes
;
}
catch
(
e
)
{
print
(
e
);
return
false
;
}
}
void
main
(
)
{
if
(!
Platform
.
isMacOS
)
{
print
(
'Error! Expected operating system "macos", actual operating system '
'is: "
${Platform.operatingSystem}
"'
);
exit
(
1
);
}
if
(!
checkCacheIsCurrent
())
{
print
(
'Warning! Your cache is either not present or not matching your flutter
\n
'
'version. Run a `flutter` command to update your cache, and re-try this
\n
'
'test.'
);
exit
(
1
);
}
final
List
<
String
>
unsignedBinaries
=
<
String
>[];
final
List
<
String
>
wrongEntitlementBinaries
=
<
String
>[];
for
(
final
String
binaryPath
in
findBinaryPaths
(
cacheDirectory
))
{
print
(
'Verifying the code signature of
$binaryPath
'
);
final
ProcessResult
codeSignResult
=
Process
.
runSync
(
'codesign'
,
<
String
>[
'-vvv'
,
binaryPath
,
],
);
if
(
codeSignResult
.
exitCode
!=
0
)
{
unsignedBinaries
.
add
(
binaryPath
);
print
(
'File "
$binaryPath
" does not appear to be codesigned.
\n
'
'The `codesign` command failed with exit code
${codeSignResult.exitCode}
:
\n
'
'
${codeSignResult.stderr}
\n
'
);
continue
;
}
else
{
print
(
'Verifying entitlements of
$binaryPath
'
);
if
(!
hasExpectedEntitlements
(
binaryPath
))
{
wrongEntitlementBinaries
.
add
(
binaryPath
);
}
}
}
if
(
unsignedBinaries
.
isNotEmpty
)
{
print
(
'Found
${unsignedBinaries.length}
unsigned binaries:'
);
unsignedBinaries
.
forEach
(
print
);
}
if
(
wrongEntitlementBinaries
.
isNotEmpty
)
{
print
(
'Found
${wrongEntitlementBinaries.length}
binaries with unexpected entitlements:'
);
wrongEntitlementBinaries
.
forEach
(
print
);
}
if
(
unsignedBinaries
.
isNotEmpty
)
{
// TODO(jmagman): Also exit if `wrongEntitlementBinaries.isNotEmpty` after https://github.com/flutter/flutter/issues/46704 is done.
exit
(
1
);
}
print
(
'Verified that binaries are codesigned and have expected entitlements.'
);
if
(
result
.
exitCode
!=
0
)
{
print
(
'codesign script exited with code
$result
.exitCode'
);
print
(
'stdout:
\n
${result.stdout}
\n
'
);
print
(
'stderr:
\n
${result.stderr}
\n
'
);
io
.
exit
(
1
);
}
print
(
'codesign script succeeded.'
);
print
(
'stdout:
\n
${result.stdout}
'
);
}
dev/bots/test.dart
View file @
353add83
...
...
@@ -662,7 +662,7 @@ Future<void> _runFrameworkTests() async {
await
_pubRunTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'bots'
));
await
_pubRunTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'devicelab'
));
await
_pubRunTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'snippets'
));
await
_pubRunTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'tools'
));
await
_pubRunTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'tools'
)
,
forceSingleCore:
true
);
await
_runFlutterTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'integration_tests'
,
'android_semantics_testing'
));
await
_runFlutterTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'manual_tests'
));
await
_runFlutterTest
(
path
.
join
(
flutterRoot
,
'dev'
,
'tools'
,
'vitool'
));
...
...
dev/tools/bin/conductor.dart
View file @
353add83
...
...
@@ -10,15 +10,17 @@
import
'dart:io'
as
io
;
import
'package:args/command_runner.dart'
;
import
'package:dev_tools/codesign.dart'
;
import
'package:dev_tools/globals.dart'
;
import
'package:dev_tools/roll_dev.dart'
;
import
'package:dev_tools/repository.dart'
;
import
'package:dev_tools/stdio.dart'
;
import
'package:file/file.dart'
;
import
'package:file/local.dart'
;
import
'package:platform/platform.dart'
;
import
'package:process/process.dart'
;
import
'package:dev_tools/repository.dart'
;
import
'package:dev_tools/roll_dev.dart'
;
import
'package:dev_tools/stdio.dart'
;
void
main
(
List
<
String
>
args
)
{
Future
<
void
>
main
(
List
<
String
>
args
)
async
{
const
FileSystem
fileSystem
=
LocalFileSystem
();
const
ProcessManager
processManager
=
LocalProcessManager
();
const
Platform
platform
=
LocalPlatform
();
...
...
@@ -29,9 +31,12 @@ void main(List<String> args) {
);
final
Checkouts
checkouts
=
Checkouts
(
fileSystem:
fileSystem
,
parentDirectory:
localFlutterRoot
.
parent
,
platform:
platform
,
processManager:
processManager
,
stdio:
stdio
,
);
final
CommandRunner
<
void
>
runner
=
CommandRunner
<
void
>(
'conductor'
,
'A tool for coordinating Flutter releases.'
,
...
...
@@ -39,17 +44,16 @@ void main(List<String> args) {
);
<
Command
<
void
>>[
RollDev
(
RollDevCommand
(
checkouts:
checkouts
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
checkouts
.
addRepo
(
fileSystem:
fileSystem
,
platform:
platform
,
repoType:
RepositoryType
.
framework
,
stdio:
stdio
,
),
stdio:
stdio
,
),
CodesignCommand
(
checkouts:
checkouts
,
flutterRoot:
localFlutterRoot
,
),
].
forEach
(
runner
.
addCommand
);
if
(!
assertsEnabled
())
{
...
...
@@ -58,20 +62,9 @@ void main(List<String> args) {
}
try
{
runner
.
run
(
args
);
await
runner
.
run
(
args
);
}
on
Exception
catch
(
e
)
{
stdio
.
printError
(
e
.
toString
());
io
.
exit
(
1
);
}
}
bool
assertsEnabled
(
)
{
// Verify asserts enabled
bool
assertsEnabled
=
false
;
assert
(()
{
assertsEnabled
=
true
;
return
true
;
}());
return
assertsEnabled
;
}
dev/tools/lib/codesign.dart
0 → 100644
View file @
353add83
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:io'
as
io
;
import
'package:args/command_runner.dart'
;
import
'package:file/file.dart'
;
import
'package:meta/meta.dart'
;
import
'package:platform/platform.dart'
;
import
'package:process/process.dart'
;
import
'./globals.dart'
;
import
'./repository.dart'
;
import
'./stdio.dart'
;
const
List
<
String
>
expectedEntitlements
=
<
String
>[
'com.apple.security.cs.allow-jit'
,
'com.apple.security.cs.allow-unsigned-executable-memory'
,
'com.apple.security.cs.allow-dyld-environment-variables'
,
'com.apple.security.network.client'
,
'com.apple.security.network.server'
,
'com.apple.security.cs.disable-library-validation'
,
];
const
String
kVerify
=
'verify'
;
const
String
kSignatures
=
'signatures'
;
const
String
kRevision
=
'revision'
;
const
String
kUpstream
=
'upstream'
;
/// Command to codesign and verify the signatures of cached binaries.
class
CodesignCommand
extends
Command
<
void
>
{
CodesignCommand
({
@required
this
.
checkouts
,
@required
this
.
flutterRoot
,
})
:
assert
(
flutterRoot
!=
null
),
fileSystem
=
checkouts
.
fileSystem
,
platform
=
checkouts
.
platform
,
stdio
=
checkouts
.
stdio
,
processManager
=
checkouts
.
processManager
{
argParser
.
addFlag
(
kVerify
,
help:
'Only verify expected binaries exist and are codesigned with entitlements.'
,
);
argParser
.
addFlag
(
kSignatures
,
defaultsTo:
true
,
help:
'When off, this command will only verify the existence of binaries, and not their
\n
'
'signatures or entitlements. Must be used with --verify flag.'
,
);
argParser
.
addOption
(
kUpstream
,
defaultsTo:
FrameworkRepository
.
defaultUpstream
,
help:
'The git remote URL to use as the Flutter framework
\'
s upstream.'
,
);
argParser
.
addOption
(
kRevision
,
help:
'The Flutter framework revision to use.'
,
);
}
final
Checkouts
checkouts
;
final
FileSystem
fileSystem
;
final
Platform
platform
;
final
ProcessManager
processManager
;
final
Stdio
stdio
;
/// Root directory of the Flutter repository.
final
Directory
flutterRoot
;
FrameworkRepository
_framework
;
FrameworkRepository
get
framework
=>
_framework
??=
FrameworkRepository
.
localRepoAsUpstream
(
checkouts
,
upstreamPath:
flutterRoot
.
path
,
);
@visibleForTesting
set
framework
(
FrameworkRepository
framework
)
=>
_framework
=
framework
;
@override
String
get
name
=>
'codesign'
;
@override
String
get
description
=>
'For codesigning and verifying the signatures of engine binaries.'
;
@override
void
run
()
{
if
(!
platform
.
isMacOS
)
{
throw
ConductorException
(
'Error! Expected operating system "macos", actual operating system is: '
'"
${platform.operatingSystem}
"'
);
}
if
(
argResults
[
'verify'
]
as
bool
!=
true
)
{
throw
ConductorException
(
'Sorry, but codesigning is not implemented yet. Please pass the '
'--
$kVerify
flag to verify signatures.'
);
}
String
revision
;
if
(
argResults
.
wasParsed
(
kRevision
))
{
stdio
.
printError
(
'Warning! When providing an arbitrary revision, the contents of the cache may not'
);
stdio
.
printError
(
'match the expected binaries in the conductor tool. It is preferred to check out'
);
stdio
.
printError
(
'the desired revision and run that version of the conductor.
\n
'
);
revision
=
argResults
[
kRevision
]
as
String
;
}
else
{
revision
=
(
processManager
.
runSync
(
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
],
workingDirectory:
framework
.
checkoutDirectory
.
path
,
).
stdout
as
String
).
trim
();
assert
(
revision
.
isNotEmpty
);
}
framework
.
checkout
(
revision
);
// Ensure artifacts present
framework
.
runFlutter
(<
String
>[
'precache'
,
'--android'
,
'--ios'
,
'--macos'
]);
verifyExist
();
if
(
argResults
[
kSignatures
]
as
bool
)
{
verifySignatures
();
}
}
/// Binaries that are expected to be codesigned and have entitlements.
///
/// This list should be kept in sync with the actual contents of Flutter's
/// cache.
List
<
String
>
get
binariesWithEntitlements
{
return
<
String
>[
'artifacts/engine/android-arm-profile/darwin-x64/gen_snapshot'
,
'artifacts/engine/android-arm-release/darwin-x64/gen_snapshot'
,
'artifacts/engine/android-arm64-profile/darwin-x64/gen_snapshot'
,
'artifacts/engine/android-arm64-release/darwin-x64/gen_snapshot'
,
'artifacts/engine/android-x64-profile/darwin-x64/gen_snapshot'
,
'artifacts/engine/android-x64-release/darwin-x64/gen_snapshot'
,
'artifacts/engine/darwin-x64-profile/gen_snapshot'
,
'artifacts/engine/darwin-x64-release/gen_snapshot'
,
'artifacts/engine/darwin-x64/flutter_tester'
,
'artifacts/engine/darwin-x64/gen_snapshot'
,
'artifacts/engine/ios-profile/gen_snapshot_arm64'
,
'artifacts/engine/ios-profile/gen_snapshot_armv7'
,
'artifacts/engine/ios-release/gen_snapshot_arm64'
,
'artifacts/engine/ios-release/gen_snapshot_armv7'
,
'artifacts/engine/ios/gen_snapshot_arm64'
,
'artifacts/engine/ios/gen_snapshot_armv7'
,
'artifacts/ios-deploy/ios-deploy'
,
'artifacts/libimobiledevice/idevicescreenshot'
,
'artifacts/libimobiledevice/idevicesyslog'
,
'artifacts/libimobiledevice/libimobiledevice-1.0.6.dylib'
,
'artifacts/libplist/libplist-2.0.3.dylib'
,
'artifacts/openssl/libcrypto.1.1.dylib'
,
'artifacts/openssl/libssl.1.1.dylib'
,
'artifacts/usbmuxd/iproxy'
,
'artifacts/usbmuxd/libusbmuxd-2.0.6.dylib'
,
'dart-sdk/bin/dart'
,
'dart-sdk/bin/dartaotruntime'
,
'dart-sdk/bin/utils/gen_snapshot'
,
].
map
((
String
relativePath
)
=>
fileSystem
.
path
.
join
(
framework
.
cacheDirectory
,
relativePath
)).
toList
();
}
/// Binaries that are only expected to be codesigned.
///
/// This list should be kept in sync with the actual contents of Flutter's
/// cache.
List
<
String
>
get
binariesWithoutEntitlements
{
return
<
String
>[
'artifacts/engine/darwin-x64-profile/FlutterMacOS.framework/Versions/A/FlutterMacOS'
,
'artifacts/engine/darwin-x64-release/FlutterMacOS.framework/Versions/A/FlutterMacOS'
,
'artifacts/engine/darwin-x64/FlutterMacOS.framework/Versions/A/FlutterMacOS'
,
'artifacts/engine/darwin-x64/font-subset'
,
'artifacts/engine/ios-profile/Flutter.xcframework/ios-armv7_arm64/Flutter.framework/Flutter'
,
'artifacts/engine/ios-profile/Flutter.xcframework/ios-x86_64-simulator/Flutter.framework/Flutter'
,
'artifacts/engine/ios-release/Flutter.xcframework/ios-armv7_arm64/Flutter.framework/Flutter'
,
'artifacts/engine/ios-release/Flutter.xcframework/ios-x86_64-simulator/Flutter.framework/Flutter'
,
'artifacts/engine/ios/Flutter.xcframework/ios-armv7_arm64/Flutter.framework/Flutter'
,
'artifacts/engine/ios/Flutter.xcframework/ios-x86_64-simulator/Flutter.framework/Flutter'
,
].
map
((
String
relativePath
)
=>
fileSystem
.
path
.
join
(
framework
.
cacheDirectory
,
relativePath
)).
toList
();
}
/// Verify the existence of all expected binaries in cache.
///
/// This function ignores code signatures and entitlements, and is intended to
/// be run on every commit. It should throw if either new binaries are added
/// to the cache or expected binaries removed. In either case, this class'
/// [binariesWithEntitlements] or [binariesWithoutEntitlements] lists should
/// be updated accordingly.
@visibleForTesting
void
verifyExist
()
{
final
Set
<
String
>
foundFiles
=
<
String
>{};
for
(
final
String
binaryPath
in
findBinaryPaths
(
framework
.
cacheDirectory
))
{
if
(
binariesWithEntitlements
.
contains
(
binaryPath
))
{
foundFiles
.
add
(
binaryPath
);
}
else
if
(
binariesWithoutEntitlements
.
contains
(
binaryPath
))
{
foundFiles
.
add
(
binaryPath
);
}
else
{
throw
ConductorException
(
'Found unexpected binary in cache:
$binaryPath
'
);
}
}
final
List
<
String
>
allExpectedFiles
=
binariesWithEntitlements
+
binariesWithoutEntitlements
;
if
(
foundFiles
.
length
<
allExpectedFiles
.
length
)
{
final
List
<
String
>
unfoundFiles
=
allExpectedFiles
.
where
(
(
String
file
)
=>
!
foundFiles
.
contains
(
file
),
).
toList
();
stdio
.
printError
(
'Expected binaries not found in cache:
\n\n
${unfoundFiles.join('\n')}
\n
'
);
stdio
.
printError
(
'If this commit is removing binaries from the cache, this test should be fixed by'
);
stdio
.
printError
(
'removing the relevant entry from either the `binariesWithEntitlements` or'
);
stdio
.
printError
(
'`binariesWithoutEntitlements` getters in dev/tools/lib/codesign.dart.'
);
throw
ConductorException
(
'Did not find all expected binaries!'
);
}
stdio
.
printStatus
(
'All expected binaries present.'
);
}
/// Verify code signatures and entitlements of all binaries in the cache.
@visibleForTesting
void
verifySignatures
()
{
final
List
<
String
>
unsignedBinaries
=
<
String
>[];
final
List
<
String
>
wrongEntitlementBinaries
=
<
String
>[];
final
List
<
String
>
unexpectedBinaries
=
<
String
>[];
for
(
final
String
binaryPath
in
findBinaryPaths
(
framework
.
cacheDirectory
))
{
bool
verifySignature
=
false
;
bool
verifyEntitlements
=
false
;
if
(
binariesWithEntitlements
.
contains
(
binaryPath
))
{
verifySignature
=
true
;
verifyEntitlements
=
true
;
}
if
(
binariesWithoutEntitlements
.
contains
(
binaryPath
))
{
verifySignature
=
true
;
}
if
(!
verifySignature
&&
!
verifyEntitlements
)
{
unexpectedBinaries
.
add
(
binaryPath
);
stdio
.
printError
(
'Unexpected binary
$binaryPath
found in cache!'
);
continue
;
}
stdio
.
printTrace
(
'Verifying the code signature of
$binaryPath
'
);
final
io
.
ProcessResult
codeSignResult
=
processManager
.
runSync
(
<
String
>[
'codesign'
,
'-vvv'
,
binaryPath
,
],
);
if
(
codeSignResult
.
exitCode
!=
0
)
{
unsignedBinaries
.
add
(
binaryPath
);
stdio
.
printError
(
'File "
$binaryPath
" does not appear to be codesigned.
\n
'
'The `codesign` command failed with exit code
${codeSignResult.exitCode}
:
\n
'
'
${codeSignResult.stderr}
\n
'
);
continue
;
}
if
(
verifyEntitlements
)
{
stdio
.
printTrace
(
'Verifying entitlements of
$binaryPath
'
);
if
(!
hasExpectedEntitlements
(
binaryPath
))
{
wrongEntitlementBinaries
.
add
(
binaryPath
);
}
}
}
// First print all deviations from expectations
if
(
unsignedBinaries
.
isNotEmpty
)
{
stdio
.
printError
(
'Found
${unsignedBinaries.length}
unsigned binaries:'
);
unsignedBinaries
.
forEach
(
print
);
}
if
(
wrongEntitlementBinaries
.
isNotEmpty
)
{
stdio
.
printError
(
'Found
${wrongEntitlementBinaries.length}
binaries with unexpected entitlements:'
);
wrongEntitlementBinaries
.
forEach
(
print
);
}
if
(
unexpectedBinaries
.
isNotEmpty
)
{
stdio
.
printError
(
'Found
${unexpectedBinaries.length}
unexpected binaries in the cache:'
);
unexpectedBinaries
.
forEach
(
print
);
}
// Finally, exit on any invalid state
if
(
unsignedBinaries
.
isNotEmpty
)
{
throw
ConductorException
(
'Test failed because unsigned binaries detected.'
);
}
if
(
wrongEntitlementBinaries
.
isNotEmpty
)
{
throw
ConductorException
(
'Test failed because files found with the wrong entitlements:
\n
'
'
${wrongEntitlementBinaries.join('\n')}
'
);
}
if
(
unexpectedBinaries
.
isNotEmpty
)
{
throw
ConductorException
(
'Test failed because unexpected binaries found in the cache.'
);
}
stdio
.
printStatus
(
'Verified that binaries for commit
${argResults[kRevision] as String}
are codesigned and have '
'expected entitlements.'
);
}
List
<
String
>
_allBinaryPaths
;
/// Find every binary file in the given [rootDirectory].
List
<
String
>
findBinaryPaths
(
String
rootDirectory
)
{
if
(
_allBinaryPaths
!=
null
)
{
return
_allBinaryPaths
;
}
final
io
.
ProcessResult
result
=
processManager
.
runSync
(
<
String
>[
'find'
,
rootDirectory
,
'-type'
,
'f'
,
],
);
final
List
<
String
>
allFiles
=
(
result
.
stdout
as
String
)
.
split
(
'
\n
'
)
.
where
((
String
s
)
=>
s
.
isNotEmpty
)
.
toList
();
_allBinaryPaths
=
allFiles
.
where
(
isBinary
).
toList
();
return
_allBinaryPaths
;
}
/// Check mime-type of file at [filePath] to determine if it is binary.
bool
isBinary
(
String
filePath
)
{
final
io
.
ProcessResult
result
=
processManager
.
runSync
(
<
String
>[
'file'
,
'--mime-type'
,
'-b'
,
// is binary
filePath
,
],
);
return
(
result
.
stdout
as
String
).
contains
(
'application/x-mach-binary'
);
}
/// Check if the binary has the expected entitlements.
bool
hasExpectedEntitlements
(
String
binaryPath
)
{
final
io
.
ProcessResult
entitlementResult
=
processManager
.
runSync
(
<
String
>[
'codesign'
,
'--display'
,
'--entitlements'
,
':-'
,
binaryPath
,
],
);
if
(
entitlementResult
.
exitCode
!=
0
)
{
stdio
.
printError
(
'The `codesign --entitlements` command failed with exit code
${entitlementResult.exitCode}
:
\n
'
'
${entitlementResult.stderr}
\n
'
);
return
false
;
}
bool
passes
=
true
;
final
String
output
=
entitlementResult
.
stdout
as
String
;
for
(
final
String
entitlement
in
expectedEntitlements
)
{
final
bool
entitlementExpected
=
binariesWithEntitlements
.
contains
(
binaryPath
);
if
(
output
.
contains
(
entitlement
)
!=
entitlementExpected
)
{
stdio
.
printError
(
'File "
$binaryPath
"
${entitlementExpected ? 'does not have expected' : 'has unexpected'}
'
'entitlement
$entitlement
.'
);
passes
=
false
;
}
}
return
passes
;
}
}
dev/tools/lib/git.dart
View file @
353add83
...
...
@@ -45,6 +45,7 @@ class Git {
return
processManager
.
runSync
(
<
String
>[
'git'
,
...
args
],
workingDirectory:
workingDirectory
,
environment:
<
String
,
String
>{
'GIT_TRACE'
:
'1'
},
);
}
...
...
dev/tools/lib/globals.dart
View file @
353add83
...
...
@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:file/file.dart'
;
import
'package:file/local.dart'
;
import
'package:platform/platform.dart'
;
const
String
kIncrement
=
'increment'
;
const
String
kCommit
=
'commit'
;
const
String
kRemoteName
=
'remote'
;
...
...
@@ -24,3 +28,61 @@ String stdoutToString(dynamic input) {
final
String
str
=
input
as
String
;
return
str
.
trim
();
}
class
ConductorException
implements
Exception
{
ConductorException
(
this
.
message
);
final
String
message
;
@override
String
toString
()
=>
'Exception:
$message
'
;
}
Directory
_flutterRoot
;
Directory
get
localFlutterRoot
{
if
(
_flutterRoot
!=
null
)
{
return
_flutterRoot
;
}
String
filePath
;
const
FileSystem
fileSystem
=
LocalFileSystem
();
const
Platform
platform
=
LocalPlatform
();
// If a test
if
(
platform
.
script
.
scheme
==
'data'
)
{
final
RegExp
pattern
=
RegExp
(
r'(file:\/\/[^"]*[/\\]dev\/tools[/\\][^"]+\.dart)'
,
multiLine:
true
,
);
final
Match
match
=
pattern
.
firstMatch
(
Uri
.
decodeFull
(
platform
.
script
.
path
));
if
(
match
==
null
)
{
throw
Exception
(
'Cannot determine path of script!
\n
${platform.script.path}
'
,
);
}
filePath
=
Uri
.
parse
(
match
.
group
(
1
)).
path
.
replaceAll
(
r'%20'
,
' '
);
}
else
{
filePath
=
platform
.
script
.
toFilePath
();
}
final
String
checkoutsDirname
=
fileSystem
.
path
.
normalize
(
fileSystem
.
path
.
join
(
fileSystem
.
path
.
dirname
(
filePath
),
'..'
,
// flutter/dev/tools
'..'
,
// flutter/dev
'..'
,
// flutter
),
);
_flutterRoot
=
fileSystem
.
directory
(
checkoutsDirname
);
return
_flutterRoot
;
}
bool
assertsEnabled
(
)
{
// Verify asserts enabled
bool
assertsEnabled
=
false
;
assert
(()
{
assertsEnabled
=
true
;
return
true
;
}());
return
assertsEnabled
;
}
dev/tools/lib/repository.dart
View file @
353add83
...
...
@@ -16,7 +16,7 @@ import './stdio.dart';
import
'./version.dart'
;
/// A source code repository.
class
Repository
{
abstract
class
Repository
{
Repository
({
@required
this
.
name
,
@required
this
.
upstream
,
...
...
@@ -46,22 +46,35 @@ class Repository {
Directory
_checkoutDirectory
;
///
Lazily-loaded d
irectory for the repository checkout.
///
D
irectory for the repository checkout.
///
///
Cloning a repository is time-consuming, thus the repository is not cloned
///
until this getter is call
ed.
///
Since cloning a repository takes a long time, we do not ensure it is
///
cloned on the filesystem until this getter is access
ed.
Directory
get
checkoutDirectory
{
if
(
_checkoutDirectory
!=
null
)
{
return
_checkoutDirectory
;
}
_checkoutDirectory
=
parentDirectory
.
childDirectory
(
name
);
if
(
checkoutDirectory
.
existsSync
()
&&
!
useExistingCheckout
)
{
deleteDirectory
();
if
(!
useExistingCheckout
&&
_checkoutDirectory
.
existsSync
())
{
stdio
.
printTrace
(
'Deleting
$name
from
${_checkoutDirectory.path}
...'
);
_checkoutDirectory
.
deleteSync
(
recursive:
true
);
}
else
if
(
useExistingCheckout
&&
_checkoutDirectory
.
existsSync
())
{
git
.
run
(
<
String
>[
'checkout'
,
'master'
],
'Checkout to master branch'
,
workingDirectory:
_checkoutDirectory
.
path
,
);
git
.
run
(
<
String
>[
'pull'
,
'--ff-only'
],
'Updating
$name
repo'
,
workingDirectory:
_checkoutDirectory
.
path
,
);
}
if
(!
checkoutDirectory
.
existsSync
())
{
stdio
.
printTrace
(
'Cloning
$name
to
${checkoutDirectory.path}
...'
);
if
(!
_checkoutDirectory
.
existsSync
())
{
stdio
.
printTrace
(
'Cloning
$name
from
$upstream
to
${_checkoutDirectory.path}
...'
);
git
.
run
(
<
String
>[
'clone'
,
'--'
,
upstream
,
checkoutDirectory
.
path
],
<
String
>[
'clone'
,
'--'
,
upstream
,
_
checkoutDirectory
.
path
],
'Cloning
$name
repo'
,
workingDirectory:
parentDirectory
.
path
,
);
...
...
@@ -72,27 +85,16 @@ class Repository {
git
.
run
(
<
String
>[
'checkout'
,
channel
,
'--'
],
'check out branch
$channel
locally'
,
workingDirectory:
checkoutDirectory
.
path
,
workingDirectory:
_
checkoutDirectory
.
path
,
);
}
}
}
else
{
stdio
.
printTrace
(
'Using existing
$name
repo at
${checkoutDirectory.path}
...'
,
);
}
return
_checkoutDirectory
;
}
void
deleteDirectory
()
{
if
(!
checkoutDirectory
.
existsSync
())
{
stdio
.
printTrace
(
'Tried to delete
${checkoutDirectory.path}
but it does not exist.'
,
);
return
;
}
stdio
.
printTrace
(
'Deleting
$name
from
${checkoutDirectory.path}
...'
);
checkoutDirectory
.
deleteSync
(
recursive:
true
);
final
String
revision
=
reverseParse
(
'HEAD'
);
stdio
.
printTrace
(
'Repository
$name
is checked out at revision "
$revision
".'
);
return
_checkoutDirectory
;
}
/// The URL of the remote named [remoteName].
...
...
@@ -124,6 +126,14 @@ class Repository {
);
}
void
checkout
(
String
revision
)
{
git
.
run
(
<
String
>[
'checkout'
,
revision
],
'checkout
$revision
'
,
workingDirectory:
checkoutDirectory
.
path
,
);
}
/// Obtain the version tag of the previous dev release.
String
getFullTag
(
String
remoteName
)
{
const
String
glob
=
'*.*.*-*.*.pre'
;
...
...
@@ -142,7 +152,7 @@ class Repository {
<
String
>[
'rev-parse'
,
ref
],
'look up the commit for the ref
$ref
'
,
workingDirectory:
checkoutDirectory
.
path
,
);
)
.
trim
()
;
assert
(
revisionHash
.
isNotEmpty
);
return
revisionHash
;
}
...
...
@@ -216,24 +226,6 @@ class Repository {
);
}
Version
flutterVersion
()
{
// Build tool
processManager
.
runSync
(<
String
>[
fileSystem
.
path
.
join
(
checkoutDirectory
.
path
,
'bin'
,
'flutter'
),
'help'
,
]);
// Check version
final
io
.
ProcessResult
result
=
processManager
.
runSync
(<
String
>[
fileSystem
.
path
.
join
(
checkoutDirectory
.
path
,
'bin'
,
'flutter'
),
'--version'
,
'--machine'
,
]);
final
Map
<
String
,
dynamic
>
versionJson
=
jsonDecode
(
globals
.
stdoutToString
(
result
.
stdout
),
)
as
Map
<
String
,
dynamic
>;
return
Version
.
fromString
(
versionJson
[
'frameworkVersion'
]
as
String
);
}
/// Create an empty commit and return the revision.
@visibleForTesting
String
authorEmptyCommit
([
String
message
=
'An empty commit'
])
{
...
...
@@ -261,20 +253,120 @@ class Repository {
///
/// This method is for testing purposes.
@visibleForTesting
Repository
cloneRepository
(
String
cloneName
);
}
class
FrameworkRepository
extends
Repository
{
FrameworkRepository
(
this
.
checkouts
,
{
String
name
=
'framework'
,
String
upstream
=
FrameworkRepository
.
defaultUpstream
,
bool
localUpstream
=
false
,
bool
useExistingCheckout
=
false
,
})
:
super
(
name:
name
,
upstream:
upstream
,
fileSystem:
checkouts
.
fileSystem
,
localUpstream:
localUpstream
,
parentDirectory:
checkouts
.
directory
,
platform:
checkouts
.
platform
,
processManager:
checkouts
.
processManager
,
stdio:
checkouts
.
stdio
,
useExistingCheckout:
useExistingCheckout
,
);
/// A [FrameworkRepository] with the host conductor's repo set as upstream.
///
/// This is useful when testing a commit that has not been merged upstream
/// yet.
factory
FrameworkRepository
.
localRepoAsUpstream
(
Checkouts
checkouts
,
{
String
name
=
'framework'
,
bool
useExistingCheckout
=
false
,
@required
String
upstreamPath
,
})
{
return
FrameworkRepository
(
checkouts
,
name:
name
,
upstream:
'file://
$upstreamPath
/'
,
localUpstream:
false
,
useExistingCheckout:
useExistingCheckout
,
);
}
final
Checkouts
checkouts
;
static
const
String
defaultUpstream
=
'https://github.com/flutter/flutter.git'
;
String
get
cacheDirectory
=>
fileSystem
.
path
.
join
(
checkoutDirectory
.
path
,
'bin'
,
'cache'
,
);
@override
Repository
cloneRepository
(
String
cloneName
)
{
assert
(
localUpstream
);
cloneName
??=
'clone-of-
$name
'
;
return
Repository
(
fileSystem:
fileSystem
,
return
Framework
Repository
(
checkouts
,
name:
cloneName
,
parentDirectory:
parentDirectory
,
platform:
platform
,
processManager:
processManager
,
stdio:
stdio
,
upstream:
'file://
${checkoutDirectory.path}
/'
,
useExistingCheckout:
useExistingCheckout
,
);
}
void
_ensureToolReady
()
{
final
File
toolsStamp
=
fileSystem
.
directory
(
cacheDirectory
).
childFile
(
'flutter_tools.stamp'
);
if
(
toolsStamp
.
existsSync
())
{
final
String
toolsStampHash
=
toolsStamp
.
readAsStringSync
().
trim
();
final
String
repoHeadHash
=
reverseParse
(
'HEAD'
);
if
(
toolsStampHash
==
repoHeadHash
)
{
return
;
}
}
stdio
.
printTrace
(
'Building tool...'
);
// Build tool
processManager
.
runSync
(<
String
>[
fileSystem
.
path
.
join
(
checkoutDirectory
.
path
,
'bin'
,
'flutter'
),
'help'
,
]);
}
io
.
ProcessResult
runFlutter
(
List
<
String
>
args
)
{
_ensureToolReady
();
return
processManager
.
runSync
(<
String
>[
fileSystem
.
path
.
join
(
checkoutDirectory
.
path
,
'bin'
,
'flutter'
),
...
args
,
]);
}
@override
void
checkout
(
String
revision
)
{
super
.
checkout
(
revision
);
// The tool will overwrite old cached artifacts, but not delete unused
// artifacts from a previous version. Thus, delete the entire cache and
// re-populate.
final
Directory
cache
=
fileSystem
.
directory
(
cacheDirectory
);
if
(
cache
.
existsSync
())
{
stdio
.
printTrace
(
'Deleting cache...'
);
cache
.
deleteSync
(
recursive:
true
);
}
_ensureToolReady
();
}
Version
flutterVersion
()
{
// Check version
final
io
.
ProcessResult
result
=
runFlutter
(<
String
>[
'--version'
,
'--machine'
]);
final
Map
<
String
,
dynamic
>
versionJson
=
jsonDecode
(
globals
.
stdoutToString
(
result
.
stdout
),
)
as
Map
<
String
,
dynamic
>;
return
Version
.
fromString
(
versionJson
[
'frameworkVersion'
]
as
String
);
}
}
/// An enum of all the repositories that the Conductor supports.
...
...
@@ -285,81 +377,22 @@ enum RepositoryType {
class
Checkouts
{
Checkouts
({
@required
Platform
platform
,
@required
this
.
fileSystem
,
@required
this
.
platform
,
@required
this
.
processManager
,
Directory
parentDirectory
,
String
directoryName
=
'checkouts'
,
})
{
if
(
parentDirectory
!=
null
)
{
directory
=
parentDirectory
.
childDirectory
(
directoryName
);
}
else
{
String
filePath
;
// If a test
if
(
platform
.
script
.
scheme
==
'data'
)
{
final
RegExp
pattern
=
RegExp
(
r'(file:\/\/[^"]*[/\\]dev\/tools[/\\][^"]+\.dart)'
,
multiLine:
true
,
);
final
Match
match
=
pattern
.
firstMatch
(
Uri
.
decodeFull
(
platform
.
script
.
path
));
if
(
match
==
null
)
{
throw
Exception
(
'Cannot determine path of script!
\n
${platform.script.path}
'
,
);
}
filePath
=
Uri
.
parse
(
match
.
group
(
1
)).
path
.
replaceAll
(
r'%20'
,
' '
);
}
else
{
filePath
=
platform
.
script
.
toFilePath
();
}
final
String
checkoutsDirname
=
fileSystem
.
path
.
normalize
(
fileSystem
.
path
.
join
(
fileSystem
.
path
.
dirname
(
filePath
),
'..'
,
'checkouts'
,
),
);
directory
=
fileSystem
.
directory
(
checkoutsDirname
);
}
@required
this
.
stdio
,
@required
Directory
parentDirectory
,
String
directoryName
=
'flutter_conductor_checkouts'
,
})
:
assert
(
parentDirectory
!=
null
),
directory
=
parentDirectory
.
childDirectory
(
directoryName
)
{
if
(!
directory
.
existsSync
())
{
directory
.
createSync
(
recursive:
true
);
}
}
Directory
directory
;
final
Directory
directory
;
final
FileSystem
fileSystem
;
final
Platform
platform
;
final
ProcessManager
processManager
;
Repository
addRepo
({
@required
RepositoryType
repoType
,
@required
Stdio
stdio
,
@required
Platform
platform
,
FileSystem
fileSystem
,
String
upstream
,
String
name
,
bool
localUpstream
=
false
,
bool
useExistingCheckout
=
false
,
})
{
switch
(
repoType
)
{
case
RepositoryType
.
framework
:
name
??=
'framework'
;
upstream
??=
'https://github.com/flutter/flutter.git'
;
break
;
case
RepositoryType
.
engine
:
name
??=
'engine'
;
upstream
??=
'https://github.com/flutter/engine.git'
;
break
;
}
return
Repository
(
name:
name
,
upstream:
upstream
,
stdio:
stdio
,
platform:
platform
,
fileSystem:
fileSystem
,
parentDirectory:
directory
,
processManager:
processManager
,
localUpstream:
localUpstream
,
useExistingCheckout:
useExistingCheckout
,
);
}
final
Stdio
stdio
;
}
dev/tools/lib/roll_dev.dart
View file @
353add83
...
...
@@ -14,12 +14,12 @@ import './stdio.dart';
import
'./version.dart'
;
/// Create a new dev release without cherry picks.
class
RollDev
extends
Command
<
void
>
{
RollDev
({
this
.
fileSystem
,
this
.
platfor
m
,
this
.
repository
,
this
.
stdio
,
class
RollDev
Command
extends
Command
<
void
>
{
RollDev
Command
({
@required
this
.
checkouts
,
@required
this
.
fileSyste
m
,
@required
this
.
platform
,
@required
this
.
stdio
,
})
{
argParser
.
addOption
(
kIncrement
,
...
...
@@ -60,10 +60,10 @@ class RollDev extends Command<void> {
argParser
.
addFlag
(
kYes
,
negatable:
false
,
abbr:
'y'
,
help:
'Skip the confirmation prompt.'
);
}
final
Checkouts
checkouts
;
final
FileSystem
fileSystem
;
final
Platform
platform
;
final
Stdio
stdio
;
final
Repository
repository
;
@override
String
get
name
=>
'roll-dev'
;
...
...
@@ -76,9 +76,7 @@ class RollDev extends Command<void> {
void
run
()
{
rollDev
(
argResults:
argResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repository
,
repository:
FrameworkRepository
(
checkouts
),
stdio:
stdio
,
usage:
argParser
.
usage
,
);
...
...
@@ -93,9 +91,7 @@ bool rollDev({
@required
String
usage
,
@required
ArgResults
argResults
,
@required
Stdio
stdio
,
@required
Platform
platform
,
@required
FileSystem
fileSystem
,
@required
Repository
repository
,
@required
FrameworkRepository
repository
,
String
remoteName
=
'origin'
,
})
{
final
String
level
=
argResults
[
kIncrement
]
as
String
;
...
...
dev/tools/lib/stdio.dart
View file @
353add83
...
...
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:io'
;
import
'dart:io'
as
io
;
import
'package:meta/meta.dart'
;
...
...
@@ -31,9 +31,15 @@ class VerboseStdio extends Stdio {
@required
this
.
stdin
,
})
:
assert
(
stdout
!=
null
),
assert
(
stderr
!=
null
),
assert
(
stdin
!=
null
);
final
Stdout
stdout
;
final
Stdout
stderr
;
final
Stdin
stdin
;
factory
VerboseStdio
.
local
()
=>
VerboseStdio
(
stdout:
io
.
stdout
,
stderr:
io
.
stderr
,
stdin:
io
.
stdin
,
);
final
io
.
Stdout
stdout
;
final
io
.
Stdout
stderr
;
final
io
.
Stdin
stdin
;
@override
void
printError
(
String
message
)
{
...
...
dev/tools/test/codesign_integration_test.dart
0 → 100644
View file @
353add83
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:args/command_runner.dart'
;
import
'package:file/file.dart'
;
import
'package:file/local.dart'
;
import
'package:platform/platform.dart'
;
import
'package:process/process.dart'
;
import
'package:dev_tools/codesign.dart'
show
CodesignCommand
;
import
'package:dev_tools/globals.dart'
;
import
'package:dev_tools/repository.dart'
show
Checkouts
;
import
'./common.dart'
;
/// Verify all binaries in the Flutter cache are expected by Conductor.
void
main
(
)
{
test
(
'validate the expected binaries from the conductor codesign command are present in the cache'
,
()
async
{
const
Platform
platform
=
LocalPlatform
();
const
FileSystem
fileSystem
=
LocalFileSystem
();
const
ProcessManager
processManager
=
LocalProcessManager
();
final
TestStdio
stdio
=
TestStdio
(
verbose:
true
);
final
Checkouts
checkouts
=
Checkouts
(
fileSystem:
fileSystem
,
parentDirectory:
localFlutterRoot
.
parent
,
platform:
platform
,
processManager:
processManager
,
stdio:
stdio
,
);
final
CommandRunner
<
void
>
runner
=
CommandRunner
<
void
>(
'codesign-test'
,
''
)
..
addCommand
(
CodesignCommand
(
checkouts:
checkouts
,
flutterRoot:
localFlutterRoot
));
try
{
await
runner
.
run
(<
String
>[
'codesign'
,
'--verify'
,
// Only verify if the correct binaries are in the cache
'--no-signatures'
,
]);
}
on
ConductorException
catch
(
e
)
{
print
(
stdio
.
error
);
print
(
fixItInstructions
);
fail
(
e
.
message
);
}
on
Exception
{
print
(
'stdout:
\n
${stdio.stdout}
'
);
print
(
'stderr:
\n
${stdio.error}
'
);
rethrow
;
}
},
onPlatform:
<
String
,
dynamic
>{
'windows'
:
const
Skip
(
'codesign command is only supported on macos'
),
'linux'
:
const
Skip
(
'codesign command is only supported on macos'
),
});
}
const
String
fixItInstructions
=
'''
Codesign integration test failed.
This means that the binary files found in the Flutter cache do not match those
expected by the conductor tool (either an expected file was not found in the
cache or an unexpected file was found in the cache).
This usually happens either during an engine roll or a change to the caching
logic in flutter_tools. If this is a valid change, then the conductor source
code should be updated, specifically either the [binariesWithEntitlements] or
[binariesWithoutEntitlements] lists, depending on if the file should have macOS
entitlements applied during codesigning.
'''
;
dev/tools/test/codesign_test.dart
0 → 100644
View file @
353add83
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:args/command_runner.dart'
;
import
'package:dev_tools/codesign.dart'
;
import
'package:dev_tools/globals.dart'
;
import
'package:dev_tools/repository.dart'
;
import
'package:file/file.dart'
;
import
'package:file/memory.dart'
;
import
'package:meta/meta.dart'
;
import
'package:platform/platform.dart'
;
import
'../../../packages/flutter_tools/test/src/fake_process_manager.dart'
;
import
'./common.dart'
;
void
main
(
)
{
group
(
'codesign command'
,
()
{
const
String
flutterRoot
=
'/flutter'
;
const
String
checkoutsParentDirectory
=
'
$flutterRoot
/dev/tools/'
;
const
String
flutterCache
=
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework/bin/cache'
;
const
String
flutterBin
=
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework/bin/flutter'
;
const
String
revision
=
'abcd1234'
;
CommandRunner
<
void
>
runner
;
Checkouts
checkouts
;
MemoryFileSystem
fileSystem
;
FakePlatform
platform
;
TestStdio
stdio
;
FakeProcessManager
processManager
;
const
List
<
String
>
binariesWithEntitlements
=
<
String
>[
'
$flutterCache
/dart-sdk/bin/dart'
,
'
$flutterCache
/dart-sdk/bin/dartaotruntime'
,
];
const
List
<
String
>
binariesWithoutEntitlements
=
<
String
>[
'
$flutterCache
/engine/darwin-x64/font-subset'
,
];
const
List
<
String
>
allBinaries
=
<
String
>[
...
binariesWithEntitlements
,
...
binariesWithoutEntitlements
,
];
void
createRunner
({
String
operatingSystem
=
'macos'
,
List
<
FakeCommand
>
commands
,
})
{
stdio
=
TestStdio
();
fileSystem
=
MemoryFileSystem
.
test
();
platform
=
FakePlatform
(
operatingSystem:
operatingSystem
);
processManager
=
FakeProcessManager
.
list
(
commands
??
<
FakeCommand
>[]);
checkouts
=
Checkouts
(
fileSystem:
fileSystem
,
parentDirectory:
fileSystem
.
directory
(
checkoutsParentDirectory
),
platform:
platform
,
processManager:
processManager
,
stdio:
stdio
,
);
final
FakeCodesignCommand
command
=
FakeCodesignCommand
(
checkouts:
checkouts
,
binariesWithEntitlements:
binariesWithEntitlements
,
binariesWithoutEntitlements:
binariesWithoutEntitlements
,
flutterRoot:
fileSystem
.
directory
(
flutterRoot
),
);
runner
=
CommandRunner
<
void
>(
'codesign-test'
,
''
)
..
addCommand
(
command
);
}
test
(
'throws exception if not run from macos'
,
()
async
{
createRunner
(
operatingSystem:
'linux'
);
expect
(
()
async
=>
await
runner
.
run
(<
String
>[
'codesign'
]),
throwsExceptionWith
(
'Error! Expected operating system "macos"'
),
);
});
test
(
'throws exception if verify flag is not provided'
,
()
async
{
createRunner
();
expect
(
()
async
=>
await
runner
.
run
(<
String
>[
'codesign'
]),
throwsExceptionWith
(
'Sorry, but codesigning is not implemented yet. Please pass the --
$kVerify
flag to verify signatures'
),
);
});
test
(
'succeeds if every binary is codesigned and has correct entitlements'
,
()
async
{
final
List
<
FakeCommand
>
codesignCheckCommands
=
<
FakeCommand
>[];
for
(
final
String
bin
in
binariesWithEntitlements
)
{
codesignCheckCommands
.
add
(
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
bin
],
),
);
codesignCheckCommands
.
add
(
FakeCommand
(
command:
<
String
>[
'codesign'
,
'--display'
,
'--entitlements'
,
':-'
,
bin
],
stdout:
expectedEntitlements
.
join
(
'
\n
'
),
),
);
}
for
(
final
String
bin
in
binariesWithoutEntitlements
)
{
codesignCheckCommands
.
add
(
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
bin
],
),
);
}
createRunner
(
commands:
<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'git'
,
'clone'
,
'--'
,
'file://
$flutterRoot
/'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
revision
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'checkout'
,
revision
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'precache'
,
'--android'
,
'--ios'
,
'--macos'
,
]),
FakeCommand
(
command:
const
<
String
>[
'find'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework/bin/cache'
,
'-type'
,
'f'
,
],
stdout:
allBinaries
.
join
(
'
\n
'
),
),
for
(
String
bin
in
allBinaries
)
FakeCommand
(
command:
<
String
>[
'file'
,
'--mime-type'
,
'-b'
,
bin
],
stdout:
'application/x-mach-binary'
,
),
...
codesignCheckCommands
,
]);
await
runner
.
run
(<
String
>[
'codesign'
,
'--
$kVerify
'
,
'--
$kRevision
'
,
revision
]);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
test
(
'fails if a single binary is not codesigned'
,
()
async
{
final
List
<
FakeCommand
>
codesignCheckCommands
=
<
FakeCommand
>[];
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
'
$flutterCache
/dart-sdk/bin/dart'
],
),
);
codesignCheckCommands
.
add
(
FakeCommand
(
command:
const
<
String
>[
'codesign'
,
'--display'
,
'--entitlements'
,
':-'
,
'
$flutterCache
/dart-sdk/bin/dart'
,
],
stdout:
expectedEntitlements
.
join
(
'
\n
'
),
)
);
// Not signed
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
'
$flutterCache
/dart-sdk/bin/dartaotruntime'
],
exitCode:
1
,
),
);
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
'
$flutterCache
/engine/darwin-x64/font-subset'
],
),
);
createRunner
(
commands:
<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'git'
,
'clone'
,
'--'
,
'file://
$flutterRoot
/'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
revision
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'checkout'
,
revision
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'precache'
,
'--android'
,
'--ios'
,
'--macos'
,
]),
FakeCommand
(
command:
const
<
String
>[
'find'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework/bin/cache'
,
'-type'
,
'f'
,
],
stdout:
allBinaries
.
join
(
'
\n
'
),
),
for
(
String
bin
in
allBinaries
)
FakeCommand
(
command:
<
String
>[
'file'
,
'--mime-type'
,
'-b'
,
bin
],
stdout:
'application/x-mach-binary'
,
),
...
codesignCheckCommands
,
]);
expect
(
()
async
=>
await
runner
.
run
(<
String
>[
'codesign'
,
'--
$kVerify
'
,
'--
$kRevision
'
,
revision
]),
throwsExceptionWith
(
'Test failed because unsigned binaries detected.'
),
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
test
(
'fails if a single binary has the wrong entitlements'
,
()
async
{
final
List
<
FakeCommand
>
codesignCheckCommands
=
<
FakeCommand
>[];
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
'
$flutterCache
/dart-sdk/bin/dart'
],
),
);
codesignCheckCommands
.
add
(
FakeCommand
(
command:
const
<
String
>[
'codesign'
,
'--display'
,
'--entitlements'
,
':-'
,
'
$flutterCache
/dart-sdk/bin/dart'
],
stdout:
expectedEntitlements
.
join
(
'
\n
'
),
)
);
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
'
$flutterCache
/dart-sdk/bin/dartaotruntime'
],
),
);
// No entitlements
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'--display'
,
'--entitlements'
,
':-'
,
'
$flutterCache
/dart-sdk/bin/dartaotruntime'
],
)
);
codesignCheckCommands
.
add
(
const
FakeCommand
(
command:
<
String
>[
'codesign'
,
'-vvv'
,
'
$flutterCache
/engine/darwin-x64/font-subset'
],
),
);
createRunner
(
commands:
<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'git'
,
'clone'
,
'--'
,
'file://
$flutterRoot
/'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
revision
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'checkout'
,
revision
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'precache'
,
'--android'
,
'--ios'
,
'--macos'
,
]),
FakeCommand
(
command:
const
<
String
>[
'find'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework/bin/cache'
,
'-type'
,
'f'
,
],
stdout:
allBinaries
.
join
(
'
\n
'
),
),
for
(
String
bin
in
allBinaries
)
FakeCommand
(
command:
<
String
>[
'file'
,
'--mime-type'
,
'-b'
,
bin
],
stdout:
'application/x-mach-binary'
,
),
...
codesignCheckCommands
,
]);
expect
(
()
async
=>
await
runner
.
run
(<
String
>[
'codesign'
,
'--
$kVerify
'
,
'--
$kRevision
'
,
revision
]),
throwsExceptionWith
(
'Test failed because files found with the wrong entitlements'
),
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
test
(
'does not check signatures or entitlements if --no-
$kSignatures
specified'
,
()
async
{
createRunner
(
commands:
<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'git'
,
'clone'
,
'--'
,
'file://
$flutterRoot
/'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
revision
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'checkout'
,
revision
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'help'
,
]),
const
FakeCommand
(
command:
<
String
>[
flutterBin
,
'precache'
,
'--android'
,
'--ios'
,
'--macos'
,
]),
FakeCommand
(
command:
const
<
String
>[
'find'
,
'
${checkoutsParentDirectory}
flutter_conductor_checkouts/framework/bin/cache'
,
'-type'
,
'f'
,
],
stdout:
allBinaries
.
join
(
'
\n
'
),
),
for
(
String
bin
in
allBinaries
)
FakeCommand
(
command:
<
String
>[
'file'
,
'--mime-type'
,
'-b'
,
bin
],
stdout:
'application/x-mach-binary'
,
),
]);
try
{
await
runner
.
run
(<
String
>[
'codesign'
,
'--
$kVerify
'
,
'--no-
$kSignatures
'
,
'--
$kRevision
'
,
revision
,
]);
}
on
ConductorException
{
//print(stdio.error);
rethrow
;
}
expect
(
processManager
.
hasRemainingExpectations
,
false
,
);
});
});
}
class
FakeCodesignCommand
extends
CodesignCommand
{
FakeCodesignCommand
({
@required
Checkouts
checkouts
,
@required
this
.
binariesWithEntitlements
,
@required
this
.
binariesWithoutEntitlements
,
@required
Directory
flutterRoot
,
})
:
super
(
checkouts:
checkouts
,
flutterRoot:
flutterRoot
);
@override
final
List
<
String
>
binariesWithEntitlements
;
@override
final
List
<
String
>
binariesWithoutEntitlements
;
}
dev/tools/test/common.dart
View file @
353add83
...
...
@@ -4,6 +4,7 @@
import
'dart:io'
;
import
'package:file/file.dart'
;
import
'package:test/test.dart'
hide
TypeMatcher
,
isInstanceOf
;
import
'package:test/test.dart'
as
test_package
show
TypeMatcher
;
...
...
dev/tools/test/roll_dev_integration_test.dart
View file @
353add83
...
...
@@ -7,6 +7,7 @@ import 'package:file/local.dart';
import
'package:platform/platform.dart'
;
import
'package:process/process.dart'
;
import
'package:dev_tools/globals.dart'
;
import
'package:dev_tools/roll_dev.dart'
show
rollDev
;
import
'package:dev_tools/repository.dart'
;
import
'package:dev_tools/version.dart'
;
...
...
@@ -22,8 +23,8 @@ void main() {
const
String
usageString
=
'Usage: flutter conductor.'
;
Checkouts
checkouts
;
Repository
frameworkUpstream
;
Repository
framework
;
Framework
Repository
frameworkUpstream
;
Framework
Repository
framework
;
setUp
(()
{
platform
=
const
LocalPlatform
();
...
...
@@ -32,22 +33,20 @@ void main() {
stdio
=
TestStdio
(
verbose:
true
);
checkouts
=
Checkouts
(
fileSystem:
fileSystem
,
parentDirectory:
localFlutterRoot
.
parent
,
platform:
platform
,
processManager:
processManager
,
);
frameworkUpstream
=
checkouts
.
addRepo
(
repoType:
RepositoryType
.
framework
,
name:
'framework-upstream'
,
stdio:
stdio
,
platform:
platform
,
localUpstream:
true
,
fileSystem:
fileSystem
,
useExistingCheckout:
false
,
);
frameworkUpstream
=
FrameworkRepository
(
checkouts
,
localUpstream:
true
);
// This repository has [frameworkUpstream] set as its push/pull remote.
framework
=
frameworkUpstream
.
cloneRepository
(
'test-framework'
);
framework
=
FrameworkRepository
(
checkouts
,
name:
'test-framework'
,
upstream:
'file://
${frameworkUpstream.checkoutDirectory.path}
/'
,
);
});
test
(
'increment m'
,
()
{
...
...
@@ -68,8 +67,6 @@ void main() {
usage:
usageString
,
argResults:
fakeArgResults
,
stdio:
stdio
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
framework
,
),
true
,
...
...
@@ -107,8 +104,6 @@ void main() {
usage:
usageString
,
argResults:
fakeArgResults
,
stdio:
stdio
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
framework
,
),
true
,
...
...
dev/tools/test/roll_dev_test.dart
View file @
353add83
...
...
@@ -24,7 +24,7 @@ void main() {
FakeArgResults
fakeArgResults
;
MemoryFileSystem
fileSystem
;
TestStdio
stdio
;
Repository
repo
;
Framework
Repository
repo
;
Checkouts
checkouts
;
FakePlatform
platform
;
FakeProcessManager
processManager
;
...
...
@@ -39,12 +39,9 @@ void main() {
parentDirectory:
fileSystem
.
directory
(
checkoutsParentDirectory
),
platform:
platform
,
processManager:
processManager
,
);
repo
=
checkouts
.
addRepo
(
platform:
platform
,
repoType:
RepositoryType
.
framework
,
stdio:
stdio
,
);
repo
=
FrameworkRepository
(
checkouts
);
});
test
(
'returns false if level not provided'
,
()
{
...
...
@@ -56,8 +53,6 @@ void main() {
expect
(
rollDev
(
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
usage:
usage
,
...
...
@@ -75,8 +70,6 @@ void main() {
expect
(
rollDev
(
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
usage:
usage
,
...
...
@@ -92,8 +85,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -115,8 +113,6 @@ void main() {
try
{
rollDev
(
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
usage:
usage
,
...
...
@@ -137,8 +133,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -187,8 +188,6 @@ void main() {
rollDev
(
usage:
usage
,
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
),
...
...
@@ -206,8 +205,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -267,8 +271,6 @@ void main() {
()
=>
rollDev
(
usage:
usage
,
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
),
...
...
@@ -283,8 +285,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -333,8 +340,6 @@ void main() {
()
=>
rollDev
(
usage:
usage
,
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
),
...
...
@@ -353,8 +358,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -410,8 +420,6 @@ void main() {
expect
(
()
=>
rollDev
(
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
usage:
usage
,
...
...
@@ -427,8 +435,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -501,8 +514,6 @@ void main() {
rollDev
(
usage:
usage
,
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
),
...
...
@@ -517,8 +528,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -595,8 +611,6 @@ void main() {
rollDev
(
usage:
usage
,
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
),
...
...
@@ -611,8 +625,13 @@ void main() {
'clone'
,
'--'
,
kUpstreamRemote
,
'
${checkoutsParentDirectory}
checkouts/framework'
,
'
${checkoutsParentDirectory}
flutter_conductor_
checkouts/framework'
,
]),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
,
],
stdout:
commit
),
const
FakeCommand
(
command:
<
String
>[
'git'
,
'remote'
,
...
...
@@ -684,8 +703,6 @@ void main() {
expect
(
rollDev
(
argResults:
fakeArgResults
,
fileSystem:
fileSystem
,
platform:
platform
,
repository:
repo
,
stdio:
stdio
,
usage:
usage
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment