-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[bitbucket] enable projects #7251
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/** | ||
* Copyright (c) 2020 Gitpod GmbH. All rights reserved. | ||
* Licensed under the Gitpod Enterprise Source Code License, | ||
* See License.enterprise.txt in the project root folder. | ||
*/ | ||
|
||
import { AuthProviderInfo, ProviderRepository, User } from "@gitpod/gitpod-protocol"; | ||
import { inject, injectable } from "inversify"; | ||
import { TokenProvider } from "../../../src/user/token-provider"; | ||
import { Bitbucket } from "bitbucket"; | ||
import { URL } from "url"; | ||
|
||
@injectable() | ||
export class BitbucketAppSupport { | ||
|
||
@inject(TokenProvider) protected readonly tokenProvider: TokenProvider; | ||
|
||
async getProviderRepositoriesForUser(params: { user: User, provider: AuthProviderInfo }): Promise<ProviderRepository[]> { | ||
const token = await this.tokenProvider.getTokenForHost(params.user, params.provider.host); | ||
const oauthToken = token.value; | ||
|
||
const api = new Bitbucket({ | ||
baseUrl: `https://api.${params.provider.host}/2.0`, | ||
auth: { | ||
token: oauthToken | ||
} | ||
}); | ||
|
||
const result: ProviderRepository[] = []; | ||
const ownersRepos: ProviderRepository[] = []; | ||
|
||
const identity = params.user.identities.find(i => i.authProviderId === params.provider.authProviderId); | ||
if (!identity) { | ||
return result; | ||
} | ||
const usersBitbucketAccount = identity.authName; | ||
|
||
const workspaces = (await api.workspaces.getWorkspaces({ pagelen: 100 })).data.values?.map(w => w.slug!) || []; | ||
|
||
const reposPromise = Promise.all(workspaces.map(workspace => api.repositories.list({ | ||
workspace, | ||
pagelen: 100, | ||
role: "admin" // installation of webhooks is allowed for admins only | ||
}).catch(e => { | ||
console.error(e) | ||
}))); | ||
|
||
const reposInWorkspace = await reposPromise; | ||
for (const repos of reposInWorkspace) { | ||
if (repos) { | ||
for (const repo of (repos.data.values || [])) { | ||
let cloneUrl = repo.links!.clone!.find((x: any) => x.name === "https")!.href!; | ||
if (cloneUrl) { | ||
const url = new URL(cloneUrl); | ||
url.username = ''; | ||
cloneUrl = url.toString(); | ||
} | ||
const fullName = repo.full_name!; | ||
const updatedAt = repo.updated_on!; | ||
const accountAvatarUrl = repo.links!.avatar?.href!; | ||
const account = fullName.split("/")[0]; | ||
|
||
(account === usersBitbucketAccount ? ownersRepos : result).push({ | ||
name: repo.name!, | ||
account, | ||
cloneUrl, | ||
updatedAt, | ||
accountAvatarUrl, | ||
}) | ||
} | ||
} | ||
} | ||
|
||
// put owner's repos first. the frontend will pick first account to continue with | ||
result.unshift(...ownersRepos); | ||
return result; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
|
||
import { Branch, CommitInfo, Repository, User } from "@gitpod/gitpod-protocol"; | ||
import { inject, injectable } from 'inversify'; | ||
import { URL } from "url"; | ||
import { RepoURL } from '../repohost/repo-url'; | ||
import { RepositoryProvider } from '../repohost/repository-provider'; | ||
import { BitbucketApiFactory } from './bitbucket-api-factory'; | ||
|
@@ -18,26 +19,82 @@ export class BitbucketRepositoryProvider implements RepositoryProvider { | |
async getRepo(user: User, owner: string, name: string): Promise<Repository> { | ||
const api = await this.apiFactory.create(user); | ||
const repo = (await api.repositories.get({ workspace: owner, repo_slug: name })).data; | ||
const cloneUrl = repo.links!.clone!.find((x: any) => x.name === "https")!.href!; | ||
let cloneUrl = repo.links!.clone!.find((x: any) => x.name === "https")!.href!; | ||
if (cloneUrl) { | ||
const url = new URL(cloneUrl); | ||
url.username = ''; | ||
cloneUrl = url.toString(); | ||
} | ||
const host = RepoURL.parseRepoUrl(cloneUrl)!.host; | ||
const description = repo.description; | ||
const avatarUrl = repo.owner!.links!.avatar!.href; | ||
const webUrl = repo.links!.html!.href; | ||
return { host, owner, name, cloneUrl, description, avatarUrl, webUrl }; | ||
} | ||
|
||
async getBranch(user: User, owner: string, repo: string, branch: string): Promise<Branch> { | ||
// todo | ||
throw new Error("not implemented"); | ||
async getBranch(user: User, owner: string, repo: string, branchName: string): Promise<Branch> { | ||
const api = await this.apiFactory.create(user); | ||
const response = await api.repositories.getBranch({ | ||
workspace: owner, | ||
repo_slug: repo, | ||
name: branchName | ||
}) | ||
|
||
const branch = response.data; | ||
|
||
return { | ||
htmlUrl: branch.links?.html?.href!, | ||
name: branch.name!, | ||
commit: { | ||
sha: branch.target?.hash!, | ||
author: branch.target?.author?.user?.display_name!, | ||
authorAvatarUrl: branch.target?.author?.user?.links?.avatar?.href, | ||
authorDate: branch.target?.date!, | ||
commitMessage: branch.target?.message || "missing commit message", | ||
} | ||
}; | ||
} | ||
|
||
async getBranches(user: User, owner: string, repo: string): Promise<Branch[]> { | ||
// todo | ||
return []; | ||
const branches: Branch[] = []; | ||
const api = await this.apiFactory.create(user); | ||
const response = await api.repositories.listBranches({ | ||
workspace: owner, | ||
repo_slug: repo, | ||
sort: "target.date" | ||
}) | ||
|
||
for (const branch of response.data.values!) { | ||
branches.push({ | ||
htmlUrl: branch.links?.html?.href!, | ||
name: branch.name!, | ||
commit: { | ||
sha: branch.target?.hash!, | ||
author: branch.target?.author?.user?.display_name!, | ||
authorAvatarUrl: branch.target?.author?.user?.links?.avatar?.href, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: So jealous the commit author avatar is displayed for Bitbucket repositories but not for GitLab and GitHub repositories. 😭 question: Definitely out of the scope of this PR, but is this as simple to fix for GitLab and GitHub repositories? Happy to also open a follow up issue for this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unfortunately, nope! they don't carry it like here. |
||
authorDate: branch.target?.date!, | ||
commitMessage: branch.target?.message || "missing commit message", | ||
} | ||
}); | ||
} | ||
|
||
return branches; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: Although branches are successfully retrieved, we There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fyi: Opened #7366 to track this. Cc @AlexTugarev @jldec |
||
} | ||
|
||
async getCommitInfo(user: User, owner: string, repo: string, ref: string): Promise<CommitInfo | undefined> { | ||
// todo | ||
return undefined; | ||
const api = await this.apiFactory.create(user); | ||
const response = await api.commits.get({ | ||
workspace: owner, | ||
repo_slug: repo, | ||
commit: ref | ||
}) | ||
const commit = response.data; | ||
return { | ||
sha: commit.hash!, | ||
author: commit.author?.user?.display_name!, | ||
authorDate: commit.date!, | ||
commitMessage: commit.message || "missing commit message", | ||
authorAvatarUrl: commit.author?.user?.links?.avatar?.href, | ||
}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: This could be considered out of the scope of this PR, but in contrast with GitLab and GitHub repositories, adding a Bitbucket repository🅰️ does not add a webhook for enabling prebuilds and 🅱️ does not automatically trigger a first prebuilds after adding the project.
🍊 🍊 🍊 🍊
thought: This is also interesting to test and make it clearler if separating the steps adding a project and enabling prebuilds makes sense. Re-posting from #7031 (comment) for visibility:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I'll have a look into this. Thanks @gtsiolis!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fyi: Opened #7367 to track this. Cc @AlexTugarev @jldec