@@ -21,6 +21,9 @@ const {Octokit} = require('@octokit/rest');
2121const {
2222 FIRST_PARTY_DOTSLASH_FILES ,
2323} = require ( './write-dotslash-release-asset-urls' ) ;
24+ const os = require ( 'os' ) ;
25+ const { promises : fs } = require ( 'fs' ) ;
26+ const { spawnSync} = require ( 'child_process' ) ;
2427
2528const config = {
2629 allowPositionals : true ,
@@ -93,112 +96,133 @@ async function uploadReleaseAssetsForDotSlash(
9396 const existingAssetsByName = new Map (
9497 existingAssets . data . map ( asset => [ asset . name , asset ] ) ,
9598 ) ;
96- for ( const filename of FIRST_PARTY_DOTSLASH_FILES ) {
97- const fullPath = path . join ( REPO_ROOT , filename ) ;
98- console . log ( `Uploading assets for ${ filename } ...` ) ;
99- const uploadPromises = [ ] ;
100- await processDotSlashFileInPlace (
101- fullPath ,
102- (
103- providers ,
104- // NOTE: We mostly ignore suggestedFilename in favour of reading the actual asset URLs
105- suggestedFilename ,
106- ) => {
107- let upstreamUrl , targetReleaseAssetInfo ;
108- for ( const provider of providers ) {
109- if ( provider . type != null && provider . type !== 'http' ) {
99+ const tempDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'dotslash-uploads-' ) ) ;
100+ try {
101+ for ( const filename of FIRST_PARTY_DOTSLASH_FILES ) {
102+ const fullPath = path . join ( REPO_ROOT , filename ) ;
103+ console . log ( `Uploading assets for ${ filename } ...` ) ;
104+ const uploadPromises = [ ] ;
105+ await processDotSlashFileInPlace (
106+ fullPath ,
107+ (
108+ providers ,
109+ // NOTE: We mostly ignore suggestedFilename in favour of reading the actual asset URLs
110+ suggestedFilename ,
111+ ) => {
112+ let upstreamUrl , targetReleaseAssetInfo ;
113+ for ( const provider of providers ) {
114+ if ( provider . type != null && provider . type !== 'http' ) {
115+ console . log (
116+ 'Skipping non-HTTP provider: ' + JSON . stringify ( provider ) ,
117+ ) ;
118+ continue ;
119+ }
120+ const url = provider . url ;
121+ if ( url . startsWith ( releaseAssetPrefix ) ) {
122+ const name = decodeURIComponent (
123+ url . slice ( releaseAssetPrefix . length ) ,
124+ ) ;
125+ targetReleaseAssetInfo = { name, url} ;
126+ } else {
127+ upstreamUrl = url ;
128+ }
129+ if ( upstreamUrl != null && targetReleaseAssetInfo != null ) {
130+ break ;
131+ }
132+ }
133+ if ( targetReleaseAssetInfo == null ) {
134+ // This DotSlash providers array does not reference any relevant release asset URLs, so we can ignore it.
110135 console . log (
111- 'Skipping non-HTTP provider: ' + JSON . stringify ( provider ) ,
136+ `[ ${ suggestedFilename } (suggested)] No provider URLs matched release asset prefix: ${ releaseAssetPrefix } ` ,
112137 ) ;
113- continue ;
138+ return ;
114139 }
115- const url = provider . url ;
116- if ( url . startsWith ( releaseAssetPrefix ) ) {
117- const name = decodeURIComponent (
118- url . slice ( releaseAssetPrefix . length ) ,
140+ if ( upstreamUrl == null ) {
141+ throw new Error (
142+ `No upstream URL found for release asset ${ targetReleaseAssetInfo . name } ` ,
119143 ) ;
120- targetReleaseAssetInfo = { name, url} ;
121- } else {
122- upstreamUrl = url ;
123144 }
124- if ( upstreamUrl != null && targetReleaseAssetInfo != null ) {
125- break ;
126- }
127- }
128- if ( targetReleaseAssetInfo == null ) {
129- // This DotSlash providers array does not reference any relevant release asset URLs, so we can ignore it.
130- console . log (
131- `[${ suggestedFilename } (suggested)] No provider URLs matched release asset prefix: ${ releaseAssetPrefix } ` ,
132- ) ;
133- return ;
134- }
135- if ( upstreamUrl == null ) {
136- throw new Error (
137- `No upstream URL found for release asset ${ targetReleaseAssetInfo . name } ` ,
138- ) ;
139- }
140- uploadPromises . push (
141- ( async ( ) => {
142- if ( existingAssetsByName . has ( targetReleaseAssetInfo . name ) ) {
143- if ( ! force ) {
144- console . log (
145- `[${ targetReleaseAssetInfo . name } ] Skipping existing release asset...` ,
145+ uploadPromises . push (
146+ ( async ( ) => {
147+ if ( existingAssetsByName . has ( targetReleaseAssetInfo . name ) ) {
148+ if ( ! force ) {
149+ console . log (
150+ `[${ targetReleaseAssetInfo . name } ] Skipping existing release asset...` ,
151+ ) ;
152+ return ;
153+ }
154+ if ( dryRun ) {
155+ console . log (
156+ `[${ targetReleaseAssetInfo . name } ] Dry run: Not deleting existing release asset.` ,
157+ ) ;
158+ } else {
159+ console . log (
160+ `[${ targetReleaseAssetInfo . name } ] Deleting existing release asset...` ,
161+ ) ;
162+ await octokit . repos . deleteReleaseAsset ( {
163+ owner : 'facebook' ,
164+ repo : 'react-native' ,
165+ asset_id : existingAssetsByName . get (
166+ targetReleaseAssetInfo . name ,
167+ ) . id ,
168+ } ) ;
169+ }
170+ }
171+ console . log (
172+ `[${ targetReleaseAssetInfo . name } ] Downloading from ${ upstreamUrl } ...` ,
173+ ) ;
174+ const tempFile = path . join ( tempDir , targetReleaseAssetInfo . name ) ;
175+ const curlOutput = spawnSync (
176+ // use long form of options for clarity, e.g. --silent instead of -s
177+ 'curl' ,
178+ [
179+ '--silent' ,
180+ '--location' ,
181+ '--output' ,
182+ tempFile ,
183+ upstreamUrl ,
184+ // get the content-type header
185+ '--write-out' ,
186+ '%{header_json}' ,
187+ ] ,
188+ { encoding : 'utf8' , stdio : [ 'ignore' , 'pipe' , 'pipe' ] } ,
189+ ) ;
190+ if ( curlOutput . status !== 0 ) {
191+ throw new Error (
192+ `Failed to download ${ upstreamUrl } : ${ curlOutput . stderr } ` ,
146193 ) ;
147- return ;
148194 }
195+ const data = await fs . readFile ( tempFile ) ;
196+ const headers = JSON . parse ( curlOutput . stdout ) ;
149197 if ( dryRun ) {
150198 console . log (
151- `[${ targetReleaseAssetInfo . name } ] Dry run: Not deleting existing release asset .` ,
199+ `[${ targetReleaseAssetInfo . name } ] Dry run: Not uploading to release.` ,
152200 ) ;
201+ return ;
153202 } else {
154203 console . log (
155- `[${ targetReleaseAssetInfo . name } ] Deleting existing release asset ...` ,
204+ `[${ targetReleaseAssetInfo . name } ] Uploading to release...` ,
156205 ) ;
157- await octokit . repos . deleteReleaseAsset ( {
206+ await octokit . repos . uploadReleaseAsset ( {
158207 owner : 'facebook' ,
159208 repo : 'react-native' ,
160- asset_id : existingAssetsByName . get (
161- targetReleaseAssetInfo . name ,
162- ) . id ,
209+ release_id : releaseId ,
210+ name : targetReleaseAssetInfo . name ,
211+ data,
212+ headers : {
213+ 'content-type' :
214+ headers [ 'content-type' ] ?? 'application/octet-stream' ,
215+ } ,
163216 } ) ;
164217 }
165- }
166- console . log (
167- `[${ targetReleaseAssetInfo . name } ] Downloading from ${ upstreamUrl } ...` ,
168- ) ;
169- const response = await fetch ( upstreamUrl ) ;
170- if ( ! response . ok ) {
171- throw new Error (
172- `Failed to download ${ upstreamUrl } : ${ response . status } ${ response . statusText } ` ,
173- ) ;
174- }
175- const data = await response . arrayBuffer ( ) ;
176- const contentType = response . headers . get ( 'content-type' ) ;
177- if ( dryRun ) {
178- console . log (
179- `[${ targetReleaseAssetInfo . name } ] Dry run: Not uploading to release.` ,
180- ) ;
181- return ;
182- } else {
183- console . log (
184- `[${ targetReleaseAssetInfo . name } ] Uploading to release...` ,
185- ) ;
186- await octokit . repos . uploadReleaseAsset ( {
187- owner : 'facebook' ,
188- repo : 'react-native' ,
189- release_id : releaseId ,
190- name : targetReleaseAssetInfo . name ,
191- data,
192- headers : {
193- 'content-type' : contentType ,
194- } ,
195- } ) ;
196- }
197- } ) ( ) ,
198- ) ;
199- } ,
200- ) ;
201- await Promise . all ( uploadPromises ) ;
218+ } ) ( ) ,
219+ ) ;
220+ } ,
221+ ) ;
222+ await Promise . all ( uploadPromises ) ;
223+ }
224+ } finally {
225+ await fs . rm ( tempDir , { recursive : true , force : true } ) ;
202226 }
203227}
204228
0 commit comments