Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface GitSCMSourceInfo extends DeploySource {
commit: string;
scm: string;
endpointGuid: string;
accessToken?: string;
}

// Structure used to provide metadata about the Git Url source
Expand Down Expand Up @@ -253,7 +254,8 @@ export class DeployApplicationDeployer {
commit: appSource.gitDetails.commit,
url: appSource.gitDetails.url,
scm: appSource.type.id,
endpointGuid: appSource.gitDetails.endpointGuid
endpointGuid: appSource.gitDetails.endpointGuid,
accessToken: appSource.gitDetails.accessToken
};

const msg = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,22 @@
<div *ngIf="sourceType.group === 'gitscm'">
<div class="github-project-details">
<div>
<mat-form-field>
<input type="text" matInput [(ngModel)]="gh"
placeholder="GitHub Enterprise url (optional)" name="githubEnterpriseUrl">
</mat-form-field>
<mat-error
*ngIf="isInvalidGithubEnterpriseUrl">
GitHub Enterprise deployment url is not valid
</mat-error>
<mat-form-field>
<input type="text" matInput [(ngModel)]="accessToken"
placeholder="GitHub Access Token (optional)" name="githubAccessToken">
</mat-form-field>
<mat-form-field>
<input type="text" matInput [matAutocomplete]="auto" [disabled]="isRedeploy" [(ngModel)]="repository"
placeholder="Project" name="projectName"
[appGithubProjectExists]="sourceType.id + ',' + sourceType.endpointGuid" required>
[appGithubProjectExists]="sourceType.id + ',' + sourceType.endpointGuid + ',' + (accessToken || '')" required>
<!-- Repository auto complete helper -->
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
<mat-option *ngFor="let repo of suggestedRepos$ | async" [value]="repo.name">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import { getCommitGuid } from '../../../../../../git/src/store/git-entity-factor
import { DeployApplicationState, SourceType } from '../../../../store/types/deploy-application.types';
import { ApplicationDeploySourceTypes, DEPLOY_TYPES_IDS } from '../deploy-application-steps.types';
import { GitSuggestedRepo } from './../../../../../../git/src/store/git.public-types';
import { GitHubSCM } from 'frontend/packages/git/src/shared/scm/github-scm';
import { BaseSCM } from 'frontend/packages/git/src/shared/scm/scm-base';



Expand Down Expand Up @@ -98,6 +100,11 @@ export class DeployApplicationStep2Component
// We don't have any repositories to suggest initially - need user to start typing
suggestedRepos$: Observable<GitSuggestedRepo[]>;

// GitHub Enterprise/private repos
isInvalidGithubEnterpriseUrl: boolean;
accessToken: string;
// --------------

// Git URL
gitUrl: string;
gitUrlBranchName: string;
Expand Down Expand Up @@ -141,6 +148,7 @@ export class DeployApplicationStep2Component
projectName: this.repository,
branch: this.repositoryBranch,
url: repo.entity.clone_url,
accessToken: this.accessToken,
commit: this.isRedeploy ? this.commitInfo.sha : undefined,
endpointGuid: this.sourceType.endpointGuid,
}, null));
Expand Down Expand Up @@ -345,6 +353,21 @@ export class DeployApplicationStep2Component
this.subscriptions.push(setProjectName.subscribe());

