Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ide] Add IntelliJ desktop IDE support #6270

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .werft/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ export async function build(context, version) {
if (withContrib || publishRelease) {
exec(`leeway build --docker-build-options network=host --werft=true -c remote ${dontTest ? '--dont-test' : ''} -Dversion=${version} -DimageRepoBase=${imageRepo} contrib:all`);
}
exec(`leeway build --docker-build-options network=host --werft=true -c remote ${retag} --coverage-output-path=${coverageOutput} -Dversion=${version} -DremoveSources=false -DimageRepoBase=${imageRepo} -DlocalAppVersion=${localAppVersion} -DnpmPublishTrigger=${publishToNpm ? Date.now() : 'false'}`);
const jetbrainsArgs = `-DINTELLIJ_PLUGIN_PLATFORM_VERSION=${process.env.INTELLIJ_PLUGIN_PLATFORM_VERSION} -DINTELLIJ_BACKEND_URL=${process.env.INTELLIJ_BACKEND_URL} -DGOLAND_BACKEND_URL=${process.env.GOLAND_BACKEND_URL}`;
exec(`leeway build --docker-build-options network=host --werft=true -c remote ${retag} --coverage-output-path=${coverageOutput} -Dversion=${version} -DremoveSources=false -DimageRepoBase=${imageRepo} -DlocalAppVersion=${localAppVersion} ${jetbrainsArgs} -DnpmPublishTrigger=${publishToNpm ? Date.now() : 'false'}`);
if (publishRelease) {
try {
werft.phase("publish", "checking version semver compliance...");
Expand Down
3 changes: 3 additions & 0 deletions .werft/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ pod:
secretKeyRef:
name: codecov
key: token
envFrom:
- secretRef:
name: jetbrains-secrets
command:
- bash
- -c
Expand Down
17 changes: 16 additions & 1 deletion chart/templates/server-ide-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
{{ template "gitpod.comp.imageRepo" . }}:{{- $comp.insidersVersion | default (include "gitpod.comp.version" .) -}}
{{- end -}}

{{- define "ide-images-aliases"}}
{{- $ := .root -}}
{{- $gp := .gp -}}
code: {{ (include "stable-image-full" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.codeImage)) }}
code-latest: {{ (include "insider-image-full" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.codeImage)) }}
{{ end }}

{{- define "desktop-ide-images-aliases"}}
{{- $ := .root -}}
{{- $gp := .gp -}}
intellij: {{ (include "gitpod.comp.imageFull" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.desktopIdeImages.intellij)) }}
goland: {{ (include "gitpod.comp.imageFull" (dict "root" $ "gp" $gp "comp" $gp.components.workspace.desktopIdeImages.goland)) }}
{{ end }}

{{- if $comp.serverIdeConfigDeploy.enabled }}
apiVersion: v1
kind: ConfigMap
Expand All @@ -33,6 +47,7 @@ data:
{
"ideVersion": "{{ .Values.components.workspace.codeImage.stableVersion }}",
"ideImageRepo": "{{ template "gitpod.comp.imageRepo" (dict "root" . "gp" $.Values "comp" .Values.components.workspace.codeImage) }}",
"ideImageAliases": {{ (dict "code-latest" (include "insider-image-full" (dict "root" . "gp" $.Values "comp" .Values.components.workspace.codeImage)) "code" (include "stable-image-full" (dict "root" . "gp" $.Values "comp" .Values.components.workspace.codeImage))) | toJson }}
"ideImageAliases": {{ (include "ide-images-aliases" (dict "root" . "gp" $.Values)) | fromYaml | toJson }},
"desktopIdeImageAliases": {{ (include "desktop-ide-images-aliases" (dict "root" . "gp" $.Values)) | fromYaml | toJson }}
}
{{- end }}
5 changes: 5 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ components:
imageName: "ide/code"
stableVersion: "commit-7107fb8bde0b1f92265402ad5fa09a51022b14dd"
insidersVersion: "nightly"
desktopIdeImages:
intellij:
imageName: "ide/intellij"
goland:
imageName: "ide/goland"
supervisor:
imageName: "supervisor"
dockerUp:
Expand Down
3 changes: 2 additions & 1 deletion components/BUILD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ packages:
- dev:all-app
- installer:app
- components/gitpod-protocol:all
- components/ide/jetbrains/backend-plugin:plugin
- operations/observability/mixins:lint
- name: docker-versions
type: docker
Expand All @@ -41,6 +40,8 @@ packages:
- components/ee/ws-scheduler:docker
- components/gitpod-db:docker
- components/ide/code:docker
- components/ide/jetbrains/image:intellij
- components/ide/jetbrains/image:goland
- components/ide/theia:docker
- components/image-builder:docker
- components/image-builder-mk3:docker
Expand Down
1 change: 1 addition & 0 deletions components/dashboard/src/images/golandLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions components/dashboard/src/images/intellijIdeaLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions components/dashboard/src/settings/Preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
*/

