mirror of
https://github.com/desktop/desktop
synced 2024-09-19 16:12:20 +00:00
207 lines
4.9 KiB
TypeScript
207 lines
4.9 KiB
TypeScript
const PUBLISH_CHANNELS = ['production', 'test', 'beta']
|
|
import * as distInfo from './dist-info'
|
|
import * as gitInfo from '../app/git-info'
|
|
import * as packageInfo from '../app/package-info'
|
|
|
|
if (PUBLISH_CHANNELS.indexOf(distInfo.getReleaseChannel()) < 0) {
|
|
console.log('Not a publishable build. Skipping publish.')
|
|
process.exit(0)
|
|
}
|
|
|
|
const releaseSHA = distInfo.getReleaseSHA()
|
|
if (!releaseSHA) {
|
|
console.log(`No release SHA found for build. Skipping publish.`)
|
|
process.exit(0)
|
|
}
|
|
|
|
const currentTipSHA = gitInfo.getSHA()
|
|
if (
|
|
!currentTipSHA ||
|
|
!currentTipSHA.toUpperCase().startsWith(releaseSHA!.toUpperCase())
|
|
) {
|
|
console.log(
|
|
`Current tip '${currentTipSHA}' does not match release SHA '${releaseSHA}'. Skipping publish.`
|
|
)
|
|
process.exit(0)
|
|
}
|
|
|
|
import * as Fs from 'fs'
|
|
import { execSync } from 'child_process'
|
|
import * as AWS from 'aws-sdk'
|
|
import * as Crypto from 'crypto'
|
|
import * as request from 'request'
|
|
|
|
console.log('Packaging…')
|
|
execSync('yarn package')
|
|
|
|
let sha = ''
|
|
if (process.platform === 'darwin') {
|
|
sha = process.env.CIRCLE_SHA1
|
|
} else if (process.platform === 'win32') {
|
|
sha = process.env.APPVEYOR_REPO_COMMIT
|
|
}
|
|
|
|
sha = sha.substr(0, 8)
|
|
|
|
console.log('Uploading…')
|
|
|
|
let uploadPromise = null
|
|
if (process.platform === 'darwin') {
|
|
uploadPromise = uploadOSXAssets()
|
|
} else if (process.platform === 'win32') {
|
|
uploadPromise = uploadWindowsAssets()
|
|
} else {
|
|
console.error(`I dunno how to publish a release for ${process.platform} :(`)
|
|
process.exit(1)
|
|
}
|
|
|
|
uploadPromise!
|
|
.then(artifacts => {
|
|
const names = artifacts.map(function(item, index) {
|
|
return item.name
|
|
})
|
|
console.log(`Uploaded artifacts: ${names}`)
|
|
return updateDeploy(artifacts)
|
|
})
|
|
.catch(e => {
|
|
console.error(`Publishing failed: ${e}`)
|
|
process.exit(1)
|
|
})
|
|
|
|
function uploadOSXAssets() {
|
|
const uploads = [upload(distInfo.getOSXZipName(), distInfo.getOSXZipPath())]
|
|
return Promise.all(uploads)
|
|
}
|
|
|
|
function uploadWindowsAssets() {
|
|
const uploads = [
|
|
upload(
|
|
distInfo.getWindowsInstallerName(),
|
|
distInfo.getWindowsInstallerPath()
|
|
),
|
|
upload(
|
|
distInfo.getWindowsStandaloneName(),
|
|
distInfo.getWindowsStandalonePath()
|
|
),
|
|
upload(
|
|
distInfo.getWindowsFullNugetPackageName(),
|
|
distInfo.getWindowsFullNugetPackagePath()
|
|
),
|
|
]
|
|
|
|
if (distInfo.shouldMakeDelta()) {
|
|
uploads.push(
|
|
upload(
|
|
distInfo.getWindowsDeltaNugetPackageName(),
|
|
distInfo.getWindowsDeltaNugetPackagePath()
|
|
)
|
|
)
|
|
}
|
|
|
|
return Promise.all(uploads)
|
|
}
|
|
|
|
interface IUploadResult {
|
|
name: string
|
|
url: string
|
|
size: number
|
|
sha: string
|
|
}
|
|
|
|
function upload(assetName: string, assetPath: string) {
|
|
const s3Info = {
|
|
accessKeyId: process.env.S3_KEY,
|
|
secretAccessKey: process.env.S3_SECRET,
|
|
}
|
|
const s3 = new AWS.S3(s3Info)
|
|
|
|
const bucket = process.env.S3_BUCKET
|
|
const key = `releases/${packageInfo.getVersion()}-${sha}/${assetName.replace(
|
|
/ /g,
|
|
''
|
|
)}`
|
|
const url = `https://s3.amazonaws.com/${bucket}/${key}`
|
|
|
|
const uploadParams = {
|
|
Bucket: bucket,
|
|
ACL: 'public-read',
|
|
Key: key,
|
|
Body: Fs.createReadStream(assetPath),
|
|
}
|
|
|
|
return new Promise<IUploadResult>((resolve, reject) => {
|
|
s3.upload(
|
|
uploadParams,
|
|
(error: Error, data: AWS.S3.ManagedUpload.SendData) => {
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
const stats = Fs.statSync(assetPath)
|
|
const hash = Crypto.createHash('sha1')
|
|
hash.setEncoding('hex')
|
|
const input = Fs.createReadStream(assetPath)
|
|
|
|
hash.on('finish', () => {
|
|
const sha = hash.read() as string
|
|
resolve({ name: assetName, url, size: stats['size'], sha })
|
|
})
|
|
|
|
input.pipe(hash)
|
|
}
|
|
}
|
|
)
|
|
})
|
|
}
|
|
|
|
function createSignature(body: any) {
|
|
const hmac = Crypto.createHmac('sha1', process.env.DEPLOYMENT_SECRET)
|
|
hmac.update(JSON.stringify(body))
|
|
return `sha1=${hmac.digest('hex')}`
|
|
}
|
|
|
|
function updateDeploy(artifacts: ReadonlyArray<IUploadResult>) {
|
|
const { rendererSize, mainSize } = distInfo.getBundleSizes()
|
|
const body = {
|
|
context: process.platform,
|
|
branch_name: distInfo.getReleaseBranchName(),
|
|
artifacts,
|
|
stats: {
|
|
platform: process.platform,
|
|
rendererBundleSize: rendererSize,
|
|
mainBundleSize: mainSize,
|
|
},
|
|
}
|
|
const signature = createSignature(body)
|
|
const options = {
|
|
method: 'POST',
|
|
url: 'https://central.github.com/api/deploy_built',
|
|
headers: {
|
|
'X-Hub-Signature': signature,
|
|
},
|
|
json: true,
|
|
body,
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
request(options, (error, response, body) => {
|
|
if (error) {
|
|
reject(error)
|
|
return
|
|
}
|
|
|
|
if (response.statusCode !== 200) {
|
|
reject(
|
|
new Error(
|
|
`Received a non-200 response (${
|
|
response.statusCode
|
|
}): ${JSON.stringify(body)}`
|
|
)
|
|
)
|
|
return
|
|
}
|
|
|
|
resolve()
|
|
})
|
|
})
|
|
}
|