Skip to content

Commit

Permalink
feat(publish): add --update-bot option
Browse files Browse the repository at this point in the history
  • Loading branch information
tripodsan committed Apr 24, 2019
1 parent d069e9e commit 4be9093
Show file tree
Hide file tree
Showing 9 changed files with 771 additions and 24 deletions.
39 changes: 36 additions & 3 deletions src/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

const yargsOpenwhisk = require('./yargs-openwhisk.js');
const yargsFastly = require('./yargs-fastly.js');
const yargsGithub = require('./yargs-github.js');
const { makeLogger } = require('./log-common.js');

module.exports = function strain() {
Expand All @@ -28,6 +29,7 @@ module.exports = function strain() {
builder: (yargs) => {
yargsOpenwhisk(yargs);
yargsFastly(yargs);
yargsGithub(yargs);
yargs
.option('dry-run', {
alias: 'dryRun',
Expand All @@ -46,6 +48,17 @@ module.exports = function strain() {
type: 'string',
default: 'https://adobeioruntime.net/api/v1/web/helix/default/publish',
})
.option('api-config-purge', {
alias: 'apiConfigPurge',
describe: 'API URL for helix bot config service',
type: 'string',
default: 'https://app.project-helix.io/config/purge',
})
.option('update-bot-config', {
alias: 'updateBotConfig',
describe: 'Update the helix bot configuration on the affected content repositories.',
type: 'boolean',
})
.demandOption(
'fastly-auth',
'Authentication is required. You can pass the key via the HLX_FASTLY_AUTH environment variable, too',
Expand All @@ -54,8 +67,20 @@ module.exports = function strain() {
'fastly-namespace',
'Fastly Service ID is required',
)
.check((args) => {
if (args.githubToken && args.updateBotConfig === undefined) {
// eslint-disable-next-line no-param-reassign
args.updateBotConfig = true;
} else if (args.updateBotConfig && !args.githubToken) {
return new Error('Github token is required in order to update bot config.\n'
+ 'Provide one via --github-token or via the HLX_GITHUB_TOKEN environment variable.\n'
+ 'You can use `hlx auth` to automatically obtain a new token.');
}
return true;
})
.group(['wsk-auth', 'wsk-namespace', 'fastly-auth', 'fastly-namespace'], 'Deployment Options')
.group(['wsk-host', 'dry-run'], 'Advanced Options')
.group(['github-token', 'update-bot-config'], 'Helix Bot Options')
.help();
},
handler: async (argv) => {
Expand All @@ -69,15 +94,23 @@ module.exports = function strain() {
executor = executor || new PublishCommand(makeLogger(argv));
}

await executor
const cmd = executor
.withWskAuth(argv.wskAuth)
.withWskHost(argv.wskHost)
.withWskNamespace(argv.wskNamespace)
.withFastlyNamespace(argv.fastlyNamespace)
.withFastlyAuth(argv.fastlyAuth)
.withDryRun(argv.dryRun)
.withPublishAPI(argv.apiPublish)
.run();
.withPublishAPI(argv.apiPublish);

if (argv.remote) {
// only support updating the bot config for remote publish
cmd
.withGithubToken(argv.githubToken)
.withUpdateBotConfig(argv.updateBotConfig)
.withConfigPurgeAPI(argv.apiConfigPurge);
}
await cmd.run();
},

};
Expand Down
167 changes: 156 additions & 11 deletions src/remotepublish.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class RemotePublishCommand extends AbstractCommand {
this._fastly_auth = null;
this._dryRun = false;
this._publishAPI = 'https://adobeioruntime.net/api/v1/web/helix/default/publish';
this._githubToken = '';
this._updateBotConfig = false;
this._configPurgeAPI = 'https://app.project-helix.io/config/purge';
}

tick(ticks = 1, message, name) {
Expand All @@ -49,16 +52,14 @@ class RemotePublishCommand extends AbstractCommand {
}

progressBar() {
if (this._bar) {
return this._bar;
if (!this._bar) {
this._bar = new ProgressBar('Publishing [:bar] :action :etas', {
total: 24 + (this._updateBotConfig ? 2 : 0),
width: 50,
renderThrottle: 1,
stream: process.stdout,
});
}
this._bar = new ProgressBar('Publishing [:bar] :action :etas', {
total: 23,
width: 50,
renderThrottle: 1,
stream: process.stdout,
});

return this._bar;
}

Expand Down Expand Up @@ -97,6 +98,21 @@ class RemotePublishCommand extends AbstractCommand {
return this;
}

withGithubToken(value) {
this._githubToken = value;
return this;
}

withUpdateBotConfig(value) {
this._updateBotConfig = value;
return this;
}

withConfigPurgeAPI(value) {
this._configPurgeAPI = value;
return this;
}

showNextStep(dryrun) {
this.progressBar().terminate();
if (dryrun) {
Expand Down Expand Up @@ -154,6 +170,7 @@ class RemotePublishCommand extends AbstractCommand {
}

servicePublish() {
this.tick(1, 'preparing service config for Helix', true);
return request.post(this._publishAPI, {
json: true,
body: {
Expand All @@ -163,10 +180,10 @@ class RemotePublishCommand extends AbstractCommand {
version: this._version,
},
}).then(() => {
this.tick(10, 'set service config up for Helix', true);
this.tick(9, 'set service config up for Helix', true);
return true;
}).catch((e) => {
this.tick(10, 'failed to set service config up for Helix', true);
this.tick(9, 'failed to set service config up for Helix', true);
this.log.error(`Remote publish service failed ${e}`);
throw new Error('Unable to setup service config');
});
Expand All @@ -192,21 +209,149 @@ class RemotePublishCommand extends AbstractCommand {
});
}

async updateBotConfig() {
const repos = {};
this.tick(1, 'updating helix-bot purge config', true);
this._strainsToPublish.forEach((strain) => {
const url = strain.content;
// todo: respect path
const urlString = `${url.protocol}://${url.host}/${url.owner}/${url.repo}.git#${url.ref}`;
if (!repos[urlString]) {
repos[urlString] = {
strains: [],
key: `${url.owner}/${url.repo}#${url.ref}`,
};
}
repos[urlString].strains.push(strain.name);
});
const response = await request.post(this._configPurgeAPI, {
json: true,
body: {
github_token: this._githubToken,
content_repositories: Object.keys(repos),
fastly_service_id: this._fastly_namespace,
fastly_token: this._fastly_auth,
},
});

this._botStatus = {
repos,
response,
};
this.tick(1, 'updated helix-bot purge config', true);
}

showHelixBotResponse() {
if (!this._botStatus) {
return;
}
const { repos, response } = this._botStatus;
// create summary
const reposNoBot = [];
const reposUpdated = [];
const reposErrors = [];
Object.keys(repos).forEach((repoUrl) => {
const repo = repos[repoUrl];
const info = response[repo.key];
if (!info) {
this.log.error(`Internal error: ${repo.key} should be in the service response`);
reposErrors.push(repo);
return;
}
if (info.errors) {
this.log.error(`${repo.key} update failed: ${info.errors}`);
reposErrors.push(repo);
return;
}
if (!info.installation_id) {
reposNoBot.push(repo);
return;
}
if (!info.config || !info.config.caches) {
this.log.error(`Internal error: ${repo.key} status does not have configuration details.`);
reposErrors.push(repo);
return;
}
// find the fastlyId in the cache
const cacheInfo = info.config.caches
.find(cache => cache.fastlyServiceId === this._fastly_namespace);
if (!cacheInfo) {
this.log.error(`Internal error: ${repo.key} status does have a configuration entry for given fastly service id.`);
reposErrors.push(repo);
return;
}
if (cacheInfo.errors) {
this.log.error(`${repo.key} update failed for given fastly service id: ${cacheInfo.errors}`);
reposErrors.push(repo);
return;
}
reposUpdated.push(repo);
});

if (reposUpdated.length > 0) {
this.log.info('');
this.log.info('Updated the purge-configuration of the following repositories:');
reposUpdated.forEach((repo) => {
this.log.info(chalk`- {cyan ${repo.key}} {grey (${repo.strains.join(', ')})}`);
});
}

if (reposErrors.length > 0) {
this.log.info('');
this.log.info('The purge-configuration of following repositories were not updated due to errors (see log for details):');
reposErrors.forEach((repo) => {
this.log.info(chalk`- {cyan ${repo.key}} {grey (${repo.strains.join(', ')})}`);
});
}

if (reposNoBot.length > 0) {
this.log.info('');
this.log.info('The following repositories are referenced by strains but don\'t have the helix-bot setup:');
reposNoBot.forEach((repo) => {
this.log.info(chalk`- {cyan ${repo.key}} {grey (${repo.strains.join(', ')})}`);
});
this.log.info(chalk`\nVisit {blue https://github.com/apps/helix-bot} to manage the helix bot installations.`);
}
}

async init() {
await super.init();
this._fastly = fastly(this._fastly_auth, this._fastly_namespace);

// gather all content repositories of the affected strains
this._strainsToPublish = this.config.strains.getByFilter((strain) => {
if (strain.isProxy()) {
this.log.debug(`ignoring proxy strain ${strain.name}`);
return false;
}
// skip the strains where we can't determine the action name
if (!strain.package) {
this.log.debug(`ignoring unaffected strain ${strain.name}`);
return false;
}
return true;
});
}

async run() {
await this.init();
if (this._strainsToPublish.length === 0) {
this.log.warn(chalk`None of the strains contains {cyan package} information. Aborting command.`);
return;
}
try {
this.tick(1, 'preparing fastly transaction', true);
await this._fastly.transact(async (version) => {
this._version = version;
await this.servicePublish();
await this.serviceAddLogger();
await this.updateFastlySecrets();
}, !this._dryRun);
if (this._updateBotConfig) {
await this.updateBotConfig();
}
await this.purgeFastly();
this.showHelixBotResponse();
this.showNextStep(this._dryRun);
} catch (e) {
const message = 'Error while running the Publish command';
Expand Down
20 changes: 20 additions & 0 deletions src/yargs-github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

module.exports = function commonArgs(yargs) {
return yargs
.option('github-token', {
alias: 'githubToken',
describe: 'Github access token',
type: 'string',
});
};
3 changes: 3 additions & 0 deletions test/fixtures/all.env
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ HLX_WSK_HOST = myruntime.net
# publish
HLX_REMOTE = false
HLX_API_PUBLISH = foobar.api
HLX_GITHUB_TOKEN = github-token-foobar
HLX_UPDATE_BOT_CONFIG = true
HLX_API_CONFIG_PURGE = purge.api

# perf
HLX_JUNIT = some-results.xml
Expand Down
18 changes: 17 additions & 1 deletion test/fixtures/deployed.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
strains:
default:
- name: default
content: ssh://[email protected]/adobe/helix-cli.git
code: ssh://[email protected]/adobe/helix-cli.git
static: ssh://[email protected]/adobe/helix-cli.git
package: dirty
url: https://www.project-helix.io/cli

- name: api
content: ssh://[email protected]/adobe/helix-content.git
code: ssh://[email protected]/adobe/helix-cli.git
static: ssh://[email protected]/adobe/helix-cli.git
package: dirty
url: https://www.project-helix.io/api

- name: non-deployed
content: ssh://[email protected]/adobe/helix-cli.git
code: ssh://[email protected]/adobe/helix-cli.git
static: ssh://[email protected]/adobe/helix-cli.git
url: https://www.project-helix.io/cli

- name: proxy
origin: https://www.adobe.com
21 changes: 21 additions & 0 deletions test/fixtures/non-deployed.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
strains:
- name: default
content: ssh://[email protected]/adobe/helix-cli.git
code: ssh://[email protected]/adobe/helix-cli.git
static: ssh://[email protected]/adobe/helix-cli.git
url: https://www.project-helix.io/cli

- name: api
content: ssh://[email protected]/adobe/helix-content.git
code: ssh://[email protected]/adobe/helix-cli.git
static: ssh://[email protected]/adobe/helix-cli.git
url: https://www.project-helix.io/api

- name: non-deployed
content: ssh://[email protected]/adobe/helix-cli.git
code: ssh://[email protected]/adobe/helix-cli.git
static: ssh://[email protected]/adobe/helix-cli.git
url: https://www.project-helix.io/cli

- name: proxy
origin: https://www.adobe.com
Loading

0 comments on commit 4be9093

Please sign in to comment.