Skip to content

Commit

Permalink
Merge pull request #64 from loopmediagroup/dev
Browse files Browse the repository at this point in the history
[Gally]: master <- dev
  • Loading branch information
simlu committed Jun 9, 2018
2 parents a25031a + a6e5dbb commit ae27211
Show file tree
Hide file tree
Showing 17 changed files with 241 additions and 75 deletions.
3 changes: 3 additions & 0 deletions .gally.json
@@ -1,5 +1,8 @@
{
"defaultBranch": "master",
"repository": {
"url": "https://github.com/loopmediagroup/gally.git"
},
"branches": {
"dev": {
"upstream": "master",
Expand Down
22 changes: 18 additions & 4 deletions README.md
Expand Up @@ -16,6 +16,13 @@ Git-Ally - Automation around Github.com Repository Management

$ npm i -g gally

## Github Credentials

You will be prompted to enter a personal github token that will then be stored as plain text on disk.

If an environment variable `GH_TOKEN` is present and credentials are not set,
the environment variable is used and no prompt is displayed.

## Contents of `.gally.json`

To create a config template run `ga init`. The configuration is an object with the following top level keys.
Expand All @@ -25,6 +32,11 @@ To create a config template run `ga init`. The configuration is an object with t
Type: `object`<br>
The default branch for the github repository.

### repository.url

Type: `string`<br>
The main github repository of this project.

### protection

Type: `object`<br>
Expand All @@ -45,20 +57,22 @@ All commands are available as `ga` or `gally`.

### pr [branch]

Create PR from `origin/CURRNET_BRANCH` to remote `upstream/TARGET_BRANCH` with
Open PR Url from `origin/CURRNET_BRANCH` to remote `upstream/TARGET_BRANCH` with

$ ga pr [branch]

where `branch` is the target branch (defaults to dev).

### promote \<branch\>
### promote [remote] \<branch\>

Create PR from `upstream/INPUT_BRANCH` to "upstream" branch `upstream/BRANCH` with

$ ga promote <branch>

where the upstream branch is defined in the configuration file under "upstream".

You can define a custom remote if so desired.

### init

Create a new `.gally.json` file by running
Expand All @@ -67,6 +81,6 @@ Create a new `.gally.json` file by running

For details on how to adjust the configuration, see the corresponding section.

### sync \<upstream\>
### sync [remote]

Synchronize config `.gally.json` to remote github repository.
Synchronize config `.gally.json` to remote github repository defined in config or using remote if passed.
28 changes: 21 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions src/cmds/promote.js
@@ -1,11 +1,12 @@
const os = require("os");
const git = require("./../util/git");
const github = require("./../util/github");
const gally = require("./../gally");
const open = require("./../util/open");
const logger = require("./../util/logger");

exports.command = 'promote <branch>';
exports.command = 'promote [remote] <branch>';
exports.desc = 'Open pr URL from provided to configured "upstream" branch';
exports.builder = {};
exports.handler = argv => gally
.load(`${os.homedir()}/.gally`, process.cwd())
.then(config => git.ghPromoteUrl(config, argv.branch).then(open.url));
.then(config => github.promoteBranch(config, argv.remote, argv.branch))
.then(logger.info);
2 changes: 1 addition & 1 deletion src/cmds/sync.js
Expand Up @@ -2,7 +2,7 @@ const os = require("os");
const gally = require("./../gally");
const github = require("./../util/github");

exports.command = 'sync <remote>';
exports.command = 'sync [remote]';
exports.desc = 'Apply local configuration to remote github repo.';
exports.builder = {};
exports.handler = argv => gally
Expand Down
3 changes: 3 additions & 0 deletions src/configs/default.json
@@ -1,5 +1,8 @@
{
"defaultBranch": "master",
"repository": {
"url": "https://github.com/ACCOUNT/REPO.git"
},
"branches": {
"dev": {
"upstream": "stage",
Expand Down
11 changes: 1 addition & 10 deletions src/gally.js
Expand Up @@ -17,16 +17,7 @@ module.exports.load = async (configDir, cwd) => {
const globalConfig = json.loadOrDefault(globalConfigFile);
const credentials = json.loadOrDefault(credentialsFile);

if (get(credentials, "github.username") === undefined) {
const username = (await inquirer.prompt([{
type: 'input',
message: 'Enter github username',
name: 'username'
}])).username;
set(credentials, "github.username", username);
}

if (get(credentials, "github.token") === undefined) {
if (get(credentials, "github.token", process.env.GH_TOKEN) === undefined) {
const token = (await inquirer.prompt([{
type: 'password',
message: 'Enter github personal access token',
Expand Down
11 changes: 0 additions & 11 deletions src/util/git.js
Expand Up @@ -29,14 +29,3 @@ module.exports.ghPrUrl = async (branch = "dev") => {

return `${target}...${source}?expand=1`;
};


module.exports.ghPromoteUrl = async (config, branch) => {
const upstream = await getRemoteUrl(await getRemoteOrBestGuess("upstream", "origin"));
const upstreamBranch = config.config.local.branches[branch].upstream;

const source = branch;
const target = `${upstream.slice(0, -4)}/compare/${upstreamBranch}`;

return `${target}...${source}?expand=1`;
};
35 changes: 28 additions & 7 deletions src/util/github.js
Expand Up @@ -4,23 +4,44 @@ const git = require("./git");
const logger = require("./logger");
const gitBranch = require("./git/branch");
const githubBranch = require("./github/branch");
const githubPr = require("./github/pr");

const evaluate = async (config, remote) => {
const getToken = config => get(config, "credentials.github.token", process.env.GH_TOKEN);

const getRepoKey = async (config, remote = undefined) => {
const remoteUrl = remote ? await git.getRemoteUrl(remote) : get(config, "config.local.repository.url");
return remoteUrl.slice(0, -4).split("/").slice(-2).join("/");
};

const promoteBranch = async (config, remote, branch) => {
const repoKey = await getRepoKey(config, remote);
const upstreamBranch = config.config.local.branches[branch].upstream;
const result = await githubPr.create(branch, upstreamBranch, repoKey, getToken(config));
if (result.statusCode === 201) {
return result.body.html_url;
}
if (get(result, "body.errors[0].message", "").startsWith("A pull request already exists for ")) {
return `https://github.com/${repoKey}/pulls`;
}
return `${result.statusCode}: ${get(result, "body.message")}`;
};
module.exports.promoteBranch = promoteBranch;

const evaluate = async (config, remote = undefined) => {
if (config.config.local === null) {
throw new Error(`Missing ".gally.json". Please run "gally init."`);
}

const remoteUrl = await git.getRemoteUrl(remote);
const repoKey = remoteUrl.slice(0, -4).split("/").slice(-2).join("/");
const repoKey = await getRepoKey(config, remote);

// check default branch
const defaultBranch = await githubBranch.getDefaultBranch(repoKey, config.credentials.github.token);
const defaultBranch = await githubBranch.getDefaultBranch(repoKey, getToken(config));
if (get(config, "config.local.defaultBranch", "master") !== defaultBranch) {
throw new Error("Incorrect default branch configured!");
}

// obtain branches and do basic checking
const remoteBranches = await githubBranch.list(repoKey, config.credentials.github.token);
const remoteBranches = await githubBranch.list(repoKey, getToken(config));
const configBranches = Object.keys(get(config, "config.local.branches", {}));
const branchInfo = gitBranch.evaluate(configBranches, remoteBranches);
if (branchInfo.unexpected.length !== 0) {
Expand All @@ -32,7 +53,7 @@ const evaluate = async (config, remote) => {
if (toCreate.length !== 0) {
logger.info(`Creating Branches: ${chalk.green(toCreate.join(", "))}`);
const result = await Promise
.all(toCreate.map(b => githubBranch.create(b, repoKey, config.credentials.github.token)));
.all(toCreate.map(b => githubBranch.create(b, repoKey, getToken(config))));
if (result.every(e => e === true)) {
logger.info(chalk.green("ok"));
// update branchInfo
Expand All @@ -56,7 +77,7 @@ const evaluate = async (config, remote) => {
: `${e} [${chalk.green("protected")}]`))
.join(", ")}`);
const result = await Promise.all(Object.keys(toSync).map(b => githubBranch
.updateProtection(b, toSync[b], repoKey, config.credentials.github.token)));
.updateProtection(b, toSync[b], repoKey, getToken(config))));
if (result.every(e => e === true)) {
logger.info(chalk.green("ok"));
} else {
Expand Down
16 changes: 16 additions & 0 deletions src/util/github/pr.js
@@ -0,0 +1,16 @@
const request = require("./../github/request");

const create = (source, target, repoKey, token) => request.post(
`https://api.github.com/repos/${repoKey}/pulls`,
token,
{
body: {
title: `[Gally]: ${target} <- ${source}`,
head: source,
base: target,
maintainer_can_modify: false,
body: "Automatically created by Git-Ally"
}
}
);
module.exports.create = create;
28 changes: 14 additions & 14 deletions test/cmds/promote.spec.js
@@ -1,39 +1,39 @@
const expect = require('chai').expect;
const open = require("./../../src/util/open");
const github = require("./../../src/util/github");
const promote = require("./../../src/cmds/promote");
const gally = require("./../../src/gally");
const git = require("./../../src/util/git");
const logger = require("./../../src/util/logger");

describe("Testing `promote <branch>`", () => {
let gallyLoad;
const urls = [];
let gitGhPromoteUrl;
let openUrl;
const logs = [];
let githubPromoteBranch;
let loggerInfo;

before(() => {
gallyLoad = gally.load;
gally.load = () => Promise.resolve({});
gitGhPromoteUrl = git.ghPromoteUrl;
git.ghPromoteUrl = () => Promise.resolve("URL");
openUrl = open.url;
open.url = (url) => {
urls.push(url);
githubPromoteBranch = github.promoteBranch;
github.promoteBranch = () => Promise.resolve("URL");
loggerInfo = logger.info;
logger.info = (log) => {
logs.push(log);
};
});

after(() => {
git.ghPromoteUrl = gitGhPromoteUrl;
open.url = openUrl;
github.promoteBranch = githubPromoteBranch;
logger.info = loggerInfo;
gally.load = gallyLoad;
});

beforeEach(() => {
urls.length = 0;
logs.length = 0;
});

it("Testing promote (Integration)", (done) => {
promote.handler({}).then(() => {
expect(urls).to.deep.equal(['URL']);
expect(logs).to.deep.equal(["URL"]);
done();
});
});
Expand Down

0 comments on commit ae27211

Please sign in to comment.