Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(@clack/prompts): adapt spinner to CI environment #169

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/thin-moose-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clack/prompts': patch
---

Adapt `spinner` to CI environment
3 changes: 2 additions & 1 deletion examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
},
"scripts": {
"start": "jiti ./index.ts",
"spinner": "jiti ./spinner.ts"
"spinner": "jiti ./spinner.ts",
"spinner-ci": "npx cross-env GITHUB_ACTIONS=\"true\" jiti ./spinner-ci.ts"
},
"devDependencies": {
"jiti": "^1.17.0"
Expand Down
36 changes: 36 additions & 0 deletions examples/basic/spinner-ci.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* This example addresses a issue reported in GitHub Actions where `spinner` was excessively writing messages,
* leading to confusion and cluttered output.
* To enhance the CI workflow and provide a smoother experience,
* the following changes have been made only for CI environment:
* - Messages will now only be written when a `spinner` method is called and the message updated, preventing unnecessary message repetition.
* - There will be no loading dots animation, instead it will be always `...`
* - Instead of erase the previous message, action that is blocked during CI, it will just write a new one.
*
* Issue: https://github.com/natemoo-re/clack/issues/168
*/
import * as p from '@clack/prompts';

const s = p.spinner();
let progress = 0;
let counter = 0;
let loop: NodeJS.Timer;

p.intro('Running spinner in CI environment');
s.start('spinner.start');
new Promise((resolve) => {
loop = setInterval(() => {
if (progress % 1000 === 0) {
counter++;
}
progress += 100;
s.message(`spinner.message [${counter}]`);
if (counter > 6) {
clearInterval(loop);
resolve(true);
}
}, 100);
}).then(() => {
s.stop('spinner.stop');
p.outro('Done');
});
2 changes: 1 addition & 1 deletion examples/basic/spinner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as p from '@clack/prompts';
p.intro('spinner start...');

const spin = p.spinner();
const total = 10000;
const total = 6000;
let progress = 0;
spin.start();

Expand Down
3 changes: 2 additions & 1 deletion packages/prompts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
"dependencies": {
"@clack/core": "workspace:*",
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
"sisteransi": "^1.0.5",
"std-env": "^3.4.3"
},
"devDependencies": {
"is-unicode-supported": "^1.3.0"
Expand Down
32 changes: 24 additions & 8 deletions packages/prompts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import isUnicodeSupported from 'is-unicode-supported';
import color from 'picocolors';
import { cursor, erase } from 'sisteransi';
import { isCI } from 'std-env';

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

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

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

const clearPrevMessage = () => {
if (_prevMessage === undefined) return;
if (isCI) process.stdout.write('\n');
const prevLines = _prevMessage.split('\n');
process.stdout.write(cursor.move(-999, prevLines.length - 1));
process.stdout.write(erase.down(prevLines.length));
};

const parseMessage = (msg: string): string => {
return msg.replace(/\.+$/, '');
};

const start = (msg: string = ''): void => {
isSpinnerActive = true;
unblock = block();
_message = msg.replace(/\.+$/, '');
_message = parseMessage(msg);
process.stdout.write(`${color.gray(S_BAR)}\n`);
let frameIndex = 0;
let dotsTimer = 0;
registerHooks();
loop = setInterval(() => {
if (isCI && _message === _prevMessage) {
return;
}
clearPrevMessage();
_prevMessage = _message;
const frame = color.magenta(frames[frameIndex]);
const loadingDots = '.'.repeat(Math.floor(dotsTimer)).slice(0, 3);
process.stdout.write(cursor.move(-999, 0));
process.stdout.write(erase.down(1));
const loadingDots = isCI ? '...' : '.'.repeat(Math.floor(dotsTimer)).slice(0, 3);
process.stdout.write(`${frame} ${_message}${loadingDots}`);
frameIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0;
dotsTimer = dotsTimer < frames.length ? dotsTimer + 0.125 : 0;
}, delay);
};

const stop = (msg: string = '', code: number = 0): void => {
_message = msg ?? _message;
isSpinnerActive = false;
clearInterval(loop);
clearPrevMessage();
const step =
code === 0
? color.green(S_STEP_SUBMIT)
: code === 1
? color.red(S_STEP_CANCEL)
: color.red(S_STEP_ERROR);
process.stdout.write(cursor.move(-999, 0));
process.stdout.write(erase.down(1));
_message = parseMessage(msg ?? _message);
process.stdout.write(`${step} ${_message}\n`);
clearHooks();
unblock();
};

const message = (msg: string = ''): void => {
_message = msg ?? _message;
_message = parseMessage(msg ?? _message);
};

return {
Expand Down
21 changes: 16 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.