Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/user-settings/EnvironmentVariables.tsx
2500 views
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 { UserEnvVar, UserEnvVarValue } from "@gitpod/gitpod-protocol";
8
import { useCallback, useEffect, useRef, useState } from "react";
9
import ConfirmationModal from "../components/ConfirmationModal";
10
import { Item, ItemField, ItemsList } from "../components/ItemsList";
11
import Modal, { ModalBody, ModalFooter, ModalHeader } from "../components/Modal";
12
import { PageWithSettingsSubMenu } from "./PageWithSettingsSubMenu";
13
import { EnvironmentVariableEntry } from "./EnvironmentVariableEntry";
14
import { Heading2, Subheading } from "../components/typography/headings";
15
import { envVarClient } from "../service/public-api";
16
import { UserEnvironmentVariable } from "@gitpod/public-api/lib/gitpod/v1/envvar_pb";
17
import { Button } from "@podkit/buttons/Button";
18
import { TextInputField } from "../components/forms/TextInputField";
19
20
interface EnvVarModalProps {
21
envVar: UserEnvVarValue;
22
onClose: () => void;
23
save: (v: UserEnvVarValue) => void;
24
validate: (v: UserEnvVarValue) => string | undefined;
25
}
26
27
function AddEnvVarModal(p: EnvVarModalProps) {
28
const [ev, setEv] = useState({ ...p.envVar });
29
const [error, setError] = useState("");
30
const ref = useRef(ev);
31
32
const update = (pev: Partial<UserEnvVarValue>) => {
33
const newEnv = { ...ref.current, ...pev };
34
setEv(newEnv);
35
ref.current = newEnv;
36
};
37
38
useEffect(() => {
39
setEv({ ...p.envVar });
40
setError("");
41
}, [p.envVar]);
42
43
const isNew = !p.envVar.id;
44
let save = useCallback(async () => {
45
const v = ref.current;
46
const errorMsg = p.validate(v);
47
if (!!errorMsg) {
48
setError(errorMsg);
49
} else {
50
await p.save(v);
51
p.onClose();
52
}
53
}, [p]);
54
55
return (
56
<Modal visible={true} onClose={p.onClose} onSubmit={save}>
57
<ModalHeader>{isNew ? "New" : "Edit"} Variable</ModalHeader>
58
<ModalBody>
59
{error ? (
60
<div className="bg-kumquat-light rounded-md p-3 text-gitpod-red text-sm mb-2">{error}</div>
61
) : null}
62
<TextInputField
63
label="Name"
64
value={ev.name}
65
type="text"
66
autoFocus
67
onChange={(val) => update({ name: val })}
68
/>
69
70
<TextInputField label="Value" value={ev.value} type="text" onChange={(val) => update({ value: val })} />
71
72
<TextInputField
73
label="Scope"
74
hint={
75
<>
76
You can pass a variable for a specific project or use wildcard character (<code>*/*</code>)
77
to make it available in more projects.
78
</>
79
}
80
value={ev.repositoryPattern}
81
type="text"
82
placeholder="e.g. owner/repository"
83
onChange={(val) => update({ repositoryPattern: val })}
84
/>
85
</ModalBody>
86
<ModalFooter>
87
<Button variant="secondary" onClick={p.onClose}>
88
Cancel
89
</Button>
90
<Button type="submit">{isNew ? "Add" : "Update"} Variable</Button>
91
</ModalFooter>
92
</Modal>
93
);
94
}
95
96
function DeleteEnvVarModal(p: { variable: UserEnvVarValue; deleteVariable: () => void; onClose: () => void }) {
97
return (
98
<ConfirmationModal
99
title="Delete Variable"
100
areYouSureText="Are you sure you want to delete this variable?"
101
buttonText="Delete Variable"
102
onClose={p.onClose}
103
onConfirm={async () => {
104
await p.deleteVariable();
105
p.onClose();
106
}}
107
>
108
<div className="grid grid-cols-2 gap-4 px-3 text-sm text-gray-400">
109
<span className="truncate">Name</span>
110
<span className="truncate">Scope</span>
111
</div>
112
<div className="grid grid-cols-2 gap-4 p-3 mt-3 text-gray-400 bg-pk-surface-secondary rounded-xl">
113
<span className="truncate text-gray-900 dark:text-gray-50">{p.variable.name}</span>
114
<span className="truncate text-sm">{p.variable.repositoryPattern}</span>
115
</div>
116
</ConfirmationModal>
117
);
118
}
119
120
function sortEnvVars(a: UserEnvironmentVariable, b: UserEnvironmentVariable) {
121
if (a.name === b.name) {
122
return a.repositoryPattern > b.repositoryPattern ? 1 : -1;
123
}
124
return a.name > b.name ? 1 : -1;
125
}
126
127
export default function EnvVars() {
128
const [envVars, setEnvVars] = useState([] as UserEnvVarValue[]);
129
const [currentEnvVar, setCurrentEnvVar] = useState({
130
id: undefined,
131
name: "",
132
value: "",
133
repositoryPattern: "",
134
} as UserEnvVarValue);
135
const [isAddEnvVarModalVisible, setAddEnvVarModalVisible] = useState(false);
136
const [isDeleteEnvVarModalVisible, setDeleteEnvVarModalVisible] = useState(false);
137
const update = async () => {
138
await envVarClient.listUserEnvironmentVariables({}).then((r) =>
139
setEnvVars(
140
r.environmentVariables.sort(sortEnvVars).map((e) => ({
141
id: e.id,
142
name: e.name,
143
value: e.value,
144
repositoryPattern: e.repositoryPattern,
145
})),
146
),
147
);
148
};
149
150
useEffect(() => {
151
update();
152
}, []);
153
154
const add = () => {
155
setCurrentEnvVar({ id: undefined, name: "", value: "", repositoryPattern: "" });
156
setAddEnvVarModalVisible(true);
157
setDeleteEnvVarModalVisible(false);
158
};
159
160
const edit = (variable: UserEnvVarValue) => {
161
setCurrentEnvVar(variable);
162
setAddEnvVarModalVisible(true);
163
setDeleteEnvVarModalVisible(false);
164
};
165
166
const confirmDeleteVariable = (variable: UserEnvVarValue) => {
167
setCurrentEnvVar(variable);
168
setAddEnvVarModalVisible(false);
169
setDeleteEnvVarModalVisible(true);
170
};
171
172
const save = async (variable: UserEnvVarValue) => {
173
if (variable.id) {
174
await envVarClient.updateUserEnvironmentVariable({
175
environmentVariableId: variable.id,
176
name: variable.name,
177
value: variable.value,
178
repositoryPattern: variable.repositoryPattern,
179
});
180
} else {
181
await envVarClient.createUserEnvironmentVariable({
182
name: variable.name,
183
value: variable.value,
184
repositoryPattern: variable.repositoryPattern,
185
});
186
}
187
188
await update();
189
};
190
191
const deleteVariable = async (variable: UserEnvVarValue) => {
192
await envVarClient.deleteUserEnvironmentVariable({
193
environmentVariableId: variable.id,
194
});
195
await update();
196
};
197
198
const validate = (variable: UserEnvVarValue): string | undefined => {
199
const name = variable.name;
200
const pattern = variable.repositoryPattern;
201
const validationError = UserEnvVar.validate(variable);
202
if (validationError) {
203
return validationError;
204
}
205
if (!variable.id && envVars.some((v) => v.name === name && v.repositoryPattern === pattern)) {
206
return "A variable with this name and scope already exists";
207
}
208
return undefined;
209
};
210
211
return (
212
<PageWithSettingsSubMenu>
213
{isAddEnvVarModalVisible && (
214
<AddEnvVarModal
215
save={save}
216
envVar={currentEnvVar}
217
validate={validate}
218
onClose={() => setAddEnvVarModalVisible(false)}
219
/>
220
)}
221
{isDeleteEnvVarModalVisible && (
222
<DeleteEnvVarModal
223
variable={currentEnvVar}
224
deleteVariable={async () => await deleteVariable(currentEnvVar)}
225
onClose={() => setDeleteEnvVarModalVisible(false)}
226
/>
227
)}
228
<div className="flex items-start sm:justify-between mb-2">
229
<div>
230
<Heading2>Environment Variables</Heading2>
231
<Subheading>
232
Variables are used to store information like passwords.{" "}
233
<a
234
className="gp-link"
235
href="https://www.gitpod.io/docs/configure/projects/environment-variables#environment-variables"
236
target="_blank"
237
rel="noreferrer"
238
>
239
Learn more
240
</a>
241
</Subheading>
242
</div>
243
{envVars.length !== 0 ? (
244
<div className="flex mt-0">
245
<Button onClick={add} className="ml-2">
246
New Variable
247
</Button>
248
</div>
249
) : null}
250
</div>
251
{envVars.length === 0 ? (
252
<div className="bg-pk-surface-secondary rounded-xl w-full h-96">
253
<div className="pt-28 flex flex-col items-center w-96 m-auto">
254
<Heading2 className="text-pk-content-invert-secondary text-center pb-3">
255
No Environment Variables
256
</Heading2>
257
<Subheading className="text-center pb-6">
258
In addition to user-specific environment variables you can also pass variables through a
259
workspace creation URL.
260
</Subheading>
261
<Button onClick={add}>New Variable</Button>
262
</div>
263
</div>
264
) : (
265
<ItemsList>
266
<Item header={true}>
267
<ItemField className="w-5/12 my-auto">Name</ItemField>
268
<ItemField className="w-5/12 my-auto">Scope</ItemField>
269
</Item>
270
{envVars.map((variable) => (
271
<EnvironmentVariableEntry
272
key={variable.id}
273
variable={variable}
274
edit={edit}
275
confirmDeleteVariable={confirmDeleteVariable}
276
/>
277
))}
278
</ItemsList>
279
)}
280
</PageWithSettingsSubMenu>
281
);
282
}
283
284