Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions packages/cli/src/commands/pipelines/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Args, ux} from '@oclif/core'
import {getPipeline} from '../../lib/api'
import GitHubAPI from '../../lib/pipelines/github-api'
import KolkrabbiAPI from '../../lib/pipelines/kolkrabbi-api'
import {createPipelineRepository} from '../../lib/pipelines/repos-api'
import getGitHubToken from '../../lib/pipelines/setup/get-github-token'
import getNameAndRepo from '../../lib/pipelines/setup/get-name-and-repo'
import getRepo from '../../lib/pipelines/setup/get-repo'
Expand Down Expand Up @@ -47,20 +48,25 @@ export default class Connect extends Command {
return
}

const kolkrabbi = new KolkrabbiAPI(this.config.userAgent, () => this.heroku.auth)
const github = new GitHubAPI(this.config.userAgent, await getGitHubToken(kolkrabbi))

const {
name: pipelineName,
repo: repoName,
} = await getNameAndRepo(combinedInputs)

const repo = await getRepo(github, repoName)

const pipeline = await getPipeline(this.heroku, pipelineName)

ux.action.start('Linking to repo')
await kolkrabbi.createPipelineRepository(pipeline.body.id, repo.id)
// Attempt repos-api connection first
try {
const repoUrl = repoName.includes('.') ? repoName : `https://github.com/${repoName}`
await createPipelineRepository(this.heroku, pipeline.body.id!, repoUrl)
// Fallback to kolkrabbi
} catch {
const kolkrabbi = new KolkrabbiAPI(this.config.userAgent, () => this.heroku.auth)
const github = new GitHubAPI(this.config.userAgent, await getGitHubToken(kolkrabbi))
const repo = await getRepo(github, repoName)
await kolkrabbi.createPipelineRepository(pipeline.body.id, repo.id)
}

ux.action.stop()
}
}
1 change: 1 addition & 0 deletions packages/cli/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const V3_HEADER = 'application/vnd.heroku+json; version=3'
export const SDK_HEADER = 'application/vnd.heroku+json; version=3.sdk'
export const FILTERS_HEADER = `${V3_HEADER}.filters`
export const PIPELINES_HEADER = `${V3_HEADER}.pipelines`
export const REPOSITORIES_HEADER = `${V3_HEADER}.repositories-api`
const CI_HEADER = `${V3_HEADER}.ci`

export type Owner = Pick<Heroku.Account, 'id' | 'type'> | Pick<Heroku.Team, 'id' | 'type'>
Expand Down
18 changes: 18 additions & 0 deletions packages/cli/src/lib/pipelines/repos-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {APIClient} from '@heroku-cli/command'
import {REPOSITORIES_HEADER} from '../api'

export interface PipelineRepository {
repository?: {
id?: string
url?: string
}
}

export function createPipelineRepository(heroku: APIClient, pipelineId: string, repoUrl: string) {
return heroku.request<PipelineRepository>(`/pipelines/${pipelineId}/repo`, {
method: 'POST',
headers: {Accept: REPOSITORIES_HEADER},
body: {repo_url: repoUrl},
})
}

71 changes: 71 additions & 0 deletions packages/cli/test/unit/commands/pipelines/connect.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ describe('pipelines:connect', function () {
test
.stderr()
.stdout()
.nock('https://api.heroku.com', api => {
const pipeline = {
id: '123',
name: 'my-pipeline',
}
api.get(`/pipelines/${pipeline.name}`).reply(200, pipeline)
api.post(`/pipelines/${pipeline.id}/repo`).reply(422, {})
})
.nock('https://kolkrabbi.heroku.com', kolkrabbi => {
kolkrabbi.get('/account/github/token').reply(401, {})
})
Expand Down Expand Up @@ -59,6 +67,14 @@ describe('pipelines:connect', function () {

describe('with an account connected to GitHub experiencing request failures', function () {
test
.nock('https://api.heroku.com', api => {
const pipeline = {
id: '123',
name: 'my-pipeline',
}
api.get(`/pipelines/${pipeline.name}`).reply(200, pipeline)
api.post(`/pipelines/${pipeline.id}/repo`).reply(422, {})
})
.nock('https://kolkrabbi.heroku.com', kolkrabbi => {
const kolkrabbiAccount = {
github: {
Expand All @@ -81,4 +97,59 @@ describe('pipelines:connect', function () {
})
.it('shows an error if GitHub request fails')
})

describe('with an account connected via repos-api', function () {
test
.nock('https://api.heroku.com', api => {
const pipeline = {
id: '123',
name: 'my-pipeline',
}
api.get(`/pipelines/${pipeline.name}`).reply(200, pipeline)
api.post(`/pipelines/${pipeline.id}/repo`).reply(201, {})
})
.stderr()
.stdout()
.command(['pipelines:connect', 'my-pipeline', '--repo=github.com/my-org/my-repo'])
.it('shows success', ctx => {
expect(ctx.stderr).to.include('Linking to repo...')
expect(ctx.stdout).to.equal('')
})
})

describe('repos-api fallback to kolkrabbi', function () {
test
.nock('https://api.heroku.com', api => {
const pipeline = {
id: '123',
name: 'my-pipeline',
}
api.get(`/pipelines/${pipeline.name}`).reply(200, pipeline)
api.post(`/pipelines/${pipeline.id}/repo`).reply(422, {})
})
.nock('https://kolkrabbi.heroku.com', kolkrabbi => {
const kolkrabbiAccount = {
github: {
token: '123-abc',
},
}
kolkrabbi.get('/account/github/token').reply(200, kolkrabbiAccount)
kolkrabbi.post('/pipelines/123/repository').reply(201, {})
})
.nock('https://api.github.com', github => {
const repo = {
id: 1235,
default_branch: 'main',
name: 'my-org/my-repo',
}
github.get(`/repos/${repo.name}`).reply(200, repo)
})
.stderr()
.stdout()
.command(['pipelines:connect', 'my-pipeline', '--repo=my-org/my-repo'])
.it('shows success', ctx => {
expect(ctx.stderr).to.include('Linking to repo...')
expect(ctx.stdout).to.equal('')
})
})
})
Loading