@gitpod/gitpod-protocol 0.1.5-prs-prebuilds.16 → 0.1.5-release-2022.6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/data/gitpod-schema.json +18 -2
  2. package/lib/accounting-protocol.d.ts +7 -0
  3. package/lib/accounting-protocol.d.ts.map +1 -1
  4. package/lib/accounting-protocol.js +8 -1
  5. package/lib/accounting-protocol.js.map +1 -1
  6. package/lib/admin-protocol.d.ts +4 -0
  7. package/lib/admin-protocol.d.ts.map +1 -1
  8. package/lib/admin-protocol.js.map +1 -1
  9. package/lib/attribution.d.ts +20 -0
  10. package/lib/attribution.d.ts.map +1 -0
  11. package/lib/attribution.js +40 -0
  12. package/lib/attribution.js.map +1 -0
  13. package/lib/blocked-repositories-protocol.d.ts +13 -0
  14. package/lib/blocked-repositories-protocol.d.ts.map +1 -0
  15. package/lib/blocked-repositories-protocol.js +8 -0
  16. package/lib/blocked-repositories-protocol.js.map +1 -0
  17. package/lib/context-url.js +1 -1
  18. package/lib/context-url.js.map +1 -1
  19. package/lib/experiments/always-default.d.ts +8 -0
  20. package/lib/experiments/always-default.d.ts.map +1 -0
  21. package/lib/experiments/always-default.js +20 -0
  22. package/lib/experiments/always-default.js.map +1 -0
  23. package/lib/experiments/configcat-server.d.ts +8 -0
  24. package/lib/experiments/configcat-server.d.ts.map +1 -0
  25. package/lib/experiments/configcat-server.js +35 -0
  26. package/lib/experiments/configcat-server.js.map +1 -0
  27. package/lib/experiments/configcat.d.ts +22 -0
  28. package/lib/experiments/configcat.d.ts.map +1 -0
  29. package/lib/experiments/configcat.js +53 -0
  30. package/lib/experiments/configcat.js.map +1 -0
  31. package/lib/experiments/types.d.ts +23 -0
  32. package/lib/experiments/types.d.ts.map +1 -0
  33. package/lib/experiments/types.js +10 -0
  34. package/lib/experiments/types.js.map +1 -0
  35. package/lib/gitpod-service.d.ts +20 -2
  36. package/lib/gitpod-service.d.ts.map +1 -1
  37. package/lib/gitpod-service.js.map +1 -1
  38. package/lib/index.d.ts +1 -0
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +1 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/installation-admin-protocol.d.ts +3 -0
  43. package/lib/installation-admin-protocol.d.ts.map +1 -1
  44. package/lib/installation-admin-protocol.js +1 -0
  45. package/lib/installation-admin-protocol.js.map +1 -1
  46. package/lib/license-protocol.d.ts +7 -0
  47. package/lib/license-protocol.d.ts.map +1 -1
  48. package/lib/license-protocol.js.map +1 -1
  49. package/lib/messaging/error.d.ts +1 -0
  50. package/lib/messaging/error.d.ts.map +1 -1
  51. package/lib/messaging/error.js +2 -0
  52. package/lib/messaging/error.js.map +1 -1
  53. package/lib/protocol.d.ts +56 -1
  54. package/lib/protocol.d.ts.map +1 -1
  55. package/lib/protocol.js +97 -3
  56. package/lib/protocol.js.map +1 -1
  57. package/lib/protocol.spec.d.ts +7 -0
  58. package/lib/protocol.spec.d.ts.map +1 -0
  59. package/lib/protocol.spec.js +127 -0
  60. package/lib/protocol.spec.js.map +1 -0
  61. package/lib/team-subscription-protocol.d.ts +16 -0
  62. package/lib/team-subscription-protocol.d.ts.map +1 -1
  63. package/lib/team-subscription-protocol.js +12 -1
  64. package/lib/team-subscription-protocol.js.map +1 -1
  65. package/lib/teams-projects-protocol.d.ts +2 -0
  66. package/lib/teams-projects-protocol.d.ts.map +1 -1
  67. package/lib/teams-projects-protocol.js.map +1 -1
  68. package/lib/usage.d.ts +22 -0
  69. package/lib/usage.d.ts.map +1 -0
  70. package/lib/usage.js +115 -0
  71. package/lib/usage.js.map +1 -0
  72. package/lib/util/debug-app.d.ts +25 -0
  73. package/lib/util/debug-app.d.ts.map +1 -0
  74. package/lib/util/debug-app.js +100 -0
  75. package/lib/util/debug-app.js.map +1 -0
  76. package/lib/util/logging.d.ts.map +1 -1
  77. package/lib/util/logging.js +8 -1
  78. package/lib/util/logging.js.map +1 -1
  79. package/lib/util/timeutil.d.ts +1 -0
  80. package/lib/util/timeutil.d.ts.map +1 -1
  81. package/lib/util/timeutil.js +7 -1
  82. package/lib/util/timeutil.js.map +1 -1
  83. package/lib/webhook-event.d.ts +44 -0
  84. package/lib/webhook-event.d.ts.map +1 -0
  85. package/lib/webhook-event.js +8 -0
  86. package/lib/webhook-event.js.map +1 -0
  87. package/lib/workspace-cluster.d.ts +5 -7
  88. package/lib/workspace-cluster.d.ts.map +1 -1
  89. package/lib/workspace-cluster.js.map +1 -1
  90. package/lib/workspace-instance.d.ts +11 -0
  91. package/lib/workspace-instance.d.ts.map +1 -1
  92. package/lib/wsready.js +1 -1
  93. package/package.json +9 -6
  94. package/pkg-yarn.lock +7 -5
  95. package/provenance-bundle.jsonl +1 -1
  96. package/src/accounting-protocol.ts +10 -0
  97. package/src/admin-protocol.ts +7 -0
  98. package/src/attribution.ts +48 -0
  99. package/src/blocked-repositories-protocol.ts +13 -0
  100. package/src/context-url.ts +1 -1
  101. package/src/experiments/always-default.ts +24 -0
  102. package/src/experiments/configcat-server.ts +38 -0
  103. package/src/experiments/configcat.ts +58 -0
  104. package/src/experiments/types.ts +35 -0
  105. package/src/gitpod-service.ts +31 -1
  106. package/src/index.ts +1 -0
  107. package/src/installation-admin-protocol.ts +3 -0
  108. package/src/license-protocol.ts +7 -0
  109. package/src/messaging/error.ts +3 -0
  110. package/src/protocol.spec.ts +97 -0
  111. package/src/protocol.ts +151 -4
  112. package/src/team-subscription-protocol.ts +24 -0
  113. package/src/teams-projects-protocol.ts +2 -0
  114. package/src/usage.ts +146 -0
  115. package/src/util/debug-app.ts +81 -0
  116. package/src/util/logging.ts +11 -2
  117. package/src/util/timeutil.ts +6 -0
  118. package/src/webhook-event.ts +55 -0
  119. package/src/workspace-cluster.ts +2 -4
  120. package/src/workspace-instance.ts +17 -0
  121. package/src/wsready.ts +1 -1
