mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
1246 lines
35 KiB
TypeScript
1246 lines
35 KiB
TypeScript
/**
|
|
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
* Licensed under the GNU Affero General Public License (AGPL).
|
|
* See License-AGPL.txt in the project root for license information.
|
|
*/
|
|
|
|
import { WorkspaceInstance, PortVisibility } from "./workspace-instance";
|
|
import { RoleOrPermission } from "./permission";
|
|
import { Project } from "./teams-projects-protocol";
|
|
|
|
export interface UserInfo {
|
|
name?: string
|
|
}
|
|
|
|
export interface User {
|
|
/** The user id */
|
|
id: string
|
|
|
|
/** The timestamp when the user entry was created */
|
|
creationDate: string
|
|
|
|
avatarUrl?: string
|
|
|
|
name?: string
|
|
|
|
/** Optional for backwards compatibility */
|
|
fullName?: string
|
|
|
|
identities: Identity[]
|
|
|
|
/**
|
|
* Whether the user has been blocked to use our service, because of TOS violation for example.
|
|
* Optional for backwards compatibility.
|
|
*/
|
|
blocked?: boolean;
|
|
|
|
/** A map of random settings that alter the behaviour of Gitpod on a per-user basis */
|
|
featureFlags?: UserFeatureSettings;
|
|
|
|
/** The permissions and/or roles the user has */
|
|
rolesOrPermissions?: RoleOrPermission[];
|
|
|
|
/** Whether the user is logical deleted. This flag is respected by all queries in UserDB */
|
|
markedDeleted?: boolean;
|
|
|
|
additionalData?: AdditionalUserData;
|
|
}
|
|
|
|
export namespace User {
|
|
export function is(data: any): data is User {
|
|
return data
|
|
&& data.hasOwnProperty('id')
|
|
&& data.hasOwnProperty('identities')
|
|
}
|
|
export function getIdentity(user: User, authProviderId: string): Identity | undefined {
|
|
return user.identities.find(id => id.authProviderId === authProviderId);
|
|
}
|
|
export function censor(user: User): User {
|
|
const res = { ...user };
|
|
delete (res.additionalData);
|
|
res.identities = res.identities.map(i => {
|
|
delete (i.tokens);
|
|
|
|
// The user field is not in the Identity shape, but actually exists on DBIdentity.
|
|
// Trying to push this object out via JSON RPC will fail because of the cyclic nature
|
|
// of this field.
|
|
delete ((i as any).user);
|
|
return i;
|
|
});
|
|
return res;
|
|
}
|
|
export function getPrimaryEmail(user: User): string {
|
|
const identities = user.identities.filter(i => !!i.primaryEmail);
|
|
if (identities.length <= 0) {
|
|
throw new Error(`No identity with primary email for user: ${user.id}!`);
|
|
}
|
|
|
|
return identities[0].primaryEmail!;
|
|
}
|
|
export function getName(user: User): string | undefined {
|
|
const name = user.fullName || user.name;
|
|
if (name) {
|
|
return name;
|
|
}
|
|
|
|
for (const id of user.identities) {
|
|
if (id.authName !== "") {
|
|
return id.authName;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export interface AdditionalUserData {
|
|
platforms?: UserPlatform[];
|
|
emailNotificationSettings?: EmailNotificationSettings;
|
|
featurePreview?: boolean;
|
|
ideSettings?: IDESettings;
|
|
// key is the name of the news, string the iso date when it was seen
|
|
whatsNewSeen?: { [key: string]: string }
|
|
// key is the name of the OAuth client i.e. local app, string the iso date when it was approved
|
|
// TODO(rl): provide a management UX to allow rescinding of approval
|
|
oauthClientsApproved?: { [key: string]: string }
|
|
// to remember GH Orgs the user installed/updated the GH App for
|
|
knownGitHubOrgs?: string[];
|
|
|
|
// Git clone URL pointing to the user's dotfile repo
|
|
dotfileRepo?: string;
|
|
}
|
|
|
|
export interface EmailNotificationSettings {
|
|
allowsChangelogMail?: boolean;
|
|
allowsDevXMail?: boolean;
|
|
allowsOnboardingMail?: boolean;
|
|
}
|
|
|
|
export type IDESettings = {
|
|
defaultIde?: string
|
|
useDesktopIde?: boolean
|
|
defaultDesktopIde?: string
|
|
useLatestVersion?: boolean
|
|
}
|
|
|
|
export interface UserPlatform {
|
|
uid: string;
|
|
userAgent: string;
|
|
browser: string;
|
|
os: string;
|
|
lastUsed: string;
|
|
firstUsed: string;
|
|
/**
|
|
* Since when does the user have the browser extension installe don this device.
|
|
*/
|
|
browserExtensionInstalledSince?: string;
|
|
|
|
/**
|
|
* Since when does the user not have the browser extension installed anymore (but previously had).
|
|
*/
|
|
browserExtensionUninstalledSince?: string;
|
|
}
|
|
|
|
export interface UserFeatureSettings {
|
|
/**
|
|
* Permanent feature flags are added to each and every workspace instance
|
|
* this user starts.
|
|
*/
|
|
permanentWSFeatureFlags?: NamedWorkspaceFeatureFlag[];
|
|
}
|
|
|
|
/**
|
|
* The values of this type MUST MATCH enum values in WorkspaceFeatureFlag from ws-manager/client/core_pb.d.ts
|
|
* If they don't we'll break things during workspace startup.
|
|
*/
|
|
export const WorkspaceFeatureFlags = { "full_workspace_backup": undefined, "fixed_resources": undefined };
|
|
export type NamedWorkspaceFeatureFlag = keyof (typeof WorkspaceFeatureFlags);
|
|
|
|
export interface EnvVarWithValue {
|
|
name: string;
|
|
value: string;
|
|
}
|
|
|
|
export interface ProjectEnvVarWithValue extends EnvVarWithValue {
|
|
id: string;
|
|
projectId: string;
|
|
censored: boolean;
|
|
}
|
|
|
|
export type ProjectEnvVar = Omit<ProjectEnvVarWithValue, 'value'>;
|
|
|
|
export interface UserEnvVarValue extends EnvVarWithValue {
|
|
id?: string;
|
|
repositoryPattern: string; // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
|
|
}
|
|
export interface UserEnvVar extends UserEnvVarValue {
|
|
id: string;
|
|
userId: string;
|
|
deleted?: boolean;
|
|
}
|
|
|
|
export namespace UserEnvVar {
|
|
|
|
// DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
|
|
export function normalizeRepoPattern(pattern: string) {
|
|
return pattern.toLocaleLowerCase();
|
|
}
|
|
|
|
// DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
|
|
export function score(value: UserEnvVarValue): number {
|
|
// We use a score to enforce precedence:
|
|
// value/value = 0
|
|
// value/* = 1
|
|
// */value = 2
|
|
// */* = 3
|
|
// #/# = 4 (used for env vars passed through the URL)
|
|
// the lower the score, the higher the precedence.
|
|
const [ownerPattern, repoPattern] = splitRepositoryPattern(value.repositoryPattern);
|
|
let score = 0;
|
|
if (repoPattern == "*") {
|
|
score += 1;
|
|
}
|
|
if (ownerPattern == '*') {
|
|
score += 2;
|
|
}
|
|
if (ownerPattern == "#" || repoPattern == "#") {
|
|
score = 4;
|
|
}
|
|
return score;
|
|
}
|
|
|
|
// DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
|
|
export function filter<T extends UserEnvVarValue>(vars: T[], owner: string, repo: string): T[] {
|
|
let result = vars.filter(e => {
|
|
const [ownerPattern, repoPattern] = splitRepositoryPattern(e.repositoryPattern);
|
|
if (ownerPattern !== '*' && ownerPattern !== '#' && (!!owner && ownerPattern !== owner.toLocaleLowerCase())) {
|
|
return false;
|
|
}
|
|
if (repoPattern !== '*' && repoPattern !== '#' && (!!repo && repoPattern !== repo.toLocaleLowerCase())) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
const resmap = new Map<string, T[]>();
|
|
result.forEach(e => {
|
|
const l = (resmap.get(e.name) || []);
|
|
l.push(e);
|
|
resmap.set(e.name, l);
|
|
});
|
|
|
|
result = [];
|
|
for (const name of resmap.keys()) {
|
|
const candidates = resmap.get(name);
|
|
if (!candidates) {
|
|
// not sure how this can happen, but so be it
|
|
continue;
|
|
}
|
|
|
|
if (candidates.length == 1) {
|
|
result.push(candidates[0]);
|
|
continue;
|
|
}
|
|
|
|
let minscore = 10;
|
|
let bestCandidate: T | undefined;
|
|
for (const e of candidates) {
|
|
const score = UserEnvVar.score(e);
|
|
if (!bestCandidate || score < minscore) {
|
|
minscore = score;
|
|
bestCandidate = e;
|
|
}
|
|
}
|
|
result.push(bestCandidate!);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322
|
|
export function splitRepositoryPattern(repositoryPattern: string): string[] {
|
|
const patterns = repositoryPattern.split('/');
|
|
const repoPattern = patterns.slice(1).join('/')
|
|
const ownerPattern = patterns[0];
|
|
return [ownerPattern, repoPattern];
|
|
}
|
|
}
|
|
|
|
export interface GitpodToken {
|
|
|
|
/** Hash value (SHA256) of the token (primary key). */
|
|
tokenHash: string
|
|
|
|
/** Human readable name of the token */
|
|
name?: string
|
|
|
|
/** Token kind */
|
|
type: GitpodTokenType
|
|
|
|
/** The user the token belongs to. */
|
|
user: User
|
|
|
|
/** Scopes (e.g. limition to read-only) */
|
|
scopes: string[]
|
|
|
|
/** Created timestamp */
|
|
created: string
|
|
|
|
// token is deleted on the database and about to be collected by db-sync
|
|
deleted?: boolean
|
|
}
|
|
|
|
export enum GitpodTokenType {
|
|
API_AUTH_TOKEN = 0,
|
|
MACHINE_AUTH_TOKEN = 1
|
|
}
|
|
|
|
export interface OneTimeSecret {
|
|
id: string
|
|
|
|
value: string
|
|
|
|
expirationTime: string;
|
|
|
|
deleted: boolean;
|
|
}
|
|
|
|
export interface WorkspaceInstanceUser {
|
|
name?: string;
|
|
avatarUrl?: string;
|
|
instanceId: string;
|
|
userId: string;
|
|
lastSeen: string;
|
|
}
|
|
|
|
export interface Identity {
|
|
authProviderId: string;
|
|
authId: string;
|
|
authName: string;
|
|
primaryEmail?: string;
|
|
/** @deprecated */
|
|
tokens?: Token[];
|
|
/** This is a flag that triggers the HARD DELETION of this entity */
|
|
deleted?: boolean;
|
|
// readonly identities cannot be modified by the user
|
|
readonly?: boolean;
|
|
}
|
|
|
|
export type IdentityLookup = Pick<Identity, "authProviderId" | "authId">;
|
|
|
|
export namespace Identity {
|
|
export function is(data: any): data is Identity {
|
|
return data.hasOwnProperty('authProviderId')
|
|
&& data.hasOwnProperty('authId')
|
|
&& data.hasOwnProperty('authName')
|
|
}
|
|
export function equals(id1: IdentityLookup, id2: IdentityLookup) {
|
|
return id1.authProviderId === id2.authProviderId
|
|
&& id1.authId === id2.authId
|
|
}
|
|
}
|
|
|
|
export interface Token {
|
|
value: string;
|
|
scopes: string[];
|
|
updateDate?: string;
|
|
expiryDate?: string;
|
|
idToken?: string;
|
|
refreshToken?: string;
|
|
username?: string;
|
|
}
|
|
export interface TokenEntry {
|
|
uid: string;
|
|
authProviderId: string;
|
|
authId: string;
|
|
token: Token;
|
|
expiryDate?: string;
|
|
refreshable?: boolean;
|
|
/** This is a flag that triggers the HARD DELETION of this entity */
|
|
deleted?: boolean;
|
|
}
|
|
|
|
export interface EmailDomainFilterEntry {
|
|
domain: string;
|
|
negative: boolean;
|
|
}
|
|
|
|
export interface EduEmailDomain {
|
|
domain: string;
|
|
}
|
|
|
|
export type AppInstallationPlatform = "github";
|
|
export type AppInstallationState = "claimed.user" | "claimed.platform" | "installed" | "uninstalled";
|
|
export interface AppInstallation {
|
|
platform: AppInstallationPlatform;
|
|
installationID: string;
|
|
ownerUserID?: string;
|
|
platformUserID?: string;
|
|
state: AppInstallationState;
|
|
creationTime: string;
|
|
lastUpdateTime: string;
|
|
}
|
|
|
|
export interface PendingGithubEvent {
|
|
id: string;
|
|
githubUserId: string;
|
|
creationDate: Date;
|
|
type: string;
|
|
event: string;
|
|
}
|
|
|
|
export interface Snapshot {
|
|
id: string;
|
|
creationTime: string;
|
|
availableTime?: string;
|
|
originalWorkspaceId: string;
|
|
bucketId: string;
|
|
layoutData?: string;
|
|
state: SnapshotState;
|
|
message?: string;
|
|
}
|
|
|
|
export type SnapshotState = 'pending' | 'available' | 'error';
|
|
|
|
export interface LayoutData {
|
|
workspaceId: string;
|
|
lastUpdatedTime: string;
|
|
layoutData: string;
|
|
}
|
|
|
|
export interface Workspace {
|
|
id: string;
|
|
creationTime: string;
|
|
contextURL: string;
|
|
description: string;
|
|
ownerId: string;
|
|
projectId?: string;
|
|
context: WorkspaceContext;
|
|
config: WorkspaceConfig;
|
|
|
|
/**
|
|
* The source where to get the workspace base image from. This source is resolved
|
|
* during workspace creation. Once a base image has been built the information in here
|
|
* is superseded by baseImageNameResolved.
|
|
*/
|
|
imageSource?: WorkspaceImageSource;
|
|
|
|
/**
|
|
* The resolved, fix name of the workspace image. We only use this
|
|
* to access the logs during an image build.
|
|
*/
|
|
imageNameResolved?: string
|
|
|
|
/**
|
|
* The resolved/built fixed named of the base image. This field is only set if the workspace
|
|
* already has its base image built.
|
|
*/
|
|
baseImageNameResolved?: string
|
|
|
|
shareable?: boolean;
|
|
pinned?: boolean;
|
|
|
|
// workspace is hard-deleted on the database and about to be collected by db-sync
|
|
readonly deleted?: boolean;
|
|
|
|
/**
|
|
* Mark as deleted (user-facing). The actual deletion of the workspace content is executed
|
|
* with a (configurable) delay
|
|
*/
|
|
softDeleted?: WorkspaceSoftDeletion;
|
|
|
|
/**
|
|
* Marks the time when the workspace was marked as softDeleted. The actual deletion of the
|
|
* workspace content happens after a configurable period
|
|
*/
|
|
softDeletedTime?: string;
|
|
|
|
/**
|
|
* Marks the time when the workspace content has been deleted.
|
|
*/
|
|
contentDeletedTime?: string;
|
|
|
|
type: WorkspaceType;
|
|
|
|
basedOnPrebuildId?: string;
|
|
|
|
basedOnSnapshotId?: string;
|
|
}
|
|
|
|
export type WorkspaceSoftDeletion = "user" | "gc";
|
|
|
|
export type WorkspaceType = "regular" | "prebuild" | "probe";
|
|
|
|
export namespace Workspace {
|
|
|
|
export function getFullRepositoryName(ws: Workspace): string | undefined {
|
|
if (CommitContext.is(ws.context)) {
|
|
return ws.context.repository.owner + '/' + ws.context.repository.name
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function getFullRepositoryUrl(ws: Workspace): string | undefined {
|
|
if (CommitContext.is(ws.context)) {
|
|
return `https://${ws.context.repository.host}/${getFullRepositoryName(ws)}`
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function getPullRequestNumber(ws: Workspace): number | undefined {
|
|
if (PullRequestContext.is(ws.context)) {
|
|
return ws.context.nr;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function getIssueNumber(ws: Workspace): number | undefined {
|
|
if (IssueContext.is(ws.context)) {
|
|
return ws.context.nr;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function getBranchName(ws: Workspace): string | undefined {
|
|
if (CommitContext.is(ws.context)) {
|
|
return ws.context.ref;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function getCommit(ws: Workspace): string | undefined {
|
|
if (CommitContext.is(ws.context)) {
|
|
return ws.context.revision && ws.context.revision.substr(0, 8);
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export interface PreparePluginUploadParams {
|
|
fullPluginName: string;
|
|
}
|
|
|
|
export interface ResolvePluginsParams {
|
|
config?: WorkspaceConfig
|
|
builtins?: ResolvedPlugins
|
|
vsxRegistryUrl?: string
|
|
}
|
|
|
|
export interface InstallPluginsParams {
|
|
pluginIds: string[]
|
|
}
|
|
|
|
export interface UninstallPluginParams {
|
|
pluginId: string;
|
|
}
|
|
|
|
export interface GuessGitTokenScopesParams {
|
|
host: string
|
|
repoUrl: string
|
|
gitCommand: string
|
|
currentToken: GitToken
|
|
}
|
|
|
|
export interface GitToken {
|
|
token: string
|
|
user: string
|
|
scopes: string[]
|
|
}
|
|
|
|
export interface GuessedGitTokenScopes {
|
|
message?: string
|
|
scopes?: string[]
|
|
}
|
|
|
|
export type ResolvedPluginKind = 'user' | 'workspace' | 'builtin';
|
|
|
|
export interface ResolvedPlugins {
|
|
[pluginId: string]: ResolvedPlugin | undefined
|
|
}
|
|
|
|
export interface ResolvedPlugin {
|
|
fullPluginName: string;
|
|
url: string;
|
|
kind: ResolvedPluginKind;
|
|
}
|
|
|
|
export interface VSCodeConfig {
|
|
extensions?: string[];
|
|
}
|
|
|
|
export interface WorkspaceConfig {
|
|
image?: ImageConfig;
|
|
ports?: PortConfig[];
|
|
tasks?: TaskConfig[];
|
|
checkoutLocation?: string;
|
|
workspaceLocation?: string;
|
|
gitConfig?: { [config: string]: string };
|
|
github?: GithubAppConfig;
|
|
vscode?: VSCodeConfig;
|
|
|
|
/** deprecated. Enabled by default **/
|
|
experimentalNetwork?: boolean;
|
|
|
|
/**
|
|
* Where the config object originates from.
|
|
*
|
|
* repo - from the repository
|
|
* project-db - from the "Project" stored in the database
|
|
* definitly-gp - from github.com/gitpod-io/definitely-gp
|
|
* derived - computed based on analyzing the repository
|
|
* additional-content - config comes from additional content, usually provided through the project's configuration
|
|
* default - our static catch-all default config
|
|
*/
|
|
_origin?: 'repo' | 'project-db' | 'definitely-gp' | 'derived' | 'additional-content' | 'default';
|
|
|
|
/**
|
|
* Set of automatically infered feature flags. That's not something the user can set, but
|
|
* that is set by gitpod at workspace creation time.
|
|
*/
|
|
_featureFlags?: NamedWorkspaceFeatureFlag[];
|
|
}
|
|
|
|
export interface GithubAppConfig {
|
|
prebuilds?: GithubAppPrebuildConfig
|
|
}
|
|
export interface GithubAppPrebuildConfig {
|
|
master?: boolean
|
|
branches?: boolean
|
|
pullRequests?: boolean
|
|
pullRequestsFromForks?: boolean
|
|
addCheck?: boolean | 'prevent-merge-on-error'
|
|
addBadge?: boolean
|
|
addLabel?: boolean | string
|
|
addComment?: boolean
|
|
}
|
|
export namespace GithubAppPrebuildConfig {
|
|
export function is(obj: boolean | GithubAppPrebuildConfig): obj is GithubAppPrebuildConfig {
|
|
return !(typeof obj === 'boolean');
|
|
}
|
|
}
|
|
|
|
export type WorkspaceImageSource = WorkspaceImageSourceDocker | WorkspaceImageSourceReference;
|
|
export interface WorkspaceImageSourceDocker {
|
|
dockerFilePath: string
|
|
dockerFileHash: string
|
|
dockerFileSource?: Commit
|
|
}
|
|
export namespace WorkspaceImageSourceDocker {
|
|
export function is(obj: object): obj is WorkspaceImageSourceDocker {
|
|
return 'dockerFileHash' in obj
|
|
&& 'dockerFilePath' in obj;
|
|
}
|
|
}
|
|
export interface WorkspaceImageSourceReference {
|
|
/** The resolved, fix base image reference */
|
|
baseImageResolved: string;
|
|
}
|
|
export namespace WorkspaceImageSourceReference {
|
|
export function is(obj: object): obj is WorkspaceImageSourceReference {
|
|
return 'baseImageResolved' in obj;
|
|
}
|
|
}
|
|
|
|
export type PrebuiltWorkspaceState
|
|
// the prebuild is queued and may start at anytime
|
|
= "queued"
|
|
// the workspace prebuild is currently running (i.e. there's a workspace pod deployed)
|
|
| "building"
|
|
// the prebuild failed due to some issue with the system (e.g. missed a message, could not start workspace)
|
|
| "aborted"
|
|
// the prebuild timed out
|
|
| "timeout"
|
|
// the prebuild has finished and a snapshot is available
|
|
| "available";
|
|
|
|
export interface PrebuiltWorkspace {
|
|
id: string;
|
|
cloneURL: string;
|
|
branch?: string;
|
|
projectId?: string;
|
|
commit: string;
|
|
buildWorkspaceId: string;
|
|
creationTime: string;
|
|
state: PrebuiltWorkspaceState;
|
|
error?: string;
|
|
snapshot?: string;
|
|
}
|
|
|
|
export namespace PrebuiltWorkspace {
|
|
export function isDone(pws: PrebuiltWorkspace) {
|
|
return pws.state === "available" || pws.state === "timeout" || pws.state === 'aborted';
|
|
}
|
|
|
|
export function isAvailable(pws: PrebuiltWorkspace) {
|
|
return pws.state === "available" && !!pws.snapshot;
|
|
}
|
|
|
|
export function buildDidSucceed(pws: PrebuiltWorkspace) {
|
|
return pws.state === "available" && !pws.error;
|
|
}
|
|
}
|
|
|
|
export interface PrebuiltWorkspaceUpdatable {
|
|
id: string;
|
|
prebuiltWorkspaceId: string;
|
|
owner: string;
|
|
repo: string;
|
|
isResolved: boolean;
|
|
installationId: string;
|
|
issue?: string;
|
|
contextUrl?: string;
|
|
}
|
|
|
|
export interface WhitelistedRepository {
|
|
url: string
|
|
name: string
|
|
description?: string
|
|
avatar?: string
|
|
}
|
|
|
|
export type PortOnOpen = 'open-browser' | 'open-preview' | 'notify' | 'ignore';
|
|
|
|
export interface PortConfig {
|
|
port: number;
|
|
onOpen?: PortOnOpen;
|
|
visibility?: PortVisibility;
|
|
description?: string;
|
|
name?: string;
|
|
}
|
|
export namespace PortConfig {
|
|
export function is(config: any): config is PortConfig {
|
|
return config && ('port' in config) && (typeof config.port === 'number');
|
|
}
|
|
}
|
|
|
|
export interface PortRangeConfig {
|
|
port: string;
|
|
onOpen?: PortOnOpen;
|
|
}
|
|
export namespace PortRangeConfig {
|
|
export function is(config: any): config is PortRangeConfig {
|
|
return config && ('port' in config) && (typeof config.port === 'string' || config.port instanceof String);
|
|
}
|
|
}
|
|
|
|
export interface TaskConfig {
|
|
name?: string;
|
|
before?: string;
|
|
init?: string;
|
|
prebuild?: string;
|
|
command?: string;
|
|
env?: { [env: string]: any };
|
|
openIn?: 'bottom' | 'main' | 'left' | 'right';
|
|
openMode?: 'split-top' | 'split-left' | 'split-right' | 'split-bottom' | 'tab-before' | 'tab-after';
|
|
}
|
|
|
|
export namespace TaskConfig {
|
|
export function is(config: any): config is TaskConfig {
|
|
return config
|
|
&& ('command' in config || 'init' in config || 'before' in config);
|
|
}
|
|
}
|
|
|
|
export namespace WorkspaceImageBuild {
|
|
export type Phase = 'BaseImage' | 'GitpodLayer' | 'Error' | 'Done';
|
|
export interface StateInfo {
|
|
phase: Phase
|
|
currentStep?: number
|
|
maxSteps?: number
|
|
}
|
|
export interface LogContent {
|
|
text: string
|
|
upToLine?: number
|
|
isDiff?: boolean
|
|
}
|
|
export type LogCallback = (info: StateInfo, content: LogContent | undefined) => void;
|
|
export namespace LogLine {
|
|
export const DELIMITER = '\r\n';
|
|
export const DELIMITER_REGEX = /\r?\n/;
|
|
}
|
|
}
|
|
|
|
export type ImageConfig = ImageConfigString | ImageConfigFile;
|
|
export type ImageConfigString = string;
|
|
export namespace ImageConfigString {
|
|
export function is(config: ImageConfig | undefined): config is ImageConfigString {
|
|
return typeof config === 'string';
|
|
}
|
|
|
|
}
|
|
export interface ImageConfigFile {
|
|
// Path to the Dockerfile relative to repository root
|
|
file: string,
|
|
// Path to the docker build context relative to repository root
|
|
context?: string
|
|
}
|
|
export namespace ImageConfigFile {
|
|
export function is(config: ImageConfig | undefined): config is ImageConfigFile {
|
|
return typeof config === 'object'
|
|
&& 'file' in config;
|
|
}
|
|
}
|
|
export interface ExternalImageConfigFile extends ImageConfigFile {
|
|
externalSource: Commit;
|
|
}
|
|
export namespace ExternalImageConfigFile {
|
|
export function is(config: any | undefined): config is ExternalImageConfigFile {
|
|
return typeof config === 'object'
|
|
&& 'file' in config
|
|
&& 'externalSource' in config;
|
|
}
|
|
}
|
|
|
|
export interface WorkspaceContext {
|
|
title: string;
|
|
/** 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. */
|
|
normalizedContextURL?: string;
|
|
forceCreateNewWorkspace?: boolean;
|
|
forceImageBuild?: boolean;
|
|
}
|
|
|
|
export namespace WorkspaceContext {
|
|
export function is(context: any): context is WorkspaceContext {
|
|
return context
|
|
&& 'title' in context;
|
|
}
|
|
}
|
|
|
|
export interface WithSnapshot {
|
|
snapshotBucketId: string;
|
|
}
|
|
export namespace WithSnapshot {
|
|
export function is(context: any): context is WithSnapshot {
|
|
return context
|
|
&& 'snapshotBucketId' in context;
|
|
}
|
|
}
|
|
|
|
export interface WithPrebuild {
|
|
snapshotBucketId: string;
|
|
prebuildWorkspaceId: string;
|
|
wasPrebuilt: true;
|
|
}
|
|
export namespace WithPrebuild {
|
|
export function is(context: any): context is WithPrebuild {
|
|
return context
|
|
&& 'snapshotBucketId' in context
|
|
&& 'prebuildWorkspaceId' in context
|
|
&& 'wasPrebuilt' in context;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* WithDefaultConfig contexts disable the download of the gitpod.yml from the repository
|
|
* and fall back to the built-in configuration.
|
|
*/
|
|
export interface WithDefaultConfig {
|
|
withDefaultConfig: true;
|
|
}
|
|
|
|
export namespace WithDefaultConfig {
|
|
export function is(context: any): context is WithDefaultConfig {
|
|
return context
|
|
&& 'withDefaultConfig' in context
|
|
&& context.withDefaultConfig;
|
|
}
|
|
|
|
export function mark(ctx: WorkspaceContext): WorkspaceContext & WithDefaultConfig {
|
|
return {
|
|
...ctx,
|
|
withDefaultConfig: true
|
|
}
|
|
}
|
|
}
|
|
|
|
export interface SnapshotContext extends WorkspaceContext, WithSnapshot {
|
|
snapshotId: string;
|
|
}
|
|
|
|
export namespace SnapshotContext {
|
|
export function is(context: any): context is SnapshotContext {
|
|
return context
|
|
&& WithSnapshot.is(context)
|
|
&& 'snapshotId' in context;
|
|
}
|
|
}
|
|
|
|
export interface StartPrebuildContext extends WorkspaceContext {
|
|
actual: WorkspaceContext;
|
|
commitHistory?: string[];
|
|
project?: Project;
|
|
branch?: string;
|
|
}
|
|
|
|
export namespace StartPrebuildContext {
|
|
export function is(context: any): context is StartPrebuildContext {
|
|
return context
|
|
&& 'actual' in context;
|
|
}
|
|
}
|
|
|
|
export interface PrebuiltWorkspaceContext extends WorkspaceContext {
|
|
originalContext: WorkspaceContext;
|
|
prebuiltWorkspace: PrebuiltWorkspace;
|
|
snapshotBucketId?: string;
|
|
}
|
|
|
|
export namespace PrebuiltWorkspaceContext {
|
|
export function is(context: any): context is PrebuiltWorkspaceContext {
|
|
return context
|
|
&& 'originalContext' in context
|
|
&& 'prebuiltWorkspace' in context;
|
|
}
|
|
}
|
|
|
|
export interface WithReferrerContext extends WorkspaceContext {
|
|
referrer: string
|
|
referrerIde?: string
|
|
}
|
|
|
|
export namespace WithReferrerContext {
|
|
export function is(context: any): context is WithReferrerContext {
|
|
return context
|
|
&& 'referrer' in context;
|
|
}
|
|
}
|
|
|
|
export interface WithEnvvarsContext extends WorkspaceContext {
|
|
envvars: EnvVarWithValue[];
|
|
}
|
|
|
|
export namespace WithEnvvarsContext {
|
|
export function is(context: any): context is WithEnvvarsContext {
|
|
return context
|
|
&& 'envvars' in context
|
|
}
|
|
}
|
|
|
|
export interface WorkspaceProbeContext extends WorkspaceContext {
|
|
responseURL: string
|
|
responseToken: string
|
|
}
|
|
|
|
export namespace WorkspaceProbeContext {
|
|
export function is(context: any): context is WorkspaceProbeContext {
|
|
return context
|
|
&& 'responseURL' in context
|
|
&& 'responseToken' in context;
|
|
}
|
|
}
|
|
|
|
export type RefType = "branch" | "tag" | "revision";
|
|
export namespace RefType {
|
|
export const getRefType = (commit: Commit): RefType => {
|
|
if (!commit.ref) {
|
|
return "revision";
|
|
}
|
|
// This fallback is meant to handle the cases where (for historic reasons) ref is present but refType is missing
|
|
return commit.refType || "branch";
|
|
}
|
|
}
|
|
|
|
export interface Commit {
|
|
repository: Repository
|
|
revision: string
|
|
|
|
// Might contain either a branch or a tag (determined by refType)
|
|
ref?: string
|
|
|
|
// refType is only set if ref is present (and not for old workspaces, before this feature was added)
|
|
refType?: RefType
|
|
}
|
|
|
|
export interface AdditionalContentContext extends WorkspaceContext {
|
|
|
|
/**
|
|
* utf-8 encoded contents that will be copied on top of the workspace's filesystem
|
|
*/
|
|
additionalFiles: {[filePath: string]: string};
|
|
|
|
}
|
|
|
|
export namespace AdditionalContentContext {
|
|
export function is(ctx: any): ctx is AdditionalContentContext {
|
|
return 'additionalFiles' in ctx;
|
|
}
|
|
|
|
export function hasDockerConfig(ctx: any, config: WorkspaceConfig): boolean {
|
|
return is(ctx) && ImageConfigFile.is(config.image) && !!ctx.additionalFiles[config.image.file];
|
|
}
|
|
}
|
|
|
|
export interface CommitContext extends WorkspaceContext, Commit {
|
|
/** @deprecated Moved to .repository.cloneUrl, left here for backwards-compatibility for old workspace contextes in the DB */
|
|
cloneUrl?: string
|
|
}
|
|
|
|
export namespace CommitContext {
|
|
export function is(commit: any): commit is CommitContext {
|
|
return WorkspaceContext.is(commit)
|
|
&& 'repository' in commit
|
|
&& 'revision' in commit
|
|
}
|
|
}
|
|
|
|
export interface PullRequestContext extends CommitContext {
|
|
nr: number;
|
|
ref: string;
|
|
base: {
|
|
repository: Repository
|
|
ref: string
|
|
}
|
|
}
|
|
|
|
export namespace PullRequestContext {
|
|
export function is(ctx: any): ctx is PullRequestContext {
|
|
return CommitContext.is(ctx)
|
|
&& 'nr' in ctx
|
|
&& 'ref' in ctx
|
|
&& 'base' in ctx
|
|
}
|
|
}
|
|
|
|
export interface IssueContext extends CommitContext {
|
|
nr: number;
|
|
ref: string;
|
|
localBranch: string;
|
|
}
|
|
|
|
export namespace IssueContext {
|
|
export function is(ctx: any): ctx is IssueContext {
|
|
return CommitContext.is(ctx)
|
|
&& 'nr' in ctx
|
|
&& 'ref' in ctx
|
|
&& 'localBranch' in ctx
|
|
}
|
|
}
|
|
|
|
export interface NavigatorContext extends CommitContext {
|
|
path: string;
|
|
isFile: boolean;
|
|
}
|
|
|
|
export namespace NavigatorContext {
|
|
export function is(ctx: any): ctx is NavigatorContext {
|
|
return CommitContext.is(ctx)
|
|
&& 'path' in ctx
|
|
&& 'isFile' in ctx
|
|
}
|
|
}
|
|
|
|
export interface Repository {
|
|
host: string;
|
|
owner: string;
|
|
name: string;
|
|
cloneUrl: string;
|
|
description?: string;
|
|
avatarUrl?: string;
|
|
webUrl?: string;
|
|
defaultBranch?: string;
|
|
/** Optional for backwards compatibility */
|
|
private?: boolean;
|
|
fork?: {
|
|
// The direct parent of this fork
|
|
parent: Repository
|
|
}
|
|
}
|
|
export interface Branch {
|
|
name: string;
|
|
commit: CommitInfo;
|
|
htmlUrl: string;
|
|
}
|
|
|
|
export interface CommitInfo {
|
|
author: string;
|
|
sha: string;
|
|
commitMessage: string;
|
|
authorAvatarUrl?: string;
|
|
authorDate?: string;
|
|
}
|
|
|
|
export namespace Repository {
|
|
export function fullRepoName(repo: Repository): string {
|
|
return `${repo.host}/${repo.owner}/${repo.name}`;
|
|
}
|
|
}
|
|
|
|
export interface WorkspaceInstancePortsChangedEvent {
|
|
type: "PortsChanged";
|
|
instanceID: string;
|
|
portsOpened: number[]
|
|
portsClosed: number[]
|
|
}
|
|
|
|
export namespace WorkspaceInstancePortsChangedEvent {
|
|
|
|
export function is(data: any): data is WorkspaceInstancePortsChangedEvent {
|
|
return data && data.type == "PortsChanged";
|
|
}
|
|
|
|
}
|
|
|
|
export interface WorkspaceInfo {
|
|
workspace: Workspace
|
|
latestInstance?: WorkspaceInstance
|
|
}
|
|
|
|
export namespace WorkspaceInfo {
|
|
export function lastActiveISODate(info: WorkspaceInfo): string {
|
|
return info.latestInstance?.creationTime || info.workspace.creationTime;
|
|
}
|
|
}
|
|
|
|
export type RunningWorkspaceInfo = WorkspaceInfo & { latestInstance: WorkspaceInstance };
|
|
|
|
export interface WorkspaceCreationResult {
|
|
createdWorkspaceId?: string;
|
|
workspaceURL?: string;
|
|
existingWorkspaces?: WorkspaceInfo[];
|
|
runningWorkspacePrebuild?: {
|
|
prebuildID: string
|
|
workspaceID: string
|
|
instanceID: string
|
|
starting: RunningWorkspacePrebuildStarting
|
|
sameCluster: boolean
|
|
}
|
|
runningPrebuildWorkspaceID?: string;
|
|
}
|
|
export type RunningWorkspacePrebuildStarting = 'queued' | 'starting' | 'running';
|
|
|
|
export enum CreateWorkspaceMode {
|
|
// Default returns a running prebuild if there is any, otherwise creates a new workspace (using a prebuild if one is available)
|
|
Default = 'default',
|
|
// ForceNew creates a new workspace irrespective of any running prebuilds. This mode is guaranteed to actually create a workspace - but may degrade user experience as currently runnig prebuilds are ignored.
|
|
ForceNew = 'force-new',
|
|
// UsePrebuild polls the database waiting for a currently running prebuild to become available. This mode exists to handle the db-sync delay.
|
|
UsePrebuild = 'use-prebuild',
|
|
// SelectIfRunning returns a list of currently running workspaces for the context URL if there are any, otherwise falls back to Default mode
|
|
SelectIfRunning = 'select-if-running',
|
|
}
|
|
|
|
export namespace WorkspaceCreationResult {
|
|
export function is(data: any): data is WorkspaceCreationResult {
|
|
return data && (
|
|
'createdWorkspaceId' in data
|
|
|| 'existingWorkspaces' in data
|
|
|| 'runningWorkspacePrebuild' in data
|
|
|| 'runningPrebuildWorkspaceID' in data
|
|
)
|
|
}
|
|
}
|
|
|
|
export interface UserMessage {
|
|
readonly id: string;
|
|
readonly title?: string;
|
|
/**
|
|
* date from where on this message should be shown
|
|
*/
|
|
readonly from?: string;
|
|
readonly content?: string;
|
|
readonly url?: string;
|
|
}
|
|
|
|
export interface AuthProviderInfo {
|
|
readonly authProviderId: string;
|
|
readonly authProviderType: string;
|
|
readonly host: string;
|
|
readonly ownerId?: string;
|
|
readonly verified: boolean;
|
|
readonly isReadonly?: boolean;
|
|
readonly hiddenOnDashboard?: boolean;
|
|
readonly loginContextMatcher?: string;
|
|
readonly disallowLogin?: boolean;
|
|
readonly icon?: string;
|
|
readonly description?: string;
|
|
|
|
readonly settingsUrl?: string;
|
|
readonly scopes?: string[];
|
|
readonly requirements?: {
|
|
readonly default: string[];
|
|
readonly publicRepo: string[];
|
|
readonly privateRepo: string[];
|
|
}
|
|
}
|
|
|
|
export interface AuthProviderEntry {
|
|
readonly id: string;
|
|
readonly type: AuthProviderEntry.Type;
|
|
readonly host: string;
|
|
readonly ownerId: string;
|
|
|
|
readonly status: AuthProviderEntry.Status;
|
|
|
|
readonly oauth: OAuth2Config;
|
|
}
|
|
|
|
export interface OAuth2Config {
|
|
readonly clientId: string;
|
|
readonly clientSecret: string;
|
|
readonly callBackUrl: string;
|
|
readonly authorizationUrl: string;
|
|
readonly tokenUrl: string;
|
|
readonly scope?: string;
|
|
readonly scopeSeparator?: string;
|
|
|
|
readonly settingsUrl?: string;
|
|
readonly authorizationParams?: { [key: string]: string }
|
|
readonly configURL?: string;
|
|
}
|
|
|
|
export namespace AuthProviderEntry {
|
|
export type Type = "GitHub" | "GitLab" | string;
|
|
export type Status = "pending" | "verified";
|
|
export type NewEntry = Pick<AuthProviderEntry, "ownerId" | "host" | "type"> & { clientId?: string, clientSecret?: string };
|
|
export type UpdateEntry = Pick<AuthProviderEntry, "id" | "ownerId"> & Pick<OAuth2Config, "clientId" | "clientSecret">;
|
|
export function redact(entry: AuthProviderEntry): AuthProviderEntry {
|
|
return {
|
|
...entry,
|
|
oauth: {
|
|
...entry.oauth,
|
|
clientSecret: "redacted"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export interface Configuration {
|
|
readonly daysBeforeGarbageCollection: number;
|
|
readonly garbageCollectionStartDate: number;
|
|
}
|
|
|
|
export interface TheiaPlugin {
|
|
id: string;
|
|
pluginName: string;
|
|
pluginId?: string;
|
|
/**
|
|
* Id of the user which uploaded this plugin.
|
|
*/
|
|
userId?: string;
|
|
bucketName: string;
|
|
path: string;
|
|
hash?: string;
|
|
state: TheiaPlugin.State;
|
|
}
|
|
export namespace TheiaPlugin {
|
|
export enum State {
|
|
Uploading = "uploading",
|
|
Uploaded = "uploaded",
|
|
CheckinFailed = "checkin-failed",
|
|
}
|
|
}
|
|
|
|
export interface TermsAcceptanceEntry {
|
|
readonly userId: string;
|
|
readonly termsRevision: string;
|
|
readonly acceptionTime: string;
|
|
}
|
|
|
|
export interface Terms {
|
|
readonly revision: string;
|
|
readonly activeSince: string;
|
|
readonly adminOnlyTerms: boolean;
|
|
readonly updateMessage: string;
|
|
readonly content: string;
|
|
readonly formElements?: object;
|
|
}
|