import { useContext, useState } from "react";
import CheckBox from "../components/CheckBox";
import { PageWithSubMenu } from "../components/PageWithSubMenu";
import SelectableCard from "../components/SelectableCard";
import Tooltip from "../components/Tooltip";
import vscode from '../images/vscode.svg';
import ideaLogo from '../images/intellijIdeaLogo.svg';
import golandLogo from '../images/golandLogo.svg';
import { getGitpodService } from "../service/service";
import { ThemeContext } from "../theme-context";
import { UserContext } from "../user-context";
Expand All @@ -19,6 +22,7 @@ type Theme = 'light' | 'dark' | 'system';
export default function Preferences() {
const { user } = useContext(UserContext);
const { setIsDark } = useContext(ThemeContext);

const [defaultIde, setDefaultIde] = useState<string>(user?.additionalData?.ideSettings?.defaultIde || 'code');
const actuallySetDefaultIde = async (value: string) => {
const additionalData = user?.additionalData || {};
Expand All @@ -28,6 +32,31 @@ export default function Preferences() {
await getGitpodService().server.updateLoggedInUser({ additionalData });
setDefaultIde(value);
}

const desktopIdeFeatureEnabled = !!user?.rolesOrPermissions?.includes('admin');
geropl marked this conversation as resolved.
Show resolved Hide resolved

const [defaultDesktopIde, setDefaultDesktopIde] = useState<string>(user?.additionalData?.ideSettings?.defaultDesktopIde || 'intellij');
const actuallySetDefaultDesktopIde = async (value: string) => {
const additionalData = user?.additionalData || {};
const settings = additionalData.ideSettings || {};
settings.defaultDesktopIde = value;
additionalData.ideSettings = settings;
await getGitpodService().server.updateLoggedInUser({ additionalData });
setDefaultDesktopIde(value);
}

const [useDesktopIde, setUseDesktopIde] = useState<boolean>(user?.additionalData?.ideSettings?.useDesktopIde || false);
const actuallySetUseDesktopIde = async (value: boolean) => {
const additionalData = user?.additionalData || {};
const settings = additionalData.ideSettings || {};
settings.useDesktopIde = value;
// Make sure that default desktop IDE is set even when the user did not explicitly select one.
settings.defaultDesktopIde = defaultDesktopIde;
additionalData.ideSettings = settings;
await getGitpodService().server.updateLoggedInUser({ additionalData });
setUseDesktopIde(value);
}

const [theme, setTheme] = useState<Theme>(localStorage.theme || 'light');
const actuallySetTheme = (theme: Theme) => {
if (theme === 'dark' || theme === 'system') {
Expand Down Expand Up @@ -59,6 +88,29 @@ export default function Preferences() {
</SelectableCard>
</Tooltip>
</div>
{desktopIdeFeatureEnabled &&
<div className="mt-4 space-x-4 flex">
<CheckBox
title="Use Desktop IDE"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Shall we use the same verb as the action button on the loading page?

Suggested change
title="Use Desktop IDE"
title="Open in Desktop IDE"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to: #6438

desc="Choose whether you would like to open your workspace in a desktop IDE instead."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Given that we could add more local IDEs here in the future, could it help to make the UI less verbose by using a dropdown selection here instead of the card selection component? This could also help with using proper identation of the options.

Early design draft with a dropdown selection
Screenshot 2021-10-22 at 7 34 04 PM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a longer conversation regarding this one in: #5641 (comment), let me cross-reference it there, also.

checked={useDesktopIde}
onChange={(evt) => actuallySetUseDesktopIde(evt.target.checked)} />
</div>
}
{desktopIdeFeatureEnabled && useDesktopIde &&
<div className="mt-4 space-x-4 flex">
<SelectableCard className="w-36 h-40" title="IntelliJ IDEA" selected={defaultDesktopIde === 'intellij'} onClick={() => actuallySetDefaultDesktopIde('intellij')}>
<div className="flex justify-center mt-3">
<img className="w-16 filter-grayscale self-center" src={ideaLogo} />
</div>
</SelectableCard>
<SelectableCard className="w-36 h-40" title="GoLand" selected={defaultDesktopIde === 'goland'} onClick={() => actuallySetDefaultDesktopIde('goland')}>
<div className="flex justify-center mt-3">
<img className="w-16 filter-grayscale self-center" src={golandLogo} />
</div>
</SelectableCard>
</div>
}
<h3 className="mt-12">Theme</h3>
<p className="text-base text-gray-500">Early bird or night owl? Choose your side.</p>
<div className="mt-4 space-x-4 flex">
Expand Down
11 changes: 7 additions & 4 deletions components/dashboard/src/start/StartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export enum StartPhase {
Creating = 2,
Starting = 3,
Running = 4,
Stopping = 5,
Stopped = 6,
IdeReady = 5,
Stopping = 6,
Stopped = 7,
};

function getPhaseTitle(phase?: StartPhase, error?: StartWorkspaceError) {
Expand All @@ -32,6 +33,8 @@ function getPhaseTitle(phase?: StartPhase, error?: StartWorkspaceError) {
return "Starting";
case StartPhase.Running:
return "Starting";
case StartPhase.IdeReady:
return "Your Workspace is Ready!";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Wondering if a simpler phase would be better here.

Suggested change
return "Your Workspace is Ready!";
return "Running";

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to: #6438

case StartPhase.Stopping:
return "Stopping";
case StartPhase.Stopped:
Expand Down Expand Up @@ -84,9 +87,9 @@ export function StartPage(props: StartPageProps) {
return <div className="w-screen h-screen align-middle">
<div className="flex flex-col mx-auto items-center text-center h-screen">
<div className="h-1/3"></div>
<img src={gitpodIcon} alt="Gitpod's logo" className={`h-16 flex-shrink-0 ${(error || phase === StartPhase.Stopped) ? '' : 'animate-bounce'}`} />
<img src={gitpodIcon} alt="Gitpod's logo" className={`h-16 flex-shrink-0 ${(error || phase === StartPhase.Stopped || phase === StartPhase.IdeReady) ? '' : 'animate-bounce'}`} />
<h3 className="mt-8 text-xl">{title}</h3>
{typeof(phase) === 'number' && phase < StartPhase.Stopping && <ProgressBar phase={phase} error={!!error} />}
{typeof(phase) === 'number' && phase < StartPhase.IdeReady && <ProgressBar phase={phase} error={!!error} />}
{error && <StartError error={error} />}
{props.children}
</div>
Expand Down
30 changes: 28 additions & 2 deletions components/dashboard/src/start/StartWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export interface StartWorkspaceState {
workspace?: Workspace;
hasImageBuildLogs?: boolean;
error?: StartWorkspaceError;
desktopIde?: {
link: string
label: string
}
}

export default class StartWorkspace extends React.Component<StartWorkspaceProps, StartWorkspaceState> {
Expand All @@ -46,6 +50,10 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
const error = { message: event.data.state.ideFrontendFailureCause };
this.setState({ error });
}
if (event.data.state.desktopIdeLink) {
const label = event.data.state.desktopIdeLabel || "Open Desktop IDE";
this.setState({ desktopIde: { link: event.data.state.desktopIdeLink, label } });
}
}
}
window.addEventListener('message', setStateEventListener, false);
Expand Down Expand Up @@ -269,8 +277,26 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
if (isHeadless) {
return <HeadlessWorkspaceView instanceId={this.state.workspaceInstance.id} />;
}
phase = StartPhase.Running;
statusMessage = <p className="text-base text-gray-400">Opening IDE …</p>;
if (!this.state.desktopIde) {
phase = StartPhase.Running;
statusMessage = <p className="text-base text-gray-400">Opening IDE …</p>;
} else {
phase = StartPhase.IdeReady;
statusMessage = <div>
<div className="flex space-x-3 items-center text-left rounded-xl m-auto px-4 h-16 w-72 mt-4 mb-2 bg-gray-100 dark:bg-gray-800">
<div className="rounded-full w-3 h-3 text-sm bg-green-500">&nbsp;</div>
<div>
<p className="text-gray-700 dark:text-gray-200 font-semibold">{this.state.workspaceInstance.workspaceId}</p>
<a target="_parent" href={this.state.workspace?.contextURL}><p className="w-56 truncate hover:text-blue-600 dark:hover:text-blue-400" >{this.state.workspace?.contextURL}</p></a>
</div>
</div>
<div className="mt-10 justify-center flex space-x-2">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Although out of the scope of these changes, here's an early draft on how these actions could be unified, compressed, and always available during workspace loading phase based on a relevant discussion (internal). 🍔

We could later also include a dropdown option in the workspace start so that users can change this setting ad-hoc during workspace start in the loading phases where this is possible. 💡

In contrast, a dropdown selection could allow the loading phase to proceed and only when ready and needed would prompt to open the local VS Code application, but I could be wrong here. This would also connect and surface the selection option from /settings.

Thinking out loud here, I’d expect this option to be non-editable once the loading phase is near the end where we load the Web IDE.

Screenshot 2021-10-22 at 7 43 50 PM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the heads up!

Let's address this in: #5641

<a target="_blank" href={this.state.desktopIde.link}><button>{this.state.desktopIde.label}</button></a>
<button className="secondary" onClick={() => window.parent.postMessage({ type: 'openBrowserIde' }, '*')}>Open VS Code in Browser</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: What do you think of rephrasing this and associating the browser IDE with the product?

Suggested change
<button className="secondary" onClick={() => window.parent.postMessage({ type: 'openBrowserIde' }, '*')}>Open VS Code in Browser</button>
<button className="secondary" onClick={() => window.parent.postMessage({ type: 'openBrowserIde' }, '*')}>Open in Gitpod</button>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come across this, and I don't think it's a good idea. The "Open in Gitpod" buttons that exist around the internet in Git repositories will lead to whatever IDE you have configured in your account. This button "Open VS Code in Browser" will lead to VS Code no matter what.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point @atduarte!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: What do you think of changing the order of the actions so that the primary action is always on the outer right? This will follow the existing pattern that exists for timed out workspaces, and more.

Desktop IDE options Stopped Workspace
Screenshot 2021-10-22 at 7 29 37 PM Screenshot 2021-10-22 at 7 33 16 PM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to: #6438

</div>
</div>;
}

break;

// Interrupted is an exceptional state where the container should be running but is temporarily unavailable.
Expand Down
2 changes: 1 addition & 1 deletion components/gitpod-protocol/java/.project
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</natures>
<filteredResources>
<filter>
<id>1632232136569</id>
<id>0</id>
<name></name>
<type>30</type>
<matcher>
Expand Down
2 changes: 2 additions & 0 deletions components/gitpod-protocol/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ export interface EmailNotificationSettings {

export type IDESettings = {
defaultIde?: string
useDesktopIde?: boolean
defaultDesktopIde?: string
}

export interface UserPlatform {
Expand Down
3 changes: 3 additions & 0 deletions components/gitpod-protocol/src/workspace-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,7 @@ export interface WorkspaceInstanceConfiguration {

// ideImage is the ref of the IDE image this instance uses.
ideImage: string;

// desktopIdeImage is the ref of the desktop IDE image this instance uses.
desktopIdeImage?: string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to future self: Might be worth having a ordered map of layers instead of new fields here.

}
2 changes: 1 addition & 1 deletion components/ide/jetbrains/backend-plugin/.project
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</natures>
<filteredResources>
<filter>
<id>1634286379907</id>
<id>0</id>
<name></name>
<type>30</type>
<matcher>
Expand Down
3 changes: 3 additions & 0 deletions components/ide/jetbrains/backend-plugin/BUILD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ packages:
- "gradle/wrapper/*"
- "gradlew"
- "settings.gradle.kts"
argdeps:
- INTELLIJ_PLUGIN_PLATFORM_VERSION
env:
- JAVA_HOME=/home/gitpod/.sdkman/candidates/java/current
- INTELLIJ_PLUGIN_PLATFORM_VERSION=${INTELLIJ_PLUGIN_PLATFORM_VERSION}
config:
commands:
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "buildPlugin"]
52 changes: 52 additions & 0 deletions components/ide/jetbrains/image/BUILD.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
packages:
- name: docker
type: generic
argdeps:
- version
deps:
- :intellij
- :goland
- name: intellij
type: docker
srcs:
- "startup.sh"
- "supervisor-ide-config_intellij.json"
- "status/go.mod"
- "status/main.go"
deps:
- components/ide/jetbrains/backend-plugin:plugin
argdeps:
- imageRepoBase
- INTELLIJ_BACKEND_URL
config:
dockerfile: leeway.Dockerfile
metadata:
helm-component: workspace.desktopIdeImages.intellij
buildArgs:
JETBRAINS_BACKEND_URL: ${INTELLIJ_BACKEND_URL}
SUPERVISOR_IDE_CONFIG: supervisor-ide-config_intellij.json
image:
- ${imageRepoBase}/ide/intellij:${version}
- ${imageRepoBase}/ide/intellij:commit-${__git_commit}
- name: goland
type: docker
srcs:
- "startup.sh"
- "supervisor-ide-config_goland.json"
- "status/go.mod"
- "status/main.go"
deps:
- components/ide/jetbrains/backend-plugin:plugin
argdeps:
- imageRepoBase
- GOLAND_BACKEND_URL
config:
dockerfile: leeway.Dockerfile
metadata:
helm-component: workspace.desktopIdeImages.goland
buildArgs:
JETBRAINS_BACKEND_URL: ${GOLAND_BACKEND_URL}
SUPERVISOR_IDE_CONFIG: supervisor-ide-config_goland.json
image:
- ${imageRepoBase}/ide/goland:${version}
- ${imageRepoBase}/ide/goland:commit-${__git_commit}
Loading