Skip to content

Commit

Permalink
Get git-related functionality working
Browse files Browse the repository at this point in the history
  • Loading branch information
azz committed Feb 24, 2018
1 parent 1b716f4 commit c979c4f
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 33 deletions.
46 changes: 43 additions & 3 deletions source/danger.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import * as GitHub from "@octokit/rest"

declare module "danger" {
type MarkdownString = string

// This is `danger.bitbucket_server` inside the JSON

interface BitBucketServerJSONDSL {
/** The pull request and repository metadata */
metadata: RepoMetaData
/** The related JIRA issues */
issues: JIRAIssue[]
/** The PR metadata */
Expand Down Expand Up @@ -85,13 +86,52 @@ declare module "danger" {
}[]
}

type BitBucketServerPRStatus = "APPROVED" | "UNAPPROVED" | "NEEDS_WORK"
interface BitBucketServerDiff {
destination?: BitBucketServerFile
source?: BitBucketServerFile
hunks: BitBucketServerHunk[]
truncated: boolean
toHash: string
fromHash: string
whitespace: "SHOW" | "IGNORE_ALL"
}

interface BitBucketServerFile {
components: string[]
name: string
parent: string
toString: string
}

interface BitBucketServerHunk {
destinationLine: number
destinationSpan: number
segments: BitBucketServerSegment[]
sourceLine: number
sourceSpan: number
truncated: boolean
}

interface BitBucketServerSegment {
lines: BitBucketServerLine[]
truncated: boolean
type: "ADDED" | "REMOVED"
}

interface BitBucketServerLine {
source: number
destination: number
line: string
truncated: boolean
conflictMarker?: "OURS"
commentIds?: number[]
}

interface BitBucketServerPRParticipant {
user: BitBucketServerUser
role: "AUTHOR" | "REVIEWER" | "PARTICIPANT"
approved: boolean
status: BitBucketServerPRStatus
status: "APPROVED" | "UNAPPROVED" | "NEEDS_WORK"
}

/**
Expand Down
47 changes: 45 additions & 2 deletions source/dsl/BitBucketServerDSL.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { RepoMetaData } from "../ci_source/ci_source"

// This is `danger.bitbucket_server` inside the JSON

export interface BitBucketServerJSONDSL {
/** The pull request and repository metadata */
metadata: RepoMetaData
/** The related JIRA issues */
issues: JIRAIssue[]
/** The PR metadata */
Expand Down Expand Up @@ -76,13 +80,52 @@ export interface BitBucketServerCommit {
}[]
}

export type BitBucketServerPRStatus = "APPROVED" | "UNAPPROVED" | "NEEDS_WORK"
export interface BitBucketServerDiff {
destination?: BitBucketServerFile
source?: BitBucketServerFile
hunks: BitBucketServerHunk[]
truncated: boolean
toHash: string
fromHash: string
whitespace: "SHOW" | "IGNORE_ALL"
}

export interface BitBucketServerFile {
components: string[]
name: string
parent: string
toString: string
}

export interface BitBucketServerHunk {
destinationLine: number
destinationSpan: number
segments: BitBucketServerSegment[]
sourceLine: number
sourceSpan: number
truncated: boolean
}

export interface BitBucketServerSegment {
lines: BitBucketServerLine[]
truncated: boolean
type: "ADDED" | "REMOVED"
}

export interface BitBucketServerLine {
source: number
destination: number
line: string
truncated: boolean
conflictMarker?: "OURS"
commentIds?: number[]
}

export interface BitBucketServerPRParticipant {
user: BitBucketServerUser
role: "AUTHOR" | "REVIEWER" | "PARTICIPANT"
approved: boolean
status: BitBucketServerPRStatus
status: "APPROVED" | "UNAPPROVED" | "NEEDS_WORK"
}

