@gitpod/gitpod-protocol 0.1.5-me-ingress.14 → 0.1.5-me-image.3

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 (100) hide show
  1. package/data/gitpod-schema.json +12 -0
  2. package/lib/admin-protocol.d.ts +10 -0
  3. package/lib/admin-protocol.d.ts.map +1 -1
  4. package/lib/admin-protocol.js.map +1 -1
  5. package/lib/analytics.d.ts +4 -5
  6. package/lib/analytics.d.ts.map +1 -1
  7. package/lib/context-url.d.ts +18 -5
  8. package/lib/context-url.d.ts.map +1 -1
  9. package/lib/context-url.js +58 -5
  10. package/lib/context-url.js.map +1 -1
  11. package/lib/context-url.spec.d.ts +2 -0
  12. package/lib/context-url.spec.d.ts.map +1 -1
  13. package/lib/context-url.spec.js +25 -4
  14. package/lib/context-url.spec.js.map +1 -1
  15. package/lib/gitpod-service.d.ts +23 -2
  16. package/lib/gitpod-service.d.ts.map +1 -1
  17. package/lib/gitpod-service.js +4 -2
  18. package/lib/gitpod-service.js.map +1 -1
  19. package/lib/ide-protocol.d.ts +24 -0
  20. package/lib/ide-protocol.d.ts.map +1 -1
  21. package/lib/index.d.ts +1 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +1 -0
  24. package/lib/index.js.map +1 -1
  25. package/lib/installation-admin-protocol.d.ts +27 -0
  26. package/lib/installation-admin-protocol.d.ts.map +1 -0
  27. package/lib/installation-admin-protocol.js +30 -0
  28. package/lib/installation-admin-protocol.js.map +1 -0
  29. package/lib/messaging/error.d.ts +1 -0
  30. package/lib/messaging/error.d.ts.map +1 -1
  31. package/lib/messaging/error.js +2 -0
  32. package/lib/messaging/error.js.map +1 -1
  33. package/lib/messaging/node/connection.d.ts +1 -7
  34. package/lib/messaging/node/connection.d.ts.map +1 -1
  35. package/lib/messaging/node/connection.js +20 -18
  36. package/lib/messaging/node/connection.js.map +1 -1
  37. package/lib/permission.d.ts +7 -1
  38. package/lib/permission.d.ts.map +1 -1
  39. package/lib/permission.js +24 -4
  40. package/lib/permission.js.map +1 -1
  41. package/lib/protocol.d.ts +22 -39
  42. package/lib/protocol.d.ts.map +1 -1
  43. package/lib/protocol.js +16 -4
  44. package/lib/protocol.js.map +1 -1
  45. package/lib/teams-projects-protocol.d.ts +3 -0
  46. package/lib/teams-projects-protocol.d.ts.map +1 -1
  47. package/lib/teams-projects-protocol.js +7 -0
  48. package/lib/teams-projects-protocol.js.map +1 -1
  49. package/lib/util/generate-workspace-id.d.ts.map +1 -1
  50. package/lib/util/generate-workspace-id.js +1 -0
  51. package/lib/util/generate-workspace-id.js.map +1 -1
  52. package/lib/util/generate-workspace-id.spec.js +1 -0
  53. package/lib/util/generate-workspace-id.spec.js.map +1 -1
  54. package/lib/util/gitpod-host-url.d.ts +0 -1
  55. package/lib/util/gitpod-host-url.d.ts.map +1 -1
  56. package/lib/util/gitpod-host-url.js +0 -3
  57. package/lib/util/gitpod-host-url.js.map +1 -1
  58. package/lib/util/tracing.d.ts +2 -1
  59. package/lib/util/tracing.d.ts.map +1 -1
  60. package/lib/util/tracing.js +29 -6
  61. package/lib/util/tracing.js.map +1 -1
  62. package/lib/workspace-cluster.d.ts +9 -7
  63. package/lib/workspace-cluster.d.ts.map +1 -1
  64. package/lib/workspace-cluster.js +18 -1
  65. package/lib/workspace-cluster.js.map +1 -1
  66. package/lib/workspace-instance.d.ts +19 -0
  67. package/lib/workspace-instance.d.ts.map +1 -1
  68. package/package.json +2 -2
  69. package/pkg-yarn.lock +2 -2
  70. package/provenance-bundle.jsonl +1 -1
  71. package/src/admin-protocol.ts +17 -4
  72. package/src/analytics.ts +4 -6
  73. package/src/context-url.spec.ts +18 -4
  74. package/src/context-url.ts +62 -6
  75. package/src/gitpod-service.ts +33 -6
  76. package/src/ide-frontend-service.ts +1 -1
  77. package/src/ide-protocol.ts +27 -0
  78. package/src/index.ts +1 -0
  79. package/src/installation-admin-protocol.ts +42 -0
  80. package/src/messaging/error.ts +3 -0
  81. package/src/messaging/node/connection.ts +20 -23
  82. package/src/permission.ts +22 -3
  83. package/src/protocol.ts +37 -43
  84. package/src/teams-projects-protocol.ts +7 -1
  85. package/src/util/generate-workspace-id.spec.ts +1 -0
  86. package/src/util/generate-workspace-id.ts +1 -0
  87. package/src/util/gitpod-host-url.ts +1 -5
  88. package/src/util/tracing.ts +29 -7
  89. package/src/workspace-cluster.ts +17 -9
  90. package/src/workspace-instance.ts +20 -0
  91. package/lib/messaging/connection-error-handler.d.ts +0 -27
  92. package/lib/messaging/connection-error-handler.d.ts.map +0 -1
  93. package/lib/messaging/connection-error-handler.js +0 -34
  94. package/lib/messaging/connection-error-handler.js.map +0 -1
  95. package/lib/util/safe-promise.d.ts +0 -11
  96. package/lib/util/safe-promise.d.ts.map +0 -1
  97. package/lib/util/safe-promise.js +0 -31
  98. package/lib/util/safe-promise.js.map +0 -1
  99. package/src/messaging/connection-error-handler.ts +0 -62
  100. package/src/util/safe-promise.ts +0 -26
