Skip to content

Commit

Permalink
fix: correctly copy to clipboard in astro info (#12641)
Browse files Browse the repository at this point in the history
* fix: correctly copy to clipboard on mac

* Add platform-specific clipboard handling for Linux

* Wording

* Update .changeset/angry-pumas-act.md

Co-authored-by: Emanuele Stoppa <[email protected]>

* Use console.info

---------

Co-authored-by: Emanuele Stoppa <[email protected]>
  • Loading branch information
ascorbic and ematipico authored Dec 5, 2024
1 parent 03958d9 commit 48ca399
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-pumas-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a bug where `astro info --copy` wasn't working correctly on `macOS` systems.
107 changes: 77 additions & 30 deletions packages/astro/src/cli/info/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { execSync } from 'node:child_process';
import { spawnSync } from 'node:child_process';
import { arch, platform } from 'node:os';
import * as colors from 'kleur/colors';
import prompts from 'prompts';
Expand Down Expand Up @@ -49,63 +49,110 @@ export async function printInfo({ flags }: InfoOptions) {
applyPolyfill();
const { userConfig } = await resolveConfig(flagsToAstroInlineConfig(flags), 'info');
const output = await getInfoOutput({ userConfig, print: true });

await copyToClipboard(output);
await copyToClipboard(output, flags.copy);
}

async function copyToClipboard(text: string) {
export async function copyToClipboard(text: string, force?: boolean) {
text = text.trim();
const system = platform();
let command = '';
let args: Array<string> = [];

if (system === 'darwin') {
command = 'pbcopy';
} else if (system === 'win32') {
command = 'clip';
} else {
// Unix: check if a supported command is installed
const unixCommands = [
['xclip', '-sel clipboard -l 1'],
['wl-copy', '"$0"'],

const unixCommands: Array<[string, Array<string>]> = [
['xclip', ['-sel', 'clipboard', '-l', '1']],
['wl-copy', []],
];
for (const [unixCommand, args] of unixCommands) {
for (const [unixCommand, unixArgs] of unixCommands) {
try {
const output = execSync(`which ${unixCommand}`, { encoding: 'utf8', stdio: 'pipe' });
if (output[0] !== '/') {
// Did not find a path. Skip!
continue;
const output = spawnSync('which', [unixCommand], { encoding: 'utf8' });
if (output.stdout.trim()) {
command = unixCommand;
args = unixArgs;
break;
}
command = `${unixCommand} ${args}`;
} catch {
// Failed to execute which. Skip!
continue;
}
}
// Did not find supported command. Bail out!
if (!command) return;
}

console.log();
const { shouldCopy } = await prompts({
type: 'confirm',
name: 'shouldCopy',
message: 'Copy to clipboard?',
initial: true,
});
if (!shouldCopy) return;
if (!command) {
console.error(colors.red('\nClipboard command not found!'));
console.info('Please manually copy the text above.');
return;
}

try {
execSync(command.replaceAll('$0', text), {
stdio: 'ignore',
input: text,
encoding: 'utf8',
if (!force) {
const { shouldCopy } = await prompts({
type: 'confirm',
name: 'shouldCopy',
message: 'Copy to clipboard?',
initial: true,
});

if (!shouldCopy) return;
}

try {
const result = spawnSync(command, args, { input: text });
if (result.error) {
throw result.error;
}
console.info(colors.green('Copied to clipboard!'));
} catch {
console.error(
colors.red(`\nSorry, something went wrong!`) + ` Please copy the text above manually.`,
);
}
}

export function readFromClipboard() {
const system = platform();
let command = '';
let args: Array<string> = [];

if (system === 'darwin') {
command = 'pbpaste';
} else if (system === 'win32') {
command = 'powershell';
args = ['-command', 'Get-Clipboard'];
} else {
const unixCommands: Array<[string, Array<string>]> = [
['xclip', ['-sel', 'clipboard', '-o']],
['wl-paste', []],
];
for (const [unixCommand, unixArgs] of unixCommands) {
try {
const output = spawnSync('which', [unixCommand], { encoding: 'utf8' });
if (output.stdout.trim()) {
command = unixCommand;
args = unixArgs;
break;
}
} catch {
continue;
}
}
}

if (!command) {
throw new Error('Clipboard read command not found!');
}

const result = spawnSync(command, args, { encoding: 'utf8' });
if (result.error) {
throw result.error;
}
return result.stdout.trim();
}

const PLATFORM_TO_OS: Partial<Record<ReturnType<typeof platform>, string>> = {
darwin: 'macOS',
win32: 'Windows',
Expand Down Expand Up @@ -140,7 +187,7 @@ function printRow(label: string, value: string | string[], print: boolean) {
}
plaintext += '\n';
if (print) {
console.log(richtext);
console.info(richtext);
}
return plaintext;
}
20 changes: 20 additions & 0 deletions packages/astro/test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';
import { stripVTControlCharacters } from 'node:util';
import { cli, cliServerLogSetup, loadFixture, parseCliDevStart } from './test-utils.js';
import { readFromClipboard } from '../dist/cli/info/index.js';
import { platform } from 'node:process';

describe('astro cli', () => {
const cliServerLogSetupWithFixture = (flags, cmd) => {
Expand Down Expand Up @@ -78,6 +80,24 @@ describe('astro cli', () => {
assert.equal(proc.stdout.includes(pkgVersion), true);
});

it('astro info', async () => {
const proc = await cli('info', '--copy');
const pkgURL = new URL('../package.json', import.meta.url);
const pkgVersion = await fs.readFile(pkgURL, 'utf8').then((data) => JSON.parse(data).version);
assert.ok(proc.stdout.includes(`v${pkgVersion}`));
assert.equal(proc.exitCode, 0);

// On Linux we only check if we have Wayland or x11. In Codespaces it falsely reports that it does have x11
if(platform === 'linux' && ((!process.env.WAYLAND_DISPLAY && !process.env.DISPLAY) || process.env.CODESPACES)) {
assert.ok(proc.stdout.includes('Please manually copy the text above'));
} else {
assert.ok(proc.stdout.includes('Copied to clipboard!'));
const clipboardContent = await readFromClipboard();
assert.ok(clipboardContent.includes(`v${pkgVersion}`));
}

});

it(
'astro check no errors',
{
Expand Down

0 comments on commit 48ca399

Please sign in to comment.