/**
Expand Down
1 change: 1 addition & 0 deletions source/platforms/BitBucketServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class BitBucketServer implements Platform {
const issues = await this.api.getIssues()

return {
metadata: this.api.repoMetadata,
pr,
commits,
comments,
Expand Down
32 changes: 23 additions & 9 deletions source/platforms/bitbucket_server/BitBucketServerAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BitBucketServerPRComment,
JIRAIssue,
BitBucketServerPRActivity,
BitBucketServerDiff,
} from "../../dsl/BitBucketServerDSL"

import { RepoMetaData, Env } from "../../ci_source/ci_source"
Expand Down Expand Up @@ -80,9 +81,19 @@ export class BitBucketServerAPI {
return (await res.json()).values
}

getPullRequestDiff = async () => {
// TODO: possible?
return ""
getStructuredDiff = async (base: string, head: string): Promise<BitBucketServerDiff[]> => {
const { repoSlug } = this.repoMetadata
const path = `rest/api/1.0/${repoSlug}/compare/diff?withComments=false&from=${base}&to=${head}`
const res = await this.get(path)
throwIfNotOk(res)
return (await res.json()).diffs
}

getPullRequestDiff = async (): Promise<BitBucketServerDiff[]> => {
const path = `${this.getPRBasePath()}/diff?withComments=false`
const res = await this.get(path)
throwIfNotOk(res)
return (await res.json()).diffs
}

getPullRequestComments = async (): Promise<BitBucketServerPRActivity[]> => {
Expand Down Expand Up @@ -119,10 +130,13 @@ export class BitBucketServerAPI {
.filter(comment => v.includes(comment!.text, dangerSignaturePostfix))
}

getFileContents = async (filePath: string) => {
const { repoSlug } = this.repoMetadata
const path = `${repoSlug}/` + `raw/${filePath}` + `?at=${this.pr.toRef.id}`
const res = await this.get(path)
getFileContents = async (filePath: string, repoSlug: string, refspec: string) => {
const path = `${repoSlug}/` + `raw/${filePath}` + `?at=${refspec}`
const res = await this.get(path, undefined, true)
if (res.status === 404) {
return ""
}
throwIfNotOk(res)
return await res.text()
}

Expand Down Expand Up @@ -193,8 +207,8 @@ export class BitBucketServerAPI {
)
}

get = (path: string, headers: any = {}, body: any = {}): Promise<node_fetch.Response> =>
this.api(path, headers, body, "GET")
get = (path: string, headers: any = {}, suppressErrors?: boolean): Promise<node_fetch.Response> =>
this.api(path, headers, undefined, "GET", suppressErrors)

post = (path: string, headers: any = {}, body: any = {}, suppressErrors?: boolean): Promise<node_fetch.Response> =>
this.api(path, headers, JSON.stringify(body), "POST", suppressErrors)
Expand Down
49 changes: 43 additions & 6 deletions source/platforms/bitbucket_server/BitBucketServerGit.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { GitDSL, GitJSONDSL } from "../../dsl/GitDSL"
import { BitBucketServerCommit, BitBucketServerDSL } from "../../dsl/BitBucketServerDSL"
import { BitBucketServerCommit, BitBucketServerDSL, BitBucketServerDiff } from "../../dsl/BitBucketServerDSL"
import { GitCommit } from "../../dsl/Commit"

import { BitBucketServerAPI } from "../bitbucket_server/BitBucketServerAPI"

import { diffToGitJSONDSL } from "../git/diffToGitJSONDSL"
import { GitJSONToGitDSLConfig, gitJSONToGitDSL } from "../git/gitJSONToGitDSL"
import { GitJSONToGitDSLConfig, gitJSONToGitDSL, GitStructuredDiff, Chunk } from "../git/gitJSONToGitDSL"

import * as debug from "debug"
import { EOL } from "os"
import { RepoMetaData } from "../../ci_source/ci_source"
const d = debug("danger:BitBucketServerGit")

Expand Down Expand Up @@ -49,7 +49,7 @@ export default async function gitDSLForBitBucketServer(api: BitBucketServerAPI):
const commits = gitCommits.map(commit =>
bitBucketServerCommitToGitCommit(commit, api.repoMetadata, api.repoCredentials.host)
)
return diffToGitJSONDSL(diff, commits)
return bitBucketServerDiffToGitJSONDSL(diff, commits)
}

export const bitBucketServerGitDSL = (
Expand All @@ -58,13 +58,50 @@ export const bitBucketServerGitDSL = (
bitBucketServerAPI: BitBucketServerAPI
): GitDSL => {
const config: GitJSONToGitDSLConfig = {
repo: `${bitBucketServer.pr.fromRef.repository.project.key}/${bitBucketServer.pr.fromRef.repository.slug}`,
repo:
`projects/${bitBucketServer.pr.fromRef.repository.project.key}/` +
`repos/${bitBucketServer.pr.fromRef.repository.slug}`,
baseSHA: bitBucketServer.pr.fromRef.latestCommit,
headSHA: bitBucketServer.pr.toRef.latestCommit,
getFileContents: bitBucketServerAPI.getFileContents,
getFullDiff: bitBucketServerAPI.getPullRequestDiff,
getFullStructuredDiff: async (base: string, head: string) => {
const diff = await bitBucketServerAPI.getStructuredDiff(base, head)
return bitBucketServerDiffToGitStructuredDiff(diff)
},
}

d("Setting up git DSL with: ", config)
return gitJSONToGitDSL(json, config)
}

const bitBucketServerDiffToGitJSONDSL = (diffs: BitBucketServerDiff[], commits: GitCommit[]): GitJSONDSL => {
const deleted_files = diffs.filter(diff => diff.source && !diff.destination).map(diff => diff.source!.toString)
const created_files = diffs.filter(diff => !diff.source && diff.destination).map(diff => diff.destination!.toString)
const modified_files = diffs.filter(diff => diff.source && diff.destination).map(diff => diff.destination!.toString)

return {
modified_files,
created_files,
deleted_files,
commits,
}
}

const bitBucketServerDiffToGitStructuredDiff = (diffs: BitBucketServerDiff[]): GitStructuredDiff => {
const chunks: Chunk[] = []

for (const diff of diffs) {
for (const hunk of diff.hunks) {
chunks.push({
from: diff.source && diff.source.toString,
to: diff.destination && diff.destination.toString,
changes: hunk.segments.map(segment => ({
type: segment.type === "ADDED" ? ("add" as "add") : ("del" as "del"),
content: segment.lines.map(({ line }) => line).join(EOL),
})),
})
}
}

return { chunks }
}
3 changes: 2 additions & 1 deletion source/platforms/git/diffToGitJSONDSL.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as parseDiff from "parse-diff"
import { includes } from "lodash"
import { GitCommit } from "../../dsl/Commit"
import { GitJSONDSL } from "../../dsl/GitDSL"

/**
* This function is essentially a "go from a diff to some simple structured data"
* it's the steps needed for danger process.
*/

