feat: add deploy (formerly publish-gh-pages) command (#979)

This commit is contained in:
Endilie Yacop Sucipto 2018-09-25 20:26:39 +08:00 committed by GitHub
parent 9b6ec5b9bb
commit 604f02edeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 218 additions and 2 deletions

View file

@ -4,7 +4,7 @@ const chalk = require('chalk');
const semver = require('semver'); const semver = require('semver');
const path = require('path'); const path = require('path');
const program = require('commander'); const program = require('commander');
const {build, eject, init, start} = require('../lib'); const {build, eject, init, deploy, start} = require('../lib');
const requiredVersion = require('../package.json').engines.node; const requiredVersion = require('../package.json').engines.node;
if (!semver.satisfies(process.version, requiredVersion)) { if (!semver.satisfies(process.version, requiredVersion)) {
@ -56,6 +56,13 @@ program
wrapCommand(init)(path.resolve(projectDir)); wrapCommand(init)(path.resolve(projectDir));
}); });
program
.command('deploy [siteDir]')
.description('deploy website')
.action((siteDir = '.') => {
wrapCommand(deploy)(path.resolve(siteDir));
});
program program
.command('start [siteDir]') .command('start [siteDir]')
.description('Start development server') .description('Start development server')

175
v2/lib/commands/deploy.js Normal file
View file

@ -0,0 +1,175 @@
const path = require('path');
const shell = require('shelljs');
const fs = require('fs-extra');
const build = require('./build');
const loadConfig = require('../load/config');
module.exports = async function deploy(siteDir) {
console.log('Deploy command invoked ...');
if (!shell.which('git')) {
throw new Error('Sorry, this script requires git');
}
const gitUser = process.env.GIT_USER;
if (!gitUser) {
throw new Error(`Please set the GIT_USER`);
}
// The branch that contains the latest docs changes that will be deployed
const currentBranch =
process.env.CURRENT_BRANCH ||
shell.exec('git rev-parse --abbrev-ref HEAD').stdout.trim();
const siteConfig = loadConfig(siteDir);
const organizationName =
process.env.ORGANIZATION_NAME ||
process.env.CIRCLE_PROJECT_USERNAME ||
siteConfig.organizationName;
if (!organizationName) {
throw new Error(
"Missing project organization name. Did you forget to define 'organizationName' in siteConfig.js? You may also export it via the organizationName environment variable.",
);
}
const projectName =
process.env.PROJECT_NAME ||
process.env.CIRCLE_PROJECT_REPONAME ||
siteConfig.projectName;
if (!projectName) {
throw new Error(
"Missing project name. Did you forget to define 'projectName' in siteConfig.js? You may also export it via the projectName environment variable.",
);
}
// We never deploy on pull request
const isPullRequest =
process.env.CI_PULL_REQUEST || process.env.CIRCLE_PULL_REQUEST;
if (isPullRequest) {
shell.echo('Skipping deploy on a pull request');
shell.exit(0);
}
// github.io indicates organization repos that deploy via master. All others use gh-pages.
const deploymentBranch =
projectName.indexOf('.github.io') !== -1 ? 'master' : 'gh-pages';
const githubHost =
process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com';
const useSSH = process.env.USE_SSH;
const remoteBranch = useSSH
? `git@${githubHost}:${organizationName}/${projectName}.git`
: `https://${gitUser}@${githubHost}/${organizationName}/${projectName}.git`;
// Check if this is a cross-repo publish
const currentRepoUrl = shell
.exec('git config --get remote.origin.url')
.stdout.trim();
const crossRepoPublish = !currentRepoUrl.endsWith(
`${organizationName}/${projectName}.git`,
);
// We don't allow deploying to the same branch unless it's a cross publish
if (currentBranch === deploymentBranch && !crossRepoPublish) {
throw new Error(
`Cannot deploy from a ${deploymentBranch} branch. Only to it`,
);
}
// Save the commit hash that triggers publish-gh-pages before checking out to deployment branch
const currentCommit = shell.exec('git rev-parse HEAD').stdout.trim();
// build static html files, then push to deploymentBranch branch of specified repo
build(siteDir)
.then(() => {
shell.cd(siteDir);
shell.cd('build');
if (
shell.exec(
`git clone ${remoteBranch} ${projectName}-${deploymentBranch}`,
).code !== 0
) {
throw new Error('Error: git clone failed');
}
shell.cd(`${projectName}-${deploymentBranch}`);
// If the default branch is the one we're deploying to, then we'll fail to create it.
// This is the case of a cross-repo publish, where we clone a github.io repo with a default master branch.
const defaultBranch = shell
.exec('git rev-parse --abbrev-ref HEAD')
.stdout.trim();
if (defaultBranch !== deploymentBranch) {
if (shell.exec(`git checkout origin/${deploymentBranch}`).code !== 0) {
if (
shell.exec(`git checkout --orphan ${deploymentBranch}`).code !== 0
) {
throw new Error(`Error: Git checkout ${deploymentBranch} failed`);
}
} else if (
shell.exec(`git checkout -b ${deploymentBranch}`).code +
shell.exec(
`git branch --set-upstream-to=origin/${deploymentBranch}`,
).code !==
0
) {
throw new Error(`Error: Git checkout ${deploymentBranch} failed`);
}
}
shell.exec('git rm -rf .');
shell.cd('../..');
const fromPath = path.join('build');
const toPath = path.join('build', `${projectName}-${deploymentBranch}}`);
// In github.io case, project is deployed to root. Need to not recursively
// copy the deployment-branch to be.
const excludePath = `${projectName}-${deploymentBranch}`;
// cannot use shell.cp because it doesn't support copying dotfiles and we
// need to copy directories like .circleci, for example
// https://github.com/shelljs/shelljs/issues/79
fs.copy(
fromPath,
toPath,
src => {
if (src.indexOf('.DS_Store') !== -1) {
return false;
}
if (src.indexOf(excludePath) !== -1) {
return false;
}
return true;
},
error => {
if (error) {
throw new Error(
`Error: Copying build assets failed with error '${error}'`,
);
}
shell.cd(path.join('build', `${projectName}-${deploymentBranch}`));
shell.exec('git add --all');
const commitResults = shell.exec(
`git commit -m "Deploy website" -m "Deploy website version based on ${currentCommit}"`,
);
if (shell.exec(`git push origin ${deploymentBranch}`).code !== 0) {
throw new Error('Error: Git push failed');
} else if (commitResults.code === 0) {
// The commit might return a non-zero value when site is up to date.
const websiteURL =
githubHost === 'github.com'
? `https://${organizationName}.github.io/${projectName}` // gh-pages hosted repo
: `https://${githubHost}/pages/${organizationName}/${projectName}`; // GitHub enterprise hosting.
shell.echo(`Website is live at: ${websiteURL}`);
shell.exit(0);
}
},
);
})
.catch(buildError => {
console.error(buildError);
process.exit(1);
});
};

