diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b6e4eba0..4bc44ec00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,6 @@ jobs: with: node-version-file: '.nvmrc' - run: '.ci/scripts/lint.sh' - - run: 'npm run ci:release-dry-run' - id: release-dry-run - run: 'npm run ci:bundlesize' id: bundlesize # TODO: This fails on forked PRs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index abdf2663f..9324ff602 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,16 +2,26 @@ name: release on: workflow_dispatch: + inputs: + dry-run: + type: boolean + description: 'Run release process in dry-run mode' + default: true permissions: contents: read jobs: - release-npm: + release: runs-on: ubuntu-latest permissions: - # Needed to wrtie the release changelog + # Needed to write the release changelog contents: write + services: + verdaccio: + image: verdaccio/verdaccio:5 + ports: + - 4873:4873 steps: - uses: actions/checkout@v3 with: @@ -25,7 +35,8 @@ jobs: run: npm ci - name: Read NPM vault secrets - uses: hashicorp/vault-action@v2.5.0 + if: inputs.dry-run != null && !inputs.dry-run + uses: hashicorp/vault-action@v2.7.3 with: method: approle url: ${{ secrets.VAULT_ADDR }} @@ -34,7 +45,9 @@ jobs: secrets: | totp/code/npmjs-elasticmachine code | TOTP_CODE - - uses: elastic/apm-pipeline-library/.github/actions/setup-npmrc@current + - name: Configure npm registry + uses: elastic/apm-pipeline-library/.github/actions/setup-npmrc@current + if: inputs.dry-run != null && !inputs.dry-run with: vault-url: ${{ secrets.VAULT_ADDR }} vault-role-id: ${{ secrets.VAULT_ROLE_ID }} @@ -42,36 +55,36 @@ jobs: secret: secret/jenkins-ci/npmjs/elasticmachine secret-key: token + - name: Configure git user + uses: elastic/apm-pipeline-library/.github/actions/setup-git@current + - name: Publish the release + env: + DRY_RUN: ${{ inputs.dry-run }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm run ci:release - release-cdn: - runs-on: ubuntu-latest - env: - BUCKET_NAME: 'apm-rum-357700bc' - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - - - name: Install dependencies - run: npm ci - - - name: Build dist - run: npm run build - - - uses: hashicorp/vault-action@v2.5.0 + - name: Setup credentials + env: + DRY_RUN: ${{ inputs.dry-run }} + run: | + if [ "${DRY_RUN}" == "false" ]; then + echo 'ELASTIC_CDN_BUCKET_NAME=apm-rum-357700bc' >> ${GITHUB_ENV} + echo 'ELASTIC_CDN_CREDENTIALS=secret/gce/elastic-cdn/service-account/apm-rum-admin' >> ${GITHUB_ENV} + else + echo 'ELASTIC_CDN_BUCKET_NAME=oblt-apm-agent-rum-js-ci' >> ${GITHUB_ENV} + echo 'ELASTIC_CDN_CREDENTIALS=secret/observability-team/ci/service-account/apm-agent-rum-js' >> ${GITHUB_ENV} + fi + + - name: Read GCE vault secrets + uses: hashicorp/vault-action@v2.7.3 with: + method: approle url: ${{ secrets.VAULT_ADDR }} roleId: ${{ secrets.VAULT_ROLE_ID }} secretId: ${{ secrets.VAULT_SECRET_ID }} - method: approle secrets: | - secret/gce/elastic-cdn/service-account/apm-rum-admin value | GOOGLE_CREDENTIALS ; + ${{ env.ELASTIC_CDN_CREDENTIALS }} value | GOOGLE_CREDENTIALS ; - name: 'Authenticate to Google Cloud' uses: 'google-github-actions/auth@v1' @@ -88,7 +101,7 @@ jobs: with: parent: false path: 'packages/rum/dist/bundles/' - destination: '${{ env.BUCKET_NAME }}/${{ fromJSON(steps.prepare-release.outputs.versions).version }}' + destination: '${{ env.ELASTIC_CDN_BUCKET_NAME }}/${{ fromJSON(steps.prepare-release.outputs.versions).version }}' glob: '*.js' process_gcloudignore: false @@ -97,7 +110,7 @@ jobs: with: parent: false path: 'packages/rum/dist/bundles/' - destination: '${{ env.BUCKET_NAME }}/${{ fromJSON(steps.prepare-release.outputs.versions).major_version }}' + destination: '${{ env.ELASTIC_CDN_BUCKET_NAME }}/${{ fromJSON(steps.prepare-release.outputs.versions).major_version }}' glob: '*.js' process_gcloudignore: false @@ -106,14 +119,13 @@ jobs: with: parent: false path: 'index.html' - destination: '${{ env.BUCKET_NAME }}' + destination: '${{ env.ELASTIC_CDN_BUCKET_NAME }}' process_gcloudignore: false status: if: always() needs: - - release-npm - - release-cdn + - release runs-on: ubuntu-latest steps: - id: check diff --git a/.gitignore b/.gitignore index b4dc3b70e..33baf7ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ coverage # ignore lerna-debug and npm-debug logs *debug.log packages/**/*.tgz +.npmrc diff --git a/lerna.json b/lerna.json index b343d710d..03186de86 100644 --- a/lerna.json +++ b/lerna.json @@ -10,7 +10,7 @@ "conventionalCommits": true, "message": "chore(release): publish", "changelogPreset": "conventionalcommits", - "gitRemote": "upstream", + "gitRemote": "origin", "loglevel": "verbose" }, "run": { diff --git a/package-lock.json b/package-lock.json index b13f4de6c..ea78d7964 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5988,6 +5988,23 @@ "chalk": "^2.3.1", "execa": "^1.0.0", "strong-log-transformer": "^2.0.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "@lerna/clean": { @@ -14810,6 +14827,23 @@ "requires": { "execa": "^1.0.0", "find-versions": "^3.0.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "bin-version-check": { @@ -18286,6 +18320,23 @@ "requires": { "execa": "^1.0.0", "ip-regex": "^2.1.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "defaults": { @@ -19834,18 +19885,125 @@ "dev": true }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "executable": { @@ -22395,6 +22553,21 @@ "parse-json": "^4.0.0" } }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -24234,7 +24407,8 @@ "jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", - "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true }, "js-tokens": { "version": "4.0.0", diff --git a/package.json b/package.json index 5b53dc89f..3ef76bd7e 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,7 @@ "package:snapshot": "npx lerna exec npm pack", "clean": "npx lerna exec -- rm -rf dist/", "ci:prepare-release": "node ./scripts/ci-prepare-release.js", - "ci:release": "lerna publish --otp=${TOTP_CODE} --yes && npm run github-release", - "ci:release-dry-run": "bash ./scripts/ci-release-dry-run.sh" + "ci:release": "node ./scripts/ci-release.mjs" }, "lint-staged": { "*.{js,jsx,ts}": [ @@ -128,6 +127,7 @@ "eslint-plugin-react": "^7.17.0", "eslint-plugin-rulesdir": "^0.1.0", "eslint-plugin-standard": "^4.0.1", + "execa": "^7.1.1", "express": "^4.17.3", "express-http-proxy": "^1.6.0", "faker": "^5.1.0", diff --git a/scripts/ci-release-dry-run.sh b/scripts/ci-release-dry-run.sh deleted file mode 100644 index 24affbdd8..000000000 --- a/scripts/ci-release-dry-run.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# Bash strict mode -set -eo pipefail -trap 's=$?; echo >&2 "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR - -# Create a tmp dir -WORKSPACE=$(mktemp -d) -DRYRUN_PATH="${WORKSPACE}/dryrun.txt" - -# Setup git env to allow lerna to generate versions properly -git checkout -b dry-run-branch -f -git config user.email "CI email" -git config user.name "CI name" - -npm run release-dry-run | tee "${DRYRUN_PATH}" || true - -# Extract passed and failed -VERSIONING=$(git log -1 --format="%b") -if [ ! -z "${GITHUB_OUTPUT}" ]; then - EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) - echo "DRYRUN_RESPONSE<<$EOF" >> "$GITHUB_OUTPUT" - echo "dryrun_response=${VERSIONING}" >> "${GITHUB_OUTPUT}" - echo "$EOF" >> "$GITHUB_OUTPUT" -fi - -# Cleanup -rm -rf "${WORKSPACE}" diff --git a/scripts/ci-release.mjs b/scripts/ci-release.mjs new file mode 100644 index 000000000..a79327399 --- /dev/null +++ b/scripts/ci-release.mjs @@ -0,0 +1,168 @@ +/** + * MIT License + * + * Copyright (c) 2017-present, Elasticsearch BV + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +import * as process from 'node:process' +import fetch from 'node-fetch' +import { execa } from 'execa' +import { writeFile } from 'node:fs/promises' +import * as path from 'node:path' + +function raiseError(msg) { + console.log(msg) + process.exit(1) +} + +async function gitContext() { + try { + const { stdout: username } = await execa("git", ["config", "user.name"]) + const { stdout: email } = await execa("git", ["config", "user.email"]) + return { + username: username, + email: email + } + } catch (err) { + raiseError("Failed to extract git context") + } +} + +// Script logic +async function main() { + const isDryRun = process.env.DRY_RUN == null || process.env.DRY_RUN !== 'false' + + // Extract git context + const ctx = await gitContext() + console.log(`Git User: username=${ctx.username}, email=${ctx.email}`) + + if (isDryRun) { + await dryRunMode() + } else { + await prodMode() + } +} + +async function dryRunMode() { + console.log("Running in dry-run mode") + + const registryUrl = process.env.REGISTRY_URL || "http://localhost:4873" + console.log(`Checking local registry url: ${registryUrl}`) + try { + await fetch(registryUrl); + } catch (err) { + raiseError("The local registry isn't available") + } + + try { + // Ref: https://github.com/npm/npm-registry-client/blob/856eefea40a2a88618835978e281300e3406924b/lib/adduser.js#L63-L68 + const response = await fetch(`${registryUrl}/-/user/org.couchdb.user:test`, { + method: "PUT", + headers: { + "Accept": "application/json", + "Content-Type": "application/json" + }, + body: JSON.stringify({ + name: "test", + password: "test", + email: "test@elastic.co" + }) + }); + + let npmrcData = "registry=http://localhost:4873\n" + if (response.status === 201) { + const { token: token } = await response.json() + npmrcData += `//localhost:4873/:_authToken=${token}\n` + } else { + raiseError("Failed to add user to private registry") + } + + await writeFile(path.join(process.cwd(), '.npmrc'), npmrcData) + } catch (err) { + raiseError("Failed to login to private registry") + } + + try { + await execa("npx", ["lerna", "publish", `--registry=${registryUrl}`, "--no-push", "--yes"], {stdin: process.stdin}) + .pipeStdout(process.stdout) + .pipeStderr(process.stderr) + } catch (err) { + raiseError("Failed to publish npm packages") + } + + await execa("git", ["diff", process.cwd()]) + .pipeStdout(process.stdout) + .pipeStderr(process.stderr) + + await execa("git", ["log", "-1", "--stat", process.cwd()]) + .pipeStdout(process.stdout) + .pipeStderr(process.stderr) +} + +async function prodMode() { + console.log("Running in prod mode") + + const totpCode = process.env.TOTP_CODE + if (totpCode == null || totpCode === "") { + raiseError("The 'TOTP_CODE' env var isn't defined") + } + + const githubToken = process.env.GITHUB_TOKEN + if (githubToken == null || githubToken === "") { + raiseError("The 'GITHUB_TOKEN' env var isn't defined") + } + + try { + await execa("npx", ["lerna", "publish", `--otp=${totpCode}`, "--yes"], { + stdin: process.stdin, + env: { + GH_TOKEN: githubToken + } + }) + .pipeStdout(process.stdout) + .pipeStderr(process.stderr) + } catch (err) { + raiseError("Failed to publish npm packages") + } + + try { + await execa("npm", ["run", "github-release"], { + stdin: process.stdin, + env: { + GITHUB_TOKEN: githubToken + } + }) + .pipeStdout(process.stdout) + .pipeStderr(process.stderr) + } catch (err) { + raiseError("Failed to publish github release") + } +} + +// Entrypoint +;(async () => { + try { + await main() + } catch (err) { + raiseError(err) + } +})()