Skip to content

Commit

Permalink
Automatically publish prereleases to npm (#155)
Browse files Browse the repository at this point in the history
This commit creates `x.x.x-insiders.COMMIT_HASH` builds of all of our public packages, and publishes them to npm with the `insiders` tag, so one could say `npm install @theatre/[email protected]` and use the package at that particular commit.

Co-authored-by: Aria Minaei <[email protected]>
  • Loading branch information
fulopkovacs and AriaMinaei committed May 18, 2022
1 parent 3d10325 commit 8520c74
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 3 deletions.
10 changes: 10 additions & 0 deletions .github/.yarnrc.publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Auth config for publishing to npm registry.
# It's put in /.github so it's only picked up by
# github actions.

npmPublishRegistry: 'https://registry.npmjs.org'

npmRegistries:
//registry.npmjs.org:
npmAlwaysAuth: true
npmAuthToken: ${NODE_AUTH_TOKEN}
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [main]

jobs:
build:
Test:
runs-on: ubuntu-latest

strategy:
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/publish-prerelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: 'Publish Prerelease'
on:
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install
- name: Build the theatre packages
run: yarn build
- name: Update .yarnrc.yml with the auth config for the npmPublishRegistry
run: cat .github/.yarnrc.publish.yml >> .yarnrc.yml
- name: Publish the theatre packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# LATEST_COMMIT_HASH: ${{ github.event.pull_request.head.sha }}
run: yarn zx scripts/prerelease.mjs
4 changes: 2 additions & 2 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ nodeLinker: node-modules

plugins:
- path: .yarn/plugins/@yarnpkg/plugin-compat.cjs
spec: "@yarnpkg/plugin-compat"
spec: '@yarnpkg/plugin-compat'
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
spec: '@yarnpkg/plugin-interactive-tools'

yarnPath: .yarn/releases/yarn-3.2.0.cjs
149 changes: 149 additions & 0 deletions scripts/prerelease.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* This script publishes the insider packages from the CI. You can't run it locally unless you have a a valid npm access token and you store its value in the `NPM_TOKEN` environmental variable.
*/

import os from 'os'
import path from 'path'

const packagesToPublish = [
'@theatre/core',
'@theatre/studio',
'@theatre/dataverse',
'@theatre/react',
'@theatre/browser-bundles',
'@theatre/r3f',
]

/**
* Receives a version number and returns it without the tags, if there are any
*
* @param {string} version - Version number
* @returns Version number without the tags
*
* @example
* ```javascript
* const version_1 = '0.4.8-dev3-ec175817'
* const version_2 = '0.4.8'
*
* stripTag(version_1) === stripTag(version_2) === '0.4.8' // returns `true`
* ```
*/
function stripTag(version) {
const regExp = /^[0-9]+\.[0-9]+\.[0-9]+/g
const matches = version.match(regExp)
if (!matches) {
throw new Error(`Version number not found in "${version}"`)
}

return matches[0]
}

/**
* Creates a version number like `0.4.8-insiders.ec175817`
*
* @param {string} packageName - Name of the package
* @param {string} commitHash - A commit hash
*/
function getNewVersionName(packageName, commitHash) {
// The `r3f` package has its own release schedule, so its version numbers
// are almost always different from the rest of the packages.
const pathToPackageJson =
packageName === '@theatre/r3f'
? path.resolve(__dirname, '..', 'packages', 'r3f', 'package.json')
: path.resolve(__dirname, '../', './package.json')

const jsonData = JSON.parse(
fs.readFileSync(pathToPackageJson, {encoding: 'utf-8'}),
)
const strippedVersion = stripTag(jsonData.version)

return `${strippedVersion}-insiders.${commitHash}`
}

/**
* Assigns the new versions to the packages
*
* @param {{name: string, location: string}[]} workspacesListObjects - An Array of objects containing information about the workspaces
* @param {string} latestCommitHash - Hash of the latest commit
*/
async function assignVersions(workspacesListObjects, latestCommitHash) {
for (const workspaceData of workspacesListObjects) {
const pathToPackage = path.resolve(
__dirname,
'../',
workspaceData.location,
'./package.json',
)

const original = JSON.parse(
fs.readFileSync(pathToPackage, {encoding: 'utf-8'}),
)

let {version, dependencies, peerDependencies, devDependencies} = original
// The @theatre/r3f package curently doesn't track the same version number of the other packages like @theatre/core,
// so we need to generate version numbers independently for each package
version = getNewVersionName(workspaceData.name, latestCommitHash)
// Normally we don't have to override the package versions in dependencies because yarn would already convert
// all the "workspace:*" versions to a fixed version before publishing. However, packages like @theatre/studio
// have a peerDependency on @theatre/core set to "*" (meaning they would work with any version of @theatre/core).
// This is not the desired behavior in pre-release versions, so here, we'll fix those "*" versions to the set version.
for (const deps of [dependencies, peerDependencies, devDependencies]) {
if (!deps) continue
for (const wpObject of workspacesListObjects) {
if (deps[wpObject.name]) {
deps[wpObject.name] = getNewVersionName(
wpObject.name,
latestCommitHash,
)
}
}
}
const newJson = {
...original,
version,
dependencies,
peerDependencies,
devDependencies,
}
fs.writeFileSync(
path.join(pathToPackage),
JSON.stringify(newJson, undefined, 2),
{encoding: 'utf-8'},
)
await $`prettier --write ${workspaceData.location + '/package.json'}`
}
}

;(async function () {
// @ts-ignore ignore
process.env.THEATRE_IS_PUBLISHING = true
// In the CI `git log -1` points to a fake merge commit,
// so we have to use the value of a special GitHub context variable
// through the `GITHUB_SHA` environmental variable.

// The length of the abbreviated commit hash can change, that's why we
// need the lenght of the fake merge commit's abbreviated hash.
const fakeMergeCommitHashLength = (await $`git log -1 --pretty=format:%h`)
.stdout.length

const latestCommitHash = process.env.GITHUB_SHA.slice(
0,
fakeMergeCommitHashLength,
)

const workspacesListString = await $`yarn workspaces list --json`
const workspacesListObjects = workspacesListString.stdout
.split(os.EOL)
// strip out empty lines
.filter(Boolean)
.map((x) => JSON.parse(x))

await assignVersions(workspacesListObjects, latestCommitHash)

await Promise.all(
packagesToPublish.map((workspaceName) => {
const npmTag = 'insiders'
return $`yarn workspace ${workspaceName} npm publish --access public --tag ${npmTag}`
}),
)
})()

1 comment on commit 8520c74

@vercel
Copy link

@vercel vercel bot commented on 8520c74 May 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.