From 1ab4a7014d4910326d6bd696ec7c0707916ac722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Mon, 8 Jul 2024 14:08:24 +0000 Subject: [PATCH 01/16] Start prebuilds on started workspaces --- .../server/src/projects/projects-service.ts | 23 --------------- .../server/src/workspace/workspace-starter.ts | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index 12fb3659c9f81a..026fc365f881a7 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -30,7 +30,6 @@ import { Authorizer, SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/author import { TransactionalContext } from "@gitpod/gitpod-db/lib/typeorm/transactional-db-impl"; import { daysBefore, isDateSmaller } from "@gitpod/gitpod-protocol/lib/util/timeutil"; import deepmerge from "deepmerge"; -import { ScmService } from "../scm/scm-service"; import { runWithSubjectId } from "../util/request-context"; import { InstallationService } from "../auth/installation-service"; import { IDEService } from "../ide-service"; @@ -50,7 +49,6 @@ export class ProjectsService { @inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider, @inject(IAnalyticsWriter) private readonly analytics: IAnalyticsWriter, @inject(Authorizer) private readonly auth: Authorizer, - @inject(ScmService) private readonly scmService: ScmService, @inject(IDEService) private readonly ideService: IDEService, @inject(LazyPrebuildManager) private readonly prebuildManager: LazyPrebuildManager, @@ -422,7 +420,6 @@ export class ProjectsService { partialProject.settings = deepmerge(toBeMerged, partialProject.settings); await this.checkProjectSettings(user.id, partialProject.settings); } - await this.handleEnablePrebuild(user, partialProject); return this.projectDB.updateProject(partialProject); } private async checkProjectSettings(userId: string, settings?: PartialProject["settings"]) { @@ -451,26 +448,6 @@ export class ProjectsService { } } - private async handleEnablePrebuild(user: User, partialProject: PartialProject): Promise { - const enablePrebuildsNew = partialProject?.settings?.prebuilds?.enable; - if (typeof enablePrebuildsNew === "boolean") { - const project = await this.projectDB.findProjectById(partialProject.id); - if (!project) { - return; - } - const enablePrebuildsPrev = !!project.settings?.prebuilds?.enable; - const installWebhook = enablePrebuildsNew && !enablePrebuildsPrev; - const uninstallWebhook = !enablePrebuildsNew && enablePrebuildsPrev; - if (installWebhook) { - await this.scmService.installWebhookForPrebuilds(project, user); - } - if (uninstallWebhook) { - // TODO - // await this.scmService.uninstallWebhookForPrebuilds(project, user); - } - } - } - async isProjectConsideredInactive(userId: string, projectId: string): Promise { const isOlderThan7Days = (d1: string) => isDateSmaller(d1, daysBefore(new Date().toISOString(), 7)); diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index 77b5cf88b8cd71..c58fe5b1e0c919 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -135,6 +135,8 @@ import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/expe import { ctxIsAborted, runWithRequestContext, runWithSubjectId } from "../util/request-context"; import { SubjectId } from "../auth/subject-id"; import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { PrebuildManager } from "../prebuilds/prebuild-manager"; +import { ContextParser } from "./context-parser-service"; export interface StartWorkspaceOptions extends Omit { excludeFeatureFlags?: NamedWorkspaceFeatureFlag[]; @@ -229,6 +231,8 @@ export class WorkspaceStarter { @inject(RedisMutex) private readonly redisMutex: RedisMutex, @inject(RedisPublisher) private readonly publisher: RedisPublisher, @inject(EnvVarService) private readonly envVarService: EnvVarService, + @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, + @inject(ContextParser) private readonly contextParser: ContextParser, ) {} public async startWorkspace( @@ -628,6 +632,31 @@ export class WorkspaceStarter { }, timestamp: new Date(instance.creationTime), }); + + if (workspace.projectId) { + const project = await this.projectDB.findProjectById(workspace.projectId); + if (!project) { + return; + } + + const context = (await this.contextParser.handle( + { span }, + user, + workspace.contextURL, + )) as CommitContext; + + log.info({ instanceId: instance.id, workspaceId: workspace.id }, "starting prebuild after workspace", { + projectId: project.id, + projectName: project.name, + contextURL: workspace.contextURL, + context, + }); + const prebuild = await this.prebuildManager.startPrebuild( + { span }, + { user, project, forcePrebuild: false, context }, + ); + log.info("Prebuild ID", { prebuildId: prebuild.prebuildId }); + } } catch (err) { if (isGrpcError(err) && err.code === grpc.status.ALREADY_EXISTS) { // This might happen because of timing: When we did the "workspaceAlreadyExists" check above, the DB state was not updated yet. From 345005d37d80fd5bf8a0158166084db5ed711331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Mon, 8 Jul 2024 15:17:04 +0000 Subject: [PATCH 02/16] Fix circl dep --- components/server/src/workspace/workspace-starter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index c58fe5b1e0c919..af0bfb0ae132ff 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -135,8 +135,8 @@ import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/expe import { ctxIsAborted, runWithRequestContext, runWithSubjectId } from "../util/request-context"; import { SubjectId } from "../auth/subject-id"; import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { PrebuildManager } from "../prebuilds/prebuild-manager"; import { ContextParser } from "./context-parser-service"; +import { LazyPrebuildManager } from "../projects/projects-service"; export interface StartWorkspaceOptions extends Omit { excludeFeatureFlags?: NamedWorkspaceFeatureFlag[]; @@ -231,7 +231,7 @@ export class WorkspaceStarter { @inject(RedisMutex) private readonly redisMutex: RedisMutex, @inject(RedisPublisher) private readonly publisher: RedisPublisher, @inject(EnvVarService) private readonly envVarService: EnvVarService, - @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, + @inject(LazyPrebuildManager) private readonly prebuildManager: LazyPrebuildManager, @inject(ContextParser) private readonly contextParser: ContextParser, ) {} @@ -651,7 +651,7 @@ export class WorkspaceStarter { contextURL: workspace.contextURL, context, }); - const prebuild = await this.prebuildManager.startPrebuild( + const prebuild = await this.prebuildManager().startPrebuild( { span }, { user, project, forcePrebuild: false, context }, ); From 2c8ba20e69d17fa08c5f1415e8560f5d821de79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Mon, 8 Jul 2024 15:39:14 +0000 Subject: [PATCH 03/16] move out to workspace creation --- .../src/workspace/gitpod-server-impl.ts | 25 ++++++++++++++++ .../server/src/workspace/workspace-starter.ts | 29 ------------------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 80733cb60c728c..b3f7cf9ff6825b 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -94,6 +94,7 @@ import { } from "@gitpod/gitpod-protocol/lib/teams-projects-protocol"; import { ClientMetadata, traceClientMetadata } from "../websocket/websocket-connection-manager"; import { + CommitContext, EmailDomainFilterEntry, EnvVarWithValue, LinkedInProfile, @@ -887,6 +888,30 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { const startWorkspaceResult = await this.workspaceService.startWorkspace(ctx, user, workspace.id, opts); ctx.span?.log({ event: "startWorkspaceComplete", ...startWorkspaceResult }); + if (workspace.projectId) { + const project = await this.projectsService.getProject(user.id, workspace.projectId); + if (project) { + const context = (await this.contextParser.handle(ctx, user, workspace.contextURL)) as CommitContext; + log.info({ workspaceId: workspace.id }, "starting prebuild after workspace", { + projectId: project.id, + projectName: project.name, + contextURL: workspace.contextURL, + context, + }); + this.prebuildManager + .startPrebuild(ctx, { user, project, forcePrebuild: false, context }) + .then((prebuild) => { + log.info("Prebuild ID", { prebuildId: prebuild.prebuildId }); + }) + .catch((err) => { + log.error("Failed to start prebuild after workspace creation", { + error: err, + workspaceId: workspace.id, + }); + }); + } + } + return { workspaceURL: startWorkspaceResult.workspaceURL, createdWorkspaceId: workspace.id, diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index af0bfb0ae132ff..77b5cf88b8cd71 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -135,8 +135,6 @@ import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/expe import { ctxIsAborted, runWithRequestContext, runWithSubjectId } from "../util/request-context"; import { SubjectId } from "../auth/subject-id"; import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { ContextParser } from "./context-parser-service"; -import { LazyPrebuildManager } from "../projects/projects-service"; export interface StartWorkspaceOptions extends Omit { excludeFeatureFlags?: NamedWorkspaceFeatureFlag[]; @@ -231,8 +229,6 @@ export class WorkspaceStarter { @inject(RedisMutex) private readonly redisMutex: RedisMutex, @inject(RedisPublisher) private readonly publisher: RedisPublisher, @inject(EnvVarService) private readonly envVarService: EnvVarService, - @inject(LazyPrebuildManager) private readonly prebuildManager: LazyPrebuildManager, - @inject(ContextParser) private readonly contextParser: ContextParser, ) {} public async startWorkspace( @@ -632,31 +628,6 @@ export class WorkspaceStarter { }, timestamp: new Date(instance.creationTime), }); - - if (workspace.projectId) { - const project = await this.projectDB.findProjectById(workspace.projectId); - if (!project) { - return; - } - - const context = (await this.contextParser.handle( - { span }, - user, - workspace.contextURL, - )) as CommitContext; - - log.info({ instanceId: instance.id, workspaceId: workspace.id }, "starting prebuild after workspace", { - projectId: project.id, - projectName: project.name, - contextURL: workspace.contextURL, - context, - }); - const prebuild = await this.prebuildManager().startPrebuild( - { span }, - { user, project, forcePrebuild: false, context }, - ); - log.info("Prebuild ID", { prebuildId: prebuild.prebuildId }); - } } catch (err) { if (isGrpcError(err) && err.code === grpc.status.ALREADY_EXISTS) { // This might happen because of timing: When we did the "workspaceAlreadyExists" check above, the DB state was not updated yet. From 71386af8a00e831a8b4fa237af9980fc3c29059f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Mon, 8 Jul 2024 18:54:04 +0000 Subject: [PATCH 04/16] Do it in the workspace service --- .../src/workspace/gitpod-server-impl.ts | 25 ----------- .../server/src/workspace/workspace-service.ts | 43 ++++++++++++++++++- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index b3f7cf9ff6825b..80733cb60c728c 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -94,7 +94,6 @@ import { } from "@gitpod/gitpod-protocol/lib/teams-projects-protocol"; import { ClientMetadata, traceClientMetadata } from "../websocket/websocket-connection-manager"; import { - CommitContext, EmailDomainFilterEntry, EnvVarWithValue, LinkedInProfile, @@ -888,30 +887,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { const startWorkspaceResult = await this.workspaceService.startWorkspace(ctx, user, workspace.id, opts); ctx.span?.log({ event: "startWorkspaceComplete", ...startWorkspaceResult }); - if (workspace.projectId) { - const project = await this.projectsService.getProject(user.id, workspace.projectId); - if (project) { - const context = (await this.contextParser.handle(ctx, user, workspace.contextURL)) as CommitContext; - log.info({ workspaceId: workspace.id }, "starting prebuild after workspace", { - projectId: project.id, - projectName: project.name, - contextURL: workspace.contextURL, - context, - }); - this.prebuildManager - .startPrebuild(ctx, { user, project, forcePrebuild: false, context }) - .then((prebuild) => { - log.info("Prebuild ID", { prebuildId: prebuild.prebuildId }); - }) - .catch((err) => { - log.error("Failed to start prebuild after workspace creation", { - error: err, - workspaceId: workspace.id, - }); - }); - } - } - return { workspaceURL: startWorkspaceResult.workspaceURL, createdWorkspaceId: workspace.id, diff --git a/components/server/src/workspace/workspace-service.ts b/components/server/src/workspace/workspace-service.ts index ae70a08330dae5..7d6c1a13bc66a2 100644 --- a/components/server/src/workspace/workspace-service.ts +++ b/components/server/src/workspace/workspace-service.ts @@ -8,6 +8,7 @@ import { inject, injectable } from "inversify"; import * as grpc from "@grpc/grpc-js"; import { RedisPublisher, WorkspaceDB } from "@gitpod/gitpod-db/lib"; import { + CommitContext, GetWorkspaceTimeoutResult, GitpodClient, GitpodServer, @@ -61,7 +62,7 @@ import { EntitlementService, MayStartWorkspaceResult } from "../billing/entitlem import * as crypto from "crypto"; import { WorkspaceRegion, isWorkspaceRegion } from "@gitpod/gitpod-protocol/lib/workspace-cluster"; import { RegionService } from "./region-service"; -import { ProjectsService } from "../projects/projects-service"; +import { LazyPrebuildManager, ProjectsService } from "../projects/projects-service"; import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-provider"; import { SupportedWorkspaceClass } from "@gitpod/gitpod-protocol/lib/workspace-class"; import { Config } from "../config"; @@ -75,6 +76,7 @@ import { SnapshotService } from "./snapshot-service"; import { InstallationService } from "../auth/installation-service"; import { PublicAPIConverter } from "@gitpod/public-api-common/lib/public-api-converter"; import { WatchWorkspaceStatusResponse } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb"; +import { ContextParser } from "./context-parser-service"; export const GIT_STATUS_LENGTH_CAP_BYTES = 4096; @@ -103,6 +105,8 @@ export class WorkspaceService { @inject(RedisPublisher) private readonly publisher: RedisPublisher, @inject(HeadlessLogService) private readonly headlessLogService: HeadlessLogService, @inject(Authorizer) private readonly auth: Authorizer, + @inject(ContextParser) private readonly contextParser: ContextParser, + @inject(LazyPrebuildManager) private readonly prebuildManager: LazyPrebuildManager, @inject(RedisSubscriber) private readonly subscriber: RedisSubscriber, @inject(PublicAPIConverter) private readonly apiConverter: PublicAPIConverter, @@ -193,6 +197,9 @@ export class WorkspaceService { } this.asyncUpdateDeletionEligabilityTime(user.id, workspace.id); this.asyncUpdateDeletionEligabilityTimeForUsedPrebuild(user.id, workspace); + if (project) { + this.asyncStartPrebuild({ ctx, project, workspace, user }); + } return workspace; } @@ -383,6 +390,40 @@ export class WorkspaceService { ); } + private asyncStartPrebuild({ + ctx, + project, + workspace, + user, + }: { + ctx: TraceContext; + project: Project; + workspace: Workspace; + user: User; + }): void { + (async () => { + const context = (await this.contextParser.handle(ctx, user, workspace.contextURL)) as CommitContext; + log.info({ workspaceId: workspace.id }, "starting prebuild after workspace creation", { + projectId: project.id, + projectName: project.name, + contextURL: workspace.contextURL, + context, + }); + await this.prebuildManager().startPrebuild(ctx, { + user, + project, + forcePrebuild: false, + context, + }); + })().catch((err) => + log.error( + { userId: user.id, workspaceId: workspace.id }, + "Failed to start prebuild after workspace creation", + err, + ), + ); + } + private asyncUpdateDeletionEligabilityTime(userId: string, workspaceId: string): void { this.updateDeletionEligabilityTime(userId, workspaceId).catch((err) => log.error({ userId, workspaceId }, "Failed to update deletion eligibility time", err), From a6af92047ebababecfafc5c83e40b16ae67f8979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Jul 2024 10:09:59 +0000 Subject: [PATCH 05/16] remove webhook tests --- .../src/projects/projects-service.spec.db.ts | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/components/server/src/projects/projects-service.spec.db.ts b/components/server/src/projects/projects-service.spec.db.ts index 4d5a4df98062a3..961ecbb3692d68 100644 --- a/components/server/src/projects/projects-service.spec.db.ts +++ b/components/server/src/projects/projects-service.spec.db.ts @@ -179,41 +179,6 @@ describe("ProjectsService", async () => { ); }); - describe("enablePrebuild handling", async () => { - it("should install webhook on new projects", async () => { - const webhooks = container.get>("webhooks"); - webhooks.clear(); - const ps = container.get(ProjectsService); - const project = await createTestProject(ps, org, owner); // using new default settings - await ps.updateProject(owner, { - id: project.id, - settings: { - prebuilds: { enable: true }, - }, - }); - expect(webhooks).to.contain(project.cloneUrl); - }); - - it("should install webhook on pre-existing projects", async () => { - const webhooks = container.get>("webhooks"); - webhooks.clear(); - const cloneUrl = "https://github.com/gitpod-io/gitpod.git"; - const ps = container.get(ProjectsService); - const project = await createTestProject(ps, org, owner, { - name: "test-pro", - cloneUrl, - settings: {}, - }); - await ps.updateProject(owner, { - id: project.id, - settings: { - prebuilds: { enable: true }, - }, - }); - expect(webhooks).to.contain(project.cloneUrl); - }); - }); - it("should findProjects", async () => { const ps = container.get(ProjectsService); const project = await createTestProject(ps, org, owner); From a5976fdb99c878dd618439c2ac3250db9083ec8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Jul 2024 10:44:11 +0000 Subject: [PATCH 06/16] remove webhook handling code --- .../src/auth/host-context-provider-impl.ts | 2 - .../bitbucket-server-container-module.ts | 3 - .../bitbucket/bitbucket-container-module.ts | 3 - .../src/github/github-container-module.ts | 3 - .../src/gitlab/gitlab-container-module.ts | 3 - .../server/src/prebuilds/bitbucket-app.ts | 268 +--------- .../bitbucket-server-service.spec.ts | 125 ----- .../src/prebuilds/bitbucket-server-service.ts | 75 --- .../server/src/prebuilds/bitbucket-service.ts | 78 --- components/server/src/prebuilds/github-app.ts | 489 +----------------- .../src/prebuilds/github-enterprise-app.ts | 280 +--------- .../server/src/prebuilds/github-service.ts | 86 --- components/server/src/prebuilds/gitlab-app.ts | 235 +-------- .../server/src/prebuilds/gitlab-service.ts | 107 ---- .../server/src/repohost/repo-service.ts | 15 - .../server/src/repohost/repository-host.ts | 2 - components/server/src/scm/scm-service.ts | 26 +- .../test/service-testing-container-module.ts | 5 - .../server/src/user/token-service.spec.db.ts | 6 - .../src/workspace/context-service.spec.db.ts | 4 - 20 files changed, 19 insertions(+), 1796 deletions(-) delete mode 100644 components/server/src/prebuilds/bitbucket-server-service.spec.ts delete mode 100644 components/server/src/prebuilds/bitbucket-server-service.ts delete mode 100644 components/server/src/prebuilds/bitbucket-service.ts delete mode 100644 components/server/src/prebuilds/github-service.ts delete mode 100644 components/server/src/prebuilds/gitlab-service.ts delete mode 100644 components/server/src/repohost/repo-service.ts diff --git a/components/server/src/auth/host-context-provider-impl.ts b/components/server/src/auth/host-context-provider-impl.ts index e29caa1dfac447..e16dd2685bb5f3 100644 --- a/components/server/src/auth/host-context-provider-impl.ts +++ b/components/server/src/auth/host-context-provider-impl.ts @@ -12,7 +12,6 @@ import { AuthProviderService } from "./auth-provider-service"; import { HostContextProvider, HostContextProviderFactory } from "./host-context-provider"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { HostContainerMapping } from "./host-container-mapping"; -import { RepositoryService } from "../repohost/repo-service"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat"; @@ -153,7 +152,6 @@ export class HostContextProviderImpl implements HostContextProvider { const container = parentContainer.createChild(); container.bind(AuthProviderParams).toConstantValue(authProviderConfig); container.bind(HostContext).toSelf().inSingletonScope(); - container.bind(RepositoryService).toSelf().inSingletonScope(); const hostContainerMapping = parentContainer.get(HostContainerMapping); const containerModules = hostContainerMapping.get(authProviderConfig.type); diff --git a/components/server/src/bitbucket-server/bitbucket-server-container-module.ts b/components/server/src/bitbucket-server/bitbucket-server-container-module.ts index 725299eba3d176..572c537750e71d 100644 --- a/components/server/src/bitbucket-server/bitbucket-server-container-module.ts +++ b/components/server/src/bitbucket-server/bitbucket-server-container-module.ts @@ -16,8 +16,6 @@ import { BitbucketServerFileProvider } from "./bitbucket-server-file-provider"; import { BitbucketServerRepositoryProvider } from "./bitbucket-server-repository-provider"; import { BitbucketServerTokenHelper } from "./bitbucket-server-token-handler"; import { BitbucketServerTokenValidator } from "./bitbucket-server-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { BitbucketServerService } from "../prebuilds/bitbucket-server-service"; export const bitbucketServerContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -33,5 +31,4 @@ export const bitbucketServerContainerModule = new ContainerModule((bind, _unbind bind(BitbucketServerTokenHelper).toSelf().inSingletonScope(); bind(BitbucketServerTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(BitbucketServerTokenValidator); - rebind(RepositoryService).to(BitbucketServerService).inSingletonScope(); }); diff --git a/components/server/src/bitbucket/bitbucket-container-module.ts b/components/server/src/bitbucket/bitbucket-container-module.ts index 9b4413baa387a7..406b1f61404e5b 100644 --- a/components/server/src/bitbucket/bitbucket-container-module.ts +++ b/components/server/src/bitbucket/bitbucket-container-module.ts @@ -16,8 +16,6 @@ import { BitbucketFileProvider } from "./bitbucket-file-provider"; import { BitbucketRepositoryProvider } from "./bitbucket-repository-provider"; import { BitbucketTokenHelper } from "./bitbucket-token-handler"; import { BitbucketTokenValidator } from "./bitbucket-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { BitbucketService } from "../prebuilds/bitbucket-service"; export const bitbucketContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -33,5 +31,4 @@ export const bitbucketContainerModule = new ContainerModule((bind, _unbind, _isB bind(BitbucketTokenHelper).toSelf().inSingletonScope(); bind(BitbucketTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(BitbucketTokenValidator); - rebind(RepositoryService).to(BitbucketService).inSingletonScope(); }); diff --git a/components/server/src/github/github-container-module.ts b/components/server/src/github/github-container-module.ts index b6ba17e111ac67..46ab7b9985f031 100644 --- a/components/server/src/github/github-container-module.ts +++ b/components/server/src/github/github-container-module.ts @@ -16,8 +16,6 @@ import { GithubRepositoryProvider } from "./github-repository-provider"; import { GitHubTokenHelper } from "./github-token-helper"; import { IGitTokenValidator } from "../workspace/git-token-validator"; import { GitHubTokenValidator } from "./github-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { GitHubService } from "../prebuilds/github-service"; export const githubContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -34,5 +32,4 @@ export const githubContainerModule = new ContainerModule((bind, _unbind, _isBoun bind(GitHubTokenHelper).toSelf().inSingletonScope(); bind(GitHubTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(GitHubTokenValidator); - rebind(RepositoryService).to(GitHubService).inSingletonScope(); }); diff --git a/components/server/src/gitlab/gitlab-container-module.ts b/components/server/src/gitlab/gitlab-container-module.ts index ffdbace30872c3..b7234ef3f432fd 100644 --- a/components/server/src/gitlab/gitlab-container-module.ts +++ b/components/server/src/gitlab/gitlab-container-module.ts @@ -16,8 +16,6 @@ import { GitlabContextParser } from "./gitlab-context-parser"; import { GitlabRepositoryProvider } from "./gitlab-repository-provider"; import { GitLabTokenHelper } from "./gitlab-token-helper"; import { GitLabTokenValidator } from "./gitlab-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { GitlabService } from "../prebuilds/gitlab-service"; export const gitlabContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -33,5 +31,4 @@ export const gitlabContainerModule = new ContainerModule((bind, _unbind, _isBoun bind(GitLabTokenHelper).toSelf().inSingletonScope(); bind(GitLabTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(GitLabTokenValidator); - rebind(RepositoryService).to(GitlabService).inSingletonScope(); }); diff --git a/components/server/src/prebuilds/bitbucket-app.ts b/components/server/src/prebuilds/bitbucket-app.ts index 6dfce65b8f7fc6..44a4140cd6e330 100644 --- a/components/server/src/prebuilds/bitbucket-app.ts +++ b/components/server/src/prebuilds/bitbucket-app.ts @@ -5,35 +5,11 @@ */ import express from "express"; -import { postConstruct, injectable, inject } from "inversify"; -import { TeamDB, WebhookEventDB } from "@gitpod/gitpod-db/lib"; -import { User, CommitContext, CommitInfo, Project, WebhookEvent } from "@gitpod/gitpod-protocol"; -import { PrebuildManager } from "./prebuild-manager"; -import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; -import { TokenService } from "../user/token-service"; -import { ContextParser } from "../workspace/context-parser-service"; -import { HostContextProvider } from "../auth/host-context-provider"; -import { RepoURL } from "../repohost"; -import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { UserService } from "../user/user-service"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { URL } from "url"; -import { ProjectsService } from "../projects/projects-service"; -import { SubjectId } from "../auth/subject-id"; -import { runWithSubjectId } from "../util/request-context"; -import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; +import { postConstruct, injectable } from "inversify"; @injectable() export class BitbucketApp { - constructor( - @inject(UserService) private readonly userService: UserService, - @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, - @inject(TeamDB) private readonly teamDB: TeamDB, - @inject(ContextParser) private readonly contextParser: ContextParser, - @inject(HostContextProvider) private readonly hostCtxProvider: HostContextProvider, - @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, - @inject(ProjectsService) private readonly projectService: ProjectsService, - ) {} + constructor() {} private _router = express.Router(); public static path = "/apps/bitbucket/"; @@ -43,39 +19,12 @@ export class BitbucketApp { this._router.post("/", async (req, res) => { try { if (req.header("X-Event-Key") === "repo:push") { - const span = TraceContext.startSpan("BitbucketApp.handleEvent", {}); const secretToken = req.query["token"] as string; - const event = await this.webhookEvents.createEvent({ - type: "push", - status: "received", - rawEvent: JSON.stringify(req.body), - }); if (!secretToken) { - await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); throw new Error("No secretToken provided."); } - const user = await this.findUser({ span }, secretToken); - if (!user) { - // If the webhook installer is no longer found in Gitpod's DB - // we should send a UNAUTHORIZED signal. - res.statusCode = 401; - res.send(); - span.finish(); - await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); - return; - } - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const data = toData(req.body); - if (data) { - await this.handlePushHook({ span }, data, user, event); - } - } catch (err) { - TraceContext.setError({ span }, err); - throw err; - } finally { - span.finish(); - } + + console.warn("Bitbucket push event received, but not handling it"); } else { console.warn(`Ignoring unsupported bitbucket event: ${req.header("X-Event-Key")}`); } @@ -88,216 +37,7 @@ export class BitbucketApp { }); } - private async findUser(ctx: TraceContext, secretToken: string): Promise { - const span = TraceContext.startSpan("BitbucketApp.findUser", ctx); - try { - span.setTag("secret-token", secretToken); - const [userid, tokenValue] = secretToken.split("|"); - const user = await this.userService.findUserById(userid, userid); - if (!user) { - throw new Error("No user found for " + secretToken + " found."); - } else if (!!user.blocked) { - throw new Error(`Blocked user ${user.id} tried to start prebuild.`); - } - const identity = user.identities.find((i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID); - if (!identity) { - throw new Error(`User ${user.id} has no identity for '${TokenService.GITPOD_AUTH_PROVIDER_ID}'.`); - } - const tokens = await this.userService.findTokensForIdentity(userid, identity); - const token = tokens.find((t) => t.token.value === tokenValue); - if (!token) { - throw new Error(`User ${user.id} has no token with given value.`); - } - return user; - } finally { - span.finish(); - } - } - - private async handlePushHook( - ctx: TraceContext, - data: ParsedRequestData, - user: User, - event: WebhookEvent, - ): Promise { - const span = TraceContext.startSpan("Bitbucket.handlePushHook", ctx); - try { - const cloneURL = data.gitCloneUrl; - const projects = await runWithSubjectId(SYSTEM_USER, () => - this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, cloneURL), - ); - for (const project of projects) { - try { - const projectOwner = await this.findProjectOwner(project, user); - - const contextURL = this.createContextUrl(data); - span.setTag("contextURL", contextURL); - const context = (await this.contextParser.handle({ span }, user, contextURL)) as CommitContext; - await this.webhookEvents.updateEvent(event.id, { - authorizedUserId: user.id, - projectId: project?.id, - cloneUrl: context.repository.cloneUrl, - branch: context.ref, - commit: context.revision, - }); - const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); - const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ - config, - project, - context, - }); - if (!prebuildPrecondition.shouldRun) { - log.info("Bitbucket push event: No prebuild.", { config, context }); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "ignored_unconfigured", - status: "processed", - message: prebuildPrecondition.reason, - }); - continue; - } - - await runWithSubjectId(SubjectId.fromUserId(projectOwner.id), async () => { - log.info("Starting prebuild.", { contextURL }); - const { host, owner, repo } = RepoURL.parseRepoUrl(data.repoUrl)!; - const hostCtx = this.hostCtxProvider.get(host); - let commitInfo: CommitInfo | undefined; - if (hostCtx?.services?.repositoryProvider) { - commitInfo = await hostCtx.services.repositoryProvider.getCommitInfo( - user, - owner, - repo, - data.commitHash, - ); - } - const ws = await this.prebuildManager.startPrebuild( - { span }, - { - user: projectOwner, - project, - context, - commitInfo, - }, - ); - if (!ws.done) { - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_triggered", - status: "processed", - prebuildId: ws.prebuildId, - }); - } - }); - } catch (error) { - log.error("Error processing Bitbucket webhook event", error); - } - } - } catch (e) { - console.error("Error processing Bitbucket webhook event", e); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_trigger_failed", - status: "processed", - }); - throw e; - } finally { - span.finish(); - } - } - - /** - * Finds the relevant user account and project to the provided webhook event information. - * - * First of all it tries to find the project for the given `cloneURL`, then it tries to - * find the installer, which is also supposed to be a team member. As a fallback, it - * looks for a team member which also has a bitbucket.org connection. - * - * @param cloneURL of the webhook event - * @param webhookInstaller the user account known from the webhook installation - * @returns a promise which resolves to a user account and an optional project. - */ - private async findProjectOwner(project: Project, webhookInstaller: User): Promise { - try { - if (!project.teamId) { - throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Project has no teamId"); - } - const teamMembers = await this.teamDB.findMembersByTeam(project.teamId); - if (teamMembers.some((t) => t.userId === webhookInstaller.id)) { - return webhookInstaller; - } - const hostContext = this.hostCtxProvider.get(new URL(project.cloneUrl).host); - const authProviderId = hostContext?.authProvider.authProviderId; - for (const teamMember of teamMembers) { - const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => - this.userService.findUserById(teamMember.userId, teamMember.userId), - ); - if (user && user.identities.some((i) => i.authProviderId === authProviderId)) { - return user; - } - } - } catch (err) { - log.info({ userId: webhookInstaller.id }, "Failed to find project and owner", err); - } - return webhookInstaller; - } - - private createContextUrl(data: ParsedRequestData) { - const contextUrl = `${data.repoUrl}/src/${data.commitHash}/?at=${encodeURIComponent(data.branchName)}`; - return contextUrl; - } - get router(): express.Router { return this._router; } } - -function toData(body: BitbucketPushHook): ParsedRequestData | undefined { - const branchName = body.push.changes[0]?.new?.name; - const commitHash = body.push.changes[0]?.new?.target?.hash; - if (!branchName || !commitHash) { - return undefined; - } - const result = { - branchName, - commitHash, - repoUrl: body.repository.links.html.href, - gitCloneUrl: body.repository.links.html.href + ".git", - }; - if (!result.commitHash || !result.repoUrl) { - console.error("Bitbucket push event: unexpected request body."); - throw new Error("Unexpected request body."); - } - return result; -} - -interface ParsedRequestData { - branchName: string; - repoUrl: string; - gitCloneUrl: string; - commitHash: string; -} - -interface BitbucketPushHook { - push: { - changes: { - new: { - name: string; // e.g. "foo/bar-bazz" - type: "branch" | string; - target: { - hash: string; // e.g. "1b283e4d7a849a89151548398cc836d15149179c" - }; - } | null; // in case where a branch is deleted - }[]; - }; - actor: { - account_id: string; // e.g. "557058:964d5de0-9ae8-47e7-9ca2-9448caeb50ea" - }; - repository: BitbucketRepository; -} - -interface BitbucketRepository { - links: { - html: { - href: string; //e.g. "https://bitbucket.org/sefftinge/sample-repository" - }; - }; - full_name: string; // e.g. "sefftinge/sample-repository", - is_private: boolean; -} diff --git a/components/server/src/prebuilds/bitbucket-server-service.spec.ts b/components/server/src/prebuilds/bitbucket-server-service.spec.ts deleted file mode 100644 index 9bfad03e73ca59..00000000000000 --- a/components/server/src/prebuilds/bitbucket-server-service.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/** - * Copyright (c) 2022 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 { User } from "@gitpod/gitpod-protocol"; -import { ifEnvVarNotSet } from "@gitpod/gitpod-protocol/lib/util/skip-if"; -import { Container, ContainerModule } from "inversify"; -import { retries, skip, suite, test, timeout } from "@testdeck/mocha"; -import { AuthProviderParams } from "../auth/auth-provider"; -import { HostContextProvider } from "../auth/host-context-provider"; -import { BitbucketServerApi } from "../bitbucket-server/bitbucket-server-api"; -import { BitbucketServerContextParser } from "../bitbucket-server/bitbucket-server-context-parser"; -import { BitbucketServerTokenHelper } from "../bitbucket-server/bitbucket-server-token-handler"; -import { TokenProvider } from "../user/token-provider"; -import { BitbucketServerService } from "./bitbucket-server-service"; -import { expect } from "chai"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url"; - -@suite(timeout(10000), retries(1), skip(ifEnvVarNotSet("GITPOD_TEST_TOKEN_BITBUCKET_SERVER"))) -class TestBitbucketServerService { - protected service: BitbucketServerService; - protected user: User; - - static readonly AUTH_HOST_CONFIG: Partial = { - id: "MyBitbucketServer", - type: "BitbucketServer", - verified: true, - description: "", - icon: "", - host: "bitbucket.gitpod-self-hosted.com", - oauth: { - callBackUrl: "", - clientId: "not-used", - clientSecret: "", - tokenUrl: "", - scope: "", - authorizationUrl: "", - }, - }; - - public before() { - const container = new Container(); - container.load( - new ContainerModule((bind, unbind, isBound, rebind) => { - bind(BitbucketServerService).toSelf().inSingletonScope(); - bind(BitbucketServerContextParser).toSelf().inSingletonScope(); - bind(AuthProviderParams).toConstantValue(TestBitbucketServerService.AUTH_HOST_CONFIG); - bind(BitbucketServerTokenHelper).toSelf().inSingletonScope(); - bind(TokenService).toConstantValue({ - createGitpodToken: async () => ({ token: { value: "foobar123-token" } }), - } as any); - bind(Config).toConstantValue({ - hostUrl: new GitpodHostUrl("https://gitpod.io"), - }); - bind(TokenProvider).toConstantValue({ - getTokenForHost: async () => { - return { - value: process.env["GITPOD_TEST_TOKEN_BITBUCKET_SERVER"] || "undefined", - scopes: [], - }; - }, - }); - bind(BitbucketServerApi).toSelf().inSingletonScope(); - bind(HostContextProvider).toConstantValue({ - get: (hostname: string) => { - authProvider: { - ("BBS"); - } - }, - }); - }), - ); - this.service = container.get(BitbucketServerService); - this.user = { - creationDate: "", - id: "user1", - identities: [ - { - authId: "user1", - authName: "AlexTugarev", - authProviderId: "MyBitbucketServer", - }, - ], - }; - } - - @test async test_installAutomatedPrebuilds_ok() { - try { - await this.service.installAutomatedPrebuilds( - this.user, - "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123", - ); - } catch (error) { - expect.fail(error); - } - } - - @test async test_installAutomatedPrebuilds_unauthorized() { - try { - await this.service.installAutomatedPrebuilds( - this.user, - "https://bitbucket.gitpod-self-hosted.com/users/jldec/repos/test-repo", - ); - expect.fail("should have failed"); - } catch (error) {} - } - - @test async test_installAutomatedPrebuilds_in_project_ok() { - try { - await this.service.installAutomatedPrebuilds( - this.user, - "https://bitbucket.gitpod-self-hosted.com/projects/jldec/repos/jldec-repo-march-30", - ); - } catch (error) { - expect.fail(error); - } - } -} - -module.exports = new TestBitbucketServerService(); diff --git a/components/server/src/prebuilds/bitbucket-server-service.ts b/components/server/src/prebuilds/bitbucket-server-service.ts deleted file mode 100644 index 08f3a06c0f6e6e..00000000000000 --- a/components/server/src/prebuilds/bitbucket-server-service.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { User } from "@gitpod/gitpod-protocol"; -import { inject, injectable } from "inversify"; -import { BitbucketServerApi } from "../bitbucket-server/bitbucket-server-api"; -import { BitbucketServerContextParser } from "../bitbucket-server/bitbucket-server-context-parser"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { BitbucketServerApp } from "./bitbucket-server-app"; - -@injectable() -export class BitbucketServerService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(BitbucketServerApi) private readonly api: BitbucketServerApi, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(BitbucketServerContextParser) private readonly contextParser: BitbucketServerContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - const { owner, repoName, repoKind } = await this.contextParser.parseURL(user, cloneUrl); - - const existing = await this.api.getWebhooks(user, { - repoKind, - repositorySlug: repoName, - owner, - }); - const hookUrl = this.getHookUrl(); - if (existing.values && existing.values.some((hook) => hook.url && hook.url.indexOf(hookUrl) !== -1)) { - console.log(`BBS webhook already installed.`, { cloneUrl }); - return; - } - const tokenEntry = await this.tokenService.createGitpodToken( - user, - BitbucketServerService.PREBUILD_TOKEN_SCOPE, - cloneUrl, - ); - try { - await this.api.setWebhook( - user, - { repoKind, repositorySlug: repoName, owner }, - { - name: `Gitpod Prebuilds for ${this.config.hostUrl}.`, - active: true, - configuration: { - secret: "foobar123-secret", - }, - url: hookUrl + `?token=${encodeURIComponent(user.id + "|" + tokenEntry.token.value)}`, - events: ["repo:refs_changed"], - }, - ); - console.log("BBS: webhook installed.", { cloneUrl }); - } catch (error) { - console.error(`BBS: webhook installation failed.`, error, { cloneUrl, error }); - } - } - - protected getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: BitbucketServerApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/prebuilds/bitbucket-service.ts b/components/server/src/prebuilds/bitbucket-service.ts deleted file mode 100644 index c3f65870982ec0..00000000000000 --- a/components/server/src/prebuilds/bitbucket-service.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { User } from "@gitpod/gitpod-protocol"; -import { inject, injectable } from "inversify"; -import { BitbucketApiFactory } from "../bitbucket/bitbucket-api-factory"; -import { BitbucketApp } from "./bitbucket-app"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { BitbucketContextParser } from "../bitbucket/bitbucket-context-parser"; - -@injectable() -export class BitbucketService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(BitbucketApiFactory) private readonly api: BitbucketApiFactory, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(BitbucketContextParser) private readonly bitbucketContextParser: BitbucketContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - try { - const api = await this.api.create(user); - const { owner, repoName } = await this.bitbucketContextParser.parseURL(user, cloneUrl); - const existing = await api.repositories.listWebhooks({ - repo_slug: repoName, - workspace: owner, - }); - const hookUrl = this.getHookUrl(); - if ( - existing.data.values && - existing.data.values.some((hook) => hook.url && hook.url.indexOf(hookUrl) !== -1) - ) { - console.log(`bitbucket webhook already installed on ${owner}/${repoName}`); - return; - } - const tokenEntry = await this.tokenService.createGitpodToken( - user, - BitbucketService.PREBUILD_TOKEN_SCOPE, - cloneUrl, - ); - const response = await api.repositories.createWebhook({ - repo_slug: repoName, - workspace: owner, - // see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/hooks#post - _body: { - description: `Gitpod Prebuilds for ${this.config.hostUrl}.`, - url: hookUrl + `?token=${user.id + "|" + tokenEntry.token.value}`, - active: true, - events: ["repo:push"], - }, - }); - if (response.status !== 201) { - throw new Error(`Couldn't install webhook for ${cloneUrl}: ${response.status}`); - } - console.log("Installed Bitbucket Webhook for " + cloneUrl); - } catch (error) { - console.error("Failed to install Bitbucket webhook for " + cloneUrl, error); - } - } - - protected getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: BitbucketApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/prebuilds/github-app.ts b/components/server/src/prebuilds/github-app.ts index 97f4cc72f67149..1081f70df41544 100644 --- a/components/server/src/prebuilds/github-app.ts +++ b/components/server/src/prebuilds/github-app.ts @@ -9,41 +9,16 @@ import { getPrivateKey } from "@probot/get-private-key"; import * as fs from "fs-extra"; import { injectable, inject } from "inversify"; import { Config } from "../config"; -import { - AppInstallationDB, - TracedWorkspaceDB, - DBWithTracing, - UserDB, - WorkspaceDB, - ProjectDB, - TeamDB, - WebhookEventDB, -} from "@gitpod/gitpod-db/lib"; +import { AppInstallationDB, UserDB, ProjectDB } from "@gitpod/gitpod-db/lib"; import express from "express"; -import { log, LogContext, LogrusLogLevel } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { - WorkspaceConfig, - User, - Project, - StartPrebuildResult, - CommitContext, - CommitInfo, -} from "@gitpod/gitpod-protocol"; -import { GithubAppRules } from "./github-app-rules"; -import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; -import { PrebuildManager } from "./prebuild-manager"; +import { log, LogrusLogLevel } from "@gitpod/gitpod-protocol/lib/util/logging"; import { PrebuildStatusMaintainer } from "./prebuilt-status-maintainer"; import { Options, ApplicationFunctionOptions } from "probot/lib/types"; import { asyncHandler } from "../express-util"; -import { ContextParser } from "../workspace/context-parser-service"; -import { HostContextProvider } from "../auth/host-context-provider"; -import { RepoURL } from "../repohost"; import { ApplicationError, ErrorCode } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { UserService } from "../user/user-service"; import { ProjectsService } from "../projects/projects-service"; import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; import { runWithSubjectId, runWithRequestContext } from "../util/request-context"; -import { SubjectId } from "../auth/subject-id"; /** * GitHub app urls: @@ -61,16 +36,8 @@ export class GithubApp { @inject(Config) private readonly config: Config, @inject(PrebuildStatusMaintainer) private readonly statusMaintainer: PrebuildStatusMaintainer, @inject(ProjectDB) private readonly projectDB: ProjectDB, - @inject(TeamDB) private readonly teamDB: TeamDB, @inject(UserDB) private readonly userDB: UserDB, @inject(AppInstallationDB) private readonly appInstallationDB: AppInstallationDB, - @inject(UserService) private readonly userService: UserService, - @inject(TracedWorkspaceDB) private readonly workspaceDB: DBWithTracing, - @inject(GithubAppRules) private readonly appRules: GithubAppRules, - @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, - @inject(ContextParser) private readonly contextParser: ContextParser, - @inject(HostContextProvider) private readonly hostCtxProvider: HostContextProvider, - @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, @inject(ProjectsService) private readonly projectService: ProjectsService, ) { if (config.githubApp?.enabled) { @@ -108,11 +75,9 @@ export class GithubApp { // Backward-compatibility: Redirect old badge URLs (e.g. "/api/apps/github/pbs/github.com/gitpod-io/gitpod/5431d5735c32ab7d5d840a4d1a7d7c688d1f0ce9.svg") options.getRouter && - options - .getRouter("/pbs") - .get("/*", (req: express.Request, res: express.Response, next: express.NextFunction) => { - res.redirect(301, this.getBadgeImageURL()); - }); + options.getRouter("/pbs").get("/*", (req: express.Request, res: express.Response) => { + res.redirect(301, this.getBadgeImageURL()); + }); app.on("installation.created", (ctx: Context<"installation.created">) => { handleEvent(ctx.name, async () => { @@ -172,14 +137,6 @@ export class GithubApp { // TODO(at): handle deleted as well }); - app.on("push", (ctx: Context<"push">) => { - handleEvent(ctx.name, () => this.handlePushEvent(ctx)); - }); - - app.on(["pull_request.opened", "pull_request.synchronize", "pull_request.reopened"], (ctx) => { - handleEvent(ctx.name, () => this.handlePullRequest(ctx)); - }); - options.getRouter && options.getRouter("/reconfigure").get( "/", @@ -223,445 +180,9 @@ export class GithubApp { }); } - private async findProjectOwner(project: Project, installationOwner: User): Promise { - const user = await this.selectUserForPrebuild(installationOwner, project); - if (!user) { - log.info(`Did not find user for installation. Probably an incomplete app installation.`, { - repo: project.cloneUrl, - project, - }); - return installationOwner; - } - return user; - } - - private async handlePushEvent(ctx: Context<"push">): Promise { - const span = TraceContext.startSpan("GithubApp.handlePushEvent", {}); - span.setTag("request", ctx.id); - - // trim commits to avoid DB pollution - // https://github.com/gitpod-io/gitpod/issues/11578 - ctx.payload.head_commit = null; - - const event = await this.webhookEvents.createEvent({ - type: "push", - status: "received", - rawEvent: JSON.stringify(ctx.payload), - }); - - try { - const installationId = ctx.payload.installation?.id; - const installationOwner = await this.findInstallationOwner(installationId); - if (!installationOwner) { - log.info("Did not find user for installation. Probably an incomplete app installation.", { - repo: ctx.payload.repository, - installationId, - }); - return; - } - if (!!installationOwner.blocked) { - log.info(`Blocked user tried to start prebuild`, { repo: ctx.payload.repository }); - await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); - return; - } - const pl = ctx.payload; - const branch = this.getBranchFromRef(pl.ref); - if (!branch) { - // This is a valid case if we receive a tag push, for instance. - log.debug("Unable to get branch from ref", { ref: pl.ref }); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "ignored_unconfigured", - status: "processed", - }); - return; - } - const logCtx: LogContext = { userId: installationOwner.id }; - - const repo = pl.repository; - const contextURL = `${repo.html_url}/tree/${branch}`; - span.setTag("contextURL", contextURL); - const context = (await this.contextParser.handle({ span }, installationOwner, contextURL)) as CommitContext; - const projects = await runWithSubjectId(SYSTEM_USER, async () => - this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, context.repository.cloneUrl), - ); - for (const project of projects) { - try { - const user = await this.findProjectOwner(project, installationOwner); - const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); - - await this.webhookEvents.updateEvent(event.id, { - authorizedUserId: user.id, - projectId: project.id, - cloneUrl: context.repository.cloneUrl, - branch: context.ref, - commit: context.revision, - }); - const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ - config, - project, - context, - }); - - if (!prebuildPrecondition.shouldRun) { - const reason = `GitHub push event: No prebuild.`; - log.debug(logCtx, reason, { contextURL }); - span.log({ "not-running": reason, config: config }); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "ignored_unconfigured", - status: "processed", - message: prebuildPrecondition.reason, - }); - continue; - } - - await runWithSubjectId(SubjectId.fromUserId(user.id), async () => { - const commitInfo = await this.getCommitInfo(user, repo.html_url, ctx.payload.after); - this.prebuildManager - .startPrebuild({ span }, { user, context, project: project!, commitInfo }) - .then(async (result) => { - if (!result.done) { - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_triggered", - status: "processed", - prebuildId: result.prebuildId, - }); - } - }) - .catch(async (err) => { - log.error(logCtx, "Error while starting prebuild", err, { contextURL }); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_trigger_failed", - status: "processed", - }); - }); - }); - } catch (error) { - log.error("Error processing GitHub webhook event", error); - } - } - } catch (e) { - TraceContext.setError({ span }, e); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_trigger_failed", - status: "processed", - }); - throw e; - } finally { - span.finish(); - } - } - - private async getCommitInfo(user: User, repoURL: string, commitSHA: string) { - const parsedRepo = RepoURL.parseRepoUrl(repoURL)!; - const hostCtx = this.hostCtxProvider.get(parsedRepo.host); - let commitInfo: CommitInfo | undefined; - if (hostCtx?.services?.repositoryProvider) { - commitInfo = await hostCtx?.services?.repositoryProvider.getCommitInfo( - user, - parsedRepo.owner, - parsedRepo.repo, - commitSHA, - ); - } - return commitInfo; - } - - private getBranchFromRef(ref: string): string | undefined { - const headsPrefix = "refs/heads/"; - if (ref.startsWith(headsPrefix)) { - return ref.substring(headsPrefix.length); - } - - return undefined; - } - - private async handlePullRequest( - ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, - ): Promise { - const span = TraceContext.startSpan("GithubApp.handlePullRequest", {}); - span.setTag("request", ctx.id); - - const event = await this.webhookEvents.createEvent({ - type: ctx.name, - status: "received", - rawEvent: JSON.stringify(ctx.payload), - }); - - try { - const installationId = ctx.payload.installation?.id; - const cloneURL = ctx.payload.repository.clone_url; - // we are only interested in PRs that want to contribute to our repo - if (ctx.payload.pull_request?.base?.repo?.clone_url !== cloneURL) { - log.info("Ignoring inverse PR", ctx.payload.pull_request); - return; - } - const pr = ctx.payload.pull_request; - const contextURL = pr.html_url; - const installationOwner = await this.findInstallationOwner(installationId); - if (!installationOwner) { - log.info("Did not find user for installation. Probably an incomplete app installation.", { - repo: ctx.payload.repository, - installationId, - }); - return; - } - const context = (await this.contextParser.handle({ span }, installationOwner, contextURL)) as CommitContext; - - const projects = await runWithSubjectId(SubjectId.fromUserId(installationOwner.id), () => - this.projectService.findProjectsByCloneUrl(installationOwner.id, context.repository.cloneUrl), - ); - for (const project of projects) { - const user = await this.findProjectOwner(project, installationOwner); - - const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); - - await this.webhookEvents.updateEvent(event.id, { - authorizedUserId: user.id, - projectId: project?.id, - cloneUrl: context.repository.cloneUrl, - branch: context.ref, - commit: context.revision, - }); - - const prebuildStartResult = await this.onPrStartPrebuild({ span }, ctx, config, context, user, project); - if (prebuildStartResult) { - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_triggered", - status: "processed", - prebuildId: prebuildStartResult.prebuildId, - }); - - await this.onPrAddCheck({ span }, config, ctx, prebuildStartResult); - this.onPrAddBadge(config, ctx); - await this.onPrAddComment(config, ctx); - } else { - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "ignored_unconfigured", - status: "processed", - }); - } - } - } catch (e) { - TraceContext.setError({ span }, e); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_trigger_failed", - status: "processed", - }); - throw e; - } finally { - span.finish(); - } - } - - private async onPrAddCheck( - tracecContext: TraceContext, - config: WorkspaceConfig | undefined, - ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, - start: StartPrebuildResult, - ) { - if (!start) { - return; - } - - if (!this.appRules.shouldDo(config, "addCheck")) { - return; - } - - const span = TraceContext.startSpan("onPrAddCheck", tracecContext); - try { - const pws = await this.workspaceDB.trace({ span }).findPrebuildByWorkspaceID(start.wsid); - if (!pws) { - return; - } - - const installationId = ctx.payload.installation?.id; - if (!installationId) { - log.info("Did not find user for installation. Probably an incomplete app installation.", { - repo: ctx.payload.repository, - installationId, - }); - return; - } - await this.statusMaintainer.registerCheckRun( - { span }, - installationId, - pws, - { - ...ctx.repo(), - head_sha: ctx.payload.pull_request.head.sha, - details_url: this.config.hostUrl.withContext(ctx.payload.pull_request.html_url).toString(), - }, - config, - ); - } catch (err) { - TraceContext.setError({ span }, err); - throw err; - } finally { - span.finish(); - } - } - - private async onPrStartPrebuild( - tracecContext: TraceContext, - ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, - config: WorkspaceConfig, - context: CommitContext, - user: User, - project: Project, - ): Promise { - const pr = ctx.payload.pull_request; - const contextURL = pr.html_url; - - const isFork = pr.head.repo.id !== pr.base.repo.id; - if (isFork) { - log.debug({ userId: user.id }, `GitHub PR event from fork.`, { - contextURL, - userId: user.id, - project, - isFork, - }); - return; - } - - const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ config, project, context }); - if (prebuildPrecondition.shouldRun) { - const result = await runWithSubjectId(SubjectId.fromUserId(user.id), async () => { - const commitInfo = await this.getCommitInfo(user, ctx.payload.repository.html_url, pr.head.sha); - return await this.prebuildManager.startPrebuild(tracecContext, { - user, - context, - project: project!, - commitInfo, - }); - }); - if (result?.done) { - return undefined; - } - return result; - } else { - log.debug({ userId: user.id }, `GitHub push event: No prebuild.`, { - contextURL, - userId: user.id, - project, - }); - return; - } - } - - private onPrAddBadge( - config: WorkspaceConfig | undefined, - ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, - ) { - if (!this.appRules.shouldDo(config, "addBadge")) { - // we shouldn't add (or update) a button here - return; - } - - const pr = ctx.payload.pull_request; - const contextURL = pr.html_url; - const body: string | null = pr.body; - if (!body) { - return; - } - const button = ``; - if (body.includes(button)) { - // the button is already in the comment - return; - } - - const newBody = body + `\n\n${button}\n\n`; - const updatePrPromise = ctx.octokit.pulls.update({ ...ctx.repo(), pull_number: pr.number, body: newBody }); - updatePrPromise.catch((err) => log.error("Error while updating PR body", err, { contextURL })); - } - - private async onPrAddComment( - config: WorkspaceConfig | undefined, - ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, - ) { - if (!this.appRules.shouldDo(config, "addComment")) { - return; - } - - const pr = ctx.payload.pull_request; - const contextURL = pr.html_url; - const button = ``; - const comments = await ctx.octokit.issues.listComments(ctx.issue()); - const existingComment = comments.data.find((c: any) => c.body.indexOf(button) > -1); - if (existingComment) { - return; - } - - const newComment = ctx.issue({ body: `\n\n${button}\n\n` }); - const newCommentPromise = ctx.octokit.issues.createComment(newComment); - newCommentPromise.catch((err) => log.error("Error while adding new PR comment", err, { contextURL })); - } - private getBadgeImageURL(): string { return this.config.hostUrl.with({ pathname: "/button/open-in-gitpod.svg" }).toString(); } - - /** - * Finds the relevant user account to create a prebuild with. - * - * First it tries to find the installer of the GitHub App installation - * among the members of the project team. As a fallback, it tries so pick - * any of the team members which also has a github.com connection. - * - * For projects under a personal account, it simply returns the installer. - * - * @param installationOwner given user account of the GitHub App installation - * @param project the project associated with the `cloneURL` - * @returns a promise that resolves to a `User` or undefined - */ - private async selectUserForPrebuild(installationOwner?: User, project?: Project): Promise { - if (!project) { - return installationOwner; - } - if (!project.teamId) { - return installationOwner; - } - const teamMembers = await this.teamDB.findMembersByTeam(project.teamId); - if (!!installationOwner && teamMembers.some((t) => t.userId === installationOwner.id)) { - return installationOwner; - } - for (const teamMember of teamMembers) { - const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => - this.userService.findUserById(teamMember.userId, teamMember.userId), - ); - if (user && user.identities.some((i) => i.authProviderId === "Public-GitHub")) { - return user; - } - } - } - - /** - * - * @param installationId read from webhook event - * @returns the user account of the GitHub App installation - */ - private async findInstallationOwner(installationId?: number): Promise { - if (!installationId) { - return; - } - // Legacy mode - // - const installation = await this.appInstallationDB.findInstallation("github", String(installationId)); - if (!installation) { - return; - } - - const ownerID = installation.ownerUserID || "this-should-never-happen"; - const user = await this.userService.findUserById(ownerID, ownerID); - if (!user) { - return; - } - - return user; - } } function handleEvent(eventName: string, p: () => Promise): void { diff --git a/components/server/src/prebuilds/github-enterprise-app.ts b/components/server/src/prebuilds/github-enterprise-app.ts index 6f802a4642a2db..a41b4b2d55db72 100644 --- a/components/server/src/prebuilds/github-enterprise-app.ts +++ b/components/server/src/prebuilds/github-enterprise-app.ts @@ -5,38 +5,13 @@ */ import express from "express"; -import { createHmac, timingSafeEqual } from "crypto"; -import { Buffer } from "buffer"; -import { postConstruct, injectable, inject } from "inversify"; -import { TeamDB, WebhookEventDB } from "@gitpod/gitpod-db/lib"; -import { PrebuildManager } from "./prebuild-manager"; +import { postConstruct, injectable } from "inversify"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; -import { TokenService } from "../user/token-service"; -import { HostContextProvider } from "../auth/host-context-provider"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { CommitContext, CommitInfo, Project, User, WebhookEvent } from "@gitpod/gitpod-protocol"; -import { GitHubService } from "./github-service"; -import { URL } from "url"; -import { ContextParser } from "../workspace/context-parser-service"; -import { RepoURL } from "../repohost"; -import { UserService } from "../user/user-service"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { ProjectsService } from "../projects/projects-service"; -import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; -import { runWithSubjectId } from "../util/request-context"; -import { SubjectId } from "../auth/subject-id"; @injectable() export class GitHubEnterpriseApp { - constructor( - @inject(UserService) private readonly userService: UserService, - @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, - @inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider, - @inject(TeamDB) private readonly teamDB: TeamDB, - @inject(ContextParser) private readonly contextParser: ContextParser, - @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, - @inject(ProjectsService) private readonly projectService: ProjectsService, - ) {} + constructor() {} private _router = express.Router(); public static path = "/apps/ghe/"; @@ -49,35 +24,9 @@ export class GitHubEnterpriseApp { const payload = req.body as GitHubEnterprisePushPayload; const span = TraceContext.startSpan("GitHubEnterpriseApp.handleEvent", {}); span.setTag("payload", payload); - const event = await this.webhookEvents.createEvent({ - type: "push", - status: "received", - rawEvent: JSON.stringify(req.body), - }); - let user: User | undefined; - try { - user = await this.findUser({ span }, payload, req); - } catch (error) { - TraceContext.setError({ span }, error); - log.error("Cannot find user.", error, {}); - } - if (!user) { - res.statusCode = 401; - res.send("Unauthorized: Cannot find authorized user."); - span.finish(); - await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); - return; - } - - try { - await this.handlePushHook({ span }, payload, user, event); - } catch (err) { - TraceContext.setError({ span }, err); - throw err; - } finally { - span.finish(); - } + log.debug("GitHub Enterprise push event received, ignoring it", { event }); + span.finish(); } else { log.info("Unknown GitHub Enterprise event received", { event }); } @@ -85,227 +34,6 @@ export class GitHubEnterpriseApp { }); } - private async findUser( - ctx: TraceContext, - payload: GitHubEnterprisePushPayload, - req: express.Request, - ): Promise { - const span = TraceContext.startSpan("GitHubEnterpriseApp.findUser", ctx); - try { - let host = req.header("X-Github-Enterprise-Host"); - if (!host) { - // If the GitHub installation doesn't identify itself, we fall back to the hostname from the repository URL. - const repoUrl = new URL(payload.repository.url); - host = repoUrl.hostname; - } - const hostContext = this.hostContextProvider.get(host || ""); - if (!hostContext) { - throw new Error("Unsupported GitHub Enterprise host: " + host); - } - const cloneURL = payload.repository.clone_url; - const projectOwners = await this.findProjectOwners(cloneURL); - if (!projectOwners) { - throw new Error("No project found."); - } - for (const user of projectOwners.users) { - const gitpodIdentity = user.identities.find( - (i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID, - ); - if (!gitpodIdentity) { - continue; - } - // Verify the webhook signature - const signature = req.header("X-Hub-Signature-256"); - const body = (req as any).rawBody; - const tokenEntries = (await this.userService.findTokensForIdentity(user.id, gitpodIdentity)).filter( - (tokenEntry) => { - return tokenEntry.token.scopes.includes(GitHubService.PREBUILD_TOKEN_SCOPE); - }, - ); - const signatureMatched = tokenEntries.some((tokenEntry) => { - const sig = - "sha256=" + - createHmac("sha256", user.id + "|" + tokenEntry.token.value) - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - .update(body) - .digest("hex"); - return timingSafeEqual(Buffer.from(sig), Buffer.from(signature ?? "")); - }); - if (signatureMatched) { - if (!!user.blocked) { - throw new Error(`Blocked user ${user.id} tried to start prebuild.`); - } - return user; - } - } - } finally { - span.finish(); - } - } - - private async handlePushHook( - ctx: TraceContext, - payload: GitHubEnterprisePushPayload, - user: User, - event: WebhookEvent, - ): Promise { - const span = TraceContext.startSpan("GitHubEnterpriseApp.handlePushHook", ctx); - try { - const cloneURL = payload.repository.clone_url; - const contextURL = this.createContextUrl(payload); - const context = (await this.contextParser.handle({ span }, user, contextURL)) as CommitContext; - const projects = await runWithSubjectId(SYSTEM_USER, () => - this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, context.repository.cloneUrl), - ); - span.setTag("contextURL", contextURL); - for (const project of projects) { - try { - const projectOwner = await this.findProjectOwner(project, user); - - await this.webhookEvents.updateEvent(event.id, { - authorizedUserId: user.id, - projectId: project.id, - cloneUrl: cloneURL, - branch: context.ref, - commit: context.revision, - }); - - const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); - const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ - config, - project, - context, - }); - - if (!prebuildPrecondition.shouldRun) { - log.info("GitHub Enterprise push event: No prebuild.", { config, context }); - - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "ignored_unconfigured", - status: "processed", - message: prebuildPrecondition.reason, - }); - continue; - } - - log.debug("GitHub Enterprise push event: Starting prebuild.", { contextURL }); - - await runWithSubjectId(SubjectId.fromUserId(projectOwner.id), async () => { - const commitInfo = await this.getCommitInfo(user, payload.repository.url, payload.after); - const ws = await this.prebuildManager.startPrebuild( - { span }, - { - context, - user: projectOwner, - project: project, - commitInfo, - }, - ); - if (!ws.done) { - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_triggered", - status: "processed", - prebuildId: ws.prebuildId, - }); - } - }); - } catch (error) { - log.error("Error processing GitHub webhook event", error); - } - } - } catch (e) { - log.error("Error processing GitHub Enterprise webhook event.", e); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_trigger_failed", - status: "processed", - }); - } finally { - span.finish(); - } - } - - private async getCommitInfo(user: User, repoURL: string, commitSHA: string) { - const parsedRepo = RepoURL.parseRepoUrl(repoURL)!; - const hostCtx = this.hostContextProvider.get(parsedRepo.host); - let commitInfo: CommitInfo | undefined; - if (hostCtx?.services?.repositoryProvider) { - commitInfo = await hostCtx?.services?.repositoryProvider.getCommitInfo( - user, - parsedRepo.owner, - parsedRepo.repo, - commitSHA, - ); - } - return commitInfo; - } - - private async findProjectOwner(project: Project, webhookInstaller: User): Promise { - try { - if (!project.teamId) { - throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Project has no teamId."); - } - const teamMembers = await this.teamDB.findMembersByTeam(project.teamId || ""); - if (teamMembers.some((t) => t.userId === webhookInstaller.id)) { - return webhookInstaller; - } - const hostContext = this.hostContextProvider.get(new URL(project.cloneUrl).host); - const authProviderId = hostContext?.authProvider.authProviderId; - for (const teamMember of teamMembers) { - const user = await runWithSubjectId(SubjectId.fromUserId(webhookInstaller.id), () => - this.userService.findUserById(webhookInstaller.id, teamMember.userId), - ); - if (user && user.identities.some((i) => i.authProviderId === authProviderId)) { - return user; - } - } - } catch (err) { - log.info({ userId: webhookInstaller.id }, "Failed to find project and owner", err); - } - return webhookInstaller; - } - - private async findProjectOwners(cloneURL: string): Promise<{ users: User[]; project: Project } | undefined> { - try { - const projects = await runWithSubjectId(SYSTEM_USER, async () => - this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, cloneURL), - ); - const project = projects[0]; - if (project) { - const users = []; - const owners = (await this.teamDB.findMembersByTeam(project.teamId || "")).filter( - (m) => m.role === "owner", - ); - const hostContext = this.hostContextProvider.get(new URL(cloneURL).host); - const authProviderId = hostContext?.authProvider.authProviderId; - for (const teamMember of owners) { - const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => - this.userService.findUserById(teamMember.userId, teamMember.userId), - ); - if (user && user.identities.some((i) => i.authProviderId === authProviderId)) { - users.push(user); - } - } - return { users, project }; - } - } catch (err) { - log.info("Failed to find project and owner", err); - } - return undefined; - } - - private getBranchFromRef(ref: string): string | undefined { - const headsPrefix = "refs/heads/"; - if (ref.startsWith(headsPrefix)) { - return ref.substring(headsPrefix.length); - } - - return undefined; - } - - private createContextUrl(payload: GitHubEnterprisePushPayload) { - return `${payload.repository.url}/tree/${this.getBranchFromRef(payload.ref)}`; - } - get router(): express.Router { return this._router; } diff --git a/components/server/src/prebuilds/github-service.ts b/components/server/src/prebuilds/github-service.ts deleted file mode 100644 index 9d338df2b3b24e..00000000000000 --- a/components/server/src/prebuilds/github-service.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { inject, injectable } from "inversify"; -import { GitHubApiError, GitHubRestApi } from "../github/api"; -import { GitHubEnterpriseApp } from "./github-enterprise-app"; -import { GithubContextParser } from "../github/github-context-parser"; -import { User } from "@gitpod/gitpod-protocol"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { RepoURL } from "../repohost"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { UnauthorizedError } from "../errors"; -import { GitHubScope } from "../github/scopes"; -import { containsScopes } from "./token-scopes-inclusion"; - -@injectable() -export class GitHubService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(GitHubRestApi) protected readonly githubApi: GitHubRestApi, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(GithubContextParser) private readonly githubContextParser: GithubContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - const parsedRepoUrl = RepoURL.parseRepoUrl(cloneUrl); - if (!parsedRepoUrl) { - throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Clone URL not parseable.`); - } - let tokenEntry; - try { - const { owner, repoName: repo } = await this.githubContextParser.parseURL(user, cloneUrl); - const webhooks = (await this.githubApi.run(user, (gh) => gh.repos.listWebhooks({ owner, repo }))).data; - for (const webhook of webhooks) { - if (webhook.config.url === this.getHookUrl()) { - await this.githubApi.run(user, (gh) => - gh.repos.deleteWebhook({ owner, repo, hook_id: webhook.id }), - ); - } - } - tokenEntry = await this.tokenService.createGitpodToken(user, GitHubService.PREBUILD_TOKEN_SCOPE, cloneUrl); - const config = { - url: this.getHookUrl(), - content_type: "json", - secret: user.id + "|" + tokenEntry.token.value, - }; - await this.githubApi.run(user, (gh) => gh.repos.createWebhook({ owner, repo, config })); - } catch (error) { - // Hint: here we catch all GH API errors to forward them as Unauthorized to FE, - // eventually that should be done depending on the error code. - // Also, if user is not connected at all, then the GH API wrapper is throwing - // the same error type, but with `providerIsConnected: false`. - - if (GitHubApiError.is(error)) { - // TODO check for `error.code` - throw UnauthorizedError.create({ - host: parsedRepoUrl.host, - providerType: "GitHub", - repoName: parsedRepoUrl.repo, - requiredScopes: GitHubScope.Requirements.PRIVATE_REPO, - providerIsConnected: true, - isMissingScopes: containsScopes(tokenEntry?.token.scopes, GitHubScope.Requirements.PRIVATE_REPO), - }); - } - throw error; - } - } - - protected getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: GitHubEnterpriseApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/prebuilds/gitlab-app.ts b/components/server/src/prebuilds/gitlab-app.ts index 3432aabd6d1e04..557bdbfa1cabf8 100644 --- a/components/server/src/prebuilds/gitlab-app.ts +++ b/components/server/src/prebuilds/gitlab-app.ts @@ -5,35 +5,12 @@ */ import express from "express"; -import { postConstruct, injectable, inject } from "inversify"; -import { TeamDB, WebhookEventDB } from "@gitpod/gitpod-db/lib"; -import { Project, User, CommitContext, CommitInfo, WebhookEvent } from "@gitpod/gitpod-protocol"; -import { PrebuildManager } from "./prebuild-manager"; -import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; -import { TokenService } from "../user/token-service"; -import { HostContextProvider } from "../auth/host-context-provider"; -import { GitlabService } from "./gitlab-service"; +import { postConstruct, injectable } from "inversify"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { ContextParser } from "../workspace/context-parser-service"; -import { RepoURL } from "../repohost"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { UserService } from "../user/user-service"; -import { ProjectsService } from "../projects/projects-service"; -import { runWithSubjectId } from "../util/request-context"; -import { SubjectId } from "../auth/subject-id"; -import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; @injectable() export class GitLabApp { - constructor( - @inject(UserService) private readonly userService: UserService, - @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, - @inject(HostContextProvider) private readonly hostCtxProvider: HostContextProvider, - @inject(TeamDB) private readonly teamDB: TeamDB, - @inject(ContextParser) private readonly contextParser: ContextParser, - @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, - @inject(ProjectsService) private readonly projectService: ProjectsService, - ) {} + constructor() {} private _router = express.Router(); public static path = "/apps/gitlab/"; @@ -60,220 +37,18 @@ export class GitLabApp { // https://github.com/gitpod-io/gitpod/issues/11578 context.commits = []; - const event = await this.webhookEvents.createEvent({ - type: "push", - status: "received", - rawEvent: JSON.stringify(req.body), - }); - if (eventType !== "Push Hook" || !secretToken) { log.warn("Unhandled GitLab event.", { event: eventType, secretToken: !!secretToken }); res.status(200).send("Unhandled event."); - await this.webhookEvents.updateEvent(event.id, { status: "ignored" }); return; - } - - const span = TraceContext.startSpan("GitLapApp.handleEvent", {}); - span.setTag("request", context); - log.debug("GitLab push event received.", { event: eventType, context }); - let user: User | undefined; - try { - user = await this.findUser({ span }, context, secretToken); - } catch (error) { - log.error("Cannot find user.", error, { context }); - TraceContext.setError({ span }, error); - } - if (!user) { - // webhooks are not supposed to return 4xx codes on application issues. - // sending "Unauthorized" as content to support inspection of webhook delivery logs. - span.finish(); - res.status(200).send("Unauthorized."); - await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); - // TODO(at) explore ways to mark a project having issues with permissions. + } else if (eventType === "Push Hook") { + log.debug("GitLab push event received, but not handling it", { event: eventType, context }); + res.status(200).send("Unhandled event."); return; } - /** no await */ this.handlePushHook({ span }, context, user, event).catch((error) => { - console.error(`Couldn't handle request.`, error, { headers: req.headers }); - TraceContext.setError({ span }, error); - }); - - span.finish(); - res.status(201).send("Prebuild request handled."); }); } - private async findUser(ctx: TraceContext, context: GitLabPushHook, secretToken: string): Promise { - const span = TraceContext.startSpan("GitLapApp.findUser", ctx); - try { - const [userid, tokenValue] = secretToken.split("|"); - const user = await this.userService.findUserById(userid, userid); - if (!user) { - throw new Error("No user found for " + secretToken + " found."); - } else if (!!user.blocked) { - throw new Error(`Blocked user ${user.id} tried to start prebuild.`); - } - const identity = user.identities.find((i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID); - if (!identity) { - throw new Error(`User ${user.id} has no identity for '${TokenService.GITPOD_AUTH_PROVIDER_ID}'.`); - } - const tokens = await this.userService.findTokensForIdentity(userid, identity); - const token = tokens.find((t) => t.token.value === tokenValue); - if (!token) { - throw new Error(`User ${user.id} has no token with given value.`); - } - if ( - token.token.scopes.indexOf(GitlabService.PREBUILD_TOKEN_SCOPE) === -1 || - token.token.scopes.indexOf(context.repository.git_http_url) === -1 - ) { - throw new Error( - `The provided token is not valid for the repository ${context.repository.git_http_url}.`, - ); - } - return user; - } finally { - span.finish(); - } - } - - private async handlePushHook( - ctx: TraceContext, - body: GitLabPushHook, - user: User, - event: WebhookEvent, - ): Promise { - const span = TraceContext.startSpan("GitLapApp.handlePushHook", ctx); - try { - const cloneUrl = this.getCloneUrl(body); - const projects = await runWithSubjectId(SYSTEM_USER, () => - this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, cloneUrl), - ); - for (const project of projects) { - try { - const projectOwner = await this.findProjectOwner(project, user); - - const contextURL = this.createBranchContextUrl(body); - log.debug({ userId: user.id }, "GitLab push hook: Context URL", { context: body, contextURL }); - span.setTag("contextURL", contextURL); - const context = (await this.contextParser.handle({ span }, user, contextURL)) as CommitContext; - - await this.webhookEvents.updateEvent(event.id, { - authorizedUserId: user.id, - projectId: project?.id, - cloneUrl: context.repository.cloneUrl, - branch: context.ref, - commit: context.revision, - }); - - const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); - const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ - config, - project, - context, - }); - if (!prebuildPrecondition.shouldRun) { - log.info("GitLab push event: No prebuild.", { config, context }); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "ignored_unconfigured", - status: "processed", - message: prebuildPrecondition.reason, - }); - continue; - } - - log.debug({ userId: user.id }, "GitLab push event: Starting prebuild", { body, contextURL }); - - const workspaceUser = projectOwner || user; - await runWithSubjectId(SubjectId.fromUserId(workspaceUser.id), async () => { - const commitInfo = await this.getCommitInfo(user, body.repository.git_http_url, body.after); - const ws = await this.prebuildManager.startPrebuild( - { span }, - { - user: workspaceUser, - project: project, - context, - commitInfo, - }, - ); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_triggered", - status: "processed", - prebuildId: ws.prebuildId, - }); - }); - } catch (error) { - log.error("Error processing GitLab webhook event", error); - } - } - } catch (e) { - log.error("Error processing GitLab webhook event", e, body); - await this.webhookEvents.updateEvent(event.id, { - prebuildStatus: "prebuild_trigger_failed", - status: "processed", - }); - } finally { - span.finish(); - } - } - - private async getCommitInfo(user: User, repoURL: string, commitSHA: string) { - const parsedRepo = RepoURL.parseRepoUrl(repoURL)!; - const hostCtx = this.hostCtxProvider.get(parsedRepo.host); - let commitInfo: CommitInfo | undefined; - if (hostCtx?.services?.repositoryProvider) { - commitInfo = await hostCtx?.services?.repositoryProvider.getCommitInfo( - user, - parsedRepo.owner, - parsedRepo.repo, - commitSHA, - ); - } - return commitInfo; - } - - /** - * Finds the relevant user account and project to the provided webhook event information. - * - * First of all it tries to find the project for the given `cloneURL`, then it tries to - * find the installer, which is also supposed to be a team member. As a fallback, it - * looks for a team member which also has a gitlab.com connection. - * - * @param cloneURL of the webhook event - * @param webhookInstaller the user account known from the webhook installation - * @returns a promise which resolves to a user account and an optional project. - */ - private async findProjectOwner(project: Project, webhookInstaller: User): Promise { - try { - if (!project.teamId) { - throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Project has no teamId."); - } - const teamMembers = await this.teamDB.findMembersByTeam(project.teamId || ""); - if (teamMembers.some((t) => t.userId === webhookInstaller.id)) { - return webhookInstaller; - } - for (const teamMember of teamMembers) { - const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => - this.userService.findUserById(teamMember.userId, teamMember.userId), - ); - if (user && user.identities.some((i) => i.authProviderId === "Public-GitLab")) { - return user; - } - } - } catch (err) { - log.info({ userId: webhookInstaller.id }, "Failed to find project and owner", err); - } - return webhookInstaller; - } - - private createBranchContextUrl(body: GitLabPushHook) { - const repoUrl = body.repository.git_http_url; - const contextURL = `${repoUrl.substr(0, repoUrl.length - 4)}/-/tree${body.ref.substr("refs/head/".length)}`; - return contextURL; - } - - private getCloneUrl(body: GitLabPushHook) { - return body.repository.git_http_url; - } - get router(): express.Router { return this._router; } diff --git a/components/server/src/prebuilds/gitlab-service.ts b/components/server/src/prebuilds/gitlab-service.ts deleted file mode 100644 index d5effe4a75b3ac..00000000000000 --- a/components/server/src/prebuilds/gitlab-service.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { User } from "@gitpod/gitpod-protocol"; -import { inject, injectable } from "inversify"; -import { GitLabApi, GitLab } from "../gitlab/api"; -import { GitLabApp } from "./gitlab-app"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { GitlabContextParser } from "../gitlab/gitlab-context-parser"; -import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { RepoURL } from "../repohost"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { UnauthorizedRepositoryAccessError } from "@gitpod/public-api-common/lib/public-api-errors"; -import { UnauthorizedError } from "../errors"; -import { GitLabScope } from "../gitlab/scopes"; -import { containsScopes } from "./token-scopes-inclusion"; - -@injectable() -export class GitlabService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(GitLabApi) protected api: GitLabApi, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(GitlabContextParser) private readonly gitlabContextParser: GitlabContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - const parsedRepoUrl = RepoURL.parseRepoUrl(cloneUrl); - if (!parsedRepoUrl) { - throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Clone URL not parseable.`); - } - - let api; - try { - api = await this.api.create(user); // throw UnauthorizedError - } catch (error) { - if (error instanceof UnauthorizedRepositoryAccessError) { - error.info.host = parsedRepoUrl.host; - error.info.providerIsConnected = false; - error.info.isMissingScopes = true; - error.info.providerType = "GitLab"; - } - throw error; - } - - let tokenEntry; - try { - // throws GitLabApiError 404 - const { owner, repoName } = await this.gitlabContextParser.parseURL(user, cloneUrl); - const gitlabProjectId = `${owner}/${repoName}`; - // throws GitLabApiError 403 - const hooks = (await api.ProjectHooks.all(gitlabProjectId)) as unknown as GitLab.ProjectHook[]; - if (GitLab.ApiError.is(hooks)) { - throw hooks; - } - let existingProps: any = {}; - for (const hook of hooks) { - if (hook.url === this.getHookUrl()) { - log.info("Deleting existing hook"); - existingProps = hook; - // throws GitLabApiError 403 - await api.ProjectHooks.remove(gitlabProjectId, hook.id); - } - } - tokenEntry = await this.tokenService.createGitpodToken(user, GitlabService.PREBUILD_TOKEN_SCOPE, cloneUrl); - // throws GitLabApiError 403 - await api.ProjectHooks.add(gitlabProjectId, this.getHookUrl(), >{ - ...existingProps, - push_events: true, - token: user.id + "|" + tokenEntry.token.value, - }); - log.info("Installed Webhook for " + cloneUrl, { cloneUrl, userId: user.id }); - } catch (error) { - if (GitLab.ApiError.is(error)) { - // TODO check for `error.code` - - throw UnauthorizedError.create({ - host: parsedRepoUrl.host, - providerType: "GitLab", - repoName: parsedRepoUrl.repo, - requiredScopes: GitLabScope.Requirements.REPO, - providerIsConnected: true, - isMissingScopes: containsScopes(tokenEntry?.token?.scopes, GitLabScope.Requirements.REPO), - }); - } - throw error; - } - } - - private getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: GitLabApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/repohost/repo-service.ts b/components/server/src/repohost/repo-service.ts deleted file mode 100644 index 6b5f69b4854d32..00000000000000 --- a/components/server/src/repohost/repo-service.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { User } from "@gitpod/gitpod-protocol"; -import { injectable } from "inversify"; - -@injectable() -export class RepositoryService { - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - throw new Error("unsupported"); - } -} diff --git a/components/server/src/repohost/repository-host.ts b/components/server/src/repohost/repository-host.ts index eba852ee0bf488..e87752ab0d1655 100644 --- a/components/server/src/repohost/repository-host.ts +++ b/components/server/src/repohost/repository-host.ts @@ -8,11 +8,9 @@ import { inject, injectable } from "inversify"; import { FileProvider } from "./file-provider"; import { RepositoryProvider } from "./repository-provider"; -import { RepositoryService } from "./repo-service"; @injectable() export class RepositoryHost { @inject(FileProvider) fileProvider: FileProvider; @inject(RepositoryProvider) repositoryProvider: RepositoryProvider; - @inject(RepositoryService) repositoryService: RepositoryService; } diff --git a/components/server/src/scm/scm-service.ts b/components/server/src/scm/scm-service.ts index b13dbfc68a7cb6..8cf20186727a71 100644 --- a/components/server/src/scm/scm-service.ts +++ b/components/server/src/scm/scm-service.ts @@ -8,9 +8,8 @@ import { inject, injectable } from "inversify"; import { Authorizer } from "../authorization/authorizer"; import { Config } from "../config"; import { TokenProvider } from "../user/token-provider"; -import { CommitContext, Project, SuggestedRepository, Token, User, WorkspaceInfo } from "@gitpod/gitpod-protocol"; +import { CommitContext, Project, SuggestedRepository, Token, WorkspaceInfo } from "@gitpod/gitpod-protocol"; import { HostContextProvider } from "../auth/host-context-provider"; -import { RepoURL } from "../repohost"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { AuthProviderService } from "../auth/auth-provider-service"; import { UserService } from "../user/user-service"; @@ -53,29 +52,6 @@ export class ScmService { return token; } - public async installWebhookForPrebuilds(project: Project, installer: User) { - // Install the prebuilds webhook if possible - const { teamId, cloneUrl } = project; - const parsedUrl = RepoURL.parseRepoUrl(project.cloneUrl); - const hostContext = parsedUrl?.host ? this.hostContextProvider.get(parsedUrl?.host) : undefined; - - if (!hostContext) { - throw new ApplicationError(ErrorCodes.NOT_FOUND, `SCM provider not found.`); - } - - const repositoryService = hostContext.services?.repositoryService; - if (repositoryService) { - const logPayload = { organizationId: teamId, installer: installer.id, cloneUrl: project.cloneUrl }; - try { - await repositoryService.installAutomatedPrebuilds(installer, cloneUrl); - log.info("Webhook for prebuilds installed.", logPayload); - } catch (error) { - log.error("Failed to install webhook for prebuilds.", error, logPayload); - throw error; - } - } - } - /** * `guessTokenScopes` allows clients to retrieve scopes that would be necessary for a specified * git operation on a specified repository. diff --git a/components/server/src/test/service-testing-container-module.ts b/components/server/src/test/service-testing-container-module.ts index f494f7d12ccd02..f0b682b4da4512 100644 --- a/components/server/src/test/service-testing-container-module.ts +++ b/components/server/src/test/service-testing-container-module.ts @@ -110,11 +110,6 @@ const mockApplyingContainerModule = new ContainerModule((bind, unbound, isbound, }, }, services: { - repositoryService: { - installAutomatedPrebuilds: async (user: any, cloneUrl: string) => { - webhooks.add(cloneUrl); - }, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; diff --git a/components/server/src/user/token-service.spec.db.ts b/components/server/src/user/token-service.spec.db.ts index f32a98d97abad9..8b0f1bdb403c5c 100644 --- a/components/server/src/user/token-service.spec.db.ts +++ b/components/server/src/user/token-service.spec.db.ts @@ -88,9 +88,6 @@ describe("TokenService", async () => { }, }, services: { - repositoryService: { - installAutomatedPrebuilds: async (user: any, cloneUrl: string) => {}, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; @@ -116,9 +113,6 @@ describe("TokenService", async () => { }, }, services: { - repositoryService: { - installAutomatedPrebuilds: async (user: any, cloneUrl: string) => {}, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; diff --git a/components/server/src/workspace/context-service.spec.db.ts b/components/server/src/workspace/context-service.spec.db.ts index cdb65828da5d07..c86981a5723808 100644 --- a/components/server/src/workspace/context-service.spec.db.ts +++ b/components/server/src/workspace/context-service.spec.db.ts @@ -138,10 +138,6 @@ describe("ContextService", async () => { }, }, services: { - repositoryService: { - installAutomatedPrebuilds: () => {}, - canInstallAutomatedPrebuilds: async () => {}, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; From b8dab2038c2b48486babcbacbecdc19e4bbbf041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Jul 2024 11:43:14 +0000 Subject: [PATCH 07/16] Revert "remove webhook handling code" This reverts commit d7c4550ec6b16091344607bfeee234b1f0e5705b. --- .../src/auth/host-context-provider-impl.ts | 2 + .../bitbucket-server-container-module.ts | 3 + .../bitbucket/bitbucket-container-module.ts | 3 + .../src/github/github-container-module.ts | 3 + .../src/gitlab/gitlab-container-module.ts | 3 + .../server/src/prebuilds/bitbucket-app.ts | 268 +++++++++- .../bitbucket-server-service.spec.ts | 125 +++++ .../src/prebuilds/bitbucket-server-service.ts | 75 +++ .../server/src/prebuilds/bitbucket-service.ts | 78 +++ components/server/src/prebuilds/github-app.ts | 489 +++++++++++++++++- .../src/prebuilds/github-enterprise-app.ts | 280 +++++++++- .../server/src/prebuilds/github-service.ts | 86 +++ components/server/src/prebuilds/gitlab-app.ts | 235 ++++++++- .../server/src/prebuilds/gitlab-service.ts | 107 ++++ .../server/src/repohost/repo-service.ts | 15 + .../server/src/repohost/repository-host.ts | 2 + components/server/src/scm/scm-service.ts | 26 +- .../test/service-testing-container-module.ts | 5 + .../server/src/user/token-service.spec.db.ts | 6 + .../src/workspace/context-service.spec.db.ts | 4 + 20 files changed, 1796 insertions(+), 19 deletions(-) create mode 100644 components/server/src/prebuilds/bitbucket-server-service.spec.ts create mode 100644 components/server/src/prebuilds/bitbucket-server-service.ts create mode 100644 components/server/src/prebuilds/bitbucket-service.ts create mode 100644 components/server/src/prebuilds/github-service.ts create mode 100644 components/server/src/prebuilds/gitlab-service.ts create mode 100644 components/server/src/repohost/repo-service.ts diff --git a/components/server/src/auth/host-context-provider-impl.ts b/components/server/src/auth/host-context-provider-impl.ts index e16dd2685bb5f3..e29caa1dfac447 100644 --- a/components/server/src/auth/host-context-provider-impl.ts +++ b/components/server/src/auth/host-context-provider-impl.ts @@ -12,6 +12,7 @@ import { AuthProviderService } from "./auth-provider-service"; import { HostContextProvider, HostContextProviderFactory } from "./host-context-provider"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { HostContainerMapping } from "./host-container-mapping"; +import { RepositoryService } from "../repohost/repo-service"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat"; @@ -152,6 +153,7 @@ export class HostContextProviderImpl implements HostContextProvider { const container = parentContainer.createChild(); container.bind(AuthProviderParams).toConstantValue(authProviderConfig); container.bind(HostContext).toSelf().inSingletonScope(); + container.bind(RepositoryService).toSelf().inSingletonScope(); const hostContainerMapping = parentContainer.get(HostContainerMapping); const containerModules = hostContainerMapping.get(authProviderConfig.type); diff --git a/components/server/src/bitbucket-server/bitbucket-server-container-module.ts b/components/server/src/bitbucket-server/bitbucket-server-container-module.ts index 572c537750e71d..725299eba3d176 100644 --- a/components/server/src/bitbucket-server/bitbucket-server-container-module.ts +++ b/components/server/src/bitbucket-server/bitbucket-server-container-module.ts @@ -16,6 +16,8 @@ import { BitbucketServerFileProvider } from "./bitbucket-server-file-provider"; import { BitbucketServerRepositoryProvider } from "./bitbucket-server-repository-provider"; import { BitbucketServerTokenHelper } from "./bitbucket-server-token-handler"; import { BitbucketServerTokenValidator } from "./bitbucket-server-token-validator"; +import { RepositoryService } from "../repohost/repo-service"; +import { BitbucketServerService } from "../prebuilds/bitbucket-server-service"; export const bitbucketServerContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -31,4 +33,5 @@ export const bitbucketServerContainerModule = new ContainerModule((bind, _unbind bind(BitbucketServerTokenHelper).toSelf().inSingletonScope(); bind(BitbucketServerTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(BitbucketServerTokenValidator); + rebind(RepositoryService).to(BitbucketServerService).inSingletonScope(); }); diff --git a/components/server/src/bitbucket/bitbucket-container-module.ts b/components/server/src/bitbucket/bitbucket-container-module.ts index 406b1f61404e5b..9b4413baa387a7 100644 --- a/components/server/src/bitbucket/bitbucket-container-module.ts +++ b/components/server/src/bitbucket/bitbucket-container-module.ts @@ -16,6 +16,8 @@ import { BitbucketFileProvider } from "./bitbucket-file-provider"; import { BitbucketRepositoryProvider } from "./bitbucket-repository-provider"; import { BitbucketTokenHelper } from "./bitbucket-token-handler"; import { BitbucketTokenValidator } from "./bitbucket-token-validator"; +import { RepositoryService } from "../repohost/repo-service"; +import { BitbucketService } from "../prebuilds/bitbucket-service"; export const bitbucketContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -31,4 +33,5 @@ export const bitbucketContainerModule = new ContainerModule((bind, _unbind, _isB bind(BitbucketTokenHelper).toSelf().inSingletonScope(); bind(BitbucketTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(BitbucketTokenValidator); + rebind(RepositoryService).to(BitbucketService).inSingletonScope(); }); diff --git a/components/server/src/github/github-container-module.ts b/components/server/src/github/github-container-module.ts index 46ab7b9985f031..b6ba17e111ac67 100644 --- a/components/server/src/github/github-container-module.ts +++ b/components/server/src/github/github-container-module.ts @@ -16,6 +16,8 @@ import { GithubRepositoryProvider } from "./github-repository-provider"; import { GitHubTokenHelper } from "./github-token-helper"; import { IGitTokenValidator } from "../workspace/git-token-validator"; import { GitHubTokenValidator } from "./github-token-validator"; +import { RepositoryService } from "../repohost/repo-service"; +import { GitHubService } from "../prebuilds/github-service"; export const githubContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -32,4 +34,5 @@ export const githubContainerModule = new ContainerModule((bind, _unbind, _isBoun bind(GitHubTokenHelper).toSelf().inSingletonScope(); bind(GitHubTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(GitHubTokenValidator); + rebind(RepositoryService).to(GitHubService).inSingletonScope(); }); diff --git a/components/server/src/gitlab/gitlab-container-module.ts b/components/server/src/gitlab/gitlab-container-module.ts index b7234ef3f432fd..ffdbace30872c3 100644 --- a/components/server/src/gitlab/gitlab-container-module.ts +++ b/components/server/src/gitlab/gitlab-container-module.ts @@ -16,6 +16,8 @@ import { GitlabContextParser } from "./gitlab-context-parser"; import { GitlabRepositoryProvider } from "./gitlab-repository-provider"; import { GitLabTokenHelper } from "./gitlab-token-helper"; import { GitLabTokenValidator } from "./gitlab-token-validator"; +import { RepositoryService } from "../repohost/repo-service"; +import { GitlabService } from "../prebuilds/gitlab-service"; export const gitlabContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -31,4 +33,5 @@ export const gitlabContainerModule = new ContainerModule((bind, _unbind, _isBoun bind(GitLabTokenHelper).toSelf().inSingletonScope(); bind(GitLabTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(GitLabTokenValidator); + rebind(RepositoryService).to(GitlabService).inSingletonScope(); }); diff --git a/components/server/src/prebuilds/bitbucket-app.ts b/components/server/src/prebuilds/bitbucket-app.ts index 44a4140cd6e330..6dfce65b8f7fc6 100644 --- a/components/server/src/prebuilds/bitbucket-app.ts +++ b/components/server/src/prebuilds/bitbucket-app.ts @@ -5,11 +5,35 @@ */ import express from "express"; -import { postConstruct, injectable } from "inversify"; +import { postConstruct, injectable, inject } from "inversify"; +import { TeamDB, WebhookEventDB } from "@gitpod/gitpod-db/lib"; +import { User, CommitContext, CommitInfo, Project, WebhookEvent } from "@gitpod/gitpod-protocol"; +import { PrebuildManager } from "./prebuild-manager"; +import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { TokenService } from "../user/token-service"; +import { ContextParser } from "../workspace/context-parser-service"; +import { HostContextProvider } from "../auth/host-context-provider"; +import { RepoURL } from "../repohost"; +import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { UserService } from "../user/user-service"; +import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { URL } from "url"; +import { ProjectsService } from "../projects/projects-service"; +import { SubjectId } from "../auth/subject-id"; +import { runWithSubjectId } from "../util/request-context"; +import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; @injectable() export class BitbucketApp { - constructor() {} + constructor( + @inject(UserService) private readonly userService: UserService, + @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, + @inject(TeamDB) private readonly teamDB: TeamDB, + @inject(ContextParser) private readonly contextParser: ContextParser, + @inject(HostContextProvider) private readonly hostCtxProvider: HostContextProvider, + @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, + @inject(ProjectsService) private readonly projectService: ProjectsService, + ) {} private _router = express.Router(); public static path = "/apps/bitbucket/"; @@ -19,12 +43,39 @@ export class BitbucketApp { this._router.post("/", async (req, res) => { try { if (req.header("X-Event-Key") === "repo:push") { + const span = TraceContext.startSpan("BitbucketApp.handleEvent", {}); const secretToken = req.query["token"] as string; + const event = await this.webhookEvents.createEvent({ + type: "push", + status: "received", + rawEvent: JSON.stringify(req.body), + }); if (!secretToken) { + await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); throw new Error("No secretToken provided."); } - - console.warn("Bitbucket push event received, but not handling it"); + const user = await this.findUser({ span }, secretToken); + if (!user) { + // If the webhook installer is no longer found in Gitpod's DB + // we should send a UNAUTHORIZED signal. + res.statusCode = 401; + res.send(); + span.finish(); + await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); + return; + } + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + const data = toData(req.body); + if (data) { + await this.handlePushHook({ span }, data, user, event); + } + } catch (err) { + TraceContext.setError({ span }, err); + throw err; + } finally { + span.finish(); + } } else { console.warn(`Ignoring unsupported bitbucket event: ${req.header("X-Event-Key")}`); } @@ -37,7 +88,216 @@ export class BitbucketApp { }); } + private async findUser(ctx: TraceContext, secretToken: string): Promise { + const span = TraceContext.startSpan("BitbucketApp.findUser", ctx); + try { + span.setTag("secret-token", secretToken); + const [userid, tokenValue] = secretToken.split("|"); + const user = await this.userService.findUserById(userid, userid); + if (!user) { + throw new Error("No user found for " + secretToken + " found."); + } else if (!!user.blocked) { + throw new Error(`Blocked user ${user.id} tried to start prebuild.`); + } + const identity = user.identities.find((i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID); + if (!identity) { + throw new Error(`User ${user.id} has no identity for '${TokenService.GITPOD_AUTH_PROVIDER_ID}'.`); + } + const tokens = await this.userService.findTokensForIdentity(userid, identity); + const token = tokens.find((t) => t.token.value === tokenValue); + if (!token) { + throw new Error(`User ${user.id} has no token with given value.`); + } + return user; + } finally { + span.finish(); + } + } + + private async handlePushHook( + ctx: TraceContext, + data: ParsedRequestData, + user: User, + event: WebhookEvent, + ): Promise { + const span = TraceContext.startSpan("Bitbucket.handlePushHook", ctx); + try { + const cloneURL = data.gitCloneUrl; + const projects = await runWithSubjectId(SYSTEM_USER, () => + this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, cloneURL), + ); + for (const project of projects) { + try { + const projectOwner = await this.findProjectOwner(project, user); + + const contextURL = this.createContextUrl(data); + span.setTag("contextURL", contextURL); + const context = (await this.contextParser.handle({ span }, user, contextURL)) as CommitContext; + await this.webhookEvents.updateEvent(event.id, { + authorizedUserId: user.id, + projectId: project?.id, + cloneUrl: context.repository.cloneUrl, + branch: context.ref, + commit: context.revision, + }); + const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); + const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ + config, + project, + context, + }); + if (!prebuildPrecondition.shouldRun) { + log.info("Bitbucket push event: No prebuild.", { config, context }); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "ignored_unconfigured", + status: "processed", + message: prebuildPrecondition.reason, + }); + continue; + } + + await runWithSubjectId(SubjectId.fromUserId(projectOwner.id), async () => { + log.info("Starting prebuild.", { contextURL }); + const { host, owner, repo } = RepoURL.parseRepoUrl(data.repoUrl)!; + const hostCtx = this.hostCtxProvider.get(host); + let commitInfo: CommitInfo | undefined; + if (hostCtx?.services?.repositoryProvider) { + commitInfo = await hostCtx.services.repositoryProvider.getCommitInfo( + user, + owner, + repo, + data.commitHash, + ); + } + const ws = await this.prebuildManager.startPrebuild( + { span }, + { + user: projectOwner, + project, + context, + commitInfo, + }, + ); + if (!ws.done) { + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_triggered", + status: "processed", + prebuildId: ws.prebuildId, + }); + } + }); + } catch (error) { + log.error("Error processing Bitbucket webhook event", error); + } + } + } catch (e) { + console.error("Error processing Bitbucket webhook event", e); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_trigger_failed", + status: "processed", + }); + throw e; + } finally { + span.finish(); + } + } + + /** + * Finds the relevant user account and project to the provided webhook event information. + * + * First of all it tries to find the project for the given `cloneURL`, then it tries to + * find the installer, which is also supposed to be a team member. As a fallback, it + * looks for a team member which also has a bitbucket.org connection. + * + * @param cloneURL of the webhook event + * @param webhookInstaller the user account known from the webhook installation + * @returns a promise which resolves to a user account and an optional project. + */ + private async findProjectOwner(project: Project, webhookInstaller: User): Promise { + try { + if (!project.teamId) { + throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Project has no teamId"); + } + const teamMembers = await this.teamDB.findMembersByTeam(project.teamId); + if (teamMembers.some((t) => t.userId === webhookInstaller.id)) { + return webhookInstaller; + } + const hostContext = this.hostCtxProvider.get(new URL(project.cloneUrl).host); + const authProviderId = hostContext?.authProvider.authProviderId; + for (const teamMember of teamMembers) { + const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => + this.userService.findUserById(teamMember.userId, teamMember.userId), + ); + if (user && user.identities.some((i) => i.authProviderId === authProviderId)) { + return user; + } + } + } catch (err) { + log.info({ userId: webhookInstaller.id }, "Failed to find project and owner", err); + } + return webhookInstaller; + } + + private createContextUrl(data: ParsedRequestData) { + const contextUrl = `${data.repoUrl}/src/${data.commitHash}/?at=${encodeURIComponent(data.branchName)}`; + return contextUrl; + } + get router(): express.Router { return this._router; } } + +function toData(body: BitbucketPushHook): ParsedRequestData | undefined { + const branchName = body.push.changes[0]?.new?.name; + const commitHash = body.push.changes[0]?.new?.target?.hash; + if (!branchName || !commitHash) { + return undefined; + } + const result = { + branchName, + commitHash, + repoUrl: body.repository.links.html.href, + gitCloneUrl: body.repository.links.html.href + ".git", + }; + if (!result.commitHash || !result.repoUrl) { + console.error("Bitbucket push event: unexpected request body."); + throw new Error("Unexpected request body."); + } + return result; +} + +interface ParsedRequestData { + branchName: string; + repoUrl: string; + gitCloneUrl: string; + commitHash: string; +} + +interface BitbucketPushHook { + push: { + changes: { + new: { + name: string; // e.g. "foo/bar-bazz" + type: "branch" | string; + target: { + hash: string; // e.g. "1b283e4d7a849a89151548398cc836d15149179c" + }; + } | null; // in case where a branch is deleted + }[]; + }; + actor: { + account_id: string; // e.g. "557058:964d5de0-9ae8-47e7-9ca2-9448caeb50ea" + }; + repository: BitbucketRepository; +} + +interface BitbucketRepository { + links: { + html: { + href: string; //e.g. "https://bitbucket.org/sefftinge/sample-repository" + }; + }; + full_name: string; // e.g. "sefftinge/sample-repository", + is_private: boolean; +} diff --git a/components/server/src/prebuilds/bitbucket-server-service.spec.ts b/components/server/src/prebuilds/bitbucket-server-service.spec.ts new file mode 100644 index 00000000000000..9bfad03e73ca59 --- /dev/null +++ b/components/server/src/prebuilds/bitbucket-server-service.spec.ts @@ -0,0 +1,125 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/** + * Copyright (c) 2022 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 { User } from "@gitpod/gitpod-protocol"; +import { ifEnvVarNotSet } from "@gitpod/gitpod-protocol/lib/util/skip-if"; +import { Container, ContainerModule } from "inversify"; +import { retries, skip, suite, test, timeout } from "@testdeck/mocha"; +import { AuthProviderParams } from "../auth/auth-provider"; +import { HostContextProvider } from "../auth/host-context-provider"; +import { BitbucketServerApi } from "../bitbucket-server/bitbucket-server-api"; +import { BitbucketServerContextParser } from "../bitbucket-server/bitbucket-server-context-parser"; +import { BitbucketServerTokenHelper } from "../bitbucket-server/bitbucket-server-token-handler"; +import { TokenProvider } from "../user/token-provider"; +import { BitbucketServerService } from "./bitbucket-server-service"; +import { expect } from "chai"; +import { Config } from "../config"; +import { TokenService } from "../user/token-service"; +import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url"; + +@suite(timeout(10000), retries(1), skip(ifEnvVarNotSet("GITPOD_TEST_TOKEN_BITBUCKET_SERVER"))) +class TestBitbucketServerService { + protected service: BitbucketServerService; + protected user: User; + + static readonly AUTH_HOST_CONFIG: Partial = { + id: "MyBitbucketServer", + type: "BitbucketServer", + verified: true, + description: "", + icon: "", + host: "bitbucket.gitpod-self-hosted.com", + oauth: { + callBackUrl: "", + clientId: "not-used", + clientSecret: "", + tokenUrl: "", + scope: "", + authorizationUrl: "", + }, + }; + + public before() { + const container = new Container(); + container.load( + new ContainerModule((bind, unbind, isBound, rebind) => { + bind(BitbucketServerService).toSelf().inSingletonScope(); + bind(BitbucketServerContextParser).toSelf().inSingletonScope(); + bind(AuthProviderParams).toConstantValue(TestBitbucketServerService.AUTH_HOST_CONFIG); + bind(BitbucketServerTokenHelper).toSelf().inSingletonScope(); + bind(TokenService).toConstantValue({ + createGitpodToken: async () => ({ token: { value: "foobar123-token" } }), + } as any); + bind(Config).toConstantValue({ + hostUrl: new GitpodHostUrl("https://gitpod.io"), + }); + bind(TokenProvider).toConstantValue({ + getTokenForHost: async () => { + return { + value: process.env["GITPOD_TEST_TOKEN_BITBUCKET_SERVER"] || "undefined", + scopes: [], + }; + }, + }); + bind(BitbucketServerApi).toSelf().inSingletonScope(); + bind(HostContextProvider).toConstantValue({ + get: (hostname: string) => { + authProvider: { + ("BBS"); + } + }, + }); + }), + ); + this.service = container.get(BitbucketServerService); + this.user = { + creationDate: "", + id: "user1", + identities: [ + { + authId: "user1", + authName: "AlexTugarev", + authProviderId: "MyBitbucketServer", + }, + ], + }; + } + + @test async test_installAutomatedPrebuilds_ok() { + try { + await this.service.installAutomatedPrebuilds( + this.user, + "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123", + ); + } catch (error) { + expect.fail(error); + } + } + + @test async test_installAutomatedPrebuilds_unauthorized() { + try { + await this.service.installAutomatedPrebuilds( + this.user, + "https://bitbucket.gitpod-self-hosted.com/users/jldec/repos/test-repo", + ); + expect.fail("should have failed"); + } catch (error) {} + } + + @test async test_installAutomatedPrebuilds_in_project_ok() { + try { + await this.service.installAutomatedPrebuilds( + this.user, + "https://bitbucket.gitpod-self-hosted.com/projects/jldec/repos/jldec-repo-march-30", + ); + } catch (error) { + expect.fail(error); + } + } +} + +module.exports = new TestBitbucketServerService(); diff --git a/components/server/src/prebuilds/bitbucket-server-service.ts b/components/server/src/prebuilds/bitbucket-server-service.ts new file mode 100644 index 00000000000000..08f3a06c0f6e6e --- /dev/null +++ b/components/server/src/prebuilds/bitbucket-server-service.ts @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; +import { User } from "@gitpod/gitpod-protocol"; +import { inject, injectable } from "inversify"; +import { BitbucketServerApi } from "../bitbucket-server/bitbucket-server-api"; +import { BitbucketServerContextParser } from "../bitbucket-server/bitbucket-server-context-parser"; +import { Config } from "../config"; +import { TokenService } from "../user/token-service"; +import { BitbucketServerApp } from "./bitbucket-server-app"; + +@injectable() +export class BitbucketServerService extends RepositoryService { + static PREBUILD_TOKEN_SCOPE = "prebuilds"; + + constructor( + @inject(BitbucketServerApi) private readonly api: BitbucketServerApi, + @inject(Config) private readonly config: Config, + @inject(TokenService) private readonly tokenService: TokenService, + @inject(BitbucketServerContextParser) private readonly contextParser: BitbucketServerContextParser, + ) { + super(); + } + + async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { + const { owner, repoName, repoKind } = await this.contextParser.parseURL(user, cloneUrl); + + const existing = await this.api.getWebhooks(user, { + repoKind, + repositorySlug: repoName, + owner, + }); + const hookUrl = this.getHookUrl(); + if (existing.values && existing.values.some((hook) => hook.url && hook.url.indexOf(hookUrl) !== -1)) { + console.log(`BBS webhook already installed.`, { cloneUrl }); + return; + } + const tokenEntry = await this.tokenService.createGitpodToken( + user, + BitbucketServerService.PREBUILD_TOKEN_SCOPE, + cloneUrl, + ); + try { + await this.api.setWebhook( + user, + { repoKind, repositorySlug: repoName, owner }, + { + name: `Gitpod Prebuilds for ${this.config.hostUrl}.`, + active: true, + configuration: { + secret: "foobar123-secret", + }, + url: hookUrl + `?token=${encodeURIComponent(user.id + "|" + tokenEntry.token.value)}`, + events: ["repo:refs_changed"], + }, + ); + console.log("BBS: webhook installed.", { cloneUrl }); + } catch (error) { + console.error(`BBS: webhook installation failed.`, error, { cloneUrl, error }); + } + } + + protected getHookUrl() { + return this.config.hostUrl + .asPublicServices() + .with({ + pathname: BitbucketServerApp.path, + }) + .toString(); + } +} diff --git a/components/server/src/prebuilds/bitbucket-service.ts b/components/server/src/prebuilds/bitbucket-service.ts new file mode 100644 index 00000000000000..c3f65870982ec0 --- /dev/null +++ b/components/server/src/prebuilds/bitbucket-service.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; +import { User } from "@gitpod/gitpod-protocol"; +import { inject, injectable } from "inversify"; +import { BitbucketApiFactory } from "../bitbucket/bitbucket-api-factory"; +import { BitbucketApp } from "./bitbucket-app"; +import { Config } from "../config"; +import { TokenService } from "../user/token-service"; +import { BitbucketContextParser } from "../bitbucket/bitbucket-context-parser"; + +@injectable() +export class BitbucketService extends RepositoryService { + static PREBUILD_TOKEN_SCOPE = "prebuilds"; + + constructor( + @inject(BitbucketApiFactory) private readonly api: BitbucketApiFactory, + @inject(Config) private readonly config: Config, + @inject(TokenService) private readonly tokenService: TokenService, + @inject(BitbucketContextParser) private readonly bitbucketContextParser: BitbucketContextParser, + ) { + super(); + } + + async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { + try { + const api = await this.api.create(user); + const { owner, repoName } = await this.bitbucketContextParser.parseURL(user, cloneUrl); + const existing = await api.repositories.listWebhooks({ + repo_slug: repoName, + workspace: owner, + }); + const hookUrl = this.getHookUrl(); + if ( + existing.data.values && + existing.data.values.some((hook) => hook.url && hook.url.indexOf(hookUrl) !== -1) + ) { + console.log(`bitbucket webhook already installed on ${owner}/${repoName}`); + return; + } + const tokenEntry = await this.tokenService.createGitpodToken( + user, + BitbucketService.PREBUILD_TOKEN_SCOPE, + cloneUrl, + ); + const response = await api.repositories.createWebhook({ + repo_slug: repoName, + workspace: owner, + // see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/hooks#post + _body: { + description: `Gitpod Prebuilds for ${this.config.hostUrl}.`, + url: hookUrl + `?token=${user.id + "|" + tokenEntry.token.value}`, + active: true, + events: ["repo:push"], + }, + }); + if (response.status !== 201) { + throw new Error(`Couldn't install webhook for ${cloneUrl}: ${response.status}`); + } + console.log("Installed Bitbucket Webhook for " + cloneUrl); + } catch (error) { + console.error("Failed to install Bitbucket webhook for " + cloneUrl, error); + } + } + + protected getHookUrl() { + return this.config.hostUrl + .asPublicServices() + .with({ + pathname: BitbucketApp.path, + }) + .toString(); + } +} diff --git a/components/server/src/prebuilds/github-app.ts b/components/server/src/prebuilds/github-app.ts index 1081f70df41544..97f4cc72f67149 100644 --- a/components/server/src/prebuilds/github-app.ts +++ b/components/server/src/prebuilds/github-app.ts @@ -9,16 +9,41 @@ import { getPrivateKey } from "@probot/get-private-key"; import * as fs from "fs-extra"; import { injectable, inject } from "inversify"; import { Config } from "../config"; -import { AppInstallationDB, UserDB, ProjectDB } from "@gitpod/gitpod-db/lib"; +import { + AppInstallationDB, + TracedWorkspaceDB, + DBWithTracing, + UserDB, + WorkspaceDB, + ProjectDB, + TeamDB, + WebhookEventDB, +} from "@gitpod/gitpod-db/lib"; import express from "express"; -import { log, LogrusLogLevel } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { log, LogContext, LogrusLogLevel } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { + WorkspaceConfig, + User, + Project, + StartPrebuildResult, + CommitContext, + CommitInfo, +} from "@gitpod/gitpod-protocol"; +import { GithubAppRules } from "./github-app-rules"; +import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { PrebuildManager } from "./prebuild-manager"; import { PrebuildStatusMaintainer } from "./prebuilt-status-maintainer"; import { Options, ApplicationFunctionOptions } from "probot/lib/types"; import { asyncHandler } from "../express-util"; +import { ContextParser } from "../workspace/context-parser-service"; +import { HostContextProvider } from "../auth/host-context-provider"; +import { RepoURL } from "../repohost"; import { ApplicationError, ErrorCode } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { UserService } from "../user/user-service"; import { ProjectsService } from "../projects/projects-service"; import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; import { runWithSubjectId, runWithRequestContext } from "../util/request-context"; +import { SubjectId } from "../auth/subject-id"; /** * GitHub app urls: @@ -36,8 +61,16 @@ export class GithubApp { @inject(Config) private readonly config: Config, @inject(PrebuildStatusMaintainer) private readonly statusMaintainer: PrebuildStatusMaintainer, @inject(ProjectDB) private readonly projectDB: ProjectDB, + @inject(TeamDB) private readonly teamDB: TeamDB, @inject(UserDB) private readonly userDB: UserDB, @inject(AppInstallationDB) private readonly appInstallationDB: AppInstallationDB, + @inject(UserService) private readonly userService: UserService, + @inject(TracedWorkspaceDB) private readonly workspaceDB: DBWithTracing, + @inject(GithubAppRules) private readonly appRules: GithubAppRules, + @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, + @inject(ContextParser) private readonly contextParser: ContextParser, + @inject(HostContextProvider) private readonly hostCtxProvider: HostContextProvider, + @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, @inject(ProjectsService) private readonly projectService: ProjectsService, ) { if (config.githubApp?.enabled) { @@ -75,9 +108,11 @@ export class GithubApp { // Backward-compatibility: Redirect old badge URLs (e.g. "/api/apps/github/pbs/github.com/gitpod-io/gitpod/5431d5735c32ab7d5d840a4d1a7d7c688d1f0ce9.svg") options.getRouter && - options.getRouter("/pbs").get("/*", (req: express.Request, res: express.Response) => { - res.redirect(301, this.getBadgeImageURL()); - }); + options + .getRouter("/pbs") + .get("/*", (req: express.Request, res: express.Response, next: express.NextFunction) => { + res.redirect(301, this.getBadgeImageURL()); + }); app.on("installation.created", (ctx: Context<"installation.created">) => { handleEvent(ctx.name, async () => { @@ -137,6 +172,14 @@ export class GithubApp { // TODO(at): handle deleted as well }); + app.on("push", (ctx: Context<"push">) => { + handleEvent(ctx.name, () => this.handlePushEvent(ctx)); + }); + + app.on(["pull_request.opened", "pull_request.synchronize", "pull_request.reopened"], (ctx) => { + handleEvent(ctx.name, () => this.handlePullRequest(ctx)); + }); + options.getRouter && options.getRouter("/reconfigure").get( "/", @@ -180,9 +223,445 @@ export class GithubApp { }); } + private async findProjectOwner(project: Project, installationOwner: User): Promise { + const user = await this.selectUserForPrebuild(installationOwner, project); + if (!user) { + log.info(`Did not find user for installation. Probably an incomplete app installation.`, { + repo: project.cloneUrl, + project, + }); + return installationOwner; + } + return user; + } + + private async handlePushEvent(ctx: Context<"push">): Promise { + const span = TraceContext.startSpan("GithubApp.handlePushEvent", {}); + span.setTag("request", ctx.id); + + // trim commits to avoid DB pollution + // https://github.com/gitpod-io/gitpod/issues/11578 + ctx.payload.head_commit = null; + + const event = await this.webhookEvents.createEvent({ + type: "push", + status: "received", + rawEvent: JSON.stringify(ctx.payload), + }); + + try { + const installationId = ctx.payload.installation?.id; + const installationOwner = await this.findInstallationOwner(installationId); + if (!installationOwner) { + log.info("Did not find user for installation. Probably an incomplete app installation.", { + repo: ctx.payload.repository, + installationId, + }); + return; + } + if (!!installationOwner.blocked) { + log.info(`Blocked user tried to start prebuild`, { repo: ctx.payload.repository }); + await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); + return; + } + const pl = ctx.payload; + const branch = this.getBranchFromRef(pl.ref); + if (!branch) { + // This is a valid case if we receive a tag push, for instance. + log.debug("Unable to get branch from ref", { ref: pl.ref }); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "ignored_unconfigured", + status: "processed", + }); + return; + } + const logCtx: LogContext = { userId: installationOwner.id }; + + const repo = pl.repository; + const contextURL = `${repo.html_url}/tree/${branch}`; + span.setTag("contextURL", contextURL); + const context = (await this.contextParser.handle({ span }, installationOwner, contextURL)) as CommitContext; + const projects = await runWithSubjectId(SYSTEM_USER, async () => + this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, context.repository.cloneUrl), + ); + for (const project of projects) { + try { + const user = await this.findProjectOwner(project, installationOwner); + const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); + + await this.webhookEvents.updateEvent(event.id, { + authorizedUserId: user.id, + projectId: project.id, + cloneUrl: context.repository.cloneUrl, + branch: context.ref, + commit: context.revision, + }); + const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ + config, + project, + context, + }); + + if (!prebuildPrecondition.shouldRun) { + const reason = `GitHub push event: No prebuild.`; + log.debug(logCtx, reason, { contextURL }); + span.log({ "not-running": reason, config: config }); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "ignored_unconfigured", + status: "processed", + message: prebuildPrecondition.reason, + }); + continue; + } + + await runWithSubjectId(SubjectId.fromUserId(user.id), async () => { + const commitInfo = await this.getCommitInfo(user, repo.html_url, ctx.payload.after); + this.prebuildManager + .startPrebuild({ span }, { user, context, project: project!, commitInfo }) + .then(async (result) => { + if (!result.done) { + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_triggered", + status: "processed", + prebuildId: result.prebuildId, + }); + } + }) + .catch(async (err) => { + log.error(logCtx, "Error while starting prebuild", err, { contextURL }); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_trigger_failed", + status: "processed", + }); + }); + }); + } catch (error) { + log.error("Error processing GitHub webhook event", error); + } + } + } catch (e) { + TraceContext.setError({ span }, e); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_trigger_failed", + status: "processed", + }); + throw e; + } finally { + span.finish(); + } + } + + private async getCommitInfo(user: User, repoURL: string, commitSHA: string) { + const parsedRepo = RepoURL.parseRepoUrl(repoURL)!; + const hostCtx = this.hostCtxProvider.get(parsedRepo.host); + let commitInfo: CommitInfo | undefined; + if (hostCtx?.services?.repositoryProvider) { + commitInfo = await hostCtx?.services?.repositoryProvider.getCommitInfo( + user, + parsedRepo.owner, + parsedRepo.repo, + commitSHA, + ); + } + return commitInfo; + } + + private getBranchFromRef(ref: string): string | undefined { + const headsPrefix = "refs/heads/"; + if (ref.startsWith(headsPrefix)) { + return ref.substring(headsPrefix.length); + } + + return undefined; + } + + private async handlePullRequest( + ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, + ): Promise { + const span = TraceContext.startSpan("GithubApp.handlePullRequest", {}); + span.setTag("request", ctx.id); + + const event = await this.webhookEvents.createEvent({ + type: ctx.name, + status: "received", + rawEvent: JSON.stringify(ctx.payload), + }); + + try { + const installationId = ctx.payload.installation?.id; + const cloneURL = ctx.payload.repository.clone_url; + // we are only interested in PRs that want to contribute to our repo + if (ctx.payload.pull_request?.base?.repo?.clone_url !== cloneURL) { + log.info("Ignoring inverse PR", ctx.payload.pull_request); + return; + } + const pr = ctx.payload.pull_request; + const contextURL = pr.html_url; + const installationOwner = await this.findInstallationOwner(installationId); + if (!installationOwner) { + log.info("Did not find user for installation. Probably an incomplete app installation.", { + repo: ctx.payload.repository, + installationId, + }); + return; + } + const context = (await this.contextParser.handle({ span }, installationOwner, contextURL)) as CommitContext; + + const projects = await runWithSubjectId(SubjectId.fromUserId(installationOwner.id), () => + this.projectService.findProjectsByCloneUrl(installationOwner.id, context.repository.cloneUrl), + ); + for (const project of projects) { + const user = await this.findProjectOwner(project, installationOwner); + + const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); + + await this.webhookEvents.updateEvent(event.id, { + authorizedUserId: user.id, + projectId: project?.id, + cloneUrl: context.repository.cloneUrl, + branch: context.ref, + commit: context.revision, + }); + + const prebuildStartResult = await this.onPrStartPrebuild({ span }, ctx, config, context, user, project); + if (prebuildStartResult) { + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_triggered", + status: "processed", + prebuildId: prebuildStartResult.prebuildId, + }); + + await this.onPrAddCheck({ span }, config, ctx, prebuildStartResult); + this.onPrAddBadge(config, ctx); + await this.onPrAddComment(config, ctx); + } else { + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "ignored_unconfigured", + status: "processed", + }); + } + } + } catch (e) { + TraceContext.setError({ span }, e); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_trigger_failed", + status: "processed", + }); + throw e; + } finally { + span.finish(); + } + } + + private async onPrAddCheck( + tracecContext: TraceContext, + config: WorkspaceConfig | undefined, + ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, + start: StartPrebuildResult, + ) { + if (!start) { + return; + } + + if (!this.appRules.shouldDo(config, "addCheck")) { + return; + } + + const span = TraceContext.startSpan("onPrAddCheck", tracecContext); + try { + const pws = await this.workspaceDB.trace({ span }).findPrebuildByWorkspaceID(start.wsid); + if (!pws) { + return; + } + + const installationId = ctx.payload.installation?.id; + if (!installationId) { + log.info("Did not find user for installation. Probably an incomplete app installation.", { + repo: ctx.payload.repository, + installationId, + }); + return; + } + await this.statusMaintainer.registerCheckRun( + { span }, + installationId, + pws, + { + ...ctx.repo(), + head_sha: ctx.payload.pull_request.head.sha, + details_url: this.config.hostUrl.withContext(ctx.payload.pull_request.html_url).toString(), + }, + config, + ); + } catch (err) { + TraceContext.setError({ span }, err); + throw err; + } finally { + span.finish(); + } + } + + private async onPrStartPrebuild( + tracecContext: TraceContext, + ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, + config: WorkspaceConfig, + context: CommitContext, + user: User, + project: Project, + ): Promise { + const pr = ctx.payload.pull_request; + const contextURL = pr.html_url; + + const isFork = pr.head.repo.id !== pr.base.repo.id; + if (isFork) { + log.debug({ userId: user.id }, `GitHub PR event from fork.`, { + contextURL, + userId: user.id, + project, + isFork, + }); + return; + } + + const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ config, project, context }); + if (prebuildPrecondition.shouldRun) { + const result = await runWithSubjectId(SubjectId.fromUserId(user.id), async () => { + const commitInfo = await this.getCommitInfo(user, ctx.payload.repository.html_url, pr.head.sha); + return await this.prebuildManager.startPrebuild(tracecContext, { + user, + context, + project: project!, + commitInfo, + }); + }); + if (result?.done) { + return undefined; + } + return result; + } else { + log.debug({ userId: user.id }, `GitHub push event: No prebuild.`, { + contextURL, + userId: user.id, + project, + }); + return; + } + } + + private onPrAddBadge( + config: WorkspaceConfig | undefined, + ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, + ) { + if (!this.appRules.shouldDo(config, "addBadge")) { + // we shouldn't add (or update) a button here + return; + } + + const pr = ctx.payload.pull_request; + const contextURL = pr.html_url; + const body: string | null = pr.body; + if (!body) { + return; + } + const button = ``; + if (body.includes(button)) { + // the button is already in the comment + return; + } + + const newBody = body + `\n\n${button}\n\n`; + const updatePrPromise = ctx.octokit.pulls.update({ ...ctx.repo(), pull_number: pr.number, body: newBody }); + updatePrPromise.catch((err) => log.error("Error while updating PR body", err, { contextURL })); + } + + private async onPrAddComment( + config: WorkspaceConfig | undefined, + ctx: Context<"pull_request.opened" | "pull_request.synchronize" | "pull_request.reopened">, + ) { + if (!this.appRules.shouldDo(config, "addComment")) { + return; + } + + const pr = ctx.payload.pull_request; + const contextURL = pr.html_url; + const button = ``; + const comments = await ctx.octokit.issues.listComments(ctx.issue()); + const existingComment = comments.data.find((c: any) => c.body.indexOf(button) > -1); + if (existingComment) { + return; + } + + const newComment = ctx.issue({ body: `\n\n${button}\n\n` }); + const newCommentPromise = ctx.octokit.issues.createComment(newComment); + newCommentPromise.catch((err) => log.error("Error while adding new PR comment", err, { contextURL })); + } + private getBadgeImageURL(): string { return this.config.hostUrl.with({ pathname: "/button/open-in-gitpod.svg" }).toString(); } + + /** + * Finds the relevant user account to create a prebuild with. + * + * First it tries to find the installer of the GitHub App installation + * among the members of the project team. As a fallback, it tries so pick + * any of the team members which also has a github.com connection. + * + * For projects under a personal account, it simply returns the installer. + * + * @param installationOwner given user account of the GitHub App installation + * @param project the project associated with the `cloneURL` + * @returns a promise that resolves to a `User` or undefined + */ + private async selectUserForPrebuild(installationOwner?: User, project?: Project): Promise { + if (!project) { + return installationOwner; + } + if (!project.teamId) { + return installationOwner; + } + const teamMembers = await this.teamDB.findMembersByTeam(project.teamId); + if (!!installationOwner && teamMembers.some((t) => t.userId === installationOwner.id)) { + return installationOwner; + } + for (const teamMember of teamMembers) { + const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => + this.userService.findUserById(teamMember.userId, teamMember.userId), + ); + if (user && user.identities.some((i) => i.authProviderId === "Public-GitHub")) { + return user; + } + } + } + + /** + * + * @param installationId read from webhook event + * @returns the user account of the GitHub App installation + */ + private async findInstallationOwner(installationId?: number): Promise { + if (!installationId) { + return; + } + // Legacy mode + // + const installation = await this.appInstallationDB.findInstallation("github", String(installationId)); + if (!installation) { + return; + } + + const ownerID = installation.ownerUserID || "this-should-never-happen"; + const user = await this.userService.findUserById(ownerID, ownerID); + if (!user) { + return; + } + + return user; + } } function handleEvent(eventName: string, p: () => Promise): void { diff --git a/components/server/src/prebuilds/github-enterprise-app.ts b/components/server/src/prebuilds/github-enterprise-app.ts index a41b4b2d55db72..6f802a4642a2db 100644 --- a/components/server/src/prebuilds/github-enterprise-app.ts +++ b/components/server/src/prebuilds/github-enterprise-app.ts @@ -5,13 +5,38 @@ */ import express from "express"; -import { postConstruct, injectable } from "inversify"; +import { createHmac, timingSafeEqual } from "crypto"; +import { Buffer } from "buffer"; +import { postConstruct, injectable, inject } from "inversify"; +import { TeamDB, WebhookEventDB } from "@gitpod/gitpod-db/lib"; +import { PrebuildManager } from "./prebuild-manager"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { TokenService } from "../user/token-service"; +import { HostContextProvider } from "../auth/host-context-provider"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { CommitContext, CommitInfo, Project, User, WebhookEvent } from "@gitpod/gitpod-protocol"; +import { GitHubService } from "./github-service"; +import { URL } from "url"; +import { ContextParser } from "../workspace/context-parser-service"; +import { RepoURL } from "../repohost"; +import { UserService } from "../user/user-service"; +import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { ProjectsService } from "../projects/projects-service"; +import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; +import { runWithSubjectId } from "../util/request-context"; +import { SubjectId } from "../auth/subject-id"; @injectable() export class GitHubEnterpriseApp { - constructor() {} + constructor( + @inject(UserService) private readonly userService: UserService, + @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, + @inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider, + @inject(TeamDB) private readonly teamDB: TeamDB, + @inject(ContextParser) private readonly contextParser: ContextParser, + @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, + @inject(ProjectsService) private readonly projectService: ProjectsService, + ) {} private _router = express.Router(); public static path = "/apps/ghe/"; @@ -24,9 +49,35 @@ export class GitHubEnterpriseApp { const payload = req.body as GitHubEnterprisePushPayload; const span = TraceContext.startSpan("GitHubEnterpriseApp.handleEvent", {}); span.setTag("payload", payload); + const event = await this.webhookEvents.createEvent({ + type: "push", + status: "received", + rawEvent: JSON.stringify(req.body), + }); - log.debug("GitHub Enterprise push event received, ignoring it", { event }); - span.finish(); + let user: User | undefined; + try { + user = await this.findUser({ span }, payload, req); + } catch (error) { + TraceContext.setError({ span }, error); + log.error("Cannot find user.", error, {}); + } + if (!user) { + res.statusCode = 401; + res.send("Unauthorized: Cannot find authorized user."); + span.finish(); + await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); + return; + } + + try { + await this.handlePushHook({ span }, payload, user, event); + } catch (err) { + TraceContext.setError({ span }, err); + throw err; + } finally { + span.finish(); + } } else { log.info("Unknown GitHub Enterprise event received", { event }); } @@ -34,6 +85,227 @@ export class GitHubEnterpriseApp { }); } + private async findUser( + ctx: TraceContext, + payload: GitHubEnterprisePushPayload, + req: express.Request, + ): Promise { + const span = TraceContext.startSpan("GitHubEnterpriseApp.findUser", ctx); + try { + let host = req.header("X-Github-Enterprise-Host"); + if (!host) { + // If the GitHub installation doesn't identify itself, we fall back to the hostname from the repository URL. + const repoUrl = new URL(payload.repository.url); + host = repoUrl.hostname; + } + const hostContext = this.hostContextProvider.get(host || ""); + if (!hostContext) { + throw new Error("Unsupported GitHub Enterprise host: " + host); + } + const cloneURL = payload.repository.clone_url; + const projectOwners = await this.findProjectOwners(cloneURL); + if (!projectOwners) { + throw new Error("No project found."); + } + for (const user of projectOwners.users) { + const gitpodIdentity = user.identities.find( + (i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID, + ); + if (!gitpodIdentity) { + continue; + } + // Verify the webhook signature + const signature = req.header("X-Hub-Signature-256"); + const body = (req as any).rawBody; + const tokenEntries = (await this.userService.findTokensForIdentity(user.id, gitpodIdentity)).filter( + (tokenEntry) => { + return tokenEntry.token.scopes.includes(GitHubService.PREBUILD_TOKEN_SCOPE); + }, + ); + const signatureMatched = tokenEntries.some((tokenEntry) => { + const sig = + "sha256=" + + createHmac("sha256", user.id + "|" + tokenEntry.token.value) + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + .update(body) + .digest("hex"); + return timingSafeEqual(Buffer.from(sig), Buffer.from(signature ?? "")); + }); + if (signatureMatched) { + if (!!user.blocked) { + throw new Error(`Blocked user ${user.id} tried to start prebuild.`); + } + return user; + } + } + } finally { + span.finish(); + } + } + + private async handlePushHook( + ctx: TraceContext, + payload: GitHubEnterprisePushPayload, + user: User, + event: WebhookEvent, + ): Promise { + const span = TraceContext.startSpan("GitHubEnterpriseApp.handlePushHook", ctx); + try { + const cloneURL = payload.repository.clone_url; + const contextURL = this.createContextUrl(payload); + const context = (await this.contextParser.handle({ span }, user, contextURL)) as CommitContext; + const projects = await runWithSubjectId(SYSTEM_USER, () => + this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, context.repository.cloneUrl), + ); + span.setTag("contextURL", contextURL); + for (const project of projects) { + try { + const projectOwner = await this.findProjectOwner(project, user); + + await this.webhookEvents.updateEvent(event.id, { + authorizedUserId: user.id, + projectId: project.id, + cloneUrl: cloneURL, + branch: context.ref, + commit: context.revision, + }); + + const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); + const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ + config, + project, + context, + }); + + if (!prebuildPrecondition.shouldRun) { + log.info("GitHub Enterprise push event: No prebuild.", { config, context }); + + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "ignored_unconfigured", + status: "processed", + message: prebuildPrecondition.reason, + }); + continue; + } + + log.debug("GitHub Enterprise push event: Starting prebuild.", { contextURL }); + + await runWithSubjectId(SubjectId.fromUserId(projectOwner.id), async () => { + const commitInfo = await this.getCommitInfo(user, payload.repository.url, payload.after); + const ws = await this.prebuildManager.startPrebuild( + { span }, + { + context, + user: projectOwner, + project: project, + commitInfo, + }, + ); + if (!ws.done) { + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_triggered", + status: "processed", + prebuildId: ws.prebuildId, + }); + } + }); + } catch (error) { + log.error("Error processing GitHub webhook event", error); + } + } + } catch (e) { + log.error("Error processing GitHub Enterprise webhook event.", e); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_trigger_failed", + status: "processed", + }); + } finally { + span.finish(); + } + } + + private async getCommitInfo(user: User, repoURL: string, commitSHA: string) { + const parsedRepo = RepoURL.parseRepoUrl(repoURL)!; + const hostCtx = this.hostContextProvider.get(parsedRepo.host); + let commitInfo: CommitInfo | undefined; + if (hostCtx?.services?.repositoryProvider) { + commitInfo = await hostCtx?.services?.repositoryProvider.getCommitInfo( + user, + parsedRepo.owner, + parsedRepo.repo, + commitSHA, + ); + } + return commitInfo; + } + + private async findProjectOwner(project: Project, webhookInstaller: User): Promise { + try { + if (!project.teamId) { + throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Project has no teamId."); + } + const teamMembers = await this.teamDB.findMembersByTeam(project.teamId || ""); + if (teamMembers.some((t) => t.userId === webhookInstaller.id)) { + return webhookInstaller; + } + const hostContext = this.hostContextProvider.get(new URL(project.cloneUrl).host); + const authProviderId = hostContext?.authProvider.authProviderId; + for (const teamMember of teamMembers) { + const user = await runWithSubjectId(SubjectId.fromUserId(webhookInstaller.id), () => + this.userService.findUserById(webhookInstaller.id, teamMember.userId), + ); + if (user && user.identities.some((i) => i.authProviderId === authProviderId)) { + return user; + } + } + } catch (err) { + log.info({ userId: webhookInstaller.id }, "Failed to find project and owner", err); + } + return webhookInstaller; + } + + private async findProjectOwners(cloneURL: string): Promise<{ users: User[]; project: Project } | undefined> { + try { + const projects = await runWithSubjectId(SYSTEM_USER, async () => + this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, cloneURL), + ); + const project = projects[0]; + if (project) { + const users = []; + const owners = (await this.teamDB.findMembersByTeam(project.teamId || "")).filter( + (m) => m.role === "owner", + ); + const hostContext = this.hostContextProvider.get(new URL(cloneURL).host); + const authProviderId = hostContext?.authProvider.authProviderId; + for (const teamMember of owners) { + const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => + this.userService.findUserById(teamMember.userId, teamMember.userId), + ); + if (user && user.identities.some((i) => i.authProviderId === authProviderId)) { + users.push(user); + } + } + return { users, project }; + } + } catch (err) { + log.info("Failed to find project and owner", err); + } + return undefined; + } + + private getBranchFromRef(ref: string): string | undefined { + const headsPrefix = "refs/heads/"; + if (ref.startsWith(headsPrefix)) { + return ref.substring(headsPrefix.length); + } + + return undefined; + } + + private createContextUrl(payload: GitHubEnterprisePushPayload) { + return `${payload.repository.url}/tree/${this.getBranchFromRef(payload.ref)}`; + } + get router(): express.Router { return this._router; } diff --git a/components/server/src/prebuilds/github-service.ts b/components/server/src/prebuilds/github-service.ts new file mode 100644 index 00000000000000..9d338df2b3b24e --- /dev/null +++ b/components/server/src/prebuilds/github-service.ts @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; +import { inject, injectable } from "inversify"; +import { GitHubApiError, GitHubRestApi } from "../github/api"; +import { GitHubEnterpriseApp } from "./github-enterprise-app"; +import { GithubContextParser } from "../github/github-context-parser"; +import { User } from "@gitpod/gitpod-protocol"; +import { Config } from "../config"; +import { TokenService } from "../user/token-service"; +import { RepoURL } from "../repohost"; +import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { UnauthorizedError } from "../errors"; +import { GitHubScope } from "../github/scopes"; +import { containsScopes } from "./token-scopes-inclusion"; + +@injectable() +export class GitHubService extends RepositoryService { + static PREBUILD_TOKEN_SCOPE = "prebuilds"; + + constructor( + @inject(GitHubRestApi) protected readonly githubApi: GitHubRestApi, + @inject(Config) private readonly config: Config, + @inject(TokenService) private readonly tokenService: TokenService, + @inject(GithubContextParser) private readonly githubContextParser: GithubContextParser, + ) { + super(); + } + + async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { + const parsedRepoUrl = RepoURL.parseRepoUrl(cloneUrl); + if (!parsedRepoUrl) { + throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Clone URL not parseable.`); + } + let tokenEntry; + try { + const { owner, repoName: repo } = await this.githubContextParser.parseURL(user, cloneUrl); + const webhooks = (await this.githubApi.run(user, (gh) => gh.repos.listWebhooks({ owner, repo }))).data; + for (const webhook of webhooks) { + if (webhook.config.url === this.getHookUrl()) { + await this.githubApi.run(user, (gh) => + gh.repos.deleteWebhook({ owner, repo, hook_id: webhook.id }), + ); + } + } + tokenEntry = await this.tokenService.createGitpodToken(user, GitHubService.PREBUILD_TOKEN_SCOPE, cloneUrl); + const config = { + url: this.getHookUrl(), + content_type: "json", + secret: user.id + "|" + tokenEntry.token.value, + }; + await this.githubApi.run(user, (gh) => gh.repos.createWebhook({ owner, repo, config })); + } catch (error) { + // Hint: here we catch all GH API errors to forward them as Unauthorized to FE, + // eventually that should be done depending on the error code. + // Also, if user is not connected at all, then the GH API wrapper is throwing + // the same error type, but with `providerIsConnected: false`. + + if (GitHubApiError.is(error)) { + // TODO check for `error.code` + throw UnauthorizedError.create({ + host: parsedRepoUrl.host, + providerType: "GitHub", + repoName: parsedRepoUrl.repo, + requiredScopes: GitHubScope.Requirements.PRIVATE_REPO, + providerIsConnected: true, + isMissingScopes: containsScopes(tokenEntry?.token.scopes, GitHubScope.Requirements.PRIVATE_REPO), + }); + } + throw error; + } + } + + protected getHookUrl() { + return this.config.hostUrl + .asPublicServices() + .with({ + pathname: GitHubEnterpriseApp.path, + }) + .toString(); + } +} diff --git a/components/server/src/prebuilds/gitlab-app.ts b/components/server/src/prebuilds/gitlab-app.ts index 557bdbfa1cabf8..3432aabd6d1e04 100644 --- a/components/server/src/prebuilds/gitlab-app.ts +++ b/components/server/src/prebuilds/gitlab-app.ts @@ -5,12 +5,35 @@ */ import express from "express"; -import { postConstruct, injectable } from "inversify"; +import { postConstruct, injectable, inject } from "inversify"; +import { TeamDB, WebhookEventDB } from "@gitpod/gitpod-db/lib"; +import { Project, User, CommitContext, CommitInfo, WebhookEvent } from "@gitpod/gitpod-protocol"; +import { PrebuildManager } from "./prebuild-manager"; +import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { TokenService } from "../user/token-service"; +import { HostContextProvider } from "../auth/host-context-provider"; +import { GitlabService } from "./gitlab-service"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { ContextParser } from "../workspace/context-parser-service"; +import { RepoURL } from "../repohost"; +import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { UserService } from "../user/user-service"; +import { ProjectsService } from "../projects/projects-service"; +import { runWithSubjectId } from "../util/request-context"; +import { SubjectId } from "../auth/subject-id"; +import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; @injectable() export class GitLabApp { - constructor() {} + constructor( + @inject(UserService) private readonly userService: UserService, + @inject(PrebuildManager) private readonly prebuildManager: PrebuildManager, + @inject(HostContextProvider) private readonly hostCtxProvider: HostContextProvider, + @inject(TeamDB) private readonly teamDB: TeamDB, + @inject(ContextParser) private readonly contextParser: ContextParser, + @inject(WebhookEventDB) private readonly webhookEvents: WebhookEventDB, + @inject(ProjectsService) private readonly projectService: ProjectsService, + ) {} private _router = express.Router(); public static path = "/apps/gitlab/"; @@ -37,18 +60,220 @@ export class GitLabApp { // https://github.com/gitpod-io/gitpod/issues/11578 context.commits = []; + const event = await this.webhookEvents.createEvent({ + type: "push", + status: "received", + rawEvent: JSON.stringify(req.body), + }); + if (eventType !== "Push Hook" || !secretToken) { log.warn("Unhandled GitLab event.", { event: eventType, secretToken: !!secretToken }); res.status(200).send("Unhandled event."); + await this.webhookEvents.updateEvent(event.id, { status: "ignored" }); return; - } else if (eventType === "Push Hook") { - log.debug("GitLab push event received, but not handling it", { event: eventType, context }); - res.status(200).send("Unhandled event."); + } + + const span = TraceContext.startSpan("GitLapApp.handleEvent", {}); + span.setTag("request", context); + log.debug("GitLab push event received.", { event: eventType, context }); + let user: User | undefined; + try { + user = await this.findUser({ span }, context, secretToken); + } catch (error) { + log.error("Cannot find user.", error, { context }); + TraceContext.setError({ span }, error); + } + if (!user) { + // webhooks are not supposed to return 4xx codes on application issues. + // sending "Unauthorized" as content to support inspection of webhook delivery logs. + span.finish(); + res.status(200).send("Unauthorized."); + await this.webhookEvents.updateEvent(event.id, { status: "dismissed_unauthorized" }); + // TODO(at) explore ways to mark a project having issues with permissions. return; } + /** no await */ this.handlePushHook({ span }, context, user, event).catch((error) => { + console.error(`Couldn't handle request.`, error, { headers: req.headers }); + TraceContext.setError({ span }, error); + }); + + span.finish(); + res.status(201).send("Prebuild request handled."); }); } + private async findUser(ctx: TraceContext, context: GitLabPushHook, secretToken: string): Promise { + const span = TraceContext.startSpan("GitLapApp.findUser", ctx); + try { + const [userid, tokenValue] = secretToken.split("|"); + const user = await this.userService.findUserById(userid, userid); + if (!user) { + throw new Error("No user found for " + secretToken + " found."); + } else if (!!user.blocked) { + throw new Error(`Blocked user ${user.id} tried to start prebuild.`); + } + const identity = user.identities.find((i) => i.authProviderId === TokenService.GITPOD_AUTH_PROVIDER_ID); + if (!identity) { + throw new Error(`User ${user.id} has no identity for '${TokenService.GITPOD_AUTH_PROVIDER_ID}'.`); + } + const tokens = await this.userService.findTokensForIdentity(userid, identity); + const token = tokens.find((t) => t.token.value === tokenValue); + if (!token) { + throw new Error(`User ${user.id} has no token with given value.`); + } + if ( + token.token.scopes.indexOf(GitlabService.PREBUILD_TOKEN_SCOPE) === -1 || + token.token.scopes.indexOf(context.repository.git_http_url) === -1 + ) { + throw new Error( + `The provided token is not valid for the repository ${context.repository.git_http_url}.`, + ); + } + return user; + } finally { + span.finish(); + } + } + + private async handlePushHook( + ctx: TraceContext, + body: GitLabPushHook, + user: User, + event: WebhookEvent, + ): Promise { + const span = TraceContext.startSpan("GitLapApp.handlePushHook", ctx); + try { + const cloneUrl = this.getCloneUrl(body); + const projects = await runWithSubjectId(SYSTEM_USER, () => + this.projectService.findProjectsByCloneUrl(SYSTEM_USER_ID, cloneUrl), + ); + for (const project of projects) { + try { + const projectOwner = await this.findProjectOwner(project, user); + + const contextURL = this.createBranchContextUrl(body); + log.debug({ userId: user.id }, "GitLab push hook: Context URL", { context: body, contextURL }); + span.setTag("contextURL", contextURL); + const context = (await this.contextParser.handle({ span }, user, contextURL)) as CommitContext; + + await this.webhookEvents.updateEvent(event.id, { + authorizedUserId: user.id, + projectId: project?.id, + cloneUrl: context.repository.cloneUrl, + branch: context.ref, + commit: context.revision, + }); + + const config = await this.prebuildManager.fetchConfig({ span }, user, context, project?.teamId); + const prebuildPrecondition = this.prebuildManager.checkPrebuildPrecondition({ + config, + project, + context, + }); + if (!prebuildPrecondition.shouldRun) { + log.info("GitLab push event: No prebuild.", { config, context }); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "ignored_unconfigured", + status: "processed", + message: prebuildPrecondition.reason, + }); + continue; + } + + log.debug({ userId: user.id }, "GitLab push event: Starting prebuild", { body, contextURL }); + + const workspaceUser = projectOwner || user; + await runWithSubjectId(SubjectId.fromUserId(workspaceUser.id), async () => { + const commitInfo = await this.getCommitInfo(user, body.repository.git_http_url, body.after); + const ws = await this.prebuildManager.startPrebuild( + { span }, + { + user: workspaceUser, + project: project, + context, + commitInfo, + }, + ); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_triggered", + status: "processed", + prebuildId: ws.prebuildId, + }); + }); + } catch (error) { + log.error("Error processing GitLab webhook event", error); + } + } + } catch (e) { + log.error("Error processing GitLab webhook event", e, body); + await this.webhookEvents.updateEvent(event.id, { + prebuildStatus: "prebuild_trigger_failed", + status: "processed", + }); + } finally { + span.finish(); + } + } + + private async getCommitInfo(user: User, repoURL: string, commitSHA: string) { + const parsedRepo = RepoURL.parseRepoUrl(repoURL)!; + const hostCtx = this.hostCtxProvider.get(parsedRepo.host); + let commitInfo: CommitInfo | undefined; + if (hostCtx?.services?.repositoryProvider) { + commitInfo = await hostCtx?.services?.repositoryProvider.getCommitInfo( + user, + parsedRepo.owner, + parsedRepo.repo, + commitSHA, + ); + } + return commitInfo; + } + + /** + * Finds the relevant user account and project to the provided webhook event information. + * + * First of all it tries to find the project for the given `cloneURL`, then it tries to + * find the installer, which is also supposed to be a team member. As a fallback, it + * looks for a team member which also has a gitlab.com connection. + * + * @param cloneURL of the webhook event + * @param webhookInstaller the user account known from the webhook installation + * @returns a promise which resolves to a user account and an optional project. + */ + private async findProjectOwner(project: Project, webhookInstaller: User): Promise { + try { + if (!project.teamId) { + throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Project has no teamId."); + } + const teamMembers = await this.teamDB.findMembersByTeam(project.teamId || ""); + if (teamMembers.some((t) => t.userId === webhookInstaller.id)) { + return webhookInstaller; + } + for (const teamMember of teamMembers) { + const user = await runWithSubjectId(SubjectId.fromUserId(teamMember.userId), () => + this.userService.findUserById(teamMember.userId, teamMember.userId), + ); + if (user && user.identities.some((i) => i.authProviderId === "Public-GitLab")) { + return user; + } + } + } catch (err) { + log.info({ userId: webhookInstaller.id }, "Failed to find project and owner", err); + } + return webhookInstaller; + } + + private createBranchContextUrl(body: GitLabPushHook) { + const repoUrl = body.repository.git_http_url; + const contextURL = `${repoUrl.substr(0, repoUrl.length - 4)}/-/tree${body.ref.substr("refs/head/".length)}`; + return contextURL; + } + + private getCloneUrl(body: GitLabPushHook) { + return body.repository.git_http_url; + } + get router(): express.Router { return this._router; } diff --git a/components/server/src/prebuilds/gitlab-service.ts b/components/server/src/prebuilds/gitlab-service.ts new file mode 100644 index 00000000000000..d5effe4a75b3ac --- /dev/null +++ b/components/server/src/prebuilds/gitlab-service.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; +import { User } from "@gitpod/gitpod-protocol"; +import { inject, injectable } from "inversify"; +import { GitLabApi, GitLab } from "../gitlab/api"; +import { GitLabApp } from "./gitlab-app"; +import { Config } from "../config"; +import { TokenService } from "../user/token-service"; +import { GitlabContextParser } from "../gitlab/gitlab-context-parser"; +import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { RepoURL } from "../repohost"; +import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; +import { UnauthorizedRepositoryAccessError } from "@gitpod/public-api-common/lib/public-api-errors"; +import { UnauthorizedError } from "../errors"; +import { GitLabScope } from "../gitlab/scopes"; +import { containsScopes } from "./token-scopes-inclusion"; + +@injectable() +export class GitlabService extends RepositoryService { + static PREBUILD_TOKEN_SCOPE = "prebuilds"; + + constructor( + @inject(GitLabApi) protected api: GitLabApi, + @inject(Config) private readonly config: Config, + @inject(TokenService) private readonly tokenService: TokenService, + @inject(GitlabContextParser) private readonly gitlabContextParser: GitlabContextParser, + ) { + super(); + } + + async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { + const parsedRepoUrl = RepoURL.parseRepoUrl(cloneUrl); + if (!parsedRepoUrl) { + throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Clone URL not parseable.`); + } + + let api; + try { + api = await this.api.create(user); // throw UnauthorizedError + } catch (error) { + if (error instanceof UnauthorizedRepositoryAccessError) { + error.info.host = parsedRepoUrl.host; + error.info.providerIsConnected = false; + error.info.isMissingScopes = true; + error.info.providerType = "GitLab"; + } + throw error; + } + + let tokenEntry; + try { + // throws GitLabApiError 404 + const { owner, repoName } = await this.gitlabContextParser.parseURL(user, cloneUrl); + const gitlabProjectId = `${owner}/${repoName}`; + // throws GitLabApiError 403 + const hooks = (await api.ProjectHooks.all(gitlabProjectId)) as unknown as GitLab.ProjectHook[]; + if (GitLab.ApiError.is(hooks)) { + throw hooks; + } + let existingProps: any = {}; + for (const hook of hooks) { + if (hook.url === this.getHookUrl()) { + log.info("Deleting existing hook"); + existingProps = hook; + // throws GitLabApiError 403 + await api.ProjectHooks.remove(gitlabProjectId, hook.id); + } + } + tokenEntry = await this.tokenService.createGitpodToken(user, GitlabService.PREBUILD_TOKEN_SCOPE, cloneUrl); + // throws GitLabApiError 403 + await api.ProjectHooks.add(gitlabProjectId, this.getHookUrl(), >{ + ...existingProps, + push_events: true, + token: user.id + "|" + tokenEntry.token.value, + }); + log.info("Installed Webhook for " + cloneUrl, { cloneUrl, userId: user.id }); + } catch (error) { + if (GitLab.ApiError.is(error)) { + // TODO check for `error.code` + + throw UnauthorizedError.create({ + host: parsedRepoUrl.host, + providerType: "GitLab", + repoName: parsedRepoUrl.repo, + requiredScopes: GitLabScope.Requirements.REPO, + providerIsConnected: true, + isMissingScopes: containsScopes(tokenEntry?.token?.scopes, GitLabScope.Requirements.REPO), + }); + } + throw error; + } + } + + private getHookUrl() { + return this.config.hostUrl + .asPublicServices() + .with({ + pathname: GitLabApp.path, + }) + .toString(); + } +} diff --git a/components/server/src/repohost/repo-service.ts b/components/server/src/repohost/repo-service.ts new file mode 100644 index 00000000000000..6b5f69b4854d32 --- /dev/null +++ b/components/server/src/repohost/repo-service.ts @@ -0,0 +1,15 @@ +/** + * 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 { User } from "@gitpod/gitpod-protocol"; +import { injectable } from "inversify"; + +@injectable() +export class RepositoryService { + async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { + throw new Error("unsupported"); + } +} diff --git a/components/server/src/repohost/repository-host.ts b/components/server/src/repohost/repository-host.ts index e87752ab0d1655..eba852ee0bf488 100644 --- a/components/server/src/repohost/repository-host.ts +++ b/components/server/src/repohost/repository-host.ts @@ -8,9 +8,11 @@ import { inject, injectable } from "inversify"; import { FileProvider } from "./file-provider"; import { RepositoryProvider } from "./repository-provider"; +import { RepositoryService } from "./repo-service"; @injectable() export class RepositoryHost { @inject(FileProvider) fileProvider: FileProvider; @inject(RepositoryProvider) repositoryProvider: RepositoryProvider; + @inject(RepositoryService) repositoryService: RepositoryService; } diff --git a/components/server/src/scm/scm-service.ts b/components/server/src/scm/scm-service.ts index 8cf20186727a71..b13dbfc68a7cb6 100644 --- a/components/server/src/scm/scm-service.ts +++ b/components/server/src/scm/scm-service.ts @@ -8,8 +8,9 @@ import { inject, injectable } from "inversify"; import { Authorizer } from "../authorization/authorizer"; import { Config } from "../config"; import { TokenProvider } from "../user/token-provider"; -import { CommitContext, Project, SuggestedRepository, Token, WorkspaceInfo } from "@gitpod/gitpod-protocol"; +import { CommitContext, Project, SuggestedRepository, Token, User, WorkspaceInfo } from "@gitpod/gitpod-protocol"; import { HostContextProvider } from "../auth/host-context-provider"; +import { RepoURL } from "../repohost"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { AuthProviderService } from "../auth/auth-provider-service"; import { UserService } from "../user/user-service"; @@ -52,6 +53,29 @@ export class ScmService { return token; } + public async installWebhookForPrebuilds(project: Project, installer: User) { + // Install the prebuilds webhook if possible + const { teamId, cloneUrl } = project; + const parsedUrl = RepoURL.parseRepoUrl(project.cloneUrl); + const hostContext = parsedUrl?.host ? this.hostContextProvider.get(parsedUrl?.host) : undefined; + + if (!hostContext) { + throw new ApplicationError(ErrorCodes.NOT_FOUND, `SCM provider not found.`); + } + + const repositoryService = hostContext.services?.repositoryService; + if (repositoryService) { + const logPayload = { organizationId: teamId, installer: installer.id, cloneUrl: project.cloneUrl }; + try { + await repositoryService.installAutomatedPrebuilds(installer, cloneUrl); + log.info("Webhook for prebuilds installed.", logPayload); + } catch (error) { + log.error("Failed to install webhook for prebuilds.", error, logPayload); + throw error; + } + } + } + /** * `guessTokenScopes` allows clients to retrieve scopes that would be necessary for a specified * git operation on a specified repository. diff --git a/components/server/src/test/service-testing-container-module.ts b/components/server/src/test/service-testing-container-module.ts index f0b682b4da4512..f494f7d12ccd02 100644 --- a/components/server/src/test/service-testing-container-module.ts +++ b/components/server/src/test/service-testing-container-module.ts @@ -110,6 +110,11 @@ const mockApplyingContainerModule = new ContainerModule((bind, unbound, isbound, }, }, services: { + repositoryService: { + installAutomatedPrebuilds: async (user: any, cloneUrl: string) => { + webhooks.add(cloneUrl); + }, + }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; diff --git a/components/server/src/user/token-service.spec.db.ts b/components/server/src/user/token-service.spec.db.ts index 8b0f1bdb403c5c..f32a98d97abad9 100644 --- a/components/server/src/user/token-service.spec.db.ts +++ b/components/server/src/user/token-service.spec.db.ts @@ -88,6 +88,9 @@ describe("TokenService", async () => { }, }, services: { + repositoryService: { + installAutomatedPrebuilds: async (user: any, cloneUrl: string) => {}, + }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; @@ -113,6 +116,9 @@ describe("TokenService", async () => { }, }, services: { + repositoryService: { + installAutomatedPrebuilds: async (user: any, cloneUrl: string) => {}, + }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; diff --git a/components/server/src/workspace/context-service.spec.db.ts b/components/server/src/workspace/context-service.spec.db.ts index c86981a5723808..cdb65828da5d07 100644 --- a/components/server/src/workspace/context-service.spec.db.ts +++ b/components/server/src/workspace/context-service.spec.db.ts @@ -138,6 +138,10 @@ describe("ContextService", async () => { }, }, services: { + repositoryService: { + installAutomatedPrebuilds: () => {}, + canInstallAutomatedPrebuilds: async () => {}, + }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; From a5299b718f5f0a915cc2e3846fa2807f7a9b8836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Jul 2024 12:36:59 +0000 Subject: [PATCH 08/16] Anotate new setups with an `activity-based` activation strategy --- .../src/teams-projects-protocol.ts | 7 + .../public-api/gitpod/v1/configuration.proto | 9 + .../public-api/go/v1/configuration.pb.go | 620 ++++++++++-------- .../publicapi/v1/ConfigurationOuterClass.java | 547 ++++++++++++--- .../src/public-api-converter.ts | 13 + .../src/gitpod/v1/configuration_pb.ts | 36 + .../server/src/projects/projects-service.ts | 16 + 7 files changed, 897 insertions(+), 351 deletions(-) diff --git a/components/gitpod-protocol/src/teams-projects-protocol.ts b/components/gitpod-protocol/src/teams-projects-protocol.ts index 1099fec1c6e463..ed9e89b9d959b2 100644 --- a/components/gitpod-protocol/src/teams-projects-protocol.ts +++ b/components/gitpod-protocol/src/teams-projects-protocol.ts @@ -31,6 +31,7 @@ export interface ProjectSettings { } export namespace PrebuildSettings { export type BranchStrategy = "default-branch" | "all-branches" | "matched-branches"; + export type ActivationStrategy = "activity-based" | "webhook-based"; } export interface PrebuildSettings { @@ -55,6 +56,11 @@ export interface PrebuildSettings { * Preferred workspace class for prebuilds. */ workspaceClass?: string; + + /** + * Activation strategy for prebuilds. Defaults to "webhook-based" + */ + activationStrategy?: PrebuildSettings.ActivationStrategy; } export interface Project { @@ -88,6 +94,7 @@ export namespace Project { branchMatchingPattern: "**", prebuildInterval: 20, branchStrategy: "all-branches", + activationStrategy: "activity-based", }; /** diff --git a/components/public-api/gitpod/v1/configuration.proto b/components/public-api/gitpod/v1/configuration.proto index 623225a2f1f915..b8023700ed198f 100644 --- a/components/public-api/gitpod/v1/configuration.proto +++ b/components/public-api/gitpod/v1/configuration.proto @@ -19,12 +19,20 @@ message Configuration { WorkspaceSettings workspace_settings = 7; } +enum PrebuildActivationStrategy { + // Default value. Implicitly applies to webhoook-based activation + PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + // Default value for newly enabled prebuilds. + PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; +} + message PrebuildSettings { bool enabled = 1; string branch_matching_pattern = 2; BranchMatchingStrategy branch_strategy = 3; int32 prebuild_interval = 4; string workspace_class = 5; + PrebuildActivationStrategy activation_strategy = 6; } enum BranchMatchingStrategy { @@ -97,6 +105,7 @@ message UpdateConfigurationRequest { optional BranchMatchingStrategy branch_strategy = 3; optional int32 prebuild_interval = 4; optional string workspace_class = 5; + optional PrebuildActivationStrategy activation_strategy = 6; } message WorkspaceSettings { optional string workspace_class = 1; diff --git a/components/public-api/go/v1/configuration.pb.go b/components/public-api/go/v1/configuration.pb.go index 7fb03f246a491e..cbe571621e4320 100644 --- a/components/public-api/go/v1/configuration.pb.go +++ b/components/public-api/go/v1/configuration.pb.go @@ -25,6 +25,54 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type PrebuildActivationStrategy int32 + +const ( + // Default value. Implicitly applies to webhoook-based activation + PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED PrebuildActivationStrategy = 0 + // Default value for newly enabled prebuilds. + PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED PrebuildActivationStrategy = 1 +) + +// Enum value maps for PrebuildActivationStrategy. +var ( + PrebuildActivationStrategy_name = map[int32]string{ + 0: "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED", + 1: "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED", + } + PrebuildActivationStrategy_value = map[string]int32{ + "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED": 0, + "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED": 1, + } +) + +func (x PrebuildActivationStrategy) Enum() *PrebuildActivationStrategy { + p := new(PrebuildActivationStrategy) + *p = x + return p +} + +func (x PrebuildActivationStrategy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PrebuildActivationStrategy) Descriptor() protoreflect.EnumDescriptor { + return file_gitpod_v1_configuration_proto_enumTypes[0].Descriptor() +} + +func (PrebuildActivationStrategy) Type() protoreflect.EnumType { + return &file_gitpod_v1_configuration_proto_enumTypes[0] +} + +func (x PrebuildActivationStrategy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PrebuildActivationStrategy.Descriptor instead. +func (PrebuildActivationStrategy) EnumDescriptor() ([]byte, []int) { + return file_gitpod_v1_configuration_proto_rawDescGZIP(), []int{0} +} + type BranchMatchingStrategy int32 const ( @@ -61,11 +109,11 @@ func (x BranchMatchingStrategy) String() string { } func (BranchMatchingStrategy) Descriptor() protoreflect.EnumDescriptor { - return file_gitpod_v1_configuration_proto_enumTypes[0].Descriptor() + return file_gitpod_v1_configuration_proto_enumTypes[1].Descriptor() } func (BranchMatchingStrategy) Type() protoreflect.EnumType { - return &file_gitpod_v1_configuration_proto_enumTypes[0] + return &file_gitpod_v1_configuration_proto_enumTypes[1] } func (x BranchMatchingStrategy) Number() protoreflect.EnumNumber { @@ -74,7 +122,7 @@ func (x BranchMatchingStrategy) Number() protoreflect.EnumNumber { // Deprecated: Use BranchMatchingStrategy.Descriptor instead. func (BranchMatchingStrategy) EnumDescriptor() ([]byte, []int) { - return file_gitpod_v1_configuration_proto_rawDescGZIP(), []int{0} + return file_gitpod_v1_configuration_proto_rawDescGZIP(), []int{1} } type Configuration struct { @@ -177,11 +225,12 @@ type PrebuildSettings struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - BranchMatchingPattern string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3" json:"branch_matching_pattern,omitempty"` - BranchStrategy BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy" json:"branch_strategy,omitempty"` - PrebuildInterval int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3" json:"prebuild_interval,omitempty"` - WorkspaceClass string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3" json:"workspace_class,omitempty"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + BranchMatchingPattern string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3" json:"branch_matching_pattern,omitempty"` + BranchStrategy BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy" json:"branch_strategy,omitempty"` + PrebuildInterval int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3" json:"prebuild_interval,omitempty"` + WorkspaceClass string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3" json:"workspace_class,omitempty"` + ActivationStrategy PrebuildActivationStrategy `protobuf:"varint,6,opt,name=activation_strategy,json=activationStrategy,proto3,enum=gitpod.v1.PrebuildActivationStrategy" json:"activation_strategy,omitempty"` } func (x *PrebuildSettings) Reset() { @@ -251,6 +300,13 @@ func (x *PrebuildSettings) GetWorkspaceClass() string { return "" } +func (x *PrebuildSettings) GetActivationStrategy() PrebuildActivationStrategy { + if x != nil { + return x.ActivationStrategy + } + return PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED +} + type WorkspaceSettings struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -862,11 +918,12 @@ type UpdateConfigurationRequest_PrebuildSettings struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` - BranchMatchingPattern *string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3,oneof" json:"branch_matching_pattern,omitempty"` - BranchStrategy *BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy,oneof" json:"branch_strategy,omitempty"` - PrebuildInterval *int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3,oneof" json:"prebuild_interval,omitempty"` - WorkspaceClass *string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3,oneof" json:"workspace_class,omitempty"` + Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` + BranchMatchingPattern *string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3,oneof" json:"branch_matching_pattern,omitempty"` + BranchStrategy *BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy,oneof" json:"branch_strategy,omitempty"` + PrebuildInterval *int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3,oneof" json:"prebuild_interval,omitempty"` + WorkspaceClass *string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3,oneof" json:"workspace_class,omitempty"` + ActivationStrategy *PrebuildActivationStrategy `protobuf:"varint,6,opt,name=activation_strategy,json=activationStrategy,proto3,enum=gitpod.v1.PrebuildActivationStrategy,oneof" json:"activation_strategy,omitempty"` } func (x *UpdateConfigurationRequest_PrebuildSettings) Reset() { @@ -936,6 +993,13 @@ func (x *UpdateConfigurationRequest_PrebuildSettings) GetWorkspaceClass() string return "" } +func (x *UpdateConfigurationRequest_PrebuildSettings) GetActivationStrategy() PrebuildActivationStrategy { + if x != nil && x.ActivationStrategy != nil { + return *x.ActivationStrategy + } + return PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED +} + type UpdateConfigurationRequest_WorkspaceSettings struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1055,7 +1119,7 @@ var file_gitpod_v1_configuration_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x22, 0x86, 0x02, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x69, 0x6e, 0x67, 0x73, 0x22, 0xde, 0x02, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, @@ -1071,209 +1135,230 @@ var file_gitpod_v1_configuration_proto_rawDesc = []byte{ 0x28, 0x05, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0xb6, 0x01, - 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x40, 0x0a, 0x1c, - 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x36, - 0x0a, 0x17, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, - 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x15, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, - 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x76, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, - 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x5d, - 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, - 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x0a, - 0x17, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x22, 0x5a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x90, 0x02, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, - 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, - 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, - 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, - 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x22, 0x9d, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, - 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0xb0, 0x09, 0x0a, 0x1a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x68, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x36, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x48, 0x01, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, - 0x6b, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x48, 0x02, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x85, 0x03, 0x0a, - 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, - 0x12, 0x3b, 0x0a, 0x17, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x15, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, - 0x69, 0x6e, 0x67, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, - 0x0f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, - 0x76, 0x31, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, - 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x02, 0x52, 0x0e, 0x62, 0x72, 0x61, - 0x6e, 0x63, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x30, - 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x10, 0x70, 0x72, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x88, 0x01, 0x01, - 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, - 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x0e, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0a, - 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x62, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x70, - 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, - 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x1a, 0xb8, 0x03, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x74, - 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, - 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x23, 0x75, 0x70, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x56, 0x0a, + 0x13, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, + 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x67, 0x69, 0x74, + 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x52, 0x12, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xb6, 0x01, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, 0x74, + 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, + 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, + 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x76, + 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, + 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x6f, + 0x6e, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, + 0x6f, 0x6e, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x5d, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, + 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x5a, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x90, 0x02, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x12, + 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, + 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x69, + 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x73, 0x6f, + 0x72, 0x74, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, + 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x9d, 0x01, 0x0a, 0x1a, 0x4c, + 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, + 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x0a, 0x0a, 0x1a, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x68, 0x0a, + 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x48, 0x01, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x6b, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x48, 0x02, 0x52, 0x11, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x88, 0x01, 0x01, 0x1a, 0xfa, 0x03, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x17, 0x62, 0x72, 0x61, 0x6e, + 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x15, 0x62, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, + 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, + 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x48, 0x02, 0x52, 0x0e, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x48, 0x03, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x04, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x5b, 0x0a, 0x13, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x05, 0x52, 0x12, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, + 0x1a, 0x0a, 0x18, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, + 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x42, + 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x1a, 0xb8, 0x03, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, + 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, + 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x23, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x17, 0x72, + 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x72, 0x65, + 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x1e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, + 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1b, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, + 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x42, 0x26, 0x0a, 0x24, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, + 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x42, 0x21, 0x0a, 0x1f, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x36, - 0x0a, 0x17, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, - 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x15, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, - 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x1e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, - 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, - 0x52, 0x1b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, - 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x88, 0x01, 0x01, - 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x42, 0x26, 0x0a, 0x24, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, - 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x42, 0x21, 0x0a, 0x1f, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, - 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x42, - 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x15, - 0x0a, 0x13, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x5d, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x47, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x1d, 0x0a, - 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0xc9, 0x01, 0x0a, - 0x16, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x53, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x24, 0x42, 0x52, 0x41, 0x4e, 0x43, - 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, - 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x2b, 0x0a, 0x27, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, - 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x44, 0x45, - 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x29, - 0x0a, 0x25, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, - 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x42, - 0x52, 0x41, 0x4e, 0x43, 0x48, 0x45, 0x53, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, 0x42, 0x52, 0x41, - 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, - 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x45, 0x44, 0x5f, 0x42, 0x52, - 0x41, 0x4e, 0x43, 0x48, 0x45, 0x53, 0x10, 0x03, 0x32, 0x92, 0x04, 0x0a, 0x14, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x66, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, - 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, - 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, + 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x07, 0x0a, 0x05, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x22, 0x5d, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0x47, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x7b, 0x0a, 0x1a, 0x50, 0x72, 0x65, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2c, 0x0a, 0x28, 0x50, 0x52, 0x45, 0x42, 0x55, + 0x49, 0x4c, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, + 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2f, 0x0a, 0x2b, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, + 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, + 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x42, + 0x41, 0x53, 0x45, 0x44, 0x10, 0x01, 0x2a, 0xc9, 0x01, 0x0a, 0x16, 0x42, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x12, 0x28, 0x0a, 0x24, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, + 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2b, 0x0a, 0x27, 0x42, + 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, + 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, + 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, 0x42, 0x52, 0x41, 0x4e, + 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, + 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x45, + 0x53, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, + 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, + 0x4d, 0x41, 0x54, 0x43, 0x48, 0x45, 0x44, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x45, 0x53, + 0x10, 0x03, 0x32, 0x92, 0x04, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, + 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, + 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x63, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, - 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, - 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, 0x0a, - 0x16, 0x69, 0x6f, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x2f, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, + 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x66, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x67, 0x69, + 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, + 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1288,60 +1373,63 @@ func file_gitpod_v1_configuration_proto_rawDescGZIP() []byte { return file_gitpod_v1_configuration_proto_rawDescData } -var file_gitpod_v1_configuration_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_gitpod_v1_configuration_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_gitpod_v1_configuration_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_gitpod_v1_configuration_proto_goTypes = []interface{}{ - (BranchMatchingStrategy)(0), // 0: gitpod.v1.BranchMatchingStrategy - (*Configuration)(nil), // 1: gitpod.v1.Configuration - (*PrebuildSettings)(nil), // 2: gitpod.v1.PrebuildSettings - (*WorkspaceSettings)(nil), // 3: gitpod.v1.WorkspaceSettings - (*CreateConfigurationRequest)(nil), // 4: gitpod.v1.CreateConfigurationRequest - (*CreateConfigurationResponse)(nil), // 5: gitpod.v1.CreateConfigurationResponse - (*GetConfigurationRequest)(nil), // 6: gitpod.v1.GetConfigurationRequest - (*GetConfigurationResponse)(nil), // 7: gitpod.v1.GetConfigurationResponse - (*ListConfigurationsRequest)(nil), // 8: gitpod.v1.ListConfigurationsRequest - (*ListConfigurationsResponse)(nil), // 9: gitpod.v1.ListConfigurationsResponse - (*UpdateConfigurationRequest)(nil), // 10: gitpod.v1.UpdateConfigurationRequest - (*UpdateConfigurationResponse)(nil), // 11: gitpod.v1.UpdateConfigurationResponse - (*DeleteConfigurationRequest)(nil), // 12: gitpod.v1.DeleteConfigurationRequest - (*DeleteConfigurationResponse)(nil), // 13: gitpod.v1.DeleteConfigurationResponse - (*UpdateConfigurationRequest_PrebuildSettings)(nil), // 14: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings - (*UpdateConfigurationRequest_WorkspaceSettings)(nil), // 15: gitpod.v1.UpdateConfigurationRequest.WorkspaceSettings - (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp - (*PaginationRequest)(nil), // 17: gitpod.v1.PaginationRequest - (*Sort)(nil), // 18: gitpod.v1.Sort - (*PaginationResponse)(nil), // 19: gitpod.v1.PaginationResponse + (PrebuildActivationStrategy)(0), // 0: gitpod.v1.PrebuildActivationStrategy + (BranchMatchingStrategy)(0), // 1: gitpod.v1.BranchMatchingStrategy + (*Configuration)(nil), // 2: gitpod.v1.Configuration + (*PrebuildSettings)(nil), // 3: gitpod.v1.PrebuildSettings + (*WorkspaceSettings)(nil), // 4: gitpod.v1.WorkspaceSettings + (*CreateConfigurationRequest)(nil), // 5: gitpod.v1.CreateConfigurationRequest + (*CreateConfigurationResponse)(nil), // 6: gitpod.v1.CreateConfigurationResponse + (*GetConfigurationRequest)(nil), // 7: gitpod.v1.GetConfigurationRequest + (*GetConfigurationResponse)(nil), // 8: gitpod.v1.GetConfigurationResponse + (*ListConfigurationsRequest)(nil), // 9: gitpod.v1.ListConfigurationsRequest + (*ListConfigurationsResponse)(nil), // 10: gitpod.v1.ListConfigurationsResponse + (*UpdateConfigurationRequest)(nil), // 11: gitpod.v1.UpdateConfigurationRequest + (*UpdateConfigurationResponse)(nil), // 12: gitpod.v1.UpdateConfigurationResponse + (*DeleteConfigurationRequest)(nil), // 13: gitpod.v1.DeleteConfigurationRequest + (*DeleteConfigurationResponse)(nil), // 14: gitpod.v1.DeleteConfigurationResponse + (*UpdateConfigurationRequest_PrebuildSettings)(nil), // 15: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings + (*UpdateConfigurationRequest_WorkspaceSettings)(nil), // 16: gitpod.v1.UpdateConfigurationRequest.WorkspaceSettings + (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp + (*PaginationRequest)(nil), // 18: gitpod.v1.PaginationRequest + (*Sort)(nil), // 19: gitpod.v1.Sort + (*PaginationResponse)(nil), // 20: gitpod.v1.PaginationResponse } var file_gitpod_v1_configuration_proto_depIdxs = []int32{ - 16, // 0: gitpod.v1.Configuration.creation_time:type_name -> google.protobuf.Timestamp - 2, // 1: gitpod.v1.Configuration.prebuild_settings:type_name -> gitpod.v1.PrebuildSettings - 3, // 2: gitpod.v1.Configuration.workspace_settings:type_name -> gitpod.v1.WorkspaceSettings - 0, // 3: gitpod.v1.PrebuildSettings.branch_strategy:type_name -> gitpod.v1.BranchMatchingStrategy - 1, // 4: gitpod.v1.CreateConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration - 1, // 5: gitpod.v1.GetConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration - 17, // 6: gitpod.v1.ListConfigurationsRequest.pagination:type_name -> gitpod.v1.PaginationRequest - 18, // 7: gitpod.v1.ListConfigurationsRequest.sort:type_name -> gitpod.v1.Sort - 1, // 8: gitpod.v1.ListConfigurationsResponse.configurations:type_name -> gitpod.v1.Configuration - 19, // 9: gitpod.v1.ListConfigurationsResponse.pagination:type_name -> gitpod.v1.PaginationResponse - 14, // 10: gitpod.v1.UpdateConfigurationRequest.prebuild_settings:type_name -> gitpod.v1.UpdateConfigurationRequest.PrebuildSettings - 15, // 11: gitpod.v1.UpdateConfigurationRequest.workspace_settings:type_name -> gitpod.v1.UpdateConfigurationRequest.WorkspaceSettings - 1, // 12: gitpod.v1.UpdateConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration - 0, // 13: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings.branch_strategy:type_name -> gitpod.v1.BranchMatchingStrategy - 4, // 14: gitpod.v1.ConfigurationService.CreateConfiguration:input_type -> gitpod.v1.CreateConfigurationRequest - 6, // 15: gitpod.v1.ConfigurationService.GetConfiguration:input_type -> gitpod.v1.GetConfigurationRequest - 8, // 16: gitpod.v1.ConfigurationService.ListConfigurations:input_type -> gitpod.v1.ListConfigurationsRequest - 10, // 17: gitpod.v1.ConfigurationService.UpdateConfiguration:input_type -> gitpod.v1.UpdateConfigurationRequest - 12, // 18: gitpod.v1.ConfigurationService.DeleteConfiguration:input_type -> gitpod.v1.DeleteConfigurationRequest - 5, // 19: gitpod.v1.ConfigurationService.CreateConfiguration:output_type -> gitpod.v1.CreateConfigurationResponse - 7, // 20: gitpod.v1.ConfigurationService.GetConfiguration:output_type -> gitpod.v1.GetConfigurationResponse - 9, // 21: gitpod.v1.ConfigurationService.ListConfigurations:output_type -> gitpod.v1.ListConfigurationsResponse - 11, // 22: gitpod.v1.ConfigurationService.UpdateConfiguration:output_type -> gitpod.v1.UpdateConfigurationResponse - 13, // 23: gitpod.v1.ConfigurationService.DeleteConfiguration:output_type -> gitpod.v1.DeleteConfigurationResponse - 19, // [19:24] is the sub-list for method output_type - 14, // [14:19] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 17, // 0: gitpod.v1.Configuration.creation_time:type_name -> google.protobuf.Timestamp + 3, // 1: gitpod.v1.Configuration.prebuild_settings:type_name -> gitpod.v1.PrebuildSettings + 4, // 2: gitpod.v1.Configuration.workspace_settings:type_name -> gitpod.v1.WorkspaceSettings + 1, // 3: gitpod.v1.PrebuildSettings.branch_strategy:type_name -> gitpod.v1.BranchMatchingStrategy + 0, // 4: gitpod.v1.PrebuildSettings.activation_strategy:type_name -> gitpod.v1.PrebuildActivationStrategy + 2, // 5: gitpod.v1.CreateConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration + 2, // 6: gitpod.v1.GetConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration + 18, // 7: gitpod.v1.ListConfigurationsRequest.pagination:type_name -> gitpod.v1.PaginationRequest + 19, // 8: gitpod.v1.ListConfigurationsRequest.sort:type_name -> gitpod.v1.Sort + 2, // 9: gitpod.v1.ListConfigurationsResponse.configurations:type_name -> gitpod.v1.Configuration + 20, // 10: gitpod.v1.ListConfigurationsResponse.pagination:type_name -> gitpod.v1.PaginationResponse + 15, // 11: gitpod.v1.UpdateConfigurationRequest.prebuild_settings:type_name -> gitpod.v1.UpdateConfigurationRequest.PrebuildSettings + 16, // 12: gitpod.v1.UpdateConfigurationRequest.workspace_settings:type_name -> gitpod.v1.UpdateConfigurationRequest.WorkspaceSettings + 2, // 13: gitpod.v1.UpdateConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration + 1, // 14: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings.branch_strategy:type_name -> gitpod.v1.BranchMatchingStrategy + 0, // 15: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings.activation_strategy:type_name -> gitpod.v1.PrebuildActivationStrategy + 5, // 16: gitpod.v1.ConfigurationService.CreateConfiguration:input_type -> gitpod.v1.CreateConfigurationRequest + 7, // 17: gitpod.v1.ConfigurationService.GetConfiguration:input_type -> gitpod.v1.GetConfigurationRequest + 9, // 18: gitpod.v1.ConfigurationService.ListConfigurations:input_type -> gitpod.v1.ListConfigurationsRequest + 11, // 19: gitpod.v1.ConfigurationService.UpdateConfiguration:input_type -> gitpod.v1.UpdateConfigurationRequest + 13, // 20: gitpod.v1.ConfigurationService.DeleteConfiguration:input_type -> gitpod.v1.DeleteConfigurationRequest + 6, // 21: gitpod.v1.ConfigurationService.CreateConfiguration:output_type -> gitpod.v1.CreateConfigurationResponse + 8, // 22: gitpod.v1.ConfigurationService.GetConfiguration:output_type -> gitpod.v1.GetConfigurationResponse + 10, // 23: gitpod.v1.ConfigurationService.ListConfigurations:output_type -> gitpod.v1.ListConfigurationsResponse + 12, // 24: gitpod.v1.ConfigurationService.UpdateConfiguration:output_type -> gitpod.v1.UpdateConfigurationResponse + 14, // 25: gitpod.v1.ConfigurationService.DeleteConfiguration:output_type -> gitpod.v1.DeleteConfigurationResponse + 21, // [21:26] is the sub-list for method output_type + 16, // [16:21] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_gitpod_v1_configuration_proto_init() } @@ -1542,7 +1630,7 @@ func file_gitpod_v1_configuration_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gitpod_v1_configuration_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 15, NumExtensions: 0, NumServices: 1, diff --git a/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java b/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java index 85b97741d7631c..7289bb6ba7e925 100644 --- a/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java +++ b/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java @@ -29,6 +29,139 @@ public static void registerAllExtensions( registerAllExtensions( (com.google.protobuf.ExtensionRegistryLite) registry); } + /** + * Protobuf enum {@code gitpod.v1.PrebuildActivationStrategy} + */ + public enum PrebuildActivationStrategy + implements com.google.protobuf.ProtocolMessageEnum { + /** + *
+     * Default value. Implicitly applies to webhoook-based activation
+     * 
+ * + * PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + */ + PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED(0), + /** + *
+     * Default value for newly enabled prebuilds.
+     * 
+ * + * PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + */ + PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED(1), + UNRECOGNIZED(-1), + ; + + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 27, + /* patch= */ 2, + /* suffix= */ "", + PrebuildActivationStrategy.class.getName()); + } + /** + *
+     * Default value. Implicitly applies to webhoook-based activation
+     * 
+ * + * PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + */ + public static final int PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED_VALUE = 0; + /** + *
+     * Default value for newly enabled prebuilds.
+     * 
+ * + * PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + */ + public static final int PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED_VALUE = 1; + + + public final int getNumber() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static PrebuildActivationStrategy valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static PrebuildActivationStrategy forNumber(int value) { + switch (value) { + case 0: return PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED; + case 1: return PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + PrebuildActivationStrategy> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public PrebuildActivationStrategy findValueByNumber(int number) { + return PrebuildActivationStrategy.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return io.gitpod.publicapi.v1.ConfigurationOuterClass.getDescriptor().getEnumTypes().get(0); + } + + private static final PrebuildActivationStrategy[] VALUES = values(); + + public static PrebuildActivationStrategy valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private PrebuildActivationStrategy(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:gitpod.v1.PrebuildActivationStrategy) + } + /** * Protobuf enum {@code gitpod.v1.BranchMatchingStrategy} */ @@ -138,7 +271,7 @@ public BranchMatchingStrategy findValueByNumber(int number) { } public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() { - return io.gitpod.publicapi.v1.ConfigurationOuterClass.getDescriptor().getEnumTypes().get(0); + return io.gitpod.publicapi.v1.ConfigurationOuterClass.getDescriptor().getEnumTypes().get(1); } private static final BranchMatchingStrategy[] VALUES = values(); @@ -1776,6 +1909,17 @@ public interface PrebuildSettingsOrBuilder extends */ com.google.protobuf.ByteString getWorkspaceClassBytes(); + + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The enum numeric value on the wire for activationStrategy. + */ + int getActivationStrategyValue(); + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The activationStrategy. + */ + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy(); } /** * Protobuf type {@code gitpod.v1.PrebuildSettings} @@ -1802,6 +1946,7 @@ private PrebuildSettings() { branchMatchingPattern_ = ""; branchStrategy_ = 0; workspaceClass_ = ""; + activationStrategy_ = 0; } public static final com.google.protobuf.Descriptors.Descriptor @@ -1935,6 +2080,24 @@ public java.lang.String getWorkspaceClass() { } } + public static final int ACTIVATION_STRATEGY_FIELD_NUMBER = 6; + private int activationStrategy_ = 0; + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The enum numeric value on the wire for activationStrategy. + */ + @java.lang.Override public int getActivationStrategyValue() { + return activationStrategy_; + } + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The activationStrategy. + */ + @java.lang.Override public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -1964,6 +2127,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(workspaceClass_)) { com.google.protobuf.GeneratedMessage.writeString(output, 5, workspaceClass_); } + if (activationStrategy_ != io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED.getNumber()) { + output.writeEnum(6, activationStrategy_); + } getUnknownFields().writeTo(output); } @@ -1991,6 +2157,10 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(workspaceClass_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(5, workspaceClass_); } + if (activationStrategy_ != io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED.getNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(6, activationStrategy_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -2015,6 +2185,7 @@ public boolean equals(final java.lang.Object obj) { != other.getPrebuildInterval()) return false; if (!getWorkspaceClass() .equals(other.getWorkspaceClass())) return false; + if (activationStrategy_ != other.activationStrategy_) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -2037,6 +2208,8 @@ public int hashCode() { hash = (53 * hash) + getPrebuildInterval(); hash = (37 * hash) + WORKSPACE_CLASS_FIELD_NUMBER; hash = (53 * hash) + getWorkspaceClass().hashCode(); + hash = (37 * hash) + ACTIVATION_STRATEGY_FIELD_NUMBER; + hash = (53 * hash) + activationStrategy_; hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -2173,6 +2346,7 @@ public Builder clear() { branchStrategy_ = 0; prebuildInterval_ = 0; workspaceClass_ = ""; + activationStrategy_ = 0; return this; } @@ -2221,6 +2395,9 @@ private void buildPartial0(io.gitpod.publicapi.v1.ConfigurationOuterClass.Prebui if (((from_bitField0_ & 0x00000010) != 0)) { result.workspaceClass_ = workspaceClass_; } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.activationStrategy_ = activationStrategy_; + } } @java.lang.Override @@ -2254,6 +2431,9 @@ public Builder mergeFrom(io.gitpod.publicapi.v1.ConfigurationOuterClass.Prebuild bitField0_ |= 0x00000010; onChanged(); } + if (other.activationStrategy_ != 0) { + setActivationStrategyValue(other.getActivationStrategyValue()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -2305,6 +2485,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000010; break; } // case 42 + case 48: { + activationStrategy_ = input.readEnum(); + bitField0_ |= 0x00000020; + break; + } // case 48 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -2583,6 +2768,59 @@ public Builder setWorkspaceClassBytes( return this; } + private int activationStrategy_ = 0; + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The enum numeric value on the wire for activationStrategy. + */ + @java.lang.Override public int getActivationStrategyValue() { + return activationStrategy_; + } + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @param value The enum numeric value on the wire for activationStrategy to set. + * @return This builder for chaining. + */ + public Builder setActivationStrategyValue(int value) { + activationStrategy_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The activationStrategy. + */ + @java.lang.Override + public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + } + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @param value The activationStrategy to set. + * @return This builder for chaining. + */ + public Builder setActivationStrategy(io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + activationStrategy_ = value.getNumber(); + onChanged(); + return this; + } + /** + * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return This builder for chaining. + */ + public Builder clearActivationStrategy() { + bitField0_ = (bitField0_ & ~0x00000020); + activationStrategy_ = 0; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:gitpod.v1.PrebuildSettings) } @@ -8643,6 +8881,22 @@ public interface PrebuildSettingsOrBuilder extends */ com.google.protobuf.ByteString getWorkspaceClassBytes(); + + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return Whether the activationStrategy field is set. + */ + boolean hasActivationStrategy(); + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The enum numeric value on the wire for activationStrategy. + */ + int getActivationStrategyValue(); + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The activationStrategy. + */ + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy(); } /** * Protobuf type {@code gitpod.v1.UpdateConfigurationRequest.PrebuildSettings} @@ -8669,6 +8923,7 @@ private PrebuildSettings() { branchMatchingPattern_ = ""; branchStrategy_ = 0; workspaceClass_ = ""; + activationStrategy_ = 0; } public static final com.google.protobuf.Descriptors.Descriptor @@ -8842,6 +9097,31 @@ public java.lang.String getWorkspaceClass() { } } + public static final int ACTIVATION_STRATEGY_FIELD_NUMBER = 6; + private int activationStrategy_ = 0; + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return Whether the activationStrategy field is set. + */ + @java.lang.Override public boolean hasActivationStrategy() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The enum numeric value on the wire for activationStrategy. + */ + @java.lang.Override public int getActivationStrategyValue() { + return activationStrategy_; + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The activationStrategy. + */ + @java.lang.Override public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -8871,6 +9151,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000010) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 5, workspaceClass_); } + if (((bitField0_ & 0x00000020) != 0)) { + output.writeEnum(6, activationStrategy_); + } getUnknownFields().writeTo(output); } @@ -8898,6 +9181,10 @@ public int getSerializedSize() { if (((bitField0_ & 0x00000010) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(5, workspaceClass_); } + if (((bitField0_ & 0x00000020) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(6, activationStrategy_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -8937,6 +9224,10 @@ public boolean equals(final java.lang.Object obj) { if (!getWorkspaceClass() .equals(other.getWorkspaceClass())) return false; } + if (hasActivationStrategy() != other.hasActivationStrategy()) return false; + if (hasActivationStrategy()) { + if (activationStrategy_ != other.activationStrategy_) return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -8969,6 +9260,10 @@ public int hashCode() { hash = (37 * hash) + WORKSPACE_CLASS_FIELD_NUMBER; hash = (53 * hash) + getWorkspaceClass().hashCode(); } + if (hasActivationStrategy()) { + hash = (37 * hash) + ACTIVATION_STRATEGY_FIELD_NUMBER; + hash = (53 * hash) + activationStrategy_; + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -9105,6 +9400,7 @@ public Builder clear() { branchStrategy_ = 0; prebuildInterval_ = 0; workspaceClass_ = ""; + activationStrategy_ = 0; return this; } @@ -9159,6 +9455,10 @@ private void buildPartial0(io.gitpod.publicapi.v1.ConfigurationOuterClass.Update result.workspaceClass_ = workspaceClass_; to_bitField0_ |= 0x00000010; } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.activationStrategy_ = activationStrategy_; + to_bitField0_ |= 0x00000020; + } result.bitField0_ |= to_bitField0_; } @@ -9193,6 +9493,9 @@ public Builder mergeFrom(io.gitpod.publicapi.v1.ConfigurationOuterClass.UpdateCo bitField0_ |= 0x00000010; onChanged(); } + if (other.hasActivationStrategy()) { + setActivationStrategy(other.getActivationStrategy()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -9244,6 +9547,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000010; break; } // case 42 + case 48: { + activationStrategy_ = input.readEnum(); + bitField0_ |= 0x00000020; + break; + } // case 48 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -9559,6 +9867,66 @@ public Builder setWorkspaceClassBytes( return this; } + private int activationStrategy_ = 0; + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return Whether the activationStrategy field is set. + */ + @java.lang.Override public boolean hasActivationStrategy() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The enum numeric value on the wire for activationStrategy. + */ + @java.lang.Override public int getActivationStrategyValue() { + return activationStrategy_; + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @param value The enum numeric value on the wire for activationStrategy to set. + * @return This builder for chaining. + */ + public Builder setActivationStrategyValue(int value) { + activationStrategy_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return The activationStrategy. + */ + @java.lang.Override + public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @param value The activationStrategy to set. + * @return This builder for chaining. + */ + public Builder setActivationStrategy(io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + activationStrategy_ = value.getNumber(); + onChanged(); + return this; + } + /** + * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * @return This builder for chaining. + */ + public Builder clearActivationStrategy() { + bitField0_ = (bitField0_ & ~0x00000020); + activationStrategy_ = 0; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:gitpod.v1.UpdateConfigurationRequest.PrebuildSettings) } @@ -13565,94 +13933,103 @@ public io.gitpod.publicapi.v1.ConfigurationOuterClass.DeleteConfigurationRespons "\021prebuild_settings\030\006 \001(\0132\033.gitpod.v1.Pre" + "buildSettingsR\020prebuildSettings\022K\n\022works" + "pace_settings\030\007 \001(\0132\034.gitpod.v1.Workspac" + - "eSettingsR\021workspaceSettings\"\206\002\n\020Prebuil" + + "eSettingsR\021workspaceSettings\"\336\002\n\020Prebuil" + "dSettings\022\030\n\007enabled\030\001 \001(\010R\007enabled\0226\n\027b" + "ranch_matching_pattern\030\002 \001(\tR\025branchMatc" + "hingPattern\022J\n\017branch_strategy\030\003 \001(\0162!.g" + "itpod.v1.BranchMatchingStrategyR\016branchS" + "trategy\022+\n\021prebuild_interval\030\004 \001(\005R\020preb" + "uildInterval\022\'\n\017workspace_class\030\005 \001(\tR\016w" + - "orkspaceClass\"\266\001\n\021WorkspaceSettings\022\'\n\017w" + - "orkspace_class\030\001 \001(\tR\016workspaceClass\022@\n\034" + - "restricted_workspace_classes\030\002 \003(\tR\032rest" + - "rictedWorkspaceClasses\0226\n\027restricted_edi" + - "tor_names\030\003 \003(\tR\025restrictedEditorNames\"v" + - "\n\032CreateConfigurationRequest\022\'\n\017organiza" + - "tion_id\030\001 \001(\tR\016organizationId\022\022\n\004name\030\002 " + - "\001(\tR\004name\022\033\n\tclone_url\030\003 \001(\tR\010cloneUrl\"]" + - "\n\033CreateConfigurationResponse\022>\n\rconfigu" + - "ration\030\001 \001(\0132\030.gitpod.v1.ConfigurationR\r" + - "configuration\"D\n\027GetConfigurationRequest" + - "\022)\n\020configuration_id\030\001 \001(\tR\017configuratio" + - "nId\"Z\n\030GetConfigurationResponse\022>\n\rconfi" + - "guration\030\001 \001(\0132\030.gitpod.v1.Configuration" + - "R\rconfiguration\"\220\002\n\031ListConfigurationsRe" + - "quest\022\'\n\017organization_id\030\001 \001(\tR\016organiza" + - "tionId\022\037\n\013search_term\030\002 \001(\tR\nsearchTerm\022" + - "<\n\npagination\030\003 \001(\0132\034.gitpod.v1.Paginati" + - "onRequestR\npagination\022#\n\004sort\030\004 \003(\0132\017.gi" + - "tpod.v1.SortR\004sort\0220\n\021prebuilds_enabled\030" + - "\005 \001(\010H\000R\020prebuildsEnabled\210\001\001B\024\n\022_prebuil" + - "ds_enabled\"\235\001\n\032ListConfigurationsRespons" + - "e\022@\n\016configurations\030\001 \003(\0132\030.gitpod.v1.Co" + - "nfigurationR\016configurations\022=\n\npaginatio" + - "n\030\002 \001(\0132\035.gitpod.v1.PaginationResponseR\n" + - "pagination\"\260\t\n\032UpdateConfigurationReques" + - "t\022)\n\020configuration_id\030\001 \001(\tR\017configurati" + - "onId\022\027\n\004name\030\002 \001(\tH\000R\004name\210\001\001\022h\n\021prebuil" + - "d_settings\030\003 \001(\01326.gitpod.v1.UpdateConfi" + - "gurationRequest.PrebuildSettingsH\001R\020preb" + - "uildSettings\210\001\001\022k\n\022workspace_settings\030\004 " + - "\001(\01327.gitpod.v1.UpdateConfigurationReque" + - "st.WorkspaceSettingsH\002R\021workspaceSetting" + - "s\210\001\001\032\205\003\n\020PrebuildSettings\022\035\n\007enabled\030\001 \001" + - "(\010H\000R\007enabled\210\001\001\022;\n\027branch_matching_patt" + - "ern\030\002 \001(\tH\001R\025branchMatchingPattern\210\001\001\022O\n" + - "\017branch_strategy\030\003 \001(\0162!.gitpod.v1.Branc" + - "hMatchingStrategyH\002R\016branchStrategy\210\001\001\0220" + - "\n\021prebuild_interval\030\004 \001(\005H\003R\020prebuildInt" + - "erval\210\001\001\022,\n\017workspace_class\030\005 \001(\tH\004R\016wor" + - "kspaceClass\210\001\001B\n\n\010_enabledB\032\n\030_branch_ma" + - "tching_patternB\022\n\020_branch_strategyB\024\n\022_p" + - "rebuild_intervalB\022\n\020_workspace_class\032\270\003\n" + - "\021WorkspaceSettings\022,\n\017workspace_class\030\001 " + - "\001(\tH\000R\016workspaceClass\210\001\001\022@\n\034restricted_w" + - "orkspace_classes\030\002 \003(\tR\032restrictedWorksp" + - "aceClasses\022R\n#update_restricted_workspac" + - "e_classes\030\003 \001(\010H\001R updateRestrictedWorks" + - "paceClasses\210\001\001\0226\n\027restricted_editor_name" + - "s\030\004 \003(\tR\025restrictedEditorNames\022H\n\036update" + - "_restricted_editor_names\030\005 \001(\010H\002R\033update" + - "RestrictedEditorNames\210\001\001B\022\n\020_workspace_c" + - "lassB&\n$_update_restricted_workspace_cla" + - "ssesB!\n\037_update_restricted_editor_namesB" + - "\007\n\005_nameB\024\n\022_prebuild_settingsB\025\n\023_works" + - "pace_settings\"]\n\033UpdateConfigurationResp" + - "onse\022>\n\rconfiguration\030\001 \001(\0132\030.gitpod.v1." + - "ConfigurationR\rconfiguration\"G\n\032DeleteCo" + - "nfigurationRequest\022)\n\020configuration_id\030\001" + - " \001(\tR\017configurationId\"\035\n\033DeleteConfigura" + - "tionResponse*\311\001\n\026BranchMatchingStrategy\022" + - "(\n$BRANCH_MATCHING_STRATEGY_UNSPECIFIED\020" + - "\000\022+\n\'BRANCH_MATCHING_STRATEGY_DEFAULT_BR" + - "ANCH\020\001\022)\n%BRANCH_MATCHING_STRATEGY_ALL_B" + - "RANCHES\020\002\022-\n)BRANCH_MATCHING_STRATEGY_MA" + - "TCHED_BRANCHES\020\0032\222\004\n\024ConfigurationServic" + - "e\022f\n\023CreateConfiguration\022%.gitpod.v1.Cre" + - "ateConfigurationRequest\032&.gitpod.v1.Crea" + - "teConfigurationResponse\"\000\022]\n\020GetConfigur" + - "ation\022\".gitpod.v1.GetConfigurationReques" + - "t\032#.gitpod.v1.GetConfigurationResponse\"\000" + - "\022c\n\022ListConfigurations\022$.gitpod.v1.ListC" + - "onfigurationsRequest\032%.gitpod.v1.ListCon" + - "figurationsResponse\"\000\022f\n\023UpdateConfigura" + - "tion\022%.gitpod.v1.UpdateConfigurationRequ" + - "est\032&.gitpod.v1.UpdateConfigurationRespo" + - "nse\"\000\022f\n\023DeleteConfiguration\022%.gitpod.v1" + - ".DeleteConfigurationRequest\032&.gitpod.v1." + - "DeleteConfigurationResponse\"\000BQ\n\026io.gitp" + - "od.publicapi.v1Z7github.com/gitpod-io/gi" + - "tpod/components/public-api/go/v1b\006proto3" + "orkspaceClass\022V\n\023activation_strategy\030\006 \001" + + "(\0162%.gitpod.v1.PrebuildActivationStrateg" + + "yR\022activationStrategy\"\266\001\n\021WorkspaceSetti" + + "ngs\022\'\n\017workspace_class\030\001 \001(\tR\016workspaceC" + + "lass\022@\n\034restricted_workspace_classes\030\002 \003" + + "(\tR\032restrictedWorkspaceClasses\0226\n\027restri" + + "cted_editor_names\030\003 \003(\tR\025restrictedEdito" + + "rNames\"v\n\032CreateConfigurationRequest\022\'\n\017" + + "organization_id\030\001 \001(\tR\016organizationId\022\022\n" + + "\004name\030\002 \001(\tR\004name\022\033\n\tclone_url\030\003 \001(\tR\010cl" + + "oneUrl\"]\n\033CreateConfigurationResponse\022>\n" + + "\rconfiguration\030\001 \001(\0132\030.gitpod.v1.Configu" + + "rationR\rconfiguration\"D\n\027GetConfiguratio" + + "nRequest\022)\n\020configuration_id\030\001 \001(\tR\017conf" + + "igurationId\"Z\n\030GetConfigurationResponse\022" + + ">\n\rconfiguration\030\001 \001(\0132\030.gitpod.v1.Confi" + + "gurationR\rconfiguration\"\220\002\n\031ListConfigur" + + "ationsRequest\022\'\n\017organization_id\030\001 \001(\tR\016" + + "organizationId\022\037\n\013search_term\030\002 \001(\tR\nsea" + + "rchTerm\022<\n\npagination\030\003 \001(\0132\034.gitpod.v1." + + "PaginationRequestR\npagination\022#\n\004sort\030\004 " + + "\003(\0132\017.gitpod.v1.SortR\004sort\0220\n\021prebuilds_" + + "enabled\030\005 \001(\010H\000R\020prebuildsEnabled\210\001\001B\024\n\022" + + "_prebuilds_enabled\"\235\001\n\032ListConfiguration" + + "sResponse\022@\n\016configurations\030\001 \003(\0132\030.gitp" + + "od.v1.ConfigurationR\016configurations\022=\n\np" + + "agination\030\002 \001(\0132\035.gitpod.v1.PaginationRe" + + "sponseR\npagination\"\245\n\n\032UpdateConfigurati" + + "onRequest\022)\n\020configuration_id\030\001 \001(\tR\017con" + + "figurationId\022\027\n\004name\030\002 \001(\tH\000R\004name\210\001\001\022h\n" + + "\021prebuild_settings\030\003 \001(\01326.gitpod.v1.Upd" + + "ateConfigurationRequest.PrebuildSettings" + + "H\001R\020prebuildSettings\210\001\001\022k\n\022workspace_set" + + "tings\030\004 \001(\01327.gitpod.v1.UpdateConfigurat" + + "ionRequest.WorkspaceSettingsH\002R\021workspac" + + "eSettings\210\001\001\032\372\003\n\020PrebuildSettings\022\035\n\007ena" + + "bled\030\001 \001(\010H\000R\007enabled\210\001\001\022;\n\027branch_match" + + "ing_pattern\030\002 \001(\tH\001R\025branchMatchingPatte" + + "rn\210\001\001\022O\n\017branch_strategy\030\003 \001(\0162!.gitpod." + + "v1.BranchMatchingStrategyH\002R\016branchStrat" + + "egy\210\001\001\0220\n\021prebuild_interval\030\004 \001(\005H\003R\020pre" + + "buildInterval\210\001\001\022,\n\017workspace_class\030\005 \001(" + + "\tH\004R\016workspaceClass\210\001\001\022[\n\023activation_str" + + "ategy\030\006 \001(\0162%.gitpod.v1.PrebuildActivati" + + "onStrategyH\005R\022activationStrategy\210\001\001B\n\n\010_" + + "enabledB\032\n\030_branch_matching_patternB\022\n\020_" + + "branch_strategyB\024\n\022_prebuild_intervalB\022\n" + + "\020_workspace_classB\026\n\024_activation_strateg" + + "y\032\270\003\n\021WorkspaceSettings\022,\n\017workspace_cla" + + "ss\030\001 \001(\tH\000R\016workspaceClass\210\001\001\022@\n\034restric" + + "ted_workspace_classes\030\002 \003(\tR\032restrictedW" + + "orkspaceClasses\022R\n#update_restricted_wor" + + "kspace_classes\030\003 \001(\010H\001R updateRestricted" + + "WorkspaceClasses\210\001\001\0226\n\027restricted_editor" + + "_names\030\004 \003(\tR\025restrictedEditorNames\022H\n\036u" + + "pdate_restricted_editor_names\030\005 \001(\010H\002R\033u" + + "pdateRestrictedEditorNames\210\001\001B\022\n\020_worksp" + + "ace_classB&\n$_update_restricted_workspac" + + "e_classesB!\n\037_update_restricted_editor_n" + + "amesB\007\n\005_nameB\024\n\022_prebuild_settingsB\025\n\023_" + + "workspace_settings\"]\n\033UpdateConfiguratio" + + "nResponse\022>\n\rconfiguration\030\001 \001(\0132\030.gitpo" + + "d.v1.ConfigurationR\rconfiguration\"G\n\032Del" + + "eteConfigurationRequest\022)\n\020configuration" + + "_id\030\001 \001(\tR\017configurationId\"\035\n\033DeleteConf" + + "igurationResponse*{\n\032PrebuildActivationS" + + "trategy\022,\n(PREBUILD_ACTIVATION_STRATEGY_" + + "UNSPECIFIED\020\000\022/\n+PREBUILD_ACTIVATION_STR" + + "ATEGY_ACTIVITY_BASED\020\001*\311\001\n\026BranchMatchin" + + "gStrategy\022(\n$BRANCH_MATCHING_STRATEGY_UN" + + "SPECIFIED\020\000\022+\n\'BRANCH_MATCHING_STRATEGY_" + + "DEFAULT_BRANCH\020\001\022)\n%BRANCH_MATCHING_STRA" + + "TEGY_ALL_BRANCHES\020\002\022-\n)BRANCH_MATCHING_S" + + "TRATEGY_MATCHED_BRANCHES\020\0032\222\004\n\024Configura" + + "tionService\022f\n\023CreateConfiguration\022%.git" + + "pod.v1.CreateConfigurationRequest\032&.gitp" + + "od.v1.CreateConfigurationResponse\"\000\022]\n\020G" + + "etConfiguration\022\".gitpod.v1.GetConfigura" + + "tionRequest\032#.gitpod.v1.GetConfiguration" + + "Response\"\000\022c\n\022ListConfigurations\022$.gitpo" + + "d.v1.ListConfigurationsRequest\032%.gitpod." + + "v1.ListConfigurationsResponse\"\000\022f\n\023Updat" + + "eConfiguration\022%.gitpod.v1.UpdateConfigu" + + "rationRequest\032&.gitpod.v1.UpdateConfigur" + + "ationResponse\"\000\022f\n\023DeleteConfiguration\022%" + + ".gitpod.v1.DeleteConfigurationRequest\032&." + + "gitpod.v1.DeleteConfigurationResponse\"\000B" + + "Q\n\026io.gitpod.publicapi.v1Z7github.com/gi" + + "tpod-io/gitpod/components/public-api/go/" + + "v1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -13672,7 +14049,7 @@ public io.gitpod.publicapi.v1.ConfigurationOuterClass.DeleteConfigurationRespons internal_static_gitpod_v1_PrebuildSettings_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_gitpod_v1_PrebuildSettings_descriptor, - new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", }); + new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", "ActivationStrategy", }); internal_static_gitpod_v1_WorkspaceSettings_descriptor = getDescriptor().getMessageTypes().get(2); internal_static_gitpod_v1_WorkspaceSettings_fieldAccessorTable = new @@ -13726,7 +14103,7 @@ public io.gitpod.publicapi.v1.ConfigurationOuterClass.DeleteConfigurationRespons internal_static_gitpod_v1_UpdateConfigurationRequest_PrebuildSettings_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_gitpod_v1_UpdateConfigurationRequest_PrebuildSettings_descriptor, - new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", }); + new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", "ActivationStrategy", }); internal_static_gitpod_v1_UpdateConfigurationRequest_WorkspaceSettings_descriptor = internal_static_gitpod_v1_UpdateConfigurationRequest_descriptor.getNestedTypes().get(1); internal_static_gitpod_v1_UpdateConfigurationRequest_WorkspaceSettings_fieldAccessorTable = new diff --git a/components/public-api/typescript-common/src/public-api-converter.ts b/components/public-api/typescript-common/src/public-api-converter.ts index 75d10ff64b2082..b878472e438a67 100644 --- a/components/public-api/typescript-common/src/public-api-converter.ts +++ b/components/public-api/typescript-common/src/public-api-converter.ts @@ -74,6 +74,7 @@ import { AuditLog } from "@gitpod/public-api/lib/gitpod/v1/auditlogs_pb"; import { BranchMatchingStrategy, Configuration, + PrebuildActivationStrategy, PrebuildSettings, WorkspaceSettings, } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; @@ -1066,6 +1067,7 @@ export class PublicAPIConverter { result.branchStrategy = this.toBranchMatchingStrategy(prebuilds.branchStrategy); result.prebuildInterval = prebuilds.prebuildInterval ?? 20; result.workspaceClass = prebuilds.workspaceClass ?? ""; + result.activationStrategy = this.toPrebuildActivationStrategy(prebuilds.activationStrategy); } return result; } @@ -1082,6 +1084,17 @@ export class PublicAPIConverter { return BranchMatchingStrategy.DEFAULT_BRANCH; } + toPrebuildActivationStrategy(strategy?: PrebuildSettingsProtocol.ActivationStrategy): PrebuildActivationStrategy { + switch (strategy) { + case "webhook-based": + return PrebuildActivationStrategy.UNSPECIFIED; + case "activity-based": + return PrebuildActivationStrategy.ACTIVITY_BASED; + default: + return PrebuildActivationStrategy.UNSPECIFIED; + } + } + toWorkspaceSettings(projectSettings: ProjectSettings | undefined): WorkspaceSettings { const result = new WorkspaceSettings(); if (projectSettings?.workspaceClasses?.regular) { diff --git a/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts b/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts index 8f9db6ac1b0dab..4be1148f178ca7 100644 --- a/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts +++ b/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts @@ -14,6 +14,30 @@ import { Message, proto3, Timestamp } from "@bufbuild/protobuf"; import { PaginationRequest, PaginationResponse } from "./pagination_pb.js"; import { Sort } from "./sorting_pb.js"; +/** + * @generated from enum gitpod.v1.PrebuildActivationStrategy + */ +export enum PrebuildActivationStrategy { + /** + * Default value. Implicitly applies to webhoook-based activation + * + * @generated from enum value: PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * Default value for newly enabled prebuilds. + * + * @generated from enum value: PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + */ + ACTIVITY_BASED = 1, +} +// Retrieve enum metadata with: proto3.getEnumType(PrebuildActivationStrategy) +proto3.util.setEnumType(PrebuildActivationStrategy, "gitpod.v1.PrebuildActivationStrategy", [ + { no: 0, name: "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED" }, + { no: 1, name: "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED" }, +]); + /** * @generated from enum gitpod.v1.BranchMatchingStrategy */ @@ -148,6 +172,11 @@ export class PrebuildSettings extends Message { */ workspaceClass = ""; + /** + * @generated from field: gitpod.v1.PrebuildActivationStrategy activation_strategy = 6; + */ + activationStrategy = PrebuildActivationStrategy.UNSPECIFIED; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -161,6 +190,7 @@ export class PrebuildSettings extends Message { { no: 3, name: "branch_strategy", kind: "enum", T: proto3.getEnumType(BranchMatchingStrategy) }, { no: 4, name: "prebuild_interval", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, { no: 5, name: "workspace_class", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 6, name: "activation_strategy", kind: "enum", T: proto3.getEnumType(PrebuildActivationStrategy) }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): PrebuildSettings { @@ -581,6 +611,11 @@ export class UpdateConfigurationRequest_PrebuildSettings extends Message) { super(); proto3.util.initPartial(data, this); @@ -594,6 +629,7 @@ export class UpdateConfigurationRequest_PrebuildSettings extends Message): UpdateConfigurationRequest_PrebuildSettings { diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index 026fc365f881a7..dcb6953e9cd085 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -420,6 +420,7 @@ export class ProjectsService { partialProject.settings = deepmerge(toBeMerged, partialProject.settings); await this.checkProjectSettings(user.id, partialProject.settings); } + await this.handleEnablePrebuild(partialProject); return this.projectDB.updateProject(partialProject); } private async checkProjectSettings(userId: string, settings?: PartialProject["settings"]) { @@ -448,6 +449,21 @@ export class ProjectsService { } } + private async handleEnablePrebuild(partialProject: PartialProject): Promise { + const enablePrebuildsNew = partialProject?.settings?.prebuilds?.enable; + if (typeof enablePrebuildsNew === "boolean") { + const project = await this.projectDB.findProjectById(partialProject.id); + if (!project) { + return; + } + const enablePrebuildsPrev = !!project.settings?.prebuilds?.enable; + if (enablePrebuildsNew && !enablePrebuildsPrev && partialProject?.settings?.prebuilds) { + // new default + partialProject.settings.prebuilds.activationStrategy = "activity-based"; + } + } + } + async isProjectConsideredInactive(userId: string, projectId: string): Promise { const isOlderThan7Days = (d1: string) => isDateSmaller(d1, daysBefore(new Date().toISOString(), 7)); From 62a687bbd4e11fb55bca72c683634194a40a8493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Jul 2024 13:03:59 +0000 Subject: [PATCH 09/16] Update tests --- .../typescript-common/fixtures/toConfiguration_1.golden | 3 ++- .../typescript-common/fixtures/toConfiguration_1.json | 3 ++- .../typescript-common/fixtures/toConfiguration_2.golden | 3 ++- .../typescript-common/fixtures/toConfiguration_2.json | 3 ++- .../typescript-common/fixtures/toPrebuildSettings_1.golden | 3 ++- .../typescript-common/fixtures/toPrebuildSettings_1.json | 3 ++- .../typescript-common/fixtures/toPrebuildSettings_2.golden | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/components/public-api/typescript-common/fixtures/toConfiguration_1.golden b/components/public-api/typescript-common/fixtures/toConfiguration_1.golden index 1a40c624769f3a..303bc897ffd5ab 100644 --- a/components/public-api/typescript-common/fixtures/toConfiguration_1.golden +++ b/components/public-api/typescript-common/fixtures/toConfiguration_1.golden @@ -10,7 +10,8 @@ "branchMatchingPattern": "main", "branchStrategy": "BRANCH_MATCHING_STRATEGY_DEFAULT_BRANCH", "prebuildInterval": 20, - "workspaceClass": "dev" + "workspaceClass": "dev", + "activationStrategy": "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED" }, "workspaceSettings": { "workspaceClass": "dev", diff --git a/components/public-api/typescript-common/fixtures/toConfiguration_1.json b/components/public-api/typescript-common/fixtures/toConfiguration_1.json index 77662b6ae2f9e6..7d099fd8c34224 100644 --- a/components/public-api/typescript-common/fixtures/toConfiguration_1.json +++ b/components/public-api/typescript-common/fixtures/toConfiguration_1.json @@ -14,7 +14,8 @@ "branchMatchingPattern": "main", "branchStrategy": "default-branch", "prebuildInterval": 20, - "workspaceClass": "dev" + "workspaceClass": "dev", + "activationStrategy": "activity-based" } } } diff --git a/components/public-api/typescript-common/fixtures/toConfiguration_2.golden b/components/public-api/typescript-common/fixtures/toConfiguration_2.golden index 18b0391fb10105..586b1361f384e9 100644 --- a/components/public-api/typescript-common/fixtures/toConfiguration_2.golden +++ b/components/public-api/typescript-common/fixtures/toConfiguration_2.golden @@ -10,7 +10,8 @@ "branchMatchingPattern": "main", "branchStrategy": "BRANCH_MATCHING_STRATEGY_DEFAULT_BRANCH", "prebuildInterval": 20, - "workspaceClass": "dev" + "workspaceClass": "dev", + "activationStrategy": "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED" }, "workspaceSettings": { "workspaceClass": "dev", diff --git a/components/public-api/typescript-common/fixtures/toConfiguration_2.json b/components/public-api/typescript-common/fixtures/toConfiguration_2.json index cee757a6b4ccad..e7187f68014e03 100644 --- a/components/public-api/typescript-common/fixtures/toConfiguration_2.json +++ b/components/public-api/typescript-common/fixtures/toConfiguration_2.json @@ -14,7 +14,8 @@ "branchMatchingPattern": "main", "branchStrategy": "default-branch", "prebuildInterval": 20, - "workspaceClass": "dev" + "workspaceClass": "dev", + "activationStrategy": "webhook-based" }, "restrictedWorkspaceClasses": ["cls-1", "cls-2"] } diff --git a/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.golden b/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.golden index 8fcfd14cdf7adc..5334992d141f6d 100644 --- a/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.golden +++ b/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.golden @@ -4,7 +4,8 @@ "branchMatchingPattern": "main", "branchStrategy": "BRANCH_MATCHING_STRATEGY_DEFAULT_BRANCH", "prebuildInterval": 42, - "workspaceClass": "dev" + "workspaceClass": "dev", + "activationStrategy": "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED" }, "err": "" } diff --git a/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.json b/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.json index 4fb10b6ae57bbf..a116b6e38222c8 100644 --- a/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.json +++ b/components/public-api/typescript-common/fixtures/toPrebuildSettings_1.json @@ -3,5 +3,6 @@ "branchMatchingPattern": "main", "branchStrategy": "default-branch", "prebuildInterval": 42, - "workspaceClass": "dev" + "workspaceClass": "dev", + "activationStrategy": "activity-based" } diff --git a/components/public-api/typescript-common/fixtures/toPrebuildSettings_2.golden b/components/public-api/typescript-common/fixtures/toPrebuildSettings_2.golden index f73f10f96ca1ec..a7422181e8f8e2 100644 --- a/components/public-api/typescript-common/fixtures/toPrebuildSettings_2.golden +++ b/components/public-api/typescript-common/fixtures/toPrebuildSettings_2.golden @@ -4,7 +4,8 @@ "branchMatchingPattern": "", "branchStrategy": "BRANCH_MATCHING_STRATEGY_UNSPECIFIED", "prebuildInterval": 0, - "workspaceClass": "" + "workspaceClass": "", + "activationStrategy": "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED" }, "err": "" } From 3c521ed60f02cfbb4540c366d81f665655db7417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Jul 2024 17:04:39 +0000 Subject: [PATCH 10/16] Alert banner --- .../detail/ConfigurationDetailPrebuilds.tsx | 21 ------ .../detail/prebuilds/PrebuildSettingsForm.tsx | 74 ++++++++++++++++++- .../server/src/projects/projects-service.ts | 23 ++---- 3 files changed, 79 insertions(+), 39 deletions(-) diff --git a/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx b/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx index ba1095908edfab..487d38e5eb9bf8 100644 --- a/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx +++ b/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx @@ -9,12 +9,10 @@ import { Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb import { ConfigurationSettingsField } from "./ConfigurationSettingsField"; import { Heading3, Subheading } from "@podkit/typography/Headings"; import { SwitchInputField } from "@podkit/switch/Switch"; -import { TextMuted } from "@podkit/typography/TextMuted"; import { PrebuildSettingsForm } from "./prebuilds/PrebuildSettingsForm"; import { useConfigurationMutation } from "../../data/configurations/configuration-queries"; import { LoadingState } from "@podkit/loading/LoadingState"; import { EnablePrebuildsError } from "./prebuilds/EnablePrebuildsError"; -import { Link } from "react-router-dom"; type Props = { configuration: Configuration; @@ -66,25 +64,6 @@ export const ConfigurationDetailPrebuilds: FC = ({ configuration }) => { checked={enabled} onCheckedChange={updateEnabled} label={configuration.prebuildSettings?.enabled ? "Prebuilds are enabled" : "Prebuilds are disabled"} - description={ - - Enabling requires permissions to configure repository webhooks.{" "} - - Learn more - - . -
- - View prebuild history - - . -
- } /> diff --git a/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx b/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx index 75e056dee2aa95..7506a0e42bfddd 100644 --- a/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx +++ b/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx @@ -4,8 +4,12 @@ * See License.AGPL.txt in the project root for license information. */ -import { BranchMatchingStrategy, Configuration } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; -import { FC, FormEvent, useCallback, useState } from "react"; +import { + BranchMatchingStrategy, + Configuration, + PrebuildActivationStrategy, +} from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; +import { FC, FormEvent, useCallback, useMemo, useState } from "react"; import { ConfigurationSettingsField } from "../ConfigurationSettingsField"; import { Heading3, Subheading } from "@podkit/typography/Headings"; import { InputField } from "../../../components/forms/InputField"; @@ -17,6 +21,11 @@ import { LoadingButton } from "@podkit/buttons/LoadingButton"; import { InputFieldHint } from "../../../components/forms/InputFieldHint"; import { DEFAULT_WS_CLASS } from "../../../data/workspaces/workspace-classes-query"; import { Select, SelectItem, SelectTrigger, SelectValue, SelectContent } from "@podkit/select/Select"; +import Alert from "../../../components/Alert"; +import { useUserLoader } from "../../../hooks/use-user-loader"; +import { useUpdateCurrentUserMutation } from "../../../data/current-user/update-mutation"; +import { trackEvent } from "../../../Analytics"; +import dayjs from "dayjs"; const DEFAULT_PREBUILD_COMMIT_INTERVAL = 20; @@ -24,8 +33,14 @@ type Props = { configuration: Configuration; }; +const COACHMARK_KEY = "new_prebuilds_trigger_notification"; + export const PrebuildSettingsForm: FC = ({ configuration }) => { const { toast } = useToast(); + + const { user } = useUserLoader(); + const { mutate: updateUser } = useUpdateCurrentUserMutation(); + const updateConfiguration = useConfigurationMutation(); const [interval, setInterval] = useState( @@ -41,6 +56,8 @@ export const PrebuildSettingsForm: FC = ({ configuration }) => { configuration.prebuildSettings?.workspaceClass || DEFAULT_WS_CLASS, ); + const [isTriggerNotificationOpen, setIsTriggerNotificationOpen] = useState(true); + const handleSubmit = useCallback( (e: FormEvent) => { e.preventDefault(); @@ -83,8 +100,61 @@ export const PrebuildSettingsForm: FC = ({ configuration }) => { setBranchStrategy(parseInt(val, 10) as BranchMatchingStrategy); }, []); + const dismissNotification = useCallback(() => { + updateUser( + { + additionalData: { profile: { coachmarksDismissals: { [COACHMARK_KEY]: dayjs().toISOString() } } }, + }, + { + onSettled: (_, error) => { + trackEvent("coachmark_dismissed", { + name: COACHMARK_KEY, + success: !(error instanceof Error), + }); + setIsTriggerNotificationOpen(false); + }, + }, + ); + }, [updateUser]); + + const showTriggerNotification = useMemo(() => { + if (!isTriggerNotificationOpen || !user) { + return false; + } + + if (configuration.prebuildSettings?.activationStrategy === PrebuildActivationStrategy.ACTIVITY_BASED) { + return false; + } + + // For repositories created after activity-based prebuilds were introduced, don't show it + if (configuration.creationTime && configuration.creationTime.toDate() > new Date("7/15/2024")) { + return false; + } + + return !user.profile?.coachmarksDismissals[COACHMARK_KEY]; + }, [ + configuration.creationTime, + configuration.prebuildSettings?.activationStrategy, + isTriggerNotificationOpen, + user, + ]); + return ( + {showTriggerNotification && ( + dismissNotification()} + showIcon={true} + className="flex rounded p-2 mb-2 w-full" + > + The way prebuilds are triggered is changing.{" "} + + Learn more + + + )}
Prebuild settings These settings will be applied on every Prebuild. diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index dcb6953e9cd085..4ca4b5d86ad5ea 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -420,7 +420,13 @@ export class ProjectsService { partialProject.settings = deepmerge(toBeMerged, partialProject.settings); await this.checkProjectSettings(user.id, partialProject.settings); } - await this.handleEnablePrebuild(partialProject); + if (partialProject?.settings?.prebuilds?.enable) { + const enablePrebuildsPrev = !!existingProject.settings?.prebuilds?.enable; + if (!enablePrebuildsPrev) { + // new default + partialProject.settings.prebuilds.activationStrategy = "activity-based"; + } + } return this.projectDB.updateProject(partialProject); } private async checkProjectSettings(userId: string, settings?: PartialProject["settings"]) { @@ -449,21 +455,6 @@ export class ProjectsService { } } - private async handleEnablePrebuild(partialProject: PartialProject): Promise { - const enablePrebuildsNew = partialProject?.settings?.prebuilds?.enable; - if (typeof enablePrebuildsNew === "boolean") { - const project = await this.projectDB.findProjectById(partialProject.id); - if (!project) { - return; - } - const enablePrebuildsPrev = !!project.settings?.prebuilds?.enable; - if (enablePrebuildsNew && !enablePrebuildsPrev && partialProject?.settings?.prebuilds) { - // new default - partialProject.settings.prebuilds.activationStrategy = "activity-based"; - } - } - } - async isProjectConsideredInactive(userId: string, projectId: string): Promise { const isOlderThan7Days = (d1: string) => isDateSmaller(d1, daysBefore(new Date().toISOString(), 7)); From 97a4afd959a2f62e090ae8f4f8a2cf5ef1afa672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Wed, 10 Jul 2024 09:01:28 +0000 Subject: [PATCH 11/16] add correct changelog path --- .../repositories/detail/prebuilds/PrebuildSettingsForm.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx b/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx index 7506a0e42bfddd..8d8b26cdfc8703 100644 --- a/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx +++ b/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx @@ -150,7 +150,12 @@ export const PrebuildSettingsForm: FC = ({ configuration }) => { className="flex rounded p-2 mb-2 w-full" > The way prebuilds are triggered is changing.{" "} - + Learn more From aff1d1a60cf414e388c4f3859d6a7c9b6df8b6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Wed, 10 Jul 2024 13:09:59 +0000 Subject: [PATCH 12/16] fix tests --- components/server/src/projects/projects-service.spec.db.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/components/server/src/projects/projects-service.spec.db.ts b/components/server/src/projects/projects-service.spec.db.ts index 961ecbb3692d68..9d7448328e7e48 100644 --- a/components/server/src/projects/projects-service.spec.db.ts +++ b/components/server/src/projects/projects-service.spec.db.ts @@ -274,6 +274,7 @@ describe("ProjectsService", async () => { workspaceClass: "ultra", branchStrategy: "matched-branches", branchMatchingPattern: "feature-*", + activationStrategy: "activity-based", }, workspaceClasses: {}, }); From b44ffac4a754dad802e2f30f33f682fbbd629f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Thu, 11 Jul 2024 16:12:29 +0000 Subject: [PATCH 13/16] Remove code for registering prebuilds --- .../src/auth/host-context-provider-impl.ts | 2 - .../bitbucket-server-container-module.ts | 3 - .../bitbucket/bitbucket-container-module.ts | 3 - .../src/github/github-container-module.ts | 3 - .../src/gitlab/gitlab-container-module.ts | 3 - .../bitbucket-server-service.spec.ts | 125 ------------------ .../src/prebuilds/bitbucket-server-service.ts | 75 ----------- .../server/src/prebuilds/bitbucket-service.ts | 78 ----------- components/server/src/prebuilds/constants.ts | 7 + .../src/prebuilds/github-enterprise-app.ts | 4 +- .../server/src/prebuilds/github-service.ts | 86 ------------ components/server/src/prebuilds/gitlab-app.ts | 6 +- .../server/src/prebuilds/gitlab-service.ts | 107 --------------- .../server/src/repohost/repo-service.ts | 15 --- .../server/src/repohost/repository-host.ts | 2 - components/server/src/scm/scm-service.ts | 26 +--- .../test/service-testing-container-module.ts | 5 - .../server/src/user/token-service.spec.db.ts | 6 - .../src/workspace/context-service.spec.db.ts | 4 - 19 files changed, 13 insertions(+), 547 deletions(-) delete mode 100644 components/server/src/prebuilds/bitbucket-server-service.spec.ts delete mode 100644 components/server/src/prebuilds/bitbucket-server-service.ts delete mode 100644 components/server/src/prebuilds/bitbucket-service.ts create mode 100644 components/server/src/prebuilds/constants.ts delete mode 100644 components/server/src/prebuilds/github-service.ts delete mode 100644 components/server/src/prebuilds/gitlab-service.ts delete mode 100644 components/server/src/repohost/repo-service.ts diff --git a/components/server/src/auth/host-context-provider-impl.ts b/components/server/src/auth/host-context-provider-impl.ts index e29caa1dfac447..e16dd2685bb5f3 100644 --- a/components/server/src/auth/host-context-provider-impl.ts +++ b/components/server/src/auth/host-context-provider-impl.ts @@ -12,7 +12,6 @@ import { AuthProviderService } from "./auth-provider-service"; import { HostContextProvider, HostContextProviderFactory } from "./host-context-provider"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { HostContainerMapping } from "./host-container-mapping"; -import { RepositoryService } from "../repohost/repo-service"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat"; @@ -153,7 +152,6 @@ export class HostContextProviderImpl implements HostContextProvider { const container = parentContainer.createChild(); container.bind(AuthProviderParams).toConstantValue(authProviderConfig); container.bind(HostContext).toSelf().inSingletonScope(); - container.bind(RepositoryService).toSelf().inSingletonScope(); const hostContainerMapping = parentContainer.get(HostContainerMapping); const containerModules = hostContainerMapping.get(authProviderConfig.type); diff --git a/components/server/src/bitbucket-server/bitbucket-server-container-module.ts b/components/server/src/bitbucket-server/bitbucket-server-container-module.ts index 725299eba3d176..572c537750e71d 100644 --- a/components/server/src/bitbucket-server/bitbucket-server-container-module.ts +++ b/components/server/src/bitbucket-server/bitbucket-server-container-module.ts @@ -16,8 +16,6 @@ import { BitbucketServerFileProvider } from "./bitbucket-server-file-provider"; import { BitbucketServerRepositoryProvider } from "./bitbucket-server-repository-provider"; import { BitbucketServerTokenHelper } from "./bitbucket-server-token-handler"; import { BitbucketServerTokenValidator } from "./bitbucket-server-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { BitbucketServerService } from "../prebuilds/bitbucket-server-service"; export const bitbucketServerContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -33,5 +31,4 @@ export const bitbucketServerContainerModule = new ContainerModule((bind, _unbind bind(BitbucketServerTokenHelper).toSelf().inSingletonScope(); bind(BitbucketServerTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(BitbucketServerTokenValidator); - rebind(RepositoryService).to(BitbucketServerService).inSingletonScope(); }); diff --git a/components/server/src/bitbucket/bitbucket-container-module.ts b/components/server/src/bitbucket/bitbucket-container-module.ts index 9b4413baa387a7..406b1f61404e5b 100644 --- a/components/server/src/bitbucket/bitbucket-container-module.ts +++ b/components/server/src/bitbucket/bitbucket-container-module.ts @@ -16,8 +16,6 @@ import { BitbucketFileProvider } from "./bitbucket-file-provider"; import { BitbucketRepositoryProvider } from "./bitbucket-repository-provider"; import { BitbucketTokenHelper } from "./bitbucket-token-handler"; import { BitbucketTokenValidator } from "./bitbucket-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { BitbucketService } from "../prebuilds/bitbucket-service"; export const bitbucketContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -33,5 +31,4 @@ export const bitbucketContainerModule = new ContainerModule((bind, _unbind, _isB bind(BitbucketTokenHelper).toSelf().inSingletonScope(); bind(BitbucketTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(BitbucketTokenValidator); - rebind(RepositoryService).to(BitbucketService).inSingletonScope(); }); diff --git a/components/server/src/github/github-container-module.ts b/components/server/src/github/github-container-module.ts index b6ba17e111ac67..46ab7b9985f031 100644 --- a/components/server/src/github/github-container-module.ts +++ b/components/server/src/github/github-container-module.ts @@ -16,8 +16,6 @@ import { GithubRepositoryProvider } from "./github-repository-provider"; import { GitHubTokenHelper } from "./github-token-helper"; import { IGitTokenValidator } from "../workspace/git-token-validator"; import { GitHubTokenValidator } from "./github-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { GitHubService } from "../prebuilds/github-service"; export const githubContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -34,5 +32,4 @@ export const githubContainerModule = new ContainerModule((bind, _unbind, _isBoun bind(GitHubTokenHelper).toSelf().inSingletonScope(); bind(GitHubTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(GitHubTokenValidator); - rebind(RepositoryService).to(GitHubService).inSingletonScope(); }); diff --git a/components/server/src/gitlab/gitlab-container-module.ts b/components/server/src/gitlab/gitlab-container-module.ts index ffdbace30872c3..b7234ef3f432fd 100644 --- a/components/server/src/gitlab/gitlab-container-module.ts +++ b/components/server/src/gitlab/gitlab-container-module.ts @@ -16,8 +16,6 @@ import { GitlabContextParser } from "./gitlab-context-parser"; import { GitlabRepositoryProvider } from "./gitlab-repository-provider"; import { GitLabTokenHelper } from "./gitlab-token-helper"; import { GitLabTokenValidator } from "./gitlab-token-validator"; -import { RepositoryService } from "../repohost/repo-service"; -import { GitlabService } from "../prebuilds/gitlab-service"; export const gitlabContainerModule = new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RepositoryHost).toSelf().inSingletonScope(); @@ -33,5 +31,4 @@ export const gitlabContainerModule = new ContainerModule((bind, _unbind, _isBoun bind(GitLabTokenHelper).toSelf().inSingletonScope(); bind(GitLabTokenValidator).toSelf().inSingletonScope(); bind(IGitTokenValidator).toService(GitLabTokenValidator); - rebind(RepositoryService).to(GitlabService).inSingletonScope(); }); diff --git a/components/server/src/prebuilds/bitbucket-server-service.spec.ts b/components/server/src/prebuilds/bitbucket-server-service.spec.ts deleted file mode 100644 index 9bfad03e73ca59..00000000000000 --- a/components/server/src/prebuilds/bitbucket-server-service.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/** - * Copyright (c) 2022 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 { User } from "@gitpod/gitpod-protocol"; -import { ifEnvVarNotSet } from "@gitpod/gitpod-protocol/lib/util/skip-if"; -import { Container, ContainerModule } from "inversify"; -import { retries, skip, suite, test, timeout } from "@testdeck/mocha"; -import { AuthProviderParams } from "../auth/auth-provider"; -import { HostContextProvider } from "../auth/host-context-provider"; -import { BitbucketServerApi } from "../bitbucket-server/bitbucket-server-api"; -import { BitbucketServerContextParser } from "../bitbucket-server/bitbucket-server-context-parser"; -import { BitbucketServerTokenHelper } from "../bitbucket-server/bitbucket-server-token-handler"; -import { TokenProvider } from "../user/token-provider"; -import { BitbucketServerService } from "./bitbucket-server-service"; -import { expect } from "chai"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url"; - -@suite(timeout(10000), retries(1), skip(ifEnvVarNotSet("GITPOD_TEST_TOKEN_BITBUCKET_SERVER"))) -class TestBitbucketServerService { - protected service: BitbucketServerService; - protected user: User; - - static readonly AUTH_HOST_CONFIG: Partial = { - id: "MyBitbucketServer", - type: "BitbucketServer", - verified: true, - description: "", - icon: "", - host: "bitbucket.gitpod-self-hosted.com", - oauth: { - callBackUrl: "", - clientId: "not-used", - clientSecret: "", - tokenUrl: "", - scope: "", - authorizationUrl: "", - }, - }; - - public before() { - const container = new Container(); - container.load( - new ContainerModule((bind, unbind, isBound, rebind) => { - bind(BitbucketServerService).toSelf().inSingletonScope(); - bind(BitbucketServerContextParser).toSelf().inSingletonScope(); - bind(AuthProviderParams).toConstantValue(TestBitbucketServerService.AUTH_HOST_CONFIG); - bind(BitbucketServerTokenHelper).toSelf().inSingletonScope(); - bind(TokenService).toConstantValue({ - createGitpodToken: async () => ({ token: { value: "foobar123-token" } }), - } as any); - bind(Config).toConstantValue({ - hostUrl: new GitpodHostUrl("https://gitpod.io"), - }); - bind(TokenProvider).toConstantValue({ - getTokenForHost: async () => { - return { - value: process.env["GITPOD_TEST_TOKEN_BITBUCKET_SERVER"] || "undefined", - scopes: [], - }; - }, - }); - bind(BitbucketServerApi).toSelf().inSingletonScope(); - bind(HostContextProvider).toConstantValue({ - get: (hostname: string) => { - authProvider: { - ("BBS"); - } - }, - }); - }), - ); - this.service = container.get(BitbucketServerService); - this.user = { - creationDate: "", - id: "user1", - identities: [ - { - authId: "user1", - authName: "AlexTugarev", - authProviderId: "MyBitbucketServer", - }, - ], - }; - } - - @test async test_installAutomatedPrebuilds_ok() { - try { - await this.service.installAutomatedPrebuilds( - this.user, - "https://bitbucket.gitpod-self-hosted.com/projects/FOO/repos/repo123", - ); - } catch (error) { - expect.fail(error); - } - } - - @test async test_installAutomatedPrebuilds_unauthorized() { - try { - await this.service.installAutomatedPrebuilds( - this.user, - "https://bitbucket.gitpod-self-hosted.com/users/jldec/repos/test-repo", - ); - expect.fail("should have failed"); - } catch (error) {} - } - - @test async test_installAutomatedPrebuilds_in_project_ok() { - try { - await this.service.installAutomatedPrebuilds( - this.user, - "https://bitbucket.gitpod-self-hosted.com/projects/jldec/repos/jldec-repo-march-30", - ); - } catch (error) { - expect.fail(error); - } - } -} - -module.exports = new TestBitbucketServerService(); diff --git a/components/server/src/prebuilds/bitbucket-server-service.ts b/components/server/src/prebuilds/bitbucket-server-service.ts deleted file mode 100644 index 08f3a06c0f6e6e..00000000000000 --- a/components/server/src/prebuilds/bitbucket-server-service.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { User } from "@gitpod/gitpod-protocol"; -import { inject, injectable } from "inversify"; -import { BitbucketServerApi } from "../bitbucket-server/bitbucket-server-api"; -import { BitbucketServerContextParser } from "../bitbucket-server/bitbucket-server-context-parser"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { BitbucketServerApp } from "./bitbucket-server-app"; - -@injectable() -export class BitbucketServerService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(BitbucketServerApi) private readonly api: BitbucketServerApi, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(BitbucketServerContextParser) private readonly contextParser: BitbucketServerContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - const { owner, repoName, repoKind } = await this.contextParser.parseURL(user, cloneUrl); - - const existing = await this.api.getWebhooks(user, { - repoKind, - repositorySlug: repoName, - owner, - }); - const hookUrl = this.getHookUrl(); - if (existing.values && existing.values.some((hook) => hook.url && hook.url.indexOf(hookUrl) !== -1)) { - console.log(`BBS webhook already installed.`, { cloneUrl }); - return; - } - const tokenEntry = await this.tokenService.createGitpodToken( - user, - BitbucketServerService.PREBUILD_TOKEN_SCOPE, - cloneUrl, - ); - try { - await this.api.setWebhook( - user, - { repoKind, repositorySlug: repoName, owner }, - { - name: `Gitpod Prebuilds for ${this.config.hostUrl}.`, - active: true, - configuration: { - secret: "foobar123-secret", - }, - url: hookUrl + `?token=${encodeURIComponent(user.id + "|" + tokenEntry.token.value)}`, - events: ["repo:refs_changed"], - }, - ); - console.log("BBS: webhook installed.", { cloneUrl }); - } catch (error) { - console.error(`BBS: webhook installation failed.`, error, { cloneUrl, error }); - } - } - - protected getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: BitbucketServerApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/prebuilds/bitbucket-service.ts b/components/server/src/prebuilds/bitbucket-service.ts deleted file mode 100644 index c3f65870982ec0..00000000000000 --- a/components/server/src/prebuilds/bitbucket-service.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { User } from "@gitpod/gitpod-protocol"; -import { inject, injectable } from "inversify"; -import { BitbucketApiFactory } from "../bitbucket/bitbucket-api-factory"; -import { BitbucketApp } from "./bitbucket-app"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { BitbucketContextParser } from "../bitbucket/bitbucket-context-parser"; - -@injectable() -export class BitbucketService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(BitbucketApiFactory) private readonly api: BitbucketApiFactory, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(BitbucketContextParser) private readonly bitbucketContextParser: BitbucketContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - try { - const api = await this.api.create(user); - const { owner, repoName } = await this.bitbucketContextParser.parseURL(user, cloneUrl); - const existing = await api.repositories.listWebhooks({ - repo_slug: repoName, - workspace: owner, - }); - const hookUrl = this.getHookUrl(); - if ( - existing.data.values && - existing.data.values.some((hook) => hook.url && hook.url.indexOf(hookUrl) !== -1) - ) { - console.log(`bitbucket webhook already installed on ${owner}/${repoName}`); - return; - } - const tokenEntry = await this.tokenService.createGitpodToken( - user, - BitbucketService.PREBUILD_TOKEN_SCOPE, - cloneUrl, - ); - const response = await api.repositories.createWebhook({ - repo_slug: repoName, - workspace: owner, - // see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/hooks#post - _body: { - description: `Gitpod Prebuilds for ${this.config.hostUrl}.`, - url: hookUrl + `?token=${user.id + "|" + tokenEntry.token.value}`, - active: true, - events: ["repo:push"], - }, - }); - if (response.status !== 201) { - throw new Error(`Couldn't install webhook for ${cloneUrl}: ${response.status}`); - } - console.log("Installed Bitbucket Webhook for " + cloneUrl); - } catch (error) { - console.error("Failed to install Bitbucket webhook for " + cloneUrl, error); - } - } - - protected getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: BitbucketApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/prebuilds/constants.ts b/components/server/src/prebuilds/constants.ts new file mode 100644 index 00000000000000..b5376eee43a88d --- /dev/null +++ b/components/server/src/prebuilds/constants.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2024 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. + */ + +export const PREBUILD_TOKEN_SCOPE = "prebuilds"; diff --git a/components/server/src/prebuilds/github-enterprise-app.ts b/components/server/src/prebuilds/github-enterprise-app.ts index 6f802a4642a2db..d140ec8941a21b 100644 --- a/components/server/src/prebuilds/github-enterprise-app.ts +++ b/components/server/src/prebuilds/github-enterprise-app.ts @@ -15,7 +15,6 @@ import { TokenService } from "../user/token-service"; import { HostContextProvider } from "../auth/host-context-provider"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { CommitContext, CommitInfo, Project, User, WebhookEvent } from "@gitpod/gitpod-protocol"; -import { GitHubService } from "./github-service"; import { URL } from "url"; import { ContextParser } from "../workspace/context-parser-service"; import { RepoURL } from "../repohost"; @@ -25,6 +24,7 @@ import { ProjectsService } from "../projects/projects-service"; import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; import { runWithSubjectId } from "../util/request-context"; import { SubjectId } from "../auth/subject-id"; +import { PREBUILD_TOKEN_SCOPE } from "./constants"; @injectable() export class GitHubEnterpriseApp { @@ -119,7 +119,7 @@ export class GitHubEnterpriseApp { const body = (req as any).rawBody; const tokenEntries = (await this.userService.findTokensForIdentity(user.id, gitpodIdentity)).filter( (tokenEntry) => { - return tokenEntry.token.scopes.includes(GitHubService.PREBUILD_TOKEN_SCOPE); + return tokenEntry.token.scopes.includes(PREBUILD_TOKEN_SCOPE); }, ); const signatureMatched = tokenEntries.some((tokenEntry) => { diff --git a/components/server/src/prebuilds/github-service.ts b/components/server/src/prebuilds/github-service.ts deleted file mode 100644 index 9d338df2b3b24e..00000000000000 --- a/components/server/src/prebuilds/github-service.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { inject, injectable } from "inversify"; -import { GitHubApiError, GitHubRestApi } from "../github/api"; -import { GitHubEnterpriseApp } from "./github-enterprise-app"; -import { GithubContextParser } from "../github/github-context-parser"; -import { User } from "@gitpod/gitpod-protocol"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { RepoURL } from "../repohost"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { UnauthorizedError } from "../errors"; -import { GitHubScope } from "../github/scopes"; -import { containsScopes } from "./token-scopes-inclusion"; - -@injectable() -export class GitHubService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(GitHubRestApi) protected readonly githubApi: GitHubRestApi, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(GithubContextParser) private readonly githubContextParser: GithubContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - const parsedRepoUrl = RepoURL.parseRepoUrl(cloneUrl); - if (!parsedRepoUrl) { - throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Clone URL not parseable.`); - } - let tokenEntry; - try { - const { owner, repoName: repo } = await this.githubContextParser.parseURL(user, cloneUrl); - const webhooks = (await this.githubApi.run(user, (gh) => gh.repos.listWebhooks({ owner, repo }))).data; - for (const webhook of webhooks) { - if (webhook.config.url === this.getHookUrl()) { - await this.githubApi.run(user, (gh) => - gh.repos.deleteWebhook({ owner, repo, hook_id: webhook.id }), - ); - } - } - tokenEntry = await this.tokenService.createGitpodToken(user, GitHubService.PREBUILD_TOKEN_SCOPE, cloneUrl); - const config = { - url: this.getHookUrl(), - content_type: "json", - secret: user.id + "|" + tokenEntry.token.value, - }; - await this.githubApi.run(user, (gh) => gh.repos.createWebhook({ owner, repo, config })); - } catch (error) { - // Hint: here we catch all GH API errors to forward them as Unauthorized to FE, - // eventually that should be done depending on the error code. - // Also, if user is not connected at all, then the GH API wrapper is throwing - // the same error type, but with `providerIsConnected: false`. - - if (GitHubApiError.is(error)) { - // TODO check for `error.code` - throw UnauthorizedError.create({ - host: parsedRepoUrl.host, - providerType: "GitHub", - repoName: parsedRepoUrl.repo, - requiredScopes: GitHubScope.Requirements.PRIVATE_REPO, - providerIsConnected: true, - isMissingScopes: containsScopes(tokenEntry?.token.scopes, GitHubScope.Requirements.PRIVATE_REPO), - }); - } - throw error; - } - } - - protected getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: GitHubEnterpriseApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/prebuilds/gitlab-app.ts b/components/server/src/prebuilds/gitlab-app.ts index 3432aabd6d1e04..9ce6e5c30788f8 100644 --- a/components/server/src/prebuilds/gitlab-app.ts +++ b/components/server/src/prebuilds/gitlab-app.ts @@ -12,7 +12,6 @@ import { PrebuildManager } from "./prebuild-manager"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { TokenService } from "../user/token-service"; import { HostContextProvider } from "../auth/host-context-provider"; -import { GitlabService } from "./gitlab-service"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { ContextParser } from "../workspace/context-parser-service"; import { RepoURL } from "../repohost"; @@ -22,6 +21,7 @@ import { ProjectsService } from "../projects/projects-service"; import { runWithSubjectId } from "../util/request-context"; import { SubjectId } from "../auth/subject-id"; import { SYSTEM_USER, SYSTEM_USER_ID } from "../authorization/authorizer"; +import { PREBUILD_TOKEN_SCOPE } from "./constants"; @injectable() export class GitLabApp { @@ -122,8 +122,8 @@ export class GitLabApp { throw new Error(`User ${user.id} has no token with given value.`); } if ( - token.token.scopes.indexOf(GitlabService.PREBUILD_TOKEN_SCOPE) === -1 || - token.token.scopes.indexOf(context.repository.git_http_url) === -1 + !token.token.scopes.includes(PREBUILD_TOKEN_SCOPE) || + !token.token.scopes.includes(context.repository.git_http_url) ) { throw new Error( `The provided token is not valid for the repository ${context.repository.git_http_url}.`, diff --git a/components/server/src/prebuilds/gitlab-service.ts b/components/server/src/prebuilds/gitlab-service.ts deleted file mode 100644 index d5effe4a75b3ac..00000000000000 --- a/components/server/src/prebuilds/gitlab-service.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2022 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 { RepositoryService } from "../repohost/repo-service"; -import { User } from "@gitpod/gitpod-protocol"; -import { inject, injectable } from "inversify"; -import { GitLabApi, GitLab } from "../gitlab/api"; -import { GitLabApp } from "./gitlab-app"; -import { Config } from "../config"; -import { TokenService } from "../user/token-service"; -import { GitlabContextParser } from "../gitlab/gitlab-context-parser"; -import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; -import { RepoURL } from "../repohost"; -import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; -import { UnauthorizedRepositoryAccessError } from "@gitpod/public-api-common/lib/public-api-errors"; -import { UnauthorizedError } from "../errors"; -import { GitLabScope } from "../gitlab/scopes"; -import { containsScopes } from "./token-scopes-inclusion"; - -@injectable() -export class GitlabService extends RepositoryService { - static PREBUILD_TOKEN_SCOPE = "prebuilds"; - - constructor( - @inject(GitLabApi) protected api: GitLabApi, - @inject(Config) private readonly config: Config, - @inject(TokenService) private readonly tokenService: TokenService, - @inject(GitlabContextParser) private readonly gitlabContextParser: GitlabContextParser, - ) { - super(); - } - - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - const parsedRepoUrl = RepoURL.parseRepoUrl(cloneUrl); - if (!parsedRepoUrl) { - throw new ApplicationError(ErrorCodes.BAD_REQUEST, `Clone URL not parseable.`); - } - - let api; - try { - api = await this.api.create(user); // throw UnauthorizedError - } catch (error) { - if (error instanceof UnauthorizedRepositoryAccessError) { - error.info.host = parsedRepoUrl.host; - error.info.providerIsConnected = false; - error.info.isMissingScopes = true; - error.info.providerType = "GitLab"; - } - throw error; - } - - let tokenEntry; - try { - // throws GitLabApiError 404 - const { owner, repoName } = await this.gitlabContextParser.parseURL(user, cloneUrl); - const gitlabProjectId = `${owner}/${repoName}`; - // throws GitLabApiError 403 - const hooks = (await api.ProjectHooks.all(gitlabProjectId)) as unknown as GitLab.ProjectHook[]; - if (GitLab.ApiError.is(hooks)) { - throw hooks; - } - let existingProps: any = {}; - for (const hook of hooks) { - if (hook.url === this.getHookUrl()) { - log.info("Deleting existing hook"); - existingProps = hook; - // throws GitLabApiError 403 - await api.ProjectHooks.remove(gitlabProjectId, hook.id); - } - } - tokenEntry = await this.tokenService.createGitpodToken(user, GitlabService.PREBUILD_TOKEN_SCOPE, cloneUrl); - // throws GitLabApiError 403 - await api.ProjectHooks.add(gitlabProjectId, this.getHookUrl(), >{ - ...existingProps, - push_events: true, - token: user.id + "|" + tokenEntry.token.value, - }); - log.info("Installed Webhook for " + cloneUrl, { cloneUrl, userId: user.id }); - } catch (error) { - if (GitLab.ApiError.is(error)) { - // TODO check for `error.code` - - throw UnauthorizedError.create({ - host: parsedRepoUrl.host, - providerType: "GitLab", - repoName: parsedRepoUrl.repo, - requiredScopes: GitLabScope.Requirements.REPO, - providerIsConnected: true, - isMissingScopes: containsScopes(tokenEntry?.token?.scopes, GitLabScope.Requirements.REPO), - }); - } - throw error; - } - } - - private getHookUrl() { - return this.config.hostUrl - .asPublicServices() - .with({ - pathname: GitLabApp.path, - }) - .toString(); - } -} diff --git a/components/server/src/repohost/repo-service.ts b/components/server/src/repohost/repo-service.ts deleted file mode 100644 index 6b5f69b4854d32..00000000000000 --- a/components/server/src/repohost/repo-service.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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 { User } from "@gitpod/gitpod-protocol"; -import { injectable } from "inversify"; - -@injectable() -export class RepositoryService { - async installAutomatedPrebuilds(user: User, cloneUrl: string): Promise { - throw new Error("unsupported"); - } -} diff --git a/components/server/src/repohost/repository-host.ts b/components/server/src/repohost/repository-host.ts index eba852ee0bf488..e87752ab0d1655 100644 --- a/components/server/src/repohost/repository-host.ts +++ b/components/server/src/repohost/repository-host.ts @@ -8,11 +8,9 @@ import { inject, injectable } from "inversify"; import { FileProvider } from "./file-provider"; import { RepositoryProvider } from "./repository-provider"; -import { RepositoryService } from "./repo-service"; @injectable() export class RepositoryHost { @inject(FileProvider) fileProvider: FileProvider; @inject(RepositoryProvider) repositoryProvider: RepositoryProvider; - @inject(RepositoryService) repositoryService: RepositoryService; } diff --git a/components/server/src/scm/scm-service.ts b/components/server/src/scm/scm-service.ts index b13dbfc68a7cb6..8cf20186727a71 100644 --- a/components/server/src/scm/scm-service.ts +++ b/components/server/src/scm/scm-service.ts @@ -8,9 +8,8 @@ import { inject, injectable } from "inversify"; import { Authorizer } from "../authorization/authorizer"; import { Config } from "../config"; import { TokenProvider } from "../user/token-provider"; -import { CommitContext, Project, SuggestedRepository, Token, User, WorkspaceInfo } from "@gitpod/gitpod-protocol"; +import { CommitContext, Project, SuggestedRepository, Token, WorkspaceInfo } from "@gitpod/gitpod-protocol"; import { HostContextProvider } from "../auth/host-context-provider"; -import { RepoURL } from "../repohost"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { AuthProviderService } from "../auth/auth-provider-service"; import { UserService } from "../user/user-service"; @@ -53,29 +52,6 @@ export class ScmService { return token; } - public async installWebhookForPrebuilds(project: Project, installer: User) { - // Install the prebuilds webhook if possible - const { teamId, cloneUrl } = project; - const parsedUrl = RepoURL.parseRepoUrl(project.cloneUrl); - const hostContext = parsedUrl?.host ? this.hostContextProvider.get(parsedUrl?.host) : undefined; - - if (!hostContext) { - throw new ApplicationError(ErrorCodes.NOT_FOUND, `SCM provider not found.`); - } - - const repositoryService = hostContext.services?.repositoryService; - if (repositoryService) { - const logPayload = { organizationId: teamId, installer: installer.id, cloneUrl: project.cloneUrl }; - try { - await repositoryService.installAutomatedPrebuilds(installer, cloneUrl); - log.info("Webhook for prebuilds installed.", logPayload); - } catch (error) { - log.error("Failed to install webhook for prebuilds.", error, logPayload); - throw error; - } - } - } - /** * `guessTokenScopes` allows clients to retrieve scopes that would be necessary for a specified * git operation on a specified repository. diff --git a/components/server/src/test/service-testing-container-module.ts b/components/server/src/test/service-testing-container-module.ts index f494f7d12ccd02..f0b682b4da4512 100644 --- a/components/server/src/test/service-testing-container-module.ts +++ b/components/server/src/test/service-testing-container-module.ts @@ -110,11 +110,6 @@ const mockApplyingContainerModule = new ContainerModule((bind, unbound, isbound, }, }, services: { - repositoryService: { - installAutomatedPrebuilds: async (user: any, cloneUrl: string) => { - webhooks.add(cloneUrl); - }, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; diff --git a/components/server/src/user/token-service.spec.db.ts b/components/server/src/user/token-service.spec.db.ts index f32a98d97abad9..8b0f1bdb403c5c 100644 --- a/components/server/src/user/token-service.spec.db.ts +++ b/components/server/src/user/token-service.spec.db.ts @@ -88,9 +88,6 @@ describe("TokenService", async () => { }, }, services: { - repositoryService: { - installAutomatedPrebuilds: async (user: any, cloneUrl: string) => {}, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; @@ -116,9 +113,6 @@ describe("TokenService", async () => { }, }, services: { - repositoryService: { - installAutomatedPrebuilds: async (user: any, cloneUrl: string) => {}, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; diff --git a/components/server/src/workspace/context-service.spec.db.ts b/components/server/src/workspace/context-service.spec.db.ts index cdb65828da5d07..c86981a5723808 100644 --- a/components/server/src/workspace/context-service.spec.db.ts +++ b/components/server/src/workspace/context-service.spec.db.ts @@ -138,10 +138,6 @@ describe("ContextService", async () => { }, }, services: { - repositoryService: { - installAutomatedPrebuilds: () => {}, - canInstallAutomatedPrebuilds: async () => {}, - }, repositoryProvider: { hasReadAccess: async (user: any, owner: string, repo: string) => { return true; From 1099d91916f0c8f50c4469df07a6b9ff785d001b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Fri, 12 Jul 2024 08:01:16 +0000 Subject: [PATCH 14/16] Still pass prebuild preconditions in order to run --- .../server/src/workspace/workspace-service.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/components/server/src/workspace/workspace-service.ts b/components/server/src/workspace/workspace-service.ts index 7d6c1a13bc66a2..34b6e178a2a5c2 100644 --- a/components/server/src/workspace/workspace-service.ts +++ b/components/server/src/workspace/workspace-service.ts @@ -402,6 +402,8 @@ export class WorkspaceService { user: User; }): void { (async () => { + const prebuildManager = this.prebuildManager(); + const context = (await this.contextParser.handle(ctx, user, workspace.contextURL)) as CommitContext; log.info({ workspaceId: workspace.id }, "starting prebuild after workspace creation", { projectId: project.id, @@ -409,7 +411,18 @@ export class WorkspaceService { contextURL: workspace.contextURL, context, }); - await this.prebuildManager().startPrebuild(ctx, { + const config = await prebuildManager.fetchConfig(ctx, user, context, project?.teamId); + const prebuildPrecondition = prebuildManager.checkPrebuildPrecondition({ + config, + project, + context, + }); + if (!prebuildPrecondition.shouldRun) { + log.info("Workspace create event: No prebuild.", { config, context }); + return; + } + + await prebuildManager.startPrebuild(ctx, { user, project, forcePrebuild: false, From a2980a432e821abcf768a54cf5e6f91de02e4c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Fri, 12 Jul 2024 08:10:43 +0000 Subject: [PATCH 15/16] Re-add link to prebuild history of a repo --- .../repositories/detail/ConfigurationDetailPrebuilds.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx b/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx index 487d38e5eb9bf8..dbaf86af8ffcfc 100644 --- a/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx +++ b/components/dashboard/src/repositories/detail/ConfigurationDetailPrebuilds.tsx @@ -13,6 +13,8 @@ import { PrebuildSettingsForm } from "./prebuilds/PrebuildSettingsForm"; import { useConfigurationMutation } from "../../data/configurations/configuration-queries"; import { LoadingState } from "@podkit/loading/LoadingState"; import { EnablePrebuildsError } from "./prebuilds/EnablePrebuildsError"; +import { TextMuted } from "@podkit/typography/TextMuted"; +import { Link } from "react-router-dom"; type Props = { configuration: Configuration; @@ -64,6 +66,13 @@ export const ConfigurationDetailPrebuilds: FC = ({ configuration }) => { checked={enabled} onCheckedChange={updateEnabled} label={configuration.prebuildSettings?.enabled ? "Prebuilds are enabled" : "Prebuilds are disabled"} + description={ + + + View prebuild history + + + } /> From 378040e68e1d7df9db43a24f22190b266252c65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Fri, 12 Jul 2024 11:57:06 +0000 Subject: [PATCH 16/16] Rename to trigger strategy and change project activity indicator --- .../detail/prebuilds/PrebuildSettingsForm.tsx | 11 +- .../src/teams-projects-protocol.ts | 8 +- .../public-api/gitpod/v1/configuration.proto | 10 +- .../public-api/go/v1/configuration.pb.go | 512 +++++++++--------- .../publicapi/v1/ConfigurationOuterClass.java | 457 ++++++++-------- .../java/io/gitpod/publicapi/v1/Error.java | 322 ++++++++++- .../src/public-api-converter.ts | 12 +- .../src/gitpod/v1/configuration_pb.ts | 28 +- .../server/src/prebuilds/prebuild-manager.ts | 6 +- .../src/projects/projects-service.spec.db.ts | 2 +- .../server/src/projects/projects-service.ts | 2 +- .../server/src/workspace/workspace-service.ts | 1 + 12 files changed, 832 insertions(+), 539 deletions(-) diff --git a/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx b/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx index 8d8b26cdfc8703..2fbe2dce275769 100644 --- a/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx +++ b/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx @@ -7,7 +7,7 @@ import { BranchMatchingStrategy, Configuration, - PrebuildActivationStrategy, + PrebuildTriggerStrategy, } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; import { FC, FormEvent, useCallback, useMemo, useState } from "react"; import { ConfigurationSettingsField } from "../ConfigurationSettingsField"; @@ -122,7 +122,7 @@ export const PrebuildSettingsForm: FC = ({ configuration }) => { return false; } - if (configuration.prebuildSettings?.activationStrategy === PrebuildActivationStrategy.ACTIVITY_BASED) { + if (configuration.prebuildSettings?.triggerStrategy === PrebuildTriggerStrategy.ACTIVITY_BASED) { return false; } @@ -132,12 +132,7 @@ export const PrebuildSettingsForm: FC = ({ configuration }) => { } return !user.profile?.coachmarksDismissals[COACHMARK_KEY]; - }, [ - configuration.creationTime, - configuration.prebuildSettings?.activationStrategy, - isTriggerNotificationOpen, - user, - ]); + }, [configuration.creationTime, configuration.prebuildSettings?.triggerStrategy, isTriggerNotificationOpen, user]); return ( diff --git a/components/gitpod-protocol/src/teams-projects-protocol.ts b/components/gitpod-protocol/src/teams-projects-protocol.ts index ed9e89b9d959b2..71f2ddd368a64b 100644 --- a/components/gitpod-protocol/src/teams-projects-protocol.ts +++ b/components/gitpod-protocol/src/teams-projects-protocol.ts @@ -31,7 +31,7 @@ export interface ProjectSettings { } export namespace PrebuildSettings { export type BranchStrategy = "default-branch" | "all-branches" | "matched-branches"; - export type ActivationStrategy = "activity-based" | "webhook-based"; + export type TriggerStrategy = "activity-based" | "webhook-based"; } export interface PrebuildSettings { @@ -58,9 +58,9 @@ export interface PrebuildSettings { workspaceClass?: string; /** - * Activation strategy for prebuilds. Defaults to "webhook-based" + * The activation strategy for prebuilds. Defaults to "webhook-based" */ - activationStrategy?: PrebuildSettings.ActivationStrategy; + triggerStrategy?: PrebuildSettings.TriggerStrategy; } export interface Project { @@ -94,7 +94,7 @@ export namespace Project { branchMatchingPattern: "**", prebuildInterval: 20, branchStrategy: "all-branches", - activationStrategy: "activity-based", + triggerStrategy: "activity-based", }; /** diff --git a/components/public-api/gitpod/v1/configuration.proto b/components/public-api/gitpod/v1/configuration.proto index b8023700ed198f..25a8916df2cd62 100644 --- a/components/public-api/gitpod/v1/configuration.proto +++ b/components/public-api/gitpod/v1/configuration.proto @@ -19,11 +19,11 @@ message Configuration { WorkspaceSettings workspace_settings = 7; } -enum PrebuildActivationStrategy { +enum PrebuildTriggerStrategy { // Default value. Implicitly applies to webhoook-based activation - PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED = 0; // Default value for newly enabled prebuilds. - PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED = 1; } message PrebuildSettings { @@ -32,7 +32,7 @@ message PrebuildSettings { BranchMatchingStrategy branch_strategy = 3; int32 prebuild_interval = 4; string workspace_class = 5; - PrebuildActivationStrategy activation_strategy = 6; + PrebuildTriggerStrategy trigger_strategy = 6; } enum BranchMatchingStrategy { @@ -105,7 +105,7 @@ message UpdateConfigurationRequest { optional BranchMatchingStrategy branch_strategy = 3; optional int32 prebuild_interval = 4; optional string workspace_class = 5; - optional PrebuildActivationStrategy activation_strategy = 6; + optional PrebuildTriggerStrategy trigger_strategy = 6; } message WorkspaceSettings { optional string workspace_class = 1; diff --git a/components/public-api/go/v1/configuration.pb.go b/components/public-api/go/v1/configuration.pb.go index cbe571621e4320..421eb02f476a95 100644 --- a/components/public-api/go/v1/configuration.pb.go +++ b/components/public-api/go/v1/configuration.pb.go @@ -25,51 +25,51 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type PrebuildActivationStrategy int32 +type PrebuildTriggerStrategy int32 const ( // Default value. Implicitly applies to webhoook-based activation - PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED PrebuildActivationStrategy = 0 + PrebuildTriggerStrategy_PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED PrebuildTriggerStrategy = 0 // Default value for newly enabled prebuilds. - PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED PrebuildActivationStrategy = 1 + PrebuildTriggerStrategy_PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED PrebuildTriggerStrategy = 1 ) -// Enum value maps for PrebuildActivationStrategy. +// Enum value maps for PrebuildTriggerStrategy. var ( - PrebuildActivationStrategy_name = map[int32]string{ - 0: "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED", - 1: "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED", + PrebuildTriggerStrategy_name = map[int32]string{ + 0: "PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED", + 1: "PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED", } - PrebuildActivationStrategy_value = map[string]int32{ - "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED": 0, - "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED": 1, + PrebuildTriggerStrategy_value = map[string]int32{ + "PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED": 0, + "PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED": 1, } ) -func (x PrebuildActivationStrategy) Enum() *PrebuildActivationStrategy { - p := new(PrebuildActivationStrategy) +func (x PrebuildTriggerStrategy) Enum() *PrebuildTriggerStrategy { + p := new(PrebuildTriggerStrategy) *p = x return p } -func (x PrebuildActivationStrategy) String() string { +func (x PrebuildTriggerStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (PrebuildActivationStrategy) Descriptor() protoreflect.EnumDescriptor { +func (PrebuildTriggerStrategy) Descriptor() protoreflect.EnumDescriptor { return file_gitpod_v1_configuration_proto_enumTypes[0].Descriptor() } -func (PrebuildActivationStrategy) Type() protoreflect.EnumType { +func (PrebuildTriggerStrategy) Type() protoreflect.EnumType { return &file_gitpod_v1_configuration_proto_enumTypes[0] } -func (x PrebuildActivationStrategy) Number() protoreflect.EnumNumber { +func (x PrebuildTriggerStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use PrebuildActivationStrategy.Descriptor instead. -func (PrebuildActivationStrategy) EnumDescriptor() ([]byte, []int) { +// Deprecated: Use PrebuildTriggerStrategy.Descriptor instead. +func (PrebuildTriggerStrategy) EnumDescriptor() ([]byte, []int) { return file_gitpod_v1_configuration_proto_rawDescGZIP(), []int{0} } @@ -225,12 +225,12 @@ type PrebuildSettings struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - BranchMatchingPattern string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3" json:"branch_matching_pattern,omitempty"` - BranchStrategy BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy" json:"branch_strategy,omitempty"` - PrebuildInterval int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3" json:"prebuild_interval,omitempty"` - WorkspaceClass string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3" json:"workspace_class,omitempty"` - ActivationStrategy PrebuildActivationStrategy `protobuf:"varint,6,opt,name=activation_strategy,json=activationStrategy,proto3,enum=gitpod.v1.PrebuildActivationStrategy" json:"activation_strategy,omitempty"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + BranchMatchingPattern string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3" json:"branch_matching_pattern,omitempty"` + BranchStrategy BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy" json:"branch_strategy,omitempty"` + PrebuildInterval int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3" json:"prebuild_interval,omitempty"` + WorkspaceClass string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3" json:"workspace_class,omitempty"` + TriggerStrategy PrebuildTriggerStrategy `protobuf:"varint,6,opt,name=trigger_strategy,json=triggerStrategy,proto3,enum=gitpod.v1.PrebuildTriggerStrategy" json:"trigger_strategy,omitempty"` } func (x *PrebuildSettings) Reset() { @@ -300,11 +300,11 @@ func (x *PrebuildSettings) GetWorkspaceClass() string { return "" } -func (x *PrebuildSettings) GetActivationStrategy() PrebuildActivationStrategy { +func (x *PrebuildSettings) GetTriggerStrategy() PrebuildTriggerStrategy { if x != nil { - return x.ActivationStrategy + return x.TriggerStrategy } - return PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED + return PrebuildTriggerStrategy_PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED } type WorkspaceSettings struct { @@ -918,12 +918,12 @@ type UpdateConfigurationRequest_PrebuildSettings struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` - BranchMatchingPattern *string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3,oneof" json:"branch_matching_pattern,omitempty"` - BranchStrategy *BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy,oneof" json:"branch_strategy,omitempty"` - PrebuildInterval *int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3,oneof" json:"prebuild_interval,omitempty"` - WorkspaceClass *string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3,oneof" json:"workspace_class,omitempty"` - ActivationStrategy *PrebuildActivationStrategy `protobuf:"varint,6,opt,name=activation_strategy,json=activationStrategy,proto3,enum=gitpod.v1.PrebuildActivationStrategy,oneof" json:"activation_strategy,omitempty"` + Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` + BranchMatchingPattern *string `protobuf:"bytes,2,opt,name=branch_matching_pattern,json=branchMatchingPattern,proto3,oneof" json:"branch_matching_pattern,omitempty"` + BranchStrategy *BranchMatchingStrategy `protobuf:"varint,3,opt,name=branch_strategy,json=branchStrategy,proto3,enum=gitpod.v1.BranchMatchingStrategy,oneof" json:"branch_strategy,omitempty"` + PrebuildInterval *int32 `protobuf:"varint,4,opt,name=prebuild_interval,json=prebuildInterval,proto3,oneof" json:"prebuild_interval,omitempty"` + WorkspaceClass *string `protobuf:"bytes,5,opt,name=workspace_class,json=workspaceClass,proto3,oneof" json:"workspace_class,omitempty"` + TriggerStrategy *PrebuildTriggerStrategy `protobuf:"varint,6,opt,name=trigger_strategy,json=triggerStrategy,proto3,enum=gitpod.v1.PrebuildTriggerStrategy,oneof" json:"trigger_strategy,omitempty"` } func (x *UpdateConfigurationRequest_PrebuildSettings) Reset() { @@ -993,11 +993,11 @@ func (x *UpdateConfigurationRequest_PrebuildSettings) GetWorkspaceClass() string return "" } -func (x *UpdateConfigurationRequest_PrebuildSettings) GetActivationStrategy() PrebuildActivationStrategy { - if x != nil && x.ActivationStrategy != nil { - return *x.ActivationStrategy +func (x *UpdateConfigurationRequest_PrebuildSettings) GetTriggerStrategy() PrebuildTriggerStrategy { + if x != nil && x.TriggerStrategy != nil { + return *x.TriggerStrategy } - return PrebuildActivationStrategy_PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED + return PrebuildTriggerStrategy_PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED } type UpdateConfigurationRequest_WorkspaceSettings struct { @@ -1119,7 +1119,7 @@ var file_gitpod_v1_configuration_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x22, 0xde, 0x02, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x69, 0x6e, 0x67, 0x73, 0x22, 0xd5, 0x02, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, @@ -1135,230 +1135,228 @@ var file_gitpod_v1_configuration_proto_rawDesc = []byte{ 0x28, 0x05, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x56, 0x0a, - 0x13, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, - 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x67, 0x69, 0x74, - 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, - 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x52, 0x12, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xb6, 0x01, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, - 0x6c, 0x61, 0x73, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, - 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, - 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, 0x74, - 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, - 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, - 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, - 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x76, - 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x4d, 0x0a, + 0x10, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0f, 0x74, 0x72, 0x69, + 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xb6, 0x01, 0x0a, + 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72, + 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x36, 0x0a, + 0x17, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, + 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, + 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x76, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x5d, 0x0a, + 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x22, 0x5a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, + 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x90, + 0x02, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x6f, - 0x6e, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, - 0x6f, 0x6e, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x5d, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, - 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, + 0x74, 0x65, 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, + 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x6f, 0x72, 0x74, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, 0x65, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, + 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x22, 0x9d, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x40, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0x99, 0x0a, 0x0a, 0x1a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x5a, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x90, 0x02, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, - 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x12, - 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, - 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x73, 0x6f, - 0x72, 0x74, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, - 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x9d, 0x01, 0x0a, 0x1a, 0x4c, - 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x70, - 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x0a, 0x0a, 0x1a, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x68, 0x0a, - 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, - 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x88, 0x01, 0x01, 0x12, 0x68, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x36, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x48, 0x01, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x6b, + 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x69, 0x74, + 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x48, 0x02, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x88, 0x01, 0x01, 0x1a, 0xee, 0x03, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x48, 0x01, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x6b, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x48, 0x02, 0x52, 0x11, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x88, 0x01, 0x01, 0x1a, 0xfa, 0x03, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x17, 0x62, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, - 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x15, 0x62, 0x72, 0x61, - 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x74, 0x74, 0x65, - 0x72, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, - 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, - 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x48, 0x02, 0x52, 0x0e, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x48, 0x03, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x04, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, - 0x61, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x5b, 0x0a, 0x13, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x05, 0x52, 0x12, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, - 0x1a, 0x0a, 0x18, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, - 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x42, - 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x1a, 0xb8, 0x03, 0x0a, 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, - 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, - 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, - 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, - 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x23, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x17, 0x72, - 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x72, 0x65, - 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x1e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, - 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1b, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, - 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, - 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, - 0x73, 0x42, 0x26, 0x0a, 0x24, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, + 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, + 0x3b, 0x0a, 0x17, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, + 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x01, 0x52, 0x15, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, + 0x6e, 0x67, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x0f, + 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, + 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x02, 0x52, 0x0e, 0x62, 0x72, 0x61, 0x6e, + 0x63, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, + 0x11, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x10, 0x70, 0x72, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x12, + 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x52, 0x0a, + 0x10, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x05, 0x52, 0x0f, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x88, 0x01, + 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x1a, 0x0a, + 0x18, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, + 0x67, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x62, 0x72, + 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x42, 0x14, 0x0a, + 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x1a, 0xb8, 0x03, 0x0a, + 0x11, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, + 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x65, 0x73, 0x12, 0x52, 0x0a, 0x23, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x42, 0x21, 0x0a, 0x1f, 0x5f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, - 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x07, 0x0a, 0x05, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x22, 0x5d, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, - 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x47, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x7b, 0x0a, 0x1a, 0x50, 0x72, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2c, 0x0a, 0x28, 0x50, 0x52, 0x45, 0x42, 0x55, - 0x49, 0x4c, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, - 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2f, 0x0a, 0x2b, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, - 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, - 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x42, - 0x41, 0x53, 0x45, 0x44, 0x10, 0x01, 0x2a, 0xc9, 0x01, 0x0a, 0x16, 0x42, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x12, 0x28, 0x0a, 0x24, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, - 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2b, 0x0a, 0x27, 0x42, - 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, - 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, - 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, 0x42, 0x52, 0x41, 0x4e, - 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, - 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x45, - 0x53, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, + 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, + 0x01, 0x52, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, + 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x17, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, + 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, + 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x48, + 0x0a, 0x1e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x45, 0x64, 0x69, 0x74, 0x6f, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x26, 0x0a, 0x24, + 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x65, 0x73, 0x42, 0x21, 0x0a, 0x1f, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x6f, + 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x5d, 0x0a, + 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0d, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x47, 0x0a, 0x1a, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x72, 0x0a, 0x17, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, + 0x29, 0x0a, 0x25, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x54, 0x52, 0x49, 0x47, + 0x47, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2c, 0x0a, 0x28, 0x50, 0x52, + 0x45, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x53, + 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, + 0x5f, 0x42, 0x41, 0x53, 0x45, 0x44, 0x10, 0x01, 0x2a, 0xc9, 0x01, 0x0a, 0x16, 0x42, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x24, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, - 0x4d, 0x41, 0x54, 0x43, 0x48, 0x45, 0x44, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x45, 0x53, - 0x10, 0x03, 0x32, 0x92, 0x04, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, - 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x63, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, - 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, - 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, - 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x66, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, - 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, 0x0a, 0x16, 0x69, 0x6f, 0x2e, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, - 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x63, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2b, 0x0a, + 0x27, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, + 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, + 0x54, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x29, 0x0a, 0x25, 0x42, 0x52, + 0x41, 0x4e, 0x43, 0x48, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, + 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, + 0x48, 0x45, 0x53, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, 0x5f, + 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, + 0x59, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x45, 0x44, 0x5f, 0x42, 0x52, 0x41, 0x4e, 0x43, 0x48, + 0x45, 0x53, 0x10, 0x03, 0x32, 0x92, 0x04, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, + 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, + 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x70, + 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x2e, 0x67, 0x69, 0x74, + 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x66, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x51, 0x0a, 0x16, 0x69, 0x6f, 0x2e, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, + 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1376,7 +1374,7 @@ func file_gitpod_v1_configuration_proto_rawDescGZIP() []byte { var file_gitpod_v1_configuration_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_gitpod_v1_configuration_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_gitpod_v1_configuration_proto_goTypes = []interface{}{ - (PrebuildActivationStrategy)(0), // 0: gitpod.v1.PrebuildActivationStrategy + (PrebuildTriggerStrategy)(0), // 0: gitpod.v1.PrebuildTriggerStrategy (BranchMatchingStrategy)(0), // 1: gitpod.v1.BranchMatchingStrategy (*Configuration)(nil), // 2: gitpod.v1.Configuration (*PrebuildSettings)(nil), // 3: gitpod.v1.PrebuildSettings @@ -1403,7 +1401,7 @@ var file_gitpod_v1_configuration_proto_depIdxs = []int32{ 3, // 1: gitpod.v1.Configuration.prebuild_settings:type_name -> gitpod.v1.PrebuildSettings 4, // 2: gitpod.v1.Configuration.workspace_settings:type_name -> gitpod.v1.WorkspaceSettings 1, // 3: gitpod.v1.PrebuildSettings.branch_strategy:type_name -> gitpod.v1.BranchMatchingStrategy - 0, // 4: gitpod.v1.PrebuildSettings.activation_strategy:type_name -> gitpod.v1.PrebuildActivationStrategy + 0, // 4: gitpod.v1.PrebuildSettings.trigger_strategy:type_name -> gitpod.v1.PrebuildTriggerStrategy 2, // 5: gitpod.v1.CreateConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration 2, // 6: gitpod.v1.GetConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration 18, // 7: gitpod.v1.ListConfigurationsRequest.pagination:type_name -> gitpod.v1.PaginationRequest @@ -1414,7 +1412,7 @@ var file_gitpod_v1_configuration_proto_depIdxs = []int32{ 16, // 12: gitpod.v1.UpdateConfigurationRequest.workspace_settings:type_name -> gitpod.v1.UpdateConfigurationRequest.WorkspaceSettings 2, // 13: gitpod.v1.UpdateConfigurationResponse.configuration:type_name -> gitpod.v1.Configuration 1, // 14: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings.branch_strategy:type_name -> gitpod.v1.BranchMatchingStrategy - 0, // 15: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings.activation_strategy:type_name -> gitpod.v1.PrebuildActivationStrategy + 0, // 15: gitpod.v1.UpdateConfigurationRequest.PrebuildSettings.trigger_strategy:type_name -> gitpod.v1.PrebuildTriggerStrategy 5, // 16: gitpod.v1.ConfigurationService.CreateConfiguration:input_type -> gitpod.v1.CreateConfigurationRequest 7, // 17: gitpod.v1.ConfigurationService.GetConfiguration:input_type -> gitpod.v1.GetConfigurationRequest 9, // 18: gitpod.v1.ConfigurationService.ListConfigurations:input_type -> gitpod.v1.ListConfigurationsRequest diff --git a/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java b/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java index 7289bb6ba7e925..cfe028d2510a57 100644 --- a/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java +++ b/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/ConfigurationOuterClass.java @@ -30,26 +30,26 @@ public static void registerAllExtensions( (com.google.protobuf.ExtensionRegistryLite) registry); } /** - * Protobuf enum {@code gitpod.v1.PrebuildActivationStrategy} + * Protobuf enum {@code gitpod.v1.PrebuildTriggerStrategy} */ - public enum PrebuildActivationStrategy + public enum PrebuildTriggerStrategy implements com.google.protobuf.ProtocolMessageEnum { /** *
      * Default value. Implicitly applies to webhoook-based activation
      * 
* - * PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + * PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED = 0; */ - PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED(0), + PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED(0), /** *
      * Default value for newly enabled prebuilds.
      * 
* - * PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + * PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED = 1; */ - PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED(1), + PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED(1), UNRECOGNIZED(-1), ; @@ -60,24 +60,24 @@ public enum PrebuildActivationStrategy /* minor= */ 27, /* patch= */ 2, /* suffix= */ "", - PrebuildActivationStrategy.class.getName()); + PrebuildTriggerStrategy.class.getName()); } /** *
      * Default value. Implicitly applies to webhoook-based activation
      * 
* - * PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + * PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED = 0; */ - public static final int PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED_VALUE = 0; + public static final int PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED_VALUE = 0; /** *
      * Default value for newly enabled prebuilds.
      * 
* - * PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + * PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED = 1; */ - public static final int PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED_VALUE = 1; + public static final int PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED_VALUE = 1; public final int getNumber() { @@ -94,7 +94,7 @@ public final int getNumber() { * @deprecated Use {@link #forNumber(int)} instead. */ @java.lang.Deprecated - public static PrebuildActivationStrategy valueOf(int value) { + public static PrebuildTriggerStrategy valueOf(int value) { return forNumber(value); } @@ -102,23 +102,23 @@ public static PrebuildActivationStrategy valueOf(int value) { * @param value The numeric wire value of the corresponding enum entry. * @return The enum associated with the given numeric wire value. */ - public static PrebuildActivationStrategy forNumber(int value) { + public static PrebuildTriggerStrategy forNumber(int value) { switch (value) { - case 0: return PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED; - case 1: return PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED; + case 0: return PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED; + case 1: return PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED; default: return null; } } - public static com.google.protobuf.Internal.EnumLiteMap + public static com.google.protobuf.Internal.EnumLiteMap internalGetValueMap() { return internalValueMap; } private static final com.google.protobuf.Internal.EnumLiteMap< - PrebuildActivationStrategy> internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public PrebuildActivationStrategy findValueByNumber(int number) { - return PrebuildActivationStrategy.forNumber(number); + PrebuildTriggerStrategy> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public PrebuildTriggerStrategy findValueByNumber(int number) { + return PrebuildTriggerStrategy.forNumber(number); } }; @@ -139,9 +139,9 @@ public PrebuildActivationStrategy findValueByNumber(int number) { return io.gitpod.publicapi.v1.ConfigurationOuterClass.getDescriptor().getEnumTypes().get(0); } - private static final PrebuildActivationStrategy[] VALUES = values(); + private static final PrebuildTriggerStrategy[] VALUES = values(); - public static PrebuildActivationStrategy valueOf( + public static PrebuildTriggerStrategy valueOf( com.google.protobuf.Descriptors.EnumValueDescriptor desc) { if (desc.getType() != getDescriptor()) { throw new java.lang.IllegalArgumentException( @@ -155,11 +155,11 @@ public static PrebuildActivationStrategy valueOf( private final int value; - private PrebuildActivationStrategy(int value) { + private PrebuildTriggerStrategy(int value) { this.value = value; } - // @@protoc_insertion_point(enum_scope:gitpod.v1.PrebuildActivationStrategy) + // @@protoc_insertion_point(enum_scope:gitpod.v1.PrebuildTriggerStrategy) } /** @@ -1911,15 +1911,15 @@ public interface PrebuildSettingsOrBuilder extends getWorkspaceClassBytes(); /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The enum numeric value on the wire for activationStrategy. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The enum numeric value on the wire for triggerStrategy. */ - int getActivationStrategyValue(); + int getTriggerStrategyValue(); /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The activationStrategy. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The triggerStrategy. */ - io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy(); + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy getTriggerStrategy(); } /** * Protobuf type {@code gitpod.v1.PrebuildSettings} @@ -1946,7 +1946,7 @@ private PrebuildSettings() { branchMatchingPattern_ = ""; branchStrategy_ = 0; workspaceClass_ = ""; - activationStrategy_ = 0; + triggerStrategy_ = 0; } public static final com.google.protobuf.Descriptors.Descriptor @@ -2080,22 +2080,22 @@ public java.lang.String getWorkspaceClass() { } } - public static final int ACTIVATION_STRATEGY_FIELD_NUMBER = 6; - private int activationStrategy_ = 0; + public static final int TRIGGER_STRATEGY_FIELD_NUMBER = 6; + private int triggerStrategy_ = 0; /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The enum numeric value on the wire for activationStrategy. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The enum numeric value on the wire for triggerStrategy. */ - @java.lang.Override public int getActivationStrategyValue() { - return activationStrategy_; + @java.lang.Override public int getTriggerStrategyValue() { + return triggerStrategy_; } /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The activationStrategy. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The triggerStrategy. */ - @java.lang.Override public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { - io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); - return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + @java.lang.Override public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy getTriggerStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.forNumber(triggerStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.UNRECOGNIZED : result; } private byte memoizedIsInitialized = -1; @@ -2127,8 +2127,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(workspaceClass_)) { com.google.protobuf.GeneratedMessage.writeString(output, 5, workspaceClass_); } - if (activationStrategy_ != io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED.getNumber()) { - output.writeEnum(6, activationStrategy_); + if (triggerStrategy_ != io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED.getNumber()) { + output.writeEnum(6, triggerStrategy_); } getUnknownFields().writeTo(output); } @@ -2157,9 +2157,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(workspaceClass_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(5, workspaceClass_); } - if (activationStrategy_ != io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED.getNumber()) { + if (triggerStrategy_ != io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED.getNumber()) { size += com.google.protobuf.CodedOutputStream - .computeEnumSize(6, activationStrategy_); + .computeEnumSize(6, triggerStrategy_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -2185,7 +2185,7 @@ public boolean equals(final java.lang.Object obj) { != other.getPrebuildInterval()) return false; if (!getWorkspaceClass() .equals(other.getWorkspaceClass())) return false; - if (activationStrategy_ != other.activationStrategy_) return false; + if (triggerStrategy_ != other.triggerStrategy_) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -2208,8 +2208,8 @@ public int hashCode() { hash = (53 * hash) + getPrebuildInterval(); hash = (37 * hash) + WORKSPACE_CLASS_FIELD_NUMBER; hash = (53 * hash) + getWorkspaceClass().hashCode(); - hash = (37 * hash) + ACTIVATION_STRATEGY_FIELD_NUMBER; - hash = (53 * hash) + activationStrategy_; + hash = (37 * hash) + TRIGGER_STRATEGY_FIELD_NUMBER; + hash = (53 * hash) + triggerStrategy_; hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -2346,7 +2346,7 @@ public Builder clear() { branchStrategy_ = 0; prebuildInterval_ = 0; workspaceClass_ = ""; - activationStrategy_ = 0; + triggerStrategy_ = 0; return this; } @@ -2396,7 +2396,7 @@ private void buildPartial0(io.gitpod.publicapi.v1.ConfigurationOuterClass.Prebui result.workspaceClass_ = workspaceClass_; } if (((from_bitField0_ & 0x00000020) != 0)) { - result.activationStrategy_ = activationStrategy_; + result.triggerStrategy_ = triggerStrategy_; } } @@ -2431,8 +2431,8 @@ public Builder mergeFrom(io.gitpod.publicapi.v1.ConfigurationOuterClass.Prebuild bitField0_ |= 0x00000010; onChanged(); } - if (other.activationStrategy_ != 0) { - setActivationStrategyValue(other.getActivationStrategyValue()); + if (other.triggerStrategy_ != 0) { + setTriggerStrategyValue(other.getTriggerStrategyValue()); } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); @@ -2486,7 +2486,7 @@ public Builder mergeFrom( break; } // case 42 case 48: { - activationStrategy_ = input.readEnum(); + triggerStrategy_ = input.readEnum(); bitField0_ |= 0x00000020; break; } // case 48 @@ -2768,55 +2768,55 @@ public Builder setWorkspaceClassBytes( return this; } - private int activationStrategy_ = 0; + private int triggerStrategy_ = 0; /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The enum numeric value on the wire for activationStrategy. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The enum numeric value on the wire for triggerStrategy. */ - @java.lang.Override public int getActivationStrategyValue() { - return activationStrategy_; + @java.lang.Override public int getTriggerStrategyValue() { + return triggerStrategy_; } /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @param value The enum numeric value on the wire for activationStrategy to set. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @param value The enum numeric value on the wire for triggerStrategy to set. * @return This builder for chaining. */ - public Builder setActivationStrategyValue(int value) { - activationStrategy_ = value; + public Builder setTriggerStrategyValue(int value) { + triggerStrategy_ = value; bitField0_ |= 0x00000020; onChanged(); return this; } /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The activationStrategy. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The triggerStrategy. */ @java.lang.Override - public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { - io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); - return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy getTriggerStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.forNumber(triggerStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.UNRECOGNIZED : result; } /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @param value The activationStrategy to set. + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @param value The triggerStrategy to set. * @return This builder for chaining. */ - public Builder setActivationStrategy(io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy value) { + public Builder setTriggerStrategy(io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000020; - activationStrategy_ = value.getNumber(); + triggerStrategy_ = value.getNumber(); onChanged(); return this; } /** - * .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; * @return This builder for chaining. */ - public Builder clearActivationStrategy() { + public Builder clearTriggerStrategy() { bitField0_ = (bitField0_ & ~0x00000020); - activationStrategy_ = 0; + triggerStrategy_ = 0; onChanged(); return this; } @@ -8883,20 +8883,20 @@ public interface PrebuildSettingsOrBuilder extends getWorkspaceClassBytes(); /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return Whether the activationStrategy field is set. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return Whether the triggerStrategy field is set. */ - boolean hasActivationStrategy(); + boolean hasTriggerStrategy(); /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The enum numeric value on the wire for activationStrategy. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The enum numeric value on the wire for triggerStrategy. */ - int getActivationStrategyValue(); + int getTriggerStrategyValue(); /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The activationStrategy. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The triggerStrategy. */ - io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy(); + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy getTriggerStrategy(); } /** * Protobuf type {@code gitpod.v1.UpdateConfigurationRequest.PrebuildSettings} @@ -8923,7 +8923,7 @@ private PrebuildSettings() { branchMatchingPattern_ = ""; branchStrategy_ = 0; workspaceClass_ = ""; - activationStrategy_ = 0; + triggerStrategy_ = 0; } public static final com.google.protobuf.Descriptors.Descriptor @@ -9097,29 +9097,29 @@ public java.lang.String getWorkspaceClass() { } } - public static final int ACTIVATION_STRATEGY_FIELD_NUMBER = 6; - private int activationStrategy_ = 0; + public static final int TRIGGER_STRATEGY_FIELD_NUMBER = 6; + private int triggerStrategy_ = 0; /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return Whether the activationStrategy field is set. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return Whether the triggerStrategy field is set. */ - @java.lang.Override public boolean hasActivationStrategy() { + @java.lang.Override public boolean hasTriggerStrategy() { return ((bitField0_ & 0x00000020) != 0); } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The enum numeric value on the wire for activationStrategy. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The enum numeric value on the wire for triggerStrategy. */ - @java.lang.Override public int getActivationStrategyValue() { - return activationStrategy_; + @java.lang.Override public int getTriggerStrategyValue() { + return triggerStrategy_; } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The activationStrategy. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The triggerStrategy. */ - @java.lang.Override public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { - io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); - return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + @java.lang.Override public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy getTriggerStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.forNumber(triggerStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.UNRECOGNIZED : result; } private byte memoizedIsInitialized = -1; @@ -9152,7 +9152,7 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) com.google.protobuf.GeneratedMessage.writeString(output, 5, workspaceClass_); } if (((bitField0_ & 0x00000020) != 0)) { - output.writeEnum(6, activationStrategy_); + output.writeEnum(6, triggerStrategy_); } getUnknownFields().writeTo(output); } @@ -9183,7 +9183,7 @@ public int getSerializedSize() { } if (((bitField0_ & 0x00000020) != 0)) { size += com.google.protobuf.CodedOutputStream - .computeEnumSize(6, activationStrategy_); + .computeEnumSize(6, triggerStrategy_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -9224,9 +9224,9 @@ public boolean equals(final java.lang.Object obj) { if (!getWorkspaceClass() .equals(other.getWorkspaceClass())) return false; } - if (hasActivationStrategy() != other.hasActivationStrategy()) return false; - if (hasActivationStrategy()) { - if (activationStrategy_ != other.activationStrategy_) return false; + if (hasTriggerStrategy() != other.hasTriggerStrategy()) return false; + if (hasTriggerStrategy()) { + if (triggerStrategy_ != other.triggerStrategy_) return false; } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; @@ -9260,9 +9260,9 @@ public int hashCode() { hash = (37 * hash) + WORKSPACE_CLASS_FIELD_NUMBER; hash = (53 * hash) + getWorkspaceClass().hashCode(); } - if (hasActivationStrategy()) { - hash = (37 * hash) + ACTIVATION_STRATEGY_FIELD_NUMBER; - hash = (53 * hash) + activationStrategy_; + if (hasTriggerStrategy()) { + hash = (37 * hash) + TRIGGER_STRATEGY_FIELD_NUMBER; + hash = (53 * hash) + triggerStrategy_; } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; @@ -9400,7 +9400,7 @@ public Builder clear() { branchStrategy_ = 0; prebuildInterval_ = 0; workspaceClass_ = ""; - activationStrategy_ = 0; + triggerStrategy_ = 0; return this; } @@ -9456,7 +9456,7 @@ private void buildPartial0(io.gitpod.publicapi.v1.ConfigurationOuterClass.Update to_bitField0_ |= 0x00000010; } if (((from_bitField0_ & 0x00000020) != 0)) { - result.activationStrategy_ = activationStrategy_; + result.triggerStrategy_ = triggerStrategy_; to_bitField0_ |= 0x00000020; } result.bitField0_ |= to_bitField0_; @@ -9493,8 +9493,8 @@ public Builder mergeFrom(io.gitpod.publicapi.v1.ConfigurationOuterClass.UpdateCo bitField0_ |= 0x00000010; onChanged(); } - if (other.hasActivationStrategy()) { - setActivationStrategy(other.getActivationStrategy()); + if (other.hasTriggerStrategy()) { + setTriggerStrategy(other.getTriggerStrategy()); } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); @@ -9548,7 +9548,7 @@ public Builder mergeFrom( break; } // case 42 case 48: { - activationStrategy_ = input.readEnum(); + triggerStrategy_ = input.readEnum(); bitField0_ |= 0x00000020; break; } // case 48 @@ -9867,62 +9867,62 @@ public Builder setWorkspaceClassBytes( return this; } - private int activationStrategy_ = 0; + private int triggerStrategy_ = 0; /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return Whether the activationStrategy field is set. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return Whether the triggerStrategy field is set. */ - @java.lang.Override public boolean hasActivationStrategy() { + @java.lang.Override public boolean hasTriggerStrategy() { return ((bitField0_ & 0x00000020) != 0); } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The enum numeric value on the wire for activationStrategy. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The enum numeric value on the wire for triggerStrategy. */ - @java.lang.Override public int getActivationStrategyValue() { - return activationStrategy_; + @java.lang.Override public int getTriggerStrategyValue() { + return triggerStrategy_; } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @param value The enum numeric value on the wire for activationStrategy to set. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @param value The enum numeric value on the wire for triggerStrategy to set. * @return This builder for chaining. */ - public Builder setActivationStrategyValue(int value) { - activationStrategy_ = value; + public Builder setTriggerStrategyValue(int value) { + triggerStrategy_ = value; bitField0_ |= 0x00000020; onChanged(); return this; } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @return The activationStrategy. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @return The triggerStrategy. */ @java.lang.Override - public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy getActivationStrategy() { - io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.forNumber(activationStrategy_); - return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy.UNRECOGNIZED : result; + public io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy getTriggerStrategy() { + io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy result = io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.forNumber(triggerStrategy_); + return result == null ? io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy.UNRECOGNIZED : result; } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; - * @param value The activationStrategy to set. + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; + * @param value The triggerStrategy to set. * @return This builder for chaining. */ - public Builder setActivationStrategy(io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildActivationStrategy value) { + public Builder setTriggerStrategy(io.gitpod.publicapi.v1.ConfigurationOuterClass.PrebuildTriggerStrategy value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000020; - activationStrategy_ = value.getNumber(); + triggerStrategy_ = value.getNumber(); onChanged(); return this; } /** - * optional .gitpod.v1.PrebuildActivationStrategy activation_strategy = 6 [json_name = "activationStrategy"]; + * optional .gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6 [json_name = "triggerStrategy"]; * @return This builder for chaining. */ - public Builder clearActivationStrategy() { + public Builder clearTriggerStrategy() { bitField0_ = (bitField0_ & ~0x00000020); - activationStrategy_ = 0; + triggerStrategy_ = 0; onChanged(); return this; } @@ -13933,103 +13933,102 @@ public io.gitpod.publicapi.v1.ConfigurationOuterClass.DeleteConfigurationRespons "\021prebuild_settings\030\006 \001(\0132\033.gitpod.v1.Pre" + "buildSettingsR\020prebuildSettings\022K\n\022works" + "pace_settings\030\007 \001(\0132\034.gitpod.v1.Workspac" + - "eSettingsR\021workspaceSettings\"\336\002\n\020Prebuil" + + "eSettingsR\021workspaceSettings\"\325\002\n\020Prebuil" + "dSettings\022\030\n\007enabled\030\001 \001(\010R\007enabled\0226\n\027b" + "ranch_matching_pattern\030\002 \001(\tR\025branchMatc" + "hingPattern\022J\n\017branch_strategy\030\003 \001(\0162!.g" + "itpod.v1.BranchMatchingStrategyR\016branchS" + "trategy\022+\n\021prebuild_interval\030\004 \001(\005R\020preb" + "uildInterval\022\'\n\017workspace_class\030\005 \001(\tR\016w" + - "orkspaceClass\022V\n\023activation_strategy\030\006 \001" + - "(\0162%.gitpod.v1.PrebuildActivationStrateg" + - "yR\022activationStrategy\"\266\001\n\021WorkspaceSetti" + - "ngs\022\'\n\017workspace_class\030\001 \001(\tR\016workspaceC" + - "lass\022@\n\034restricted_workspace_classes\030\002 \003" + - "(\tR\032restrictedWorkspaceClasses\0226\n\027restri" + - "cted_editor_names\030\003 \003(\tR\025restrictedEdito" + - "rNames\"v\n\032CreateConfigurationRequest\022\'\n\017" + - "organization_id\030\001 \001(\tR\016organizationId\022\022\n" + - "\004name\030\002 \001(\tR\004name\022\033\n\tclone_url\030\003 \001(\tR\010cl" + - "oneUrl\"]\n\033CreateConfigurationResponse\022>\n" + - "\rconfiguration\030\001 \001(\0132\030.gitpod.v1.Configu" + - "rationR\rconfiguration\"D\n\027GetConfiguratio" + - "nRequest\022)\n\020configuration_id\030\001 \001(\tR\017conf" + - "igurationId\"Z\n\030GetConfigurationResponse\022" + - ">\n\rconfiguration\030\001 \001(\0132\030.gitpod.v1.Confi" + - "gurationR\rconfiguration\"\220\002\n\031ListConfigur" + - "ationsRequest\022\'\n\017organization_id\030\001 \001(\tR\016" + - "organizationId\022\037\n\013search_term\030\002 \001(\tR\nsea" + - "rchTerm\022<\n\npagination\030\003 \001(\0132\034.gitpod.v1." + - "PaginationRequestR\npagination\022#\n\004sort\030\004 " + - "\003(\0132\017.gitpod.v1.SortR\004sort\0220\n\021prebuilds_" + - "enabled\030\005 \001(\010H\000R\020prebuildsEnabled\210\001\001B\024\n\022" + - "_prebuilds_enabled\"\235\001\n\032ListConfiguration" + - "sResponse\022@\n\016configurations\030\001 \003(\0132\030.gitp" + - "od.v1.ConfigurationR\016configurations\022=\n\np" + - "agination\030\002 \001(\0132\035.gitpod.v1.PaginationRe" + - "sponseR\npagination\"\245\n\n\032UpdateConfigurati" + - "onRequest\022)\n\020configuration_id\030\001 \001(\tR\017con" + - "figurationId\022\027\n\004name\030\002 \001(\tH\000R\004name\210\001\001\022h\n" + - "\021prebuild_settings\030\003 \001(\01326.gitpod.v1.Upd" + - "ateConfigurationRequest.PrebuildSettings" + - "H\001R\020prebuildSettings\210\001\001\022k\n\022workspace_set" + - "tings\030\004 \001(\01327.gitpod.v1.UpdateConfigurat" + - "ionRequest.WorkspaceSettingsH\002R\021workspac" + - "eSettings\210\001\001\032\372\003\n\020PrebuildSettings\022\035\n\007ena" + - "bled\030\001 \001(\010H\000R\007enabled\210\001\001\022;\n\027branch_match" + - "ing_pattern\030\002 \001(\tH\001R\025branchMatchingPatte" + - "rn\210\001\001\022O\n\017branch_strategy\030\003 \001(\0162!.gitpod." + - "v1.BranchMatchingStrategyH\002R\016branchStrat" + - "egy\210\001\001\0220\n\021prebuild_interval\030\004 \001(\005H\003R\020pre" + - "buildInterval\210\001\001\022,\n\017workspace_class\030\005 \001(" + - "\tH\004R\016workspaceClass\210\001\001\022[\n\023activation_str" + - "ategy\030\006 \001(\0162%.gitpod.v1.PrebuildActivati" + - "onStrategyH\005R\022activationStrategy\210\001\001B\n\n\010_" + - "enabledB\032\n\030_branch_matching_patternB\022\n\020_" + - "branch_strategyB\024\n\022_prebuild_intervalB\022\n" + - "\020_workspace_classB\026\n\024_activation_strateg" + - "y\032\270\003\n\021WorkspaceSettings\022,\n\017workspace_cla" + - "ss\030\001 \001(\tH\000R\016workspaceClass\210\001\001\022@\n\034restric" + - "ted_workspace_classes\030\002 \003(\tR\032restrictedW" + - "orkspaceClasses\022R\n#update_restricted_wor" + - "kspace_classes\030\003 \001(\010H\001R updateRestricted" + - "WorkspaceClasses\210\001\001\0226\n\027restricted_editor" + - "_names\030\004 \003(\tR\025restrictedEditorNames\022H\n\036u" + - "pdate_restricted_editor_names\030\005 \001(\010H\002R\033u" + - "pdateRestrictedEditorNames\210\001\001B\022\n\020_worksp" + - "ace_classB&\n$_update_restricted_workspac" + - "e_classesB!\n\037_update_restricted_editor_n" + - "amesB\007\n\005_nameB\024\n\022_prebuild_settingsB\025\n\023_" + - "workspace_settings\"]\n\033UpdateConfiguratio" + - "nResponse\022>\n\rconfiguration\030\001 \001(\0132\030.gitpo" + - "d.v1.ConfigurationR\rconfiguration\"G\n\032Del" + - "eteConfigurationRequest\022)\n\020configuration" + - "_id\030\001 \001(\tR\017configurationId\"\035\n\033DeleteConf" + - "igurationResponse*{\n\032PrebuildActivationS" + - "trategy\022,\n(PREBUILD_ACTIVATION_STRATEGY_" + - "UNSPECIFIED\020\000\022/\n+PREBUILD_ACTIVATION_STR" + - "ATEGY_ACTIVITY_BASED\020\001*\311\001\n\026BranchMatchin" + - "gStrategy\022(\n$BRANCH_MATCHING_STRATEGY_UN" + - "SPECIFIED\020\000\022+\n\'BRANCH_MATCHING_STRATEGY_" + - "DEFAULT_BRANCH\020\001\022)\n%BRANCH_MATCHING_STRA" + - "TEGY_ALL_BRANCHES\020\002\022-\n)BRANCH_MATCHING_S" + - "TRATEGY_MATCHED_BRANCHES\020\0032\222\004\n\024Configura" + - "tionService\022f\n\023CreateConfiguration\022%.git" + - "pod.v1.CreateConfigurationRequest\032&.gitp" + - "od.v1.CreateConfigurationResponse\"\000\022]\n\020G" + - "etConfiguration\022\".gitpod.v1.GetConfigura" + - "tionRequest\032#.gitpod.v1.GetConfiguration" + - "Response\"\000\022c\n\022ListConfigurations\022$.gitpo" + - "d.v1.ListConfigurationsRequest\032%.gitpod." + - "v1.ListConfigurationsResponse\"\000\022f\n\023Updat" + - "eConfiguration\022%.gitpod.v1.UpdateConfigu" + - "rationRequest\032&.gitpod.v1.UpdateConfigur" + - "ationResponse\"\000\022f\n\023DeleteConfiguration\022%" + - ".gitpod.v1.DeleteConfigurationRequest\032&." + - "gitpod.v1.DeleteConfigurationResponse\"\000B" + - "Q\n\026io.gitpod.publicapi.v1Z7github.com/gi" + - "tpod-io/gitpod/components/public-api/go/" + - "v1b\006proto3" + "orkspaceClass\022M\n\020trigger_strategy\030\006 \001(\0162" + + "\".gitpod.v1.PrebuildTriggerStrategyR\017tri" + + "ggerStrategy\"\266\001\n\021WorkspaceSettings\022\'\n\017wo" + + "rkspace_class\030\001 \001(\tR\016workspaceClass\022@\n\034r" + + "estricted_workspace_classes\030\002 \003(\tR\032restr" + + "ictedWorkspaceClasses\0226\n\027restricted_edit" + + "or_names\030\003 \003(\tR\025restrictedEditorNames\"v\n" + + "\032CreateConfigurationRequest\022\'\n\017organizat" + + "ion_id\030\001 \001(\tR\016organizationId\022\022\n\004name\030\002 \001" + + "(\tR\004name\022\033\n\tclone_url\030\003 \001(\tR\010cloneUrl\"]\n" + + "\033CreateConfigurationResponse\022>\n\rconfigur" + + "ation\030\001 \001(\0132\030.gitpod.v1.ConfigurationR\rc" + + "onfiguration\"D\n\027GetConfigurationRequest\022" + + ")\n\020configuration_id\030\001 \001(\tR\017configuration" + + "Id\"Z\n\030GetConfigurationResponse\022>\n\rconfig" + + "uration\030\001 \001(\0132\030.gitpod.v1.ConfigurationR" + + "\rconfiguration\"\220\002\n\031ListConfigurationsReq" + + "uest\022\'\n\017organization_id\030\001 \001(\tR\016organizat" + + "ionId\022\037\n\013search_term\030\002 \001(\tR\nsearchTerm\022<" + + "\n\npagination\030\003 \001(\0132\034.gitpod.v1.Paginatio" + + "nRequestR\npagination\022#\n\004sort\030\004 \003(\0132\017.git" + + "pod.v1.SortR\004sort\0220\n\021prebuilds_enabled\030\005" + + " \001(\010H\000R\020prebuildsEnabled\210\001\001B\024\n\022_prebuild" + + "s_enabled\"\235\001\n\032ListConfigurationsResponse" + + "\022@\n\016configurations\030\001 \003(\0132\030.gitpod.v1.Con" + + "figurationR\016configurations\022=\n\npagination" + + "\030\002 \001(\0132\035.gitpod.v1.PaginationResponseR\np" + + "agination\"\231\n\n\032UpdateConfigurationRequest" + + "\022)\n\020configuration_id\030\001 \001(\tR\017configuratio" + + "nId\022\027\n\004name\030\002 \001(\tH\000R\004name\210\001\001\022h\n\021prebuild" + + "_settings\030\003 \001(\01326.gitpod.v1.UpdateConfig" + + "urationRequest.PrebuildSettingsH\001R\020prebu" + + "ildSettings\210\001\001\022k\n\022workspace_settings\030\004 \001" + + "(\01327.gitpod.v1.UpdateConfigurationReques" + + "t.WorkspaceSettingsH\002R\021workspaceSettings" + + "\210\001\001\032\356\003\n\020PrebuildSettings\022\035\n\007enabled\030\001 \001(" + + "\010H\000R\007enabled\210\001\001\022;\n\027branch_matching_patte" + + "rn\030\002 \001(\tH\001R\025branchMatchingPattern\210\001\001\022O\n\017" + + "branch_strategy\030\003 \001(\0162!.gitpod.v1.Branch" + + "MatchingStrategyH\002R\016branchStrategy\210\001\001\0220\n" + + "\021prebuild_interval\030\004 \001(\005H\003R\020prebuildInte" + + "rval\210\001\001\022,\n\017workspace_class\030\005 \001(\tH\004R\016work" + + "spaceClass\210\001\001\022R\n\020trigger_strategy\030\006 \001(\0162" + + "\".gitpod.v1.PrebuildTriggerStrategyH\005R\017t" + + "riggerStrategy\210\001\001B\n\n\010_enabledB\032\n\030_branch" + + "_matching_patternB\022\n\020_branch_strategyB\024\n" + + "\022_prebuild_intervalB\022\n\020_workspace_classB" + + "\023\n\021_trigger_strategy\032\270\003\n\021WorkspaceSettin" + + "gs\022,\n\017workspace_class\030\001 \001(\tH\000R\016workspace" + + "Class\210\001\001\022@\n\034restricted_workspace_classes" + + "\030\002 \003(\tR\032restrictedWorkspaceClasses\022R\n#up" + + "date_restricted_workspace_classes\030\003 \001(\010H" + + "\001R updateRestrictedWorkspaceClasses\210\001\001\0226" + + "\n\027restricted_editor_names\030\004 \003(\tR\025restric" + + "tedEditorNames\022H\n\036update_restricted_edit" + + "or_names\030\005 \001(\010H\002R\033updateRestrictedEditor" + + "Names\210\001\001B\022\n\020_workspace_classB&\n$_update_" + + "restricted_workspace_classesB!\n\037_update_" + + "restricted_editor_namesB\007\n\005_nameB\024\n\022_pre" + + "build_settingsB\025\n\023_workspace_settings\"]\n" + + "\033UpdateConfigurationResponse\022>\n\rconfigur" + + "ation\030\001 \001(\0132\030.gitpod.v1.ConfigurationR\rc" + + "onfiguration\"G\n\032DeleteConfigurationReque" + + "st\022)\n\020configuration_id\030\001 \001(\tR\017configurat" + + "ionId\"\035\n\033DeleteConfigurationResponse*r\n\027" + + "PrebuildTriggerStrategy\022)\n%PREBUILD_TRIG" + + "GER_STRATEGY_UNSPECIFIED\020\000\022,\n(PREBUILD_T" + + "RIGGER_STRATEGY_ACTIVITY_BASED\020\001*\311\001\n\026Bra" + + "nchMatchingStrategy\022(\n$BRANCH_MATCHING_S" + + "TRATEGY_UNSPECIFIED\020\000\022+\n\'BRANCH_MATCHING" + + "_STRATEGY_DEFAULT_BRANCH\020\001\022)\n%BRANCH_MAT" + + "CHING_STRATEGY_ALL_BRANCHES\020\002\022-\n)BRANCH_" + + "MATCHING_STRATEGY_MATCHED_BRANCHES\020\0032\222\004\n" + + "\024ConfigurationService\022f\n\023CreateConfigura" + + "tion\022%.gitpod.v1.CreateConfigurationRequ" + + "est\032&.gitpod.v1.CreateConfigurationRespo" + + "nse\"\000\022]\n\020GetConfiguration\022\".gitpod.v1.Ge" + + "tConfigurationRequest\032#.gitpod.v1.GetCon" + + "figurationResponse\"\000\022c\n\022ListConfiguratio" + + "ns\022$.gitpod.v1.ListConfigurationsRequest" + + "\032%.gitpod.v1.ListConfigurationsResponse\"" + + "\000\022f\n\023UpdateConfiguration\022%.gitpod.v1.Upd" + + "ateConfigurationRequest\032&.gitpod.v1.Upda" + + "teConfigurationResponse\"\000\022f\n\023DeleteConfi" + + "guration\022%.gitpod.v1.DeleteConfiguration" + + "Request\032&.gitpod.v1.DeleteConfigurationR" + + "esponse\"\000BQ\n\026io.gitpod.publicapi.v1Z7git" + + "hub.com/gitpod-io/gitpod/components/publ" + + "ic-api/go/v1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -14049,7 +14048,7 @@ public io.gitpod.publicapi.v1.ConfigurationOuterClass.DeleteConfigurationRespons internal_static_gitpod_v1_PrebuildSettings_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_gitpod_v1_PrebuildSettings_descriptor, - new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", "ActivationStrategy", }); + new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", "TriggerStrategy", }); internal_static_gitpod_v1_WorkspaceSettings_descriptor = getDescriptor().getMessageTypes().get(2); internal_static_gitpod_v1_WorkspaceSettings_fieldAccessorTable = new @@ -14103,7 +14102,7 @@ public io.gitpod.publicapi.v1.ConfigurationOuterClass.DeleteConfigurationRespons internal_static_gitpod_v1_UpdateConfigurationRequest_PrebuildSettings_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_gitpod_v1_UpdateConfigurationRequest_PrebuildSettings_descriptor, - new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", "ActivationStrategy", }); + new java.lang.String[] { "Enabled", "BranchMatchingPattern", "BranchStrategy", "PrebuildInterval", "WorkspaceClass", "TriggerStrategy", }); internal_static_gitpod_v1_UpdateConfigurationRequest_WorkspaceSettings_descriptor = internal_static_gitpod_v1_UpdateConfigurationRequest_descriptor.getNestedTypes().get(1); internal_static_gitpod_v1_UpdateConfigurationRequest_WorkspaceSettings_fieldAccessorTable = new diff --git a/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/Error.java b/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/Error.java index e6ed134ec551f3..586cd0378435cc 100644 --- a/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/Error.java +++ b/components/public-api/java/src/main/java/io/gitpod/publicapi/v1/Error.java @@ -5506,6 +5506,30 @@ public interface RepositoryNotFoundErrorOrBuilder extends */ com.google.protobuf.ByteString getLastUpdateBytes(); + + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return The repoName. + */ + java.lang.String getRepoName(); + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return The bytes for repoName. + */ + com.google.protobuf.ByteString + getRepoNameBytes(); + + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return The errorMessage. + */ + java.lang.String getErrorMessage(); + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return The bytes for errorMessage. + */ + com.google.protobuf.ByteString + getErrorMessageBytes(); } /** * Protobuf type {@code gitpod.v1.RepositoryNotFoundError} @@ -5534,6 +5558,8 @@ private RepositoryNotFoundError() { userScopes_ = com.google.protobuf.LazyStringArrayList.emptyList(); lastUpdate_ = ""; + repoName_ = ""; + errorMessage_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -5714,6 +5740,84 @@ public java.lang.String getLastUpdate() { } } + public static final int REPO_NAME_FIELD_NUMBER = 6; + @SuppressWarnings("serial") + private volatile java.lang.Object repoName_ = ""; + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return The repoName. + */ + @java.lang.Override + public java.lang.String getRepoName() { + java.lang.Object ref = repoName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + repoName_ = s; + return s; + } + } + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return The bytes for repoName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRepoNameBytes() { + java.lang.Object ref = repoName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + repoName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ERROR_MESSAGE_FIELD_NUMBER = 7; + @SuppressWarnings("serial") + private volatile java.lang.Object errorMessage_ = ""; + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return The errorMessage. + */ + @java.lang.Override + public java.lang.String getErrorMessage() { + java.lang.Object ref = errorMessage_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + errorMessage_ = s; + return s; + } + } + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return The bytes for errorMessage. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getErrorMessageBytes() { + java.lang.Object ref = errorMessage_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + errorMessage_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -5743,6 +5847,12 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(lastUpdate_)) { com.google.protobuf.GeneratedMessage.writeString(output, 5, lastUpdate_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(repoName_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 6, repoName_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(errorMessage_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 7, errorMessage_); + } getUnknownFields().writeTo(output); } @@ -5773,6 +5883,12 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(lastUpdate_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(5, lastUpdate_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(repoName_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(6, repoName_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(errorMessage_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(7, errorMessage_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -5798,6 +5914,10 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getUserScopesList())) return false; if (!getLastUpdate() .equals(other.getLastUpdate())) return false; + if (!getRepoName() + .equals(other.getRepoName())) return false; + if (!getErrorMessage() + .equals(other.getErrorMessage())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -5822,6 +5942,10 @@ public int hashCode() { } hash = (37 * hash) + LAST_UPDATE_FIELD_NUMBER; hash = (53 * hash) + getLastUpdate().hashCode(); + hash = (37 * hash) + REPO_NAME_FIELD_NUMBER; + hash = (53 * hash) + getRepoName().hashCode(); + hash = (37 * hash) + ERROR_MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getErrorMessage().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -5959,6 +6083,8 @@ public Builder clear() { userScopes_ = com.google.protobuf.LazyStringArrayList.emptyList(); lastUpdate_ = ""; + repoName_ = ""; + errorMessage_ = ""; return this; } @@ -6008,6 +6134,12 @@ private void buildPartial0(io.gitpod.publicapi.v1.Error.RepositoryNotFoundError if (((from_bitField0_ & 0x00000010) != 0)) { result.lastUpdate_ = lastUpdate_; } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.repoName_ = repoName_; + } + if (((from_bitField0_ & 0x00000040) != 0)) { + result.errorMessage_ = errorMessage_; + } } @java.lang.Override @@ -6050,6 +6182,16 @@ public Builder mergeFrom(io.gitpod.publicapi.v1.Error.RepositoryNotFoundError ot bitField0_ |= 0x00000010; onChanged(); } + if (!other.getRepoName().isEmpty()) { + repoName_ = other.repoName_; + bitField0_ |= 0x00000020; + onChanged(); + } + if (!other.getErrorMessage().isEmpty()) { + errorMessage_ = other.errorMessage_; + bitField0_ |= 0x00000040; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -6102,6 +6244,16 @@ public Builder mergeFrom( bitField0_ |= 0x00000010; break; } // case 42 + case 50: { + repoName_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000020; + break; + } // case 50 + case 58: { + errorMessage_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000040; + break; + } // case 58 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -6478,6 +6630,150 @@ public Builder setLastUpdateBytes( return this; } + private java.lang.Object repoName_ = ""; + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return The repoName. + */ + public java.lang.String getRepoName() { + java.lang.Object ref = repoName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + repoName_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return The bytes for repoName. + */ + public com.google.protobuf.ByteString + getRepoNameBytes() { + java.lang.Object ref = repoName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + repoName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string repo_name = 6 [json_name = "repoName"]; + * @param value The repoName to set. + * @return This builder for chaining. + */ + public Builder setRepoName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + repoName_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + * string repo_name = 6 [json_name = "repoName"]; + * @return This builder for chaining. + */ + public Builder clearRepoName() { + repoName_ = getDefaultInstance().getRepoName(); + bitField0_ = (bitField0_ & ~0x00000020); + onChanged(); + return this; + } + /** + * string repo_name = 6 [json_name = "repoName"]; + * @param value The bytes for repoName to set. + * @return This builder for chaining. + */ + public Builder setRepoNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + repoName_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + + private java.lang.Object errorMessage_ = ""; + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return The errorMessage. + */ + public java.lang.String getErrorMessage() { + java.lang.Object ref = errorMessage_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + errorMessage_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return The bytes for errorMessage. + */ + public com.google.protobuf.ByteString + getErrorMessageBytes() { + java.lang.Object ref = errorMessage_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + errorMessage_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @param value The errorMessage to set. + * @return This builder for chaining. + */ + public Builder setErrorMessage( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + errorMessage_ = value; + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @return This builder for chaining. + */ + public Builder clearErrorMessage() { + errorMessage_ = getDefaultInstance().getErrorMessage(); + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + return this; + } + /** + * string error_message = 7 [json_name = "errorMessage"]; + * @param value The bytes for errorMessage to set. + * @return This builder for chaining. + */ + public Builder setErrorMessageBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + errorMessage_ = value; + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:gitpod.v1.RepositoryNotFoundError) } @@ -8144,21 +8440,23 @@ public io.gitpod.publicapi.v1.Error.ImageBuildLogsNotYetAvailableError getDefaul "?\n\026InvalidCostCenterError\022%\n\016attribution" + "_id\030\001 \001(\tR\rattributionId\"\037\n\035TooManyRunni" + "ngWorkspacesError\"7\n\025InvalidGitpodYMLErr" + - "or\022\036\n\nviolations\030\001 \003(\tR\nviolations\"\251\001\n\027R" + + "or\022\036\n\nviolations\030\001 \003(\tR\nviolations\"\353\001\n\027R" + "epositoryNotFoundError\022\022\n\004host\030\001 \001(\tR\004ho" + "st\022\024\n\005owner\030\002 \001(\tR\005owner\022\"\n\ruser_is_owne" + "r\030\003 \001(\010R\013userIsOwner\022\037\n\013user_scopes\030\004 \003(" + "\tR\nuserScopes\022\037\n\013last_update\030\005 \001(\tR\nlast" + - "Update\"\374\001\n\033RepositoryUnauthorizedError\022\022" + - "\n\004host\030\001 \001(\tR\004host\022\'\n\017required_scopes\030\002 " + - "\003(\tR\016requiredScopes\022#\n\rprovider_type\030\003 \001" + - "(\tR\014providerType\022\033\n\trepo_name\030\004 \001(\tR\010rep" + - "oName\0222\n\025provider_is_connected\030\005 \001(\010R\023pr" + - "oviderIsConnected\022*\n\021is_missing_scopes\030\006" + - " \001(\010R\017isMissingScopes\"$\n\"ImageBuildLogsN" + - "otYetAvailableErrorBQ\n\026io.gitpod.publica" + - "pi.v1Z7github.com/gitpod-io/gitpod/compo" + - "nents/public-api/go/v1b\006proto3" + "Update\022\033\n\trepo_name\030\006 \001(\tR\010repoName\022#\n\re" + + "rror_message\030\007 \001(\tR\014errorMessage\"\374\001\n\033Rep" + + "ositoryUnauthorizedError\022\022\n\004host\030\001 \001(\tR\004" + + "host\022\'\n\017required_scopes\030\002 \003(\tR\016requiredS" + + "copes\022#\n\rprovider_type\030\003 \001(\tR\014providerTy" + + "pe\022\033\n\trepo_name\030\004 \001(\tR\010repoName\0222\n\025provi" + + "der_is_connected\030\005 \001(\010R\023providerIsConnec" + + "ted\022*\n\021is_missing_scopes\030\006 \001(\010R\017isMissin" + + "gScopes\"$\n\"ImageBuildLogsNotYetAvailable" + + "ErrorBQ\n\026io.gitpod.publicapi.v1Z7github." + + "com/gitpod-io/gitpod/components/public-a" + + "pi/go/v1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -8218,7 +8516,7 @@ public io.gitpod.publicapi.v1.Error.ImageBuildLogsNotYetAvailableError getDefaul internal_static_gitpod_v1_RepositoryNotFoundError_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_gitpod_v1_RepositoryNotFoundError_descriptor, - new java.lang.String[] { "Host", "Owner", "UserIsOwner", "UserScopes", "LastUpdate", }); + new java.lang.String[] { "Host", "Owner", "UserIsOwner", "UserScopes", "LastUpdate", "RepoName", "ErrorMessage", }); internal_static_gitpod_v1_RepositoryUnauthorizedError_descriptor = getDescriptor().getMessageTypes().get(9); internal_static_gitpod_v1_RepositoryUnauthorizedError_fieldAccessorTable = new diff --git a/components/public-api/typescript-common/src/public-api-converter.ts b/components/public-api/typescript-common/src/public-api-converter.ts index b878472e438a67..0dd98e7e3d999c 100644 --- a/components/public-api/typescript-common/src/public-api-converter.ts +++ b/components/public-api/typescript-common/src/public-api-converter.ts @@ -74,7 +74,7 @@ import { AuditLog } from "@gitpod/public-api/lib/gitpod/v1/auditlogs_pb"; import { BranchMatchingStrategy, Configuration, - PrebuildActivationStrategy, + PrebuildTriggerStrategy, PrebuildSettings, WorkspaceSettings, } from "@gitpod/public-api/lib/gitpod/v1/configuration_pb"; @@ -1067,7 +1067,7 @@ export class PublicAPIConverter { result.branchStrategy = this.toBranchMatchingStrategy(prebuilds.branchStrategy); result.prebuildInterval = prebuilds.prebuildInterval ?? 20; result.workspaceClass = prebuilds.workspaceClass ?? ""; - result.activationStrategy = this.toPrebuildActivationStrategy(prebuilds.activationStrategy); + result.triggerStrategy = this.toPrebuildTriggerStrategy(prebuilds.triggerStrategy); } return result; } @@ -1084,14 +1084,14 @@ export class PublicAPIConverter { return BranchMatchingStrategy.DEFAULT_BRANCH; } - toPrebuildActivationStrategy(strategy?: PrebuildSettingsProtocol.ActivationStrategy): PrebuildActivationStrategy { + toPrebuildTriggerStrategy(strategy?: PrebuildSettingsProtocol.TriggerStrategy): PrebuildTriggerStrategy { switch (strategy) { case "webhook-based": - return PrebuildActivationStrategy.UNSPECIFIED; + return PrebuildTriggerStrategy.UNSPECIFIED; case "activity-based": - return PrebuildActivationStrategy.ACTIVITY_BASED; + return PrebuildTriggerStrategy.ACTIVITY_BASED; default: - return PrebuildActivationStrategy.UNSPECIFIED; + return PrebuildTriggerStrategy.UNSPECIFIED; } } diff --git a/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts b/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts index 4be1148f178ca7..d7b861556386f9 100644 --- a/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts +++ b/components/public-api/typescript/src/gitpod/v1/configuration_pb.ts @@ -15,27 +15,27 @@ import { PaginationRequest, PaginationResponse } from "./pagination_pb.js"; import { Sort } from "./sorting_pb.js"; /** - * @generated from enum gitpod.v1.PrebuildActivationStrategy + * @generated from enum gitpod.v1.PrebuildTriggerStrategy */ -export enum PrebuildActivationStrategy { +export enum PrebuildTriggerStrategy { /** * Default value. Implicitly applies to webhoook-based activation * - * @generated from enum value: PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED = 0; + * @generated from enum value: PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED = 0; */ UNSPECIFIED = 0, /** * Default value for newly enabled prebuilds. * - * @generated from enum value: PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED = 1; + * @generated from enum value: PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED = 1; */ ACTIVITY_BASED = 1, } -// Retrieve enum metadata with: proto3.getEnumType(PrebuildActivationStrategy) -proto3.util.setEnumType(PrebuildActivationStrategy, "gitpod.v1.PrebuildActivationStrategy", [ - { no: 0, name: "PREBUILD_ACTIVATION_STRATEGY_UNSPECIFIED" }, - { no: 1, name: "PREBUILD_ACTIVATION_STRATEGY_ACTIVITY_BASED" }, +// Retrieve enum metadata with: proto3.getEnumType(PrebuildTriggerStrategy) +proto3.util.setEnumType(PrebuildTriggerStrategy, "gitpod.v1.PrebuildTriggerStrategy", [ + { no: 0, name: "PREBUILD_TRIGGER_STRATEGY_UNSPECIFIED" }, + { no: 1, name: "PREBUILD_TRIGGER_STRATEGY_ACTIVITY_BASED" }, ]); /** @@ -173,9 +173,9 @@ export class PrebuildSettings extends Message { workspaceClass = ""; /** - * @generated from field: gitpod.v1.PrebuildActivationStrategy activation_strategy = 6; + * @generated from field: gitpod.v1.PrebuildTriggerStrategy trigger_strategy = 6; */ - activationStrategy = PrebuildActivationStrategy.UNSPECIFIED; + triggerStrategy = PrebuildTriggerStrategy.UNSPECIFIED; constructor(data?: PartialMessage) { super(); @@ -190,7 +190,7 @@ export class PrebuildSettings extends Message { { no: 3, name: "branch_strategy", kind: "enum", T: proto3.getEnumType(BranchMatchingStrategy) }, { no: 4, name: "prebuild_interval", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, { no: 5, name: "workspace_class", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 6, name: "activation_strategy", kind: "enum", T: proto3.getEnumType(PrebuildActivationStrategy) }, + { no: 6, name: "trigger_strategy", kind: "enum", T: proto3.getEnumType(PrebuildTriggerStrategy) }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): PrebuildSettings { @@ -612,9 +612,9 @@ export class UpdateConfigurationRequest_PrebuildSettings extends Message) { super(); @@ -629,7 +629,7 @@ export class UpdateConfigurationRequest_PrebuildSettings extends Message): UpdateConfigurationRequest_PrebuildSettings { diff --git a/components/server/src/prebuilds/prebuild-manager.ts b/components/server/src/prebuilds/prebuild-manager.ts index a2fe1c5f944b81..ef88a5cdbe16ab 100644 --- a/components/server/src/prebuilds/prebuild-manager.ts +++ b/components/server/src/prebuilds/prebuild-manager.ts @@ -11,6 +11,7 @@ import { PrebuildWithStatus, PrebuiltWorkspace, Project, + ProjectUsage, StartPrebuildContext, StartPrebuildResult, TaskConfig, @@ -47,6 +48,7 @@ export interface StartPrebuildParams { project: Project; commitInfo?: CommitInfo; forcePrebuild?: boolean; + trigger?: keyof ProjectUsage; } export interface PrebuildFilter { @@ -327,7 +329,7 @@ export class PrebuildManager { async startPrebuild( ctx: TraceContext, - { context, project, user, commitInfo, forcePrebuild }: StartPrebuildParams, + { context, project, user, commitInfo, forcePrebuild, trigger = "lastWebhookReceived" }: StartPrebuildParams, ): Promise { const span = TraceContext.startSpan("startPrebuild", ctx); const cloneURL = context.repository.cloneUrl; @@ -338,7 +340,7 @@ export class PrebuildManager { // TODO figure out right place to mark activity of a project. For now, just moving at the beginning // of `startPrebuild` to remain previous semantics when it was happening on call sites. this.projectService - .markActive(user.id, project.id, "lastWebhookReceived") + .markActive(user.id, project.id, trigger) .catch((e) => log.error("cannot update project usage", e)); try { diff --git a/components/server/src/projects/projects-service.spec.db.ts b/components/server/src/projects/projects-service.spec.db.ts index 9d7448328e7e48..b160a6301004ad 100644 --- a/components/server/src/projects/projects-service.spec.db.ts +++ b/components/server/src/projects/projects-service.spec.db.ts @@ -274,7 +274,7 @@ describe("ProjectsService", async () => { workspaceClass: "ultra", branchStrategy: "matched-branches", branchMatchingPattern: "feature-*", - activationStrategy: "activity-based", + triggerStrategy: "activity-based", }, workspaceClasses: {}, }); diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index 4ca4b5d86ad5ea..3b2136fce8fc29 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -424,7 +424,7 @@ export class ProjectsService { const enablePrebuildsPrev = !!existingProject.settings?.prebuilds?.enable; if (!enablePrebuildsPrev) { // new default - partialProject.settings.prebuilds.activationStrategy = "activity-based"; + partialProject.settings.prebuilds.triggerStrategy = "activity-based"; } } return this.projectDB.updateProject(partialProject); diff --git a/components/server/src/workspace/workspace-service.ts b/components/server/src/workspace/workspace-service.ts index 34b6e178a2a5c2..173c4d81b4e182 100644 --- a/components/server/src/workspace/workspace-service.ts +++ b/components/server/src/workspace/workspace-service.ts @@ -427,6 +427,7 @@ export class WorkspaceService { project, forcePrebuild: false, context, + trigger: "lastWorkspaceStart", }); })().catch((err) => log.error(