@@ -24,6 +24,9 @@ import {
24
24
  GuessGitTokenScopesParams,
25
25
  GuessedGitTokenScopes,
26
26
  ProjectEnvVar,
27
+ PrebuiltWorkspace,
28
+ UserSSHPublicKeyValue,
29
+ SSHPublicKeyValue,
27
30
  } from "./protocol";
28
31
  import {
29
32
  Team,
@@ -47,10 +50,17 @@ import { LicenseService } from "./license-protocol";
47
50
  import { Emitter } from "./util/event";
48
51
  import { AccountStatement, CreditAlert } from "./accounting-protocol";
49
52
  import { GithubUpgradeURL, PlanCoupon } from "./payment-protocol";
50
- import { TeamSubscription, TeamSubscriptionSlot, TeamSubscriptionSlotResolved } from "./team-subscription-protocol";
53
+ import {
54
+ TeamSubscription,
55
+ TeamSubscription2,
56
+ TeamSubscriptionSlot,
57
+ TeamSubscriptionSlotResolved,
58
+ } from "./team-subscription-protocol";
51
59
  import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./analytics";
52
60
  import { IDEServer } from "./ide-protocol";
53
61
  import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
62
+ import { Currency } from "./plans";
63
+ import { BillableSession } from "./usage";
54
64
 
55
65
  export interface GitpodClient {
56
66
  onInstanceUpdate(instance: WorkspaceInstance): void;
@@ -142,6 +152,12 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
142
152
  setEnvVar(variable: UserEnvVarValue): Promise<void>;
143
153
  deleteEnvVar(variable: UserEnvVarValue): Promise<void>;
144
154
 
155
+ // User SSH Keys
156
+ hasSSHPublicKey(): Promise<boolean>;
157
+ getSSHPublicKeys(): Promise<UserSSHPublicKeyValue[]>;
158
+ addSSHPublicKey(value: SSHPublicKeyValue): Promise<UserSSHPublicKeyValue>;
159
+ deleteSSHPublicKey(id: string): Promise<void>;
160
+
145
161
  // Teams
146
162
  getTeams(): Promise<Team[]>;
147
163
  getTeamMembers(teamId: string): Promise<TeamMemberInfo[]>;
@@ -166,6 +182,8 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
166
182
  getUserProjects(): Promise<Project[]>;
167
183
  getProjectOverview(projectId: string): Promise<Project.Overview | undefined>;
168
184
  findPrebuilds(params: FindPrebuildsParams): Promise<PrebuildWithStatus[]>;
185
+ findPrebuildByWorkspaceID(workspaceId: string): Promise<PrebuiltWorkspace | undefined>;
186
+ getPrebuild(prebuildId: string): Promise<PrebuildWithStatus | undefined>;
169
187
  triggerPrebuild(projectId: string, branchName: string | null): Promise<StartPrebuildResult>;
170
188
  cancelPrebuild(projectId: string, prebuildId: string): Promise<void>;
171
189
  fetchProjectRepositoryConfiguration(projectId: string): Promise<string | undefined>;
@@ -235,7 +253,9 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
235
253
  */
236
254
  getChargebeeSiteId(): Promise<string>;
237
255
  createPortalSession(): Promise<{}>;
256
+ createTeamPortalSession(teamId: string): Promise<{}>;
238
257
  checkout(planId: string, planQuantity?: number): Promise<{}>;
258
+ teamCheckout(teamId: string, planId: string): Promise<{}>;
239
259
  getAvailableCoupons(): Promise<PlanCoupon[]>;
240
260
  getAppliedCoupons(): Promise<PlanCoupon[]>;
241
261
 
@@ -247,6 +267,7 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
247
267
  subscriptionCancel(subscriptionId: string): Promise<void>;
248
268
  subscriptionCancelDowngrade(subscriptionId: string): Promise<void>;
249
269
 
270
+ getTeamSubscription(teamId: string): Promise<TeamSubscription2 | undefined>;
250
271
  tsGet(): Promise<TeamSubscription[]>;
251
272
  tsGetSlots(): Promise<TeamSubscriptionSlotResolved[]>;
252
273
  tsGetUnassignedSlot(teamSubscriptionId: string): Promise<TeamSubscriptionSlot | undefined>;
@@ -262,6 +283,14 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
262
283
 
263
284
  getGithubUpgradeUrls(): Promise<GithubUpgradeURL[]>;
264
285
 
286
+ getStripePublishableKey(): Promise<string>;
287
+ getStripeSetupIntentClientSecret(): Promise<string>;
288
+ findStripeSubscriptionIdForTeam(teamId: string): Promise<string | undefined>;
289
+ subscribeTeamToStripe(teamId: string, setupIntentId: string, currency: Currency): Promise<void>;
290
+ getStripePortalUrlForTeam(teamId: string): Promise<string>;
291
+
292
+ getBilledUsage(attributionId: string): Promise<BillableSession[]>;
293
+
265
294
  /**
266
295
  * Analytics
267
296
  */
@@ -365,6 +394,7 @@ export interface SetWorkspaceTimeoutResult {
365
394
 
366
395
  export interface GetWorkspaceTimeoutResult {
367
396
  duration: WorkspaceTimeoutDuration;
397
+ durationRaw: string;
368
398
  canChange: boolean;
369
399
  }
370
400
 
package/src/index.ts CHANGED
@@ -20,3 +20,4 @@ export * from "./teams-projects-protocol";
20
20
  export * from "./snapshot-url";
21
21
  export * from "./oss-allowlist";
22
22
  export * from "./installation-admin-protocol";
23
+ export * from "./webhook-event";
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
8
8
 
9
9
  const InstallationAdminSettingsPrototype = {
10
10
  sendTelemetry: true,
11
+ sendCustomerID: true,
11
12
  };
12
13
 
13
14
  export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype;
@@ -28,6 +29,8 @@ export interface TelemetryData {
28
29
  totalUsers: number;
29
30
  totalWorkspaces: number;
30
31
  totalInstances: number;
32
+ licenseType: string;
33
+ customerID?: string;
31
34
  }
32
35
 
33
36
  export namespace InstallationAdmin {
@@ -15,9 +15,15 @@ export type LicenseIssue = "seats-exhausted";
15
15
  export interface LicenseInfo {
16
16
  key: string;
17
17
  seats: number;
18
+ userCount?: number;
18
19
  valid: boolean;
19
20
  validUntil: string;
20
21
  plan?: string;
22
+ features?: string[];
23
+ enabledFeatures?: string[];
24
+ type?: string;
25
+ errorMsg?: string;
26
+ fallbackAllowed: boolean;
21
27
  }
22
28
 
23
29
  export interface GetLicenseInfoResult {
@@ -33,5 +39,6 @@ export enum LicenseFeature {
33
39
  export interface LicenseService {
34
40
  validateLicense(): Promise<LicenseValidationResult>;
35
41
  getLicenseInfo(): Promise<GetLicenseInfoResult>;
42
+ adminGetLicense(): Promise<LicenseInfo>;
36
43
  licenseIncludesFeature(feature: LicenseFeature): Promise<boolean>;
37
44
  }
@@ -62,6 +62,9 @@ export namespace ErrorCodes {
62
62
  // 490 Too Many Running Workspace
63
63
  export const TOO_MANY_RUNNING_WORKSPACES = 490;
64
64
 
65
+ // 500 Internal Server Error
66
+ export const INTERNAL_SERVER_ERROR = 500;
67
+
65
68
  // 501 EE Feature
66
69
  export const EE_FEATURE = 501;
67
70
 
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Copyright (c) 2020 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 { suite, test } from "mocha-typescript";
8
+ import * as chai from "chai";
9
+ import { SSHPublicKeyValue } from ".";
10
+
11
+ const expect = chai.expect;
12
+
13
+ @suite
14
+ class TestSSHPublicKeyValue {
15
+ private key =
16
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCnrN9UdK1bNGPmZfenTWXLuYYDjlYvZE8S+WOfP08WpR1GETzX5ZvgYOEZGwEE8KUPHC9cge4Hvo/ydIS9aqbZ5MiVGJ8cAIq1Ic89SjlDWU6fl8TwIqOPCi2imAASlEDP4q8vMLK1N6UOW1EVbxyL3uybGd10ysC1t1FxFPveIGNsYE/MOQeuEWS16AplpXYXIfVRSlgAskeBft2w8Ud3B4gNe8ECLA/FXu96UpvZkdtOarA3JZ9Z27GveNJg9Mtmmw0+US0KXiO9x9NyH7G8+mqVDwDY+nNvaFA5gtQxkkl/uY2oz9k/B4Rjlj3jOiUXe5uQs3XUm5m8g9a9fh62DabLpA2fEvtfg+a/VqNe52dNa5YjupwvBd6Inb5uMW/TYjNl6bNHPlXFKw/nwLOVzukpkjxMZUKS6+4BGkpoasj6y2rTU/wkpbdD8J7yjI1p6J9aKkC6KksIWgN7xGmHkv2PCGDqMHTNbnQyowtNKMgA/667vAYJ0qW7HAHBFXJRs6uRi/DI3+c1QV2s4wPCpEHDIYApovQ0fbON4WDPoGMyHd7kPh9xB/bX7Dj0uMXImu1pdTd62fQ/1XXX64+vjAAXS/P9RSCD0RCRt/K3LPKl2m7GPI3y1niaE52XhxZw+ms9ays6NasNVMw/ZC+f02Ti+L5FBEVf8230RVVRQ== notfound@gitpod.io";
17
+
18
+ @test public testValidate() {
19
+ const key = this.key;
20
+ const [t, k, e] = key.split(" ");
21
+ expect(
22
+ SSHPublicKeyValue.getData({
23
+ key,
24
+ name: "NiceName",
25
+ }),
26
+ ).to.deep.equal({ type: t, key: k, email: e });
27
+ }
28
+
29
+ @test public testValidateWithDiffType() {
30
+ const key = this.key;
31
+ const [_, k, e] = key.split(" ");
32
+ expect(
33
+ SSHPublicKeyValue.getData({
34
+ key: key.replace("ssh-rsa", "sk-ecdsa-sha2-nistp256@openssh.com"),
35
+ name: "NiceName",
36
+ }),
37
+ ).to.deep.equal({ type: "sk-ecdsa-sha2-nistp256@openssh.com", key: k, email: e });
38
+ }
39
+
40
+ @test public testValidateWithoutEmail() {
41
+ const key = this.key;
42
+ const [t, k, _] = key.split(" ");
43
+ expect(
44
+ SSHPublicKeyValue.getData({
45
+ key: key.replace(" notfound@gitpod.io", ""),
46
+ name: "NiceName",
47
+ }),
48
+ ).to.deep.equal({ type: t, key: k, email: undefined });
49
+ }
50
+
51
+ @test public testValidateWithoutEmailButEndsWithSpaces() {
52
+ const key = this.key;
53
+ const [t, k, _] = key.split(" ");
54
+ expect(
55
+ SSHPublicKeyValue.getData({
56
+ key: key.replace("notfound@gitpod.io", " "),
57
+ name: "NiceName",
58
+ }),
59
+ ).to.deep.equal({ type: t, key: k, email: undefined });
60
+ }
61
+
62
+ @test public testValidateWithError() {
63
+ expect(() =>
64
+ SSHPublicKeyValue.getData({
65
+ key: "Hello World",
66
+ name: "NiceName",
67
+ }),
68
+ ).throw("Key is invalid");
69
+
70
+ expect(() =>
71
+ SSHPublicKeyValue.getData({
72
+ key: "",
73
+ name: "NiceName",
74
+ }),
75
+ ).throw("Key is invalid");
76
+ }
77
+
78
+ @test public testGetFingerprint() {
79
+ const key = this.key;
80
+ expect(
81
+ SSHPublicKeyValue.getFingerprint({
82
+ key,
83
+ name: "NiceName",
84
+ }),
85
+ ).to.equal("ykjP/b5aqoa3envmXzWpPMCGgEFMu3QvubfSTNrJCMA=");
86
+ }
87
+
88
+ @test public testGetFingerprintWithIncorrectPublicKey() {
89
+ expect(() =>
90
+ SSHPublicKeyValue.getFingerprint({
91
+ key: "Hello World",
92
+ name: "NiceName",
93
+ }),
94
+ ).to.throw("Key is invalid");
95
+ }
96
+ }
97
+ module.exports = new TestSSHPublicKeyValue(); // Only to circumvent no usage warning :-/
package/src/protocol.ts CHANGED
@@ -70,11 +70,14 @@ export namespace User {
70
70
  }
71
71
 
72
72
  /**
73
- * Tries to return the primaryEmail of the first identity this user signed up with.
73
+ * Returns the stored email or if it doesn't exist returns the primaryEmail of the first identity this user signed up with.
74
74
  * @param user
75
75
  * @returns A primaryEmail, or undefined if there is none.
76
76
  */
77
77
  export function getPrimaryEmail(user: User): string | undefined {
78
+ if (user.additionalData?.profile?.emailAddress) {
79
+ return user.additionalData?.profile?.emailAddress;
80
+ }
78
81
  const identities = user.identities.filter((i) => !!i.primaryEmail);
79
82
  if (identities.length <= 0) {
80
83
  return undefined;
@@ -136,6 +139,50 @@ export namespace User {
136
139
  }
137
140
  user.additionalData.ideSettings = newIDESettings;
138
141
  }
142
+
143
+ export function getProfile(user: User): Profile {
144
+ return {
145
+ name: User.getName(user!) || "",
146
+ email: User.getPrimaryEmail(user!) || "",
147
+ company: user?.additionalData?.profile?.companyName,
148
+ avatarURL: user?.avatarUrl,
149
+ };
150
+ }
151
+
152
+ export function setProfile(user: User, profile: Profile): User {
153
+ user.fullName = profile.name;
154
+ user.avatarUrl = profile.avatarURL;
155
+
156
+ if (!user.additionalData) {
157
+ user.additionalData = {};
158
+ }
159
+ if (!user.additionalData.profile) {
160
+ user.additionalData.profile = {};
161
+ }
162
+ user.additionalData.profile.emailAddress = profile.email;
163
+ user.additionalData.profile.companyName = profile.company;
164
+ user.additionalData.profile.lastUpdatedDetailsNudge = new Date().toISOString();
165
+
166
+ return user;
167
+ }
168
+
169
+ // The actual Profile of a User
170
+ export interface Profile {
171
+ name: string;
172
+ email: string;
173
+ company?: string;
174
+ avatarURL?: string;
175
+ }
176
+ export namespace Profile {
177
+ export function hasChanges(before: Profile, after: Profile) {
178
+ return (
179
+ before.name !== after.name ||
180
+ before.email !== after.email ||
181
+ before.company !== after.company ||
182
+ before.avatarURL !== after.avatarURL
183
+ );
184
+ }
185
+ }
139
186
  }
140
187
 
141
188
  export interface AdditionalUserData {
@@ -150,9 +197,24 @@ export interface AdditionalUserData {
150
197
  oauthClientsApproved?: { [key: string]: string };
151
198
  // to remember GH Orgs the user installed/updated the GH App for
152
199
  knownGitHubOrgs?: string[];
153
-
154
200
  // Git clone URL pointing to the user's dotfile repo
155
201
  dotfileRepo?: string;
202
+ // Identifies an explicit team or user ID to which all the user's workspace usage should be attributed to (e.g. for billing purposes)
203
+ usageAttributionId?: string;
204
+ // preferred workspace classes
205
+ workspaceClasses?: WorkspaceClasses;
206
+ // additional user profile data
207
+ profile?: ProfileDetails;
208
+ }
209
+
210
+ // The format in which we store User Profiles in
211
+ export interface ProfileDetails {
212
+ // when was the last time the user updated their profile information or has been nudged to do so.
213
+ lastUpdatedDetailsNudge?: string;
214
+ // the user's company name
215
+ companyName?: string;
216
+ // the user's email
217
+ emailAddress?: string;
156
218
  }
157
219
 
158
220
  export interface EmailNotificationSettings {
@@ -171,6 +233,11 @@ export type IDESettings = {
171
233
  useLatestVersion?: boolean;
172
234
  };
173
235
 
236
+ export interface WorkspaceClasses {
237
+ regular: string;
238
+ prebuild: string;
239
+ }
240
+
174
241
  export interface UserPlatform {
175
242
  uid: string;
176
243
  userAgent: string;
@@ -205,6 +272,7 @@ export const WorkspaceFeatureFlags = {
205
272
  full_workspace_backup: undefined,
206
273
  fixed_resources: undefined,
207
274
  persistent_volume_claim: undefined,
275
+ protected_secrets: undefined,
208
276
  };
209
277
  export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
210
278
 
@@ -242,12 +310,18 @@ export namespace UserEnvVar {
242
310
  if (name.trim() === "") {
243
311
  return "Name must not be empty.";
244
312
  }
313
+ if (name.length > 255) {
314
+ return "Name too long. Maximum name length is 255 characters.";
315
+ }
245
316
  if (!/^[a-zA-Z_]+[a-zA-Z0-9_]*$/.test(name)) {
246
317
  return "Name must match /^[a-zA-Z_]+[a-zA-Z0-9_]*$/.";
247
318
  }
248
319
  if (variable.value.trim() === "") {
249
320
  return "Value must not be empty.";
250
321
  }
322
+ if (variable.value.length > 32767) {
323
+ return "Value too long. Maximum value length is 32767 characters.";
324
+ }
251
325
  if (pattern.trim() === "") {
252
326
  return "Scope must not be empty.";
253
327
  }
@@ -350,6 +424,68 @@ export namespace UserEnvVar {
350
424
  }
351
425
  }
352
426
 
427
+ export interface SSHPublicKeyValue {
428
+ name: string;
429
+ key: string;
430
+ }
431
+ export interface UserSSHPublicKey extends SSHPublicKeyValue {
432
+ id: string;
433
+ key: string;
434
+ userId: string;
435
+ fingerprint: string;
436
+ creationTime: string;
437
+ lastUsedTime?: string;
438
+ }
439
+
440
+ export type UserSSHPublicKeyValue = Omit<UserSSHPublicKey, "key" | "userId">;
441
+
442
+ export namespace SSHPublicKeyValue {
443
+ export function validate(value: SSHPublicKeyValue): string | undefined {
444
+ if (value.name.length === 0) {
445
+ return "Title must not be empty.";
446
+ }
447
+ if (value.name.length > 255) {
448
+ return "Title too long. Maximum value length is 255 characters.";
449
+ }
450
+ if (value.key.length === 0) {
451
+ return "Key must not be empty.";
452
+ }
453
+ try {
454
+ getData(value);
455
+ } catch (e) {
456
+ return "Key is invalid. You must supply a key in OpenSSH public key format.";
457
+ }
458
+ return;
459
+ }
460
+
461
+ export function getData(value: SSHPublicKeyValue) {
462
+ // Begins with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'.
463
+ const regex =
464
+ /^(?<type>ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519|sk-ecdsa-sha2-nistp256@openssh\.com|sk-ssh-ed25519@openssh\.com) (?<key>.*?)( (?<email>.*?))?$/;
465
+ const resultGroup = regex.exec(value.key.trim());
466
+ if (!resultGroup) {
467
+ throw new Error("Key is invalid.");
468
+ }
469
+ return {
470
+ type: resultGroup.groups?.["type"] as string,
471
+ key: resultGroup.groups?.["key"] as string,
472
+ email: resultGroup.groups?.["email"] || undefined,
473
+ };
474
+ }
475
+
476
+ export function getFingerprint(value: SSHPublicKeyValue) {
477
+ const data = getData(value);
478
+ let buf = Buffer.from(data.key, "base64");
479
+ // gitlab style
480
+ // const hash = createHash("md5").update(buf).digest("hex");
481
+ // github style
482
+ const hash = createHash("sha256").update(buf).digest("base64");
483
+ return hash;
484
+ }
485
+
486
+ export const MAXIMUM_KEY_LENGTH = 5;
487
+ }
488
+
353
489
  export interface GitpodToken {
354
490
  /** Hash value (SHA256) of the token (primary key). */
355
491
  tokenHash: string;
@@ -482,6 +618,13 @@ export interface Snapshot {
482
618
  message?: string;
483
619
  }
484
620
 
621
+ export interface VolumeSnapshot {
622
+ id: string;
623
+ workspaceId: string;
624
+ creationTime: string;
625
+ volumeHandle: string;
626
+ }
627
+
485
628
  export type SnapshotState = "pending" | "available" | "error";
486
629
 
487
630
  export interface LayoutData {
@@ -551,7 +694,7 @@ export interface Workspace {
551
694
 
552
695
  export type WorkspaceSoftDeletion = "user" | "gc";
553
696
 
554
- export type WorkspaceType = "regular" | "prebuild" | "probe";
697
+ export type WorkspaceType = "regular" | "prebuild" | "probe"; // TODO(gpl) Removed prove here
555
698
 
556
699
  export namespace Workspace {
557
700
  export function getFullRepositoryName(ws: Workspace): string | undefined {
@@ -627,6 +770,7 @@ export interface JetBrainsConfig {
627
770
  }
628
771
  export interface JetBrainsProductConfig {
629
772
  prebuilds?: JetBrainsPrebuilds;
773
+ vmoptions?: string;
630
774
  }
631
775
  export interface JetBrainsPrebuilds {
632
776
  version?: "stable" | "latest" | "both";
@@ -742,7 +886,9 @@ export interface PrebuiltWorkspace {
742
886
 
743
887
  export namespace PrebuiltWorkspace {
744
888
  export function isDone(pws: PrebuiltWorkspace) {
745
- return pws.state === "available" || pws.state === "timeout" || pws.state === "aborted";
889
+ return (
890
+ pws.state === "available" || pws.state === "timeout" || pws.state === "aborted" || pws.state === "failed"
891
+ );
746
892
  }
747
893
 
748
894
  export function isAvailable(pws: PrebuiltWorkspace) {
@@ -866,6 +1012,7 @@ export namespace ExternalImageConfigFile {
866
1012
 
867
1013
  export interface WorkspaceContext {
868
1014
  title: string;
1015
+ ref?: string;
869
1016
  /** This contains the URL portion of the contextURL (which might contain other modifiers as well). It's optional because it's not set for older workspaces. */
870
1017
  normalizedContextURL?: string;
871
1018
  forceCreateNewWorkspace?: boolean;
@@ -33,6 +33,30 @@ export namespace TeamSubscription {
33
33
  };
34
34
  }
35
35
 
36
+ export interface TeamSubscription2 {
37
+ id: string;
38
+ teamId: string;
39
+ planId: string;
40
+ startDate: string;
41
+ endDate?: string;
42
+ quantity: number;
43
+ /** The Chargebee subscription id */
44
+ paymentReference: string;
45
+ cancellationDate?: string;
46
+ excludeFromMoreResources: boolean;
47
+ }
48
+
49
+ export namespace TeamSubscription2 {
50
+ export const create = (ts2: Omit<TeamSubscription2, "id">): TeamSubscription2 => {
51
+ const withId = ts2 as TeamSubscription2;
52
+ withId.id = uuidv4();
53
+ return withId;
54
+ };
55
+ export const isActive = (ts2: TeamSubscription2, date: string): boolean => {
56
+ return ts2.startDate <= date && (ts2.endDate === undefined || date < ts2.endDate);
57
+ };
58
+ }
59
+
36
60
  /**
37
61
  * A slot represents one unit of a TeamSubscription that gets assigned to one user at a time
38
62
  */
@@ -14,6 +14,8 @@ export interface ProjectConfig {
14
14
 
15
15
  export interface ProjectSettings {
16
16
  useIncrementalPrebuilds?: boolean;
17
+ usePersistentVolumeClaim?: boolean;
18
+ keepOutdatedPrebuildsRunning?: boolean;
17
19
  }
18
20
 
19
21
  export interface Project {
package/src/usage.ts ADDED
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Copyright (c) 2022 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 { WorkspaceType } from "./protocol";
8
+
9
+ export interface BillableSession {
10
+ // The id of the one paying the bill
11
+ attributionId: string;
12
+
13
+ // Relevant for workspace type. When prebuild, shows "prebuild"
14
+ userId?: string;
15
+ teamId?: string;
16
+
17
+ instanceId: string;
18
+
19
+ workspaceId: string;
20
+
21
+ workspaceType: BillableWorkspaceType;
22
+
23
+ // "standard" or "XL"
24
+ workspaceClass: string;
25
+
26
+ // When the workspace started
27
+ startTime: string;
28
+
29
+ // When the workspace ended
30
+ endTime: string;
31
+
32
+ // The credits used for this session
33
+ credits: number;
34
+
35
+ // TODO - maybe
36
+ projectId?: string;
37
+ }
38
+
39
+ export type BillableWorkspaceType = WorkspaceType;
40
+
41
+ export const billableSessionDummyData: BillableSession[] = [
42
+ {
43
+ attributionId: "some-attribution-id",
44
+ userId: "prebuild",
45
+ teamId: "prebuild",
46
+ instanceId: "some-instance-id",
47
+ workspaceId: "some-workspace-id",
48
+ workspaceType: "prebuild",
49
+ workspaceClass: "XL",
50
+ startTime: new Date(Date.now() + -3 * 24 * 3600 * 1000).toISOString(), // 3 days ago
51
+ endTime: new Date().toISOString(),
52
+ credits: 320,
53
+ projectId: "project-123",
54
+ },
55
+ {
56
+ attributionId: "some-attribution-id2",
57
+ userId: "some-user",
58
+ teamId: "some-team",
59
+ instanceId: "some-instance-id2",
60
+ workspaceId: "some-workspace-id2",
61
+ workspaceType: "regular",
62
+ workspaceClass: "standard",
63
+ startTime: new Date(Date.now() + -5 * 24 * 3600 * 1000).toISOString(),
64
+ endTime: new Date().toISOString(),
65
+ credits: 130,
66
+ projectId: "project-123",
67
+ },
68
+ {
69
+ attributionId: "some-attribution-id3",
70
+ userId: "some-other-user",
71
+ teamId: "some-other-team",
72
+ instanceId: "some-instance-id3",
73
+ workspaceId: "some-workspace-id3",
74
+ workspaceType: "regular",
75
+ workspaceClass: "XL",
76
+ startTime: new Date(Date.now() + -5 * 24 * 3600 * 1000).toISOString(),
77
+ endTime: new Date(Date.now() + -4 * 24 * 3600 * 1000).toISOString(),
78
+ credits: 150,
79
+ projectId: "project-134",
80
+ },
81
+ {
82
+ attributionId: "some-attribution-id4",
83
+ userId: "some-other-user2",
84
+ teamId: "some-other-team2",
85
+ instanceId: "some-instance-id4",
86
+ workspaceId: "some-workspace-id4",
87
+ workspaceType: "regular",
88
+ workspaceClass: "standard",
89
+ startTime: new Date(Date.now() + -10 * 24 * 3600 * 1000).toISOString(),
90
+ endTime: new Date(Date.now() + -9 * 24 * 3600 * 1000).toISOString(),
91
+ credits: 330,
92
+ projectId: "project-137",
93
+ },
94
+ {
95
+ attributionId: "some-attribution-id5",
96
+ userId: "some-other-user3",
97
+ teamId: "some-other-team3",
98
+ instanceId: "some-instance-id5",
99
+ workspaceId: "some-workspace-id5",
100
+ workspaceType: "regular",
101
+ workspaceClass: "XL",
102
+ startTime: new Date(Date.now() + -2 * 24 * 3600 * 1000).toISOString(),
103
+ endTime: new Date().toISOString(),
104
+ credits: 222,
105
+ projectId: "project-138",
106
+ },
107
+ {
108
+ attributionId: "some-attribution-id6",
109
+ userId: "some-other-user4",
110
+ teamId: "some-other-team4",
111
+ instanceId: "some-instance-id6",
112
+ workspaceId: "some-workspace-id3",
113
+ workspaceType: "regular",
114
+ workspaceClass: "XL",
115
+ startTime: new Date(Date.now() + -7 * 24 * 3600 * 1000).toISOString(),
116
+ endTime: new Date(Date.now() + -6 * 24 * 3600 * 1000).toISOString(),
117
+ credits: 300,
118
+ projectId: "project-134",
119
+ },
120
+ {
121
+ attributionId: "some-attribution-id8",
122
+ userId: "some-other-user5",
123
+ teamId: "some-other-team5",
124
+ instanceId: "some-instance-id8",
125
+ workspaceId: "some-workspace-id3",
126
+ workspaceType: "regular",
127
+ workspaceClass: "standard",
128
+ startTime: new Date(Date.now() + -1 * 24 * 3600 * 1000).toISOString(),
129
+ endTime: new Date().toISOString(),
130
+ credits: 100,
131
+ projectId: "project-567",
132
+ },
133
+ {
134
+ attributionId: "some-attribution-id7",
135
+ userId: "prebuild",
136
+ teamId: "some-other-team7",
137
+ instanceId: "some-instance-id7",
138
+ workspaceId: "some-workspace-id7",
139
+ workspaceType: "prebuild",
140
+ workspaceClass: "XL",
141
+ startTime: new Date(Date.now() + -1 * 24 * 3600 * 1000).toISOString(),
142
+ endTime: new Date().toISOString(),
143
+ credits: 200,
144
+ projectId: "project-345",
145
+ },
146
+ ];