1+ function parseGnuTime ( content , fields = [ ] ) {
2+ // Define mappings between time output and our metric names
3+ const TIME_METRICS = {
4+ 'Command being timed' : 'command' ,
5+ 'User time (seconds)' : 'user_time_seconds' ,
6+ 'System time (seconds)' : 'system_time_seconds' ,
7+ 'Percent of CPU this job got' : 'cpu_usage_percent' ,
8+ 'Elapsed (wall clock) time (h:mm:ss or m:ss)' : 'elapsed_time' ,
9+ 'Maximum resident set size (kbytes)' : 'peak_memory_kb' ,
10+ 'Average shared text size (kbytes)' : 'avg_shared_text_kb' ,
11+ 'Average unshared data size (kbytes)' : 'avg_unshared_data_kb' ,
12+ 'Average stack size (kbytes)' : 'avg_stack_kb' ,
13+ 'Average total size (kbytes)' : 'avg_total_kb' ,
14+ 'Major (requiring I/O) page faults' : 'major_page_faults' ,
15+ 'Minor (reclaiming a frame) page faults' : 'minor_page_faults' ,
16+ 'Voluntary context switches' : 'voluntary_ctx_switches' ,
17+ 'Involuntary context switches' : 'involuntary_ctx_switches' ,
18+ 'Swaps' : 'swaps' ,
19+ 'File system inputs' : 'fs_inputs' ,
20+ 'File system outputs' : 'fs_outputs' ,
21+ 'Socket messages sent' : 'socket_msgs_sent' ,
22+ 'Socket messages received' : 'socket_msgs_received' ,
23+ 'Signals delivered' : 'signals_delivered' ,
24+ 'Page size (bytes)' : 'page_size_bytes' ,
25+ 'Exit status' : 'exit_status'
26+ } ;
27+
28+ // Parse metrics
29+ const parsed = { } ;
30+ const lines = content . split ( '\n' ) ;
31+ for ( const _line of lines ) {
32+ const line = _line . trimStart ( ) . trimEnd ( ) ;
33+ for ( const [ key , metric ] of Object . entries ( TIME_METRICS ) ) {
34+ const match = line . startsWith ( key + ':' ) ? [ line , line . slice ( key . length + 1 ) . trim ( ) ] : null ;
35+ if ( match ) {
36+ let value = match [ 1 ] . trim ( ) ;
37+
38+ // Handle special cases
39+ if ( metric === 'command' ) {
40+ value = value . replace ( / ^ " | " $ / g, '' ) ; // Remove quotes
41+ } else if ( metric === 'cpu_usage_percent' ) {
42+ value = parseFloat ( value . replace ( '%' , '' ) ) ; // Remove % sign and convert to number
43+ } else if ( metric === 'elapsed_time' ) {
44+ // Keep elapsed_time as string since it's a time format
45+ value = value ;
46+ } else if ( ! isNaN ( value ) ) {
47+ // Convert any numeric strings to numbers
48+ value = value . includes ( '.' ) ? parseFloat ( value ) : parseInt ( value , 10 ) ;
49+ }
50+
51+ parsed [ metric ] = value ;
52+ }
53+ }
54+ }
55+
56+ // Convert KB to GB for memory metrics
57+ for ( const [ key , value ] of Object . entries ( parsed ) ) {
58+ if ( key . endsWith ( '_kb' ) ) {
59+ const gbKey = key . replace ( '_kb' , '_gb' ) ;
60+ parsed [ gbKey ] = parseFloat ( ( value / 1048576 ) . toFixed ( 2 ) ) ;
61+ }
62+ }
63+
64+ // Filter metrics if fields are specified
65+ const filtered = { command : parsed . command } ;
66+ if ( fields . length > 0 ) {
67+ for ( const field of fields ) {
68+ if ( parsed [ field ] !== undefined ) {
69+ filtered [ field ] = parsed [ field ] ;
70+ }
71+ }
72+ } else {
73+ Object . assign ( filtered , parsed ) ;
74+ }
75+
76+ return {
77+ command : filtered . command ,
78+ metrics : Object . fromEntries (
79+ Object . entries ( filtered ) . filter ( ( [ key ] ) => key !== 'command' )
80+ )
81+ } ;
82+ }
83+
84+ // When running as a script
85+ if ( require . main === module ) {
86+ const core = require ( '@actions/core' ) ;
87+ try {
88+ const content = core . getInput ( 'content' , { required : true } ) ;
89+ const fields = JSON . parse ( core . getInput ( 'fields' ) || '[]' ) ;
90+
91+ const result = parseGnuTime ( content , fields ) ;
92+ core . setOutput ( 'json' , JSON . stringify ( result ) ) ;
93+ } catch ( error ) {
94+ core . setFailed ( error . message ) ;
95+ }
96+ }
97+
98+ // Export for testing
99+ module . exports = parseGnuTime ;
0 commit comments