Skip to content

Commit 3ba2175

Browse files
committed
more docu, new functionality: GH_TOKEN is automatically added to repo URL
1 parent 6f0b8f5 commit 3ba2175

File tree

6 files changed

+84
-25
lines changed

6 files changed

+84
-25
lines changed

README.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ This command has the following prerequisites:
3131
- Angular project created via [Angular CLI](https://github.com/angular/angular-cli)
3232

3333

34-
## 🚀 Quick-start <a name="quickstart"></a>
34+
## 🚀 Quick-start (local development) <a name="quickstart-local"></a>
3535

3636
This quickstart assumes that you are starting from scratch.
3737
If you alreay have an existing Angular project on GitHub, skip step 1 and 2.
@@ -41,8 +41,8 @@ If you alreay have an existing Angular project on GitHub, skip step 1 and 2.
4141

4242
```sh
4343
npm install -g @angular/cli@next
44-
ng new hello-world --defaults
45-
cd hello-world
44+
ng new your-angular-project --defaults
45+
cd your-angular-project
4646
```
4747

4848
2. By default the Angular CLI initializes a git repository for you.
@@ -68,13 +68,34 @@ If you alreay have an existing Angular project on GitHub, skip step 1 and 2.
6868
Your project will be automatically build in production mode.
6969

7070
```sh
71-
ng run hello-world:deploy
71+
ng run your-angular-project:deploy
7272
```
7373

7474
5. Your project should be available at `http(s)://<username>.github.io/<projectname>`.
7575
Learn more about GitHub pages on the [official website](https://pages.github.com/).
7676

7777

78+
## 🚀 Continuous Delivery <a name="continuous-delivery"></a>
79+
80+
If you run this command on a CI/CD environment, the deployment will most likely not work out of the box.
81+
For security reasons, those environments usually have read-only privileges.
82+
Therefore you should take a look at [Github tokens](https://help.github.com/articles/creating-an-access-token-for-command-line-use/).
83+
In short: a Github token replaces username and password and can be invalidated at any time.
84+
85+
All you need to do is set an environment variable called `GH_TOKEN` in our CI/CD environment.
86+
You should also set the URL to the repository using the `--repo` option.
87+
The URL must use the HTTPS scheme.
88+
89+
```sh
90+
ng run your-angular-project:deploy --repo=https://github.com/<username>/<repositoryname>.git --name="Your Git Username" [email protected]
91+
```
92+
93+
(replace `<username>` and `<repositoryname>` with your username from GitHub and the name of your repository)
94+
95+
> Please __do NOT disable the silent mode__ if you have any credentials in the repository URL!
96+
> You have to treat the GH_TOKEN as secure as a password!
97+
98+
7899
## Options
79100

80101
- `--base-href` - specifies the base url for the application being built. Same as `ng build --base-href=XXX`.

deploy/actions.spec.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ describe('Deploy Angular apps', () => {
1414
const spy = spyOn(context, 'scheduleTarget').and.callThrough();
1515
await deploy(mockEngine, context, 'host', {});
1616

17-
expect(spy).toHaveBeenCalled();
1817
expect(spy).toHaveBeenCalledWith({
1918
target: 'build',
2019
configuration: 'production',
@@ -24,11 +23,23 @@ describe('Deploy Angular apps', () => {
2423
);
2524
});
2625

26+
it('should invoke the builder with the baseHref', async () => {
27+
const spy = spyOn(context, 'scheduleTarget').and.callThrough();
28+
await deploy(mockEngine, context, 'host', { baseHref: '/folder'});
29+
30+
expect(spy).toHaveBeenCalledWith({
31+
target: 'build',
32+
configuration: 'production',
33+
project: PROJECT
34+
},
35+
{ baseHref: '/folder' }
36+
);
37+
});
38+
2739
it('should invoke engine.run', async () => {
2840
const spy = spyOn(mockEngine, 'run').and.callThrough();
2941
await deploy(mockEngine, context, 'host', {});
3042

31-
expect(spy).toHaveBeenCalled();
3243
expect(spy).toHaveBeenCalledWith('host', {}, context.logger);
3344
});
3445

deploy/actions.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
import { BuilderContext } from '@angular-devkit/architect';
2-
import { Schema as RealDeployOptions } from './schema';
2+
import { Schema } from './schema';
33
import { json, logging } from '@angular-devkit/core';
44

5-
type DeployOptions = RealDeployOptions & json.JsonObject;
65

76
export default async function deploy(
8-
engine: { run: (dir: string, options: RealDeployOptions, logger: logging.LoggerApi) => Promise<void> },
7+
engine: { run: (dir: string, options: Schema, logger: logging.LoggerApi) => Promise<void> },
98
context: BuilderContext,
109
projectRoot: string,
11-
options: DeployOptions
10+
options: Schema
1211
) {
1312

1413
if (!context.target) {
1514
throw new Error('Cannot execute the build target');
1615
}
1716

1817
const configuration = options.configuration ? options.configuration : 'production'
18+
const overrides = {
19+
...(options.baseHref && {baseHref: options.baseHref})
20+
};
1921

2022
context.logger.info(`📦 Building "${ context.target.project }". Configuration: "${ configuration }".${ options.baseHref ? ' Your base-href: "' + options.baseHref + '"' : '' }`);
2123

2224
const build = await context.scheduleTarget({
2325
target: 'build',
2426
project: context.target.project,
2527
configuration
26-
}, {
27-
baseHref: options.baseHref ? options.baseHref : null
28-
});
28+
}, overrides as json.JsonObject);
2929
await build.result;
3030

3131
await engine.run(

deploy/builder.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
22
import { NodeJsSyncHost } from '@angular-devkit/core/node';
33
import { experimental, join, normalize, json } from '@angular-devkit/core';
4-
import { Schema as RealDeployOptions } from './schema';
4+
import { Schema } from './schema';
55

66
import deploy from './actions';
77
import * as engine from '../engine/engine';
88

9-
type DeployOptions = RealDeployOptions & json.JsonObject;
10-
119
// Call the createBuilder() function to create a builder. This mirrors
1210
// createJobHandler() but add typings specific to Architect Builders.
1311
export default createBuilder<any>(
1412
async (
15-
options: DeployOptions,
13+
options: Schema,
1614
context: BuilderContext
1715
): Promise<BuilderOutput> => {
1816
// The project root is added to a BuilderContext.

engine/engine.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as engine from './engine';
2+
import { NullLogger } from '@angular-devkit/core/src/logger';
3+
4+
describe('engine', () => {
5+
describe('prepareOptions', () => {
6+
7+
it('should replace the string GH_TOKEN in the repo url (for backwards compatibility)', () => {
8+
const options = {
9+
repo: 'https://[email protected]/organisation/your-repo.git'
10+
}
11+
process.env.GH_TOKEN = 'XXX';
12+
const finalOptions = engine.prepareOptions(options, new NullLogger())
13+
14+
expect(finalOptions.repo).toBe('https://[email protected]/organisation/your-repo.git');
15+
});
16+
17+
it('should add a GH_TOKEN to the repo url', () => {
18+
const options = {
19+
repo: 'https://github.com/organisation/your-repo.git'
20+
}
21+
process.env.GH_TOKEN = 'XXX';
22+
const finalOptions = engine.prepareOptions(options, new NullLogger())
23+
24+
expect(finalOptions.repo).toBe('https://[email protected]/organisation/your-repo.git');
25+
});
26+
});
27+
});

engine/engine.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import * as fse from 'fs-extra';
44
import { logging } from '@angular-devkit/core';
55
import { defaults } from './defaults';
66
import { GHPages } from '../interfaces';
7-
import { Schema as RealDeployOptions } from '../deploy/schema';
7+
import { Schema } from '../deploy/schema';
88

99
const ghpages = require('gh-pages');
1010

11-
export async function run(dir: string, options: RealDeployOptions, logger: logging.LoggerApi) {
11+
export async function run(dir: string, options: Schema, logger: logging.LoggerApi) {
1212

1313
options = prepareOptions(options, logger);
1414

@@ -35,7 +35,7 @@ export async function run(dir: string, options: RealDeployOptions, logger: loggi
3535
};
3636

3737

38-
function prepareOptions(origOptions: RealDeployOptions, logger: logging.LoggerApi) {
38+
export function prepareOptions(origOptions: Schema, logger: logging.LoggerApi) {
3939

4040
const options = {
4141
...defaults,
@@ -76,11 +76,13 @@ function prepareOptions(origOptions: RealDeployOptions, logger: logging.LoggerAp
7676
'CircleCI build: ' + process.env.CIRCLE_BUILD_URL;
7777
}
7878

79-
// for your convenience - here you can hack credentials into the repository URL
8079
if (process.env.GH_TOKEN && options.repo) {
80+
options.repo = options.repo.replace('http://github.com/', 'http://[email protected]/');
81+
options.repo = options.repo.replace('https://github.com/', 'https://[email protected]/');
8182
options.repo = options.repo.replace('GH_TOKEN', process.env.GH_TOKEN);
8283
}
8384

85+
8486
return options;
8587
}
8688

@@ -90,7 +92,7 @@ async function checkIfDistFolderExists(dir: string) {
9092
}
9193
}
9294

93-
async function createNotFoundPage(dir: string, options: RealDeployOptions, logger: logging.LoggerApi) {
95+
async function createNotFoundPage(dir: string, options: Schema, logger: logging.LoggerApi) {
9496

9597
if (options.dryRun) {
9698
logger.info('Dry-run / SKIPPED: copying of index.html to 404.html');
@@ -115,7 +117,7 @@ async function createNotFoundPage(dir: string, options: RealDeployOptions, logge
115117
}
116118
}
117119

118-
async function createCnameFile(dir: string, options: RealDeployOptions, logger: logging.LoggerApi) {
120+
async function createCnameFile(dir: string, options: Schema, logger: logging.LoggerApi) {
119121

120122
if (!options.cname) {
121123
return;
@@ -137,7 +139,7 @@ async function createCnameFile(dir: string, options: RealDeployOptions, logger:
137139
}
138140
}
139141

140-
async function publishViaGhPages(ghPages: GHPages, dir: string, options: RealDeployOptions, logger: logging.LoggerApi) {
142+
async function publishViaGhPages(ghPages: GHPages, dir: string, options: Schema, logger: logging.LoggerApi) {
141143
if (options.dryRun) {
142144
logger.info(`Dry-run / SKIPPED: publishing folder "${ dir }" with the following options: ` + JSON.stringify({
143145
dir: dir,
@@ -148,7 +150,7 @@ async function publishViaGhPages(ghPages: GHPages, dir: string, options: RealDep
148150
silent: options.silent || 'falsy: logging is in silent mode by default',
149151
dotfiles: options.dotfiles || 'falsy: dotfiles are included by default',
150152
cname: options.cname || 'falsy: no CNAME file will be created',
151-
}) as any);
153+
}, null, ' '));
152154
return;
153155
}
154156

0 commit comments

Comments
 (0)