View file

@ -2,10 +2,12 @@ const build = require('./commands/build');
const init = require('./commands/init'); const init = require('./commands/init');
const start = require('./commands/start'); const start = require('./commands/start');
const eject = require('./commands/eject'); const eject = require('./commands/eject');
const deploy = require('./commands/deploy');
module.exports = { module.exports = {
build, build,
eject, eject,
init, init,
start, start,
deploy,
}; };

View file

@ -27,6 +27,7 @@ module.exports = function loadConfig(siteDir, deleteCache = true) {
'chainWebpack', 'chainWebpack',
'docsUrl', 'docsUrl',
'customFields', 'customFields',
'githubHost',
]; ];
const missingFields = requiredFields.filter(field => !config[field]); const missingFields = requiredFields.filter(field => !config[field]);
if (missingFields && missingFields.length > 0) { if (missingFields && missingFields.length > 0) {

View file

@ -24,6 +24,7 @@
"start": "node bin/docusaurus start website", "start": "node bin/docusaurus start website",
"build": "node bin/docusaurus build website", "build": "node bin/docusaurus build website",
"eject": "node bin/docusaurus eject website", "eject": "node bin/docusaurus eject website",
"deploy": "node bin/docusaurus deploy website",
"lint": "eslint --cache \"lib/**/*.js\" \"bin/**/*.js\" \"test/**/*.js\"", "lint": "eslint --cache \"lib/**/*.js\" \"bin/**/*.js\" \"test/**/*.js\"",
"test": "jest --config test/jest.config.js" "test": "jest --config test/jest.config.js"
}, },
@ -78,6 +79,7 @@
"react-youtube": "^7.6.0", "react-youtube": "^7.6.0",
"remarkable": "^1.7.1", "remarkable": "^1.7.1",
"semver": "^5.5.0", "semver": "^5.5.0",
"shelljs": "^0.8.2",
"static-site-generator-webpack-plugin": "endiliey/static-site-generator-webpack-plugin#master", "static-site-generator-webpack-plugin": "endiliey/static-site-generator-webpack-plugin#master",
"style-loader": "^0.22.1", "style-loader": "^0.22.1",
"uglifyjs-webpack-plugin": "^1.3.0", "uglifyjs-webpack-plugin": "^1.3.0",

View file

@ -2901,6 +2901,17 @@ glob-to-regexp@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
glob@^7.0.0:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
version "7.1.2" version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@ -3321,6 +3332,10 @@ inquirer@3.3.0, inquirer@^3.0.6:
strip-ansi "^4.0.0" strip-ansi "^4.0.0"
through "^2.3.6" through "^2.3.6"
interpret@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@ -5719,6 +5734,12 @@ realpath-native@^1.0.0:
dependencies: dependencies:
util.promisify "^1.0.0" util.promisify "^1.0.0"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
dependencies:
resolve "^1.1.6"
recursive-readdir@2.2.1: recursive-readdir@2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
@ -5949,7 +5970,7 @@ resolve@1.1.7:
version "1.1.7" version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.5.0, resolve@^1.6.0: resolve@^1.1.6, resolve@^1.5.0, resolve@^1.6.0:
version "1.8.1" version "1.8.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
dependencies: dependencies:
@ -6133,6 +6154,14 @@ shell-quote@1.6.1:
array-reduce "~0.0.0" array-reduce "~0.0.0"
jsonify "~0.0.0" jsonify "~0.0.0"
shelljs@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.2.tgz#345b7df7763f4c2340d584abb532c5f752ca9e35"
dependencies:
glob "^7.0.0"
interpret "^1.0.0"
rechoir "^0.6.2"
shellwords@^0.1.1: shellwords@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"