22
33namespace Platformsh \Cli \Service ;
44
5+ use GuzzleHttp \Exception \BadResponseException ;
56use Platformsh \Client \Model \Activity ;
67use Platformsh \Client \Model \ActivityLog \LogItem ;
78use Platformsh \Client \Model \Project ;
@@ -99,6 +100,28 @@ public function waitAndLog(Activity $activity, $pollInterval = 3, $timestamps =
99100 return Helper::formatTime (time () - $ startTime );
100101 });
101102 $ bar ->setFormat ('[%bar%] %elapsed:6s% (%state%) ' );
103+
104+ // Set up cancellation for the activity on Ctrl+C.
105+ if (\function_exists ('\\pcntl_signal ' ) && $ activity ->operationAvailable ('cancel ' )) {
106+ declare (ticks = 1 );
107+ $ sigintReceived = false ;
108+ /** @noinspection PhpComposerExtensionStubsInspection */
109+ \pcntl_signal (SIGINT , function () use ($ activity , $ stdErr , $ bar , &$ sigintReceived ) {
110+ if ($ sigintReceived ) {
111+ exit (1 );
112+ }
113+ $ bar ->clear ();
114+ $ result = $ this ->cancel ($ activity , $ stdErr );
115+ if ($ result ) {
116+ exit (1 );
117+ }
118+ $ sigintReceived = true ;
119+ $ stdErr ->writeln ('' );
120+ $ bar ->advance ();
121+ });
122+ $ stdErr ->writeln ('Enter Ctrl+C once to cancel the activity (or twice to quit this command). ' );
123+ }
124+
102125 $ bar ->start ();
103126
104127 $ logStream = $ this ->getLogStream ($ activity , $ bar );
@@ -166,6 +189,40 @@ public function waitAndLog(Activity $activity, $pollInterval = 3, $timestamps =
166189 return false ;
167190 }
168191
192+ /**
193+ * Attempts to cancel the activity, catching and printing errors.
194+ *
195+ * @param Activity $activity
196+ * @param OutputInterface $stdErr
197+ *
198+ * @return bool
199+ */
200+ private function cancel (Activity $ activity , OutputInterface $ stdErr )
201+ {
202+ if (!$ activity ->operationAvailable ('cancel ' )) {
203+ $ stdErr ->writeln ('The activity cannot be cancelled. ' );
204+ return false ;
205+ }
206+ $ stdErr ->writeln ('Cancelling the activity... ' );
207+ try {
208+ try {
209+ $ activity ->cancel ();
210+ } catch (BadResponseException $ e ) {
211+ if ($ e ->getResponse () && $ e ->getResponse ()->getStatusCode () === 400 && \strpos ($ e ->getMessage (), 'cannot be cancelled in its current state ' )) {
212+ $ activity ->refresh ();
213+ $ stdErr ->writeln (\sprintf ('The activity cannot be cancelled in its current state (<error>%s</error>). ' , $ activity ->state ));
214+ return false ;
215+ }
216+ throw $ e ;
217+ }
218+ } catch (\Exception $ e ) {
219+ $ stdErr ->writeln (\sprintf ('Failed to cancel the activity: <error>%s</error> ' , $ e ->getMessage ()));
220+ return false ;
221+ }
222+ $ stdErr ->writeln ('The activity was successfully cancelled. ' );
223+ return true ;
224+ }
225+
169226 /**
170227 * Reads the log stream and returns LogItem objects.
171228 *
@@ -435,11 +492,11 @@ private function getStart(Activity $activity) {
435492 private function getLogStream (Activity $ activity , ProgressBar $ bar ) {
436493 $ url = $ activity ->getLink ('log ' );
437494
438- // Try fetching the stream with a 10 second timeout per call, and a .5
439- // second interval between calls, for up to 2 minutes.
440- $ readTimeout = 10 ;
441- $ interval = .5 ;
495+ // Try fetching the stream with an up to 10 second timeout per call,
496+ // and a .5 second interval between calls, for up to 2 minutes.
497+ $ readTimeout = .5 ;
442498 $ stream = \fopen ($ url , 'r ' , false , $ this ->api ->getStreamContext ($ readTimeout ));
499+ $ interval = .5 ;
443500 $ start = \microtime (true );
444501 while ($ stream === false ) {
445502 if (\microtime (true ) - $ start > 120 ) {
@@ -448,6 +505,7 @@ private function getLogStream(Activity $activity, ProgressBar $bar) {
448505 $ bar ->advance ();
449506 \usleep ($ interval * 1000000 );
450507 $ bar ->advance ();
508+ $ readTimeout = $ readTimeout >= 10 ? $ readTimeout : $ readTimeout + .5 ;
451509 $ stream = \fopen ($ url , 'r ' , false , $ this ->api ->getStreamContext ($ readTimeout ));
452510 }
453511 \stream_set_blocking ($ stream , 0 );
0 commit comments