forked from BCDevOps/nrdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
checkout.ts
146 lines (132 loc) · 6.91 KB
/
checkout.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* https://docs.atlassian.com/bitbucket-server/rest/7.6.0/bitbucket-branch-rest.html
* https://bwa.nrs.gov.bc.ca/int/jira/rest/api/2/issue/ONETEAM-275
* https://bwa.nrs.gov.bc.ca/int/jira/rest/dev-status/latest/issue/summary?issueId=131578
* https://bwa.nrs.gov.bc.ca/int/jira/rest/dev-status/1.0/issue/detail?issueId=131578&applicationType=stash&dataType=pullrequest
*/
import {flags} from '@oclif/command'
import {Issue} from '../../api/service/axios-jira-client'
import {GitBaseCommand} from '../../git-base'
import * as inquirer from 'inquirer'
import * as path from 'path'
import {BranchReference, RepositoryReference} from '../../api/service/axios-bitbucket-client'
import cli from 'cli-ux'
export default class GitCheckout extends GitBaseCommand {
static description = 'Given a Jira Issue, checks out a Git branch named Feature/[Jira Issue] to resolve that Issue.'
static examples = [
`# nrdk backlog:checkout IRS-200
# git status
On branch IRS-200`,
]
static flags = {
project: flags.string({hidden: true, char: 'p', description: 'BitBucket Project/Group Name'}),
repository: flags.string({hidden: true, char: 'r', description: 'BitBucket Repository Name'}),
branch: flags.string({hidden: true, char: 'b', description: 'Remote Branch Name'}),
username: flags.string({hidden: true, char: 'u', description: 'Username to be appended to branch names'}),
}
static args = [{name: 'issue', description: 'Jira issue key (e.g.: WEBADE-123)'}]
async action(message: string, promise: Promise<any>) {
return Promise.resolve(true)
.then(() => {
cli.action.start(message)
return promise
})
.finally(() => {
cli.action.stop()
})
}
async getIssue(issueKey: string): Promise<Issue> {
if (!this.jira()) return this.error('Jira client has not been initialized')
return this.action(`Fetching Jira Issue ${issueKey}`, this.jira().getIssue(issueKey, {fields: 'issuetype,components'}))
}
async gitCloneRepository(branchInfo: BranchReference) {
const expectedGitRemoteOriginUrl: string = branchInfo.repository.url as string
const gitTopLevel = await this._spawn('git', ['rev-parse', '--show-toplevel'])
let expectedRepoCwd = await this.cwd()
if (gitTopLevel.status !== 0) {
this.log(`Current directory (${expectedRepoCwd}) is not the root of a git repository.`)
const prompt = inquirer.createPromptModule()
let answer: any = await prompt([{type: 'confirm', name: 'clone', message: `Would you like to clone ${expectedGitRemoteOriginUrl}?`}])
if (answer.clone !== true) {
return this.exit(1)
}
expectedRepoCwd = path.resolve(expectedRepoCwd, `${branchInfo.repository.project.key}-${branchInfo.repository.slug}`)
answer = await prompt([{type: 'input', name: 'path', message: 'Where would you like to clone?', default: expectedRepoCwd}])
expectedRepoCwd = answer.path
const gitClone = await this._spawn('git', ['clone', expectedGitRemoteOriginUrl, expectedRepoCwd])
if (gitClone.status !== 0) {
return this.error(`Error cloning repository. Try running\n>git clone ${expectedGitRemoteOriginUrl} ${expectedRepoCwd}`)
}
}
const gitRemoteOriginUrl = await this._spawn('git', ['config', '--get', 'remote.origin.url'], {cwd: expectedRepoCwd})
if (expectedGitRemoteOriginUrl.trim().toLowerCase() !== gitRemoteOriginUrl.stdout.trim().toLowerCase()) {
return this.error(`Expected the git remote url to be '${expectedGitRemoteOriginUrl}', but found '${gitRemoteOriginUrl.stdout.trim()}'`)
}
const gitFetch = await this._spawn('git', ['fetch', 'origin'], {cwd: expectedRepoCwd})
if (gitFetch.status !== 0) {
return this.error('Error fetching changes from remote repository. Try running\n>git fetch origin')
}
branchInfo.repository.cwd = expectedRepoCwd
return branchInfo
}
async gitCheckoutBranch(branchInfo: BranchReference) {
if (!branchInfo.repository.cwd) return this.error('Repository work directory has not been initialized')
const gitCurrentTrackingBranchName = await this._spawn('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], {cwd: branchInfo.repository.cwd})
const expectedCurrentTrackingBranchName = gitCurrentTrackingBranchName.stdout.trim()
if (expectedCurrentTrackingBranchName !== `origin/${branchInfo.name}`) {
this.log(`Currently on branch '${expectedCurrentTrackingBranchName}'. Checking out branch '${branchInfo.name}'`)
const gitCheckout = await this._spawn('git', ['checkout', branchInfo.name], {cwd: branchInfo.repository.cwd})
if (gitCheckout.status !== 0) {
return this.error(`Error checkout branch ${branchInfo.name}. Try running\n>git checkout ${branchInfo.name}`)
}
}
this.log('Updating local branch with remote branch')
const gitRebase = await this._spawn('git', ['rebase', `origin/${branchInfo.name}`], {cwd: branchInfo.repository.cwd})
if (gitRebase.status !== 0) {
return this.error(`Error rebasing/updating branch ${branchInfo.name}. Try running\n>git rebase origin/${branchInfo.name}`)
}
}
// eslint-disable-next-line valid-jsdoc
/**
* if the issue branch doesn't exist, one will be created.
*/
async getIssueBranch(issue: Issue, repository: RepositoryReference, username?: string) {
if (!this.jira()) return this.error('Jira client has not been initialized')
// RFC issues
if (issue.fields.issuetype.name === 'RFC') {
return this.createReleaseBranch(issue, repository)
}
// non-RFC issues
this.log(`Finding RFC for issue ${issue.key}/${issue.id}`)
const rfc = await this.jira().getRfcByIssue(issue.key)
this.log(`Found RFC ${rfc.key}/${rfc.id}`)
const releaseBranch = await this.createReleaseBranch(rfc, repository)
let branchName = `feature/${issue.key}`
if (username) {
branchName += `-${username}`
}
return this.createBranch(issue, repository, branchName, releaseBranch.name)
}
async getRepository(issue: Issue, flags: any): Promise<RepositoryReference> {
if (!this.jira()) return this.error('Jira client has not been initialized')
if (!flags.repository || !flags.project) {
if (issue.fields.components.length !== 1) {
return this.error(`Expected at least 1 component set for issue '${issue.key}', but found '${issue.fields.components.length}'`)
}
const component = issue.fields.components[0]
return this.jira().getComponentRepositoryInfo(component)
}
return {slug: flags.repository, project: {key: flags.project as string}}
}
async run() {
const {args, flags} = this.parse(GitCheckout)
const issue = await this.getIssue(args.issue)
const repository: RepositoryReference = await this.getRepository(issue, flags)
const branchInfo: BranchReference = await this.getIssueBranch(issue, repository)
// clone
await this.gitCloneRepository(branchInfo)
// Checkout branch
await this.gitCheckoutBranch(branchInfo)
return branchInfo
}
}