@@ -40,6 +40,16 @@ const provider = rpcUrl ? getDefaultProvider(rpcUrl) : undefined;
40
40
let srcContent : string ;
41
41
let refCommitHash : string | undefined = undefined ;
42
42
43
+ // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
44
+ // @actions /toolkit when a failed upload closes the file descriptor causing any in-process reads to
45
+ // throw an uncaught exception.
46
+ process . on ( "uncaughtException" , ( error ) => {
47
+ core . setFailed ( error ) ;
48
+ if ( error . stack ) core . debug ( error . stack ) ;
49
+
50
+ return process . exit ( ) ;
51
+ } ) ;
52
+
43
53
async function run ( ) {
44
54
core . startGroup ( `Generate storage layout of contract "${ contract } " using foundry forge` ) ;
45
55
core . info ( `Start forge process` ) ;
@@ -48,130 +58,115 @@ async function run() {
48
58
const cmpLayout = parseLayout ( cmpContent ) ;
49
59
core . endGroup ( ) ;
50
60
51
- try {
52
- const localReportPath = resolve ( outReport ) ;
53
- fs . writeFileSync ( localReportPath , cmpContent ) ;
61
+ const localReportPath = resolve ( outReport ) ;
62
+ fs . writeFileSync ( localReportPath , cmpContent ) ;
54
63
55
- core . startGroup ( `Upload new report from "${ localReportPath } " as artifact named "${ outReport } "` ) ;
56
- const uploadResponse = await artifactClient . uploadArtifact (
57
- outReport ,
58
- [ localReportPath ] ,
59
- dirname ( localReportPath ) ,
60
- { continueOnError : false }
61
- ) ;
64
+ core . startGroup ( `Upload new report from "${ localReportPath } " as artifact named "${ outReport } "` ) ;
65
+ const uploadResponse = await artifactClient . uploadArtifact (
66
+ outReport ,
67
+ [ localReportPath ] ,
68
+ dirname ( localReportPath ) ,
69
+ { continueOnError : false }
70
+ ) ;
62
71
63
- if ( uploadResponse . failedItems . length > 0 )
64
- throw Error ( "Failed to upload storage layout report." ) ;
72
+ if ( uploadResponse . failedItems . length > 0 ) throw Error ( "Failed to upload storage layout report." ) ;
65
73
66
- core . info ( `Artifact ${ uploadResponse . artifactName } has been successfully uploaded!` ) ;
67
- } catch ( error : any ) {
68
- return core . setFailed ( error . message ) ;
69
- }
74
+ core . info ( `Artifact ${ uploadResponse . artifactName } has been successfully uploaded!` ) ;
70
75
core . endGroup ( ) ;
71
76
72
77
// cannot use artifactClient because downloads are limited to uploads in the same workflow run
73
78
// cf. https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts#downloading-or-deleting-artifacts
74
79
let artifactId : number | null = null ;
75
80
if ( context . eventName === "pull_request" ) {
76
- try {
81
+ core . startGroup (
82
+ `Searching artifact "${ baseReport } " on repository "${ repository } ", on branch "${ baseBranch } "`
83
+ ) ;
84
+ // Note that the artifacts are returned in most recent first order.
85
+ for await ( const res of octokit . paginate . iterator ( octokit . rest . actions . listArtifactsForRepo , {
86
+ owner,
87
+ repo,
88
+ } ) ) {
89
+ const artifact = res . data . find (
90
+ ( artifact ) => ! artifact . expired && artifact . name === baseReport
91
+ ) ;
92
+ if ( ! artifact ) {
93
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 800 ) ) ; // avoid reaching the API rate limit
94
+
95
+ continue ;
96
+ }
97
+
98
+ artifactId = artifact . id ;
99
+ refCommitHash = artifact . workflow_run ?. head_sha ;
100
+ core . info (
101
+ `Found artifact named "${ baseReport } " with ID "${ artifactId } " from commit "${ refCommitHash } "`
102
+ ) ;
103
+ break ;
104
+ }
105
+ core . endGroup ( ) ;
106
+
107
+ if ( artifactId ) {
77
108
core . startGroup (
78
- `Searching artifact "${ baseReport } " on repository "${ repository } ", on branch "${ baseBranch } "`
109
+ `Downloading artifact "${ baseReport } " of repository "${ repository } " with ID "${ artifactId } "`
79
110
) ;
80
- // Note that the artifacts are returned in most recent first order.
81
- for await ( const res of octokit . paginate . iterator ( octokit . rest . actions . listArtifactsForRepo , {
111
+ const res = await octokit . rest . actions . downloadArtifact ( {
82
112
owner,
83
113
repo,
84
- } ) ) {
85
- const artifact = res . data . find (
86
- ( artifact ) => ! artifact . expired && artifact . name === baseReport
87
- ) ;
88
- if ( ! artifact ) {
89
- await new Promise ( ( resolve ) => setTimeout ( resolve , 800 ) ) ; // avoid reaching the API rate limit
90
-
91
- continue ;
92
- }
93
-
94
- artifactId = artifact . id ;
95
- refCommitHash = artifact . workflow_run ?. head_sha ;
96
- core . info (
97
- `Found artifact named "${ baseReport } " with ID "${ artifactId } " from commit "${ refCommitHash } "`
98
- ) ;
99
- break ;
114
+ artifact_id : artifactId ,
115
+ archive_format : "zip" ,
116
+ } ) ;
117
+
118
+ // @ts -ignore data is unknown
119
+ const zip = new Zip ( Buffer . from ( res . data ) ) ;
120
+ for ( const entry of zip . getEntries ( ) ) {
121
+ core . info ( `Loading storage layout report from "${ entry . entryName } "` ) ;
122
+ srcContent = zip . readAsText ( entry ) ;
100
123
}
101
124
core . endGroup ( ) ;
102
-
103
- if ( artifactId ) {
104
- core . startGroup (
105
- `Downloading artifact "${ baseReport } " of repository "${ repository } " with ID "${ artifactId } "`
106
- ) ;
107
- const res = await octokit . rest . actions . downloadArtifact ( {
108
- owner,
109
- repo,
110
- artifact_id : artifactId ,
111
- archive_format : "zip" ,
112
- } ) ;
113
-
114
- // @ts -ignore data is unknown
115
- const zip = new Zip ( Buffer . from ( res . data ) ) ;
116
- for ( const entry of zip . getEntries ( ) ) {
117
- core . info ( `Loading storage layout report from "${ entry . entryName } "` ) ;
118
- srcContent = zip . readAsText ( entry ) ;
119
- }
120
- core . endGroup ( ) ;
121
- } else return core . error ( `No workflow run found with an artifact named "${ baseReport } "` ) ;
122
- } catch ( error : any ) {
123
- return core . setFailed ( error . message ) ;
124
- }
125
+ } else throw Error ( `No workflow run found with an artifact named "${ baseReport } "` ) ;
125
126
}
126
127
127
- try {
128
- core . info ( `Mapping reference storage layout report` ) ;
129
- const srcLayout = parseLayout ( srcContent ) ;
130
- core . endGroup ( ) ;
131
-
132
- core . startGroup ( "Check storage layout" ) ;
133
- const diffs = await checkLayouts ( srcLayout , cmpLayout , {
134
- address,
135
- provider,
136
- checkRemovals : failOnRemoval ,
137
- } ) ;
128
+ core . info ( `Mapping reference storage layout report` ) ;
129
+ const srcLayout = parseLayout ( srcContent ) ;
130
+ core . endGroup ( ) ;
138
131
139
- if ( diffs . length > 0 ) {
140
- core . info ( `Parse source code` ) ;
141
- const cmpDef = parseSource ( contractAbs ) ;
142
-
143
- const formattedDiffs = diffs . map ( ( diff ) => {
144
- const formattedDiff = formatDiff ( cmpDef , diff ) ;
145
-
146
- const title = diffTitles [ formattedDiff . type ] ;
147
- const level = diffLevels [ formattedDiff . type ] || "error" ;
148
- core [ level ] ( formattedDiff . message , {
149
- title,
150
- file : cmpDef . path ,
151
- startLine : formattedDiff . loc . start . line ,
152
- endLine : formattedDiff . loc . end . line ,
153
- startColumn : formattedDiff . loc . start . column ,
154
- endColumn : formattedDiff . loc . end . column ,
155
- } ) ;
156
-
157
- return formattedDiff ;
132
+ core . startGroup ( "Check storage layout" ) ;
133
+ const diffs = await checkLayouts ( srcLayout , cmpLayout , {
134
+ address,
135
+ provider,
136
+ checkRemovals : failOnRemoval ,
137
+ } ) ;
138
+
139
+ if ( diffs . length > 0 ) {
140
+ core . info ( `Parse source code` ) ;
141
+ const cmpDef = parseSource ( contractAbs ) ;
142
+
143
+ const formattedDiffs = diffs . map ( ( diff ) => {
144
+ const formattedDiff = formatDiff ( cmpDef , diff ) ;
145
+
146
+ const title = diffTitles [ formattedDiff . type ] ;
147
+ const level = diffLevels [ formattedDiff . type ] || "error" ;
148
+ core [ level ] ( formattedDiff . message , {
149
+ title,
150
+ file : cmpDef . path ,
151
+ startLine : formattedDiff . loc . start . line ,
152
+ endLine : formattedDiff . loc . end . line ,
153
+ startColumn : formattedDiff . loc . start . column ,
154
+ endColumn : formattedDiff . loc . end . column ,
158
155
} ) ;
159
156
160
- if (
161
- formattedDiffs . filter ( ( diff ) => diffLevels [ diff . type ] === "error" ) . length > 0 ||
162
- ( failOnRemoval &&
163
- formattedDiffs . filter ( ( diff ) => diff . type === StorageLayoutDiffType . VARIABLE_REMOVED )
164
- . length > 0 )
165
- )
166
- return core . setFailed (
167
- "Unsafe storage layout changes detected. Please see above for details."
168
- ) ;
169
- }
157
+ return formattedDiff ;
158
+ } ) ;
170
159
171
- core . endGroup ( ) ;
172
- } catch ( error : any ) {
173
- core . setFailed ( error . message ) ;
160
+ if (
161
+ formattedDiffs . filter ( ( diff ) => diffLevels [ diff . type ] === "error" ) . length > 0 ||
162
+ ( failOnRemoval &&
163
+ formattedDiffs . filter ( ( diff ) => diff . type === StorageLayoutDiffType . VARIABLE_REMOVED )
164
+ . length > 0 )
165
+ )
166
+ throw Error ( "Unsafe storage layout changes detected. Please see above for details." ) ;
174
167
}
168
+
169
+ core . endGroup ( ) ;
175
170
}
176
171
177
172
run ( ) ;
0 commit comments