Skip to content
This repository has been archived by the owner on Aug 16, 2024. It is now read-only.

Commit

Permalink
Add Bitbucket and Bitbucket Server support (#752)
Browse files Browse the repository at this point in the history
  • Loading branch information
vinokurig authored Jul 3, 2023
1 parent ea73e8b commit 99986b8
Show file tree
Hide file tree
Showing 23 changed files with 756 additions and 52 deletions.
8 changes: 6 additions & 2 deletions build/scripts/generate_devworkspace_templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ do
if [ -n "$devfile_url" ]; then
devfile_url=${devfile_url##*v2: }
devfile_url=${devfile_url%/}
devfile_repo=${devfile_url%/tree*}
name=$(basename "${devfile_repo}")
#generate a temporary devworkspace yaml to fetch git repository name and clone url.
npm_config_yes=true npx @eclipse-che/che-devworkspace-generator@${CHE_DEVWORKSPACE_GENERATOR_VERSION} \
--devfile-url:"${devfile_url}" \
--editor-entry:che-incubator/che-code/latest \
--output-file:"${dir}"temp.yaml
name=$(yq -r '.spec.template.projects[0].name' "${dir}temp.yaml" | sed -n '2 p')
project="${name}={{_INTERNAL_URL_}}/resources/v2/${name}.zip"

npm_config_yes=true npx @eclipse-che/che-devworkspace-generator@${CHE_DEVWORKSPACE_GENERATOR_VERSION} \
Expand Down
13 changes: 5 additions & 8 deletions build/scripts/update_devworkspace_templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@ source ./clone_and_zip.sh
mkdir -p /build/resources/v2/
for dir in /build/devfiles/*/
do
devfile_url=$(grep "v2:" "${dir}"meta.yaml) || :
if [ -n "$devfile_url" ]; then
devfile_url=${devfile_url##*v2: }
devfile_url=${devfile_url%/}
devfile_repo=${devfile_url%/tree*}
name=$(basename "${devfile_repo}")
clone_and_zip "${devfile_repo}" "${devfile_url##*/}" "/build/resources/v2/$name.zip"
fi
clone_url=$(yq -r '.spec.template.projects[0].git.remotes.origin' "${dir}temp.yaml" | sed -n '2 p')
revision=$(yq -r '.spec.template.projects[0].git.checkoutFrom.revision' "${dir}temp.yaml" | sed -n '2 p')
name=$(yq -r '.spec.template.projects[0].name' "${dir}temp.yaml" | sed -n '2 p')
clone_and_zip "${clone_url}" "${revision}" "/build/resources/v2/$name.zip"
rm "${dir}temp.yaml"
done
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**********************************************************************
* Copyright (c) 2023 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/
import { ContainerModule, interfaces } from 'inversify';

import { BitbucketServerResolver } from './bitbucket-server-resolver';
import { TYPES } from '../types';

const { Resolver } = TYPES;

const bitbucketServerModule = new ContainerModule((bind: interfaces.Bind) => {
bind(Resolver).to(BitbucketServerResolver).inSingletonScope();
});

export { bitbucketServerModule };
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**********************************************************************
* Copyright (c) 2023 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

import { BitbucketServerUrl } from './bitbucket-server-url';
import { injectable } from 'inversify';
import { Url } from '../resolve/url';
import { Resolver } from '../resolve/resolver';

@injectable()
export class BitbucketServerResolver implements Resolver {
// eslint-disable-next-line max-len
static readonly BITBUCKET_URL_PATTERNS: RegExp[] = [
/^(?<scheme>https?):\/\/(?<host>.*)\/scm\/~(?<user>[^\/]+)\/(?<repo>.*).git$/,
/^(?<scheme>https?):\/\/(?<host>.*)\/users\/(?<user>[^\/]+)\/repos\/(?<repo>[^\/]+)\/browse(\?at=(?<branch>.*))?$/,
/^(?<scheme>https?):\/\/(?<host>.*)\/scm\/(?<project>[^\/~]+)\/(?<repo>[^\/]+).git$/,
/^(?<scheme>https?):\/\/(?<host>.*)\/projects\/(?<project>[^\/]+)\/repos\/(?<repo>[^\/]+)\/browse(\?at=(?<branch>.*))?$/,
];

isValid(url: string): boolean {
return BitbucketServerResolver.BITBUCKET_URL_PATTERNS.some(p => p.test(url));
}

resolve(url: string): Url {
const regExp = BitbucketServerResolver.BITBUCKET_URL_PATTERNS.find(p => p.test(url));
if (!regExp) {
throw new Error(`Invalid bitbucket-server URL: ${url}`);
}
const match = regExp.exec(url);
const scheme = this.getGroup(match, 'scheme');
const hostName = this.getGroup(match, 'host');
const user = this.getGroup(match, 'user');
const project = this.getGroup(match, 'project');
const repo = this.getGroup(match, 'repo');
let branch = this.getGroup(match, 'branch');
if (branch !== undefined && branch.startsWith('refs%2Fheads%2F')) {
branch = branch.substring(15);
}
return new BitbucketServerUrl(scheme, hostName, user, project, repo, branch);
}

private getGroup(match: RegExpExecArray, groupName: string): string | undefined {
if (match.groups && match.groups[groupName]) {
return match.groups[groupName];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2023 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

import { Url } from '../resolve/url';

export class BitbucketServerUrl implements Url {
constructor(
private readonly scheme: string,
private readonly hostName: string,
private readonly user: string | undefined,
private readonly project: string | undefined,
private readonly repo: string,
private readonly branch: string | undefined
) {}

getContentUrl(path: string): string {
const isUser = this.user !== undefined;
return `${this.scheme}://${this.hostName}/${isUser ? 'users' : 'projects'}/${
isUser ? this.user : this.project
}/repos/${this.repo}/raw/${path}${this.branch !== undefined ? '?/at=' + this.branch : ''}`;
}

getUrl(): string {
const isUser = this.user !== undefined;
return `${this.scheme}://${this.hostName}/${isUser ? 'users' : 'projects'}/${
isUser ? this.user : this.project
}/repos/${this.repo}${this.branch !== undefined ? '/browse?at=' + this.branch : ''}`;
}

getCloneUrl(): string {
const isUser = this.user !== undefined;
return `${this.scheme}://${this.hostName}/scm/${isUser ? '~' + this.user : this.project.toLowerCase()}/${
this.repo
}.git`;
}

getRepoName(): string {
return this.repo;
}

getBranchName(): string {
return this.branch;
}
}
21 changes: 21 additions & 0 deletions tools/devworkspace-generator/src/bitbucket/bitbucket-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**********************************************************************
* Copyright (c) 2023 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/
import { ContainerModule, interfaces } from 'inversify';

import { BitbucketResolver } from './bitbucket-resolver';
import { TYPES } from '../types';

const { Resolver } = TYPES;

const bitbucketModule = new ContainerModule((bind: interfaces.Bind) => {
bind(Resolver).to(BitbucketResolver).inSingletonScope();
});

export { bitbucketModule };
46 changes: 46 additions & 0 deletions tools/devworkspace-generator/src/bitbucket/bitbucket-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**********************************************************************
* Copyright (c) 2023 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

import { BitbucketUrl } from './bitbucket-url';
import { injectable } from 'inversify';
import { Url } from '../resolve/url';
import { Resolver } from '../resolve/resolver';

@injectable()
export class BitbucketResolver implements Resolver {
// eslint-disable-next-line max-len
static readonly BITBUCKET_URL_PATTERN: RegExp =
/^https:\/\/.*@?bitbucket\.org\/(?<workspaceId>[^\/]+)\/(?<repoName>[^\/]+)(\/(src|branch)\/(?<branchName>[^\/]+))?\/?$/;

isValid(url: string): boolean {
return BitbucketResolver.BITBUCKET_URL_PATTERN.test(url);
}

resolve(url: string): Url {
const match = BitbucketResolver.BITBUCKET_URL_PATTERN.exec(url);
if (!match) {
throw new Error(`Invalid bitbucket URL: ${url}`);
}
const workspaceId = this.getGroup(match, 'workspaceId');
let repoName = this.getGroup(match, 'repoName');
if (/^[\w-][\w.-]*?\.git$/.test(repoName)) {
repoName = repoName.substring(0, repoName.length - 4);
}
const branchName = this.getGroup(match, 'branchName', 'HEAD');
return new BitbucketUrl(workspaceId, repoName, branchName);
}

private getGroup(match: RegExpExecArray, groupName: string, defaultValue?: string) {
if (match.groups && match.groups[groupName]) {
return match.groups[groupName];
}
return defaultValue;
}
}
41 changes: 41 additions & 0 deletions tools/devworkspace-generator/src/bitbucket/bitbucket-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**********************************************************************
* Copyright (c) 2023 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

import { Url } from '../resolve/url';

export class BitbucketUrl implements Url {
private static readonly BITBUCKET_URL = 'https://bitbucket.org';

constructor(
private readonly workspaceId: string,
private readonly repoName: string,
private readonly branchName: string
) {}

getContentUrl(path: string): string {
return `${BitbucketUrl.BITBUCKET_URL}/${this.workspaceId}/${this.repoName}/raw/${this.branchName}/${path}`;
}

getUrl(): string {
return `${BitbucketUrl.BITBUCKET_URL}/${this.workspaceId}/${this.repoName}/src/${this.branchName}`;
}

getCloneUrl(): string {
return `${BitbucketUrl.BITBUCKET_URL}/${this.workspaceId}/${this.repoName}.git`;
}

getRepoName(): string {
return this.repoName;
}

getBranchName(): string {
return this.branchName;
}
}
5 changes: 4 additions & 1 deletion tools/devworkspace-generator/src/github/github-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
import { ContainerModule, interfaces } from 'inversify';

import { GithubResolver } from './github-resolver';
import { TYPES } from '../types';

const { Resolver } = TYPES;

const githubModule = new ContainerModule((bind: interfaces.Bind) => {
bind(GithubResolver).toSelf().inSingletonScope();
bind(Resolver).to(GithubResolver).inSingletonScope();
});

export { githubModule };
20 changes: 13 additions & 7 deletions tools/devworkspace-generator/src/github/github-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,37 @@

import { GithubUrl } from './github-url';
import { injectable } from 'inversify';
import { Url } from '../resolve/url';
import { Resolver } from '../resolve/resolver';

/**
* Provides a github URL object allowing to interact
*/
@injectable()
export class GithubResolver {
export class GithubResolver implements Resolver {
// eslint-disable-next-line max-len
static readonly GITHUB_URL_PATTERN =
/^(?<scheme>https?):\/\/(?<host>github(\..+)?\.[^\/]+)\/(?<repoUser>[^\/]+)\/(?<repoName>[^\/]+)((\/)|\/(blob|tree)\/(?<branchName>[^\/]+)(?:\/(?<subFolder>.*))?)?$/;

resolve(link: string): GithubUrl {
isValid(url: string): boolean {
return GithubResolver.GITHUB_URL_PATTERN.test(url);
}

resolve(link: string): Url {
const match = GithubResolver.GITHUB_URL_PATTERN.exec(link);
if (!match) {
throw new Error(`Invalid github URL: ${link}`);
}
const scheme = this.getGroup(match, 'scheme');
const hostName = this.getGroup(match, 'host');
const repoUser = this.getGroup(match, 'repoUser');
const repoName = this.getGroup(match, 'repoName');
let repoName = this.getGroup(match, 'repoName');
if (/^[\w-][\w.-]*?\.git$/.test(repoName)) {
repoName = repoName.substring(0, repoName.length - 4);
}
const branchName = this.getGroup(match, 'branchName', 'HEAD');
const subFolder = this.getGroup(match, 'subFolder');
return new GithubUrl(scheme, hostName, repoUser, repoName, branchName, subFolder);
}

getGroup(match: RegExpExecArray, groupName: string, defaultValue?: string) {
private getGroup(match: RegExpExecArray, groupName: string, defaultValue?: string) {
if (match.groups && match.groups[groupName]) {
return match.groups[groupName];
}
Expand Down
7 changes: 3 additions & 4 deletions tools/devworkspace-generator/src/github/github-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

/**
* Provides helper methods on top of github URL to get for example raw content of get relative links
*/
export class GithubUrl {
import { Url } from '../resolve/url';

export class GithubUrl implements Url {
constructor(
private readonly scheme: string,
private readonly hostName: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import { Container } from 'inversify';
import { devfileModule } from '../devfile/devfile-module';
import { fetchModule } from '../fetch/fetch-module';
import { githubModule } from '../github/github-module';
import { resolveModule } from '../resolve/resolve-module';
import { pluginRegistryModule } from '../plugin-registry/plugin-registry-module';
import { bitbucketModule } from '../bitbucket/bitbucket-module';
import { bitbucketServerModule } from '../bitbucket-server/bitbucket-server-module';

/**
* Manage all bindings for inversify
Expand All @@ -28,6 +31,9 @@ export class InversifyBinding {
this.container.load(devfileModule);
this.container.load(fetchModule);
this.container.load(githubModule);
this.container.load(bitbucketModule);
this.container.load(bitbucketServerModule);
this.container.load(resolveModule);
this.container.load(pluginRegistryModule);

this.container.bind(Symbol.for('AxiosInstance')).toConstantValue(options.axiosInstance);
Expand Down
Loading

0 comments on commit 99986b8

Please sign in to comment.