2017-10-23 15:02:50 +00:00
#!/usr/bin/env node
2020-03-20 20:28:31 +00:00
if ( ! process . env . CI ) require ( 'dotenv-safe' ) . load ( ) ;
2019-08-29 14:46:54 +00:00
2018-12-20 03:36:01 +00:00
const args = require ( 'minimist' ) ( process . argv . slice ( 2 ) , {
boolean : [
'validateRelease' ,
2019-01-23 19:47:36 +00:00
'verboseNugget'
] ,
2020-03-20 15:12:18 +00:00
default : { verboseNugget : false }
2020-03-20 20:28:31 +00:00
} ) ;
2023-06-22 14:21:42 +00:00
const fs = require ( 'node:fs' ) ;
const { execSync } = require ( 'node:child_process' ) ;
2020-03-20 20:28:31 +00:00
const got = require ( 'got' ) ;
2023-06-22 14:21:42 +00:00
const path = require ( 'node:path' ) ;
2023-02-13 09:16:27 +00:00
const semver = require ( 'semver' ) ;
2020-03-20 20:28:31 +00:00
const temp = require ( 'temp' ) . track ( ) ;
2022-04-04 09:32:57 +00:00
const { BlobServiceClient } = require ( '@azure/storage-blob' ) ;
2020-08-05 15:59:52 +00:00
const { Octokit } = require ( '@octokit/rest' ) ;
2020-03-20 20:28:31 +00:00
require ( 'colors' ) ;
const pass = '✓' . green ;
const fail = '✗' . red ;
const { ELECTRON _DIR } = require ( '../lib/utils' ) ;
2022-10-25 06:44:43 +00:00
const { getElectronVersion } = require ( '../lib/get-version' ) ;
2021-05-11 16:30:35 +00:00
const getUrlHash = require ( './get-url-hash' ) ;
2024-09-23 18:07:16 +00:00
const { createGitHubTokenStrategy } = require ( './github-token' ) ;
2019-06-24 17:18:04 +00:00
2022-10-25 06:44:43 +00:00
const pkgVersion = ` v ${ getElectronVersion ( ) } ` ;
2024-05-31 17:58:39 +00:00
function getRepo ( ) {
return pkgVersion . indexOf ( 'nightly' ) > 0 ? 'nightlies' : 'electron' ;
}
const targetRepo = getRepo ( ) ;
2020-03-20 20:28:31 +00:00
let failureCount = 0 ;
2017-10-23 15:02:50 +00:00
2024-09-23 18:07:16 +00:00
const octokit = new Octokit ( {
authStrategy : createGitHubTokenStrategy ( targetRepo )
} ) ;
2017-10-23 15:02:50 +00:00
async function getDraftRelease ( version , skipValidation ) {
2019-01-08 20:05:58 +00:00
const releaseInfo = await octokit . repos . listReleases ( {
owner : 'electron' ,
repo : targetRepo
2020-03-20 20:28:31 +00:00
} ) ;
2019-01-08 20:05:58 +00:00
2020-03-20 20:28:31 +00:00
const versionToCheck = version || pkgVersion ;
2019-01-08 20:05:58 +00:00
const drafts = releaseInfo . data . filter ( release => {
2020-03-20 20:28:31 +00:00
return release . tag _name === versionToCheck && release . draft === true ;
} ) ;
2019-01-08 20:05:58 +00:00
2020-03-20 20:28:31 +00:00
const draft = drafts [ 0 ] ;
2017-10-23 15:02:50 +00:00
if ( ! skipValidation ) {
2020-03-20 20:28:31 +00:00
failureCount = 0 ;
check ( drafts . length === 1 , 'one draft exists' , true ) ;
2023-07-24 10:32:54 +00:00
if ( versionToCheck . includes ( 'beta' ) ) {
2020-03-20 20:28:31 +00:00
check ( draft . prerelease , 'draft is a prerelease' ) ;
2017-10-23 15:02:50 +00:00
}
2020-03-20 20:28:31 +00:00
check ( draft . body . length > 50 && ! draft . body . includes ( '(placeholder)' ) , 'draft has release notes' ) ;
check ( ( failureCount === 0 ) , 'Draft release looks good to go.' , true ) ;
2017-10-23 15:02:50 +00:00
}
2020-03-20 20:28:31 +00:00
return draft ;
2017-10-23 15:02:50 +00:00
}
2018-01-31 23:40:38 +00:00
async function validateReleaseAssets ( release , validatingRelease ) {
2020-03-20 20:28:31 +00:00
const requiredAssets = assetsForVersion ( release . tag _name , validatingRelease ) . sort ( ) ;
const extantAssets = release . assets . map ( asset => asset . name ) . sort ( ) ;
2021-05-11 16:30:35 +00:00
const downloadUrls = release . assets . map ( asset => ( { url : asset . browser _download _url , file : asset . name } ) ) . sort ( ( a , b ) => a . file . localeCompare ( b . file ) ) ;
2017-10-23 15:02:50 +00:00
2020-03-20 20:28:31 +00:00
failureCount = 0 ;
2023-08-31 14:36:43 +00:00
for ( const asset of requiredAssets ) {
2020-03-20 20:28:31 +00:00
check ( extantAssets . includes ( asset ) , asset ) ;
2023-08-31 14:36:43 +00:00
}
2020-03-20 20:28:31 +00:00
check ( ( failureCount === 0 ) , 'All required GitHub assets exist for release' , true ) ;
2017-10-23 15:02:50 +00:00
2018-02-22 13:53:32 +00:00
if ( ! validatingRelease || ! release . draft ) {
if ( release . draft ) {
2021-05-11 16:30:35 +00:00
await verifyDraftGitHubReleaseAssets ( release ) ;
2018-02-22 13:53:32 +00:00
} else {
2021-05-11 16:30:35 +00:00
await verifyShasumsForRemoteFiles ( downloadUrls )
2018-02-22 13:53:32 +00:00
. catch ( err => {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } error verifyingShasums ` , err ) ;
2020-03-20 20:28:31 +00:00
} ) ;
2018-02-22 13:53:32 +00:00
}
2022-04-04 09:32:57 +00:00
const azRemoteFiles = azRemoteFilesForVersion ( release . tag _name ) ;
2022-04-27 17:37:26 +00:00
await verifyShasumsForRemoteFiles ( azRemoteFiles , true ) ;
2017-10-23 15:02:50 +00:00
}
}
function check ( condition , statement , exitIfFail = false ) {
if ( condition ) {
2020-03-20 20:28:31 +00:00
console . log ( ` ${ pass } ${ statement } ` ) ;
2017-10-23 15:02:50 +00:00
} else {
2020-03-20 20:28:31 +00:00
failureCount ++ ;
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } ${ statement } ` ) ;
2020-03-20 20:28:31 +00:00
if ( exitIfFail ) process . exit ( 1 ) ;
2017-10-23 15:02:50 +00:00
}
}
2018-01-31 23:40:38 +00:00
function assetsForVersion ( version , validatingRelease ) {
2017-10-23 15:02:50 +00:00
const patterns = [
2019-09-04 18:24:46 +00:00
` chromedriver- ${ version } -darwin-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` chromedriver- ${ version } -darwin-arm64.zip ` ,
2019-09-04 18:24:46 +00:00
` chromedriver- ${ version } -linux-arm64.zip ` ,
` chromedriver- ${ version } -linux-armv7l.zip ` ,
` chromedriver- ${ version } -linux-x64.zip ` ,
` chromedriver- ${ version } -mas-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` chromedriver- ${ version } -mas-arm64.zip ` ,
2019-09-04 18:24:46 +00:00
` chromedriver- ${ version } -win32-ia32.zip ` ,
` chromedriver- ${ version } -win32-x64.zip ` ,
` chromedriver- ${ version } -win32-arm64.zip ` ,
2017-10-23 15:02:50 +00:00
` electron- ${ version } -darwin-x64-dsym.zip ` ,
2022-03-07 23:47:58 +00:00
` electron- ${ version } -darwin-x64-dsym-snapshot.zip ` ,
2017-10-23 15:02:50 +00:00
` electron- ${ version } -darwin-x64-symbols.zip ` ,
` electron- ${ version } -darwin-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` electron- ${ version } -darwin-arm64-dsym.zip ` ,
2022-03-07 23:47:58 +00:00
` electron- ${ version } -darwin-arm64-dsym-snapshot.zip ` ,
2020-07-21 11:55:00 +00:00
` electron- ${ version } -darwin-arm64-symbols.zip ` ,
` electron- ${ version } -darwin-arm64.zip ` ,
2019-01-22 21:14:01 +00:00
` electron- ${ version } -linux-arm64-symbols.zip ` ,
` electron- ${ version } -linux-arm64.zip ` ,
` electron- ${ version } -linux-armv7l-symbols.zip ` ,
` electron- ${ version } -linux-armv7l.zip ` ,
2019-11-21 01:21:44 +00:00
` electron- ${ version } -linux-x64-debug.zip ` ,
2017-10-23 15:02:50 +00:00
` electron- ${ version } -linux-x64-symbols.zip ` ,
` electron- ${ version } -linux-x64.zip ` ,
` electron- ${ version } -mas-x64-dsym.zip ` ,
2022-03-07 23:47:58 +00:00
` electron- ${ version } -mas-x64-dsym-snapshot.zip ` ,
2017-10-23 15:02:50 +00:00
` electron- ${ version } -mas-x64-symbols.zip ` ,
` electron- ${ version } -mas-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` electron- ${ version } -mas-arm64-dsym.zip ` ,
2022-03-07 23:47:58 +00:00
` electron- ${ version } -mas-arm64-dsym-snapshot.zip ` ,
2020-07-21 11:55:00 +00:00
` electron- ${ version } -mas-arm64-symbols.zip ` ,
` electron- ${ version } -mas-arm64.zip ` ,
2022-07-27 04:33:07 +00:00
` electron- ${ version } -win32-ia32-pdb.zip ` ,
` electron- ${ version } -win32-ia32-symbols.zip ` ,
2017-10-23 15:02:50 +00:00
` electron- ${ version } -win32-ia32.zip ` ,
` electron- ${ version } -win32-x64-pdb.zip ` ,
` electron- ${ version } -win32-x64-symbols.zip ` ,
` electron- ${ version } -win32-x64.zip ` ,
2019-09-04 18:24:46 +00:00
` electron- ${ version } -win32-arm64-pdb.zip ` ,
` electron- ${ version } -win32-arm64-symbols.zip ` ,
` electron- ${ version } -win32-arm64.zip ` ,
2020-03-20 15:12:18 +00:00
'electron-api.json' ,
'electron.d.ts' ,
'hunspell_dictionaries.zip' ,
2021-05-22 18:48:38 +00:00
'libcxx_headers.zip' ,
'libcxxabi_headers.zip' ,
` libcxx-objects- ${ version } -linux-arm64.zip ` ,
` libcxx-objects- ${ version } -linux-armv7l.zip ` ,
` libcxx-objects- ${ version } -linux-x64.zip ` ,
2017-10-23 15:02:50 +00:00
` ffmpeg- ${ version } -darwin-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` ffmpeg- ${ version } -darwin-arm64.zip ` ,
2019-01-22 21:14:01 +00:00
` ffmpeg- ${ version } -linux-arm64.zip ` ,
` ffmpeg- ${ version } -linux-armv7l.zip ` ,
2017-10-23 15:02:50 +00:00
` ffmpeg- ${ version } -linux-x64.zip ` ,
` ffmpeg- ${ version } -mas-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` ffmpeg- ${ version } -mas-arm64.zip ` ,
2017-10-23 15:02:50 +00:00
` ffmpeg- ${ version } -win32-ia32.zip ` ,
2019-09-04 18:24:46 +00:00
` ffmpeg- ${ version } -win32-x64.zip ` ,
` ffmpeg- ${ version } -win32-arm64.zip ` ,
` mksnapshot- ${ version } -darwin-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` mksnapshot- ${ version } -darwin-arm64.zip ` ,
2019-09-04 18:24:46 +00:00
` mksnapshot- ${ version } -linux-arm64-x64.zip ` ,
` mksnapshot- ${ version } -linux-armv7l-x64.zip ` ,
` mksnapshot- ${ version } -linux-x64.zip ` ,
` mksnapshot- ${ version } -mas-x64.zip ` ,
2020-07-21 11:55:00 +00:00
` mksnapshot- ${ version } -mas-arm64.zip ` ,
2019-09-04 18:24:46 +00:00
` mksnapshot- ${ version } -win32-ia32.zip ` ,
` mksnapshot- ${ version } -win32-x64.zip ` ,
2020-01-13 20:40:13 +00:00
` mksnapshot- ${ version } -win32-arm64-x64.zip ` ,
2020-01-14 22:10:08 +00:00
` electron- ${ version } -win32-ia32-toolchain-profile.zip ` ,
` electron- ${ version } -win32-x64-toolchain-profile.zip ` ,
` electron- ${ version } -win32-arm64-toolchain-profile.zip `
2020-03-20 20:28:31 +00:00
] ;
2018-01-31 23:40:38 +00:00
if ( ! validatingRelease ) {
2020-03-20 20:28:31 +00:00
patterns . push ( 'SHASUMS256.txt' ) ;
2018-01-31 23:40:38 +00:00
}
2020-03-20 20:28:31 +00:00
return patterns ;
2017-10-23 15:02:50 +00:00
}
2022-04-04 09:32:57 +00:00
const cloudStoreFilePaths = ( version ) => [
` iojs- ${ version } -headers.tar.gz ` ,
` iojs- ${ version } .tar.gz ` ,
` node- ${ version } .tar.gz ` ,
'node.lib' ,
'x64/node.lib' ,
'win-x64/iojs.lib' ,
'win-x86/iojs.lib' ,
'win-arm64/iojs.lib' ,
'win-x64/node.lib' ,
'win-x86/node.lib' ,
'win-arm64/node.lib' ,
'arm64/node.lib' ,
'SHASUMS.txt' ,
'SHASUMS256.txt'
] ;
function azRemoteFilesForVersion ( version ) {
const azCDN = 'https://artifacts.electronjs.org/headers/' ;
const versionPrefix = ` ${ azCDN } dist/ ${ version } / ` ;
return cloudStoreFilePaths ( version ) . map ( ( filePath ) => ( {
2021-05-11 16:30:35 +00:00
file : filePath ,
url : ` ${ versionPrefix } ${ filePath } `
} ) ) ;
2017-10-23 15:02:50 +00:00
}
function runScript ( scriptName , scriptArgs , cwd ) {
2020-03-20 20:28:31 +00:00
const scriptCommand = ` ${ scriptName } ${ scriptArgs . join ( ' ' ) } ` ;
2018-10-02 01:56:31 +00:00
const scriptOptions = {
2017-10-23 15:02:50 +00:00
encoding : 'UTF-8'
2020-03-20 20:28:31 +00:00
} ;
if ( cwd ) scriptOptions . cwd = cwd ;
2017-10-23 15:02:50 +00:00
try {
2020-03-20 20:28:31 +00:00
return execSync ( scriptCommand , scriptOptions ) ;
2017-10-23 15:02:50 +00:00
} catch ( err ) {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Error running ${ scriptName } ` , err ) ;
2020-03-20 20:28:31 +00:00
process . exit ( 1 ) ;
2017-10-23 15:02:50 +00:00
}
}
function uploadNodeShasums ( ) {
2022-04-04 09:32:57 +00:00
console . log ( 'Uploading Node SHASUMS file to artifacts.electronjs.org.' ) ;
2020-03-20 20:28:31 +00:00
const scriptPath = path . join ( ELECTRON _DIR , 'script' , 'release' , 'uploaders' , 'upload-node-checksums.py' ) ;
runScript ( scriptPath , [ '-v' , pkgVersion ] ) ;
2022-04-04 09:32:57 +00:00
console . log ( ` ${ pass } Done uploading Node SHASUMS file to artifacts.electronjs.org. ` ) ;
2017-10-23 15:02:50 +00:00
}
function uploadIndexJson ( ) {
2022-04-04 09:32:57 +00:00
console . log ( 'Uploading index.json to artifacts.electronjs.org.' ) ;
2020-03-20 20:28:31 +00:00
const scriptPath = path . join ( ELECTRON _DIR , 'script' , 'release' , 'uploaders' , 'upload-index-json.py' ) ;
runScript ( scriptPath , [ pkgVersion ] ) ;
2022-04-04 09:32:57 +00:00
console . log ( ` ${ pass } Done uploading index.json to artifacts.electronjs.org. ` ) ;
2017-10-23 15:02:50 +00:00
}
2020-09-30 20:30:10 +00:00
async function mergeShasums ( pkgVersion ) {
2022-04-04 09:32:57 +00:00
// Download individual checksum files for Electron zip files from artifact storage,
2020-09-30 20:30:10 +00:00
// concatenate them, and upload to GitHub.
2022-04-04 09:32:57 +00:00
const connectionString = process . env . ELECTRON _ARTIFACTS _BLOB _STORAGE ;
if ( ! connectionString ) {
throw new Error ( 'Please set the $ELECTRON_ARTIFACTS_BLOB_STORAGE environment variable' ) ;
2020-09-30 20:30:10 +00:00
}
2022-04-04 09:32:57 +00:00
const blobServiceClient = BlobServiceClient . fromConnectionString ( connectionString ) ;
const containerClient = blobServiceClient . getContainerClient ( 'checksums-scratchpad' ) ;
const blobsIter = containerClient . listBlobsFlat ( {
prefix : ` ${ pkgVersion } / `
2020-09-30 20:30:10 +00:00
} ) ;
const shasums = [ ] ;
2022-04-04 09:32:57 +00:00
for await ( const blob of blobsIter ) {
if ( blob . name . endsWith ( '.sha256sum' ) ) {
const blobClient = containerClient . getBlockBlobClient ( blob . name ) ;
const response = await blobClient . downloadToBuffer ( ) ;
shasums . push ( response . toString ( 'ascii' ) . trim ( ) ) ;
2020-09-30 20:30:10 +00:00
}
}
return shasums . join ( '\n' ) ;
}
2017-10-23 15:02:50 +00:00
async function createReleaseShasums ( release ) {
2020-03-20 20:28:31 +00:00
const fileName = 'SHASUMS256.txt' ;
const existingAssets = release . assets . filter ( asset => asset . name === fileName ) ;
2017-10-23 15:02:50 +00:00
if ( existingAssets . length > 0 ) {
2020-03-20 20:28:31 +00:00
console . log ( ` ${ fileName } already exists on GitHub; deleting before creating new file. ` ) ;
2019-01-08 20:05:58 +00:00
await octokit . repos . deleteReleaseAsset ( {
2017-10-23 15:02:50 +00:00
owner : 'electron' ,
2018-08-16 15:57:12 +00:00
repo : targetRepo ,
2019-01-08 20:05:58 +00:00
asset _id : existingAssets [ 0 ] . id
2017-10-23 15:02:50 +00:00
} ) . catch ( err => {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Error deleting ${ fileName } on GitHub: ` , err ) ;
process . exit ( 1 ) ;
2020-03-20 20:28:31 +00:00
} ) ;
2017-10-23 15:02:50 +00:00
}
2020-03-20 20:28:31 +00:00
console . log ( ` Creating and uploading the release ${ fileName } . ` ) ;
2020-09-30 20:30:10 +00:00
const checksums = await mergeShasums ( pkgVersion ) ;
2019-01-08 20:05:58 +00:00
2020-03-20 20:28:31 +00:00
console . log ( ` ${ pass } Generated release SHASUMS. ` ) ;
const filePath = await saveShaSumFile ( checksums , fileName ) ;
2019-01-08 20:05:58 +00:00
2020-03-20 20:28:31 +00:00
console . log ( ` ${ pass } Created ${ fileName } file. ` ) ;
await uploadShasumFile ( filePath , fileName , release . id ) ;
2019-01-08 20:05:58 +00:00
2020-03-20 20:28:31 +00:00
console . log ( ` ${ pass } Successfully uploaded ${ fileName } to GitHub. ` ) ;
2017-10-23 15:02:50 +00:00
}
2019-01-08 20:05:58 +00:00
async function uploadShasumFile ( filePath , fileName , releaseId ) {
2020-03-20 20:28:31 +00:00
const uploadUrl = ` https://uploads.github.com/repos/electron/ ${ targetRepo } /releases/ ${ releaseId } /assets{?name,label} ` ;
2019-01-08 20:05:58 +00:00
return octokit . repos . uploadReleaseAsset ( {
url : uploadUrl ,
headers : {
'content-type' : 'text/plain' ,
2019-01-11 17:53:13 +00:00
'content-length' : fs . statSync ( filePath ) . size
2019-01-08 20:05:58 +00:00
} ,
2020-08-07 19:00:12 +00:00
data : fs . createReadStream ( filePath ) ,
2017-10-23 15:02:50 +00:00
name : fileName
2019-01-08 20:05:58 +00:00
} ) . catch ( err => {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Error uploading ${ filePath } to GitHub: ` , err ) ;
2020-03-20 20:28:31 +00:00
process . exit ( 1 ) ;
} ) ;
2017-10-23 15:02:50 +00:00
}
function saveShaSumFile ( checksums , fileName ) {
2023-06-26 09:51:54 +00:00
return new Promise ( resolve => {
2017-10-23 15:02:50 +00:00
temp . open ( fileName , ( err , info ) => {
if ( err ) {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Could not create ${ fileName } file ` ) ;
2020-03-20 20:28:31 +00:00
process . exit ( 1 ) ;
2017-10-23 15:02:50 +00:00
} else {
2020-03-20 20:28:31 +00:00
fs . writeFileSync ( info . fd , checksums ) ;
2017-10-23 15:02:50 +00:00
fs . close ( info . fd , ( err ) => {
if ( err ) {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Could close ${ fileName } file ` ) ;
2020-03-20 20:28:31 +00:00
process . exit ( 1 ) ;
2017-10-23 15:02:50 +00:00
}
2020-03-20 20:28:31 +00:00
resolve ( info . path ) ;
} ) ;
2017-10-23 15:02:50 +00:00
}
2020-03-20 20:28:31 +00:00
} ) ;
} ) ;
2017-10-23 15:02:50 +00:00
}
async function publishRelease ( release ) {
2023-02-15 05:12:07 +00:00
let makeLatest = false ;
if ( ! release . prerelease ) {
const currentLatest = await octokit . repos . getLatestRelease ( {
owner : 'electron' ,
repo : targetRepo
} ) ;
2023-02-13 09:16:27 +00:00
2023-02-15 05:12:07 +00:00
makeLatest = semver . gte ( release . tag _name , currentLatest . data . tag _name ) ;
}
2023-02-13 09:16:27 +00:00
2019-01-08 20:05:58 +00:00
return octokit . repos . updateRelease ( {
2017-10-23 15:02:50 +00:00
owner : 'electron' ,
2018-08-16 15:57:12 +00:00
repo : targetRepo ,
2019-01-08 20:05:58 +00:00
release _id : release . id ,
2017-10-23 15:02:50 +00:00
tag _name : release . tag _name ,
2023-02-13 09:16:27 +00:00
draft : false ,
make _latest : makeLatest ? 'true' : 'false'
2019-01-08 20:05:58 +00:00
} ) . catch ( err => {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Error publishing release: ` , err ) ;
2020-03-20 20:28:31 +00:00
process . exit ( 1 ) ;
} ) ;
2017-10-23 15:02:50 +00:00
}
async function makeRelease ( releaseToValidate ) {
if ( releaseToValidate ) {
2018-01-31 23:40:38 +00:00
if ( releaseToValidate === true ) {
2020-03-20 20:28:31 +00:00
releaseToValidate = pkgVersion ;
2018-01-31 23:40:38 +00:00
} else {
2020-03-20 20:28:31 +00:00
console . log ( 'Release to validate !=== true' ) ;
2018-01-31 23:40:38 +00:00
}
2020-03-20 20:28:31 +00:00
console . log ( ` Validating release ${ releaseToValidate } ` ) ;
const release = await getDraftRelease ( releaseToValidate ) ;
await validateReleaseAssets ( release , true ) ;
2017-10-23 15:02:50 +00:00
} else {
2020-03-20 20:28:31 +00:00
let draftRelease = await getDraftRelease ( ) ;
2023-06-09 09:30:26 +00:00
uploadNodeShasums ( ) ;
2020-03-20 20:28:31 +00:00
await createReleaseShasums ( draftRelease ) ;
2019-01-08 20:05:58 +00:00
2017-10-23 15:02:50 +00:00
// Fetch latest version of release before verifying
2020-03-20 20:28:31 +00:00
draftRelease = await getDraftRelease ( pkgVersion , true ) ;
await validateReleaseAssets ( draftRelease ) ;
2023-06-08 20:41:23 +00:00
// index.json goes live once uploaded so do these uploads as
// late as possible to reduce the chances it contains a release
// which fails to publish. It has to be done before the final
// publish to ensure there aren't published releases not contained
// in index.json, which causes other problems in downstream projects
uploadIndexJson ( ) ;
2020-03-20 20:28:31 +00:00
await publishRelease ( draftRelease ) ;
2017-10-23 15:02:50 +00:00
console . log ( ` ${ pass } SUCCESS!!! Release has been published. Please run ` +
2020-03-20 20:28:31 +00:00
'"npm run publish-to-npm" to publish release to npm.' ) ;
2017-10-23 15:02:50 +00:00
}
}
2021-05-11 16:30:35 +00:00
const SHASUM _256 _FILENAME = 'SHASUMS256.txt' ;
const SHASUM _1 _FILENAME = 'SHASUMS.txt' ;
2019-01-08 20:05:58 +00:00
2021-05-11 16:30:35 +00:00
async function verifyDraftGitHubReleaseAssets ( release ) {
console . log ( 'Fetching authenticated GitHub artifact URLs to verify shasums' ) ;
2019-01-08 20:05:58 +00:00
2021-05-11 16:30:35 +00:00
const remoteFilesToHash = await Promise . all ( release . assets . map ( async asset => {
2021-08-12 17:34:49 +00:00
const requestOptions = octokit . repos . getReleaseAsset . endpoint ( {
2019-01-08 20:05:58 +00:00
owner : 'electron' ,
repo : targetRepo ,
2019-01-23 19:47:36 +00:00
asset _id : asset . id ,
headers : {
Accept : 'application/octet-stream'
}
2020-03-20 20:28:31 +00:00
} ) ;
2019-01-23 19:47:36 +00:00
2020-03-20 20:28:31 +00:00
const { url , headers } = requestOptions ;
2024-09-23 18:07:16 +00:00
headers . authorization = ` token ${ ( await octokit . auth ( ) ) . token } ` ;
2019-01-23 19:47:36 +00:00
const response = await got ( url , {
followRedirect : false ,
method : 'HEAD' ,
2024-09-18 03:06:08 +00:00
headers ,
throwHttpErrors : false
2020-03-20 20:28:31 +00:00
} ) ;
2019-01-23 19:47:36 +00:00
2024-09-18 03:06:08 +00:00
if ( response . statusCode !== 302 && response . statusCode !== 301 ) {
console . error ( 'Failed to HEAD github asset: ' + url ) ;
throw new Error ( 'Unexpected status HEAD\'ing github asset: ' + response . statusCode ) ;
}
2021-05-11 16:30:35 +00:00
return { url : response . headers . location , file : asset . name } ;
2017-10-23 15:02:50 +00:00
} ) ) . catch ( err => {
2023-06-07 23:49:12 +00:00
console . error ( ` ${ fail } Error downloading files from GitHub ` , err ) ;
2020-03-20 20:28:31 +00:00
process . exit ( 1 ) ;
} ) ;
2019-01-08 20:05:58 +00:00
2021-05-11 16:30:35 +00:00
await verifyShasumsForRemoteFiles ( remoteFilesToHash ) ;
2017-10-23 15:02:50 +00:00
}
2021-05-11 16:30:35 +00:00
async function getShaSumMappingFromUrl ( shaSumFileUrl , fileNamePrefix ) {
2024-09-18 03:06:08 +00:00
const response = await got ( shaSumFileUrl , {
throwHttpErrors : false
} ) ;
if ( response . statusCode !== 200 ) {
console . error ( 'Failed to fetch SHASUM mapping: ' + shaSumFileUrl ) ;
console . error ( 'Bad SHASUM mapping response: ' + response . body . trim ( ) ) ;
throw new Error ( 'Unexpected status fetching SHASUM mapping: ' + response . statusCode ) ;
}
2021-05-11 16:30:35 +00:00
const raw = response . body ;
return raw . split ( '\n' ) . map ( line => line . trim ( ) ) . filter ( Boolean ) . reduce ( ( map , line ) => {
2021-05-12 08:49:16 +00:00
const [ sha , file ] = line . replace ( ' ' , ' ' ) . split ( ' ' ) ;
2021-05-11 16:30:35 +00:00
map [ file . slice ( fileNamePrefix . length ) ] = sha ;
return map ;
} , { } ) ;
2017-10-23 15:02:50 +00:00
}
2021-05-11 16:30:35 +00:00
async function validateFileHashesAgainstShaSumMapping ( remoteFilesWithHashes , mapping ) {
for ( const remoteFileWithHash of remoteFilesWithHashes ) {
check ( remoteFileWithHash . hash === mapping [ remoteFileWithHash . file ] , ` Release asset ${ remoteFileWithHash . file } should have hash of ${ mapping [ remoteFileWithHash . file ] } but found ${ remoteFileWithHash . hash } ` , true ) ;
2017-10-23 15:02:50 +00:00
}
}
2021-05-11 16:30:35 +00:00
async function verifyShasumsForRemoteFiles ( remoteFilesToHash , filesAreNodeJSArtifacts = false ) {
console . log ( ` Generating SHAs for ${ remoteFilesToHash . length } files to verify shasums ` ) ;
// Only used for node.js artifact uploads
const shaSum1File = remoteFilesToHash . find ( ( { file } ) => file === SHASUM _1 _FILENAME ) ;
// Used for both node.js artifact uploads and normal electron artifacts
const shaSum256File = remoteFilesToHash . find ( ( { file } ) => file === SHASUM _256 _FILENAME ) ;
remoteFilesToHash = remoteFilesToHash . filter ( ( { file } ) => file !== SHASUM _1 _FILENAME && file !== SHASUM _256 _FILENAME ) ;
const remoteFilesWithHashes = await Promise . all ( remoteFilesToHash . map ( async ( file ) => {
return {
hash : await getUrlHash ( file . url , 'sha256' ) ,
... file
} ;
} ) ) ;
await validateFileHashesAgainstShaSumMapping ( remoteFilesWithHashes , await getShaSumMappingFromUrl ( shaSum256File . url , filesAreNodeJSArtifacts ? '' : '*' ) ) ;
if ( filesAreNodeJSArtifacts ) {
const remoteFilesWithSha1Hashes = await Promise . all ( remoteFilesToHash . map ( async ( file ) => {
return {
hash : await getUrlHash ( file . url , 'sha1' ) ,
... file
} ;
} ) ) ;
await validateFileHashesAgainstShaSumMapping ( remoteFilesWithSha1Hashes , await getShaSumMappingFromUrl ( shaSum1File . url , filesAreNodeJSArtifacts ? '' : '*' ) ) ;
}
2017-10-23 15:02:50 +00:00
}
2021-07-21 07:45:57 +00:00
makeRelease ( args . validateRelease )
. catch ( ( err ) => {
console . error ( 'Error occurred while making release:' , err ) ;
process . exit ( 1 ) ;
} ) ;