download-circleci-artifacts.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. const args = require('minimist')(process.argv.slice(2));
  2. const nugget = require('nugget');
  3. const request = require('request');
  4. async function makeRequest (requestOptions, parseResponse) {
  5. return new Promise((resolve, reject) => {
  6. request(requestOptions, (err, res, body) => {
  7. if (!err && res.statusCode >= 200 && res.statusCode < 300) {
  8. if (parseResponse) {
  9. const build = JSON.parse(body);
  10. resolve(build);
  11. } else {
  12. resolve(body);
  13. }
  14. } else {
  15. if (args.verbose) {
  16. console.error('Error occurred while requesting:', requestOptions.url);
  17. if (parseResponse) {
  18. try {
  19. console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions);
  20. } catch (err) {
  21. console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions);
  22. }
  23. } else {
  24. console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions);
  25. }
  26. }
  27. reject(err);
  28. }
  29. });
  30. });
  31. }
  32. async function downloadArtifact (name, buildNum, dest) {
  33. const circleArtifactUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/${args.buildNum}/artifacts?circle-token=${process.env.CIRCLE_TOKEN}`;
  34. const artifacts = await makeRequest({
  35. method: 'GET',
  36. url: circleArtifactUrl,
  37. headers: {
  38. 'Content-Type': 'application/json',
  39. Accept: 'application/json'
  40. }
  41. }, true).catch(err => {
  42. if (args.verbose) {
  43. console.log('Error calling CircleCI:', err);
  44. } else {
  45. console.error('Error calling CircleCI to get artifact details');
  46. }
  47. });
  48. const artifactToDownload = artifacts.find(artifact => {
  49. return (artifact.path === name);
  50. });
  51. if (!artifactToDownload) {
  52. console.log(`Could not find artifact called ${name} to download for build #${buildNum}.`);
  53. process.exit(1);
  54. } else {
  55. console.log(`Downloading ${artifactToDownload.url}.`);
  56. let downloadError = false;
  57. await downloadWithRetry(artifactToDownload.url, dest).catch(err => {
  58. if (args.verbose) {
  59. console.log(`${artifactToDownload.url} could not be successfully downloaded. Error was:`, err);
  60. } else {
  61. console.log(`${artifactToDownload.url} could not be successfully downloaded.`);
  62. }
  63. downloadError = true;
  64. });
  65. if (!downloadError) {
  66. console.log(`Successfully downloaded ${name}.`);
  67. }
  68. }
  69. }
  70. async function downloadWithRetry (url, directory) {
  71. let lastError;
  72. const downloadURL = `${url}?circle-token=${process.env.CIRCLE_TOKEN}`;
  73. for (let i = 0; i < 5; i++) {
  74. console.log(`Attempting to download ${url} - attempt #${(i + 1)}`);
  75. try {
  76. return await downloadFile(downloadURL, directory);
  77. } catch (err) {
  78. lastError = err;
  79. await new Promise((resolve, reject) => setTimeout(resolve, 30000));
  80. }
  81. }
  82. throw lastError;
  83. }
  84. function downloadFile (url, directory) {
  85. return new Promise((resolve, reject) => {
  86. const nuggetOpts = {
  87. dir: directory,
  88. quiet: args.verbose
  89. };
  90. nugget(url, nuggetOpts, (err) => {
  91. if (err) {
  92. reject(err);
  93. } else {
  94. resolve();
  95. }
  96. });
  97. });
  98. }
  99. if (!args.name || !args.buildNum || !args.dest) {
  100. console.log(`Download CircleCI artifacts.
  101. Usage: download-circleci-artifacts.js [--buildNum=CIRCLE_BUILD_NUMBER] [--name=artifactName] [--dest] [--verbose]`);
  102. process.exit(0);
  103. } else {
  104. downloadArtifact(args.name, args.buildNum, args.dest);
  105. }