about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/.github/actions/github-release/main.js
blob: e8dba398733c181da3306330778fda29b2310f49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const core = require('@actions/core');
const path = require("path");
const fs = require("fs");
const github = require('@actions/github');
const glob = require('glob');

function sleep(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds));
}

async function runOnce() {
  // Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
  const files = core.getInput('files');
  const name = core.getInput('name');
  const token = core.getInput('token');
  const slug = process.env.GITHUB_REPOSITORY;
  const owner = slug.split('/')[0];
  const repo = slug.split('/')[1];
  const sha = process.env.HEAD_SHA;

  core.info(`files: ${files}`);
  core.info(`name: ${name}`);

  const options = {
    request: {
      timeout: 30000,
    }
  };
  const octokit = github.getOctokit(token, options);

  // Delete the previous release since we can't overwrite one. This may happen
  // due to retrying an upload or it may happen because we're doing the dev
  // release.
  const releases = await octokit.paginate("GET /repos/:owner/:repo/releases", { owner, repo });
  for (const release of releases) {
    if (release.tag_name !== name) {
      continue;
    }
    const release_id = release.id;
    core.info(`deleting release ${release_id}`);
    await octokit.rest.repos.deleteRelease({ owner, repo, release_id });
  }

  // We also need to update the `dev` tag while we're at it on the `dev` branch.
  if (name == 'nightly') {
    try {
      core.info(`updating nightly tag`);
      await octokit.rest.git.updateRef({
        owner,
        repo,
        ref: 'tags/nightly',
        sha,
        force: true,
      });
    } catch (e) {
      core.error(e);
      core.info(`creating nightly tag`);
      await octokit.rest.git.createTag({
        owner,
        repo,
        tag: 'nightly',
        message: 'nightly release',
        object: sha,
        type: 'commit',
      });
    }
  }

  // Creates an official GitHub release for this `tag`, and if this is `dev`
  // then we know that from the previous block this should be a fresh release.
  core.info(`creating a release`);
  const release = await octokit.rest.repos.createRelease({
    owner,
    repo,
    name,
    tag_name: name,
    target_commitish: sha,
    prerelease: name === 'nightly',
  });
  const release_id = release.data.id;

  // Upload all the relevant assets for this release as just general blobs.
  for (const file of glob.sync(files)) {
    const size = fs.statSync(file).size;
    const name = path.basename(file);

    await runWithRetry(async function () {
      // We can't overwrite assets, so remove existing ones from a previous try.
      let assets = await octokit.rest.repos.listReleaseAssets({
        owner,
        repo,
        release_id
      });
      for (const asset of assets.data) {
        if (asset.name === name) {
          core.info(`delete asset ${name}`);
          const asset_id = asset.id;
          await octokit.rest.repos.deleteReleaseAsset({ owner, repo, asset_id });
        }
      }

      core.info(`upload ${file}`);
      const headers = { 'content-length': size, 'content-type': 'application/octet-stream' };
      const data = fs.createReadStream(file);
      await octokit.rest.repos.uploadReleaseAsset({
        data,
        headers,
        name,
        url: release.data.upload_url,
      });
    });
  }
}

async function runWithRetry(f) {
  const retries = 10;
  const maxDelay = 4000;
  let delay = 1000;

  for (let i = 0; i < retries; i++) {
    try {
      await f();
      break;
    } catch (e) {
      if (i === retries - 1)
        throw e;

      core.error(e);
      const currentDelay = Math.round(Math.random() * delay);
      core.info(`sleeping ${currentDelay} ms`);
      await sleep(currentDelay);
      delay = Math.min(delay * 2, maxDelay);
    }
  }
}

async function run() {
  await runWithRetry(runOnce);
}

run().catch(err => {
  core.error(err);
  core.setFailed(err.message);
});