From bc4bc3f9d79e8504b3781d8bb87383b83d6d99ec Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Tue, 29 Jan 2019 23:37:27 -0800 Subject: [PATCH 01/11] chore: bump package-lock --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9465eca..709b7b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,8 @@ { "name": "@primer/deploy", - "requires": true, + "version": "1.0.0", "lockfileVersion": 1, + "requires": true, "dependencies": { "@babel/code-frame": { "version": "7.0.0", @@ -5936,6 +5937,5 @@ "decamelize": "^1.2.0" } } - }, - "version": "1.0.0" + } } From a9e121feb47eee67f65ed5a8033a888083076730 Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Tue, 29 Jan 2019 23:55:49 -0800 Subject: [PATCH 02/11] fix: add error case for failed deployment --- src/deploy.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 8d5c50d..648822c 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -10,8 +10,12 @@ module.exports = function deploy(args) { console.log(`[deploy] deploying "${name}" with now...`) return now(args) .then(url => { - console.log(`[deploy] deployed to: ${url}`) - return {name, root: url, url} + if (url) { + console.log(`[deploy] deployed to: ${url}`) + return {name, root: url, url} + } else { + throw new Error(`Unable to get deployment URL from now: ${url}`) + } }) .then(res => { const {url} = res From c3654a77dba5674de1c4b3c501d0a337b97ee116 Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Tue, 29 Jan 2019 23:56:03 -0800 Subject: [PATCH 03/11] test: add tests for now() --- src/__tests__/now.js | 37 +++++++++++++++++++++++++++++++++++++ src/now.js | 3 ++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/now.js diff --git a/src/__tests__/now.js b/src/__tests__/now.js new file mode 100644 index 0000000..8699ec8 --- /dev/null +++ b/src/__tests__/now.js @@ -0,0 +1,37 @@ +const execa = require('execa') +const mockedEnv = require('mocked-env') +const now = require('../now') + +jest.mock('execa') +execa.mockImplementation((cmd, args, opts) => + Promise.resolve({ + stderr: '', + stdout: '' + }) +) + +describe('now()', () => { + let restoreEnv = () => {} + afterEach(() => restoreEnv()) + + it('throws if process.env.NOW_TOKEN is falsy', () => { + mockEnv({NOW_TOKEN: ''}) + expect(() => now()).toThrow() + }) + + it('calls `npx now --token=$NOW_TOKEN` with no additional args', () => { + mockEnv({NOW_TOKEN: 'xyz'}) + now() + expect(execa).lastCalledWith('npx', ['now', '--token=xyz'], {stderr: 'inherit'}) + }) + + it('calls with additional arguments passed as an array', () => { + mockEnv({NOW_TOKEN: 'abc'}) + now(['foo', 'bar']) + expect(execa).lastCalledWith('npx', ['now', '--token=abc', 'foo', 'bar'], {stderr: 'inherit'}) + }) + + function mockEnv(env) { + restoreEnv = mockedEnv(env) + } +}) diff --git a/src/now.js b/src/now.js index fbb69f1..70914fc 100644 --- a/src/now.js +++ b/src/now.js @@ -5,5 +5,6 @@ module.exports = function now(args = []) { if (!NOW_TOKEN) { throw new Error(`The NOW_TOKEN env var is required`) } - return execa('npx', ['now', `--token=${NOW_TOKEN}`, ...args], {stderr: 'inherit'}).then(res => res.stdout) + const nowArgs = ['now', `--token=${NOW_TOKEN}`, ...args] + return execa('npx', nowArgs, {stderr: 'inherit'}).then(res => res.stdout) } From 9c66732a5833d90c89929658264cd8b5736073f2 Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Wed, 30 Jan 2019 16:06:41 -0800 Subject: [PATCH 04/11] refactor: use custom readJSON() for easier mocking --- src/now-json.js | 6 +++--- src/package-json.js | 8 ++++---- src/read-json.js | 5 +++++ 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 src/read-json.js diff --git a/src/now-json.js b/src/now-json.js index 2a5e1e0..66dd460 100644 --- a/src/now-json.js +++ b/src/now-json.js @@ -1,4 +1,4 @@ -const fs = require('fs') const {join} = require('path') -const nowPath = join(process.cwd(), 'now.json') -module.exports = fs.existsSync(nowPath) ? require(nowPath) : {} +const readJSON = require('./read-json') + +module.exports = readJSON(join(process.cwd(), 'now.json')) || {} diff --git a/src/package-json.js b/src/package-json.js index 595ac56..1c5e305 100644 --- a/src/package-json.js +++ b/src/package-json.js @@ -1,4 +1,4 @@ -const fs = require('fs') -const path = require('path') -const packagePath = path.join(process.cwd(), 'package.json') -module.exports = fs.existsSync(packagePath) ? require(packagePath) : {} +const {join} = require('path') +const readJSON = require('./read-json') + +module.exports = readJSON(join(process.cwd(), 'package.json')) || {} diff --git a/src/read-json.js b/src/read-json.js new file mode 100644 index 0000000..c8b2d7f --- /dev/null +++ b/src/read-json.js @@ -0,0 +1,5 @@ +const {existsSync} = require('fs') + +module.exports = function readJSON(path) { + return existsSync(path) ? require(path) : undefined +} From 7d1cfa091604a31bc3f1b2183dd93facecc9e75e Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 11:06:19 -0800 Subject: [PATCH 05/11] feat: support path aliases with rules.json --- src/__tests__/deploy.js | 96 +++++++++++++++++++++++++++++++++++++++++ src/deploy.js | 40 +++++++++++++---- src/now-json.js | 4 -- src/package-json.js | 4 -- src/read-json.js | 2 + 5 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 src/__tests__/deploy.js delete mode 100644 src/now-json.js delete mode 100644 src/package-json.js diff --git a/src/__tests__/deploy.js b/src/__tests__/deploy.js new file mode 100644 index 0000000..ba9e41e --- /dev/null +++ b/src/__tests__/deploy.js @@ -0,0 +1,96 @@ +const execa = require('execa') +const mockedEnv = require('mocked-env') +const readJSON = require('../read-json') +const deploy = require('../deploy') +const now = require('../now') +const commitStatus = require('../commit-status') + +jest.mock('../now') +jest.mock('../read-json') +jest.mock('../commit-status') + +commitStatus.mockImplementation(() => Promise.resolve({})) + +describe('deploy()', () => { + let restoreEnv = () => {} + afterEach(() => { + restoreEnv() + // clear the call data + now.mockReset() + // restore the original behavior + readJSON.mockReset() + }) + + it('calls now() once when w/o an alias', () => { + mockFiles({ + 'package.json': {name: 'foo'}, + 'now.json': {}, + 'rules.json': null + }) + + const url = 'foo-123.now.sh' + now.mockImplementation((cmd, args, opts) => Promise.resolve(url)) + mockEnv({GITHUB_REF: ''}) + + return deploy().then(res => { + expect(res).toEqual({name: 'foo', root: url, url}) + expect(now).toHaveBeenCalledTimes(1) + expect(now).toHaveBeenCalledWith() + }) + }) + + it('calls now() twice when there is an alias', () => { + mockFiles({ + 'package.json': {name: 'foo'}, + 'now.json': {}, + 'rules.json': null + }) + + const url = 'foo-123.now.sh' + const alias = 'foo-bar.now.sh' + now.mockImplementation((cmd, args, opts) => Promise.resolve(url)) + mockEnv({GITHUB_REF: 'refs/heads/bar'}) + + return deploy().then(res => { + expect(res).toEqual({name: 'foo', root: url, alias, url: alias}) + expect(now).toHaveBeenCalledTimes(2) + expect(now).toHaveBeenNthCalledWith(1) + expect(now).toHaveBeenNthCalledWith(2, ['alias', url, alias]) + }) + }) + + it('calls now() three times when there is a rules.json', () => { + const prodAlias = 'foo.now.sh' + mockFiles({ + 'package.json': {name: 'foo'}, + 'now.json': {alias: prodAlias}, + // exists + 'rules.json': {} + }) + + const url = 'foo-123.now.sh' + const alias = 'foo-bar.now.sh' + now.mockImplementation((cmd, args, opts) => Promise.resolve(url)) + mockEnv({GITHUB_REF: 'refs/heads/bar'}) + + return deploy().then(res => { + expect(res).toEqual({name: 'foo', root: url, alias, url: prodAlias}) + expect(now).toHaveBeenCalledTimes(3) + expect(now).toHaveBeenNthCalledWith(1) + expect(now).toHaveBeenNthCalledWith(2, ['alias', url, alias]) + expect(now).toHaveBeenNthCalledWith(3, ['alias', alias, prodAlias, '-r', 'rules.json']) + }) + }) + + function mockEnv(env) { + restoreEnv = mockedEnv(env) + } + + function mockFiles(files) { + readJSON.mockImplementation(path => { + if (path in files) { + return files[path] + } + }) + } +}) diff --git a/src/deploy.js b/src/deploy.js index 648822c..955928c 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,17 +1,21 @@ -const {dirname} = require('path') -const now = require('./now') -const getAlias = require('./get-alias') +const fs = require('fs') +const {dirname, join} = require('path') const commitStatus = require('./commit-status') +const getAlias = require('./get-alias') +const now = require('./now') +const readJSON = require('./read-json') -module.exports = function deploy(args) { - const nowJson = require('./now-json') - const packageJson = require('./package-json') +module.exports = function deploy(args = []) { + const nowJson = readJSON('now.json') || {} + const packageJson = readJSON('package.json') || {} + const rulesJson = readJSON('rules.json') const name = nowJson.name || packageJson.name || dirname(process.cwd()) + console.log(`[deploy] deploying "${name}" with now...`) - return now(args) + return now() .then(url => { if (url) { - console.log(`[deploy] deployed to: ${url}`) + // console.log(`[deploy] deployed to: ${url}`) return {name, root: url, url} } else { throw new Error(`Unable to get deployment URL from now: ${url}`) @@ -21,10 +25,28 @@ module.exports = function deploy(args) { const {url} = res const alias = getAlias(name) if (alias) { - res.url = alias + res.url = res.alias = alias return now(['alias', ...args, url, alias]) .then(() => commitStatus(alias)) + .then(() => { + if (rulesJson) { + const {alias} = res + const prodAlias = nowJson.alias + if (!prodAlias) { + console.warn(`[deploy] no alias field in now.json!`) + return res + } else if (prodAlias === alias) { + console.warn(`[deploy] already aliased to production URL: ${alias}; skipping rules.json`) + return res + } + res.url = prodAlias + return now(['alias', ...args, alias, prodAlias, '-r', 'rules.json']) + .then(() => commitStatus(prodAlias)) + } + }) .then(() => res) + } else { + return res } }) } diff --git a/src/now-json.js b/src/now-json.js deleted file mode 100644 index 66dd460..0000000 --- a/src/now-json.js +++ /dev/null @@ -1,4 +0,0 @@ -const {join} = require('path') -const readJSON = require('./read-json') - -module.exports = readJSON(join(process.cwd(), 'now.json')) || {} diff --git a/src/package-json.js b/src/package-json.js deleted file mode 100644 index 1c5e305..0000000 --- a/src/package-json.js +++ /dev/null @@ -1,4 +0,0 @@ -const {join} = require('path') -const readJSON = require('./read-json') - -module.exports = readJSON(join(process.cwd(), 'package.json')) || {} diff --git a/src/read-json.js b/src/read-json.js index c8b2d7f..79b16dc 100644 --- a/src/read-json.js +++ b/src/read-json.js @@ -1,5 +1,7 @@ +const {resolve} = require('path') const {existsSync} = require('fs') module.exports = function readJSON(path) { + const resolved = resolve(process.cwd(), path) return existsSync(path) ? require(path) : undefined } From f76a6a10c711076bd097d1b654dd6df0ceb215e9 Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 11:14:48 -0800 Subject: [PATCH 06/11] fix: lint --- src/__tests__/deploy.js | 7 +++---- src/__tests__/now.js | 7 +------ src/deploy.js | 6 ++---- src/event-json.js | 2 -- src/read-json.js | 2 +- 5 files changed, 7 insertions(+), 17 deletions(-) delete mode 100644 src/event-json.js diff --git a/src/__tests__/deploy.js b/src/__tests__/deploy.js index ba9e41e..1870691 100644 --- a/src/__tests__/deploy.js +++ b/src/__tests__/deploy.js @@ -1,4 +1,3 @@ -const execa = require('execa') const mockedEnv = require('mocked-env') const readJSON = require('../read-json') const deploy = require('../deploy') @@ -29,7 +28,7 @@ describe('deploy()', () => { }) const url = 'foo-123.now.sh' - now.mockImplementation((cmd, args, opts) => Promise.resolve(url)) + now.mockImplementation(() => Promise.resolve(url)) mockEnv({GITHUB_REF: ''}) return deploy().then(res => { @@ -48,7 +47,7 @@ describe('deploy()', () => { const url = 'foo-123.now.sh' const alias = 'foo-bar.now.sh' - now.mockImplementation((cmd, args, opts) => Promise.resolve(url)) + now.mockImplementation(() => Promise.resolve(url)) mockEnv({GITHUB_REF: 'refs/heads/bar'}) return deploy().then(res => { @@ -70,7 +69,7 @@ describe('deploy()', () => { const url = 'foo-123.now.sh' const alias = 'foo-bar.now.sh' - now.mockImplementation((cmd, args, opts) => Promise.resolve(url)) + now.mockImplementation(() => Promise.resolve(url)) mockEnv({GITHUB_REF: 'refs/heads/bar'}) return deploy().then(res => { diff --git a/src/__tests__/now.js b/src/__tests__/now.js index 8699ec8..00bc90a 100644 --- a/src/__tests__/now.js +++ b/src/__tests__/now.js @@ -3,12 +3,7 @@ const mockedEnv = require('mocked-env') const now = require('../now') jest.mock('execa') -execa.mockImplementation((cmd, args, opts) => - Promise.resolve({ - stderr: '', - stdout: '' - }) -) +execa.mockImplementation(() => Promise.resolve({stderr: '', stdout: ''})) describe('now()', () => { let restoreEnv = () => {} diff --git a/src/deploy.js b/src/deploy.js index 955928c..622f064 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,5 +1,4 @@ -const fs = require('fs') -const {dirname, join} = require('path') +const {dirname} = require('path') const commitStatus = require('./commit-status') const getAlias = require('./get-alias') const now = require('./now') @@ -40,8 +39,7 @@ module.exports = function deploy(args = []) { return res } res.url = prodAlias - return now(['alias', ...args, alias, prodAlias, '-r', 'rules.json']) - .then(() => commitStatus(prodAlias)) + return now(['alias', ...args, alias, prodAlias, '-r', 'rules.json']).then(() => commitStatus(prodAlias)) } }) .then(() => res) diff --git a/src/event-json.js b/src/event-json.js deleted file mode 100644 index f300386..0000000 --- a/src/event-json.js +++ /dev/null @@ -1,2 +0,0 @@ -const {GITHUB_EVENT_PATH} = process.env -module.exports = GITHUB_EVENT_PATH ? require(GITHUB_EVENT_PATH) : {} diff --git a/src/read-json.js b/src/read-json.js index 79b16dc..c73f2f2 100644 --- a/src/read-json.js +++ b/src/read-json.js @@ -3,5 +3,5 @@ const {existsSync} = require('fs') module.exports = function readJSON(path) { const resolved = resolve(process.cwd(), path) - return existsSync(path) ? require(path) : undefined + return existsSync(resolved) ? require(resolved) : undefined } From c01f9f1d2463aa556ea6f664ab742d38f5c7c6e5 Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 11:33:00 -0800 Subject: [PATCH 07/11] fix: work out path alias logic on master branch --- src/__tests__/deploy.js | 30 ++++++++++++++++++++++++++---- src/__tests__/get-alias.js | 5 +---- src/deploy.js | 27 +++++++++++++++++---------- src/get-alias.js | 4 +--- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/__tests__/deploy.js b/src/__tests__/deploy.js index 1870691..f34ee77 100644 --- a/src/__tests__/deploy.js +++ b/src/__tests__/deploy.js @@ -32,9 +32,9 @@ describe('deploy()', () => { mockEnv({GITHUB_REF: ''}) return deploy().then(res => { - expect(res).toEqual({name: 'foo', root: url, url}) expect(now).toHaveBeenCalledTimes(1) expect(now).toHaveBeenCalledWith() + expect(res).toEqual({name: 'foo', root: url, url}) }) }) @@ -51,14 +51,14 @@ describe('deploy()', () => { mockEnv({GITHUB_REF: 'refs/heads/bar'}) return deploy().then(res => { - expect(res).toEqual({name: 'foo', root: url, alias, url: alias}) expect(now).toHaveBeenCalledTimes(2) expect(now).toHaveBeenNthCalledWith(1) expect(now).toHaveBeenNthCalledWith(2, ['alias', url, alias]) + expect(res).toEqual({name: 'foo', root: url, alias, url: alias}) }) }) - it('calls now() three times when there is a rules.json', () => { + it('calls now() two times when there is a rules.json, not on master', () => { const prodAlias = 'foo.now.sh' mockFiles({ 'package.json': {name: 'foo'}, @@ -73,11 +73,33 @@ describe('deploy()', () => { mockEnv({GITHUB_REF: 'refs/heads/bar'}) return deploy().then(res => { - expect(res).toEqual({name: 'foo', root: url, alias, url: prodAlias}) + expect(now).toHaveBeenCalledTimes(2) + expect(now).toHaveBeenNthCalledWith(1) + expect(now).toHaveBeenNthCalledWith(2, ['alias', url, alias]) + expect(res).toEqual({name: 'foo', root: url, alias, url: alias}) + }) + }) + + it('calls now() three times when there is a rules.json, on master', () => { + const prodAlias = 'primer.style' + mockFiles({ + 'package.json': {name: 'primer-style'}, + 'now.json': {alias: prodAlias}, + // exists + 'rules.json': {} + }) + + const url = 'primer-style-123.now.sh' + const alias = 'primer-style.now.sh' + now.mockImplementation(() => Promise.resolve(url)) + mockEnv({GITHUB_REF: 'refs/heads/master'}) + + return deploy().then(res => { expect(now).toHaveBeenCalledTimes(3) expect(now).toHaveBeenNthCalledWith(1) expect(now).toHaveBeenNthCalledWith(2, ['alias', url, alias]) expect(now).toHaveBeenNthCalledWith(3, ['alias', alias, prodAlias, '-r', 'rules.json']) + expect(res).toEqual({name: 'primer-style', root: url, alias, url: prodAlias}) }) }) diff --git a/src/__tests__/get-alias.js b/src/__tests__/get-alias.js index 98ab907..7dba2fc 100644 --- a/src/__tests__/get-alias.js +++ b/src/__tests__/get-alias.js @@ -1,10 +1,7 @@ -const mockedEnv = require('mocked-env') const getAlias = require('../get-alias') describe('getAlias()', () => { it('works', () => { - const restore = mockedEnv({GITHUB_REF: 'refs/heads/add-foo'}) - expect(getAlias('@primer/css')).toEqual('primer-css-add-foo.now.sh') - restore() + expect(getAlias('@primer/css', 'add-foo')).toEqual('primer-css-add-foo.now.sh') }) }) diff --git a/src/deploy.js b/src/deploy.js index 622f064..757347e 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,34 +1,41 @@ const {dirname} = require('path') +const getBranch = require('./get-branch') const commitStatus = require('./commit-status') -const getAlias = require('./get-alias') +const getBranchAlias = require('./get-alias') const now = require('./now') const readJSON = require('./read-json') -module.exports = function deploy(args = []) { +module.exports = function deploy() { const nowJson = readJSON('now.json') || {} const packageJson = readJSON('package.json') || {} const rulesJson = readJSON('rules.json') + const name = nowJson.name || packageJson.name || dirname(process.cwd()) + const branch = getBranch(name) console.log(`[deploy] deploying "${name}" with now...`) return now() .then(url => { if (url) { // console.log(`[deploy] deployed to: ${url}`) - return {name, root: url, url} + return { + name, + root: url, + url + } } else { throw new Error(`Unable to get deployment URL from now: ${url}`) } }) .then(res => { const {url} = res - const alias = getAlias(name) - if (alias) { - res.url = res.alias = alias - return now(['alias', ...args, url, alias]) - .then(() => commitStatus(alias)) + const branchAlias = getBranchAlias(name, branch) + if (branchAlias) { + res.url = res.alias = branchAlias + return now(['alias', url, branchAlias]) + .then(() => commitStatus(branchAlias)) .then(() => { - if (rulesJson) { + if (branch === 'master' && rulesJson) { const {alias} = res const prodAlias = nowJson.alias if (!prodAlias) { @@ -39,7 +46,7 @@ module.exports = function deploy(args = []) { return res } res.url = prodAlias - return now(['alias', ...args, alias, prodAlias, '-r', 'rules.json']).then(() => commitStatus(prodAlias)) + return now(['alias', alias, prodAlias, '-r', 'rules.json']).then(() => commitStatus(prodAlias)) } }) .then(() => res) diff --git a/src/get-alias.js b/src/get-alias.js index fb9f6e6..f6da0c5 100644 --- a/src/get-alias.js +++ b/src/get-alias.js @@ -1,9 +1,7 @@ -const getBranch = require('./get-branch') const slug = require('./slug') -module.exports = function getAlias(name) { +module.exports = function getAlias(name, branch) { const nameSlug = slug(name) - const branch = getBranch() if (branch === 'master') { return `${nameSlug}.now.sh` From b15189c09f529f884824ecf18d94d4f185fd206c Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 11:47:52 -0800 Subject: [PATCH 08/11] fix: make production aliases on master w/o rules.json work --- src/__tests__/deploy.js | 23 ++++++++++++++++++++++- src/deploy.js | 8 +++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/__tests__/deploy.js b/src/__tests__/deploy.js index f34ee77..c0b067d 100644 --- a/src/__tests__/deploy.js +++ b/src/__tests__/deploy.js @@ -38,7 +38,7 @@ describe('deploy()', () => { }) }) - it('calls now() twice when there is an alias', () => { + it('deploys to a branch alias on branches other than master', () => { mockFiles({ 'package.json': {name: 'foo'}, 'now.json': {}, @@ -58,6 +58,27 @@ describe('deploy()', () => { }) }) + it('deploys to the "alias" field from now.json on the master branch', () => { + const name = 'hello' + const alias = 'hello.world' + mockFiles({ + 'package.json': {name}, + 'now.json': {alias}, + 'rules.json': null + }) + + const root = 'hello-123.now.sh' + now.mockImplementation(() => Promise.resolve(root)) + mockEnv({GITHUB_REF: 'refs/heads/master'}) + + return deploy().then(res => { + expect(now).toHaveBeenCalledTimes(2) + expect(now).toHaveBeenNthCalledWith(1) + expect(now).toHaveBeenNthCalledWith(2, ['alias', root, alias]) + expect(res).toEqual({name, root, url: alias}) + }) + }) + it('calls now() two times when there is a rules.json, not on master', () => { const prodAlias = 'foo.now.sh' mockFiles({ diff --git a/src/deploy.js b/src/deploy.js index 757347e..8be4b8b 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -29,6 +29,13 @@ module.exports = function deploy() { }) .then(res => { const {url} = res + const prodAlias = nowJson.alias + if (branch === 'master' && !rulesJson) { + res.url = prodAlias + return now(['alias', url, prodAlias]) + .then(() => commitStatus(prodAlias)) + .then(() => res) + } const branchAlias = getBranchAlias(name, branch) if (branchAlias) { res.url = res.alias = branchAlias @@ -37,7 +44,6 @@ module.exports = function deploy() { .then(() => { if (branch === 'master' && rulesJson) { const {alias} = res - const prodAlias = nowJson.alias if (!prodAlias) { console.warn(`[deploy] no alias field in now.json!`) return res From f75cc404cadcbc35a59a9d78b74fd864ab4ef50a Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 12:48:11 -0800 Subject: [PATCH 09/11] docs: update readme with path alias conditions --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 484281e..2c10c2a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # primer/deploy -This GitHub Action deploys to [Now] and aliases the successful deployment to either: +This GitHub Action deploys to [Now] and aliases the successful deployment to a predictable URL according to the following conditions: -* The `alias` field in `now.json` if the branch is `master`; or -* A branch-specific URL in the form `{name}-{branch}.now.sh`, where: - * `{name}` is your app's name (in `now.json`, `package.json`, or the name of the directory); - * `{branch}` is the part after `refs/heads/` in the `GITHUB_REF` [environment variable](https://developer.github.com/actions/creating-github-actions/accessing-the-runtime-environment/#environment-variables); and - * Both strings are normalized in the alias to trim any leading non-alphanumeric characters and replace any sequence of non-alphanumeric characters with a single `-`. +1. We run `now` without any arguments to get the "root" deployment URL, which is generated by Now. +1. If the branch is `master`, we treat the `alias` field in `now.json` as the production URL and: + 1. If there is a `rules.json`: + * `now alias .now.sh` to create a fallback URL for path aliases + * `now alias -r rules.json` to set up path aliases + 1. `now alias ` to alias the production URL. +1. `now alias -.now.sh` to alias the root deployment to a branch-specific URL. + +The app name (``) and branch (``) are both "slugified" to strip invalid characters so that they'll work as URLs. Leading non-word characters are removed, and any sequence of characters that isn't alphanumeric or `-` is replaced with a single `-`. In other words, `@primer/css` becomes `primer-css`, `shawnbot/some_branch` becomes `shawnbot-some-branch`, and so on. ## Usage To use this action in your own workflow, add the following snippet to your `.github/main.workflow` file: From 94cc8bd31906add5f723d9fe9a035cf22b99da9d Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 12:58:43 -0800 Subject: [PATCH 10/11] test: add test for unset GITHUB_REF in getBranch() --- src/__tests__/get-branch.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/__tests__/get-branch.js b/src/__tests__/get-branch.js index 0816794..4e49318 100644 --- a/src/__tests__/get-branch.js +++ b/src/__tests__/get-branch.js @@ -7,4 +7,10 @@ describe('getBranch()', () => { expect(getBranch()).toEqual('foo') restore() }) + + it('returns an empty string if GITHUB_REF is unset', () => { + const restore = mockedEnv({GITHUB_REF: undefined}) + expect(getBranch()).toEqual('') + restore() + }) }) From 0762dd299f8039e777de587c8447d6a6016dfe46 Mon Sep 17 00:00:00 2001 From: Shawn Allen Date: Thu, 31 Jan 2019 12:59:10 -0800 Subject: [PATCH 11/11] test: add tests for readJSON --- src/__tests__/read-json.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/__tests__/read-json.js diff --git a/src/__tests__/read-json.js b/src/__tests__/read-json.js new file mode 100644 index 0000000..2567d82 --- /dev/null +++ b/src/__tests__/read-json.js @@ -0,0 +1,12 @@ +const readJSON = require('../read-json') + +describe('readJSON()', () => { + it('reads package.json from the cwd', () => { + const pkg = readJSON('package.json') + expect(pkg instanceof Object).toBe(true) + }) + + it('returns undefined for non-existent files', () => { + expect(readJSON('x.json')).toBe(undefined) + }) +})