Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: pipeline diff fails on v10 #3154

Merged
merged 3 commits into from
Jan 8, 2025
Merged
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
37 changes: 20 additions & 17 deletions packages/cli/src/commands/pipelines/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {Command, flags} from '@heroku-cli/command'
import {ux} from '@oclif/core'
import HTTP from '@heroku/http-call'

import {getCoupling, getReleases, listPipelineApps, SDK_HEADER} from '../../lib/api'
import {getCoupling, getPipeline, getReleases, listPipelineApps, SDK_HEADER} from '../../lib/api'
import KolkrabbiAPI from '../../lib/pipelines/kolkrabbi-api'
import {OciImage, Slug} from '../../lib/types/fir'
import type {OciImage, Slug, PipelineCoupling} from '../../lib/types/fir'
import type {Commit, GitHubDiff} from '../../lib/types/github'

interface AppInfo {
name: string;
Expand Down Expand Up @@ -35,19 +36,21 @@ async function diff(targetApp: AppInfo, downstreamApp: AppInfo, githubToken: str
// Do the actual GitHub diff
try {
const path = `${targetApp.repo}/compare/${downstreamApp.hash}...${targetApp.hash}`
const headers: { authorization: string; 'user-agent'?: string} = {authorization: 'token ' + githubToken}
const headers = {
authorization: 'token ' + githubToken,
'Content-Type': 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
}

if (herokuUserAgent) {
headers['user-agent'] = herokuUserAgent
Reflect.set(headers, 'user-agent', herokuUserAgent)
}

const githubDiff: any = await HTTP.get(`https://api.github.com/repos/${path}`, {
headers,
}).then(res => res.body)
const {body: githubDiff} = await HTTP.get<GitHubDiff>(`https://api.github.com/repos/${path}`, {headers})

ux.log('')
ux.styledHeader(`${color.app(targetApp.name)} is ahead of ${color.app(downstreamApp.name)} by ${githubDiff.ahead_by} commit${githubDiff.ahead_by === 1 ? '' : 's'}`)
const mapped = githubDiff.commits.map((commit: any) => {
const mapped = githubDiff.commits.map((commit: Commit) => {
return {
sha: commit.sha.slice(0, 7),
date: commit.commit.author.date,
Expand All @@ -64,7 +67,6 @@ async function diff(targetApp: AppInfo, downstreamApp: AppInfo, githubToken: str
message: {},
})
ux.log(`\nhttps://github.com/${path}`)
// tslint:disable-next-line: no-unused
} catch {
ux.log(`\n${color.app(targetApp.name)} was not compared to ${color.app(downstreamApp.name)} because we were unable to perform a diff`)
ux.log('are you sure you have pushed your latest commits to GitHub?')
Expand Down Expand Up @@ -126,20 +128,21 @@ export default class PipelinesDiff extends Command {
const {flags} = await this.parse(PipelinesDiff)
const targetAppName = flags.app

const coupling = await getCoupling(this.heroku, targetAppName)
.then(res => res.body)
.catch(() => {})

if (!coupling) {
let coupling: PipelineCoupling | undefined
try {
({body: coupling} = await getCoupling(this.heroku, targetAppName))
} catch {
ux.error(`This app (${targetAppName}) does not seem to be a part of any pipeline`)
return
}

const targetAppId = coupling.app.id!
const generation = coupling.generation
const {body: pipeline} = await getPipeline(this.heroku, coupling.pipeline!.id!)

const targetAppId = coupling!.app!.id!
const generation = pipeline!.generation!.name!

ux.action.start('Fetching apps from pipeline')
const allApps = await listPipelineApps(this.heroku, coupling.pipeline.id!)
const allApps = await listPipelineApps(this.heroku, coupling!.pipeline!.id!)
ux.action.stop()

const sourceStage = coupling.stage
Expand Down
20 changes: 8 additions & 12 deletions packages/cli/src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {APIClient} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import {App, PipelineCoupling, Release} from './types/fir'
import {App, Pipeline, PipelineCoupling} from './types/fir'

export const V3_HEADER = 'application/vnd.heroku+json; version=3'
export const SDK_HEADER = 'application/vnd.heroku+json; version=3.sdk'
Expand Down Expand Up @@ -55,18 +55,14 @@ export function findPipelineByName(heroku: APIClient, idOrName: string) {
})
}

export interface PipelineCouplingSdk extends Required<PipelineCoupling> {
generation: 'fir' | 'cedar'
}

export function getCoupling(heroku: APIClient, app: string) {
return heroku.get<PipelineCouplingSdk>(`/apps/${app}/pipeline-couplings`, {
return heroku.get<PipelineCoupling>(`/apps/${app}/pipeline-couplings`, {
headers: {Accept: SDK_HEADER},
})
}

export function getPipeline(heroku: APIClient, id: string) {
return heroku.request<Heroku.Pipeline>(`/pipelines/${id}`, {
return heroku.request<Pipeline>(`/pipelines/${id}`, {
method: 'GET',
headers: {Accept: PIPELINES_HEADER},
})
Expand Down Expand Up @@ -99,24 +95,24 @@ export function getAppSetup(heroku: APIClient, buildId: any) {
}

function listCouplings(heroku: APIClient, pipelineId: string) {
return heroku.get<Array<PipelineCouplingSdk>>(`/pipelines/${pipelineId}/pipeline-couplings`, {
return heroku.get<Array<PipelineCoupling>>(`/pipelines/${pipelineId}/pipeline-couplings`, {
headers: {Accept: SDK_HEADER},
})
}

export interface AppWithPipelineCoupling extends App {
pipelineCoupling: PipelineCouplingSdk
pipelineCoupling: PipelineCoupling
[k: string]: unknown
}

export async function listPipelineApps(heroku: APIClient, pipelineId: string): Promise<Array<AppWithPipelineCoupling>> {
const {body: couplings} = await listCouplings(heroku, pipelineId)
const appIds = couplings.map(coupling => coupling.app.id || '')
const appIds = couplings.map(coupling => coupling.app!.id || '')
const {body: apps} = await getAppFilter(heroku, appIds)
return apps.map(app => {
return {
...app,
pipelineCoupling: couplings.find(coupling => coupling.app.id === app.id),
pipelineCoupling: couplings.find(coupling => coupling.app!.id === app.id),
} as AppWithPipelineCoupling
})
}
Expand All @@ -138,7 +134,7 @@ export function updateCoupling(heroku: APIClient, app: string, stage: string) {
}

export function getReleases(heroku: APIClient, appId: string) {
return heroku.get<Array<Release>>(`/apps/${appId}/releases`, {
return heroku.get<Array<Heroku.Release>>(`/apps/${appId}/releases`, {
headers: {Accept: SDK_HEADER, Range: 'version ..; order=desc'},
partial: true,
})
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/lib/apps/generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ async function getApp(appOrName: App | string, herokuApi?: APIClient): Promise<A

export async function isFirApp(appOrName: App | string, herokuApi?: APIClient) {
const app = await getApp(appOrName, herokuApi)
return app.generation === 'fir'
return app.generation.name === 'fir'
}

export async function isCedarApp(appOrName: App | string, herokuApi?: APIClient) {
const app = await getApp(appOrName, herokuApi)
return app.generation === 'cedar'
return app.generation.name === 'cedar'
}
5 changes: 3 additions & 2 deletions packages/cli/src/lib/buildpacks/buildpacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {ux} from '@oclif/core'
import {findIndex as lodashFindIndex} from 'lodash'
import {Result} from 'true-myth'
import push from '../git/push'
import {OciImage, Release} from '../../lib/types/fir'
import {OciImage} from '../../lib/types/fir'
import {isURL} from 'validator'
import * as Heroku from '@heroku-cli/schema'

export type BuildpackResponse = {
buildpack: {
Expand All @@ -29,7 +30,7 @@ export class BuildpackCommand {
async fetch(app: string, isFirApp = false): Promise<any[]> {
let buildpacks: any
if (isFirApp) {
const {body: releases} = await this.heroku.request<Release[]>(`/apps/${app}/releases`, {
const {body: releases} = await this.heroku.request<Heroku.Release[]>(`/apps/${app}/releases`, {
sbosio marked this conversation as resolved.
Show resolved Hide resolved
partial: true,
headers: {
Range: 'version ..; max=10, order=desc',
Expand Down
45 changes: 40 additions & 5 deletions packages/cli/src/lib/types/fir.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,11 +672,9 @@ export interface App {
*/
current_build_architecture: unknown[];
/**
* the generation of the app
*
* @example "fir"
* Generation of the Heroku platform for this app
*/
readonly generation: string;
readonly generation: AppGeneration;
/**
* git repo URL of app
*
Expand Down Expand Up @@ -2263,6 +2261,20 @@ export interface Buildpack {
*/
homepage?: string;
}
export interface AppGeneration {
/**
* unique identifier of the generation of the Heroku platform for this app
*
* @example "01234567-89ab-cdef-0123-456789abcdef"
*/
readonly id?: string;
/**
* unique name of the generation of the Heroku platform for this app
*
* @example "cedar"
*/
readonly name?: string;
}
/**
*
* identity of app owner
Expand Down Expand Up @@ -5481,6 +5493,10 @@ export interface Pipeline {
* @example "2012-01-01T12:00:00Z"
*/
readonly updated_at?: string;
/**
* the generation of the Heroku platform for this pipeline
*/
generation?: PipelineGeneration | null;
}
export interface PipelineCouplingCreatePayload {
/**
Expand Down Expand Up @@ -5787,7 +5803,7 @@ export interface PipelinePromotionCreatePayload {
* the app being promoted from
*/
export interface PipelinePromotionCreatePayloadSource {
/**
/**
* the app which was promoted from
*/
app?: PipelinePromotionCreatePayloadSourceApp;
Expand Down Expand Up @@ -5894,6 +5910,25 @@ export interface PipelineOwner {
*/
type: string;
}
/**
*
* [Heroku Platform API - generation](https://devcenter.heroku.com/articles/platform-api-reference#generation)
* the generation of the Heroku platform for this pipeline
*/
export interface PipelineGeneration {
/**
* unique identifier of the generation of the Heroku platform for this pipeline
*
* @example "01234567-89ab-cdef-0123-456789abcdef"
*/
readonly id: string;
/**
* unique name of the generation of the Heroku platform for this pipeline
*
* @example "fir"
*/
readonly name: string;
}
export interface PipelineCreatePayload {
/**
* name of pipeline
Expand Down
95 changes: 95 additions & 0 deletions packages/cli/src/lib/types/github.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
export interface GitHubUser {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
user_view_type: string;
site_admin: boolean;
}

export interface CommitPerson {
name: string;
email: string;
date: string;
}

export interface Verification {
verified: boolean;
reason: string;
signature: string;
payload: string;
verified_at: string;
}

export interface CommitDetails {
author: CommitPerson;
committer: CommitPerson;
message: string;
tree: {
sha: string;
url: string;
};
url: string;
comment_count: number;
verification: Verification;
}

export interface CommitParent {
sha: string;
url: string;
html_url: string;
}

export interface CommitFile {
sha: string;
filename: string;
status: string;
additions: number;
deletions: number;
changes: number;
blob_url: string;
raw_url: string;
contents_url: string;
patch: string;
}

export interface Commit {
sha: string;
node_id: string;
commit: CommitDetails;
url: string;
html_url: string;
comments_url: string;
author: GitHubUser;
committer: GitHubUser;
parents: CommitParent[];
}

export interface GitHubDiff {
url: string;
html_url: string;
permalink_url: string;
diff_url: string;
patch_url: string;
base_commit: Commit;
merge_base_commit: Commit;
status: string;
ahead_by: number;
behind_by: number;
total_commits: number;
commits: Commit[];
files: CommitFile[];
}
25 changes: 2 additions & 23 deletions packages/cli/test/fixtures/apps/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ export const cedarApp: Partial<App> = {
internal_routing: null,
updated_at: '2024-10-17T16:20:42Z',
web_url: 'https://my-cedar-app-a6b0f2f1519f.herokuapp.com/',
generation: 'cedar',
base_image_name: null,
buildpacks: null,
current_build_architecture: [
'amd64',
],
generation: {name: 'cedar'},
}

export const firApp: Partial<App> = {
Expand Down Expand Up @@ -60,21 +55,5 @@ export const firApp: Partial<App> = {
internal_routing: null,
updated_at: '2024-10-17T16:20:42Z',
web_url: 'https://my-fir-app-c07499750516.herokuapp.com/',
generation: 'fir',
base_image_name: 'docker.io/heroku/heroku:24',
buildpacks: [
{
id: 'heroku/ruby',
version: '3.0.0',
homepage: 'https://github.com/heroku/buildpacks-ruby',
},
{
id: 'heroku/procfile',
version: '3.1.2',
homepage: 'https://github.com/heroku/buildpacks-procfile',
},
],
current_build_architecture: [
'arm64',
],
generation: {name: 'fir'},
}
Loading
Loading