44 * Copyright (C) 2020-2022 Posit Software, PBC
55 */
66
7- import { runningInCI } from "../core/ci-info.ts" ;
87import { GitHubRelease } from "./types.ts" ;
98
109// deno-lint-ignore-file camelcase
1110
11+ // GitHub Actions Detection
12+ export function isGitHubActions ( ) : boolean {
13+ return Deno . env . get ( "GITHUB_ACTIONS" ) === "true" ;
14+ }
15+
16+ export function isVerboseMode ( ) : boolean {
17+ return Deno . env . get ( "RUNNER_DEBUG" ) === "1" ||
18+ Deno . env . get ( "QUARTO_TEST_VERBOSE" ) === "true" ;
19+ }
20+
21+ // GitHub Actions Workflow Command Escaping
22+ // See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions
23+ export function escapeData ( s : string ) : string {
24+ return s
25+ . replace ( / % / g, "%25" )
26+ . replace ( / \r / g, "%0D" )
27+ . replace ( / \n / g, "%0A" ) ;
28+ }
29+
30+ export function escapeProperty ( s : string ) : string {
31+ return s
32+ . replace ( / % / g, "%25" )
33+ . replace ( / \r / g, "%0D" )
34+ . replace ( / \n / g, "%0A" )
35+ . replace ( / : / g, "%3A" )
36+ . replace ( / , / g, "%2C" ) ;
37+ }
38+
39+ // GitHub Actions Annotations
40+ export interface AnnotationProperties {
41+ file ?: string ;
42+ line ?: number ;
43+ endLine ?: number ;
44+ title ?: string ;
45+ }
46+
47+ function formatProperties ( props : AnnotationProperties ) : string {
48+ const parts : string [ ] = [ ] ;
49+ if ( props . file !== undefined ) {
50+ parts . push ( `file=${ escapeProperty ( props . file ) } ` ) ;
51+ }
52+ if ( props . line !== undefined ) parts . push ( `line=${ props . line } ` ) ;
53+ if ( props . endLine !== undefined ) parts . push ( `endLine=${ props . endLine } ` ) ;
54+ if ( props . title !== undefined ) {
55+ parts . push ( `title=${ escapeProperty ( props . title ) } ` ) ;
56+ }
57+ return parts . length > 0 ? " " + parts . join ( "," ) : "" ;
58+ }
59+
60+ export function error (
61+ message : string ,
62+ properties ?: AnnotationProperties ,
63+ ) : void {
64+ if ( ! isGitHubActions ( ) ) return ;
65+ const props = properties ? formatProperties ( properties ) : "" ;
66+ console . log ( `::error${ props } ::${ escapeData ( message ) } ` ) ;
67+ }
68+
69+ export function warning (
70+ message : string ,
71+ properties ?: AnnotationProperties ,
72+ ) : void {
73+ if ( ! isGitHubActions ( ) ) return ;
74+ const props = properties ? formatProperties ( properties ) : "" ;
75+ console . log ( `::warning${ props } ::${ escapeData ( message ) } ` ) ;
76+ }
77+
78+ export function notice (
79+ message : string ,
80+ properties ?: AnnotationProperties ,
81+ ) : void {
82+ if ( ! isGitHubActions ( ) ) return ;
83+ const props = properties ? formatProperties ( properties ) : "" ;
84+ console . log ( `::notice${ props } ::${ escapeData ( message ) } ` ) ;
85+ }
86+
87+ // GitHub Actions Log Grouping
88+ export function startGroup ( title : string ) : void {
89+ if ( ! isGitHubActions ( ) ) return ;
90+ console . log ( `::group::${ escapeData ( title ) } ` ) ;
91+ }
92+
93+ export function endGroup ( ) : void {
94+ if ( ! isGitHubActions ( ) ) return ;
95+ console . log ( "::endgroup::" ) ;
96+ }
97+
98+ export function withGroup < T > ( title : string , fn : ( ) => T ) : T {
99+ startGroup ( title ) ;
100+ try {
101+ return fn ( ) ;
102+ } finally {
103+ endGroup ( ) ;
104+ }
105+ }
106+
107+ export async function withGroupAsync < T > (
108+ title : string ,
109+ fn : ( ) => Promise < T > ,
110+ ) : Promise < T > {
111+ startGroup ( title ) ;
112+ try {
113+ return await fn ( ) ;
114+ } finally {
115+ endGroup ( ) ;
116+ }
117+ }
118+
119+ // Legacy group function for backward compatibility and alia
120+ export async function group < T > (
121+ title : string ,
122+ fn : ( ) => Promise < T > ,
123+ ) : Promise < T > {
124+ return await withGroupAsync ( title , fn ) ;
125+ }
126+
127+ // GitHub API
128+
12129// A Github Release for a Github Repo
13130
14131// Look up the latest release for a Github Repo
@@ -26,37 +143,3 @@ export async function getLatestRelease(repo: string): Promise<GitHubRelease> {
26143 return response . json ( ) ;
27144 }
28145}
29-
30- // NB we do not escape these here - it's the caller's responsibility to do so
31- function githubActionsWorkflowCommand (
32- command : string ,
33- value = "" ,
34- params ?: Record < string , string > ,
35- ) {
36- let paramsStr = "" ;
37- if ( params ) {
38- paramsStr = " " ;
39- let first = false ;
40- for ( const [ key , val ] of Object . entries ( params ) ) {
41- if ( ! first ) {
42- first = true ;
43- } else {
44- paramsStr += "," ;
45- }
46- paramsStr += `${ key } =${ val } ` ;
47- }
48- }
49- return `::${ command } ${ paramsStr } ::${ value } ` ;
50- }
51-
52- export async function group < T > ( title : string , fn : ( ) => Promise < T > ) {
53- if ( ! runningInCI ( ) ) {
54- return fn ( ) ;
55- }
56- console . log ( githubActionsWorkflowCommand ( "group" , title ) ) ;
57- try {
58- return await fn ( ) ;
59- } finally {
60- console . log ( githubActionsWorkflowCommand ( "endgroup" ) ) ;
61- }
62- }
0 commit comments