-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add support for parsing pull requests from bitbucket
Signed-off-by: Yoriyasu Yano <430092+yorinasub17@users.noreply.github.com>
- Loading branch information
1 parent
5a7c49e
commit 0d65d22
Showing
8 changed files
with
648 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,74 @@ | ||
// Copyright (c) Fensak, LLC. | ||
// SPDX-License-Identifier: AGPL-3.0-or-later OR BUSL-1.1 | ||
|
||
import YAML from "yaml"; | ||
import toml from "toml"; | ||
import JSON5 from "json5"; | ||
|
||
import { IChangeSetMetadata, IPatch } from "../engine/patch_types.ts"; | ||
|
||
export enum SourcePlatform { | ||
GitHub = "gh", | ||
BitBucket = "bb", | ||
} | ||
|
||
/** | ||
* Represents a repository hosted on a remote VCS (e.g., GitHub or BitBucket). | ||
* @property owner The owner of the repository. | ||
* @property name The name of the repository. | ||
*/ | ||
export type Repository = { | ||
owner: string; | ||
name: string; | ||
}; | ||
|
||
/** | ||
* Represents the decoded patches for the Pull Request. This also includes a mapping from patch IDs to the URL to | ||
* retrieve the file contents. | ||
* @property patchList The list of file patches that are included in this PR. | ||
* @property patchFetchMap A mapping from a URL hash to the URL to fetch the contents for the file. The URL hash is | ||
* the sha256 hash of the URL with a random salt. | ||
*/ | ||
export type PullRequestPatches = { | ||
metadata: IChangeSetMetadata; | ||
patchList: IPatch[]; | ||
patchFetchMap: Record<string, URL>; | ||
}; | ||
|
||
// eslint-disable-next-line no-var,@typescript-eslint/no-explicit-any | ||
export type Parser = (s: string) => any; | ||
|
||
export function objectParserFromFilename(fname: string): Parser | null { | ||
// Get the file extension to determine the file type | ||
const m = /(?:\.([^.]+))?$/.exec(fname); | ||
if (m === null) { | ||
return null; | ||
} | ||
const ext = m[1]; | ||
|
||
const supportedObjectExtensions = ["json", "json5", "yaml", "yml", "toml"]; | ||
if (!supportedObjectExtensions.includes(ext)) { | ||
return null; | ||
} | ||
|
||
// At this point, we know the object can be parsed out of the file so start to pull down the contents. | ||
// eslint-disable-next-line no-var,@typescript-eslint/no-explicit-any | ||
switch (ext) { | ||
default: | ||
// Throw error becauset this should never happen given the check for supportedObjectExtensions. | ||
throw new Error(`unsupported file extension ${ext} for ${fname}`); | ||
|
||
case "json": | ||
return JSON.parse; | ||
|
||
case "json5": | ||
return JSON5.parse; | ||
|
||
case "yaml": | ||
case "yml": | ||
return YAML.parse; | ||
|
||
case "toml": | ||
return toml.parse; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Copyright (c) Fensak, LLC. | ||
// SPDX-License-Identifier: AGPL-3.0-or-later OR BUSL-1.1 | ||
|
||
import { expect, test } from "@jest/globals"; | ||
|
||
import { BitBucket } from "../bbstd/index.ts"; | ||
import { PatchOp, LineOp, IObjectDiff } from "../engine/patch_types.ts"; | ||
|
||
import type { Repository } from "./from.ts"; | ||
import { patchFromBitBucketPullRequest } from "./from_bitbucket.ts"; | ||
|
||
const bitbucket = new BitBucket(); | ||
const testRepo: Repository = { | ||
owner: "fensak-test", | ||
name: "test-fensak-automated-readme-only", | ||
}; | ||
|
||
test("a single file change from BitBucket is parsed correctly", async () => { | ||
// View PR at | ||
// https://bitbucket.org/fensak-test/test-fensak-automated-readme-only/pull-requests/1 | ||
const patches = await patchFromBitBucketPullRequest(bitbucket, testRepo, 1); | ||
expect(patches.metadata).toEqual({ | ||
sourceBranch: "test/update-readme", | ||
targetBranch: "main", | ||
linkedPRs: [], | ||
}); | ||
expect(patches.patchList.length).toEqual(1); | ||
|
||
// Check top level patch | ||
const patch = patches.patchList[0]; | ||
expect(patch.path).toEqual("README.md"); | ||
expect(patch.op).toEqual(PatchOp.Modified); | ||
expect(patch.diff.length).toEqual(1); | ||
expect(patch.objectDiff).toBeNull(); | ||
|
||
// Check patch hunks | ||
const hunk = patch.diff[0]; | ||
expect(hunk.originalStart).toEqual(1); | ||
expect(hunk.originalLength).toEqual(3); | ||
expect(hunk.updatedStart).toEqual(1); | ||
expect(hunk.updatedLength).toEqual(5); | ||
expect(hunk.diffOperations).toEqual([ | ||
{ | ||
op: LineOp.Untouched, | ||
text: "# test-fensak-automated", | ||
newText: "", | ||
}, | ||
{ | ||
op: LineOp.Untouched, | ||
text: "", | ||
newText: "", | ||
}, | ||
{ | ||
op: LineOp.Untouched, | ||
text: "A test repo for automated testing.", | ||
newText: "", | ||
}, | ||
{ | ||
op: LineOp.Insert, | ||
text: "", | ||
newText: "", | ||
}, | ||
{ | ||
op: LineOp.Insert, | ||
text: "<!-- nonce change -->", | ||
newText: "", | ||
}, | ||
]); | ||
}); | ||
|
||
test("single config file change from BitBucket is parsed correctly", async () => { | ||
// View PR at | ||
// https://bitbucket.org/fensak-test/test-fensak-automated-readme-only/pull-requests/2 | ||
const patches = await patchFromBitBucketPullRequest(bitbucket, testRepo, 2); | ||
expect(patches.metadata).toEqual({ | ||
sourceBranch: "test/update-conf", | ||
targetBranch: "main", | ||
linkedPRs: [], | ||
}); | ||
expect(patches.patchList.length).toEqual(1); | ||
|
||
// Check top level patch | ||
const patch = patches.patchList[0]; | ||
expect(patch.path).toEqual("conf.json"); | ||
expect(patch.op).toEqual(PatchOp.Modified); | ||
expect(patch.diff.length).toEqual(1); | ||
|
||
// Check patch hunks | ||
const hunk = patch.diff[0]; | ||
expect(hunk.originalStart).toEqual(1); | ||
expect(hunk.originalLength).toEqual(3); | ||
expect(hunk.updatedStart).toEqual(1); | ||
expect(hunk.updatedLength).toEqual(3); | ||
expect(hunk.diffOperations).toEqual([ | ||
{ | ||
op: LineOp.Untouched, | ||
text: "{", | ||
newText: "", | ||
}, | ||
{ | ||
op: LineOp.Modified, | ||
text: ` "my-config": true`, | ||
newText: ` "my-config": false`, | ||
}, | ||
{ | ||
op: LineOp.Untouched, | ||
text: "}", | ||
newText: "", | ||
}, | ||
]); | ||
|
||
// Check object diffs | ||
const maybeObjDiff = patch.objectDiff; | ||
expect(maybeObjDiff).not.toBeNull(); | ||
const objDiff = maybeObjDiff as IObjectDiff; | ||
expect(objDiff.previous).toEqual({ | ||
"my-config": true, | ||
}); | ||
expect(objDiff.current).toEqual({ | ||
"my-config": false, | ||
}); | ||
expect(objDiff.diff).toEqual([ | ||
{ | ||
type: "CHANGE", | ||
path: ["my-config"], | ||
value: false, | ||
oldValue: true, | ||
}, | ||
]); | ||
}); |
Oops, something went wrong.