Skip to content

Commit e064e4c

Browse files
committed
Merge branch 'static-ci' of https://github.com/Mist3rBru/clack into static-ci
2 parents 45ee73b + 7e34e93 commit e064e4c

File tree

7 files changed

+2226
-1777
lines changed

7 files changed

+2226
-1777
lines changed

.changeset/thin-moose-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clack/prompts': patch
3+
---
4+
5+
Adapt `spinner` to CI environment

examples/basic/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
},
1111
"scripts": {
1212
"start": "jiti ./index.ts",
13-
"spinner": "jiti ./spinner.ts"
13+
"spinner": "jiti ./spinner.ts",
14+
"spinner-ci": "npx cross-env GITHUB_ACTIONS=\"true\" jiti ./spinner-ci.ts"
1415
},
1516
"devDependencies": {
1617
"jiti": "^1.17.0"

examples/basic/spinner-ci.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* This example addresses a issue reported in GitHub Actions where `spinner` was excessively writing messages,
3+
* leading to confusion and cluttered output.
4+
* To enhance the CI workflow and provide a smoother experience,
5+
* the following changes have been made only for CI environment:
6+
* - Messages will now only be written when a `spinner` method is called and the message updated, preventing unnecessary message repetition.
7+
* - There will be no loading dots animation, instead it will be always `...`
8+
* - Instead of erase the previous message, action that is blocked during CI, it will just write a new one.
9+
*
10+
* Issue: https://github.com/natemoo-re/clack/issues/168
11+
*/
12+
import * as p from '@clack/prompts';
13+
14+
const s = p.spinner();
15+
let progress = 0;
16+
let counter = 0;
17+
let loop: NodeJS.Timer;
18+
19+
p.intro('Running spinner in CI environment');
20+
s.start('spinner.start');
21+
new Promise((resolve) => {
22+
loop = setInterval(() => {
23+
if (progress % 1000 === 0) {
24+
counter++;
25+
}
26+
progress += 100;
27+
s.message(`spinner.message [${counter}]`);
28+
if (counter > 6) {
29+
clearInterval(loop);
30+
resolve(true);
31+
}
32+
}, 100);
33+
}).then(() => {
34+
s.stop('spinner.stop');
35+
p.outro('Done');
36+
});

examples/basic/spinner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as p from '@clack/prompts';
33
p.intro('spinner start...');
44

55
const spin = p.spinner();
6-
const total = 10000;
6+
const total = 6000;
77
let progress = 0;
88
spin.start();
99

packages/prompts/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
"dependencies": {
5656
"@clack/core": "workspace:*",
5757
"picocolors": "^1.0.0",
58-
"sisteransi": "^1.0.5"
58+
"sisteransi": "^1.0.5",
59+
"std-env": "^3.4.3"
5960
},
6061
"devDependencies": {
6162
"is-unicode-supported": "^1.3.0"

packages/prompts/src/index.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import isUnicodeSupported from 'is-unicode-supported';
1414
import color from 'picocolors';
1515
import { cursor, erase } from 'sisteransi';
16+
import { isCI } from 'std-env';
1617

1718
export { isCancel } from '@clack/core';
1819

@@ -644,6 +645,7 @@ export const spinner = () => {
644645
let loop: NodeJS.Timeout;
645646
let isSpinnerActive: boolean = false;
646647
let _message: string = '';
648+
let _prevMessage: string | undefined = undefined;
647649

648650
const handleExit = (code: number) => {
649651
const msg = code > 1 ? 'Something went wrong' : 'Canceled';
@@ -672,44 +674,58 @@ export const spinner = () => {
672674
process.removeListener('exit', handleExit);
673675
};
674676

677+
const clearPrevMessage = () => {
678+
if (_prevMessage === undefined) return;
679+
if (isCI) process.stdout.write('\n');
680+
const prevLines = _prevMessage.split('\n');
681+
process.stdout.write(cursor.move(-999, prevLines.length - 1));
682+
process.stdout.write(erase.down(prevLines.length));
683+
};
684+
685+
const parseMessage = (msg: string): string => {
686+
return msg.replace(/\.+$/, '');
687+
};
688+
675689
const start = (msg: string = ''): void => {
676690
isSpinnerActive = true;
677691
unblock = block();
678-
_message = msg.replace(/\.+$/, '');
692+
_message = parseMessage(msg);
679693
process.stdout.write(`${color.gray(S_BAR)}\n`);
680694
let frameIndex = 0;
681695
let dotsTimer = 0;
682696
registerHooks();
683697
loop = setInterval(() => {
698+
if (isCI && _message === _prevMessage) {
699+
return;
700+
}
701+
clearPrevMessage();
702+
_prevMessage = _message;
684703
const frame = color.magenta(frames[frameIndex]);
685-
const loadingDots = '.'.repeat(Math.floor(dotsTimer)).slice(0, 3);
686-
process.stdout.write(cursor.move(-999, 0));
687-
process.stdout.write(erase.down(1));
704+
const loadingDots = isCI ? '...' : '.'.repeat(Math.floor(dotsTimer)).slice(0, 3);
688705
process.stdout.write(`${frame} ${_message}${loadingDots}`);
689706
frameIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0;
690707
dotsTimer = dotsTimer < frames.length ? dotsTimer + 0.125 : 0;
691708
}, delay);
692709
};
693710

694711
const stop = (msg: string = '', code: number = 0): void => {
695-
_message = msg ?? _message;
696712
isSpinnerActive = false;
697713
clearInterval(loop);
714+
clearPrevMessage();
698715
const step =
699716
code === 0
700717
? color.green(S_STEP_SUBMIT)
701718
: code === 1
702719
? color.red(S_STEP_CANCEL)
703720
: color.red(S_STEP_ERROR);
704-
process.stdout.write(cursor.move(-999, 0));
705-
process.stdout.write(erase.down(1));
721+
_message = parseMessage(msg ?? _message);
706722
process.stdout.write(`${step} ${_message}\n`);
707723
clearHooks();
708724
unblock();
709725
};
710726

711727
const message = (msg: string = ''): void => {
712-
_message = msg ?? _message;
728+
_message = parseMessage(msg ?? _message);
713729
};
714730

715731
return {

0 commit comments

Comments
 (0)