Skip to content
Merged
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
13 changes: 13 additions & 0 deletions tools/pr-meta/src/__tests__/comment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,19 @@ describe( 'marker attributes', () => {
expect( parseSections( merged )[ 0 ].runUrl ).toBe( runUrl );
} );

it( 'keeps a run link with a query string', () => {
const runUrl = 'https://ci.example/runs/123?attempt=2';
const merged = bodyOf(
mergeSection(
undefined,
{ id: 'bundle-size', body: 'Size.', sha: HEAD, runUrl },
HEAD
)
);

expect( parseSections( merged )[ 0 ].runUrl ).toBe( runUrl );
} );

it.each( [
[ 'a space', 'https://example.com/a b' ],
[ 'a closing bracket', 'https://example.com/a>b' ],
Expand Down
38 changes: 38 additions & 0 deletions tools/pr-meta/src/__tests__/core.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { infoVerbatim } from '../core.ts';

describe( 'infoVerbatim', () => {
afterEach( () => {
vi.restoreAllMocks();
} );

it( 'stops workflow commands around the message and resumes them', () => {
const write = vi
.spyOn( process.stdout, 'write' )
.mockImplementation( () => true );

infoVerbatim( '::add-mask::secret\n::warning::spoofed' );

const lines = write.mock.calls.map( ( [ chunk ] ) => String( chunk ) );
const token = lines[ 0 ].match( /^::stop-commands::(.+)\n$/ )?.[ 1 ];

expect( token ).toBeTruthy();
expect( lines[ 1 ] ).toBe( '::add-mask::secret\n::warning::spoofed\n' );
expect( lines[ 2 ] ).toBe( `::${ token }::\n` );
} );

it( 'uses a fresh token each time', () => {
const write = vi
.spyOn( process.stdout, 'write' )
.mockImplementation( () => true );

infoVerbatim( 'one' );
infoVerbatim( 'two' );

const starts = write.mock.calls
.map( ( [ chunk ] ) => String( chunk ) )
.filter( ( line ) => line.startsWith( '::stop-commands::' ) );

expect( new Set( starts ).size ).toBe( 2 );
} );
} );
5 changes: 4 additions & 1 deletion tools/pr-meta/src/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ function parseAttributes(
const attributes: Pick< ParsedSection, 'sha' | 'runUrl' > = {};

for ( const pair of raw.trim().split( /\s+/ ).filter( Boolean ) ) {
const [ key, value ] = pair.split( '=' );
/* Split at the first `=` only: a run link can carry a query string. */
const separator = pair.indexOf( '=' );
const key = separator < 0 ? pair : pair.slice( 0, separator );
const value = separator < 0 ? undefined : pair.slice( separator + 1 );
if ( key === 'sha' ) {
attributes.sha = safeSha( value );
} else if ( key === 'run' ) {
Expand Down
13 changes: 13 additions & 0 deletions tools/pr-meta/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { randomUUID } from 'node:crypto';
import { readFileSync } from 'node:fs';

/*
Expand All @@ -18,6 +19,18 @@ export function info( message: string ): void {
process.stdout.write( `${ message }\n` );
}

/* Logs text that may hold `::` lines without the runner acting on them. */
export function infoVerbatim( message: string ): void {
const token = randomUUID();

info( `::stop-commands::${ token }` );
try {
info( message );
} finally {
info( `::${ token }::` );
}
}

function escapeData( value: string ): string {
return value
.replace( /%/g, '%25' )
Expand Down
4 changes: 2 additions & 2 deletions tools/pr-meta/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { info, warning } from './core.ts';
import { info, infoVerbatim, warning } from './core.ts';
import { resolveBody, run } from './run.ts';

/*
Expand All @@ -14,7 +14,7 @@ run().catch( ( error ) => {
);

try {
info( resolveBody() );
infoVerbatim( resolveBody() );
} catch {
info( 'The section body could not be read either.' );
}
Expand Down
Loading