Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple projects per repo #18774

Merged
merged 1 commit into from
Sep 25, 2023
Merged

Allow multiple projects per repo #18774

merged 1 commit into from
Sep 25, 2023

Conversation

svenefftinge
Copy link
Member

@svenefftinge svenefftinge commented Sep 22, 2023

Description

This PR includes implementation changes that are necessary to relax the 1:1 constraint between a repo and a project.

Summary generated by Copilot

🤖 Generated by Copilot at cc1c7cf

No summary available (Limit exceeded: required to process 212218 tokens, but only 50000 are allowed per call)

Related Issue(s)

Fixes EXP-640, EXP-649, EXP-648, EXP-653

How to test

Join this org: https://se-multiple-projects.preview.gitpod-dev.com/orgs/join?inviteId=58fb7dc4-2d43-41e0-a7e8-be1a43e7e9ab

It is set up with all SCMs we support

  • try create multiple projects for the same repo with different prebuild settings.
  • try start workspaces on them

Documentation

Preview status

Gitpod was successfully deployed to your preview environment.

Build Options

Build
  • /werft with-werft
    Run the build with werft instead of GHA
  • leeway-no-cache
  • /werft no-test
    Run Leeway with --dont-test
Publish
  • /werft publish-to-npm
  • /werft publish-to-jb-marketplace
Installer
  • analytics=segment
  • with-dedicated-emulation
  • workspace-feature-flags
    Add desired feature flags to the end of the line above, space separated
Preview Environment / Integration Tests
  • /werft with-local-preview
    If enabled this will build install/preview
  • /werft with-preview
  • /werft with-large-vm
  • /werft with-gce-vm
    If enabled this will create the environment on GCE infra
  • with-integration-tests=all
    Valid options are all, workspace, webapp, ide, jetbrains, vscode, ssh. If enabled, with-preview and with-large-vm will be enabled.
  • with-monitoring

/hold

@@ -10,8 +10,7 @@ import { TransactionalDB } from "./typeorm/transactional-db-impl";
export const ProjectDB = Symbol("ProjectDB");
export interface ProjectDB extends TransactionalDB<ProjectDB> {
findProjectById(projectId: string): Promise<Project | undefined>;
findProjectByCloneUrl(cloneUrl: string): Promise<Project | undefined>;
findProjectsByCloneUrls(cloneUrls: string[]): Promise<(Project & { teamOwners?: string[] })[]>;
findProjectsByCloneUrl(cloneUrl: string): Promise<Project[]>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we still need a method like this because we need to look up the project(s) when a webhook event comes in.

@@ -710,16 +711,19 @@ export class TypeORMWorkspaceDBImpl extends TransactionalDBImpl<WorkspaceDB> imp

// Find the (last triggered) prebuild for a given commit
public async findPrebuiltWorkspaceByCommit(
cloneURL: string,
projectId: string,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A cloneURL was used across the board to identify the project. Since we now can have multiple projects per cloneURL this is no longer the case, so we pass on the projected instead. This is the majority of this changeset.

if (!commit || !cloneURL) {
return undefined;
if (!commit || !projectId) {
throw new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, "Illegal arguments", { projectId, commit });
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not silently ignore such a state

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, but how comes that this is necessary in the persistence layer given the mandatory arguments. Let's check the call sites, too.

@gtsiolis
Copy link
Contributor

omg

)}
<Button onClick={() => onRepoSelected(r)} loading={isCreating}>
Select
</Button>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we no longer forbid creating a project if one for that repo already exists

components/server/src/prebuilds/bitbucket-app.ts Outdated Show resolved Hide resolved
user.id,
context.repository.cloneUrl,
);
if (projects.length > 1) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the user didn't pass in a projectId but we find more than one candidate we throw and need to tell the user.

@svenefftinge svenefftinge force-pushed the se/multiple-projects branch 2 times, most recently from 48a3c89 to faca93c Compare September 22, 2023 13:43
Copy link
Contributor

@gtsiolis gtsiolis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just stopping by to leave on comment on an issue which could be unrelated to the changes of this PR. Other than that, UX LGTM. 🏁

cloneURL: string,
webhookInstaller: User,
): Promise<{ user: User; project?: Project }> {
private async findProjectOwner(project: Project, webhookInstaller: User): Promise<User> {
Copy link
Contributor

@gtsiolis gtsiolis Sep 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Just wanted to say it feels so good to see this kind of control where 3 repository instances with different prebuilds settings (not enabled, all branches, default branch) work side-by-side like this. Don't know if it's ok, but I only see one webhook in the project, see https://gitlab.com/gitpod-io/rails/-/hooks. Cc @AlexTugarev 🙏

Screenshot 2023-09-22 at 20 17 29

);
}
const projects = await this.projectService.findProjectsByCloneUrl(user.id, cloneURL);
for (const project of projects) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to guard per project as well, otherwise if anything goes south with the first project at hand, e.g. context parsing, the other projects won't be processed.
(same applies to the other *-app.ts)

import { UserService } from "../user/user-service";
import { ProjectsService } from "../projects/projects-service";
import { SYSTEM_USER } from "../authorization/authorizer";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

const context = (await this.contextParser.handle({ span }, installationOwner, contextURL)) as CommitContext;
const projects = await this.projectService.findProjectsByCloneUrl(SYSTEM_USER, context.repository.cloneUrl);
for (const project of projects) {
const user = await this.findProjectOwner(project, installationOwner);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah! this is how we handle FGA for prebuild.

@AlexTugarev
Copy link
Member

Screenshot 2023-09-25 at 09 26 58 Screenshot 2023-09-25 at 09 27 09

A commit resulting in prebuilds being done on two project in separate Orgs 👏🏻

Copy link
Member

@AlexTugarev AlexTugarev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@svenefftinge
Copy link
Member Author

/unhold

@roboquat roboquat merged commit 14a21a7 into main Sep 25, 2023
12 of 13 checks passed
@roboquat roboquat deleted the se/multiple-projects branch September 25, 2023 09:13
const abortedState: PrebuiltWorkspaceState = "aborted";
const repo = await this.getPrebuiltWorkspaceRepo();

let query = repo.createQueryBuilder("pws");
query = query.where("pws.cloneURL = :cloneURL", { cloneURL });
query = query.where("pws.projectId != :projectId", { projectId });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a match, correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes 🙈

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing via a840064 in parallel. Shall we create a separate PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine with me to put it into your pr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants