upload-artifacts.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. 'use strict';
  2. const fs = require('fs');
  3. const os = require('os');
  4. const path = require('path');
  5. const glob = require('glob');
  6. const spawnSync = require('../lib/spawn-sync');
  7. const publishRelease = require('publish-release');
  8. const releaseNotes = require('./lib/release-notes');
  9. const uploadToAzure = require('./lib/upload-to-azure-blob');
  10. const uploadLinuxPackages = require('./lib/upload-linux-packages');
  11. const CONFIG = require('../config');
  12. const { REPO_OWNER, MAIN_REPO, NIGHTLY_RELEASE_REPO } = CONFIG;
  13. const yargs = require('yargs');
  14. const argv = yargs
  15. .usage('Usage: $0 [options]')
  16. .help('help')
  17. .describe(
  18. 'assets-path',
  19. 'Path to the folder where all release assets are stored'
  20. )
  21. .describe(
  22. 'azure-blob-path',
  23. 'Indicates the Azure Blob Path path in which the assets should be uploaded'
  24. )
  25. .describe(
  26. 'create-github-release',
  27. 'Creates a GitHub release for this build, draft if release branch or public if Nightly'
  28. )
  29. .describe(
  30. 'linux-repo-name',
  31. 'If specified, uploads Linux packages to the given repo name on packagecloud'
  32. )
  33. .wrap(yargs.terminalWidth()).argv;
  34. const releaseVersion = CONFIG.computedAppVersion;
  35. const isNightlyRelease = CONFIG.channel === 'nightly';
  36. const assetsPath = argv.assetsPath || CONFIG.buildOutputPath;
  37. const assetsPattern =
  38. '/**/*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*|atom-api.json)';
  39. const assets = glob.sync(assetsPattern, { root: assetsPath, nodir: true });
  40. const azureBlobPath = argv.azureBlobPath || `releases/v${releaseVersion}/`;
  41. if (!assets || assets.length === 0) {
  42. console.error(`No assets found under specified path: ${assetsPath}`);
  43. process.exit(1);
  44. }
  45. async function uploadArtifacts() {
  46. let releaseForVersion = await releaseNotes.getRelease(
  47. releaseVersion,
  48. process.env.GITHUB_TOKEN
  49. );
  50. if (releaseForVersion.exists && !releaseForVersion.isDraft) {
  51. console.log(
  52. `Published release already exists for ${releaseVersion}, skipping upload.`
  53. );
  54. return;
  55. }
  56. if (
  57. process.env.ATOM_RELEASES_S3_KEY &&
  58. process.env.ATOM_RELEASES_S3_SECRET &&
  59. process.env.ATOM_RELEASES_S3_BUCKET
  60. ) {
  61. console.log(
  62. `Uploading ${
  63. assets.length
  64. } release assets for ${releaseVersion} to Azure Blob Storage under '${azureBlobPath}'`
  65. );
  66. await uploadToAzure(
  67. process.env.ATOM_RELEASES_AZURE_CONN_STRING,
  68. azureBlobPath,
  69. assets
  70. );
  71. } else {
  72. console.log(
  73. '\nEnvironment variables "ATOM_RELEASES_S3_BUCKET", "ATOM_RELEASES_S3_KEY" and/or "ATOM_RELEASES_S3_SECRET" are not set, skipping S3 upload.'
  74. );
  75. }
  76. if (argv.linuxRepoName) {
  77. if (process.env.PACKAGE_CLOUD_API_KEY) {
  78. await uploadLinuxPackages(
  79. argv.linuxRepoName,
  80. process.env.PACKAGE_CLOUD_API_KEY,
  81. releaseVersion,
  82. assets
  83. );
  84. } else {
  85. console.log(
  86. '\nEnvironment variable "PACKAGE_CLOUD_API_KEY" is not set, skipping PackageCloud upload.'
  87. );
  88. }
  89. } else {
  90. console.log(
  91. '\nNo Linux package repo name specified, skipping Linux package upload.'
  92. );
  93. }
  94. const oldReleaseNotes = releaseForVersion.releaseNotes;
  95. if (oldReleaseNotes) {
  96. const oldReleaseNotesPath = path.resolve(
  97. os.tmpdir(),
  98. 'OLD_RELEASE_NOTES.md'
  99. );
  100. console.log(
  101. `Saving existing ${releaseVersion} release notes to ${oldReleaseNotesPath}`
  102. );
  103. fs.writeFileSync(oldReleaseNotesPath, oldReleaseNotes, 'utf8');
  104. // This line instructs VSTS to upload the file as an artifact
  105. console.log(
  106. `##vso[artifact.upload containerfolder=OldReleaseNotes;artifactname=OldReleaseNotes;]${oldReleaseNotesPath}`
  107. );
  108. }
  109. if (argv.createGithubRelease) {
  110. console.log(`\nGenerating new release notes for ${releaseVersion}`);
  111. let newReleaseNotes = '';
  112. if (isNightlyRelease) {
  113. newReleaseNotes = await releaseNotes.generateForNightly(
  114. releaseVersion,
  115. process.env.GITHUB_TOKEN,
  116. oldReleaseNotes
  117. );
  118. } else {
  119. newReleaseNotes = await releaseNotes.generateForVersion(
  120. releaseVersion,
  121. process.env.GITHUB_TOKEN,
  122. oldReleaseNotes
  123. );
  124. }
  125. console.log(`New release notes:\n\n${newReleaseNotes}`);
  126. const releaseSha = !isNightlyRelease
  127. ? spawnSync('git', ['rev-parse', 'HEAD'])
  128. .stdout.toString()
  129. .trimEnd()
  130. : 'master'; // Nightly tags are created in REPO_OWNER/NIGHTLY_RELEASE_REPO so the SHA is irrelevant
  131. console.log(`Creating GitHub release v${releaseVersion}`);
  132. const release = await publishReleaseAsync({
  133. token: process.env.GITHUB_TOKEN,
  134. owner: REPO_OWNER,
  135. repo: !isNightlyRelease ? MAIN_REPO : NIGHTLY_RELEASE_REPO,
  136. name: CONFIG.computedAppVersion,
  137. notes: newReleaseNotes,
  138. target_commitish: releaseSha,
  139. tag: `v${CONFIG.computedAppVersion}`,
  140. draft: !isNightlyRelease,
  141. prerelease: CONFIG.channel !== 'stable',
  142. editRelease: true,
  143. reuseRelease: true,
  144. skipIfPublished: true,
  145. assets
  146. });
  147. console.log('Release published successfully: ', release.html_url);
  148. } else {
  149. console.log('Skipping GitHub release creation');
  150. }
  151. }
  152. async function publishReleaseAsync(options) {
  153. return new Promise((resolve, reject) => {
  154. publishRelease(options, (err, release) => {
  155. if (err) {
  156. reject(err);
  157. } else {
  158. resolve(release);
  159. }
  160. });
  161. });
  162. }
  163. // Wrap the call the async function and catch errors from its promise because
  164. // Node.js doesn't yet allow use of await at the script scope
  165. uploadArtifacts().catch(err => {
  166. console.error('An error occurred while uploading the release:\n\n', err);
  167. process.exit(1);
  168. });