f2750ba645
Hopefully fixes https://www.pivotaltracker.com/story/show/141245817
363 lines
No EOL
11 KiB
TypeScript
363 lines
No EOL
11 KiB
TypeScript
import { Input, Output, Component } from 'angular-ts-decorators';
|
|
import * as moment from 'moment';
|
|
|
|
|
|
/**
|
|
* A component that lets the user set up a build trigger for a public Git repository host service.
|
|
*/
|
|
@Component({
|
|
selector: 'manageTriggerGithost',
|
|
templateUrl: '/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html'
|
|
})
|
|
export class ManageTriggerGithostComponent implements ng.IComponentController {
|
|
|
|
// FIXME: Use one-way data binding
|
|
@Input('=') public repository: any;
|
|
@Input('=') public trigger: Trigger;
|
|
@Output() public activateTrigger: (trigger: {config: any, pull_robot: any}) => void;
|
|
private config: any;
|
|
private local: any = {
|
|
namespaceOptions: {
|
|
filter: '',
|
|
predicate: 'score',
|
|
reverse: false,
|
|
page: 0
|
|
},
|
|
repositoryOptions: {
|
|
filter: '',
|
|
predicate: 'score',
|
|
reverse: false,
|
|
page: 0,
|
|
hideStale: true
|
|
},
|
|
robotOptions: {
|
|
filter: '',
|
|
predicate: 'score',
|
|
reverse: false,
|
|
page: 0
|
|
}
|
|
};
|
|
private currentState: any | null;
|
|
private namespacesPerPage: number = 10;
|
|
private repositoriesPerPage: number = 10;
|
|
private robotsPerPage: number = 10;
|
|
private namespaceTitle: string;
|
|
private namespace: any;
|
|
|
|
constructor(private ApiService: any,
|
|
private TableService: any,
|
|
private TriggerService: any,
|
|
private RolesService: any,
|
|
private $scope: ng.IScope) {
|
|
// FIXME: Here binding methods to class context in order to pass them as arguments to $scope.$watch
|
|
this.buildOrderedNamespaces = this.buildOrderedNamespaces.bind(this);
|
|
this.loadNamespaces = this.loadNamespaces.bind(this);
|
|
this.buildOrderedRepositories = this.buildOrderedRepositories.bind(this);
|
|
this.loadRepositories = this.loadRepositories.bind(this);
|
|
this.loadRepositoryRefs = this.loadRepositoryRefs.bind(this);
|
|
this.buildOrderedRobotAccounts = this.buildOrderedRobotAccounts.bind(this);
|
|
this.loadDockerfileLocations = this.loadDockerfileLocations.bind(this);
|
|
this.checkDockerfilePath = this.checkDockerfilePath.bind(this);
|
|
}
|
|
|
|
public $onInit(): void {
|
|
// TODO: Replace $scope.$watch with @Output methods for child component mutations or $onChanges for parent mutations
|
|
this.$scope.$watch(() => this.trigger, this.initialSetup.bind(this));
|
|
this.$scope.$watch(() => this.repository, this.initialSetup.bind(this));
|
|
|
|
this.$scope.$watch(() => this.local.selectedNamespace, (namespace) => {
|
|
if (namespace) {
|
|
this.loadRepositories(namespace);
|
|
}
|
|
});
|
|
|
|
this.$scope.$watch(() => this.local.selectedRepository, (repository) => {
|
|
if (repository) {
|
|
this.loadRepositoryRefs(repository);
|
|
this.loadDockerfileLocations(repository);
|
|
}
|
|
});
|
|
|
|
this.$scope.$watch(() => this.local.dockerfilePath, (path) => {
|
|
if (path && this.local.selectedRepository) {
|
|
this.checkDockerfilePath(this.local.selectedRepository, path);
|
|
}
|
|
});
|
|
|
|
this.$scope.$watch(() => this.local.namespaceOptions.predicate, this.buildOrderedNamespaces);
|
|
this.$scope.$watch(() => this.local.namespaceOptions.reverse, this.buildOrderedNamespaces);
|
|
this.$scope.$watch(() => this.local.namespaceOptions.filter, this.buildOrderedNamespaces);
|
|
this.$scope.$watch(() => this.local.repositoryOptions.predicate, this.buildOrderedRepositories);
|
|
this.$scope.$watch(() => this.local.repositoryOptions.reverse, this.buildOrderedRepositories);
|
|
this.$scope.$watch(() => this.local.repositoryOptions.filter, this.buildOrderedRepositories);
|
|
this.$scope.$watch(() => this.local.repositoryOptions.hideStale, this.buildOrderedRepositories);
|
|
this.$scope.$watch(() => this.local.robotOptions.predicate, this.buildOrderedRobotAccounts);
|
|
this.$scope.$watch(() => this.local.robotOptions.reverse, this.buildOrderedRobotAccounts);
|
|
this.$scope.$watch(() => this.local.robotOptions.filter, this.buildOrderedRobotAccounts);
|
|
}
|
|
|
|
private initialSetup(): void {
|
|
if (!this.repository || !this.trigger) { return; }
|
|
|
|
if (this.namespaceTitle) {
|
|
// Already setup.
|
|
return;
|
|
}
|
|
|
|
this.config = this.trigger['config'] || {};
|
|
this.namespaceTitle = 'organization';
|
|
this.local.selectedNamespace = null;
|
|
this.loadNamespaces();
|
|
}
|
|
|
|
public getTriggerIcon(): any {
|
|
return this.TriggerService.getIcon(this.trigger.service);
|
|
}
|
|
|
|
public createTrigger(): void {
|
|
var config: any = {
|
|
build_source: this.local.selectedRepository.full_name,
|
|
subdir: this.local.dockerfilePath.substr(1) // Remove starting /
|
|
};
|
|
|
|
if (this.local.triggerOptions.hasBranchTagFilter &&
|
|
this.local.triggerOptions.branchTagFilter) {
|
|
config['branchtag_regex'] = this.local.triggerOptions.branchTagFilter;
|
|
}
|
|
|
|
var activate = () => {
|
|
this.activateTrigger({'config': config, 'pull_robot': this.local.robotAccount});
|
|
};
|
|
|
|
if (this.local.robotAccount) {
|
|
if (this.local.robotAccount.can_read) {
|
|
activate();
|
|
} else {
|
|
// Add read permission onto the base repository for the robot and then activate the
|
|
// trigger.
|
|
var robot_name = this.local.robotAccount.name;
|
|
this.RolesService.setRepositoryRole(this.repository, 'read', 'robot', robot_name, activate);
|
|
}
|
|
} else {
|
|
activate();
|
|
}
|
|
}
|
|
|
|
private buildOrderedNamespaces(): void {
|
|
if (!this.local.namespaces) {
|
|
return;
|
|
}
|
|
|
|
var namespaces = this.local.namespaces || [];
|
|
this.local.orderedNamespaces = this.TableService.buildOrderedItems(namespaces,
|
|
this.local.namespaceOptions,
|
|
['id'],
|
|
['score']);
|
|
|
|
this.local.maxScore = 0;
|
|
namespaces.forEach((namespace) => {
|
|
this.local.maxScore = Math.max(namespace.score, this.local.maxScore);
|
|
});
|
|
}
|
|
|
|
private loadNamespaces(): void {
|
|
this.local.namespaces = null;
|
|
this.local.selectedNamespace = null;
|
|
this.local.orderedNamespaces = null;
|
|
|
|
this.local.selectedRepository = null;
|
|
this.local.orderedRepositories = null;
|
|
|
|
var params = {
|
|
'repository': this.repository.namespace + '/' + this.repository.name,
|
|
'trigger_uuid': this.trigger.id
|
|
};
|
|
|
|
this.ApiService.listTriggerBuildSourceNamespaces(null, params)
|
|
.then((resp) => {
|
|
this.local.namespaces = resp['namespaces'];
|
|
this.local.repositories = null;
|
|
this.buildOrderedNamespaces();
|
|
}, this.ApiService.errorDisplay('Could not retrieve the list of ' + this.namespaceTitle));
|
|
}
|
|
|
|
private buildOrderedRepositories(): void {
|
|
if (!this.local.repositories) {
|
|
return;
|
|
}
|
|
|
|
var repositories = this.local.repositories || [];
|
|
repositories.forEach((repository) => {
|
|
repository['last_updated_datetime'] = new Date(repository['last_updated'] * 1000);
|
|
});
|
|
|
|
if (this.local.repositoryOptions.hideStale) {
|
|
var existingRepositories = repositories;
|
|
|
|
repositories = repositories.filter((repository) => {
|
|
var older_date = moment(repository['last_updated_datetime']).add(1, 'months');
|
|
return !moment().isAfter(older_date);
|
|
});
|
|
|
|
if (existingRepositories.length > 0 && repositories.length == 0) {
|
|
repositories = existingRepositories;
|
|
}
|
|
}
|
|
|
|
this.local.orderedRepositories = this.TableService.buildOrderedItems(repositories,
|
|
this.local.repositoryOptions,
|
|
['name', 'description'],
|
|
[]);
|
|
}
|
|
|
|
private loadRepositories(namespace: any): void {
|
|
this.local.repositories = null;
|
|
this.local.selectedRepository = null;
|
|
this.local.repositoryRefs = null;
|
|
this.local.triggerOptions = {
|
|
'hasBranchTagFilter': false
|
|
};
|
|
|
|
this.local.orderedRepositories = null;
|
|
|
|
var params = {
|
|
'repository': this.repository.namespace + '/' + this.repository.name,
|
|
'trigger_uuid': this.trigger.id
|
|
};
|
|
|
|
var data = {
|
|
'namespace': namespace.id
|
|
};
|
|
|
|
this.ApiService.listTriggerBuildSources(data, params).then((resp) => {
|
|
if (namespace == this.local.selectedNamespace) {
|
|
this.local.repositories = resp['sources'];
|
|
this.buildOrderedRepositories();
|
|
}
|
|
}, this.ApiService.errorDisplay('Could not retrieve repositories'));
|
|
}
|
|
|
|
private loadRepositoryRefs(repository: any): void {
|
|
this.local.repositoryRefs = null;
|
|
this.local.triggerOptions = {
|
|
'hasBranchTagFilter': false
|
|
};
|
|
|
|
var params = {
|
|
'repository': this.repository.namespace + '/' + this.repository.name,
|
|
'trigger_uuid': this.trigger.id,
|
|
'field_name': 'refs'
|
|
};
|
|
|
|
var config = {
|
|
'build_source': repository.full_name
|
|
};
|
|
|
|
this.ApiService.listTriggerFieldValues(config, params).then((resp) => {
|
|
if (repository == this.local.selectedRepository) {
|
|
this.local.repositoryRefs = resp['values'];
|
|
this.local.repositoryFullRefs = resp['values'].map((ref) => {
|
|
var kind = ref.kind == 'branch' ? 'heads' : 'tags';
|
|
var icon = ref.kind == 'branch' ? 'fa-code-fork' : 'fa-tag';
|
|
return {
|
|
'value': kind + '/' + ref.name,
|
|
'icon': icon,
|
|
'title': ref.name
|
|
};
|
|
});
|
|
}
|
|
}, this.ApiService.errorDisplay('Could not retrieve repository refs'));
|
|
}
|
|
|
|
private loadDockerfileLocations(repository: any): void {
|
|
this.local.dockerfilePath = null;
|
|
|
|
var params = {
|
|
'repository': this.repository.namespace + '/' + this.repository.name,
|
|
'trigger_uuid': this.trigger.id
|
|
};
|
|
|
|
var config = {
|
|
'build_source': repository.full_name
|
|
};
|
|
|
|
this.ApiService.listBuildTriggerSubdirs(config, params)
|
|
.then((resp) => {
|
|
if (repository == this.local.selectedRepository) {
|
|
this.local.dockerfileLocations = resp;
|
|
}
|
|
}, this.ApiService.errorDisplay('Could not retrieve Dockerfile locations'));
|
|
}
|
|
|
|
private buildOrderedRobotAccounts(): void {
|
|
if (!this.local.triggerAnalysis || !this.local.triggerAnalysis.robots) {
|
|
return;
|
|
}
|
|
|
|
var robots = this.local.triggerAnalysis.robots;
|
|
this.local.orderedRobotAccounts = this.TableService.buildOrderedItems(robots,
|
|
this.local.robotOptions,
|
|
['name'],
|
|
[]);
|
|
}
|
|
|
|
private checkDockerfilePath(repository: any, path: string): void {
|
|
this.local.triggerAnalysis = null;
|
|
this.local.robotAccount = null;
|
|
|
|
var params = {
|
|
'repository': this.repository.namespace + '/' + this.repository.name,
|
|
'trigger_uuid': this.trigger.id
|
|
};
|
|
|
|
var config = {
|
|
'build_source': repository.full_name,
|
|
'subdir': path.substr(1)
|
|
};
|
|
|
|
var data = {
|
|
'config': config
|
|
};
|
|
|
|
this.ApiService.analyzeBuildTrigger(data, params)
|
|
.then((resp) => {
|
|
this.local.triggerAnalysis = resp;
|
|
this.buildOrderedRobotAccounts();
|
|
}, this.ApiService.errorDisplay('Could not analyze trigger'));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* A type representing local application data.
|
|
*/
|
|
export type Local = {
|
|
namespaceOptions: {
|
|
filter: string;
|
|
predicate: string;
|
|
reverse: boolean;
|
|
page: number;
|
|
};
|
|
repositoryOptions: {
|
|
filter: string;
|
|
predicate: string;
|
|
reverse: boolean;
|
|
page: number;
|
|
hideStale: boolean;
|
|
};
|
|
robotOptions: {
|
|
filter: string;
|
|
predicate: string;
|
|
reverse: boolean;
|
|
page: number;
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
* A type representing a trigger.
|
|
*/
|
|
export type Trigger = {
|
|
id: number;
|
|
service: any;
|
|
}; |