From bc36e7aacc59bc909bae05b3910570bf585dacc3 Mon Sep 17 00:00:00 2001 From: Nikaru Date: Tue, 9 Apr 2024 11:20:03 +0000 Subject: [PATCH] feat: add preview deployment to pr --- .github/workflows/checks.yaml | 47 ++++++++++++++++++++++++ package.json | 1 + scripts/common/errors.mjs | 7 ++++ scripts/common/github.mjs | 21 +++++++++++ scripts/deploy/command.mjs | 18 ++++------ scripts/deploy/config.mjs | 3 ++ scripts/deploy/utils.mjs | 67 +++++++++++++++++++++++++++++++++-- yarn.lock | 33 +++++++++++++++++ 8 files changed, 183 insertions(+), 14 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 8a39a587c9..e4e00209db 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -18,3 +18,50 @@ jobs: run: node ./scripts/check-conventional-commits/command.mjs env: REF: ${{ github.ref }} + + # for preview deployment for specific project, add vercel project id in environment section + - name: Preview Deployment + id: deploy + run: | + yarn global add vercel + yarn run deploy + env: + REF: ${{ github.ref }} + GH_TOKEN: ${{ github.token }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_PROJECT_WIDGET_CONFIG: ${{ secrets.VERCEL_PROJECT_WIDGET_CONFIG }} # widget playground + VERCEL_PROJECT_WIDGET_APP: ${{ secrets.VERCEL_PROJECT_WIDGET_APP }} # widget app + ENABLE_PREVIEW_DEPLOY: true + + outputs: + # the structure of output variable is {packageNameWithoutScope}-url like: widget-app-url + app_url: ${{ steps.deploy.outputs.widget-app-url }} + playground_url: ${{ steps.deploy.outputs.widget-playground-url }} + + # add job for each project that you want has preview deployment + app-preview: + runs-on: ubuntu-latest + needs: check + environment: + name: app-preview + url: ${{ steps.seturl.outputs.url }} + steps: + - name: Extract Preview URL + id: seturl + run: | + echo "url=${{ needs.check.outputs.app_url }}">> $GITHUB_OUTPUT + echo "Preview URL: ${{ needs.check.outputs.app_url}}" + + playground-preview: + runs-on: ubuntu-latest + needs: check + environment: + name: playground-preview + url: ${{ steps.seturl.outputs.url }} + steps: + - name: Extract Preview URL + id: seturl + run: | + echo "url=${{ needs.check.outputs.playground_url }}">> $GITHUB_OUTPUT + echo "Preview URL: ${{ needs.check.outputs.playground_url}}" diff --git a/package.json b/package.json index c2918cb194..66ee8b0ea4 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "typescript": "^5.3.3" }, "devDependencies": { + "@actions/core": "^1.10.1", "@babel/core": "^7.22.5", "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.2", diff --git a/scripts/common/errors.mjs b/scripts/common/errors.mjs index 5f1df79948..d0ceabe491 100644 --- a/scripts/common/errors.mjs +++ b/scripts/common/errors.mjs @@ -103,4 +103,11 @@ export class CrowdinError extends Error { constructor(msg) { super(msg); } +} + +export class VercelError extends Error { + name = 'VercelError'; + constructor(msg) { + super(msg); + } } \ No newline at end of file diff --git a/scripts/common/github.mjs b/scripts/common/github.mjs index 6f3ef5a0c8..abd39d5fed 100644 --- a/scripts/common/github.mjs +++ b/scripts/common/github.mjs @@ -125,6 +125,27 @@ export async function createPullRequest(pr) { return output; } +export async function createComment(comment) { + const {commentBody, issueNumber} = comment; + + if (!issueNumber || !commentBody) { + throw new GithubCommandError( + 'Creating comment cannot proceed without required parameters. \n', + JSON.stringify({ issueNumber, commentBody }) + ); + } + + const output = await execa('gh', ['issue', 'comment', issueNumber, '--body', commentBody]) + .then(({ stdout }) => stdout) + .catch((err) => { + throw new GithubCommandError( + `Failed to add comment to issue. \n ${err.stdout || err} \n` + ); + }); + + return output; +} + export function checkEnvironments() { const envs = { NPM_TOKEN: !!process.env.NPM_TOKEN, diff --git a/scripts/deploy/command.mjs b/scripts/deploy/command.mjs index 55f8d491d5..8451510719 100755 --- a/scripts/deploy/command.mjs +++ b/scripts/deploy/command.mjs @@ -1,30 +1,26 @@ #!/usr/bin/env node 'use strict'; import process from 'node:process'; -import { workspacePackages } from '../common/utils.mjs'; import { build } from '../publish/build.mjs'; import { logAsSection } from '../publish/utils.mjs'; -import { deployProjectsToVercel } from './utils.mjs'; +import { deployProjectsToVercel, getClientsListToBeDeployed } from './utils.mjs'; + -const EXCLUDED_PACKAGES = ['@rango-dev/widget-iframe']; // TODO: Working directory should be empty. async function run() { - // Detect last release and what packages has changed since then. - const packages = await workspacePackages(); - const privatePackages = packages.filter((pkg) => { - if (EXCLUDED_PACKAGES.includes(pkg.name)) return false; - return pkg.private; - }); + + const listPackagesToBeDeployed = await getClientsListToBeDeployed(); + logAsSection('[x] Check Environment'); - await build(privatePackages).catch((e) => { + await build(listPackagesToBeDeployed).catch((e) => { console.log( '[-] BUILD FAILED. Ignore it to workflow run the rest of tasks.' ); throw e; }); logAsSection('[x] Build for VERCEL'); - await deployProjectsToVercel(privatePackages).catch((e) => { + await deployProjectsToVercel(listPackagesToBeDeployed).catch((e) => { console.log( '[-] DEPLOY FAILED. Ignore it to workflow run the rest of tasks.' ); diff --git a/scripts/deploy/config.mjs b/scripts/deploy/config.mjs index 2217379d5d..6a1b025a63 100644 --- a/scripts/deploy/config.mjs +++ b/scripts/deploy/config.mjs @@ -4,6 +4,9 @@ import process from 'node:process'; const scope = `@rango-dev`; export const VERCEL_ORG_ID = process.env.VERCEL_ORG_ID; export const VERCEL_TOKEN = process.env.VERCEL_TOKEN; +export const ENABLE_PREVIEW_DEPLOY = process.env.ENABLE_PREVIEW_DEPLOY; +export const EXCLUDED_PACKAGES = ['@rango-dev/widget-iframe']; + export const VERCEL_PACKAGES = { [`${scope}/wallets-demo`]: getEnvWithFallback('VERCEL_PROJECT_WALLETS'), [`${scope}/queue-manager-demo`]: getEnvWithFallback('VERCEL_PROJECT_Q'), diff --git a/scripts/deploy/utils.mjs b/scripts/deploy/utils.mjs index 10996b58ba..25d7ecbd36 100644 --- a/scripts/deploy/utils.mjs +++ b/scripts/deploy/utils.mjs @@ -1,6 +1,9 @@ import { execa } from 'execa'; import { detectChannel } from '../common/github.mjs'; -import { VERCEL_ORG_ID, VERCEL_PACKAGES, VERCEL_TOKEN } from './config.mjs'; +import { ENABLE_PREVIEW_DEPLOY, EXCLUDED_PACKAGES, VERCEL_ORG_ID, VERCEL_PACKAGES, VERCEL_TOKEN } from './config.mjs'; +import * as actionCore from '@actions/core'; +import { VercelError } from '../common/errors.mjs'; +import { packageNameWithoutScope, workspacePackages } from '../common/utils.mjs'; export function getVercelProjectId(packageName) { return VERCEL_PACKAGES[packageName]; @@ -37,15 +40,38 @@ export async function deploySingleProjectToVercel(pkg) { ], { env } ); + await execa( 'vercel', ['build', '--cwd', pkg.location, '--token', VERCEL_TOKEN], { env } ); - await execa('vercel', [pkg.location, '--prebuilt', '--token', VERCEL_TOKEN], { - env, + + const vercelResult = await execa( + 'vercel', + [pkg.location, '--prebuilt', '--token', VERCEL_TOKEN], + { env } + ).then((result) => result.stdout) + .catch((err) => { + throw new VercelError( + `An error occurred on deploy ${pkg.name} package \n ${err.message} \n ${err.stderr}` + ); }); + // Run tail -1 on the stdout to get the last line, because `vercel` command returns the URL in the last line. + const uRLPreview = await execa('tail', ['-1'], { input: vercelResult }) + .then(result => result.stdout) + .catch((err) => { + throw new VercelError( + `An error occurred on get url preview for ${pkg.name} package \n ${err.message} \n ${err.stderr}` + ); + }); + + // set package name and url preview to github output for use in workflow + const tagName = packageNameWithoutScope(pkg.name); + actionCore.setOutput(`${tagName}-url`, uRLPreview); + + console.log(`${tagName}-url:`, uRLPreview); console.log(`${pkg.name} deployed.`); } @@ -67,3 +93,38 @@ export function groupPackagesForDeploy(packages) { return output; } + +export async function getClientsListToBeDeployed(){ + /* + Deploys packages based on the state of the `ENABLE_PREVIEW_DEPLOY` environment variable. + if ENABLE_PREVIEW_DEPLOY is true, only packages that has project id in workflow environments will be deployed. + else private packages will be deployed. + */ + + // Detect last release and what packages has changed since then. + const packages = await workspacePackages(); + const listPackagesToBeDeployed = packages.filter((pkg) => { + if (EXCLUDED_PACKAGES.includes(pkg.name)) return false; + + if (ENABLE_PREVIEW_DEPLOY) { + const hasProjectId = getVercelProjectId(pkg.name) && getVercelProjectId(pkg.name) !== 'NOT SET' ; + return pkg.private && hasProjectId; + } + else { + return pkg.private; + } + }); + + if(ENABLE_PREVIEW_DEPLOY){ + console.log('preview deployment is enabled.'); + console.log('these packages will be deployed:', listPackagesToBeDeployed.map(pkg=>pkg.name).join(', ')); + console.log('note: if you need add more packages to be deployed, first you need to add vercel project id to workflow environments then follow documentation there.'); + } + else{ + console.log('preview deployment is disabled.'); + console.log('these packages will be deployed:', listPackagesToBeDeployed.map(pkg=>pkg.name).join(', ')); + } + + + return listPackagesToBeDeployed; +} diff --git a/yarn.lock b/yarn.lock index a366a1f8ad..1bf703cac8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,22 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== +"@actions/core@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a" + integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g== + dependencies: + "@actions/http-client" "^2.0.1" + uuid "^8.3.2" + +"@actions/http-client@^2.0.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.1.tgz#ed3fe7a5a6d317ac1d39886b0bb999ded229bb38" + integrity sha512-KhC/cZsq7f8I4LfZSJKgCvEwfkE8o1538VoBeoGzokVLLnbFDEAdFD3UhoMklxo2un9NJVBdANOresx7vTHlHw== + dependencies: + tunnel "^0.0.6" + undici "^5.25.4" + "@adraffy/ens-normalize@1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" @@ -2561,6 +2577,11 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@floating-ui/core@^1.4.2": version "1.5.0" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c" @@ -17305,6 +17326,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" @@ -17519,6 +17545,13 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici@^5.25.4: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + unenv@^1.7.4: version "1.8.0" resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.8.0.tgz#0f860d5278405700bd95d47b23bc01f3a735d68c"