feat: publish beta and development tags on npm (#4769)

* feat: add logic to publish beta/dev npm

This adds new log to publish the npm package both while working on PRs and when
PRs are merged into main, allowing us to easily test changes in a
production-like setting.

Co-authored-by: Asher <ash@coder.com>
This commit is contained in:
Joe Previte 2022-01-21 16:28:56 -07:00 committed by GitHub
parent 63f99da087
commit a2f530161f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 133 additions and 11 deletions

View File

@ -177,6 +177,15 @@ jobs:
name: npm-package name: npm-package
path: ./package.tar.gz path: ./package.tar.gz
- name: Publish npm package with PR number and commit SHA
run: yarn publish:npm
env:
ENVIRONMENT: "development"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TAG: ${{ github.event.number }}
PR_NUMBER_AND_COMMIT_SHA: ${{ github.event.number }}-${{ github.event.pull_request.head.sha }}
# TODO: cache building yarn --production # TODO: cache building yarn --production
# possibly 2m30s of savings(?) # possibly 2m30s of savings(?)
# this requires refactoring our release scripts # this requires refactoring our release scripts

View File

@ -16,11 +16,13 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Run ./ci/steps/publish-npm.sh - name: Publish npm package with PR number and commit SHA
run: ./ci/steps/publish-npm.sh run: yarn publish:npm
env: env:
ENVIRONMENT: "production"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TAG: "latest"
homebrew: homebrew:
# The newest version of code-server needs to be available on npm when this runs # The newest version of code-server needs to be available on npm when this runs

27
.github/workflows/npm-dev.yaml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Publish on npm and tag with "beta"
on:
# Shows the manual trigger in GitHub UI
# helpful as a back-up in case the GitHub Actions Workflow fails
workflow_dispatch:
push:
branches:
- main
jobs:
# NOTE: this job requires curl, jq and yarn
# All of them are included in ubuntu-latest.
npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run ./ci/steps/publish-npm.sh
run: yarn publish:npm
env:
ENVIRONMENT: "staging"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TAG: "beta"
PR_NUMBER_AND_COMMIT_SHA: ${{ github.event.number }}-${{ github.event.pull_request.head.sha }}

View File

@ -4,15 +4,60 @@ set -euo pipefail
main() { main() {
cd "$(dirname "$0")/../.." cd "$(dirname "$0")/../.."
source ./ci/lib.sh source ./ci/lib.sh
source ./ci/steps/steps-lib.sh
# npm view won't exit with non-zero so we have to check the output. ## Authentication tokens
local hasVersion # Needed to publish on NPM
hasVersion=$(npm view "code-server@$VERSION" version) if ! is_env_var_set "NPM_TOKEN"; then
if [[ $hasVersion == "$VERSION" ]]; then echo "NPM_TOKEN is not set. Cannot publish to npm without credentials."
echo "$VERSION is already published" exit 1
return
fi fi
# NOTE@jsjoeio - only needed if we use the download_artifact
# because we talk to the GitHub API.
# Needed to use GitHub API
if ! is_env_var_set "GITHUB_TOKEN"; then
echo "GITHUB_TOKEN is not set. Cannot download npm release artifact without GitHub credentials."
exit 1
fi
## Environment
# This string is used to determine how we should tag the npm release.
# Environment can be one of three choices:
# "development" - this means we tag with the PR number, allowing
# a developer to install this version with `yarn add code-server@<pr-number>`
# "staging" - this means we tag with `beta`, allowing
# a developer to install this version with `yarn add code-server@beta`
# "production" - this means we tag with `latest` (default), allowing
# a developer to install this version with `yarn add code-server@latest`
if ! is_env_var_set "ENVIRONMENT"; then
echo "ENVIRONMENT is not set. Cannot determine npm tag without ENVIRONMENT."
exit 1
fi
## Publishing Information
# All the variables below are used to determine how we should publish
# the npm package. We also use this information for bumping the version.
# This is because npm won't publish your package unless it's a new version.
# i.e. for development, we bump the version to <current version>-<pr number>-<commit sha>
# example: "version": "4.0.1-4769-ad7b23cfe6ffd72914e34781ef7721b129a23040"
# We need the current package.json VERSION
if ! is_env_var_set "VERSION"; then
echo "VERSION is not set. Cannot publish to npm without VERSION."
exit 1
fi
# We need TAG to know what to publish under on npm
# Options are "latest", "beta", or "<pr number >"
# See Environment comments above to know when each is used.
if ! is_env_var_set "NPM_TAG"; then
echo "NPM_TAG is not set. This is needed for tagging the npm release."
exit 1
fi
echo "using tag: $NPM_TAG"
# This allows us to publish to npm in CI workflows
if [[ ${CI-} ]]; then if [[ ${CI-} ]]; then
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
fi fi
@ -24,7 +69,45 @@ main() {
# Ignore symlink when publishing npm package # Ignore symlink when publishing npm package
# See: https://github.com/cdr/code-server/pull/3935 # See: https://github.com/cdr/code-server/pull/3935
echo "node_modules.asar" > release/.npmignore echo "node_modules.asar" > release/.npmignore
yarn publish --non-interactive release
# NOTES:@jsjoeio
# We only need to run npm version for "development" and "staging".
# This is because our release:prep script automatically bumps the version
# in the package.json and we commit it as part of the release PR.
if [[ "$ENVIRONMENT" == "production" ]]; then
NPM_VERSION="$VERSION"
else
echo "Not a production environment"
echo "Found environment: $ENVIRONMENT"
echo "Manually bumping npm version..."
if ! is_env_var_set "PR_NUMBER_AND_COMMIT_SHA"; then
echo "PR_NUMBER_AND_COMMIT_SHA is not set. This is needed for setting the npm version in non-production environments."
exit 1
fi
# We modify the version in the package.json
# to be the current version + the PR number + commit SHA
# Example: "version": "4.0.1-4769-ad7b23cfe6ffd72914e34781ef7721b129a23040"
NPM_VERSION="$VERSION-$PR_NUMBER_AND_COMMIT_SHA"
pushd release
# NOTE:@jsjoeio
# I originally tried to use `yarn version` but ran into issues and abandoned it.
npm version "$NPM_VERSION"
popd
fi
# We need to make sure we haven't already published the version.
# This is because npm view won't exit with non-zero so we have
# to check the output.
local hasVersion
hasVersion=$(npm view "code-server@$NPM_VERSION" version)
if [[ $hasVersion == "$NPM_VERSION" ]]; then
echo "$NPM_VERSION is already published"
return
fi
yarn publish --non-interactive release --tag "$NPM_TAG"
} }
main "$@" main "$@"

View File

@ -23,6 +23,7 @@
"test:scripts": "./ci/dev/test-scripts.sh", "test:scripts": "./ci/dev/test-scripts.sh",
"package": "./ci/build/build-packages.sh", "package": "./ci/build/build-packages.sh",
"postinstall": "./ci/dev/postinstall.sh", "postinstall": "./ci/dev/postinstall.sh",
"publish:npm": "./ci/steps/publish-npm.sh",
"_audit": "./ci/dev/audit.sh", "_audit": "./ci/dev/audit.sh",
"fmt": "./ci/dev/fmt.sh", "fmt": "./ci/dev/fmt.sh",
"lint": "./ci/dev/lint.sh", "lint": "./ci/dev/lint.sh",

View File

@ -14,7 +14,7 @@ describe("settings", () => {
testDir = await tmpdir(testName) testDir = await tmpdir(testName)
}) })
describe("with invalid JSON in settings file", () => { describe("with invalid JSON in settings file", () => {
let mockSettingsFile = "coder.json" const mockSettingsFile = "coder.json"
let pathToMockSettingsFile = "" let pathToMockSettingsFile = ""
beforeEach(async () => { beforeEach(async () => {
@ -33,7 +33,7 @@ describe("settings", () => {
}) })
}) })
describe("with invalid settings file path", () => { describe("with invalid settings file path", () => {
let mockSettingsFile = "nonExistent.json" const mockSettingsFile = "nonExistent.json"
let pathToMockSettingsFile = "" let pathToMockSettingsFile = ""
beforeEach(async () => { beforeEach(async () => {