this.suggestedRepos$ = this.sourceSelectionForm.valueChanges.pipe(
tap(form => {
const isValidUrl = (input: string) => { try { var url = new URL(input); return Boolean(url) } catch (e) { return false } }

this.isInvalidGithubEnterpriseUrl = form.githubEnterpriseUrl && !isValidUrl(form.githubEnterpriseUrl)

if (form.githubEnterpriseUrl && !this.isInvalidGithubEnterpriseUrl) {
(this.scm as unknown as BaseSCM).setPublicApi(form.githubEnterpriseUrl)
}

if (form.githubAccessToken) {
(this.scm as GitHubSCM).setAccessToken(form.githubAccessToken)
} else {
(this.scm as GitHubSCM).clearAccessToken()
}
}),
map(form => form.projectName),
startWith(''),
pairwise(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ApplicationDeploySourceTypes {
name: 'GitHub',
id: DEPLOY_TYPES_IDS.GITHUB,
group: 'gitscm',
helpText: 'Please select the GitHub project and branch you would like to deploy from.',
helpText: 'Please select the GitHub project and branch you would like to deploy from. If the GitHub repository is private or located on a GitHub Enterprise deployment, include an access token (and the GitHub Enterprise deployment url).',
graphic: {
// TODO: Move cf assets to CF package (#3769)
location: '/core/assets/endpoint-icons/github-logo.png',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ export class GithubProjectExistsDirective implements Validator {
return this.lastValue.length && this.lastValue.indexOf(name) === 0;
}

private getTypeAndEndpoint(): [GitSCMType, string] {
private getTypeAndEndpointWithAuth(): [GitSCMType, string, string] {
const res = this.appGithubProjectExists.split(',');
if (res.length === 2) {
return [res[0] as GitSCMType, res[1]];
if (res.length === 3) {
return [res[0] as GitSCMType, res[1], res[2]];
}
console.warn('appGithubProjectExists value should be `<scm type>,<endpoint guid>');
console.warn('appGithubProjectExists value should be `<scm type>,<endpoint guid>,<access_token>`');
return null;
}

Expand All @@ -64,7 +64,7 @@ export class GithubProjectExistsDirective implements Validator {
debounceTime(250),
tap(createAppState => {
if (createAppState.projectExists && createAppState.projectExists.name !== c.value) {
this.store.dispatch(new CheckProjectExists(this.scmService.getSCM(...this.getTypeAndEndpoint()), c.value));
this.store.dispatch(new CheckProjectExists(this.scmService.getSCM(...this.getTypeAndEndpointWithAuth()), c.value));
}
}),
filter(createAppState =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class GithubCommitsListConfigServiceDeploy extends GithubCommitsListConfi
map((appSource: DeployApplicationSource) => {
return (appSource.type.id === 'github' || appSource.type.id === 'gitlab') ? {
scm: appSource.type.id as GitSCMType,
accessToken: appSource.gitDetails.accessToken,
projectName: appSource.gitDetails.projectName,
sha: appSource.gitDetails.branch.name,
endpointGuid: appSource.gitDetails.endpointGuid
Expand All @@ -41,7 +42,7 @@ export class GithubCommitsListConfigServiceDeploy extends GithubCommitsListConfi
filter(fetchDetails => !!fetchDetails && !!fetchDetails.projectName && !!fetchDetails.sha),
first()
).subscribe(fetchDetails => {
const scm = scmService.getSCM(fetchDetails.scm, fetchDetails.endpointGuid);
const scm = scmService.getSCM(fetchDetails.scm, fetchDetails.endpointGuid, fetchDetails.accessToken);
this.dataSource = new GithubCommitsDataSource(this.store, this, scm, fetchDetails.projectName, fetchDetails.sha);
this.initialised.next(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export interface GitAppDetails {
projectName: string;
branch: GitBranch;
endpointGuid: string;
accessToken?: string;
commit?: string;
branchName?: string;
url?: string;
Expand Down
38 changes: 28 additions & 10 deletions src/frontend/packages/git/src/shared/scm/github-scm.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { flattenPagination } from '@stratosui/store';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { map, tap, switchMap } from 'rxjs/operators';

import { GitBranch, GitCommit, GitRepo } from '../../store/git.public-types';
import { GitSuggestedRepo } from './../../store/git.public-types';
Expand All @@ -14,12 +14,29 @@ import {
import { GitSCM, SCMIcon } from './scm';
import { BaseSCM, GitApiRequest } from './scm-base';
import { GitSCMType } from './scm.service';
import { HttpOptions } from 'frontend/packages/core/src/core/core.types';

export class GitHubSCM extends BaseSCM implements GitSCM {

constructor(gitHubURL: string, endpointGuid: string) {
private options: HttpOptions;

constructor(gitHubURL: string, endpointGuid: string, accessToken?: string) {
super(gitHubURL);
this.endpointGuid = endpointGuid;
if (accessToken && accessToken.trim() != "") {
this.setAccessToken(accessToken);
}
}

setAccessToken(input: string) {
this.options = new HttpOptions();
this.options.headers = {"Authorization": `Bearer ${input}`};
}

clearAccessToken() {
if (this.options) {
this.options.headers = {};
}
}

getType(): GitSCMType {
Expand All @@ -38,19 +55,20 @@ export class GitHubSCM extends BaseSCM implements GitSCM {
}

getRepository(httpClient: HttpClient, projectName: string): Observable<GitRepo> {
return this.getAPI().pipe(
switchMap(api => httpClient.get<GitRepo>(`${api.url}/repos/${projectName}`, api.requestArgs))
return this.getAPI(this.options).pipe(
switchMap(api => {
return httpClient.get<GitRepo>(`${api.url}/repos/${projectName}`, api.requestArgs)
})
);
}

getBranch(httpClient: HttpClient, projectName: string, branchName: string): Observable<GitBranch> {
return this.getAPI().pipe(
return this.getAPI(this.options).pipe(
switchMap(api => httpClient.get<GitBranch>(`${api.url}/repos/${projectName}/branches/${branchName}`, api.requestArgs))
);
}

getBranches(httpClient: HttpClient, projectName: string): Observable<GitBranch[]> {
return this.getAPI().pipe(
return this.getAPI(this.options).pipe(
switchMap(api => {
const url = `${api.url}/repos/${projectName}/branches`;
const config = new GithubFlattenerForArrayPaginationConfig<GitBranch>(httpClient, url, api.requestArgs);
Expand All @@ -71,7 +89,7 @@ export class GitHubSCM extends BaseSCM implements GitSCM {
}

getCommitApi(projectName: string, commitSha: string): Observable<GitApiRequest> {
return this.getAPI().pipe(
return this.getAPI(this.options).pipe(
map(api => ({
...api,
url: `${api.url}/repos/${projectName}/commits/${commitSha}`,
Expand All @@ -80,7 +98,7 @@ export class GitHubSCM extends BaseSCM implements GitSCM {
}

getCommits(httpClient: HttpClient, projectName: string, ref: string): Observable<GitCommit[]> {
return this.getAPI().pipe(
return this.getAPI(this.options).pipe(
switchMap(api => httpClient.get<GitCommit[]>(
`${api.url}/repos/${projectName}/commits?sha=${ref}`, {
...api.requestArgs,
Expand All @@ -98,7 +116,7 @@ export class GitHubSCM extends BaseSCM implements GitSCM {
}

getMatchingRepositories(httpClient: HttpClient, projectName: string): Observable<GitSuggestedRepo[]> {
return this.getAPI().pipe(
return this.getAPI(this.options).pipe(
switchMap(api => {
const prjParts = projectName.split('/');
let url = `${api.url}/search/repositories?q=${projectName}+in:name+fork:true`;
Expand Down
10 changes: 7 additions & 3 deletions src/frontend/packages/git/src/shared/scm/scm-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,29 @@ export abstract class BaseSCM {

constructor(public publicApiUrl: string) { }

public setPublicApi(url: string) {
this.publicApiUrl = url
}

public getPublicApi(): string {
return this.publicApiUrl;
}

public getAPI(): Observable<GitApiRequest> {
public getAPI(options: HttpOptions = new HttpOptions()): Observable<GitApiRequest> {
return this.getEndpoint(this.endpointGuid).pipe(
map(endpoint => {
if (!endpoint) {
// No endpoint, use the default or overwritten public api associated with this type
return {
url: this.getPublicApi(),
requestArgs: {}
requestArgs: options
};
}
// We have an endpoint so always proxy via backend
return {
url: `${commonPrefix}/${endpoint.guid}`,
requestArgs: {
... new HttpOptions(),
... options,
headers: {
'x-cap-no-token': `${!endpoint.user}`
}
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/packages/git/src/shared/scm/scm.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export class GitSCMService {
) {
}

public getSCM(type: GitSCMType, endpointGuid: string): GitSCM {
public getSCM(type: GitSCMType, endpointGuid: string, accessToken?: string): GitSCM {
switch (type) {
case 'github':
return new GitHubSCM(this.gitHubURL, endpointGuid);
return new GitHubSCM(this.gitHubURL, endpointGuid, accessToken);
case 'gitlab':
return new GitLabSCM(endpointGuid);
}
Expand Down
15 changes: 10 additions & 5 deletions src/jetstream/plugins/cfapppush/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,12 @@ func (cfAppPush *CFAppPush) getGitSCMSource(clientWebSocket *websocket.Conn, tem

log.Debugf("GitSCM SCM: %s, Source: %s, branch %s, url: %s", info.SCM, info.Project, info.Branch, loggerURL)
cloneDetails := CloneDetails{
Url: cloneURL,
LoggerUrl: loggerURL,
Branch: info.Branch,
Commit: info.CommitHash,
SkipSSL: skipSSL,
Url: cloneURL,
LoggerUrl: loggerURL,
Branch: info.Branch,
Commit: info.CommitHash,
SkipSSL: skipSSL,
AccessToken: info.AcccessToken,
}
info.CommitHash, err = cloneRepository(cloneDetails, clientWebSocket, tempDir)
if err != nil {
Expand Down Expand Up @@ -625,6 +626,10 @@ func cloneRepository(cloneDetails CloneDetails, clientWebSocket *websocket.Conn,

vcsGit := GetVCS()

if len(cloneDetails.AccessToken) > 0 {
vcsGit = GetVCS(withAccessToken(cloneDetails.AccessToken))
}

err := vcsGit.Create(cloneDetails.SkipSSL, tempDir, cloneDetails.Url, cloneDetails.Branch)
if err != nil {
log.Infof("Failed to clone repo %s due to %+v", cloneDetails.LoggerUrl, err)
Expand Down
12 changes: 7 additions & 5 deletions src/jetstream/plugins/cfapppush/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type GitSCMSourceInfo struct {
SCM string `json:"scm"`
EndpointGUID string `json:"endpointGuid"` // credentials of which to use, e.g. of a private GitHub instance
Username string `json:"username"` // GitLab username has to be supplied by the frontend
AcccessToken string `json:"accessToken"` // GitHub private repos/enterprise repos can supply a token through the frontend
}

// Structure used to provide metadata about the Git Url source
Expand Down Expand Up @@ -117,9 +118,10 @@ type Applications struct {
}

type CloneDetails struct {
Url string
LoggerUrl string
Branch string
Commit string
SkipSSL bool
Url string
LoggerUrl string
Branch string
Commit string
SkipSSL bool
AccessToken string
}
Loading