Skip to content

Commit b05a8ea

Browse files
committed
[server] use owner and repo name for workspace id
This change introduces optional arguments in generateWorkspaceId for the first two segments. And makes use of it in workspace factory using the repos org/group and name. fixes #4129
1 parent 94c71c0 commit b05a8ea

File tree

5 files changed

+56
-11
lines changed

5 files changed

+56
-11
lines changed

components/gitpod-protocol/src/util/generate-workspace-id.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,21 @@ const expect = chai.expect
2727
expect(longestName.length <= 36, `"${longestName}" is longer than 36 chars (${longestName.length})`).to.be.true;
2828
}
2929

30+
@test public async testCustomName() {
31+
const data = [
32+
['foo','bar','foo-bar-'],
33+
['f','bar','.{2,16}-bar-'],
34+
['gitpod-io','gitpod','gitpodio-gitpod-'],
35+
['this is rather long and has some "§$"% special chars','also here pretty long and needs abbreviation','thisisratherlong-alsohere-'],
36+
['breatheco-de', 'python-flask-api-tutorial', 'breathecode-pythonflaskap-'],
37+
]
38+
for (const d of data) {
39+
const id = await generateWorkspaceID(d[0], d[1]);
40+
expect(id).match(new RegExp("^"+d[2]));
41+
expect(new GitpodHostUrl().withWorkspacePrefix(id, "eu").workspaceId).to.equal(id);
42+
expect(id.length <= 36, `"${id}" is longer than 36 chars (${id.length})`).to.be.true;
43+
}
44+
}
45+
3046
}
3147
module.exports = new TestGenerateWorkspaceId()

components/gitpod-protocol/src/util/generate-workspace-id.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,25 @@
55
*/
66
import randomNumber = require("random-number-csprng");
77

8-
export async function generateWorkspaceID(): Promise<string> {
9-
return (await random(colors))+'-'+(await random(animals))+'-'+(await random(characters, 8));
8+
export async function generateWorkspaceID(firstSegment?: string, secondSegment?: string): Promise<string> {
9+
const firstSeg = clean(firstSegment) || await random(colors);
10+
const secSeg = clean(secondSegment, Math.min(16, 24-firstSeg.length)) || await random(animals);
11+
return firstSeg+'-'+secSeg+'-'+(await random(characters, 8));
12+
}
13+
14+
function clean(segment: string | undefined, maxChars: number = 16) {
15+
if (segment) {
16+
let result = '';
17+
for (let i =0; i < segment.length; i++) {
18+
if (characters.indexOf(segment[i]) !== -1) {
19+
result += segment[i];
20+
}
21+
}
22+
if (result.length >= 2) {
23+
return result.substring(0, maxChars);
24+
}
25+
}
26+
return undefined;
1027
}
1128

1229
async function random(array: string[], length: number = 1): Promise<string> {

components/gitpod-protocol/src/util/gitpod-host-url.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ export interface UrlChange {
1212
}
1313
export type UrlUpdate = UrlChange | Partial<URL>;
1414

15-
const basewoWkspaceIDRegex = "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8}))";
15+
const baseWorkspaceIDRegex = "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8}))";
1616

1717
// this pattern matches v4 UUIDs as well as the new generated workspace ids (e.g. pink-panda-ns35kd21)
18-
const workspaceIDRegex = RegExp(`^${basewoWkspaceIDRegex}$`);
18+
const workspaceIDRegex = RegExp(`^${baseWorkspaceIDRegex}$`);
1919

2020
// this pattern matches URL prefixes of workspaces
21-
const workspaceUrlPrefixRegex = RegExp(`^([0-9]{4,6}-)?${basewoWkspaceIDRegex}\\.`);
21+
const workspaceUrlPrefixRegex = RegExp(`^([0-9]{4,6}-)?${baseWorkspaceIDRegex}\\.`);
2222

2323
export class GitpodHostUrl {
2424
readonly url: URL;

components/server/ee/src/workspace/workspace-factory.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { LicenseEvaluator } from '@gitpod/licensor/lib';
1414
import { Feature } from '@gitpod/licensor/lib/api';
1515
import { ResponseError } from 'vscode-jsonrpc';
1616
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
17-
import { generateWorkspaceID } from '@gitpod/gitpod-protocol/lib/util/generate-workspace-id';
1817
import { HostContextProvider } from '../../../src/auth/host-context-provider';
1918
import { RepoURL } from '../../../src/repohost';
2019

@@ -220,7 +219,7 @@ export class WorkspaceFactoryEE extends WorkspaceFactory {
220219
}
221220
}
222221

223-
const id = await generateWorkspaceID();
222+
const id = await this.generateWorkspaceID(context);
224223
const newWs: Workspace = {
225224
id,
226225
type: "regular",

components/server/src/workspace/workspace-factory.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
*/
66

77
import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB, ProjectDB, TeamDB } from '@gitpod/gitpod-db/lib';
8-
import { AdditionalContentContext, CommitContext, IssueContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext } from '@gitpod/gitpod-protocol';
8+
import { AdditionalContentContext, CommitContext, IssueContext, PrebuiltWorkspaceContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext } from '@gitpod/gitpod-protocol';
99
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
1010
import { generateWorkspaceID } from '@gitpod/gitpod-protocol/lib/util/generate-workspace-id';
1111
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
1212
import { TraceContext } from '@gitpod/gitpod-protocol/lib/util/tracing';
1313
import { inject, injectable } from 'inversify';
1414
import { ResponseError } from 'vscode-jsonrpc';
15+
import { RepoURL } from '../repohost';
1516
import { ConfigProvider } from './config-provider';
1617
import { ImageSourceProvider } from './image-source-provider';
1718

@@ -55,7 +56,7 @@ export class WorkspaceFactory {
5556
// Basically we're using the raw alpine image bait-and-switch style without adding the GP layer.
5657
const imageSource = await this.imageSourceProvider.getImageSource(ctx, user, null as any, config);
5758

58-
const id = await generateWorkspaceID();
59+
const id = await this.generateWorkspaceID(context);
5960
const date = new Date().toISOString();
6061
const newWs: Workspace = {
6162
id,
@@ -94,7 +95,7 @@ export class WorkspaceFactory {
9495
throw new Error(`The original workspace has been deleted - cannot open this snapshot.`);
9596
}
9697

97-
const id = await generateWorkspaceID();
98+
const id = await this.generateWorkspaceID(context);
9899
const date = new Date().toISOString();
99100
const newWs = <Workspace>{
100101
id,
@@ -166,7 +167,7 @@ export class WorkspaceFactory {
166167
}
167168
}
168169

169-
const id = await generateWorkspaceID();
170+
const id = await this.generateWorkspaceID(context);
170171
const newWs: Workspace = {
171172
id,
172173
type: "regular",
@@ -207,4 +208,16 @@ export class WorkspaceFactory {
207208
return context.title;
208209
}
209210

211+
protected async generateWorkspaceID(context: WorkspaceContext): Promise<string> {
212+
let ctx = context;
213+
if (PrebuiltWorkspaceContext.is(context)) {
214+
ctx = context.originalContext;
215+
}
216+
if (CommitContext.is(ctx)) {
217+
const parsed = RepoURL.parseRepoUrl(ctx.repository.cloneUrl);
218+
return await generateWorkspaceID(parsed?.owner, parsed?.repo);
219+
}
220+
return await generateWorkspaceID();
221+
}
222+
210223
}

0 commit comments

Comments
 (0)