@@ -4,10 +4,62 @@
4
4
  * See License-AGPL.txt in the project root for license information.
5
5
  */
6
6
 
7
+ import { Workspace } from ".";
8
+
9
+ /**
10
+ * The whole point of these methods is to overcome inconsistencies in our data model.
11
+ * Ideally we remove it at some point once we fixed our model, as it:
12
+ * - duplicates logic
13
+ * - but additional burden on clients (using this, copying this to other languages!)
14
+ *
15
+ * TODO(gpl) See if we can get this into `server` code to remove the burden from clients
16
+ */
7
17
  export namespace ContextURL {
8
18
  export const INCREMENTAL_PREBUILD_PREFIX = "incremental-prebuild";
9
19
  export const PREBUILD_PREFIX = "prebuild";
10
20
  export const IMAGEBUILD_PREFIX = "imagebuild";
21
+ export const SNAPSHOT_PREFIX = "snapshot";
22
+ export const REFERRER_PREFIX = 'referrer:';
23
+
24
+ /**
25
+ * This function will (try to) return the HTTP(S) URL of the context the user originally created this workspace on.
26
+ * Especially it will not contain any modifiers or be of different scheme than HTTP(S).
27
+ *
28
+ * Use this function if you need to provided a _working_ URL to the original context.
29
+ * @param ws
30
+ * @returns
31
+ */
32
+ export function getNormalizedURL(ws: Pick<Workspace, "context" | "contextURL"> | undefined): URL | undefined {
33
+ const normalized = normalize(ws);
34
+ if (!normalized) {
35
+ return undefined;
36
+ }
37
+
38
+ try {
39
+ return new URL(normalized);
40
+ } catch (err) {
41
+ console.error(`unable to parse URL from normalized contextURL: '${normalized}'`, err);
42
+ }
43
+ return undefined;
44
+ }
45
+
46
+ function normalize(ws: Pick<Workspace, "context" | "contextURL"> | undefined): string | undefined {
47
+ if (!ws) {
48
+ return undefined;
49
+ }
50
+ if (ws.context.normalizedContextURL) {
51
+ return ws.context.normalizedContextURL;
52
+ }
53
+
54
+ // fallback: we do not yet set normalizedContextURL on all workspaces, yet, let alone older existing workspaces
55
+ let fallback: string | undefined = undefined;
56
+ try {
57
+ fallback = removePrefixes(ws.contextURL);
58
+ } catch (err) {
59
+ console.error(`unable to remove prefixes from contextURL: '${ws.contextURL}'`, err);
60
+ }
61
+ return fallback;
62
+ }
11
63
 
12
64
  /**
13
65
  * The field "contextUrl" might contain prefixes like:
@@ -15,29 +67,33 @@ export namespace ContextURL {
15
67
  * - prebuild/...
16
68
  * This is the analogon to the (Prefix)ContextParser structure in "server".
17
69
  */
18
- export function parseToURL(contextUrl: string | undefined): URL | undefined {
70
+ function removePrefixes(contextUrl: string | undefined): string | undefined {
19
71
  if (contextUrl === undefined) {
20
72
  return undefined;
21
73
  }
22
74
 
23
75
  const segments = contextUrl.split("/");
24
76
  if (segments.length === 1) {
25
- return new URL(segments[0]); // this might be something, we just try
77
+ return segments[0]; // this might be something, we just try
26
78
  }
27
79
 
28
- const segmentsToURL = (offset: number): URL => {
80
+ const segmentsToURL = (offset: number): string => {
29
81
  let rest = segments.slice(offset).join("/");
82
+ if (/^git@[^:\/]+:/.test(rest)) {
83
+ rest = rest.replace(/^git@([^:\/]+):/, 'https://$1/');
84
+ }
30
85
  if (!rest.startsWith("http")) {
31
86
  rest = 'https://' + rest;
32
87
  }
33
- return new URL(rest);
88
+ return rest;
34
89
  };
35
90
 
36
-
37
91
  const firstSegment = segments[0];
38
92
  if (firstSegment === PREBUILD_PREFIX ||
39
93
  firstSegment === INCREMENTAL_PREBUILD_PREFIX ||
40
- firstSegment === IMAGEBUILD_PREFIX) {
94
+ firstSegment === IMAGEBUILD_PREFIX ||
95
+ firstSegment === SNAPSHOT_PREFIX ||
96
+ firstSegment.startsWith(REFERRER_PREFIX)) {
41
97
  return segmentsToURL(1);
42
98
  }
43
99
 
@@ -6,10 +6,10 @@
6
6
 
7
7
  import {
8
8
  User, WorkspaceInfo, WorkspaceCreationResult, WorkspaceInstanceUser,
9
- WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo, Branding, CreateWorkspaceMode,
9
+ WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo, CreateWorkspaceMode,
10
10
  Token, UserEnvVarValue, ResolvePluginsParams, PreparePluginUploadParams, Terms,
11
11
  ResolvedPlugins, Configuration, InstallPluginsParams, UninstallPluginParams, UserInfo, GitpodTokenType,
12
- GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes
12
+ GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes, ProjectEnvVar
13
13
  } from './protocol';
14
14
  import {
15
15
  Team, TeamMemberInfo,
@@ -30,6 +30,7 @@ import { GithubUpgradeURL, PlanCoupon } from './payment-protocol';
30
30
  import { TeamSubscription, TeamSubscriptionSlot, TeamSubscriptionSlotResolved } from './team-subscription-protocol';
31
31
  import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from './analytics';
32
32
  import { IDEServer } from './ide-protocol';
33
+ import { InstallationAdminSettings, TelemetryData } from './installation-admin-protocol';
33
34
 
34
35
  export interface GitpodClient {
35
36
  onInstanceUpdate(instance: WorkspaceInstance): void;
@@ -55,7 +56,6 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
55
56
  getOwnAuthProviders(): Promise<AuthProviderEntry[]>;
56
57
  updateOwnAuthProvider(params: GitpodServer.UpdateOwnAuthProviderParams): Promise<AuthProviderEntry>;
57
58
  deleteOwnAuthProvider(params: GitpodServer.DeleteOwnAuthProviderParams): Promise<void>;
58
- getBranding(): Promise<Branding>;
59
59
  getConfiguration(): Promise<Configuration>;
60
60
  getToken(query: GitpodServer.GetTokenSearchOptions): Promise<Token | undefined>;
61
61
  getGitpodTokenScopes(tokenHash: string): Promise<string[]>;
@@ -72,8 +72,15 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
72
72
  getWorkspaceOwner(workspaceId: string): Promise<UserInfo | undefined>;
73
73
  getWorkspaceUsers(workspaceId: string): Promise<WorkspaceInstanceUser[]>;
74
74
  getFeaturedRepositories(): Promise<WhitelistedRepository[]>;
75
+ getSuggestedContextURLs(): Promise<string[]>;
76
+ /**
77
+ * **Security:**
78
+ * Sensitive information like an owner token is erased, since it allows access for all team members.
79
+ * If you need to access an owner token use `getOwnerToken` instead.
80
+ */
75
81
  getWorkspace(id: string): Promise<WorkspaceInfo>;
76
82
  isWorkspaceOwner(workspaceId: string): Promise<boolean>;
83
+ getOwnerToken(workspaceId: string): Promise<string>;
77
84
 
78
85
  /**
79
86
  * Creates and starts a workspace for the given context URL.
@@ -126,6 +133,11 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
126
133
  resetGenericInvite(inviteId: string): Promise<TeamMembershipInvite>;
127
134
  deleteTeam(teamId: string, userId: string): Promise<void>;
128
135
 
136
+ // Admin Settings
137
+ adminGetSettings(): Promise<InstallationAdminSettings>;
138
+ adminUpdateSettings(settings: InstallationAdminSettings): Promise<void>;
139
+ adminGetTelemetryData(): Promise<TelemetryData>;
140
+
129
141
  // Projects
130
142
  getProviderRepositoriesForUser(params: GetProviderRepositoriesParams): Promise<ProviderRepository[]>;
131
143
  createProject(params: CreateProjectParams): Promise<Project>;
@@ -142,6 +154,9 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
142
154
  guessRepositoryConfiguration(cloneUrl: string): Promise<string | undefined>;
143
155
  setProjectConfiguration(projectId: string, configString: string): Promise<void>;
144
156
  updateProjectPartial(partialProject: PartialProject): Promise<void>;
157
+ setProjectEnvironmentVariable(projectId: string, name: string, value: string, censored: boolean): Promise<void>;
158
+ getProjectEnvironmentVariables(projectId: string): Promise<ProjectEnvVar[]>;
159
+ deleteProjectEnvironmentVariable(variableId: string): Promise<void>;
145
160
 
146
161
  // content service
147
162
  getContentBlobUploadUrl(name: string): Promise<string>
@@ -239,6 +254,16 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
239
254
  identifyUser(event: RemoteIdentifyMessage): Promise<void>;
240
255
  }
241
256
 
257
+ export interface RateLimiterError {
258
+ method?: string,
259
+
260
+ /**
261
+ * Retry after this many seconds, earliest.
262
+ * cmp.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
263
+ */
264
+ retryAfter: number,
265
+ }
266
+
242
267
  export interface CreateProjectParams {
243
268
  name: string;
244
269
  slug?: string;
@@ -384,10 +409,12 @@ export class GitpodCompositeClient<Client extends GitpodClient> implements Gitpo
384
409
 
385
410
  public registerClient(client: Partial<Client>): Disposable {
386
411
  this.clients.push(client);
387
- const index = this.clients.length;
388
412
  return {
389
413
  dispose: () => {
390
- this.clients.slice(index, 1);
414
+ const index = this.clients.indexOf(client);
415
+ if (index > -1) {
416
+ this.clients.splice(index, 1);
417
+ }
391
418
  }
392
419
  }
393
420
  }
@@ -577,7 +604,7 @@ export class WorkspaceInstanceUpdateListener {
577
604
  }
578
605
 
579
606
  export interface GitpodServiceOptions {
580
- onReconnect?: () => (void | Promise<void>)
607
+ onReconnect?: () => (void | Promise<void>)
581
608
  }
582
609
 
583
610
  export class GitpodServiceImpl<Client extends GitpodClient, Server extends GitpodServer> {
@@ -7,7 +7,7 @@
7
7
  import { Event } from "./util/event";
8
8
  import { Disposable } from "./util/disposable";
9
9
 
10
- export type IDEFrontendState = 'init' | 'ready' | 'terminated';
10
+ export type IDEFrontendState = 'init' | 'ready' | 'terminated';
11
11
 
12
12
  export interface IDEFrontendService {
13
13
  readonly state: IDEFrontendState;
@@ -29,6 +29,28 @@ export interface IDEOptions {
29
29
  * The default desktop IDE when the user has not specified one.
30
30
  */
31
31
  defaultDesktopIde: string;
32
+
33
+ /**
34
+ * Client specific IDE options.
35
+ */
36
+ clients?: { [id: string]: IDEClient };
37
+ }
38
+
39
+ export interface IDEClient {
40
+ /**
41
+ * The default desktop IDE when the user has not specified one.
42
+ */
43
+ defaultDesktopIDE?: string;
44
+
45
+ /**
46
+ * Desktop IDEs supported by the client.
47
+ */
48
+ desktopIDEs?: string[]
49
+
50
+ /**
51
+ * Steps to install the client on user machine.
52
+ */
53
+ installationSteps?: string[]
32
54
  }
33
55
 
34
56
  export interface IDEOption {
@@ -80,6 +102,11 @@ export interface IDEOption {
80
102
  */
81
103
  image: string;
82
104
 
105
+ /**
106
+ * The latest image ref to the IDE image, this image ref always resolve to digest.
107
+ */
108
+ latestImage?: string;
109
+
83
110
  /**
84
111
  * When this is `true`, the tag of this image is resolved to the latest
85
112
  * image digest regularly.
package/src/index.ts CHANGED
@@ -19,3 +19,4 @@ export * from './context-url';
19
19
  export * from './teams-projects-protocol';
20
20
  export * from './snapshot-url';
21
21
  export * from './oss-allowlist';
22
+ export * from './installation-admin-protocol';
@@ -0,0 +1,42 @@
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 { v4 as uuidv4 } from 'uuid';
8
+
9
+ const InstallationAdminSettingsPrototype = {
10
+ sendTelemetry: true
11
+ }
12
+
13
+ export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype;
14
+
15
+ export namespace InstallationAdminSettings {
16
+ export function fields(): (keyof InstallationAdminSettings)[] {
17
+ return Object.keys(InstallationAdminSettingsPrototype) as (keyof InstallationAdminSettings)[];
18
+ }
19
+ }
20
+
21
+ export interface InstallationAdmin {
22
+ id: string;
23
+ settings: InstallationAdminSettings;
24
+ }
25
+
26
+ export interface TelemetryData {
27
+ installationAdmin: InstallationAdmin
28
+ totalUsers: number
29
+ totalWorkspaces: number
30
+ totalInstances: number
31
+ }
32
+
33
+ export namespace InstallationAdmin {
34
+ export function createDefault(): InstallationAdmin {
35
+ return {
36
+ id: uuidv4(),
37
+ settings: {
38
+ ...InstallationAdminSettingsPrototype,
39
+ }
40
+ };
41
+ }
42
+ }
@@ -78,4 +78,7 @@ export namespace ErrorCodes {
78
78
 
79
79
  // 630 Snapshot Error
80
80
  export const SNAPSHOT_ERROR = 630;
81
+
82
+ // 640 Headless logs are not available (yet)
83
+ export const HEADLESS_LOG_NOT_YET_AVAILABLE = 640;
81
84
  }
@@ -6,37 +6,34 @@
6
6
  */
7
7
 
8
8
  import * as ws from "ws";
9
- import * as http from "http";
10
- import * as net from "net";
11
9
  import { IWebSocket } from "vscode-ws-jsonrpc";
12
- import { log } from '../../util/logging';
13
10
 
14
- export interface OnOpen {
15
- (webSocket: ws, request: http.IncomingMessage, socket: net.Socket, head: Buffer): void;
16
- }
17
-
18
- export function toIWebSocket(webSocket: ws) {
19
- let sendsAfterOpen = 0;
11
+ export function toIWebSocket(ws: ws) {
20
12
  return <IWebSocket>{
21
13
  send: content => {
22
- if (webSocket.readyState !== ws.OPEN) {
23
- if (sendsAfterOpen++ > 3) {
24
- //log.debug(`Repeated try to send on closed web socket (readyState was ${webSocket.readyState})`, { ws });
25
- }
14
+ if (ws.readyState >= ws.CLOSING) {
15
+ // ws is already CLOSING/CLOSED, send() would just return an error.
26
16
  return;
27
17
  }
28
- webSocket.send(content, err => {
29
- if (err) {
30
- log.error('error in ws.send()', err, { ws });
31
- }
32
- })
18
+
19
+ // in general send-errors should trigger an 'error' event already, we just make sure it actually happens.
20
+ try {
21
+ ws.send(content, err => {
22
+ if (!err) {
23
+ return;
24
+ }
25
+ ws.emit('error', err);
26
+ });
27
+ } catch (err) {
28
+ ws.emit('error', err);
29
+ }
33
30
  },
34
- onMessage: cb => webSocket.on('message', cb),
35
- onError: cb => webSocket.on('error', cb),
36
- onClose: cb => webSocket.on('close', cb),
31
+ onMessage: cb => ws.on('message', cb),
32
+ onError: cb => ws.on('error', cb),
33
+ onClose: cb => ws.on('close', cb),
37
34
  dispose: () => {
38
- if (webSocket.readyState < ws.CLOSING) {
39
- webSocket.close();
35
+ if (ws.readyState < ws.CLOSING) {
36
+ ws.close();
40
37
  }
41
38
  }
42
39
  };
package/src/permission.ts CHANGED
@@ -13,6 +13,7 @@ export const Permissions = {
13
13
  "registry-access": undefined,
14
14
  "admin-users": undefined,
15
15
  "admin-workspaces": undefined,
16
+ "admin-projects": undefined,
16
17
  "admin-api": undefined,
17
18
  "ide-settings": undefined,
18
19
  "new-workspace-cluster": undefined,
@@ -34,6 +35,22 @@ export interface Role {
34
35
  permissions: PermissionName[],
35
36
  }
36
37
 
38
+ export namespace RolesOrPermissions {
39
+ export function toPermissionSet(rolesOrPermissions: RoleOrPermission[] | undefined): Set<PermissionName> {
40
+ rolesOrPermissions = rolesOrPermissions || [];
41
+
42
+ const permissions = new Set<PermissionName>();
43
+ for (const rop of rolesOrPermissions) {
44
+ if (Permission.is(rop)) {
45
+ permissions.add(rop);
46
+ } else if (RoleName.is(rop)) {
47
+ Role.getByName(rop).permissions.forEach(p => permissions.add(p));
48
+ }
49
+ }
50
+ return permissions;
51
+ };
52
+ }
53
+
37
54
  export namespace Permission {
38
55
  /** The permission to monitor the (live) state of a Gitpod installation */
39
56
  export const MONITOR: PermissionName = "monitor";
@@ -50,6 +67,9 @@ export namespace Permission {
50
67
  /** The permission for accessing all workspace data */
51
68
  export const ADMIN_WORKSPACES: PermissionName = "admin-workspaces";
52
69
 
70
+ /** The permission for accessing all projects data */
71
+ export const ADMIN_PROJECTS: PermissionName = "admin-projects";
72
+
53
73
  /** The permission to access the admin API */
54
74
  export const ADMIN_API: PermissionName = "admin-api";
55
75
 
@@ -62,9 +82,7 @@ export namespace Permission {
62
82
  }
63
83
 
64
84
  export const all = (): PermissionName[] => {
65
- return Object.keys(Permission)
66
- .map(k => (Permission as any)[k])
67
- .filter(k => typeof(k) === 'string');
85
+ return Object.keys(Permissions) as PermissionName[];
68
86
  };
69
87
  }
70
88
 
@@ -94,6 +112,7 @@ export namespace Role {
94
112
  permissions: [
95
113
  Permission.ADMIN_USERS,
96
114
  Permission.ADMIN_WORKSPACES,
115
+ Permission.ADMIN_PROJECTS,
97
116
  Permission.ADMIN_API,
98
117
  Permission.ENFORCEMENT,
99
118
  ]
package/src/protocol.ts CHANGED
@@ -119,6 +119,7 @@ export type IDESettings = {
119
119
  defaultIde?: string
120
120
  useDesktopIde?: boolean
121
121
  defaultDesktopIde?: string
122
+ useLatestVersion?: boolean
122
123
  }
123
124
 
124
125
  export interface UserPlatform {
@@ -154,12 +155,23 @@ export interface UserFeatureSettings {
154
155
  export const WorkspaceFeatureFlags = { "full_workspace_backup": undefined, "fixed_resources": undefined };
155
156
  export type NamedWorkspaceFeatureFlag = keyof (typeof WorkspaceFeatureFlags);
156
157
 
157
- export interface UserEnvVarValue {
158
- id?: string;
158
+ export interface EnvVarWithValue {
159
159
  name: string;
160
- repositoryPattern: string;
161
160
  value: string;
162
161
  }
162
+
163
+ export interface ProjectEnvVarWithValue extends EnvVarWithValue {
164
+ id: string;
165
+ projectId: string;
166
+ censored: boolean;
167
+ }
168
+
169
+ export type ProjectEnvVar = Omit<ProjectEnvVarWithValue, 'value'>;
170
+
171
+ export interface UserEnvVarValue extends EnvVarWithValue {
172
+ id?: string;
173
+ repositoryPattern: string; // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
174
+ }
163
175
  export interface UserEnvVar extends UserEnvVarValue {
164
176
  id: string;
165
177
  userId: string;
@@ -168,10 +180,12 @@ export interface UserEnvVar extends UserEnvVarValue {
168
180
 
169
181
  export namespace UserEnvVar {
170
182
 
183
+ // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
171
184
  export function normalizeRepoPattern(pattern: string) {
172
185
  return pattern.toLocaleLowerCase();
173
186
  }
174
187
 
188
+ // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
175
189
  export function score(value: UserEnvVarValue): number {
176
190
  // We use a score to enforce precedence:
177
191
  // value/value = 0
@@ -194,6 +208,7 @@ export namespace UserEnvVar {
194
208
  return score;
195
209
  }
196
210
 
211
+ // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
197
212
  export function filter<T extends UserEnvVarValue>(vars: T[], owner: string, repo: string): T[] {
198
213
  let result = vars.filter(e => {
199
214
  const [ownerPattern, repoPattern] = splitRepositoryPattern(e.repositoryPattern);
@@ -241,10 +256,11 @@ export namespace UserEnvVar {
241
256
  return result;
242
257
  }
243
258
 
259
+ // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
244
260
  export function splitRepositoryPattern(repositoryPattern: string): string[] {
245
261
  const patterns = repositoryPattern.split('/');
246
- const repoPattern = patterns.pop() || "";
247
- const ownerPattern = patterns.join('/');
262
+ const repoPattern = patterns.slice(1).join('/')
263
+ const ownerPattern = patterns[0];
248
264
  return [ownerPattern, repoPattern];
249
265
  }
250
266
  }
@@ -591,7 +607,7 @@ export interface GithubAppPrebuildConfig {
591
607
  branches?: boolean
592
608
  pullRequests?: boolean
593
609
  pullRequestsFromForks?: boolean
594
- addCheck?: boolean
610
+ addCheck?: boolean | 'prevent-merge-on-error'
595
611
  addBadge?: boolean
596
612
  addLabel?: boolean | string
597
613
  addComment?: boolean
@@ -799,15 +815,14 @@ export namespace WithSnapshot {
799
815
  }
800
816
  }
801
817
 
802
- export interface WithPrebuild {
803
- snapshotBucketId: string;
818
+ export interface WithPrebuild extends WithSnapshot {
804
819
  prebuildWorkspaceId: string;
805
820
  wasPrebuilt: true;
806
821
  }
807
822
  export namespace WithPrebuild {
808
823
  export function is(context: any): context is WithPrebuild {
809
824
  return context
810
- && 'snapshotBucketId' in context
825
+ && WithSnapshot.is(context)
811
826
  && 'prebuildWorkspaceId' in context
812
827
  && 'wasPrebuilt' in context;
813
828
  }
@@ -876,8 +891,20 @@ export namespace PrebuiltWorkspaceContext {
876
891
  }
877
892
  }
878
893
 
894
+ export interface WithReferrerContext extends WorkspaceContext {
895
+ referrer: string
896
+ referrerIde?: string
897
+ }
898
+
899
+ export namespace WithReferrerContext {
900
+ export function is(context: any): context is WithReferrerContext {
901
+ return context
902
+ && 'referrer' in context;
903
+ }
904
+ }
905
+
879
906
  export interface WithEnvvarsContext extends WorkspaceContext {
880
- envvars: UserEnvVarValue[];
907
+ envvars: EnvVarWithValue[];
881
908
  }
882
909
 
883
910
  export namespace WithEnvvarsContext {
@@ -1175,39 +1202,6 @@ export namespace AuthProviderEntry {
1175
1202
  }
1176
1203
  }
1177
1204
 
1178
- export interface Branding {
1179
- readonly name: string;
1180
- readonly favicon?: string;
1181
- /** Either including domain OR absolute path (interpreted relative to host URL) */
1182
- readonly logo: string;
1183
- readonly startupLogo: string;
1184
- readonly showProductivityTips: boolean;
1185
- readonly redirectUrlIfNotAuthenticated?: string;
1186
- readonly redirectUrlAfterLogout?: string;
1187
- readonly homepage: string;
1188
- readonly ide?: {
1189
- readonly logo: string;
1190
- readonly showReleaseNotes: boolean;
1191
- readonly helpMenu: Branding.Link[];
1192
- }
1193
- readonly links: {
1194
- readonly header: Branding.Link[];
1195
- readonly footer: Branding.Link[];
1196
- readonly social: Branding.SocialLink[];
1197
- readonly legal: Branding.Link[];
1198
- }
1199
- }
1200
- export namespace Branding {
1201
- export interface Link {
1202
- readonly name: string;
1203
- readonly url: string;
1204
- }
1205
- export interface SocialLink {
1206
- readonly type: string;
1207
- readonly url: string;
1208
- }
1209
- }
1210
-
1211
1205
  export interface Configuration {
1212
1206
  readonly daysBeforeGarbageCollection: number;
1213
1207
  readonly garbageCollectionStartDate: number;
@@ -42,7 +42,13 @@ export namespace Project {
42
42
  }
43
43
 
44
44
  export interface Overview {
45
- branches: BranchDetails[]
45
+ branches: BranchDetails[];
46
+ }
47
+
48
+ export namespace Overview {
49
+ export function is(data?: any): data is Project.Overview {
50
+ return Array.isArray(data?.branches);
51
+ }
46
52
  }
47
53
 
48
54
  export interface BranchDetails {
@@ -34,6 +34,7 @@ const expect = chai.expect
34
34
  ['gitpod-io','gitpod','gitpodio-gitpod-'],
35
35
  ['this is rather long and has some "§$"% special chars','also here pretty long and needs abbreviation','thisisratherlon-alsohere-'],
36
36
  ['breatheco-de', 'python-flask-api-tutorial', 'breathecode-pythonflaska-'],
37
+ ['UPPER', "CaSe", "upper-case-"]
37
38
  ]
38
39
  for (const d of data) {
39
40
  const id = await generateWorkspaceID(d[0], d[1]);
@@ -15,6 +15,7 @@ function clean(segment: string | undefined, maxChars: number = 15) {
15
15
  if (!segment) {
16
16
  return undefined;
17
17
  }
18
+ segment = segment.toLowerCase();
18
19
  let result = '';
19
20
  for (let i =0; i < segment.length; i++) {
20
21
  if (characters.indexOf(segment[i]) !== -1) {
@@ -108,10 +108,6 @@ export class GitpodHostUrl {
108
108
  return this.with(url => ({ pathname: '/preferences' }));
109
109
  }
110
110
 
111
- asGraphQLApi(): GitpodHostUrl {
112
- return this.with(url => ({ pathname: '/graphql/' }));
113
- }
114
-
115
111
  asStart(workspaceId = this.workspaceId): GitpodHostUrl {
116
112
  return this.withoutWorkspacePrefix().with({
117
113
  pathname: '/start/',
@@ -154,7 +150,7 @@ export class GitpodHostUrl {
154
150
  return undefined;
155
151
  }
156
152
 
157
- get blobServe(): boolean {
153
+ get blobServe(): boolean {
158
154
  const hostSegments = this.url.host.split(".");
159
155
  if (hostSegments[0] === 'blobserve') {
160
156
  return true;