Skip to content

Commit

Permalink
feat(deploy:github-pages): support usage of gh-token for deployment f…
Browse files Browse the repository at this point in the history
…rom external env (#3121)

In order to being able to push to github from CI (Travis for example), we should be able to provide a token to the cli, as `--gh-token=XYZ`. This modification allow to use this token in the destination url. This also allow user to push to another github repository if it uses a different `--gh-username` from the one used for the checkout.
  • Loading branch information
davinkevin authored and hansl committed Jan 12, 2017
1 parent da1c197 commit 3c82b77
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 84 deletions.
78 changes: 53 additions & 25 deletions packages/angular-cli/commands/github-pages-deploy.run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,20 @@ export default function githubPagesDeployRun(options: GithubPagesDeployOptions,

function createGitHubRepoIfNeeded() {
return execPromise('git remote -v')
.then(function (stdout) {
if (!/origin\s+(https:\/\/|git@)github\.com/m.test(stdout)) {
return createGithubRepoTask.run(createGithubRepoOptions)
.then(() => {
// only push starting branch if it's not the destinationBranch
// this happens commonly when using github user pages, since
// they require the destination branch to be 'master'
if (destinationBranch !== initialBranch) {
execPromise(`git push -u origin ${initialBranch}`);
}
});
}
});
.then(function(stdout) {
if (!/origin\s+(https:\/\/|git@)github\.com/m.test(stdout)) {
return createGithubRepoTask.run(createGithubRepoOptions)
.then(() => generateRemoteUrl())
.then((upstream: string) => {
// only push starting branch if it's not the destinationBranch
// this happens commonly when using github user pages, since
// they require the destination branch to be 'master'
if (destinationBranch !== initialBranch) {
execPromise(`git push -u ${upstream} ${initialBranch}`);
}
});
}
});
}

function checkoutGhPages() {
Expand Down Expand Up @@ -191,30 +192,57 @@ export default function githubPagesDeployRun(options: GithubPagesDeployOptions,
}

function pushToGitRepo() {
return execPromise(`git push origin ${ghPagesBranch}:${destinationBranch}`)
.catch((err) => returnStartingBranch()
.catch(() => Promise.reject(err)));
return generateRemoteUrl()
.then(upstream => {
return execPromise(`git push ${upstream} ${ghPagesBranch}:${destinationBranch}`);
})
.catch((err) => returnStartingBranch()
.catch(() => Promise.reject(err) ));
}

function printProjectUrl() {
return execPromise('git remote -v')
.then((stdout) => {
let match = stdout.match(/origin\s+(?:https:\/\/|git@)github\.com(?:\:|\/)([^\/]+)/m);
let userName = match[1].toLowerCase();
let url = `https://${userName}.github.io/${options.userPage ? '' : (baseHref + '/')}`;
ui.writeLine(chalk.green(`Deployed! Visit ${url}`));
ui.writeLine('Github pages might take a few minutes to show the deployed site.');
});
return getUsernameFromGitOrigin()
.then((userName) => {
let url = `https://${userName}.github.io/${options.userPage ? '' : (baseHref + '/')}`;
ui.writeLine(chalk.green(`Deployed! Visit ${url}`));
ui.writeLine('Github pages might take a few minutes to show the deployed site.');
});
}

function failGracefully(error: Error) {
if (error && (/git clean/.test(error.message) || /Permission denied/.test(error.message))) {
ui.writeLine(error.message);
let msg = 'There was a permissions error during git file operations, ' +
'please close any open project files/folders and try again.';
'please close any open project files/folders and try again.';
msg += `\nYou might also need to return to the ${initialBranch} branch manually.`;
return Promise.reject(new SilentError(msg.concat(branchErrMsg)));
} else {
return Promise.reject(error);
}
}

function generateRemoteUrl(): Promise<String> {
if (createGithubRepoOptions.ghToken && createGithubRepoOptions.ghUsername) {
return Promise.resolve(`https://${createGithubRepoOptions.ghToken}@github.com/` +
`${createGithubRepoOptions.ghUsername}/${createGithubRepoOptions.projectName}.git`);
}

if (createGithubRepoOptions.ghToken && !createGithubRepoOptions.ghUsername) {
return getUsernameFromGitOrigin()
.then(username => {
return Promise.resolve(`https://${createGithubRepoOptions.ghToken}@github.com/` +
`${username}/${createGithubRepoOptions.projectName}.git`);
});
}

return Promise.resolve('origin');
}

function getUsernameFromGitOrigin(): Promise<String> {
return execPromise('git remote -v')
.then((stdout) => {
let match = stdout.match(/origin\s+(?:https:\/\/|git@)github\.com(?:\:|\/)([^\/]+)/m);
return match[1].toLowerCase();
});
}
}
114 changes: 57 additions & 57 deletions packages/angular-cli/commands/github-pages-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,71 @@ const Command = require('../ember-cli/lib/models/command');
import { oneLine } from 'common-tags';

export interface GithubPagesDeployOptions {
message?: string;
target?: string;
environment?: string;
userPage?: boolean;
skipBuild?: boolean;
ghToken?: string;
ghUsername?: string;
baseHref?: string;
message?: string;
target?: string;
environment?: string;
userPage?: boolean;
skipBuild?: boolean;
ghToken?: string;
ghUsername?: string;
baseHref?: string;
}

const githubPagesDeployCommand = Command.extend({
name: 'github-pages:deploy',
aliases: ['gh-pages:deploy'],
description: oneLine`
name: 'github-pages:deploy',
aliases: ['gh-pages:deploy'],
description: oneLine`
Build the test app for production, commit it into a git branch,
setup GitHub repo and push to it
`,
works: 'insideProject',
works: 'insideProject',

availableOptions: [
{
name: 'message',
type: String,
default: 'new gh-pages version',
description: 'The commit message to include with the build, must be wrapped in quotes.'
}, {
name: 'target',
type: String,
default: 'production',
aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }]
}, {
name: 'environment',
type: String,
default: '',
aliases: ['e']
}, {
name: 'user-page',
type: Boolean,
default: false,
description: 'Deploy as a user/org page'
}, {
name: 'skip-build',
type: Boolean,
default: false,
description: 'Skip building the project before deploying'
}, {
name: 'gh-token',
type: String,
default: '',
description: 'Github token'
}, {
name: 'gh-username',
type: String,
default: '',
description: 'Github username'
}, {
name: 'base-href',
type: String,
default: null,
aliases: ['bh']
}],
availableOptions: [
{
name: 'message',
type: String,
default: 'new gh-pages version',
description: 'The commit message to include with the build, must be wrapped in quotes.'
}, {
name: 'target',
type: String,
default: 'production',
aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }]
}, {
name: 'environment',
type: String,
default: '',
aliases: ['e']
}, {
name: 'user-page',
type: Boolean,
default: false,
description: 'Deploy as a user/org page'
}, {
name: 'skip-build',
type: Boolean,
default: false,
description: 'Skip building the project before deploying'
}, {
name: 'gh-token',
type: String,
default: '',
description: 'Github token'
}, {
name: 'gh-username',
type: String,
default: '',
description: 'Github username'
}, {
name: 'base-href',
type: String,
default: null,
aliases: ['bh']
}],

run: function(options: GithubPagesDeployOptions, rawArgs: string[]) {
return require('./github-pages-deploy.run').default.call(this, options, rawArgs);
}
run: function(options: GithubPagesDeployOptions, rawArgs: string[]) {
return require('./github-pages-deploy.run').default.call(this, options, rawArgs);
}
});


Expand Down
42 changes: 40 additions & 2 deletions tests/acceptance/github-pages-deploy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,44 @@ describe('Acceptance: ng github-pages:deploy', function() {
return ng(['github-pages:deploy', '--skip-build']);
});

it('should deploy with token and username', function () {
let token = 'token',
username = 'bar';

execStub.addExecSuccess('git status --porcelain')
.addExecSuccess('git rev-parse --abbrev-ref HEAD', initialBranch)
.addExecSuccess('git remote -v', remote)
.addExecSuccess(`git checkout ${ghPagesBranch}`)
.addExecSuccess('git ls-files')
.addExecSuccess('git rm -r ')
.addExecSuccess('git add .')
.addExecSuccess(`git commit -m "${message}"`)
.addExecSuccess(`git checkout ${initialBranch}`)
.addExecSuccess(`git push https://${token}@github.com/${username}/${project}.git ${ghPagesBranch}:${ghPagesBranch}`)
.addExecSuccess('git remote -v', remote);

return ng(['github-pages:deploy', '--skip-build', `--gh-token=${token}`, `--gh-username=${username}`]);
})

it('should deploy with token only', function () {
let token = 'token';

execStub.addExecSuccess('git status --porcelain')
.addExecSuccess('git rev-parse --abbrev-ref HEAD', initialBranch)
.addExecSuccess('git remote -v', remote)
.addExecSuccess(`git checkout ${ghPagesBranch}`)
.addExecSuccess('git ls-files')
.addExecSuccess('git rm -r ')
.addExecSuccess('git add .')
.addExecSuccess(`git commit -m "${message}"`)
.addExecSuccess(`git checkout ${initialBranch}`)
.addExecSuccess('git remote -v', remote)
.addExecSuccess(`git push https://${token}@github.com/username/${project}.git ${ghPagesBranch}:${ghPagesBranch}`)
.addExecSuccess('git remote -v', remote);

return ng(['github-pages:deploy', '--skip-build', `--gh-token=${token}`]);
});

it('should deploy with changed defaults', function() {
let userPageBranch = 'master',
message = 'not new gh-pages version';
Expand Down Expand Up @@ -126,14 +164,14 @@ describe('Acceptance: ng github-pages:deploy', function() {
.addExecSuccess('git rev-parse --abbrev-ref HEAD', initialBranch)
.addExecSuccess('git remote -v', noRemote)
.addExecSuccess(`git remote add origin git@github.com:${username}/${project}.git`)
.addExecSuccess(`git push -u origin ${initialBranch}`)
.addExecSuccess(`git push -u https://${token}@github.com/${username}/${project}.git ${initialBranch}`)
.addExecSuccess(`git checkout ${ghPagesBranch}`)
.addExecSuccess('git ls-files')
.addExecSuccess('git rm -r ')
.addExecSuccess('git add .')
.addExecSuccess(`git commit -m "${message}"`)
.addExecSuccess(`git checkout ${initialBranch}`)
.addExecSuccess(`git push origin ${ghPagesBranch}:${ghPagesBranch}`)
.addExecSuccess(`git push https://${token}@github.com/${username}/${project}.git ${ghPagesBranch}:${ghPagesBranch}`)
.addExecSuccess('git remote -v', remote);

var httpsStub = sinon.stub(https, 'request', httpsRequestStubFunc);
Expand Down

0 comments on commit 3c82b77

Please sign in to comment.