Skip to content

Commit 55b137c

Browse files
committed
[dashboard] github app installation
1 parent 7b4c1c3 commit 55b137c

File tree

4 files changed

+109
-2
lines changed

4 files changed

+109
-2
lines changed

components/dashboard/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const Integrations = React.lazy(() => import(/* webpackPrefetch: true */ './sett
2525
const Preferences = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Preferences'));
2626
const StartWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './start/StartWorkspace'));
2727
const CreateWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './start/CreateWorkspace'));
28+
const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ './prebuilds/InstallGitHubApp'));
2829

2930
function Loading() {
3031
return <>
@@ -79,6 +80,7 @@ function App() {
7980
<Route path="/teams" exact component={Teams} />
8081
<Route path="/variables" exact component={EnvironmentVariables} />
8182
<Route path="/preferences" exact component={Preferences} />
83+
<Route path="/install-github-app" exact component={InstallGitHubApp} />
8284

8385
<Route path={["/", "/login"]} exact>
8486
<Redirect to="/workspaces"/>

components/dashboard/src/components/Modal.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import { useEffect } from "react";
88

99
export default function Modal(props: {
10+
title?: string;
11+
buttons?: React.ReactChild[] | React.ReactChild,
1012
children: React.ReactChild[] | React.ReactChild,
1113
visible: boolean,
1214
closeable?: boolean,
@@ -58,7 +60,14 @@ export default function Modal(props: {
5860
</svg>
5961
</div>
6062
)}
61-
{props.children}
63+
{props.title ? <><h3 className="pb-2">{props.title}</h3>
64+
<div className="border-t border-b border-gray-200 mt-2 -mx-6 px-6 py-4">
65+
{props.children}
66+
</div>
67+
<div className="flex justify-end mt-6 space-x-2">
68+
{props.buttons}
69+
</div></>:
70+
props.children }
6271
</div>
6372
</div>
6473
</div>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { useLocation } from "react-router";
8+
import Modal from "../components/Modal";
9+
import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred";
10+
import { getGitpodService, gitpodHostUrl } from "../service/service";
11+
import { useState } from "react";
12+
import info from "../images/info.svg";
13+
14+
async function registerApp(installationId: string, setModal: (modal: 'done' | string | undefined) => void) {
15+
try {
16+
await getGitpodService().server.registerGithubApp(installationId);
17+
18+
const returnTo = encodeURIComponent(gitpodHostUrl.with({ pathname: `login-success` }).toString());
19+
const url = gitpodHostUrl.withApi({
20+
pathname: '/authorize',
21+
search: `returnTo=${returnTo}&host=github.com&scopes=repo`
22+
}).toString();
23+
window.open(url, "gitpod-login");
24+
25+
const result = new Deferred<void>(1000 * 60 * 10 /* 10 min */);
26+
result.promise.catch(e => setModal('error'));
27+
const listener = (event: MessageEvent<any>) => {
28+
// todo: check event.origin
29+
if (event.data === "auth-success") {
30+
if (event.source && "close" in event.source && event.source.close) {
31+
console.log(`try to close window`);
32+
event.source.close();
33+
} else {
34+
// todo: not here, but add a button to the /login-success page to close, if this should not work as expected
35+
}
36+
window.removeEventListener("message", listener);
37+
setModal('done');
38+
result.resolve();
39+
}
40+
};
41+
window.addEventListener("message", listener);
42+
return result.promise;
43+
} catch (e) {
44+
setModal(e.message);
45+
}
46+
}
47+
48+
export default function InstallGitHubApp() {
49+
const location = useLocation();
50+
const [modal, setModal] = useState<'done' | string | undefined>();
51+
const params = new URLSearchParams(location.search);
52+
const installationId = params.get("installation_id") || undefined;
53+
if (!installationId) {
54+
return <div className="lg:px-28 px-10 flex flex-col space-y-2">
55+
<div className="px-6 py-3 flex justify-between space-x-2 text-gray-400 border-t border-gray-200 h-96">
56+
<div className="flex flex-col items-center w-96 m-auto">
57+
<h3 className="text-center pb-3 text-gray-500">No Installation ID Found</h3>
58+
<div className="text-center pb-6 text-gray-500">Did you came here from the GitHub app's page?</div>
59+
</div>
60+
</div>
61+
</div>
62+
}
63+
64+
const goToApp = () => window.location.href = gitpodHostUrl.toString();
65+
66+
return <>
67+
<div className="lg:px-28 px-10 flex flex-col space-y-2">
68+
<div className="px-6 py-3 flex justify-between space-x-2 text-gray-400">
69+
<div className="flex flex-col items-center m-auto max-w-lg mt-40">
70+
<h3 className="text-center pb-3 text-gray-500">Install GitHub App</h3>
71+
<div className="text-center pb-6 text-gray-500">You are about to install the GitHub app for Gitpod.</div>
72+
<div className="flex rounded-md bg-gray-200 p-3">
73+
<img className="w-4 h-4 ml-2 mr-3 mt-1" src={info} alt="info" />
74+
<span className="text-gray-500">This action will also allow Gitpod to access private repositories. You can edit git provider permissions later in user settings.</span>
75+
</div>
76+
<div className="mt-6">
77+
<button className="secondary">Cancel</button>
78+
<button className="ml-2" onClick={() => registerApp(installationId, setModal)}>Install App</button>
79+
</div>
80+
</div>
81+
</div>
82+
</div>
83+
<Modal title="Installation Successfull" visible={modal === 'done'} onClose={goToApp} buttons={<button onClick={goToApp}>Go to Dashboard</button>}>
84+
<div className="pb-6 text-gray-500">The GitHub app was installed successfully. Have a look at the <a className="text-blue-500" href="https://www.gitpod.io/docs/prebuilds/" rel="noopener">documentation</a> to find out how to configure it.</div>
85+
</Modal>
86+
<Modal title="Failed to Install" visible={!!modal && modal !== 'done'} onClose={goToApp} buttons={[
87+
<button className="secondary" onClick={goToApp}>Cancel</button>,
88+
<button className="" onClick={() => registerApp(installationId, setModal)}>Try Again</button>
89+
]}>
90+
<div className="pb-6 text-gray-500">Could not install the GitHub app.</div>
91+
<div className="flex rounded-md bg-gray-200 p-3">
92+
<img className="w-4 h-4 ml-2 mr-3 mt-1" src={info} alt="info" />
93+
<span className="text-gray-500">{modal}</span>
94+
</div>
95+
</Modal></>;
96+
}

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,7 @@ export class GitpodServerImpl<Client extends GitpodClient, Server extends Gitpod
12051205
const user = this.checkAndBlockUser();
12061206

12071207
if (!this.env.githubAppEnabled) {
1208-
throw new ResponseError(ErrorCodes.NOT_FOUND, 'User is not authenticated. Please login.');
1208+
throw new ResponseError(ErrorCodes.NOT_FOUND, 'No GitHub app enabled for this installation. Please talk to your administrator.');
12091209
}
12101210

12111211
await this.appInstallationDB.recordNewInstallation('github', 'user', installationId, user.id);

0 commit comments

Comments
 (0)