@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.
- package/data/gitpod-schema.json +12 -0
- package/lib/admin-protocol.d.ts +10 -0
- package/lib/admin-protocol.d.ts.map +1 -1
- package/lib/admin-protocol.js.map +1 -1
- package/lib/analytics.d.ts +4 -5
- package/lib/analytics.d.ts.map +1 -1
- package/lib/context-url.d.ts +18 -5
- package/lib/context-url.d.ts.map +1 -1
- package/lib/context-url.js +58 -5
- package/lib/context-url.js.map +1 -1
- package/lib/context-url.spec.d.ts +2 -0
- package/lib/context-url.spec.d.ts.map +1 -1
- package/lib/context-url.spec.js +25 -4
- package/lib/context-url.spec.js.map +1 -1
- package/lib/gitpod-service.d.ts +23 -2
- package/lib/gitpod-service.d.ts.map +1 -1
- package/lib/gitpod-service.js +4 -2
- package/lib/gitpod-service.js.map +1 -1
- package/lib/ide-protocol.d.ts +24 -0
- package/lib/ide-protocol.d.ts.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/installation-admin-protocol.d.ts +27 -0
- package/lib/installation-admin-protocol.d.ts.map +1 -0
- package/lib/installation-admin-protocol.js +30 -0
- package/lib/installation-admin-protocol.js.map +1 -0
- package/lib/messaging/error.d.ts +1 -0
- package/lib/messaging/error.d.ts.map +1 -1
- package/lib/messaging/error.js +2 -0
- package/lib/messaging/error.js.map +1 -1
- package/lib/messaging/node/connection.d.ts +1 -7
- package/lib/messaging/node/connection.d.ts.map +1 -1
- package/lib/messaging/node/connection.js +20 -18
- package/lib/messaging/node/connection.js.map +1 -1
- package/lib/permission.d.ts +7 -1
- package/lib/permission.d.ts.map +1 -1
- package/lib/permission.js +24 -4
- package/lib/permission.js.map +1 -1
- package/lib/protocol.d.ts +22 -39
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +16 -4
- package/lib/protocol.js.map +1 -1
- package/lib/teams-projects-protocol.d.ts +3 -0
- package/lib/teams-projects-protocol.d.ts.map +1 -1
- package/lib/teams-projects-protocol.js +7 -0
- package/lib/teams-projects-protocol.js.map +1 -1
- package/lib/util/generate-workspace-id.d.ts.map +1 -1
- package/lib/util/generate-workspace-id.js +1 -0
- package/lib/util/generate-workspace-id.js.map +1 -1
- package/lib/util/generate-workspace-id.spec.js +1 -0
- package/lib/util/generate-workspace-id.spec.js.map +1 -1
- package/lib/util/gitpod-host-url.d.ts +0 -1
- package/lib/util/gitpod-host-url.d.ts.map +1 -1
- package/lib/util/gitpod-host-url.js +0 -3
- package/lib/util/gitpod-host-url.js.map +1 -1
- package/lib/util/tracing.d.ts +2 -1
- package/lib/util/tracing.d.ts.map +1 -1
- package/lib/util/tracing.js +29 -6
- package/lib/util/tracing.js.map +1 -1
- package/lib/workspace-cluster.d.ts +9 -7
- package/lib/workspace-cluster.d.ts.map +1 -1
- package/lib/workspace-cluster.js +18 -1
- package/lib/workspace-cluster.js.map +1 -1
- package/lib/workspace-instance.d.ts +19 -0
- package/lib/workspace-instance.d.ts.map +1 -1
- package/package.json +2 -2
- package/pkg-yarn.lock +2 -2
- package/provenance-bundle.jsonl +1 -1
- package/src/admin-protocol.ts +17 -4
- package/src/analytics.ts +4 -6
- package/src/context-url.spec.ts +18 -4
- package/src/context-url.ts +62 -6
- package/src/gitpod-service.ts +33 -6
- package/src/ide-frontend-service.ts +1 -1
- package/src/ide-protocol.ts +27 -0
- package/src/index.ts +1 -0
- package/src/installation-admin-protocol.ts +42 -0
- package/src/messaging/error.ts +3 -0
- package/src/messaging/node/connection.ts +20 -23
- package/src/permission.ts +22 -3
- package/src/protocol.ts +37 -43
- package/src/teams-projects-protocol.ts +7 -1
- package/src/util/generate-workspace-id.spec.ts +1 -0
- package/src/util/generate-workspace-id.ts +1 -0
- package/src/util/gitpod-host-url.ts +1 -5
- package/src/util/tracing.ts +29 -7
- package/src/workspace-cluster.ts +17 -9
- package/src/workspace-instance.ts +20 -0
- package/lib/messaging/connection-error-handler.d.ts +0 -27
- package/lib/messaging/connection-error-handler.d.ts.map +0 -1
- package/lib/messaging/connection-error-handler.js +0 -34
- package/lib/messaging/connection-error-handler.js.map +0 -1
- package/lib/util/safe-promise.d.ts +0 -11
- package/lib/util/safe-promise.d.ts.map +0 -1
- package/lib/util/safe-promise.js +0 -31
- package/lib/util/safe-promise.js.map +0 -1
- package/src/messaging/connection-error-handler.ts +0 -62
- package/src/util/safe-promise.ts +0 -26
package/src/context-url.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
77
|
+
return segments[0]; // this might be something, we just try
|
|
26
78
|
}
|
|
27
79
|
|
|
28
|
-
const segmentsToURL = (offset: number):
|
|
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
|
|
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
|
|
package/src/gitpod-service.ts
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
User, WorkspaceInfo, WorkspaceCreationResult, WorkspaceInstanceUser,
|
|
9
|
-
WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo,
|
|
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.
|
|
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 |
|
|
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' |
|
|
10
|
+
export type IDEFrontendState = 'init' | 'ready' | 'terminated';
|
|
11
11
|
|
|
12
12
|
export interface IDEFrontendService {
|
|
13
13
|
readonly state: IDEFrontendState;
|
package/src/ide-protocol.ts
CHANGED
|
@@ -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
|
@@ -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
|
+
}
|
package/src/messaging/error.ts
CHANGED
|
@@ -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
|
|
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 (
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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 =>
|
|
35
|
-
onError: cb =>
|
|
36
|
-
onClose: 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 (
|
|
39
|
-
|
|
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(
|
|
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
|
|
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.
|
|
247
|
-
const ownerPattern = patterns
|
|
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
|
-
&&
|
|
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:
|
|
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;
|