Merge branch 'master' into spi
Signed-off-by: Alexander Garagatyi <agaragatyi@codenvy.com>6.19.x
commit
7877e47f6a
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-agent-shared</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-agent</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>exec-agent</artifactId>
|
||||
<name>Agent :: Exec</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>go-agents</artifactId>
|
||||
<name>Agent :: Golang agents</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>ls-csharp-agent</artifactId>
|
||||
<name>Language Server C# Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>ls-json-agent</artifactId>
|
||||
<name>Language Server Json Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>ls-php-agent</artifactId>
|
||||
<name>Language Server PHP Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>ls-python-agent</artifactId>
|
||||
<name>Language Server python Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>ls-typescript-agent</artifactId>
|
||||
<name>Language Server typescript Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@
|
|||
<parent>
|
||||
<artifactId>che-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Che Agents Parent</name>
|
||||
<modules>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>ssh-agent</artifactId>
|
||||
<name>SSH Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>terminal-agent</artifactId>
|
||||
<name>Agent :: Terminal</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-agents-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>unison-agent</artifactId>
|
||||
<name>Unison Agent</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-assembly-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>assembly-ide-war</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-assembly-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>assembly-main</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-assembly-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>assembly-wsagent-server</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
[ -z "${JPDA_ADDRESS}" ] && JPDA_ADDRESS="4403"
|
||||
|
||||
[ -z "${UMASK}" ] && UMASK="022"
|
||||
|
||||
#Tomcat options
|
||||
[ -z "${CATALINA_OPTS}" ] && CATALINA_OPTS="-Dcom.sun.management.jmxremote \
|
||||
-Dcom.sun.management.jmxremote.ssl=false \
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-assembly-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>assembly-wsagent-war</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-assembly-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>assembly-wsmaster-war</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public class WsMasterModule extends AbstractModule {
|
|||
|
||||
bind(org.eclipse.che.security.oauth.OAuthAuthenticatorProvider.class)
|
||||
.to(org.eclipse.che.security.oauth.OAuthAuthenticatorProviderImpl.class);
|
||||
bind(org.eclipse.che.api.auth.oauth.OAuthTokenProvider.class)
|
||||
bind(org.eclipse.che.security.oauth.shared.OAuthTokenProvider.class)
|
||||
.to(org.eclipse.che.security.oauth.OAuthAuthenticatorTokenProvider.class);
|
||||
bind(org.eclipse.che.security.oauth.OAuthAuthenticationService.class);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@
|
|||
<parent>
|
||||
<artifactId>che-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<artifactId>che-assembly-parent</artifactId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Che IDE :: Parent</name>
|
||||
<modules>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-api-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-api-dto-maven-plugin</artifactId>
|
||||
<packaging>maven-plugin</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-api-dto</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-api-model</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-db-vendor-h2</artifactId>
|
||||
<name>Che Core :: DB :: Vendor H2</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-db-vendor-postgresql</artifactId>
|
||||
<name>Che Core :: DB :: Vendor PostgreSQL</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-db</artifactId>
|
||||
<name>Che Core :: DB</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-typescript-dto-maven-plugin</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-annotations</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-inject</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-j2ee</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-json</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-schedule</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-test</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-xml</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-parent</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@
|
|||
<parent>
|
||||
<artifactId>che-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-parent</artifactId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Che Core Parent</name>
|
||||
<modules>
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@
|
|||
<parent>
|
||||
<artifactId>che-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.dashboard</groupId>
|
||||
<artifactId>che-dashboard-war</artifactId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<name>Che Dashboard :: Web App</name>
|
||||
<inceptionYear>2015</inceptionYear>
|
||||
|
|
|
|||
|
|
@ -29,12 +29,16 @@ export class CreateProjectSamplesController {
|
|||
* Default constructor that is using resource
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor($scope, $filter: ng.IFilterService, cheAPI: CheAPI) {
|
||||
constructor($scope: ng.IScope, $filter: ng.IFilterService, cheAPI: CheAPI) {
|
||||
this.$filter = $filter;
|
||||
|
||||
this.templates = cheAPI.getProjectTemplate().getAllProjectTemplates();
|
||||
if (!this.templates.length) {
|
||||
cheAPI.getProjectTemplate().fetchTemplates();
|
||||
const templatesPromise = cheAPI.getProjectTemplate().fetchTemplates();
|
||||
templatesPromise.finally(() => {
|
||||
this.templates = cheAPI.getProjectTemplate().getAllProjectTemplates();
|
||||
this.filterAndSortTemplates();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$watch(() => {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
* Codenvy, S.A. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
import {DockerfileParser} from '../../../components/api/environment/docker-file-parser';
|
||||
|
||||
|
||||
const COMPOSE = 'compose';
|
||||
const DOCKERFILE = 'dockerfile';
|
||||
|
|
@ -21,6 +23,12 @@ const DOCKERIMAGE = 'dockerimage';
|
|||
*/
|
||||
export class StackValidationService {
|
||||
|
||||
dockerfileParser: DockerfileParser;
|
||||
|
||||
constructor() {
|
||||
this.dockerfileParser = new DockerfileParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return result of recipe validation.
|
||||
* @param stack {che.IStack}
|
||||
|
|
@ -247,9 +255,13 @@ export class StackValidationService {
|
|||
if (!recipe.content) {
|
||||
isValid = false;
|
||||
errors.push('Unknown recipe content.');
|
||||
} else if (!/^FROM\s+\w+/m.test(recipe.content)) {
|
||||
isValid = false;
|
||||
errors.push('The dockerfile is invalid.');
|
||||
} else {
|
||||
try {
|
||||
this.dockerfileParser.parse(recipe.content);
|
||||
} catch (e) {
|
||||
isValid = false;
|
||||
errors.push(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!recipe.contentType) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export class EditServerDialogController {
|
|||
usedReferences: string[];
|
||||
|
||||
port: number;
|
||||
portMin: number = 1024;
|
||||
portMin: number = 0;
|
||||
portMax: number = 65535;
|
||||
protocol: string;
|
||||
reference: string;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
che-place-holder="Port number"
|
||||
ng-model="editServerDialogController.port"
|
||||
type="number"
|
||||
min="1024"
|
||||
min="0"
|
||||
max="65535"
|
||||
custom-validator="editServerDialogController.isUniquePort($value)"
|
||||
aria-label="Server port number"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<che-popup title="Remove machine" on-close="deleteDevMachineDialogController.hide()">
|
||||
<che-popup title="Remove machine" on-close="deleteDevMachineDialogController.cancel()">
|
||||
<div layout="column" flex
|
||||
class="delete-dev-machine-dialog">
|
||||
|
||||
|
|
|
|||
|
|
@ -233,7 +233,6 @@ export class WorkspaceMachineConfigController {
|
|||
* @param {MouseEvent} $event
|
||||
* @returns {ng.IPromise<any>}
|
||||
*/
|
||||
// todo
|
||||
showDeleteDevMachineDialog($event: MouseEvent): ng.IPromise<any> {
|
||||
return this.$mdDialog.show({
|
||||
targetEvent: $event,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
<div ng-message="maxlength">Command's preview URL should be less than 256 characters long.</div>
|
||||
</che-input>
|
||||
</div>
|
||||
<che-button-primary che-button-title="{{editCommandDialogController.index === -1 ? 'Add' : 'Edit'}}"
|
||||
<che-button-primary che-button-title="{{editCommandDialogController.index === -1 ? 'Add' : 'Save'}}"
|
||||
ng-click="editCommandDialogController.updateCommand()"
|
||||
ng-disabled="commandForm.$invalid">
|
||||
</che-button-primary>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
* Codenvy, S.A. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
import {DockerfileParser} from '../../../../../components/api/environment/docker-file-parser';
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
|
|
@ -20,6 +21,9 @@
|
|||
export class WorkspaceRecipeAuthoringController {
|
||||
$timeout: ng.ITimeoutService;
|
||||
|
||||
dockerfileParser: DockerfileParser;
|
||||
recipeValidationError: string;
|
||||
|
||||
editingTimeoutPromise: ng.IPromise<any>;
|
||||
|
||||
recipeFormat: string;
|
||||
|
|
@ -43,6 +47,8 @@ export class WorkspaceRecipeAuthoringController {
|
|||
constructor($scope: ng.IScope, $timeout: ng.ITimeoutService) {
|
||||
this.$timeout = $timeout;
|
||||
|
||||
this.dockerfileParser = new DockerfileParser();
|
||||
|
||||
this.editorOptions = {
|
||||
lineWrapping: true,
|
||||
lineNumbers: true,
|
||||
|
|
@ -81,6 +87,7 @@ export class WorkspaceRecipeAuthoringController {
|
|||
|
||||
this.editingTimeoutPromise = this.$timeout(() => {
|
||||
this.detectFormat(content);
|
||||
this.validateRecipe(content);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +105,16 @@ export class WorkspaceRecipeAuthoringController {
|
|||
}
|
||||
}
|
||||
|
||||
onRecipeChange() {
|
||||
validateRecipe(content: string): void {
|
||||
this.recipeValidationError = '';
|
||||
try {
|
||||
this.dockerfileParser.parse(content);
|
||||
} catch (e) {
|
||||
this.recipeValidationError = e.message;
|
||||
}
|
||||
}
|
||||
|
||||
onRecipeChange(): void {
|
||||
this.$timeout(() => {
|
||||
this.detectFormat(this.recipeScriptCopy);
|
||||
this.recipeChange({
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
<div ng-message="required">The recipe is required.</div>
|
||||
</che-error-messages>
|
||||
</che-input>
|
||||
<div class="errors-container"
|
||||
ng-if="workspaceRecipeAuthoringController.recipeValidationError">
|
||||
{{workspaceRecipeAuthoringController.recipeValidationError}}
|
||||
</div>
|
||||
<div class="recipe-docs-link">
|
||||
<a href="https://www.eclipse.org/che/docs/workspace/stacks/index.html#stack-administration" target="_blank">Custom stack documentation</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,3 +17,11 @@
|
|||
|
||||
.che-label-container-row
|
||||
align-items center
|
||||
|
||||
.errors-container
|
||||
color $error-color
|
||||
min-height 20px
|
||||
margin-right 2px
|
||||
|
||||
span
|
||||
margin-right 2px
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export class WorkspaceDetailsController {
|
|||
namespaceId: string = '';
|
||||
namespaceLabel: string;
|
||||
namespaceLabels: Array<string>;
|
||||
namespaceInfo: String;
|
||||
onNamespaceChanged: Function;
|
||||
workspaceId: string = '';
|
||||
workspaceName: string = '';
|
||||
|
|
@ -188,12 +189,14 @@ export class WorkspaceDetailsController {
|
|||
this.namespaceId = namespace ? namespace.id : (this.getNamespaces().length ? this.getNamespaces()[0].id : undefined);
|
||||
this.namespaceLabel = namespace ? namespace.label : (this.getNamespaces().length ? this.getNamespaces()[0].label : undefined);
|
||||
this.namespaceLabels = this.getNamespaces().length ? this.lodash.pluck(this.getNamespaces(), 'label') : [];
|
||||
this.fetchNamespaceInfo();
|
||||
|
||||
this.onNamespaceChanged = (label: string) => {
|
||||
let namespace = this.getNamespaces().find((namespace: any) => {
|
||||
return namespace.label === label;
|
||||
});
|
||||
this.namespaceId = namespace ? namespace.id : this.namespaceId;
|
||||
this.fetchNamespaceInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -268,7 +271,7 @@ export class WorkspaceDetailsController {
|
|||
*/
|
||||
getWorkspaceStatus(): string {
|
||||
if (this.isCreationFlow) {
|
||||
return 'CREATING';
|
||||
return 'New';
|
||||
}
|
||||
|
||||
let unknownStatus = 'unknown';
|
||||
|
|
@ -312,6 +315,17 @@ export class WorkspaceDetailsController {
|
|||
}
|
||||
}
|
||||
|
||||
fetchNamespaceInfo() {
|
||||
if (!this.cheNamespaceRegistry.getAdditionalInfo()) {
|
||||
this.namespaceInfo = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.cheNamespaceRegistry.getAdditionalInfo()(this.namespaceId).then((info: string) => {
|
||||
this.namespaceInfo = info;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when Team button is clicked in Edit mode.
|
||||
* Redirects to billing details or team details.
|
||||
|
|
@ -657,7 +671,7 @@ export class WorkspaceDetailsController {
|
|||
|
||||
return tabs.some((tabIndex: number) => {
|
||||
return this.checkFormsNotValid(tabIndex);
|
||||
});
|
||||
}) || this.isDisableWorkspaceCreation();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -687,5 +701,41 @@ export class WorkspaceDetailsController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns namespaces empty message if set.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getNamespaceEmptyMessage(): string {
|
||||
return this.cheNamespaceRegistry.getEmptyMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns namespaces caption.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getNamespaceCaption(): string {
|
||||
return this.cheNamespaceRegistry.getCaption();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns namespaces additional information.
|
||||
*
|
||||
* @returns {()=>Function}
|
||||
*/
|
||||
getNamespaceAdditionalInfo(): Function {
|
||||
return this.cheNamespaceRegistry.getAdditionalInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether workspace creation should be disabled based on namespaces.
|
||||
*
|
||||
* @returns {boolean|string}
|
||||
*/
|
||||
isDisableWorkspaceCreation(): boolean {
|
||||
let namespaces = this.cheNamespaceRegistry.getNamespaces();
|
||||
return (this.isCreationFlow && (!namespaces || namespaces.length === 0) && this.cheNamespaceRegistry.getEmptyMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,13 +62,23 @@
|
|||
</div>
|
||||
</che-label-container>
|
||||
<!-- Namespace -->
|
||||
<che-label-container che-label-name="Team"
|
||||
ng-if="workspaceDetailsController.getNamespaces().length > 0">
|
||||
<che-filter-selector che-values="workspaceDetailsController.namespaceLabels"
|
||||
ng-if="workspaceDetailsController.isCreationFlow"
|
||||
ng-model="workspaceDetailsController.namespaceLabel"
|
||||
che-on-change="workspaceDetailsController.onNamespaceChanged"></che-filter-selector>
|
||||
<che-button-default ng-if="!workspaceDetailsController.isCreationFlow"
|
||||
<che-label-container che-label-name="{{workspaceDetailsController.getNamespaceCaption()}}"
|
||||
ng-if="workspaceDetailsController.getNamespaces().length > 0 || workspaceDetailsController.getNamespaceEmptyMessage()">
|
||||
|
||||
<div ng-if="workspaceDetailsController.isCreationFlow && workspaceDetailsController.getNamespaces().length > 0"
|
||||
layout="row" layout-align="start center">
|
||||
<che-filter-selector che-values="workspaceDetailsController.namespaceLabels"
|
||||
ng-model="workspaceDetailsController.namespaceLabel"
|
||||
che-on-change="workspaceDetailsController.onNamespaceChanged"></che-filter-selector>
|
||||
<span class="namespace-additional-info" ng-if="workspaceDetailsController.namespaceInfo">
|
||||
{{workspaceDetailsController.namespaceInfo}}
|
||||
</span>
|
||||
</div>
|
||||
<div ng-if="workspaceDetailsController.getNamespaces().length === 0 && workspaceDetailsController.getNamespaceEmptyMessage()" class="empty-namespace-label">
|
||||
<i class="fa-exclamation-triangle fa fa-2x"></i>
|
||||
{{workspaceDetailsController.getNamespaceEmptyMessage()}}
|
||||
</div>
|
||||
<che-button-default ng-if="!workspaceDetailsController.isCreationFlow && workspaceDetailsController.getNamespaces().length > 0"
|
||||
che-button-title="{{workspaceDetailsController.getNamespaceLabel(workspaceDetailsController.namespaceId)}}"
|
||||
ng-disabled="!workspaceDetailsController.getNamespace(workspaceDetailsController.namespaceId) || !workspaceDetailsController.getNamespace(workspaceDetailsController.namespaceId).location"
|
||||
ng-click="workspaceDetailsController.namespaceOnClick(workspaceDetailsController.namespaceId)"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,18 @@
|
|||
.namespace-button button
|
||||
text-transform none !important
|
||||
|
||||
.namespace-additional-info
|
||||
color $label-secondary-color
|
||||
margin-left 10px
|
||||
|
||||
.empty-namespace-label
|
||||
color $warning-color
|
||||
margin-bottom 10px
|
||||
|
||||
i
|
||||
margin-right 10px
|
||||
font-size 16pt
|
||||
|
||||
span.workspace-status-text
|
||||
color lighten($che-ide-background-color, 40%) !important
|
||||
margin-bottom 4px
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ describe('If recipe has content', () => {
|
|||
},
|
||||
'recipe': {
|
||||
'type': 'dockerfile',
|
||||
'content': 'FROM codenvy/ubuntu_jdk8\nENV myName=\"John Doe\" myDog=Rex\\ The\\ Dog \\\n myCat=fluffy',
|
||||
'content': 'FROM codenvy/ubuntu_jdk8\nENV myName="John Doe" myDog=Rex\\ The\\ Dog \\\n myCat=fluffy',
|
||||
'contentType': 'text/x-dockerfile'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,84 +17,315 @@ import {DockerfileParser} from './docker-file-parser';
|
|||
* @author Oleksii Kurinnyi
|
||||
*/
|
||||
|
||||
describe('Simper dockerfile parser', () => {
|
||||
describe('Simple dockerfile parser >', () => {
|
||||
let parser;
|
||||
|
||||
beforeEach(() => {
|
||||
parser = new DockerfileParser();
|
||||
});
|
||||
|
||||
describe('method _parseArgument()', () => {
|
||||
it('should parse ENV argument as single variable form #1', () => {
|
||||
let instruction = 'ENV',
|
||||
argument = 'name environment variable value';
|
||||
describe('parsing directives >', () => {
|
||||
|
||||
let result = parser._parseArgument(instruction, argument);
|
||||
it(`should know 'escape' directive`, () => {
|
||||
const dockerfile = `# escape=\\
|
||||
FROM codenvy/ubuntu_jdk8`;
|
||||
|
||||
let expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['name', 'environment variable value']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
it('should parse ENV argument as single variable form #2', () => {
|
||||
let instruction = 'ENV',
|
||||
argument = 'SBT_OPTS \'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'';
|
||||
|
||||
let result = parser._parseArgument(instruction, argument);
|
||||
|
||||
let expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['SBT_OPTS', '\'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse ENV argument as multiple variables form #1', () => {
|
||||
let instruction = 'ENV',
|
||||
argument = 'key=value';
|
||||
let result = parser._parseArgument(instruction, argument);
|
||||
|
||||
let expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['key', 'value']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse ENV argument as multiple variables form #2', () => {
|
||||
let instruction = 'ENV',
|
||||
argument = 'myName="John Doe" myDog=Rex\ The\ Dog myCat=fluffy';
|
||||
|
||||
let result = parser._parseArgument(instruction, argument);
|
||||
|
||||
let expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['myName', 'John Doe']
|
||||
const expectedResult = [{
|
||||
directive: '# escape=\\'
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myDog', 'Rex The Dog']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myCat', 'fluffy']
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should treat unknown directive as a comment`, () => {
|
||||
const dockerfile = `# directive=value
|
||||
FROM codenvy/ubuntu_jdk8`;
|
||||
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
comment: '# directive=value'
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should throw an error if there are two identical directives`, () => {
|
||||
const dockerfile = `# escape=\\
|
||||
# escape=\`
|
||||
FROM codenvy/ubuntu_jdk8`;
|
||||
const parse = () => {
|
||||
parser.parse(dockerfile);
|
||||
};
|
||||
|
||||
expect(parse).toThrowError(TypeError);
|
||||
});
|
||||
|
||||
it(`should treat known directive as a comment after an empty line`, () => {
|
||||
const dockerfile = `
|
||||
# escape=\\
|
||||
FROM codenvy/ubuntu_jdk8`;
|
||||
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
emptyLine: true
|
||||
}, {
|
||||
comment: '# escape=\\'
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should treat known directive as a comment after a comment`, () => {
|
||||
const dockerfile = `# comment line
|
||||
# escape=\\
|
||||
FROM codenvy/ubuntu_jdk8`;
|
||||
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
comment: '# comment line'
|
||||
}, {
|
||||
comment: '# escape=\\'
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should treat known directive as a comment after a builder instruction`, () => {
|
||||
const dockerfile = `FROM codenvy/ubuntu_jdk8
|
||||
# escape=\\`;
|
||||
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}, {
|
||||
comment: '# escape=\\'
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('method parseArgument()', () => {
|
||||
|
||||
describe('ENV argument as single variable form >', () => {
|
||||
|
||||
it('should parse environment variable #1', () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'name environment variable value';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['name', 'environment variable value']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse environment variable #2', () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'SBT_OPTS \'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['SBT_OPTS', '\'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should throw an error if incorrect ENV argument is written in single variable form`, () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'myNameJohnDoe'; // space between name and value is missed
|
||||
|
||||
const parse = () => {
|
||||
parser.parseArgument(instruction, argument);
|
||||
};
|
||||
|
||||
expect(parse).toThrowError(TypeError);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ENV argument as multiple variables form >', () => {
|
||||
|
||||
it('should parse single environment variable with backslashes', () => {
|
||||
const dockerfile = `# escape=\\
|
||||
FROM codenvy/ubuntu_jdk8
|
||||
ENV myDog=Rex\\ The\\ Dog`;
|
||||
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
directive: '# escape=\\'
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myDog', 'Rex The Dog']
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse single environment variable with backtick', () => {
|
||||
const dockerfile = `# escape=\`
|
||||
FROM codenvy/ubuntu_jdk8
|
||||
ENV myDog=Rex\` The\` Dog`;
|
||||
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
directive: '# escape=\`'
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myDog', 'Rex The Dog']
|
||||
}];
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse ENV argument as multiple variables form #1', () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'key=value';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['key', 'value']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse ENV argument as multiple variables form #2', () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog myCat=fluffy';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['myName', 'John Doe']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myDog', 'Rex The Dog']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myCat', 'fluffy']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should parse ENV argument as multiple variables form #3', () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog \\\n myCat=fluffy';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['myName', 'John Doe']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myDog', 'Rex The Dog']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myCat', 'fluffy']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should parse ENV argument as multiple variables form #4`, () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog\\ myCat=fluffy';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['myName', 'John Doe']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myDog', 'Rex The Dog myCat=fluffy']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should parse ENV argument as multiple variables form #5`, () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'myVar=\\\\\\ \\\\\\\\';
|
||||
|
||||
const result = parser.parseArgument(instruction, argument);
|
||||
|
||||
const expectedResult = [{
|
||||
instruction: 'ENV',
|
||||
argument: ['myVar', '\\ \\\\']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it(`should throw an error if incorrect ENV argument is written in multiple variables form`, () => {
|
||||
const instruction = 'ENV',
|
||||
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog myCat fluffy'; // the 'equal' symbol is missed
|
||||
|
||||
const parse = () => {
|
||||
parser.parseArgument(instruction, argument);
|
||||
};
|
||||
|
||||
expect(parse).toThrowError(TypeError);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should parse a dockerfile', () => {
|
||||
let dockerfile = 'FROM codenvy/ubuntu_jdk8ENV'
|
||||
+ '\n#ENV myCat fluffy'
|
||||
+ '\nENV myDog Rex The Dog'
|
||||
+ '\nENV myName="John Doe"';
|
||||
const dockerfile = `# escape=\\
|
||||
|
||||
let result = parser.parse(dockerfile);
|
||||
FROM codenvy/ubuntu_jdk8
|
||||
#ENV myCat fluffy
|
||||
ENV myDog Rex The Dog
|
||||
ENV myName="John Doe"
|
||||
ENV myText long \\
|
||||
multiline \\
|
||||
value
|
||||
ENV myVal=\\\\\\ \\\\\\\\`;
|
||||
|
||||
let expectedResult = [{
|
||||
const result = parser.parse(dockerfile);
|
||||
|
||||
const expectedResult = [{
|
||||
directive: '# escape=\\'
|
||||
}, {
|
||||
emptyLine: true
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8ENV'
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}, {
|
||||
comment: '#ENV myCat fluffy'
|
||||
}, {
|
||||
|
|
@ -103,14 +334,24 @@ describe('Simper dockerfile parser', () => {
|
|||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myName', 'John Doe']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myText', 'long \nmultiline \nvalue']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myVal', '\\ \\\\']
|
||||
}];
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should stringify an object into a dockerfile', () => {
|
||||
let instructions = [{
|
||||
const instructions = [{
|
||||
directive: '# escape=\\'
|
||||
}, {
|
||||
emptyLine: true
|
||||
}, {
|
||||
instruction: 'FROM',
|
||||
argument: 'codenvy/ubuntu_jdk8ENV'
|
||||
argument: 'codenvy/ubuntu_jdk8'
|
||||
}, {
|
||||
comment: '#ENV myCat fluffy'
|
||||
}, {
|
||||
|
|
@ -119,14 +360,26 @@ describe('Simper dockerfile parser', () => {
|
|||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myName', 'John Doe']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myText', 'long \nmultiline \nvalue']
|
||||
}, {
|
||||
instruction: 'ENV',
|
||||
argument: ['myVal', '\\ \\\\']
|
||||
}];
|
||||
|
||||
let result = parser.dump(instructions);
|
||||
const result = parser.dump(instructions);
|
||||
|
||||
let expectedResult = 'FROM codenvy/ubuntu_jdk8ENV'
|
||||
+ '\n#ENV myCat fluffy'
|
||||
+ '\nENV myDog Rex The Dog'
|
||||
+ '\nENV myName John Doe';
|
||||
const expectedResult = `# escape=\\
|
||||
|
||||
FROM codenvy/ubuntu_jdk8
|
||||
#ENV myCat fluffy
|
||||
ENV myDog Rex The Dog
|
||||
ENV myName John Doe
|
||||
ENV myText long \\
|
||||
multiline \\
|
||||
value
|
||||
ENV myVal \\\\\ \\\\\\\\`;
|
||||
expect(result.trim()).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -19,36 +19,105 @@ interface IRecipeLine {
|
|||
instruction?: string;
|
||||
argument?: string | string[];
|
||||
comment?: string;
|
||||
directive?: string;
|
||||
emptyLine?: boolean;
|
||||
}
|
||||
|
||||
export class DockerfileParser {
|
||||
/**
|
||||
* RegExp to match the very first instruction to be 'FROM'.
|
||||
*/
|
||||
fromRE: RegExp;
|
||||
backslashLineBreakRE: RegExp;
|
||||
lineBreakRE: RegExp;
|
||||
commentLineRE: RegExp;
|
||||
/**
|
||||
* RegExp to match an empty line.
|
||||
*/
|
||||
emptyLineRE: RegExp;
|
||||
/**
|
||||
* RegExp to match a comment line.
|
||||
*/
|
||||
commentRE: RegExp;
|
||||
/**
|
||||
* RegExp to match a single dockerfile instruction.
|
||||
*/
|
||||
instructionRE: RegExp;
|
||||
envVariablesRE: RegExp;
|
||||
quotesTestRE: RegExp;
|
||||
quoteAtStartReplaceRE: RegExp;
|
||||
quoteAtEndReplaceRE: RegExp;
|
||||
backslashSpaceRE: RegExp;
|
||||
/**
|
||||
* RegExp to match leading and trailing quotes.
|
||||
*/
|
||||
quotesRE: RegExp;
|
||||
/**
|
||||
* RegExp to match a dockerfile parsing directive.
|
||||
*/
|
||||
directiveRE: RegExp;
|
||||
/**
|
||||
* RegExp to match an environment variable in form <code>name="quoted value"</code>.
|
||||
*/
|
||||
envVariableQuotedValueRE: RegExp;
|
||||
/**
|
||||
* RegExp to match any line break.
|
||||
*/
|
||||
linebreaksRE: RegExp;
|
||||
/**
|
||||
* RegExp to match any escaped whitespace or line break.
|
||||
*/
|
||||
escapedWhitespacesAndLinebreaksRE: RegExp;
|
||||
/**
|
||||
* RegExp to match any escaped escape symbol.
|
||||
*/
|
||||
escapedEscapeSymbolsRE: RegExp;
|
||||
/**
|
||||
* RegExp to match escaped line break at start of the line.
|
||||
*/
|
||||
escapedLineBreakAtStartRE: RegExp;
|
||||
/**
|
||||
* RegExp to match environment variable name.
|
||||
*/
|
||||
variableNameRE: RegExp;
|
||||
/**
|
||||
* RegExp to match first unescaped whitespace or line break.
|
||||
*/
|
||||
unEscapedWhitespaceRE: RegExp;
|
||||
|
||||
/**
|
||||
* Allowed values for parsing directives.
|
||||
*/
|
||||
directiveValues: {
|
||||
escape: string[],
|
||||
[name: string]: string[]
|
||||
};
|
||||
/**
|
||||
* Parsing directives with default values.
|
||||
*/
|
||||
directives: {
|
||||
escape?: string,
|
||||
[name: string]: string
|
||||
};
|
||||
/**
|
||||
* Current escape to be used as part of a RegExp
|
||||
*/
|
||||
escape: string;
|
||||
/**
|
||||
* RegExp to match any escape symbol.
|
||||
*/
|
||||
escapeRE: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.fromRE = /^FROM\s+\w+/m;
|
||||
this.backslashLineBreakRE = /\\\r?\n(\s+)?/;
|
||||
this.lineBreakRE = /\r?\n/;
|
||||
this.commentLineRE = /^#/;
|
||||
this.instructionRE = /(\w+)\s+?(.+)/;
|
||||
this.envVariablesRE = /(?:^|(?:\s+))([^\s=]+?)=([^=]+?)(?:(?=\s+\w+=)|$)/g;
|
||||
// | | | |
|
||||
// | | | \- start of next variable name or end of line
|
||||
// | | \- variable value
|
||||
// | \- variable name
|
||||
// \- start of line or spaces before variable name
|
||||
this.quotesTestRE = /^(["']).+(\1)$/;
|
||||
this.quoteAtStartReplaceRE = /^["']/g;
|
||||
this.quoteAtEndReplaceRE = /["']$/g;
|
||||
this.backslashSpaceRE = /\\\s/g;
|
||||
this.fromRE = /^FROM$/i;
|
||||
this.emptyLineRE = /^\s*\r?\n/;
|
||||
this.commentRE = /^\s*#/;
|
||||
this.quotesRE = /^["']|['"]$/g;
|
||||
this.envVariableQuotedValueRE = /^([^\s=]+?)=['"]([^'"]+?)['"]\s*/;
|
||||
this.linebreaksRE = /(\r?\n)/g;
|
||||
this.variableNameRE = /^([a-z_][a-z0-9_]*)=/i;
|
||||
|
||||
this.directiveValues = {
|
||||
escape: ['\\', '`']
|
||||
};
|
||||
this.directives = {};
|
||||
const knownDirectives = 'escape';
|
||||
this.directiveRE = new RegExp('^\\s*#\\s*(' + knownDirectives + ')\\s*=\\s*([^\\s]+)', 'i');
|
||||
|
||||
// set default parsing directive
|
||||
this.updateDirectives('escape', this.directiveValues.escape[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -58,105 +127,100 @@ export class DockerfileParser {
|
|||
* @returns {IRecipeLine[]}
|
||||
*/
|
||||
parse(content: string): IRecipeLine[] {
|
||||
if (!this.fromRE.test(content)) {
|
||||
throw new TypeError('Dockerfile should start with \'FROM\' instruction. Cannot parse this recipe.');
|
||||
}
|
||||
let recipeContent = content;
|
||||
|
||||
// join multiline instructions
|
||||
content = this._joinMultilineInstructions(content);
|
||||
const instructions: IRecipeLine[] = [];
|
||||
const uniqueDirectives: string[] = [];
|
||||
let counter = 1000;
|
||||
let lookingForDirectives = true;
|
||||
let firstInstruction = true;
|
||||
|
||||
// split dockerfile into separate instruction lines
|
||||
let instructionLines: string[] = content.split(this.lineBreakRE);
|
||||
// set default parsing directive
|
||||
this.updateDirectives('escape', this.directiveValues.escape[0]);
|
||||
|
||||
// split instruction line into instruction and argument
|
||||
let instructions: IRecipeLine[] = [];
|
||||
instructionLines.forEach((line: string) => {
|
||||
line = line.trim();
|
||||
while (recipeContent.length && counter) {
|
||||
counter--;
|
||||
|
||||
// check for comment line
|
||||
if (this.commentLineRE.test(line)) {
|
||||
instructions.push({comment: line});
|
||||
return;
|
||||
// process parsing directive
|
||||
if (lookingForDirectives) {
|
||||
if (!this.emptyLineRE.test(recipeContent) && this.directiveRE.test(recipeContent)) {
|
||||
const parts = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n')),
|
||||
directiveStr = parts[0];
|
||||
recipeContent = parts[1];
|
||||
|
||||
let [ , name, value] = this.directiveRE.exec(directiveStr);
|
||||
|
||||
if (this.directiveValues[name].indexOf(value) === -1) {
|
||||
// directive value is not allowed
|
||||
// hence this line should be treated as comment
|
||||
instructions.push({comment: directiveStr});
|
||||
lookingForDirectives = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
name = name.toLowerCase();
|
||||
if (uniqueDirectives.indexOf(name) !== -1) {
|
||||
throw new TypeError(`Directive "${name}" is invalid due to appearing twice.`);
|
||||
}
|
||||
uniqueDirectives.push(name);
|
||||
|
||||
this.updateDirectives(name, value);
|
||||
instructions.push({
|
||||
directive: directiveStr
|
||||
});
|
||||
continue;
|
||||
}
|
||||
lookingForDirectives = false;
|
||||
}
|
||||
|
||||
let m = line.match(this.instructionRE);
|
||||
if (m) {
|
||||
let instruction = m[1],
|
||||
argument = m[2];
|
||||
// process empty line
|
||||
if (this.emptyLineRE.test(recipeContent)) {
|
||||
const parts = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n'));
|
||||
recipeContent = parts[1];
|
||||
|
||||
instructions.push({emptyLine: true});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// process comment
|
||||
if (this.commentRE.test(recipeContent)) {
|
||||
const parts = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n')),
|
||||
commentStr = parts[0];
|
||||
recipeContent = parts[1];
|
||||
|
||||
instructions.push({comment: commentStr});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// process instruction
|
||||
if (this.instructionRE.test(recipeContent)) {
|
||||
const [fullMatch, instruction, argument] = this.instructionRE.exec(recipeContent);
|
||||
|
||||
if (firstInstruction && !this.fromRE.test(instruction)) {
|
||||
throw new TypeError('Dockerfile should start with \'FROM\' instruction.');
|
||||
}
|
||||
firstInstruction = false;
|
||||
|
||||
// parse argument
|
||||
let results: IRecipeLine[] = this._parseArgument(instruction, argument);
|
||||
|
||||
let results: IRecipeLine[] = this.parseArgument(instruction, argument);
|
||||
results.forEach((result: IRecipeLine) => {
|
||||
instructions.push(result);
|
||||
});
|
||||
|
||||
const parts = this.splitBySymbolAtIndex(recipeContent, fullMatch.length);
|
||||
recipeContent = parts[1];
|
||||
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove line breaks from lines which end with backslash
|
||||
*
|
||||
* @param content {string}
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_joinMultilineInstructions(content: string): string {
|
||||
return content.replace(this.backslashLineBreakRE, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an argument string depending on instruction
|
||||
*
|
||||
* @param instruction {string}
|
||||
* @param argumentStr {string}
|
||||
* @returns {IRecipeLine[]}
|
||||
* @private
|
||||
*/
|
||||
_parseArgument(instruction: string, argumentStr: string): IRecipeLine[] {
|
||||
let results: IRecipeLine[] = [];
|
||||
|
||||
switch (instruction) {
|
||||
case 'ENV':
|
||||
let firstSpaceIndex = argumentStr.indexOf(' '),
|
||||
firstEqualIndex = argumentStr.indexOf('=');
|
||||
if (firstEqualIndex > -1 && (firstSpaceIndex === -1 || firstEqualIndex < firstSpaceIndex)) {
|
||||
// this argument string contains one or more environment variables
|
||||
let match;
|
||||
while (match = this.envVariablesRE.exec(argumentStr)) {
|
||||
let name: string = match[1],
|
||||
value: string = match[2];
|
||||
if (this.quotesTestRE.test(value)) {
|
||||
value = value.replace(this.quoteAtStartReplaceRE, '');
|
||||
value = value.replace(this.quoteAtEndReplaceRE, '');
|
||||
}
|
||||
if (this.backslashSpaceRE.test(value)) {
|
||||
value = value.replace(this.backslashSpaceRE, ' ');
|
||||
}
|
||||
|
||||
results.push({
|
||||
instruction: instruction,
|
||||
argument: [name, value]
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// this argument string contains only one environment variable
|
||||
results.push({
|
||||
instruction: instruction,
|
||||
argument: [argumentStr.slice(0, firstSpaceIndex), argumentStr.slice(firstSpaceIndex + 1)]
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
results.push({
|
||||
instruction: instruction,
|
||||
argument: argumentStr
|
||||
});
|
||||
// got weird line
|
||||
const [line, ] = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n'));
|
||||
throw new TypeError(`Cannot parse recipe from line: ${line}`);
|
||||
}
|
||||
|
||||
return results;
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -169,29 +233,239 @@ export class DockerfileParser {
|
|||
let content = '';
|
||||
|
||||
instructions.forEach((line: IRecipeLine) => {
|
||||
if (line.comment) {
|
||||
if (line.emptyLine) {
|
||||
content += '\n';
|
||||
} else if (line.directive) {
|
||||
content += line.directive + '\n';
|
||||
} else if (line.comment) {
|
||||
content += line.comment + '\n';
|
||||
} else {
|
||||
content += line.instruction + ' ' + this._stringifyArgument(line) + '\n';
|
||||
content += line.instruction + ' ' + this.stringifyArgument(line) + '\n';
|
||||
}
|
||||
});
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of given symbol in the string.
|
||||
*
|
||||
* @param {string} content a string
|
||||
* @param {string} delimiter a substring, position of which has to be found.
|
||||
* @return {number}
|
||||
*/
|
||||
private getSplitIndex(content: string, delimiter: string): number {
|
||||
return content.indexOf(delimiter) === -1 ? content.length : content.indexOf(delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a RegExp to match any escape symbol.
|
||||
* Builds a RegExp to match a dockerfile instruction.
|
||||
* Builds a RegExp to match any escaped whitespace or line break.
|
||||
* Builds a RegExp to match any escaped whitespace or line break.
|
||||
* Builds a RegExp to match escaped line break at line start.
|
||||
* Builds a RegExp to match first unescaped whitespace or line break.
|
||||
*/
|
||||
private buildParsingDirectiveSpecificRegExps(): void {
|
||||
this.escapeRE = new RegExp('(' + this.escape + ')', 'g');
|
||||
|
||||
const instructions = 'FROM|RUN|CMD|LABEL|MAINTAINER|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ARG|ONBUILD|STOPSIGNAL|HEALTHCHECK|SHELL';
|
||||
this.instructionRE = new RegExp('^\\s*(' + instructions + ')\\s+((?:.|' + this.escape + '\\r?\\n)+(?!\\r?\\n).)', 'i');
|
||||
|
||||
this.escapedWhitespacesAndLinebreaksRE = new RegExp('([^' + this.escape + ']?(?:' + this.escape + '{2})*)' + this.escape + '(\\s|\\r?\\n|' + this.escape + ')', 'g');
|
||||
|
||||
this.escapedEscapeSymbolsRE = new RegExp('' + this.escape + '(' + this.escape + ')', 'g');
|
||||
|
||||
this.escapedLineBreakAtStartRE = new RegExp('^' + this.escape + '\\r?\\n');
|
||||
|
||||
this.unEscapedWhitespaceRE = new RegExp('[^' + this.escape + '](?:' + this.escape + '{2})*(\\s|\\r?\\n)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates parsing directives object and rebuilds appropriate RegExp.
|
||||
*
|
||||
* @param {string} directive a directive's name
|
||||
* @param {string} value a directive's value
|
||||
*/
|
||||
private updateDirectives(directive: string, value: string): void {
|
||||
this.escape = value === '\\' ? '\\\\' : value;
|
||||
this.directives[directive] = value;
|
||||
|
||||
switch (directive) {
|
||||
case 'escape':
|
||||
this.buildParsingDirectiveSpecificRegExps();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a string by symbol at an index.
|
||||
*
|
||||
* @param {string} what a string to be split.
|
||||
* @param {number} where an index to split the string.
|
||||
* @return {[string,string]}
|
||||
*/
|
||||
private splitBySymbolAtIndex(what: string, where: number): string[] {
|
||||
if (where < 0 || where >= what.length) {
|
||||
where = what.length;
|
||||
return [what, ''];
|
||||
}
|
||||
|
||||
return [what.slice(0, where), what.slice(where + 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an argument string depending on instruction
|
||||
*
|
||||
* @param instruction {string}
|
||||
* @param argumentStr {string}
|
||||
* @returns {IRecipeLine[]}
|
||||
*/
|
||||
private parseArgument(instruction: string, argumentStr: string): IRecipeLine[] {
|
||||
const results: IRecipeLine[] = [];
|
||||
|
||||
switch (instruction) {
|
||||
case 'ENV':
|
||||
const variables: string[][] = this.parseENVInstruction(argumentStr);
|
||||
variables.forEach((variable: string[]) => {
|
||||
results.push({
|
||||
instruction: instruction,
|
||||
argument: variable
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
results.push({
|
||||
instruction: instruction,
|
||||
argument: argumentStr
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private parseENVInstruction(content: string): string[][] {
|
||||
const results: string[][] = [];
|
||||
|
||||
const firstSpaceIndex = content.indexOf(' '),
|
||||
firstEqualIndex = content.indexOf('=');
|
||||
|
||||
if (firstEqualIndex === -1 && firstSpaceIndex === -1) {
|
||||
throw new TypeError(`Cannot parse environment variable name and value from string "${content}"`);
|
||||
}
|
||||
|
||||
if (firstSpaceIndex > -1 && (firstEqualIndex === -1 || firstSpaceIndex < firstEqualIndex)) {
|
||||
|
||||
// this argument string contains only one environment variable
|
||||
let [name, value] = [content.slice(0, firstSpaceIndex), content.slice(firstSpaceIndex + 1)];
|
||||
|
||||
value = this.unEscapeString(value);
|
||||
|
||||
results.push([name, value]);
|
||||
|
||||
} else {
|
||||
|
||||
// this argument string contains one or more environment variables
|
||||
let count = 100;
|
||||
while (content.length && count) {
|
||||
count--;
|
||||
|
||||
// remove a linebreak at the start of string
|
||||
content = content.replace(this.escapedLineBreakAtStartRE, '');
|
||||
|
||||
// remove a whitespace at the start of string
|
||||
content = content.replace(/^\s+/, '');
|
||||
|
||||
// check if string begins with variable name
|
||||
if (!this.variableNameRE.test(content)) {
|
||||
throw new TypeError(`Cannot parse environment variable name and value from string "${content}"`);
|
||||
}
|
||||
|
||||
if (this.envVariableQuotedValueRE.test(content)) {
|
||||
|
||||
/* variable with quoted value */
|
||||
|
||||
let [fullMatch, name, value] = this.envVariableQuotedValueRE.exec(content);
|
||||
|
||||
value = value.replace(this.quotesRE, '');
|
||||
|
||||
results.push([name, value]);
|
||||
|
||||
const parts = this.splitBySymbolAtIndex(content, fullMatch.length - 1);
|
||||
// cut processed variable from the string
|
||||
content = parts[1];
|
||||
} else {
|
||||
|
||||
/* variable with escaped value */
|
||||
|
||||
// look for the point where the variable ends
|
||||
const unEscapedWhitespaceMatch = this.unEscapedWhitespaceRE.exec(content);
|
||||
|
||||
let variableLength: number;
|
||||
if (!unEscapedWhitespaceMatch) {
|
||||
// the rest of string is a single variable
|
||||
variableLength = content.length;
|
||||
} else {
|
||||
variableLength = unEscapedWhitespaceMatch.index + unEscapedWhitespaceMatch.length - 1;
|
||||
}
|
||||
|
||||
const parts = this.splitBySymbolAtIndex(content, variableLength);
|
||||
const variableStr = parts[0];
|
||||
// cut processed variable from the string
|
||||
content = parts[1];
|
||||
|
||||
const equalIndex = variableStr.indexOf('=');
|
||||
|
||||
const varParts = this.splitBySymbolAtIndex(variableStr, equalIndex);
|
||||
let name = varParts[0],
|
||||
value = varParts[1];
|
||||
|
||||
value = this.unEscapeString(value);
|
||||
|
||||
results.push([name, value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps argument object depending on instruction.
|
||||
*
|
||||
* @param line {IRecipeLine}
|
||||
* @param {IRecipeLine} line
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_stringifyArgument(line: IRecipeLine): string {
|
||||
private stringifyArgument(line: IRecipeLine): string {
|
||||
switch (line.instruction) {
|
||||
case 'ENV':
|
||||
return (line.argument as string[]).join(' ');
|
||||
const [name, value] = line.argument as string[];
|
||||
return name + ' ' + this.escapeString(value);
|
||||
default:
|
||||
return line.argument as string;
|
||||
}
|
||||
}
|
||||
|
||||
private escapeString(content: string): string {
|
||||
// this replace should be the very first
|
||||
content = content.replace(this.escapeRE, this.directives.escape + '$1');
|
||||
|
||||
content = content.replace(this.linebreaksRE, this.directives.escape + '$1');
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes whitespaces and escape symbols.
|
||||
*
|
||||
* @param {string} content a string to process.
|
||||
*/
|
||||
private unEscapeString(content: string): string {
|
||||
content = content.replace(this.escapedWhitespacesAndLinebreaksRE, '$1$2');
|
||||
|
||||
// this replace should be the very last
|
||||
content = content.replace(this.escapedEscapeSymbolsRE, '$1');
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
* Codenvy, S.A. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
export interface INamespace {
|
||||
id: string;
|
||||
label: string;
|
||||
|
|
@ -25,6 +24,9 @@ export class CheNamespaceRegistry {
|
|||
private $q: ng.IQService;
|
||||
private fetchPromise: ng.IPromise<any>;
|
||||
private namespaces : INamespace[];
|
||||
private emptyMessage: string;
|
||||
private caption: string;
|
||||
private getAdditionalInfoFunction: Function;
|
||||
|
||||
/**
|
||||
* Default constructor that is using resource
|
||||
|
|
@ -33,6 +35,8 @@ export class CheNamespaceRegistry {
|
|||
constructor($q: ng.IQService) {
|
||||
this.$q = $q;
|
||||
this.namespaces = [];
|
||||
|
||||
this.caption = 'Namespace';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,4 +80,58 @@ export class CheNamespaceRegistry {
|
|||
getNamespaces() : INamespace[] {
|
||||
return this.namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set empty message (message is displayed, when no namespaces).
|
||||
*
|
||||
* @param message empty message
|
||||
*/
|
||||
setEmptyMessage(message: string): void {
|
||||
this.emptyMessage = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns empty message to display, when no namespaces.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getEmptyMessage(): string {
|
||||
return this.emptyMessage ? this.emptyMessage : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set display caption of the namespaces.
|
||||
*
|
||||
* @param caption namespaces caption
|
||||
*/
|
||||
setCaption(caption: string): void {
|
||||
this.caption = caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caption of the namespaces.
|
||||
*
|
||||
* @returns {string} namepsaces caption
|
||||
*/
|
||||
getCaption(): string {
|
||||
return this.caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the function for retrieving available RAM for the namespaces.
|
||||
*
|
||||
* @param getAdditionalInfo additional information function
|
||||
*/
|
||||
setGetAdditionalInfo(getAdditionalInfo: Function): void {
|
||||
this.getAdditionalInfoFunction = getAdditionalInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns function, that returns promise.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
getAdditionalInfo(): Function {
|
||||
return this.getAdditionalInfoFunction;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,12 +68,10 @@ pre_cmd_archetype() {
|
|||
fi
|
||||
|
||||
ARCHETYPE_ACTION="all"
|
||||
ARCHETYPE_ID="che-plugin-ide-menu-archetype"
|
||||
# ARCHETYPE_VERSION=$(get_image_version)
|
||||
ARCHETYPE_ID="plugin-menu-archetype"
|
||||
|
||||
##############################
|
||||
# REPLACE THIS WITH $(get_image_version) AFTER CI SYSTEMS GENERATING
|
||||
ARCHETYPE_VERSION=5.6.0-SNAPSHOT
|
||||
ARCHETYPE_VERSION=$(get_image_version)
|
||||
ASSEMBLY_VERSION=$ARCHETYPE_VERSION
|
||||
ASSEMBLY_GROUP="com.sample"
|
||||
ASSEMBLY_ID="assembly"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
IMAGE_INIT=eclipse/che-init:5.10.0
|
||||
IMAGE_CHE=eclipse/che-server:5.10.0
|
||||
IMAGE_COMPOSE=docker/compose:1.8.1
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
eclipse/alpine_jdk8
|
||||
eclipse/aspnet
|
||||
eclipse/centos_jdk8
|
||||
eclipse/cpp_gcc
|
||||
eclipse/debian_jdk8
|
||||
eclipse/debian_jdk8_node
|
||||
eclipse/debian_jre
|
||||
eclipse/dotnet_core
|
||||
eclipse/hadoop-dev
|
||||
eclipse/meteor
|
||||
eclipse/node
|
||||
eclipse/php
|
||||
eclipse/platformio
|
||||
eclipse/ruby_rails
|
||||
eclipse/selenium
|
||||
eclipse/ubuntu_android
|
||||
eclipse/ubuntu_go
|
||||
eclipse/ubuntu_gradle
|
||||
eclipse/ubuntu_jdk8
|
||||
eclipse/ubuntu_jre
|
||||
eclipse/ubuntu_python
|
||||
eclipse/ubuntu_wildfly8
|
||||
registry.centos.org/che-stacks/vertx
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
IMAGE_INIT=eclipse/che-init:5.8.1
|
||||
IMAGE_CHE=eclipse/che-server:5.8.1
|
||||
IMAGE_COMPOSE=docker/compose:1.8.1
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
eclipse/alpine_jdk8
|
||||
eclipse/aspnet
|
||||
eclipse/centos_jdk8
|
||||
eclipse/cpp_gcc
|
||||
eclipse/debian_jdk8
|
||||
eclipse/debian_jdk8_node
|
||||
eclipse/debian_jre
|
||||
eclipse/dotnet_core
|
||||
eclipse/hadoop-dev
|
||||
eclipse/meteor
|
||||
eclipse/node
|
||||
eclipse/php
|
||||
eclipse/platformio
|
||||
eclipse/ruby_rails
|
||||
eclipse/selenium
|
||||
eclipse/ubuntu_android
|
||||
eclipse/ubuntu_go
|
||||
eclipse/ubuntu_gradle
|
||||
eclipse/ubuntu_jdk8
|
||||
eclipse/ubuntu_jre
|
||||
eclipse/ubuntu_python
|
||||
eclipse/ubuntu_wildfly8
|
||||
registry.centos.org/che-stacks/vertx
|
||||
|
||||
|
|
@ -1 +1 @@
|
|||
5.8.0
|
||||
5.9.0
|
||||
|
|
@ -17,13 +17,13 @@
|
|||
<parent>
|
||||
<artifactId>maven-depmgt-pom</artifactId>
|
||||
<groupId>org.eclipse.che.depmgt</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>dto-typescript</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Che TypeScript DTO</name>
|
||||
<properties>
|
||||
<che.version>5.9.0-SNAPSHOT</che.version>
|
||||
<che.version>5.10.0-SNAPSHOT</che.version>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-dyna-provider-generator-maven-plugin</artifactId>
|
||||
<packaging>maven-plugin</packaging>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-ide-api</artifactId>
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-auth</artifactId>
|
||||
<artifactId>che-core-api-auth-shared</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.util.Objects;
|
|||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_GOAL_ATTRIBUTE_NAME;
|
||||
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME;
|
||||
|
||||
/** Data object for {@link Command}. */
|
||||
public class CommandImpl implements Command {
|
||||
|
|
@ -123,6 +124,18 @@ public class CommandImpl implements Command {
|
|||
getAttributes().put(COMMAND_GOAL_ATTRIBUTE_NAME, goalId);
|
||||
}
|
||||
|
||||
|
||||
/** Returns command's preview URL or {@code null} if none. */
|
||||
@Nullable
|
||||
public String getPreviewURL() {
|
||||
return getAttributes().get(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/** Sets command's preview URL. */
|
||||
public void setPreviewURL(String previewURL) {
|
||||
getAttributes().put(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME, previewURL);
|
||||
}
|
||||
|
||||
/** Returns command's applicable context. */
|
||||
public ApplicableContext getApplicableContext() {
|
||||
return context;
|
||||
|
|
|
|||
|
|
@ -22,10 +22,6 @@ import org.eclipse.che.ide.api.debug.Breakpoint;
|
|||
@DTO
|
||||
public interface StorableBreakpointDto {
|
||||
|
||||
void setActive(boolean active);
|
||||
|
||||
boolean isActive();
|
||||
|
||||
void setLineNumber(int lineNumber);
|
||||
|
||||
int getLineNumber();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import com.google.web.bindery.event.shared.EventBus;
|
|||
import org.eclipse.che.api.project.shared.dto.event.FileStateUpdateDto;
|
||||
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.editor.EditorAgent;
|
||||
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
|
||||
import org.eclipse.che.ide.api.event.FileContentUpdateEvent;
|
||||
import org.eclipse.che.ide.api.notification.NotificationManager;
|
||||
import org.eclipse.che.ide.api.resources.ExternalResourceDelta;
|
||||
|
|
@ -25,7 +27,9 @@ import org.eclipse.che.ide.resource.Path;
|
|||
import org.eclipse.che.ide.util.loging.Log;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.List;
|
||||
|
||||
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.EMERGE_MODE;
|
||||
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
|
||||
|
|
@ -44,14 +48,19 @@ public class EditorFileStatusNotificationOperation implements JsonRpcRequestBiOp
|
|||
|
||||
private final EventBus eventBus;
|
||||
private final DeletedFilesController deletedFilesController;
|
||||
private final Provider<EditorAgent> editorAgentProvider;
|
||||
private final AppContext appContext;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
@Inject
|
||||
public EditorFileStatusNotificationOperation(EventBus eventBus, DeletedFilesController deletedFilesController, AppContext appContext) {
|
||||
public EditorFileStatusNotificationOperation(EventBus eventBus,
|
||||
DeletedFilesController deletedFilesController,
|
||||
Provider<EditorAgent> editorAgentProvider,
|
||||
AppContext appContext) {
|
||||
this.eventBus = eventBus;
|
||||
this.deletedFilesController = deletedFilesController;
|
||||
this.editorAgentProvider = editorAgentProvider;
|
||||
this.appContext = appContext;
|
||||
}
|
||||
|
||||
|
|
@ -88,10 +97,21 @@ public class EditorFileStatusNotificationOperation implements JsonRpcRequestBiOp
|
|||
appContext.getWorkspaceRoot().synchronize(new ExternalResourceDelta(path, path, REMOVED));
|
||||
if (notificationManager != null && !deletedFilesController.remove(stringPath)) {
|
||||
notificationManager.notify("External operation", "File '" + name + "' is removed", SUCCESS, EMERGE_MODE);
|
||||
closeOpenedEditor(path);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeOpenedEditor(Path path) {
|
||||
final EditorAgent editorAgent = editorAgentProvider.get();
|
||||
final List<EditorPartPresenter> openedEditors = editorAgent.getOpenedEditors();
|
||||
for (EditorPartPresenter openEditor : openedEditors) {
|
||||
if (openEditor.getEditorInput().getFile().getLocation().equals(path)) {
|
||||
editorAgent.closeEditor(openEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-ide-app</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.command.toolbar.previews;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.MachineRuntimeInfo;
|
||||
import org.eclipse.che.api.core.model.machine.Server;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.machine.DevMachine;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Represents an item for displaying in the 'Previews' list. */
|
||||
class PreviewUrl {
|
||||
|
||||
private final AppContext appContext;
|
||||
|
||||
private final String url;
|
||||
private final String displayName;
|
||||
|
||||
PreviewUrl(String url, AppContext appContext) {
|
||||
this.url = url;
|
||||
this.appContext = appContext;
|
||||
this.displayName = getDisplayNameForPreviewUrl(url);
|
||||
}
|
||||
|
||||
/** Returns actual preview URL. */
|
||||
String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns preview URL in user-friendly form.
|
||||
* E.g. {@code dev-machine:8080} instead of {@code http://172.19.21.35:32801}.
|
||||
*/
|
||||
String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
private String getDisplayNameForPreviewUrl(String previewUrl) {
|
||||
final DevMachine devMachine = appContext.getDevMachine();
|
||||
final MachineRuntimeInfo devMachineRuntime = devMachine.getRuntime();
|
||||
|
||||
if (devMachineRuntime == null) {
|
||||
return previewUrl;
|
||||
}
|
||||
|
||||
for (Entry<String, ? extends Server> entry : devMachineRuntime.getServers().entrySet()) {
|
||||
Server server = entry.getValue();
|
||||
String serverUrl = server.getUrl();
|
||||
|
||||
if (serverUrl == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (previewUrl.startsWith(serverUrl)) {
|
||||
String port = entry.getKey();
|
||||
|
||||
// server's port may be in form of '8080/tcp' so need to cut protocol name
|
||||
final int slashIndex = port.lastIndexOf('/');
|
||||
if (slashIndex > -1) {
|
||||
port = port.substring(0, slashIndex);
|
||||
}
|
||||
|
||||
return previewUrl.replace(serverUrl, devMachine.getDisplayName() + ':' + port);
|
||||
}
|
||||
}
|
||||
|
||||
return previewUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PreviewUrl that = (PreviewUrl)o;
|
||||
return Objects.equals(url, that.url) &&
|
||||
Objects.equals(displayName, that.displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(url, displayName);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.command.toolbar.previews;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** Represents an item for displaying in the 'Previews' list. */
|
||||
class PreviewUrlItem {
|
||||
|
||||
private final String url;
|
||||
private final String displayName;
|
||||
|
||||
PreviewUrlItem(String url, String displayName) {
|
||||
this.url = url;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PreviewUrlItem that = (PreviewUrlItem)o;
|
||||
return Objects.equals(url, that.url) &&
|
||||
Objects.equals(displayName, that.displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(url, displayName);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,17 +24,17 @@ import static com.google.gwt.dom.client.Style.Unit.PX;
|
|||
|
||||
/**
|
||||
* Renders widgets for the 'Previews' list. Always returns the same
|
||||
* instance of the header widget which is shared among all {@link PreviewUrlItem}s.
|
||||
* instance of the header widget which is shared among all {@link PreviewUrl}s.
|
||||
*/
|
||||
class PreviewUrlItemRenderer implements DropdownListItemRenderer {
|
||||
|
||||
static final HeaderWidget HEADER_WIDGET = new HeaderWidget();
|
||||
|
||||
private final BaseListItem<PreviewUrlItem> item;
|
||||
private final BaseListItem<PreviewUrl> item;
|
||||
|
||||
private Widget listWidget;
|
||||
|
||||
PreviewUrlItemRenderer(BaseListItem<PreviewUrlItem> item) {
|
||||
PreviewUrlItemRenderer(BaseListItem<PreviewUrl> item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,18 +17,13 @@ import com.google.inject.Provider;
|
|||
import com.google.inject.Singleton;
|
||||
import com.google.web.bindery.event.shared.EventBus;
|
||||
|
||||
import org.eclipse.che.api.core.model.machine.Machine;
|
||||
import org.eclipse.che.api.core.model.machine.MachineRuntimeInfo;
|
||||
import org.eclipse.che.api.core.model.machine.Server;
|
||||
import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime;
|
||||
import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessResponseDto;
|
||||
import org.eclipse.che.api.promises.client.Function;
|
||||
import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessesResponseDto;
|
||||
import org.eclipse.che.api.promises.client.Promise;
|
||||
import org.eclipse.che.api.promises.client.PromiseProvider;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.command.CommandImpl;
|
||||
import org.eclipse.che.ide.api.command.CommandManager;
|
||||
import org.eclipse.che.ide.api.machine.DevMachine;
|
||||
import org.eclipse.che.ide.api.machine.ExecAgentCommandManager;
|
||||
import org.eclipse.che.ide.api.machine.events.ProcessFinishedEvent;
|
||||
import org.eclipse.che.ide.api.machine.events.ProcessStartedEvent;
|
||||
|
|
@ -38,11 +33,9 @@ import org.eclipse.che.ide.api.macro.MacroProcessor;
|
|||
import org.eclipse.che.ide.api.mvp.Presenter;
|
||||
import org.eclipse.che.ide.command.toolbar.ToolbarMessages;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME;
|
||||
|
||||
/** Drives the UI for displaying preview URLs of the running processes. */
|
||||
@Singleton
|
||||
|
|
@ -91,76 +84,37 @@ public class PreviewsPresenter implements Presenter, PreviewsView.ActionDelegate
|
|||
eventBus.addHandler(ProcessFinishedEvent.TYPE, event -> updateView());
|
||||
}
|
||||
|
||||
/** Updates view with preview URLs of all running processes. */
|
||||
/** Updates view with the preview URLs of running processes. */
|
||||
private void updateView() {
|
||||
view.removeAllURLs();
|
||||
|
||||
final WorkspaceRuntime runtime = appContext.getActiveRuntime();
|
||||
|
||||
if (runtime != null) {
|
||||
runtime.getMachines().forEach(machine -> execAgentClient.getProcesses(machine.getId(), false).then(processes -> {
|
||||
processes.forEach(process -> getPreviewUrl(process.getPid(), machine).then(view::addUrl).catchError(ignore -> {}));
|
||||
}));
|
||||
runtime.getMachines().forEach(machine -> {
|
||||
Promise<List<GetProcessesResponseDto>> machineProcesses = execAgentClient.getProcesses(machine.getId(), false);
|
||||
machineProcesses.then(processes -> {
|
||||
processes.forEach(process -> getPreviewUrl(process).then(view::addUrl).catchError(ignore -> {
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns promise that resolves preview URL of the command which has launched
|
||||
* the process with the given {@code pid} on the specified {@code machine}.
|
||||
* Returns promise that resolves preview URL for the given process.
|
||||
* Returns promise that rejects with an error if preview URL isn't available.
|
||||
*/
|
||||
private Promise<PreviewUrlItem> getPreviewUrl(int pid, Machine machine) {
|
||||
return execAgentClient.getProcess(machine.getId(), pid)
|
||||
// get command's preview URL
|
||||
.then((Function<GetProcessResponseDto, String>)process -> {
|
||||
final Optional<CommandImpl> commandOptional = commandManager.getCommand(process.getName());
|
||||
private Promise<String> getPreviewUrl(GetProcessesResponseDto process) {
|
||||
final String previewUrl = commandManager.getCommand(process.getName())
|
||||
.map(CommandImpl::getPreviewURL)
|
||||
.orElse(null);
|
||||
|
||||
return commandOptional.map(command -> command.getAttributes().get(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME))
|
||||
.orElse(null);
|
||||
})
|
||||
// expand macros used in preview URL
|
||||
.thenPromise(previewUrl -> {
|
||||
if (!isNullOrEmpty(previewUrl)) {
|
||||
return macroProcessorProvider.get().expandMacros(previewUrl);
|
||||
}
|
||||
return promiseProvider.reject(new Exception(messages.previewsNotAvailableError()));
|
||||
})
|
||||
// compose preview URL's display name
|
||||
.then((Function<String, PreviewUrlItem>)previewUrl -> new PreviewUrlItem(previewUrl,
|
||||
getPreviewUrlDisplayName(previewUrl)
|
||||
.orElse(previewUrl)));
|
||||
}
|
||||
|
||||
private Optional<String> getPreviewUrlDisplayName(String previewUrl) {
|
||||
final DevMachine devMachine = appContext.getDevMachine();
|
||||
final MachineRuntimeInfo devMachineRuntime = devMachine.getRuntime();
|
||||
|
||||
if (devMachineRuntime == null) {
|
||||
return Optional.empty();
|
||||
if (!isNullOrEmpty(previewUrl)) {
|
||||
return macroProcessorProvider.get().expandMacros(previewUrl);
|
||||
}
|
||||
|
||||
for (Entry<String, ? extends Server> entry : devMachineRuntime.getServers().entrySet()) {
|
||||
Server server = entry.getValue();
|
||||
String serverUrl = server.getUrl();
|
||||
|
||||
if (serverUrl == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (previewUrl.startsWith(serverUrl)) {
|
||||
String displayName = previewUrl.replace(serverUrl, devMachine.getDisplayName() + ':' + entry.getKey());
|
||||
|
||||
// cut protocol from display name
|
||||
final int protocolIndex = displayName.lastIndexOf('/');
|
||||
if (protocolIndex > -1) {
|
||||
displayName = displayName.substring(0, protocolIndex);
|
||||
}
|
||||
|
||||
return Optional.of(displayName);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
return promiseProvider.reject(new Exception(messages.previewsNotAvailableError()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -169,7 +123,7 @@ public class PreviewsPresenter implements Presenter, PreviewsView.ActionDelegate
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onUrlChosen(PreviewUrlItem previewUrlItem) {
|
||||
Window.open(previewUrlItem.getUrl(), "_blank", null);
|
||||
public void onUrlChosen(String previewUrl) {
|
||||
Window.open(previewUrl, "_blank", null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import org.eclipse.che.ide.api.mvp.View;
|
|||
public interface PreviewsView extends View<PreviewsView.ActionDelegate> {
|
||||
|
||||
/** Add preview URL to the view. */
|
||||
void addUrl(PreviewUrlItem previewUrlItem);
|
||||
void addUrl(String previewUrl);
|
||||
|
||||
/** Remove preview URL from the view. */
|
||||
void removeUrl(PreviewUrlItem previewUrlItem);
|
||||
void removeUrl(String previewUrl);
|
||||
|
||||
/** Remove all preview URLs from the view. */
|
||||
void removeAllURLs();
|
||||
|
|
@ -27,6 +27,6 @@ public interface PreviewsView extends View<PreviewsView.ActionDelegate> {
|
|||
interface ActionDelegate {
|
||||
|
||||
/** Called when preview URL has been chosen. */
|
||||
void onUrlChosen(PreviewUrlItem previewUrlItem);
|
||||
void onUrlChosen(String previewUrl);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.google.gwt.user.client.ui.Widget;
|
|||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.command.toolbar.ToolbarMessages;
|
||||
import org.eclipse.che.ide.ui.Tooltip;
|
||||
import org.eclipse.che.ide.ui.dropdown.BaseListItem;
|
||||
|
|
@ -34,18 +35,21 @@ import static org.eclipse.che.ide.ui.menu.PositionController.VerticalAlign.BOTTO
|
|||
public class PreviewsViewImpl implements PreviewsView {
|
||||
|
||||
/** Mapping of URL to list item. */
|
||||
private final Map<PreviewUrlItem, BaseListItem<PreviewUrlItem>> listItems;
|
||||
private final Map<String, BaseListItem<PreviewUrl>> listItems;
|
||||
|
||||
private final DropdownList dropdownList;
|
||||
private final NoPreviewsItem noPreviewsItem;
|
||||
private final NoPreviewsItemRenderer noPreviewsItemRenderer;
|
||||
private final ToolbarMessages messages;
|
||||
private final AppContext appContext;
|
||||
|
||||
private ActionDelegate delegate;
|
||||
|
||||
@Inject
|
||||
public PreviewsViewImpl(ToolbarMessages messages) {
|
||||
public PreviewsViewImpl(ToolbarMessages messages, AppContext appContext) {
|
||||
this.messages = messages;
|
||||
this.appContext = appContext;
|
||||
|
||||
listItems = new HashMap<>();
|
||||
|
||||
dropdownList = new DropdownList(HEADER_WIDGET, false);
|
||||
|
|
@ -84,23 +88,24 @@ public class PreviewsViewImpl implements PreviewsView {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addUrl(PreviewUrlItem previewUrlItem) {
|
||||
if (listItems.containsKey(previewUrlItem)) {
|
||||
return; // no sense to add the equals URLs even if they belong to different commands
|
||||
public void addUrl(String previewUrl) {
|
||||
if (listItems.containsKey(previewUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BaseListItem<PreviewUrlItem> listItem = new BaseListItem<>(previewUrlItem);
|
||||
PreviewUrlItemRenderer renderer = new PreviewUrlItemRenderer(listItem);
|
||||
final PreviewUrl displayablePreviewUrl = new PreviewUrl(previewUrl, appContext);
|
||||
final BaseListItem<PreviewUrl> listItem = new BaseListItem<>(displayablePreviewUrl);
|
||||
final PreviewUrlItemRenderer renderer = new PreviewUrlItemRenderer(listItem);
|
||||
|
||||
listItems.put(previewUrlItem, listItem);
|
||||
listItems.put(previewUrl, listItem);
|
||||
dropdownList.addItem(listItem, renderer);
|
||||
|
||||
checkNoPreviewsItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUrl(PreviewUrlItem previewUrlItem) {
|
||||
final BaseListItem<PreviewUrlItem> listItem = listItems.remove(previewUrlItem);
|
||||
public void removeUrl(String previewUrl) {
|
||||
final BaseListItem<PreviewUrl> listItem = listItems.remove(previewUrl);
|
||||
|
||||
if (listItem != null) {
|
||||
dropdownList.removeItem(listItem);
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.context;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.user.client.Random;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
|
@ -50,22 +48,18 @@ import org.eclipse.che.ide.resources.ResourceManagerInitializer;
|
|||
import org.eclipse.che.ide.resources.impl.ResourceDeltaImpl;
|
||||
import org.eclipse.che.ide.resources.impl.ResourceManager;
|
||||
import org.eclipse.che.ide.statepersistance.AppStateManager;
|
||||
import org.eclipse.che.ide.util.Arrays;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static com.google.gwt.user.client.Random.nextInt;
|
||||
import static java.util.Arrays.binarySearch;
|
||||
import static java.util.Arrays.copyOf;
|
||||
import static java.util.Arrays.sort;
|
||||
import static org.eclipse.che.ide.api.resources.Resource.PROJECT;
|
||||
import static java.util.Collections.addAll;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.ADDED;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_FROM;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_TO;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.SYNCHRONIZED;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.UPDATED;
|
||||
|
||||
/**
|
||||
|
|
@ -85,25 +79,29 @@ public class AppContextImpl implements AppContext,
|
|||
ResourceManagerInitializer {
|
||||
private static final String APP_ID = String.valueOf(nextInt(Integer.MAX_VALUE));
|
||||
|
||||
private static final Project[] NO_PROJECTS = {};
|
||||
private final QueryParameters queryParameters;
|
||||
private final List<String> projectsInImport;
|
||||
private final EventBus eventBus;
|
||||
private final ResourceManager.ResourceManagerFactory resourceManagerFactory;
|
||||
private final Provider<EditorAgent> editorAgentProvider;
|
||||
private final Provider<AppStateManager> appStateManager;
|
||||
|
||||
private final QueryParameters queryParameters;
|
||||
private final List<String> projectsInImport;
|
||||
private final List<Project> rootProjects = newArrayList();
|
||||
private final List<Resource> selectedResources = newArrayList();
|
||||
|
||||
private Workspace usersWorkspace;
|
||||
private Workspace userWorkspace;
|
||||
private CurrentUser currentUser;
|
||||
private FactoryDto factory;
|
||||
private Path projectsRoot;
|
||||
private ActiveRuntime runtime;
|
||||
private ResourceManager resourceManager;
|
||||
|
||||
/**
|
||||
* List of actions with parameters which comes from startup URL.
|
||||
* Can be processed after IDE initialization as usual after starting ws-agent.
|
||||
*/
|
||||
private List<StartUpAction> startAppActions;
|
||||
|
||||
private Resource currentResource;
|
||||
private Resource[] currentResources;
|
||||
|
||||
@Inject
|
||||
public AppContextImpl(EventBus eventBus,
|
||||
QueryParameters queryParameters,
|
||||
|
|
@ -124,31 +122,39 @@ public class AppContextImpl implements AppContext,
|
|||
eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this);
|
||||
}
|
||||
|
||||
private static native String masterFromIDEConfig() /*-{
|
||||
if ($wnd.IDE && $wnd.IDE.config) {
|
||||
return $wnd.IDE.config.restContext;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}-*/;
|
||||
|
||||
@Override
|
||||
public Workspace getWorkspace() {
|
||||
return usersWorkspace;
|
||||
return userWorkspace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorkspace(Workspace workspace) {
|
||||
if (workspace != null) {
|
||||
usersWorkspace = workspace;
|
||||
userWorkspace = workspace;
|
||||
if (workspace.getRuntime() != null) {
|
||||
runtime = new ActiveRuntime(workspace.getRuntime());
|
||||
}
|
||||
} else {
|
||||
usersWorkspace = null;
|
||||
userWorkspace = null;
|
||||
runtime = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkspaceId() {
|
||||
if (usersWorkspace == null) {
|
||||
if (userWorkspace == null) {
|
||||
throw new IllegalArgumentException(getClass() + " Workspace can not be null.");
|
||||
}
|
||||
|
||||
return usersWorkspace.getId();
|
||||
return userWorkspace.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -200,7 +206,6 @@ public class AppContextImpl implements AppContext,
|
|||
return runtime.getDevMachine();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initResourceManager(final Callback<ResourceManager, Exception> callback) {
|
||||
if (runtime.getDevMachine() == null) {
|
||||
|
|
@ -208,17 +213,18 @@ public class AppContextImpl implements AppContext,
|
|||
callback.onFailure(new NullPointerException("Dev machine is not initialized"));
|
||||
}
|
||||
|
||||
if (projects != null) {
|
||||
for (Project project : projects) {
|
||||
if (!rootProjects.isEmpty()) {
|
||||
for (Project project : rootProjects) {
|
||||
eventBus.fireEvent(new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED)));
|
||||
}
|
||||
projects = null;
|
||||
rootProjects.clear();
|
||||
}
|
||||
|
||||
resourceManager = resourceManagerFactory.newResourceManager(runtime.getDevMachine());
|
||||
resourceManager.getWorkspaceProjects().then(projects -> {
|
||||
AppContextImpl.this.projects = projects;
|
||||
java.util.Arrays.sort(AppContextImpl.this.projects, ResourcePathComparator.getInstance());
|
||||
rootProjects.clear();
|
||||
addAll(rootProjects, projects);
|
||||
rootProjects.sort(ResourcePathComparator.getInstance());
|
||||
callback.onSuccess(resourceManager);
|
||||
eventBus.fireEvent(new WorkspaceReadyEvent(projects));
|
||||
}).catchError(error -> {
|
||||
|
|
@ -228,7 +234,7 @@ public class AppContextImpl implements AppContext,
|
|||
|
||||
@Override
|
||||
public String getWorkspaceName() {
|
||||
return usersWorkspace.getConfig().getName();
|
||||
return userWorkspace.getConfig().getName();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
|
@ -237,82 +243,60 @@ public class AppContextImpl implements AppContext,
|
|||
final ResourceDelta delta = event.getDelta();
|
||||
final Resource resource = delta.getResource();
|
||||
|
||||
/* Note: There is important to keep projects array in sorted state, because it is mutable and removing projects from it
|
||||
need array to be sorted. Search specific projects realized with binary search. */
|
||||
|
||||
if (!(resource.getResourceType() == PROJECT && resource.getLocation().segmentCount() == 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (projects == null) {
|
||||
return; //Normal situation, workspace config updated and project has not been loaded fully. Just skip this situation.
|
||||
}
|
||||
|
||||
if (delta.getKind() == ADDED) {
|
||||
Project[] newProjects = copyOf(projects, projects.length + 1);
|
||||
newProjects[projects.length] = (Project)resource;
|
||||
projects = newProjects;
|
||||
sort(projects, ResourcePathComparator.getInstance());
|
||||
} else if (delta.getKind() == REMOVED) {
|
||||
int size = projects.length;
|
||||
int index = java.util.Arrays.binarySearch(projects, resource, ResourcePathComparator.getInstance());
|
||||
int numMoved = projects.length - index - 1;
|
||||
if (numMoved > 0) {
|
||||
System.arraycopy(projects, index + 1, projects, index, numMoved);
|
||||
}
|
||||
projects = copyOf(projects, --size);
|
||||
if ((delta.getFlags() & (MOVED_FROM | MOVED_TO)) != 0) {
|
||||
|
||||
if (currentResource != null && currentResource.equals(delta.getResource())) {
|
||||
currentResource = null;
|
||||
}
|
||||
|
||||
if (currentResources != null) {
|
||||
for (Resource currentResource : currentResources) {
|
||||
if (currentResource.equals(delta.getResource())) {
|
||||
currentResources = Arrays.remove(currentResources, currentResource);
|
||||
for (Project rootProject : rootProjects) {
|
||||
if (rootProject.getLocation().equals(delta.getFromPath()) && resource.isProject()) {
|
||||
rootProjects.set(rootProjects.indexOf(rootProject), resource.asProject());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Resource selectedResource : selectedResources) {
|
||||
if (selectedResource.getLocation().equals(delta.getFromPath())) {
|
||||
selectedResources.set(selectedResources.indexOf(selectedResource), resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (resource.getLocation().segmentCount() == 1 && resource.isProject()) {
|
||||
boolean exists = rootProjects.stream().anyMatch(it -> it.getLocation().equals(resource.getLocation()));
|
||||
|
||||
if (!exists) {
|
||||
rootProjects.add(resource.asProject());
|
||||
rootProjects.sort(ResourcePathComparator.getInstance());
|
||||
}
|
||||
}
|
||||
} else if (delta.getKind() == REMOVED) {
|
||||
|
||||
for (Project rootProject : rootProjects) {
|
||||
if (rootProject.getLocation().equals(resource.getLocation()) && resource.isProject()) {
|
||||
rootProjects.remove(rootProjects.indexOf(rootProject));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Resource selectedResource : selectedResources) {
|
||||
if (selectedResource.getLocation().equals(resource.getLocation())) {
|
||||
selectedResources.remove(selectedResources.indexOf(selectedResource));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (delta.getKind() == UPDATED) {
|
||||
int index = -1;
|
||||
|
||||
// Project may be moved to another location, so we need to remove previous one and store new project in cache.
|
||||
|
||||
if (delta.getFlags() == MOVED_FROM) {
|
||||
for (int i = 0; i < projects.length; i++) {
|
||||
if (projects[i].getLocation().equals(delta.getFromPath())) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index = binarySearch(projects, resource);
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
projects[index] = (Project)resource;
|
||||
}
|
||||
|
||||
sort(projects, ResourcePathComparator.getInstance());
|
||||
} else if (delta.getKind() == SYNCHRONIZED && resource.isProject() && resource.getLocation().segmentCount() == 1) {
|
||||
for (int i = 0; i < projects.length; i++) {
|
||||
if (projects[i].getLocation().equals(resource.getLocation())) {
|
||||
projects[i] = (Project)resource;
|
||||
for (Project rootProject : rootProjects) {
|
||||
if (rootProject.getLocation().equals(resource.getLocation()) && resource.isProject()) {
|
||||
rootProjects.set(rootProjects.indexOf(rootProject), resource.asProject());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentResources != null) {
|
||||
for (int i = 0; i < currentResources.length; i++) {
|
||||
if (currentResources[i].getLocation().equals(resource.getLocation())) {
|
||||
currentResources[i] = resource;
|
||||
break;
|
||||
}
|
||||
for (Resource selectedResource : selectedResources) {
|
||||
if (selectedResource.getLocation().equals(resource.getLocation())) {
|
||||
selectedResources.set(selectedResources.indexOf(selectedResource), resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentResource != null && currentResource.getLocation().equals(resource.getLocation())) {
|
||||
currentResource = resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -332,54 +316,22 @@ public class AppContextImpl implements AppContext,
|
|||
return;
|
||||
}
|
||||
|
||||
currentResource = null;
|
||||
currentResources = null;
|
||||
selectedResources.clear();
|
||||
|
||||
if (selection == null || selection.getHeadElement() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Object headObject = selection.getHeadElement();
|
||||
final List<?> allObjects = selection.getAllElements();
|
||||
|
||||
if (headObject instanceof HasDataObject) {
|
||||
Object data = ((HasDataObject)headObject).getData();
|
||||
|
||||
if (data instanceof Resource) {
|
||||
currentResource = (Resource)data;
|
||||
}
|
||||
} else if (headObject instanceof Resource) {
|
||||
currentResource = (Resource)headObject;
|
||||
}
|
||||
|
||||
Set<Resource> resources = Sets.newHashSet();
|
||||
|
||||
for (Object object : allObjects) {
|
||||
if (object instanceof HasDataObject) {
|
||||
Object data = ((HasDataObject)object).getData();
|
||||
|
||||
if (data instanceof Resource) {
|
||||
resources.add((Resource)data);
|
||||
if (selection != null) {
|
||||
for (Object o : selection.getAllElements()) {
|
||||
if (o instanceof HasDataObject && ((HasDataObject)o).getData() instanceof Resource) {
|
||||
selectedResources.add((Resource)((HasDataObject)o).getData());
|
||||
} else if (o instanceof Resource) {
|
||||
selectedResources.add((Resource)o);
|
||||
}
|
||||
} else if (object instanceof Resource) {
|
||||
resources.add((Resource)object);
|
||||
}
|
||||
}
|
||||
|
||||
currentResources = resources.toArray(new Resource[resources.size()]);
|
||||
}
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final ResourceManager.ResourceManagerFactory resourceManagerFactory;
|
||||
private final Provider<EditorAgent> editorAgentProvider;
|
||||
private final Provider<AppStateManager> appStateManager;
|
||||
|
||||
private ResourceManager resourceManager;
|
||||
private Project[] projects;
|
||||
|
||||
@Override
|
||||
public Project[] getProjects() {
|
||||
return projects == null ? new Project[0] : projects;
|
||||
return rootProjects.toArray(new Project[rootProjects.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -391,22 +343,21 @@ public class AppContextImpl implements AppContext,
|
|||
|
||||
@Override
|
||||
public Resource getResource() {
|
||||
return currentResource;
|
||||
return selectedResources.isEmpty() ? null : selectedResources.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource[] getResources() {
|
||||
return currentResources;
|
||||
return selectedResources.toArray(new Resource[selectedResources.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project getRootProject() {
|
||||
if (projects == null) {
|
||||
if (rootProjects.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (currentResource == null || currentResources == null) {
|
||||
|
||||
if (selectedResources.isEmpty()) {
|
||||
EditorAgent editorAgent = editorAgentProvider.get();
|
||||
if (editorAgent == null) {
|
||||
return null;
|
||||
|
|
@ -421,37 +372,36 @@ public class AppContextImpl implements AppContext,
|
|||
|
||||
if (file instanceof SyntheticNode) {
|
||||
final Path projectPath = ((SyntheticNode)file).getProject();
|
||||
for (Project project : projects) {
|
||||
for (Project project : rootProjects) {
|
||||
if (project.getLocation().equals(projectPath)) {
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentResource == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
Project root = null;
|
||||
|
||||
Project root = null;
|
||||
|
||||
for (Project project : projects) {
|
||||
if (project.getLocation().isPrefixOf(currentResource.getLocation())) {
|
||||
root = project;
|
||||
for (Project project : rootProjects) {
|
||||
if (project.getLocation().isPrefixOf(selectedResources.get(0).getLocation())) {
|
||||
root = project;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 1; i < currentResources.length; i++) {
|
||||
if (!root.getLocation().isPrefixOf(currentResources[i].getLocation())) {
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
for (int i = 1; i < selectedResources.size(); i++) {
|
||||
if (!root.getLocation().isPrefixOf(selectedResources.get(i).getLocation())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -467,20 +417,18 @@ public class AppContextImpl implements AppContext,
|
|||
@Override
|
||||
public void onWorkspaceStopped(WorkspaceStoppedEvent event) {
|
||||
appStateManager.get().persistWorkspaceState(getWorkspaceId()).then(ignored -> {
|
||||
for (Project project : projects) {
|
||||
for (Project project : rootProjects) {
|
||||
eventBus.fireEvent(new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED)));
|
||||
}
|
||||
|
||||
projects = NO_PROJECTS;
|
||||
rootProjects.clear();
|
||||
resourceManager = null;
|
||||
});
|
||||
|
||||
//goto close all editors
|
||||
final EditorAgent editorAgent = editorAgentProvider.get();
|
||||
final List<EditorPartPresenter> openedEditors = editorAgent.getOpenedEditors();
|
||||
for (EditorPartPresenter editor : openedEditors) {
|
||||
editorAgent.closeEditor(editor);
|
||||
}
|
||||
clearRuntime();
|
||||
}
|
||||
|
||||
private void clearRuntime() {
|
||||
runtime = null;
|
||||
}
|
||||
|
||||
|
|
@ -491,7 +439,7 @@ public class AppContextImpl implements AppContext,
|
|||
@Override
|
||||
public String getMasterEndpoint() {
|
||||
String fromUrl = queryParameters.getByName("master");
|
||||
if(fromUrl == null || fromUrl.isEmpty())
|
||||
if (fromUrl == null || fromUrl.isEmpty())
|
||||
return masterFromIDEConfig();
|
||||
else
|
||||
return fromUrl;
|
||||
|
|
@ -500,7 +448,7 @@ public class AppContextImpl implements AppContext,
|
|||
@Override
|
||||
public String getDevAgentEndpoint() {
|
||||
String fromUrl = queryParameters.getByName("agent");
|
||||
if(fromUrl == null || fromUrl.isEmpty())
|
||||
if (fromUrl == null || fromUrl.isEmpty())
|
||||
return runtime.getDevMachine().getWsAgentBaseUrl();
|
||||
else
|
||||
return fromUrl;
|
||||
|
|
@ -515,13 +463,4 @@ public class AppContextImpl implements AppContext,
|
|||
public ActiveRuntime getActiveRuntime() {
|
||||
return runtime;
|
||||
}
|
||||
|
||||
|
||||
private static native String masterFromIDEConfig() /*-{
|
||||
if ($wnd.IDE && $wnd.IDE.config) {
|
||||
return $wnd.IDE.config.restContext;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}-*/;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,8 +279,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
|
|||
if (breakpointRenderer != null) {
|
||||
breakpointRenderer.setLineActive(lineNumber, true);
|
||||
}
|
||||
|
||||
preserveBreakpoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -311,8 +309,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
|
|||
|
||||
currentBreakpoint = null;
|
||||
}
|
||||
|
||||
preserveBreakpoints();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -502,10 +498,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
|
|||
List<StorableBreakpointDto> allDtoBreakpoints = new LinkedList<StorableBreakpointDto>();
|
||||
|
||||
List<Breakpoint> allBreakpoints = getBreakpointList();
|
||||
if (currentBreakpoint != null) {
|
||||
allBreakpoints.add(currentBreakpoint);
|
||||
}
|
||||
|
||||
for (Breakpoint breakpoint : allBreakpoints) {
|
||||
StorableBreakpointDto dto = dtoFactory.createDto(StorableBreakpointDto.class);
|
||||
dto.setType(breakpoint.getType());
|
||||
|
|
@ -525,9 +517,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
|
|||
dto.setFileProjectConfig(projectDto); //TODO need to think to change argument type from dto to model interface
|
||||
}
|
||||
}
|
||||
|
||||
dto.setActive(breakpoint.isActive());
|
||||
|
||||
allDtoBreakpoints.add(dto);
|
||||
}
|
||||
|
||||
|
|
@ -558,17 +547,12 @@ public class BreakpointManagerImpl implements BreakpointManager,
|
|||
return appContext.getWorkspaceRoot().getFile(dto.getPath()).then(new Function<Optional<File>, Void>() {
|
||||
@Override
|
||||
public Void apply(Optional<File> file) throws FunctionException {
|
||||
if (!file.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
if (dto.getType() == Type.CURRENT) {
|
||||
doSetCurrentBreakpoint(file.get(), dto.getLineNumber());
|
||||
} else {
|
||||
if (file.isPresent() && dto.getType() == Type.BREAKPOINT) {
|
||||
addBreakpoint(new Breakpoint(dto.getType(),
|
||||
dto.getLineNumber(),
|
||||
dto.getPath(),
|
||||
file.get(),
|
||||
dto.isActive()));
|
||||
false));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -672,12 +656,10 @@ public class BreakpointManagerImpl implements BreakpointManager,
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onBreakpointDeleted(Breakpoint breakpoint) {
|
||||
}
|
||||
public void onBreakpointDeleted(Breakpoint breakpoint) { }
|
||||
|
||||
@Override
|
||||
public void onAllBreakpointsDeleted() {
|
||||
}
|
||||
public void onAllBreakpointsDeleted() { }
|
||||
|
||||
@Override
|
||||
public void onPreStepInto() {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ import org.eclipse.che.ide.api.preferences.PreferencesManager;
|
|||
import org.eclipse.che.ide.api.resources.Resource;
|
||||
import org.eclipse.che.ide.api.resources.VirtualFile;
|
||||
import org.eclipse.che.ide.api.selection.Selection;
|
||||
import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent;
|
||||
import org.eclipse.che.ide.editor.synchronization.EditorContentSynchronizer;
|
||||
import org.eclipse.che.ide.part.editor.multipart.EditorMultiPartStackPresenter;
|
||||
import org.eclipse.che.ide.part.explorer.project.ProjectExplorerPresenter;
|
||||
|
|
@ -92,7 +93,8 @@ public class EditorAgentImpl implements EditorAgent,
|
|||
ActivePartChangedHandler,
|
||||
SelectionChangedHandler,
|
||||
WindowActionHandler,
|
||||
StateComponent {
|
||||
StateComponent,
|
||||
WorkspaceStoppedEvent.Handler {
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final WorkspaceAgent workspaceAgent;
|
||||
|
|
@ -138,6 +140,7 @@ public class EditorAgentImpl implements EditorAgent,
|
|||
eventBus.addHandler(ActivePartChangedEvent.TYPE, this);
|
||||
eventBus.addHandler(SelectionChangedEvent.TYPE, this);
|
||||
eventBus.addHandler(WindowActionEvent.TYPE, this);
|
||||
eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -622,6 +625,13 @@ public class EditorAgentImpl implements EditorAgent,
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorkspaceStopped(WorkspaceStoppedEvent event) {
|
||||
for (EditorPartPresenter editor : getOpenedEditors()) {
|
||||
closeEditor(editor);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RestoreStateEditorCallBack extends OpenEditorCallbackImpl {
|
||||
private final int cursorOffset;
|
||||
private final int topLine;
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@ import org.eclipse.che.ide.api.action.ActionManager;
|
|||
import org.eclipse.che.ide.api.action.DefaultActionGroup;
|
||||
import org.eclipse.che.ide.api.action.IdeActions;
|
||||
import org.eclipse.che.ide.api.event.FileEvent;
|
||||
import org.eclipse.che.ide.api.event.FileEvent.FileEventHandler;
|
||||
import org.eclipse.che.ide.api.resources.File;
|
||||
import org.eclipse.che.ide.api.resources.Resource;
|
||||
import org.eclipse.che.ide.api.resources.ResourceChangedEvent;
|
||||
import org.eclipse.che.ide.api.resources.VirtualFile;
|
||||
import org.eclipse.che.ide.util.Pair;
|
||||
import org.eclipse.che.ide.util.loging.Log;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
|
@ -30,6 +32,7 @@ import java.util.List;
|
|||
import static com.google.common.collect.Lists.newLinkedList;
|
||||
import static org.eclipse.che.ide.api.constraints.Constraints.FIRST;
|
||||
import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.OPEN;
|
||||
import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED;
|
||||
|
||||
/**
|
||||
* Default implementation of Recent File List.
|
||||
|
|
@ -37,7 +40,7 @@ import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.OPEN;
|
|||
* @author Vlad Zhukovskiy
|
||||
*/
|
||||
@Singleton
|
||||
public class RecentFileStore implements RecentFileList, FileEventHandler {
|
||||
public class RecentFileStore implements RecentFileList {
|
||||
|
||||
public static final int MAX_FILES_IN_STACK = 25;
|
||||
public static final int MAX_PATH_LENGTH_TO_DISPLAY = 50;
|
||||
|
|
@ -60,7 +63,32 @@ public class RecentFileStore implements RecentFileList, FileEventHandler {
|
|||
this.actionManager = actionManager;
|
||||
this.recentFileActionFactory = recentFileActionFactory;
|
||||
|
||||
eventBus.addHandler(FileEvent.TYPE, this);
|
||||
eventBus.addHandler(FileEvent.TYPE, event -> {
|
||||
if (event.getOperationType() == OPEN) {
|
||||
VirtualFile file = event.getFile();
|
||||
if (file instanceof File) {
|
||||
add((File)file);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.addHandler(ResourceChangedEvent.getType(), event -> {
|
||||
if (event.getDelta().getKind() != REMOVED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Resource resource = event.getDelta().getResource();
|
||||
|
||||
if (!resource.isFile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (recentStorage.contains(resource.asFile())) {
|
||||
if (!remove(resource.asFile())) {
|
||||
Log.warn(getClass(), "File has not been removed from recent list");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void ensureGroupExist() {
|
||||
|
|
@ -69,17 +97,6 @@ public class RecentFileStore implements RecentFileList, FileEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onFileOperation(FileEvent event) {
|
||||
if (event.getOperationType() == OPEN) {
|
||||
VirtualFile file = event.getFile();
|
||||
if (file instanceof File) {
|
||||
add((File)file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ public class ProjectExplorerStateComponent implements StateComponent {
|
|||
|
||||
@Override
|
||||
public void loadState(@NotNull JsonObject state) {
|
||||
|
||||
if (state.hasKey(SHOW_HIDDEN_FILES)) {
|
||||
projectExplorer.showHiddenFiles(state.getBoolean(SHOW_HIDDEN_FILES));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -544,7 +544,9 @@ public final class ResourceManager {
|
|||
checkArgument(!source.getLocation().isRoot(), "Workspace root is not allowed to be copied");
|
||||
|
||||
return findResource(destination, true).thenPromise(resource -> {
|
||||
checkState(!resource.isPresent() || force, "Cannot create '" + destination.toString() + "'. Resource already exists.");
|
||||
if (resource.isPresent() && !force){
|
||||
return promises.reject(new IllegalStateException("Cannot create '" + destination.toString() + "'. Resource already exists."));
|
||||
}
|
||||
|
||||
return ps.copy(source.getLocation(), destination.parent(), destination.lastSegment(), force)
|
||||
.thenPromise(ignored -> findResource(destination, false)
|
||||
|
|
|
|||
|
|
@ -174,4 +174,5 @@ public class WorkspacePresenter implements Presenter, WorkspaceView.ActionDelega
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -321,13 +321,17 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
|
|||
private JsonObject getPartStackState(PartStack partStack, WorkBenchPartController partController) {
|
||||
JsonObject state = Json.createObject();
|
||||
state.put("SIZE", partController.getSize());
|
||||
state.put("STATE", partStack.getPartStackState().name());
|
||||
|
||||
if (partStack.getParts().isEmpty()) {
|
||||
state.put("HIDDEN", true);
|
||||
} else {
|
||||
if (partStack.getActivePart() != null) {
|
||||
state.put("ACTIVE_PART", partStack.getActivePart().getClass().getName());
|
||||
}
|
||||
|
||||
state.put("HIDDEN", partController.isHidden());
|
||||
|
||||
JsonArray parts = Json.createArray();
|
||||
state.put("PARTS", parts);
|
||||
int i = 0;
|
||||
|
|
@ -343,18 +347,22 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
|
|||
@Override
|
||||
public void loadState(@NotNull JsonObject state) {
|
||||
if (state.hasKey("PART_STACKS")) {
|
||||
JsonObject part_stacks = state.getObject("PART_STACKS");
|
||||
for (String partStackType : part_stacks.keys()) {
|
||||
JsonObject partStack = part_stacks.getObject(partStackType);
|
||||
JsonObject partStacksState = state.getObject("PART_STACKS");
|
||||
|
||||
// Don't restore part dimensions if perspective is maximized.
|
||||
boolean perspectiveMaximized = isPerspectiveMaximized(partStacksState);
|
||||
|
||||
for (String partStackType : partStacksState.keys()) {
|
||||
JsonObject partStackState = partStacksState.getObject(partStackType);
|
||||
switch (PartStackType.valueOf(partStackType)) {
|
||||
case INFORMATION:
|
||||
restorePartController(partStacks.get(INFORMATION), belowPartController, partStack);
|
||||
loadPartStackState(partStacks.get(INFORMATION), belowPartController, partStackState, perspectiveMaximized);
|
||||
break;
|
||||
case NAVIGATION:
|
||||
restorePartController(partStacks.get(NAVIGATION), leftPartController, partStack);
|
||||
loadPartStackState(partStacks.get(NAVIGATION), leftPartController, partStackState, perspectiveMaximized);
|
||||
break;
|
||||
case TOOLING:
|
||||
restorePartController(partStacks.get(TOOLING), rightPartController, partStack);
|
||||
loadPartStackState(partStacks.get(TOOLING), rightPartController, partStackState, perspectiveMaximized);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -370,9 +378,38 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
|
|||
}
|
||||
}
|
||||
|
||||
private void restorePartController(PartStack partStack, WorkBenchPartController controller, JsonObject partStackJSON) {
|
||||
if (partStackJSON.hasKey("PARTS")) {
|
||||
JsonArray parts = partStackJSON.get("PARTS");
|
||||
/**
|
||||
* Determines whether perspective is maximized.
|
||||
*
|
||||
* @param partStacksState part stack state
|
||||
* @return <b>true</b> is perspective has maximized part stack
|
||||
*/
|
||||
private boolean isPerspectiveMaximized(JsonObject partStacksState) {
|
||||
for (String partStackType : partStacksState.keys()) {
|
||||
JsonObject partStackState = partStacksState.getObject(partStackType);
|
||||
if (partStackState.hasKey("STATE") && PartStack.State.MAXIMIZED.name().equals(partStackState.getString("STATE"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set part stack state.
|
||||
*
|
||||
* @param partStack
|
||||
* @param controller
|
||||
* @param partStackState
|
||||
* @param skipRestoreDimensions
|
||||
*/
|
||||
private void loadPartStackState(PartStack partStack,
|
||||
WorkBenchPartController controller,
|
||||
JsonObject partStackState,
|
||||
boolean skipRestoreDimensions) {
|
||||
if (partStackState.hasKey("PARTS")) {
|
||||
JsonArray parts = partStackState.get("PARTS");
|
||||
|
||||
for (int i = 0; i < parts.length(); i++) {
|
||||
JsonObject value = parts.get(i);
|
||||
if (value.hasKey("CLASS")) {
|
||||
|
|
@ -389,8 +426,8 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
|
|||
}
|
||||
|
||||
// restore part stack's active part
|
||||
if (partStackJSON.hasKey("ACTIVE_PART")) {
|
||||
String activePart = partStackJSON.getString("ACTIVE_PART");
|
||||
if (partStackState.hasKey("ACTIVE_PART")) {
|
||||
String activePart = partStackState.getString("ACTIVE_PART");
|
||||
Provider<PartPresenter> provider = dynaProvider.getProvider(activePart);
|
||||
if (provider != null) {
|
||||
partStack.setActivePart(provider.get());
|
||||
|
|
@ -403,13 +440,17 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
|
|||
return;
|
||||
}
|
||||
|
||||
if (partStackJSON.hasKey("HIDDEN") && partStackJSON.getBoolean("HIDDEN")) {
|
||||
if (skipRestoreDimensions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (partStackState.hasKey("HIDDEN") && partStackState.getBoolean("HIDDEN")) {
|
||||
partStack.minimize();
|
||||
return;
|
||||
}
|
||||
|
||||
if (partStackJSON.hasKey("SIZE")) {
|
||||
double size = partStackJSON.getNumber("SIZE");
|
||||
if (partStackState.hasKey("SIZE")) {
|
||||
double size = partStackState.getNumber("SIZE");
|
||||
|
||||
// Size of the part must not be less 100 pixels.
|
||||
if (size <= MIN_PART_SIZE) {
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.command.toolbar.previews;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/** Tests for {@link PreviewUrlItem}. */
|
||||
public class PreviewUrlItemTest {
|
||||
|
||||
private static final String URL = "http://preview.com";
|
||||
private static final String DISPLAY_NAME = "dev-machine:8080";
|
||||
|
||||
private PreviewUrlItem previewUrlItem;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
previewUrlItem = new PreviewUrlItem(URL, DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUrl() throws Exception {
|
||||
assertEquals(URL, previewUrlItem.getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDisplayName() throws Exception {
|
||||
assertEquals(DISPLAY_NAME, previewUrlItem.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012-2017 Codenvy, S.A.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Codenvy, S.A. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.che.ide.command.toolbar.previews;
|
||||
|
||||
import org.eclipse.che.api.machine.shared.dto.MachineRuntimeInfoDto;
|
||||
import org.eclipse.che.api.machine.shared.dto.ServerDto;
|
||||
import org.eclipse.che.ide.api.app.AppContext;
|
||||
import org.eclipse.che.ide.api.machine.DevMachine;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/** Tests for {@link PreviewUrl}. */
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PreviewUrlTest {
|
||||
|
||||
private static final String PREVIEW_URL = "http://preview.com/param";
|
||||
private static final String MACHINE_NAME = "dev-machine";
|
||||
private static final String SERVER_PORT = "8080";
|
||||
|
||||
@Mock
|
||||
private AppContext appContext;
|
||||
|
||||
private PreviewUrl previewUrl;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ServerDto server = mock(ServerDto.class);
|
||||
when(server.getUrl()).thenReturn("http://preview.com");
|
||||
|
||||
Map<String, ServerDto> servers = new HashMap<>();
|
||||
servers.put(SERVER_PORT + "/tcp", server);
|
||||
|
||||
MachineRuntimeInfoDto machineRuntimeInfo = mock(MachineRuntimeInfoDto.class);
|
||||
when(machineRuntimeInfo.getServers()).thenReturn(servers);
|
||||
|
||||
DevMachine devMachine = mock(DevMachine.class);
|
||||
when(devMachine.getDisplayName()).thenReturn(MACHINE_NAME);
|
||||
when(devMachine.getRuntime()).thenReturn(machineRuntimeInfo);
|
||||
|
||||
when(appContext.getDevMachine()).thenReturn(devMachine);
|
||||
|
||||
previewUrl = new PreviewUrl(PREVIEW_URL, appContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUrl() throws Exception {
|
||||
assertEquals(PREVIEW_URL, previewUrl.getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDisplayName() throws Exception {
|
||||
assertEquals(MACHINE_NAME + ':' + SERVER_PORT + "/param", previewUrl.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
|
@ -153,4 +153,5 @@ public class AppStateManagerTest {
|
|||
assertThat(jsonObject.hasKey("key1")).isTrue();
|
||||
assertThat(jsonObject.getString("key1")).isEqualTo("value1");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import org.eclipse.che.ide.api.constraints.Constraints;
|
|||
import org.eclipse.che.ide.api.editor.AbstractEditorPresenter;
|
||||
import org.eclipse.che.ide.api.event.ActivePartChangedEvent;
|
||||
import org.eclipse.che.ide.api.parts.PartPresenter;
|
||||
import org.eclipse.che.ide.api.parts.PartStack;
|
||||
import org.eclipse.che.ide.api.parts.PartStackView;
|
||||
import org.eclipse.che.ide.part.PartStackPresenter;
|
||||
import org.eclipse.che.ide.workspace.PartStackPresenterFactory;
|
||||
|
|
@ -39,6 +40,7 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
|
|
@ -110,6 +112,8 @@ public class AbstractPerspectivePersistenceTest {
|
|||
when(view.getInformationPanel()).thenReturn(simpleLayoutPanel);
|
||||
when(view.getToolPanel()).thenReturn(simplePanel);
|
||||
|
||||
when(partStackPresenter.getPartStackState()).thenReturn(PartStack.State.NORMAL);
|
||||
|
||||
when(controllerFactory.createController(Matchers.<SplitLayoutPanel>anyObject(),
|
||||
Matchers.<SimplePanel>anyObject())).thenReturn(workBenchController);
|
||||
|
||||
|
|
@ -224,4 +228,40 @@ public class AbstractPerspectivePersistenceTest {
|
|||
verify(partStackPresenter).addPart(partPresenter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRestoreMaximizedPartStack() throws Exception {
|
||||
JsonObject state = Json.createObject();
|
||||
|
||||
JsonObject parts = Json.createObject();
|
||||
state.put("PART_STACKS", parts);
|
||||
|
||||
JsonObject partStack = Json.createObject();
|
||||
parts.put("INFORMATION", partStack);
|
||||
|
||||
partStack.put("STATE", PartStack.State.MAXIMIZED.name());
|
||||
|
||||
JsonArray partsArray = Json.createArray();
|
||||
partStack.put("PARTS", partsArray);
|
||||
|
||||
JsonObject part = Json.createObject();
|
||||
partsArray.set(0, part);
|
||||
part.put("CLASS", "foo.Bar");
|
||||
|
||||
partStack.put("SIZE", 142);
|
||||
|
||||
// partStackPresenter.getParts() must return non empty list
|
||||
final List<PartPresenter> partPresenters = new ArrayList<>();
|
||||
partPresenters.add(partPresenter);
|
||||
when(partStackPresenter.getParts()).thenAnswer(new Answer<List<? extends PartPresenter>>() {
|
||||
@Override
|
||||
public List<? extends PartPresenter> answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return partPresenters;
|
||||
}
|
||||
});
|
||||
|
||||
perspective.loadState(state);
|
||||
|
||||
verify(workBenchController, never()).setSize(142d);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,4 +106,5 @@ public class WorkspacePresenterPersistenceTest {
|
|||
|
||||
verify(perspective1).loadState(perspective1State);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-ide-generators</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-ide-stacks</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-ide-templates</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-ide-ui</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-orion-editor</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@
|
|||
}
|
||||
|
||||
.textViewTooltipOnHover {
|
||||
overflow: visible;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.orionCodenvy .textViewFind {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-core-ide-parent</artifactId>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>che-core-commons-gwt</artifactId>
|
||||
<name>Che Core :: Commons :: GWT</name>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<parent>
|
||||
<artifactId>che-infrastructures-parent</artifactId>
|
||||
<groupId>org.eclipse.che</groupId>
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>infrastructure-docker</artifactId>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ package org.eclipse.che.workspace.infrastructure.docker;
|
|||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.eclipse.che.api.core.NotFoundException;
|
||||
|
|
@ -33,7 +34,9 @@ import org.eclipse.che.plugin.docker.client.UserSpecificDockerRegistryCredential
|
|||
import org.eclipse.che.plugin.docker.client.exception.ContainerNotFoundException;
|
||||
import org.eclipse.che.plugin.docker.client.exception.ImageNotFoundException;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerConfig;
|
||||
import org.eclipse.che.plugin.docker.client.json.ContainerInfo;
|
||||
import org.eclipse.che.plugin.docker.client.json.HostConfig;
|
||||
import org.eclipse.che.plugin.docker.client.json.ImageConfig;
|
||||
import org.eclipse.che.plugin.docker.client.json.PortBinding;
|
||||
import org.eclipse.che.plugin.docker.client.json.Volume;
|
||||
import org.eclipse.che.plugin.docker.client.json.container.NetworkingConfig;
|
||||
|
|
@ -76,6 +79,7 @@ import java.util.regex.Pattern;
|
|||
import static java.lang.String.format;
|
||||
import static java.lang.Thread.sleep;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
|
@ -95,6 +99,28 @@ public class ServiceStarter {
|
|||
|
||||
public static final Pattern SNAPSHOT_LOCATION_PATTERN = Pattern.compile("(.+/)?" + MACHINE_SNAPSHOT_PREFIX + ".+");
|
||||
|
||||
static final String CONTAINER_EXITED_ERROR = "We detected that a machine exited unexpectedly. " +
|
||||
"This may be caused by a container in interactive mode " +
|
||||
"or a container that requires additional arguments to start. " +
|
||||
"Please check the container recipe.";
|
||||
|
||||
// CMDs and entrypoints that lead to exiting of container right after start
|
||||
private Set<List<String>> badCMDs = ImmutableSet.of(singletonList("/bin/bash"),
|
||||
singletonList("/bin/sh"),
|
||||
singletonList("bash"),
|
||||
singletonList("sh"),
|
||||
Arrays.asList("/bin/sh", "-c", "/bin/sh"),
|
||||
Arrays.asList("/bin/sh", "-c", "/bin/bash"),
|
||||
Arrays.asList("/bin/sh", "-c", "bash"),
|
||||
Arrays.asList("/bin/sh", "-c", "sh"));
|
||||
private Set<List<String>> badEntrypoints =
|
||||
ImmutableSet.<List<String>>builder().addAll(badCMDs)
|
||||
.add(Arrays.asList("/bin/sh", "-c"))
|
||||
.add(Arrays.asList("/bin/bash", "-c"))
|
||||
.add(Arrays.asList("sh", "-c"))
|
||||
.add(Arrays.asList("bash", "-c"))
|
||||
.build();
|
||||
|
||||
private final DockerConnector docker;
|
||||
private final UserSpecificDockerRegistryCredentialsProvider dockerCredentials;
|
||||
private final ExecutorService executor;
|
||||
|
|
@ -273,6 +299,8 @@ public class ServiceStarter {
|
|||
|
||||
docker.startContainer(StartContainerParams.create(container));
|
||||
|
||||
checkContainerIsRunning(container);
|
||||
|
||||
readContainerLogsInSeparateThread(container,
|
||||
workspaceId,
|
||||
service.getId(),
|
||||
|
|
@ -478,6 +506,8 @@ public class ServiceStarter {
|
|||
|
||||
addStaticDockerConfiguration(config);
|
||||
|
||||
setNonExitingContainerCommandIfNeeded(config);
|
||||
|
||||
return docker.createContainer(CreateContainerParams.create(config)
|
||||
.withContainerName(service.getContainerName()))
|
||||
.getId();
|
||||
|
|
@ -523,6 +553,36 @@ public class ServiceStarter {
|
|||
composeService.getNetworks().addAll(additionalNetworks);
|
||||
}
|
||||
|
||||
// We can detect certain situation when container exited right after start.
|
||||
// We can detect
|
||||
// - when no command/entrypoint is set
|
||||
// - when most common shell interpreters are used and require additional arguments
|
||||
// - when most common shell interpreters are used and they require interactive mode which we don't support
|
||||
// When we identify such situation we change CMD/entrypoint in such a way that it runs "tail -f /dev/null".
|
||||
// This command does nothing and lasts until workspace is stopped.
|
||||
// Images such as "ubuntu" or "openjdk" fits this situation.
|
||||
protected void setNonExitingContainerCommandIfNeeded(ContainerConfig containerConfig) throws IOException {
|
||||
ImageConfig imageConfig = docker.inspectImage(containerConfig.getImage()).getConfig();
|
||||
List<String> cmd = imageConfig.getCmd() == null ?
|
||||
null : Arrays.asList(imageConfig.getCmd());
|
||||
List<String> entrypoint = imageConfig.getEntrypoint() == null ?
|
||||
null : Arrays.asList(imageConfig.getEntrypoint());
|
||||
|
||||
if ((entrypoint == null || badEntrypoints.contains(entrypoint)) && (cmd == null || badCMDs.contains(cmd))) {
|
||||
containerConfig.setCmd("tail", "-f", "/dev/null");
|
||||
containerConfig.setEntrypoint((String[])null);
|
||||
}
|
||||
}
|
||||
|
||||
// Inspect container right after start to check if it is running,
|
||||
// otherwise throw error that command should not exit right after container start
|
||||
protected void checkContainerIsRunning(String container) throws IOException, ServerException {
|
||||
ContainerInfo containerInfo = docker.inspectContainer(container);
|
||||
if ("exited".equals(containerInfo.getState().getStatus())) {
|
||||
throw new ServerException(CONTAINER_EXITED_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void readContainerLogsInSeparateThread(String container,
|
||||
String workspaceId,
|
||||
String machineId,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue