From a5d6ddcd7c7b8f8fe3f375087bb538dc2654368b Mon Sep 17 00:00:00 2001 From: Eren Yeager Date: Tue, 30 May 2023 11:39:30 +0000 Subject: [PATCH] chore: make separate step for publish --- .eslintignore | 1 + .github/workflows/deploy.yaml | 45 +++++++++ .github/workflows/publish.yaml | 19 ++-- package.json | 1 + scripts/common/utils.mjs | 10 +- scripts/deploy/command.mjs | 35 +++++++ scripts/{publish => deploy}/config.mjs | 6 +- scripts/deploy/utils.mjs | 60 +++++++++++ scripts/publish/command.mjs | 54 +++++----- scripts/publish/utils.mjs | 133 ++++++++++--------------- 10 files changed, 238 insertions(+), 126 deletions(-) create mode 100644 .github/workflows/deploy.yaml create mode 100755 scripts/deploy/command.mjs rename scripts/{publish => deploy}/config.mjs (66%) create mode 100644 scripts/deploy/utils.mjs diff --git a/.eslintignore b/.eslintignore index 5b0ec4f020..c2c9d09944 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ node_modules dist *.js +scripts \ No newline at end of file diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000000..673a0f7ece --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,45 @@ +name: Deploy +on: + workflow_dispatch: + +jobs: + deploy-it: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup NodeJS + uses: actions/setup-node@v3 + with: + node-version: '18' + cache: 'yarn' + + - name: Git + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + + - name: Install packages + run: yarn install --frozen-lockfile + + - name: Deploy packages + run: yarn run deploy + env: + REF: ${{ github.ref }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + GH_TOKEN: ${{ github.token }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_PROJECT_WALLETS: ${{ secrets.VERCEL_PROJECT_WALLETS }} + VERCEL_PROJECT_Q: ${{ secrets.VERCEL_PROJECT_Q }} + VERCEL_PROJECT_WALLET_ADAPTER: ${{ secrets.VERCEL_PROJECT_WALLET_ADAPTER }} + VERCEL_PROJECT_WIDGET_CONFIG: ${{ secrets.VERCEL_PROJECT_WIDGET_CONFIG }} + VERCEL_PROJECT_WIDGET_APP: ${{ secrets.VERCEL_PROJECT_WIDGET_APP }} diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index d9f21c5c2c..398ca8b769 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -19,12 +19,6 @@ jobs: node-version: '18' cache: 'yarn' - - name: Restore cache - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - name: Git run: | git config --global user.name 'github-actions[bot]' @@ -37,6 +31,12 @@ jobs: env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Restore cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + - name: Install packages run: yarn install --frozen-lockfile @@ -46,10 +46,3 @@ jobs: REF: ${{ github.ref }} BASE_REF: ${{ github.event.pull_request.base.ref }} GH_TOKEN: ${{ github.token }} - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - VERCEL_PROJECT_WALLETS: ${{ secrets.VERCEL_PROJECT_WALLETS }} - VERCEL_PROJECT_Q: ${{ secrets.VERCEL_PROJECT_Q }} - VERCEL_PROJECT_WALLET_ADAPTER: ${{ secrets.VERCEL_PROJECT_WALLET_ADAPTER }} - VERCEL_PROJECT_WIDGET_CONFIG: ${{ secrets.VERCEL_PROJECT_WIDGET_CONFIG }} - VERCEL_PROJECT_WIDGET_APP: ${{ secrets.VERCEL_PROJECT_WIDGET_APP }} diff --git a/package.json b/package.json index 3ef899aa8a..373c246b93 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "lint": "nx run-many --target=lint", "format": "nx run-many --target=format", "publish": "node ./scripts/publish/command.mjs", + "deploy": "node ./scripts/deploy/command.mjs", "release": "node ./scripts/release/command.mjs", "upgrade-all": "node ./scripts/upgrade-all/command.mjs --project " }, diff --git a/scripts/common/utils.mjs b/scripts/common/utils.mjs index 7e9308ade6..c7800ecf27 100644 --- a/scripts/common/utils.mjs +++ b/scripts/common/utils.mjs @@ -3,6 +3,7 @@ import { fileURLToPath } from 'url'; import { readFileSync } from 'fs'; import { join } from 'path'; import { execa } from 'execa'; +import process from 'node:process'; const root = join(printDirname(), '..', '..'); @@ -36,7 +37,9 @@ export function pacakgeJson(location) { export async function packageNamesToPackagesWithInfo(names) { const allPackages = await workspacePackages(); - return names.map((pkgName) => allPackages.find((pkg) => pkg.name === pkgName)); + return names.map((pkgName) => + allPackages.find((pkg) => pkg.name === pkgName) + ); } /* @@ -46,3 +49,8 @@ export async function packageNamesToPackagesWithInfo(names) { export function packageNameWithoutScope(name) { return name.replace(/@.+\//, ''); } + +// we are adding a fallback, to make sure predefiend VERCEL_PACKAGES always will be true. +export function getEnvWithFallback(name) { + return process.env[name] || 'NOT SET'; +} diff --git a/scripts/deploy/command.mjs b/scripts/deploy/command.mjs new file mode 100755 index 0000000000..c3595412bc --- /dev/null +++ b/scripts/deploy/command.mjs @@ -0,0 +1,35 @@ +#!/usr/bin/env node +'use strict'; +import process from 'node:process'; +import { workspacePackages } from '../common/utils.mjs'; +import { buildPackages, logAsSection } from '../publish/utils.mjs'; +import { deployProjectsToVercel } from './utils.mjs'; + +// TODO: Working directory should be empty. +async function run() { + // Detect last relase and what packages has changed since then. + const packages = await workspacePackages(); + const privatePackages = packages.filter((pkg) => { + return pkg.private; + }); + + await buildPackages(privatePackages).catch((e) => { + console.log( + '[-] BUILD FAILED. Ignore it to workflow run the rest of tasks.' + ); + console.log(e); + }); + logAsSection('[x] Build for VERCEL'); + await deployProjectsToVercel(privatePackages).catch((e) => { + console.log( + '[-] DEPLOY FAILED. Ignore it to workflow run the rest of tasks.' + ); + console.log(e); + }); + logAsSection('[x] Deploy to VERCEL'); +} + +run().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/publish/config.mjs b/scripts/deploy/config.mjs similarity index 66% rename from scripts/publish/config.mjs rename to scripts/deploy/config.mjs index 31086b7383..830053286a 100644 --- a/scripts/publish/config.mjs +++ b/scripts/deploy/config.mjs @@ -1,4 +1,5 @@ -import { getEnvWithFallback } from './utils.mjs'; +import { getEnvWithFallback } from '../common/utils.mjs'; +import process from 'node:process'; const scope = `@rango-dev`; export const VERCEL_ORG_ID = process.env.VERCEL_ORG_ID; @@ -7,6 +8,5 @@ export const VERCEL_PACKAGES = { [`${scope}/wallets-demo`]: getEnvWithFallback('VERCEL_PROJECT_WALLETS'), [`${scope}/queue-manager-demo`]: getEnvWithFallback('VERCEL_PROJECT_Q'), [`${scope}/wallets-adapter-demo`]: getEnvWithFallback('VERCEL_PROJECT_WALLET_ADAPTER'), - [`${scope}/widget-playground`]: getEnvWithFallback('VERCEL_PROJECT_WIDGET_CONFIG'), - [`${scope}/widget-app`]: getEnvWithFallback('VERCEL_PROJECT_WIDGET_APP'), + [`${scope}/config-client`]: getEnvWithFallback('VERCEL_PROJECT_WIDGET_CONFIG'), }; diff --git a/scripts/deploy/utils.mjs b/scripts/deploy/utils.mjs new file mode 100644 index 0000000000..8eb93d7edd --- /dev/null +++ b/scripts/deploy/utils.mjs @@ -0,0 +1,60 @@ +import { execa } from 'execa'; +import { detectChannel } from '../publish/utils.mjs'; +import { VERCEL_ORG_ID, VERCEL_PACKAGES, VERCEL_TOKEN } from './config.mjs'; + +export function getVercelProjectId(packageName) { + return VERCEL_PACKAGES[packageName]; +} + +export async function deployProjectsToVercel(pkgs) { + await Promise.all(pkgs.map((pkg) => deploySingleProjectToVercel(pkg))); +} +export async function deploySingleProjectToVercel(pkg) { + const deployTo = detectChannel() === 'prod' ? 'production' : 'preview'; + + const env = { + VERCEL_ORG_ID: VERCEL_ORG_ID, + VERCEL_PROJECT_ID: getVercelProjectId(pkg.name), + }; + + console.log(`start deplyoing ${pkg.name}...`); + + await execa( + 'yarn', + [ + 'vercel', + 'pull', + '--cwd', + pkg.location, + '--environment', + deployTo, + '--token', + VERCEL_TOKEN, + '--yes', + ], + { env }, + ); + await execa('yarn', ['vercel', 'build', '--cwd', pkg.location, '--token', VERCEL_TOKEN], { env }); + await execa('yarn', ['vercel', pkg.location, '--prebuilt', '--token', VERCEL_TOKEN], { env }); + + console.log(`${pkg.name} deployed.`); +} + +export function groupPackagesForDeploy(packages) { + const output = { + npm: [], + vercel: [], + }; + + packages.forEach((pkg) => { + if (!!getVercelProjectId(pkg.name)) { + output.vercel.push(pkg); + } else if (!pkg.private) { + // If getVercelProjectId returns undefined, it's possible to be added as npm package + // So here we are making sure it's not a private package and can be published using npm + output.npm.push(pkg); + } + }); + + return output; +} diff --git a/scripts/publish/command.mjs b/scripts/publish/command.mjs index c232e32ec2..aab4840d6d 100755 --- a/scripts/publish/command.mjs +++ b/scripts/publish/command.mjs @@ -1,15 +1,14 @@ #!/usr/bin/env node 'use strict'; +import process from 'node:process'; import { buildPackages, changed, - deployProjectsToVercel, detectChannel, exportNx, generateChangelog, getLastReleasedHashId, - groupPackagesForDeploy, increaseVersionForMain, increaseVersionForNext, logAsSection, @@ -23,6 +22,7 @@ import { nxToGraph } from '../common/graph/helpers.mjs'; import { execa, $ } from 'execa'; import { performance } from 'node:perf_hooks'; import { packageNamesToPackagesWithInfo } from '../common/utils.mjs'; +import { groupPackagesForDeploy } from '../deploy/utils.mjs'; // TODO: Working directory should be empty. async function run() { @@ -36,7 +36,9 @@ async function run() { logAsSection('Run...', `at ${baseCommit}`); console.log( - changedPkgs.map((pkg) => `- ${pkg.name} (current version: ${pkg.version})`).join('\n'), + changedPkgs + .map((pkg) => `- ${pkg.name} (current version: ${pkg.version})`) + .join('\n') ); // If any package has changed, we exit from the process. @@ -52,15 +54,19 @@ async function run() { nxToGraph(nxGraph, graph); graph.onlyAffected(changedPkgs.map((pkg) => pkg.name)); const sortedList = graph.sort(); - const sortedPackagesToPublish = await packageNamesToPackagesWithInfo([...sortedList]); + const sortedPackagesToPublish = await packageNamesToPackagesWithInfo([ + ...sortedList, + ]); performance.mark('end-analyze'); console.log( - `Execution time: ${performance.measure('analyze', 'start-analyze', 'end-analyze').duration}ms`, + `Execution time: ${ + performance.measure('analyze', 'start-analyze', 'end-analyze').duration + }ms` ); console.log( 'Packages will be published, in this order:\n', - sortedPackagesToPublish.map((pkg) => pkg.name).join(' -> '), + sortedPackagesToPublish.map((pkg) => pkg.name).join(' -> ') ); console.log('::endgroup::'); @@ -75,7 +81,8 @@ async function run() { // Git tag & commit console.log(`::group::Tag, commit and push to repository.`); logAsSection(`Git Tagging..`); - const tagOptions = channel === 'prod' ? { skipGitTagging: false } : { skipGitTagging: true }; + const tagOptions = + channel === 'prod' ? { skipGitTagging: false } : { skipGitTagging: true }; await tagPackagesAndCommit(updatedPackages, tagOptions); logAsSection(`Pushing tags to remote...`); @@ -108,13 +115,17 @@ async function publish(changedPkg, channel) { console.log( updatedPackages - .map((pkg) => `[x] Versioninig: ${pkg.name} (next version: ${pkg.version})`) - .join('\n'), + .map( + (pkg) => `[x] Versioninig: ${pkg.name} (next version: ${pkg.version})` + ) + .join('\n') ); // 2. Changelog & Github Release if (channel === 'prod') { - await Promise.all(updatedPackages.map((pkg) => generateChangelog(pkg, { saveToFile: true }))); + await Promise.all( + updatedPackages.map((pkg) => generateChangelog(pkg, { saveToFile: true })) + ); await Promise.all(updatedPackages.map(makeGithubRelease)); logAsSection(`[x] Github Release & Changelog generated.`); @@ -129,7 +140,7 @@ async function publish(changedPkg, channel) { const duration_build = performance.measure( `publish-build-${changedPkg.name}`, `start-publish-build-${changedPkg.name}`, - `end-publish-build-${changedPkg.name}`, + `end-publish-build-${changedPkg.name}` ).duration; logAsSection(`[x] Build for NPM ${duration_build}ms`); performance.mark(`start-publish-npm-${changedPkg.name}`); @@ -139,27 +150,12 @@ async function publish(changedPkg, channel) { const duration_npm = performance.measure( `publish-npm-${changedPkg.name}`, `start-publish-npm-${changedPkg.name}`, - `end-publish-npm-${changedPkg.name}`, + `end-publish-npm-${changedPkg.name}` ).duration; logAsSection(`[x] Publish on NPM ${duration_npm}ms`); } - // 4. Publish to Vercel - if (packages.vercel.length) { - // TODO: This is not a good solution, because it will build the package itself twice. - await buildPackages(packages.vercel).catch((e) => { - console.log('[-] BUILD FAILED. Ignore it to workflow run the rest of tasks.'); - console.log(e); - }); - logAsSection('[x] Build for VERCEL'); - await deployProjectsToVercel(packages.vercel).catch((e) => { - console.log('[-] DEPLOY FAILED. Ignore it to workflow run the rest of tasks.'); - console.log(e); - }); - logAsSection('[x] Deploy to VERCEL'); - } - - // 6. Yarn upgrade-all + // 5. Yarn upgrade-all const { stdout: upgradeStdOut } = await execa('yarn', [ 'upgrade-all', changedPkg.name, @@ -173,7 +169,7 @@ async function publish(changedPkg, channel) { const duration_publish = performance.measure( `publish-${changedPkg.name}`, `start-publish-${changedPkg.name}`, - `end-publish-${changedPkg.name}`, + `end-publish-${changedPkg.name}` ).duration; console.log(`Execution time: ${duration_publish}ms`); console.log(`::endgroup::`); diff --git a/scripts/publish/utils.mjs b/scripts/publish/utils.mjs index cd8ea08ffb..5f2d678fa8 100644 --- a/scripts/publish/utils.mjs +++ b/scripts/publish/utils.mjs @@ -2,8 +2,12 @@ import chalk from 'chalk'; import { execa } from 'execa'; import { join } from 'path'; import conventionanRecommendBump from 'conventional-recommended-bump'; -import { packageNameWithoutScope, printDirname, workspacePackages } from '../common/utils.mjs'; -import { VERCEL_ORG_ID, VERCEL_PACKAGES, VERCEL_TOKEN } from './config.mjs'; +import { + getEnvWithFallback, + packageNameWithoutScope, + printDirname, + workspacePackages, +} from '../common/utils.mjs'; import { importJson } from '../common/graph/helpers.mjs'; import { overrideNPMVersionOnLocal } from '../common/npm.mjs'; const root = join(printDirname(), '..', '..'); @@ -27,7 +31,10 @@ export async function generateChangelog(pkg, options) { command.push('-i', changelogPath, '-s'); } - const { stdout: bin } = await execa('yarn', ['bin', 'conventional-changelog']); + const { stdout: bin } = await execa('yarn', [ + 'bin', + 'conventional-changelog', + ]); const { stdout } = await execa(bin, command); return stdout; } @@ -37,12 +44,22 @@ export async function makeGithubRelease(updatedPkg) { saveToFile: false, }); const tag = generateTagName(updatedPkg); - await execa('gh', ['release', 'create', tag, '--target', 'main', '--notes', notes]); + await execa('gh', [ + 'release', + 'create', + tag, + '--target', + 'main', + '--notes', + notes, + ]); } export async function packageNamesToPackagesWithInfo(names) { const allPackages = await workspacePackages(); - return names.map((pkgName) => allPackages.find((pkg) => pkg.name === pkgName)); + return names.map((pkgName) => + allPackages.find((pkg) => pkg.name === pkgName) + ); } /** @@ -65,49 +82,18 @@ export async function recommendBump(pkg) { } else { resolve(recommendation); } - }, + } ); }); } -export async function deployProjectsToVercel(pkgs) { - await Promise.all(pkgs.map((pkg) => deploySingleProjectToVercel(pkg))); -} -export async function deploySingleProjectToVercel(pkg) { - const deployTo = detectChannel() === 'prod' ? 'production' : 'preview'; - - const env = { - VERCEL_ORG_ID: VERCEL_ORG_ID, - VERCEL_PROJECT_ID: getVercelProjectId(pkg.name), - }; - - console.log(`start deplyoing ${pkg.name}...`); - - await execa( - 'yarn', - [ - 'vercel', - 'pull', - '--cwd', - pkg.location, - '--environment', - deployTo, - '--token', - VERCEL_TOKEN, - '--yes', - ], - { env }, - ); - await execa('yarn', ['vercel', 'build', '--cwd', pkg.location, '--token', VERCEL_TOKEN], { env }); - await execa('yarn', ['vercel', pkg.location, '--prebuilt', '--token', VERCEL_TOKEN], { env }); - - console.log(`${pkg.name} deployed.`); -} export async function publishPackages(updatedPkgs, dist = 'next') { const result = await Promise.all( updatedPkgs.map(({ location }) => - execa('yarn', ['publish', location, '--tag', dist]).then(({ stdout }) => stdout), - ), + execa('yarn', ['publish', location, '--tag', dist]).then( + ({ stdout }) => stdout + ) + ) ); console.log('Published...'); @@ -141,7 +127,10 @@ export async function pushToRemote(branch, remote = 'origin') { console.log(stdout); } -export async function tagPackagesAndCommit(updatedPackages, { skipGitTagging }) { +export async function tagPackagesAndCommit( + updatedPackages, + { skipGitTagging } +) { const tags = updatedPackages.map(generateTagName); const subject = `chore(release): publish\n\n`; @@ -150,11 +139,19 @@ export async function tagPackagesAndCommit(updatedPackages, { skipGitTagging }) // making a publish commit await execa('git', ['add', '.']); - await execa('git', ['commit', '-m', message, '-m', `Affected packages: ${tags.join(',')}`]); + await execa('git', [ + 'commit', + '-m', + message, + '-m', + `Affected packages: ${tags.join(',')}`, + ]); // creating annotated tags based on packages if (!skipGitTagging) { - await Promise.all(tags.map((tag) => execa('git', ['tag', '-a', tag, '-m', tag]))); + await Promise.all( + tags.map((tag) => execa('git', ['tag', '-a', tag, '-m', tag])) + ); } return tags; @@ -167,7 +164,7 @@ export async function increaseVersionForNext(changedPkgs) { const checkVersionAndIncrease = async () => { const versions = await overrideNPMVersionOnLocal(name, dist); console.log( - `Checking local & npm versions for ${name}@${dist}: \n local: ${versions.local_version} \n npm: ${versions.npm_version}`, + `Checking local & npm versions for ${name}@${dist}: \n local: ${versions.local_version} \n npm: ${versions.npm_version}` ); return await execa('yarn', [ 'workspace', @@ -179,7 +176,7 @@ export async function increaseVersionForNext(changedPkgs) { ]).then(({ stdout }) => stdout); }; return checkVersionAndIncrease(); - }), + }) ); // Getting latest packages info to show the updated version. @@ -201,8 +198,8 @@ export async function increaseVersionForMain(changedPkgs) { recommendBump(name).then((recommendation) => { console.log(`::debug::${{ name, recommendation }}`); nextVersions[name] = recommendation.releaseType; - }), - ), + }) + ) ); } catch (e) { throw new Error(`Calculating next version failed. details: ${e}`); @@ -223,7 +220,7 @@ export async function increaseVersionForMain(changedPkgs) { }; return checkVersionAndIncrease(); - }), + }) ); // Getting latest packages info to show the updated version. @@ -252,7 +249,7 @@ export async function changed(since) { changed: !!result, }; }); - }), + }) ); return all.filter((pkg) => pkg.changed); } @@ -268,7 +265,12 @@ export function detectChannel() { // TODO: Check for when there is no tag. export async function getLastReleasedHashId(useTag = false) { if (useTag) { - const { stdout: hash } = await execa('git', ['rev-list', '--max-count', 1, '--tags']); + const { stdout: hash } = await execa('git', [ + 'rev-list', + '--max-count', + 1, + '--tags', + ]); return hash; } else { const { stdout: hash } = await execa('git', [ @@ -283,29 +285,6 @@ export async function getLastReleasedHashId(useTag = false) { } } -export function groupPackagesForDeploy(packages) { - const output = { - npm: [], - vercel: [], - }; - - packages.forEach((pkg) => { - if (!!getVercelProjectId(pkg.name)) { - output.vercel.push(pkg); - } else if (!pkg.private) { - // If getVercelProjectId returns undefined, it's possible to be added as npm package - // So here we are making sure it's not a private package and can be published using npm - output.npm.push(pkg); - } - }); - - return output; -} - -export function getVercelProjectId(packageName) { - return VERCEL_PACKAGES[packageName]; -} - export function logAsSection(title, sub = '') { let message = chalk.bgBlue.white.bold(title); if (!!sub) { @@ -315,12 +294,6 @@ export function logAsSection(title, sub = '') { console.log(message); } - -// we are adding a fallback, to make sure predefiend VERCEL_PACKAGES always will be true. -export function getEnvWithFallback(name) { - return process.env[name] || 'NOT SET'; -} - export function generateTagName(pkg) { return `${packageNameWithoutScope(pkg.name)}@${pkg.version}`; }