export const diffToGitJSONDSL = (diff: string, commits: GitCommit[]) => {
export const diffToGitJSONDSL = (diff: string, commits: GitCommit[]): GitJSONDSL => {
const fileDiffs: any[] = parseDiff(diff)

const addedDiffs = fileDiffs.filter((diff: any) => diff["new"])
Expand Down
30 changes: 24 additions & 6 deletions source/platforms/git/gitJSONToGitDSL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,22 @@ export interface GitJSONToGitDSLConfig {
/** A promise which will return the string content of a file at a sha */
getFileContents: (path: string, repo: string | undefined, sha: string) => Promise<string>
/** A promise which will return the diff string content for a file between shas */
getFullDiff: (base: string, head: string) => Promise<string>
getFullDiff?: (base: string, head: string) => Promise<string>
getFullStructuredDiff?: (base: string, head: string) => Promise<GitStructuredDiff>
}

export interface GitStructuredDiff {
chunks: Chunk[]
}

export interface Chunk {
changes: Changes
from?: string
to?: string
}

export type Changes = { type: "add" | "del"; content: string }[]

export const gitJSONToGitDSL = (gitJSONRep: GitJSONDSL, config: GitJSONToGitDSLConfig): GitDSL => {
/**
* Takes a filename, and pulls from the PR the two versions of a file
Expand Down Expand Up @@ -133,18 +146,23 @@ export const gitJSONToGitDSL = (gitJSONRep: GitJSONDSL, config: GitJSONToGitDSLC
const byType = (t: string) => ({ type }: { type: string }) => type === t
const getContent = ({ content }: { content: string }) => content

type Changes = { type: string; content: string }[]

/**
* Gets the git-style diff for a single file.
*
* @param filename File path for the diff
*/
const diffForFile = async (filename: string) => {
const diff = await config.getFullDiff(config.baseSHA, config.headSHA)
let fileDiffs: GitStructuredDiff

const fileDiffs: any[] = parseDiff(diff)
const structuredDiff = fileDiffs.find((diff: any) => diff.from === filename || diff.to === filename)
if (config.getFullStructuredDiff) {
fileDiffs = await config.getFullStructuredDiff(config.baseSHA, config.headSHA)
} else {
const diff = await config.getFullDiff!(config.baseSHA, config.headSHA)
fileDiffs = { chunks: parseDiff(diff) }
}
const structuredDiff: GitStructuredDiff = {
chunks: fileDiffs.chunks.filter(diff => diff.from === filename || diff.to === filename),
}

if (!structuredDiff) {
return null
Expand Down
8 changes: 7 additions & 1 deletion source/platforms/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ export function getPlatformForEnv(env: Env, source: CISource, requireAuth = true
// BitBucket Server
const bbsHost = env["DANGER_BITBUCKETSERVER_HOST"]
if (bbsHost) {
const api = new BitBucketServerAPI(source, bitbucketServerRepoCredentialsFromEnv(env))
const api = new BitBucketServerAPI(
{
pullRequestID: source.pullRequestID,
repoSlug: source.repoSlug,
},
bitbucketServerRepoCredentialsFromEnv(env)
)
const bbs = new BitBucketServer(api)
return bbs
}
Expand Down
7 changes: 2 additions & 5 deletions source/runner/jsonToDSL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {

export const jsonToDSL = async (dsl: DangerDSLJSONType): Promise<DangerDSLType> => {
const api = apiForDSL(dsl)
const platformExists = [dsl.github].some(p => !!p)
const platformExists = [dsl.github, dsl.bitbucket_server].some(p => !!p)
const github = dsl.github && githubJSONToGitHubDSL(dsl.github, api as GitHubNodeAPI)
const bitbucket_server = dsl.bitbucket_server
// const gitlab = dsl.gitlab && githubJSONToGitLabDSL(dsl.gitlab, api)
Expand Down Expand Up @@ -42,10 +42,7 @@ export const jsonToDSL = async (dsl: DangerDSLJSONType): Promise<DangerDSLType>

const apiForDSL = (dsl: DangerDSLJSONType): GitHubNodeAPI | BitBucketServerAPI => {
if (process.env["DANGER_BITBUCKETSERVER_HOST"]) {
return new BitBucketServerAPI(
{ repoSlug: "", pullRequestID: "" },
bitbucketServerRepoCredentialsFromEnv(process.env)
)
return new BitBucketServerAPI(dsl.bitbucket_server!.metadata, bitbucketServerRepoCredentialsFromEnv(process.env))
}

const api = new GitHubNodeAPI({
Expand Down

0 comments on commit c979c4f

Please sign in to comment.