about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/.github/actions/github-release/main.js
diff options
context:
space:
mode:
authorJosh McKinney <joshka@users.noreply.github.com>2024-03-28 20:39:21 -0700
committerJosh McKinney <joshka@users.noreply.github.com>2024-03-28 20:42:44 -0700
commit29d28f801cf7cac6a2b80dadfd60f3f6823bc3cf (patch)
tree3c05166c97442ea82947c2dcaea6b0bd03992a41 /src/tools/rust-analyzer/.github/actions/github-release/main.js
parentab10eea62ec59bc676a946ae8be562edcaccb950 (diff)
Make `cargo run` always available for binaries
Previously, items for `cargo test` and `cargo check` would appear as in
the `Select Runnable` quick pick that appears when running
`rust-analyzer: Run`, but `run` would only appear as a runnable if a
`main`` function was selected in the editor. This change adds `cargo
run` as an always available runnable command for binary packages.

This makes it easier to develop cli / tui applications, as now users can
run application from anywhere in their codebase.
Diffstat (limited to 'src/tools/rust-analyzer/.github/actions/github-release/main.js')
-rw-r--r--src/tools/rust-analyzer/.github/actions/github-release/main.js144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/.github/actions/github-release/main.js b/src/tools/rust-analyzer/.github/actions/github-release/main.js
new file mode 100644
index 00000000000..e8dba398733
--- /dev/null
+++ b/src/tools/rust-analyzer/.github/actions/github-release/main.js
@@ -0,0 +1,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);
+});