3
3
// found in the LICENSE file.
4
4
5
5
import 'dart:async' ;
6
+ import 'dart:convert' ;
6
7
import 'dart:io' as io;
7
8
8
9
import 'package:colorize/colorize.dart' ;
9
10
import 'package:file/file.dart' ;
11
+ import 'package:http/http.dart' as http;
12
+ import 'package:meta/meta.dart' ;
13
+ import 'package:pub_semver/pub_semver.dart' ;
10
14
import 'package:pubspec_parse/pubspec_parse.dart' ;
11
15
12
16
import 'common.dart' ;
@@ -18,17 +22,40 @@ class PublishCheckCommand extends PluginCommand {
18
22
Directory packagesDir,
19
23
FileSystem fileSystem, {
20
24
ProcessRunner processRunner = const ProcessRunner (),
21
- }) : super (packagesDir, fileSystem, processRunner: processRunner) {
25
+ this .httpClient,
26
+ }) : _pubVersionFinder =
27
+ PubVersionFinder (httpClient: httpClient ?? http.Client ()),
28
+ super (packagesDir, fileSystem, processRunner: processRunner) {
22
29
argParser.addFlag (
23
30
_allowPrereleaseFlag,
24
31
help: 'Allows the pre-release SDK warning to pass.\n '
25
32
'When enabled, a pub warning, which asks to publish the package as a pre-release version when '
26
33
'the SDK constraint is a pre-release version, is ignored.' ,
27
34
defaultsTo: false ,
28
35
);
36
+ argParser.addFlag (_machineFlag,
37
+ help: 'Switch outputs to a machine readable JSON. \n '
38
+ 'The JSON contains a "status" field indicating the final status of the command, the possible values are:\n '
39
+ ' $_statusNeedsPublish : There is at least one package need to be published. They also passed all publish checks.\n '
40
+ ' $_statusMessageNoPublish : There are no packages needs to be published. Either no pubspec change detected or all versions have already been published.\n '
41
+ ' $_statusMessageError : Some error has occurred.' ,
42
+ defaultsTo: false ,
43
+ negatable: true );
29
44
}
30
45
31
46
static const String _allowPrereleaseFlag = 'allow-pre-release' ;
47
+ static const String _machineFlag = 'machine' ;
48
+ static const String _statusNeedsPublish = 'needs-publish' ;
49
+ static const String _statusMessageNoPublish = 'no-publish' ;
50
+ static const String _statusMessageError = 'error' ;
51
+ static const String _statusKey = 'status' ;
52
+ static const String _humanMessageKey = 'humanMessage' ;
53
+
54
+ final List <String > _validStatus = < String > [
55
+ _statusNeedsPublish,
56
+ _statusMessageNoPublish,
57
+ _statusMessageError
58
+ ];
32
59
33
60
@override
34
61
final String name = 'publish-check' ;
@@ -37,31 +64,74 @@ class PublishCheckCommand extends PluginCommand {
37
64
final String description =
38
65
'Checks to make sure that a plugin *could* be published.' ;
39
66
67
+ /// The custom http client used to query versions on pub.
68
+ final http.Client httpClient;
69
+
70
+ final PubVersionFinder _pubVersionFinder;
71
+
72
+ // The output JSON when the _machineFlag is on.
73
+ final Map <String , dynamic > _machineOutput = < String , dynamic > {};
74
+
75
+ final List <String > _humanMessages = < String > [];
76
+
40
77
@override
41
78
Future <void > run () async {
79
+ final ZoneSpecification logSwitchSpecification = ZoneSpecification (
80
+ print: (Zone self, ZoneDelegate parent, Zone zone, String message) {
81
+ final bool logMachineMessage = argResults[_machineFlag] as bool ;
82
+ if (logMachineMessage && message != _prettyJson (_machineOutput)) {
83
+ _humanMessages.add (message);
84
+ } else {
85
+ parent.print (zone, message);
86
+ }
87
+ });
88
+
89
+ await runZoned (_runCommand, zoneSpecification: logSwitchSpecification);
90
+ }
91
+
92
+ Future <void > _runCommand () async {
42
93
final List <Directory > failedPackages = < Directory > [];
43
94
95
+ String status = _statusMessageNoPublish;
44
96
await for (final Directory plugin in getPlugins ()) {
45
- if (! (await _passesPublishCheck (plugin))) {
46
- failedPackages.add (plugin);
97
+ final _PublishCheckResult result = await _passesPublishCheck (plugin);
98
+ switch (result) {
99
+ case _PublishCheckResult ._notPublished:
100
+ if (failedPackages.isEmpty) {
101
+ status = _statusNeedsPublish;
102
+ }
103
+ break ;
104
+ case _PublishCheckResult ._published:
105
+ break ;
106
+ case _PublishCheckResult ._error:
107
+ failedPackages.add (plugin);
108
+ status = _statusMessageError;
109
+ break ;
47
110
}
48
111
}
112
+ _pubVersionFinder.httpClient.close ();
49
113
50
114
if (failedPackages.isNotEmpty) {
51
115
final String error =
52
- 'FAIL: The following ${failedPackages .length } package(s) failed the '
116
+ 'The following ${failedPackages .length } package(s) failed the '
53
117
'publishing check:' ;
54
118
final String joinedFailedPackages = failedPackages.join ('\n ' );
119
+ _printImportantStatusMessage ('$error \n $joinedFailedPackages ' ,
120
+ isError: true );
121
+ } else {
122
+ _printImportantStatusMessage ('All packages passed publish check!' ,
123
+ isError: false );
124
+ }
55
125
56
- final Colorize colorizedError = Colorize ( '$ error \n $ joinedFailedPackages ' )
57
- .. red ( );
58
- print (colorizedError) ;
59
- throw ToolExit ( 1 );
126
+ if (argResults[_machineFlag] as bool ) {
127
+ _setStatus (status );
128
+ _machineOutput[_humanMessageKey] = _humanMessages ;
129
+ print ( _prettyJson (_machineOutput) );
60
130
}
61
131
62
- final Colorize passedMessage =
63
- Colorize ( 'All packages passed publish check!' ).. green ( );
64
- print (passedMessage);
132
+ if (failedPackages.isNotEmpty) {
133
+ throw ToolExit ( 1 );
134
+ }
65
135
}
66
136
67
137
Pubspec _tryParsePubspec (Directory package) {
@@ -89,17 +159,23 @@ class PublishCheckCommand extends PluginCommand {
89
159
final Completer <void > stdOutCompleter = Completer <void >();
90
160
process.stdout.listen (
91
161
(List <int > event) {
92
- io.stdout.add (event);
93
- outputBuffer.write (String .fromCharCodes (event));
162
+ final String output = String .fromCharCodes (event);
163
+ if (output.isNotEmpty) {
164
+ print (output);
165
+ outputBuffer.write (output);
166
+ }
94
167
},
95
168
onDone: () => stdOutCompleter.complete (),
96
169
);
97
170
98
171
final Completer <void > stdInCompleter = Completer <void >();
99
172
process.stderr.listen (
100
173
(List <int > event) {
101
- io.stderr.add (event);
102
- outputBuffer.write (String .fromCharCodes (event));
174
+ final String output = String .fromCharCodes (event);
175
+ if (output.isNotEmpty) {
176
+ _printImportantStatusMessage (output, isError: true );
177
+ outputBuffer.write (output);
178
+ }
103
179
},
104
180
onDone: () => stdInCompleter.complete (),
105
181
);
@@ -121,24 +197,97 @@ class PublishCheckCommand extends PluginCommand {
121
197
'Packages with an SDK constraint on a pre-release of the Dart SDK should themselves be published as a pre-release version.' );
122
198
}
123
199
124
- Future <bool > _passesPublishCheck (Directory package) async {
200
+ Future <_PublishCheckResult > _passesPublishCheck (Directory package) async {
125
201
final String packageName = package.basename;
126
202
print ('Checking that $packageName can be published.' );
127
203
128
204
final Pubspec pubspec = _tryParsePubspec (package);
129
205
if (pubspec == null ) {
130
- return false ;
206
+ print ('no pubspec' );
207
+ return _PublishCheckResult ._error;
131
208
} else if (pubspec.publishTo == 'none' ) {
132
209
print ('Package $packageName is marked as unpublishable. Skipping.' );
133
- return true ;
210
+ return _PublishCheckResult ._published;
211
+ }
212
+
213
+ final Version version = pubspec.version;
214
+ final _PublishCheckResult alreadyPublishedResult =
215
+ await _checkIfAlreadyPublished (
216
+ packageName: packageName, version: version);
217
+ if (alreadyPublishedResult == _PublishCheckResult ._published) {
218
+ print (
219
+ 'Package $packageName version: $version has already be published on pub.' );
220
+ return alreadyPublishedResult;
221
+ } else if (alreadyPublishedResult == _PublishCheckResult ._error) {
222
+ print ('Check pub version failed $packageName ' );
223
+ return _PublishCheckResult ._error;
134
224
}
135
225
136
226
if (await _hasValidPublishCheckRun (package)) {
137
227
print ('Package $packageName is able to be published.' );
138
- return true ;
228
+ return _PublishCheckResult ._notPublished ;
139
229
} else {
140
230
print ('Unable to publish $packageName ' );
141
- return false ;
231
+ return _PublishCheckResult ._error;
232
+ }
233
+ }
234
+
235
+ // Check if `packageName` already has `version` published on pub.
236
+ Future <_PublishCheckResult > _checkIfAlreadyPublished (
237
+ {String packageName, Version version}) async {
238
+ final PubVersionFinderResponse pubVersionFinderResponse =
239
+ await _pubVersionFinder.getPackageVersion (package: packageName);
240
+ _PublishCheckResult result;
241
+ switch (pubVersionFinderResponse.result) {
242
+ case PubVersionFinderResult .success:
243
+ result = pubVersionFinderResponse.versions.contains (version)
244
+ ? _PublishCheckResult ._published
245
+ : _PublishCheckResult ._notPublished;
246
+ break ;
247
+ case PubVersionFinderResult .fail:
248
+ print ('''
249
+ Error fetching version on pub for $packageName .
250
+ HTTP Status ${pubVersionFinderResponse .httpResponse .statusCode }
251
+ HTTP response: ${pubVersionFinderResponse .httpResponse .body }
252
+ ''' );
253
+ result = _PublishCheckResult ._error;
254
+ break ;
255
+ case PubVersionFinderResult .noPackageFound:
256
+ result = _PublishCheckResult ._notPublished;
257
+ break ;
142
258
}
259
+ return result;
260
+ }
261
+
262
+ void _setStatus (String status) {
263
+ assert (_validStatus.contains (status));
264
+ _machineOutput[_statusKey] = status;
265
+ }
266
+
267
+ String _prettyJson (Map <String , dynamic > map) {
268
+ return const JsonEncoder .withIndent (' ' ).convert (_machineOutput);
143
269
}
270
+
271
+ void _printImportantStatusMessage (String message, {@required bool isError}) {
272
+ final String statusMessage = '${isError ? 'ERROR' : 'SUCCESS' }: $message ' ;
273
+ if (argResults[_machineFlag] as bool ) {
274
+ print (statusMessage);
275
+ } else {
276
+ final Colorize colorizedMessage = Colorize (statusMessage);
277
+ if (isError) {
278
+ colorizedMessage.red ();
279
+ } else {
280
+ colorizedMessage.green ();
281
+ }
282
+ print (colorizedMessage);
283
+ }
284
+ }
285
+ }
286
+
287
+ enum _PublishCheckResult {
288
+ _notPublished,
289
+
290
+ _published,
291
+
292
+ _error,
144
293
}
0 commit comments