Skip to content

Commit 1625317

Browse files
committed
feat(deploy): add github pages deploy
1 parent cc578db commit 1625317

File tree

6 files changed

+244
-11
lines changed

6 files changed

+244
-11
lines changed

README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ with two sub-routes. The file structure will be as follows:
122122
...
123123
```
124124

125-
By default the cli will add the import statements for HeroList and HeroDetail to
125+
By default the cli will add the import statements for HeroList and HeroDetail to
126126
`hero-root.component.ts`:
127127

128128
```
@@ -147,7 +147,7 @@ export const CliRouteConfig = [
147147
Visiting `http://localhost:4200/hero` will show the hero list.
148148

149149

150-
There is an optional flag for `skip-router-generation` which will not add the route to the `CliRouteConfig` for the application.
150+
There is an optional flag for `skip-router-generation` which will not add the route to the `CliRouteConfig` for the application.
151151

152152
### Creating a build
153153

@@ -165,7 +165,7 @@ ng test
165165

166166
Tests will execute after a build is executed via [Karma](http://karma-runner.github.io/0.13/index.html)
167167

168-
If run with the watch argument `--watch` (shorthand `-w`) builds will run when source files have changed
168+
If run with the watch argument `--watch` (shorthand `-w`) builds will run when source files have changed
169169
and tests will run after each successful build
170170

171171

@@ -182,16 +182,24 @@ End-to-end tests are ran via [Protractor](https://angular.github.io/protractor/)
182182

183183
### Deploying the app via GitHub Pages
184184

185-
The CLI currently comes bundled with [angular-cli-github-pages addon](https://github.com/IgorMinar/angular-cli-github-pages).
186-
187-
This means that you can deploy your apps quickly via:
185+
You can deploy your apps quickly via:
188186

189187
```
190-
git commit -a -m "final tweaks before deployment - what could go wrong?"
191-
ng github-pages:deploy
188+
git checkout master
189+
ng github-pages:deploy --message "Optional commit message"
192190
```
193191

194-
Checkout [angular-cli-github-pages addon](https://github.com/IgorMinar/angular-cli-github-pages) docs for more info.
192+
This will do the following:
193+
194+
- creates GitHub repo for the current project if one doesn't exist
195+
- rebuilds the app at the current `HEAD`
196+
- creates a local `gh-pages` branch if one doesn't exist
197+
- moves your app to the `gh-pages` branch and creates a commit
198+
- pushes the `gh-pages` branch to github
199+
- returns back to the original `HEAD`
200+
201+
This functionality relies on ssh authentication for all git operations that communicate with github.com.
202+
To simplify the authentication, be sure to [setup your ssh keys](https://help.github.com/articles/generating-ssh-keys/).
195203

196204
### Linting and formatting code
197205

addon/ng2/blueprints/ng2/files/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
},
2323
"devDependencies": {
2424
"angular-cli": "0.0.*",
25-
"angular-cli-github-pages": "^0.2.0",
2625
"clang-format": "^1.0.35",
2726
"codelyzer": "0.0.12",
2827
"ember-cli-inject-live-reload": "^1.4.0",
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import * as Command from 'ember-cli/lib/models/command';
2+
import * as SilentError from 'silent-error';
3+
import { exec } from 'child_process';
4+
import * as Promise from 'ember-cli/lib/ext/promise';
5+
import * as chalk from 'chalk';
6+
import * as fs from 'fs';
7+
import * as fse from 'fs-extra';
8+
import * as path from 'path';
9+
import * as BuildTask from 'ember-cli/lib/tasks/build';
10+
import * as win from 'ember-cli/lib/utilities/windows-admin';
11+
import * as CreateGithubRepo from '../tasks/create-github-repo';
12+
13+
const fsReadFile = Promise.denodeify(fs.readFile);
14+
const fsWriteFile = Promise.denodeify(fs.writeFile);
15+
const fsReadDir = Promise.denodeify(fs.readdir);
16+
const fsCopy = Promise.denodeify(fse.copy);
17+
const execPromise = Promise.denodeify(exec);
18+
19+
module.exports = Command.extend({
20+
name: 'github-pages:deploy',
21+
aliases: ['gh-pages:deploy'],
22+
description: 'Build the test app for production, commit it into a git branch, setup GitHub repo and push to it',
23+
works: 'insideProject',
24+
25+
availableOptions: [
26+
{
27+
name: 'message',
28+
type: String,
29+
default: 'new gh-pages version',
30+
description: 'The commit message to include with the build, must be wrapped in quotes.'
31+
}, {
32+
name: 'environment',
33+
type: String,
34+
default: 'production',
35+
description: 'The Angular environment to create a build for'
36+
}, {
37+
name: 'branch',
38+
type: String,
39+
default: 'gh-pages',
40+
description: 'The git branch to push your pages to'
41+
}],
42+
43+
run: function(options, rawArgs) {
44+
var ui = this.ui;
45+
var root = this.project.root;
46+
var execOptions = {
47+
cwd: root
48+
};
49+
var projectName = this.project.pkg.name;
50+
51+
let currentBranchName;
52+
53+
var buildTask = new BuildTask({
54+
ui: this.ui,
55+
analytics: this.analytics,
56+
project: this.project
57+
});
58+
59+
var buildOptions = {
60+
environment: options.environment,
61+
outputPath: 'dist/'
62+
};
63+
64+
var createGithubRepoTask = new CreateGithubRepo({
65+
ui: this.ui,
66+
analytics: this.analytics,
67+
project: this.project
68+
});
69+
70+
var createGithubRepoOptions = {
71+
projectName
72+
};
73+
74+
return checkForPendingChanges()
75+
.then(() => win.checkWindowsElevation(this.ui))
76+
.then(() => buildTask.run(buildOptions))
77+
.then(saveStartingBranchName)
78+
.then(createGitHubRepoIfNeeded)
79+
.then(checkoutGhPages)
80+
.then(copyFiles)
81+
.then(updateBaseHref)
82+
.then(addAndCommit)
83+
.then(returnStartingBranch)
84+
.then(pushToGitRepo)
85+
.then(printProjectUrl);
86+
87+
function checkForPendingChanges() {
88+
return execPromise('git status --porcelain')
89+
.then(stdout => {
90+
if (/\w+/m.test(stdout)) {
91+
let msg = 'Uncommitted file changes found! Please commit all changes before deploying.';
92+
return Promise.reject(new SilentError(msg));
93+
}
94+
});
95+
}
96+
97+
function saveStartingBranchName() {
98+
return execPromise('git rev-parse --abbrev-ref HEAD')
99+
.then((stdout) => currentBranchName = stdout);
100+
}
101+
102+
function createGitHubRepoIfNeeded() {
103+
return execPromise('git remote -v')
104+
.then(function(stdout) {
105+
if (!/origin\s+git@github\.com/m.test(stdout)) {
106+
return createGithubRepoTask.run(createGithubRepoOptions)
107+
});
108+
}
109+
110+
function checkoutGhPages() {
111+
return execPromise(`git checkout ${options.branch}`)
112+
.catch(createGhPagesBranch)
113+
}
114+
115+
function createGhPagesBranch() {
116+
return execPromise(`git checkout --orphan ${options.branch}`)
117+
.then(() => execPromise('git rm --cached -r .', execOptions))
118+
.then(() => execPromise('git add .gitignore', execOptions))
119+
.then(() => execPromise('git clean -f -d', execOptions))
120+
.then(() => execPromise('git commit -m \"initial gh-pages commit\"'));
121+
}
122+
123+
function copyFiles() {
124+
return fsReadDir('dist')
125+
.then((files) => Promise.all(files.map((file) => fsCopy(path.join('dist', file), path.join('.', file)))))
126+
}
127+
128+
function updateBaseHref() {
129+
let indexHtml = path.join(root, 'index.html');
130+
return fsReadFile(indexHtml, 'utf8')
131+
.then((data) => data.replace(/<base href="\/">/g, `<base href="/${projectName}/>"`))
132+
.then((data) => fsWriteFile(indexHtml, data, 'utf8'));
133+
}
134+
135+
function addAndCommit() {
136+
return execPromise('git add .', execOptions)
137+
.then(() => execPromise(`git commit -m "${options.message}"`))
138+
.catch(() => Promise.reject(new SilentError('No changes found. Deployment skipped.')));
139+
}
140+
141+
function returnStartingBranch() {
142+
return execPromise(`git checkout ${currentBranchName}`);
143+
}
144+
145+
function pushToGitRepo(committed) {
146+
return execPromise(`git push origin ${options.branch}`);
147+
}
148+
149+
function printProjectUrl() {
150+
return execPromise('git remote -v')
151+
.then((stdout) => {
152+
let userName = stdout.match(/origin\s+git@github\.com\:([^\/]+)/m)[1].toLowerCase();
153+
ui.writeLine(chalk.green(`Deployed! Visit https://${userName}.github.io/${projectName}/`));
154+
ui.writeLine('Github pages might take a few minutes to show the deployed site.');
155+
})
156+
}
157+
}
158+
});

addon/ng2/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ module.exports = {
1414
'format': require('./commands/format'),
1515
'version': require('./commands/version'),
1616
'completion': require('./commands/completion'),
17-
'doc': require('./commands/doc')
17+
'doc': require('./commands/doc'),
18+
'github-pages-deploy': require('./commands/github-pages-deploy')
1819
};
1920
}
2021
};

addon/ng2/tasks/create-github-repo.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import * as Promise from 'ember-cli/lib/ext/promise';
2+
import * as Task from 'ember-cli/lib/models/task';
3+
import { exec } from 'child_process';
4+
import * as https from 'https';
5+
6+
const execPromise = Promise.denodeify(exec);
7+
8+
module.exports = Task.extend({
9+
run: function(commandOptions) {
10+
var ui = this.ui;
11+
12+
ui.writeLine("\nIn order to deploy this project via GitHub Pages, we must first create a repository for it.");
13+
ui.writeLine("It's safer to use a token than to use a password, so you will need to create one.\n");
14+
ui.writeLine("Go to the following page and click 'Generate new token'.");
15+
ui.writeLine("https://github.com/settings/tokens\n");
16+
ui.writeLine("Choose 'public_repo' as scope and then click 'Generate token'.\n");
17+
18+
return new Promise(function(resolve, reject) {
19+
ui.prompt([
20+
{
21+
name: 'ghToken',
22+
type: 'input',
23+
message: 'Please enter GitHub token you just created (used only once to create the repo):',
24+
validate: function(token) {
25+
return /.+/.test(token);
26+
}
27+
}, {
28+
name: 'ghUserName',
29+
type: 'input',
30+
message: 'and your GitHub user name:',
31+
validate: function(userName) {
32+
return /\w+/.test(userName);
33+
}
34+
}], function(answers) {
35+
36+
var postData = JSON.stringify({
37+
'name': commandOptions.projectName
38+
});
39+
40+
var req = https.request({
41+
hostname: 'api.github.com',
42+
port: 443,
43+
path: '/user/repos',
44+
method: 'POST',
45+
headers: {
46+
'Authorization': `token ${answers.ghToken}`,
47+
'Content-Type': 'application/json',
48+
'Content-Length': postData.length,
49+
'User-Agent': 'angular-cli-github-pages'
50+
}
51+
});
52+
53+
req.on('response', function(response) {
54+
if (response.statusCode === 201) {
55+
resolve(execPromise(`git remote add origin [email protected]:${answers.ghUserName}/${commandOptions.projectName}.git`))
56+
} else {
57+
reject(`Failed to create GitHub repo. Error: ${response.statusCode} ${response.statusMessage}`);
58+
}
59+
});
60+
61+
req.write(postData);
62+
req.end();
63+
});
64+
});
65+
}
66+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"ember-cli": "2.4.2",
4141
"exit": "^0.1.2",
4242
"fs-extra": "^0.26.6",
43+
"inquirer": "^0.10.1",
4344
"leek": "0.0.21",
4445
"lodash": "^4.6.1",
4546
"multidep": "^2.0.0",

0 commit comments

Comments
 (0)