From 5e2b7454a8eac7177127cd5b8d721f13fa13e106 Mon Sep 17 00:00:00 2001 From: vigneshrajsb Date: Mon, 27 Jul 2026 14:40:18 -0700 Subject: [PATCH] fix: getEnvironmentsToBuild queried a relation removed in phase 1 The dead-code cleanup (#228) removed Environment.relationMappings.services along with the rest of the legacy DB-config path, but left this call site: Environment.find().withGraphJoined('services').where('services.repositoryId', ...) Objection throws UnknownRelationError: services when it runs. It is reached from createBuildAndDeploys -> getEnvironmentsToBuild whenever environmentId is null, i.e. a repository whose defaultEnvId is not set (github.ts passes repository?.defaultEnvId straight through), so it has been latent rather than loud. That else-branch was the DB-service-based environment lookup: "find the environments that own a service belonging to this repo". With YAML config a repository's environment is its defaultEnvId, so the branch has no meaning anymore and is removed rather than repaired. Returning an empty array is what the caller already expects - it logs "no matching environments" and returns. Also stops pushing a possibly-undefined findOne() result into the array, which would surface later as an undefined element during environments.map(). Adds regression coverage that fails on the previous implementation: the Environment.find mock throws if called, and the missing-environment case uses toStrictEqual so [undefined] cannot pass as []. --- src/server/services/__tests__/build.test.ts | 48 +++++++++++++++++++++ src/server/services/build.ts | 18 +++----- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/server/services/__tests__/build.test.ts b/src/server/services/__tests__/build.test.ts index 6d42691..4123b89 100644 --- a/src/server/services/__tests__/build.test.ts +++ b/src/server/services/__tests__/build.test.ts @@ -660,6 +660,54 @@ describe('BuildService destroyBuildEnvironment', () => { }); }); +describe('BuildService getEnvironmentsToBuild', () => { + const createService = (foundEnvironment: any) => + new BuildService( + { + models: { + Environment: { + findOne: jest.fn().mockResolvedValue(foundEnvironment), + // Phase 1 removed Environment.relationMappings.services. The old else-branch called + // Environment.find().withGraphJoined('services'), which throws UnknownRelationError. + find: jest.fn(() => { + throw new Error('Environment.find() must not be called: the DB-service lookup was removed'); + }), + }, + }, + } as any, + {} as any, + {} as any, + { + registerQueue: jest.fn(() => ({ add: jest.fn(), process: jest.fn(), on: jest.fn() })), + } as any + ); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('returns the environment for a known environmentId', async () => { + const environment = { id: 7 }; + const service = createService(environment); + + await expect((service as any).getEnvironmentsToBuild(7)).resolves.toEqual([environment]); + }); + + test('returns empty for a null environmentId instead of querying the removed services relation', async () => { + const service = createService(undefined); + + await expect((service as any).getEnvironmentsToBuild(null)).resolves.toEqual([]); + expect((service as any).db.models.Environment.find).not.toHaveBeenCalled(); + }); + + test('returns empty when the environment is missing rather than a list holding undefined', async () => { + const service = createService(undefined); + + // toStrictEqual: toEqual treats [undefined] as [], which would hide the old push-undefined behaviour. + await expect((service as any).getEnvironmentsToBuild(99)).resolves.toStrictEqual([]); + }); +}); + describe('BuildService stale deploy reconciliation', () => { let buildService: BuildService; let deployableQuery: any; diff --git a/src/server/services/build.ts b/src/server/services/build.ts index 249628e..3ff11f8 100644 --- a/src/server/services/build.ts +++ b/src/server/services/build.ts @@ -2079,7 +2079,7 @@ export default class BuildService extends BaseService { environmentId, lifecycleConfig, }: DeployOptions & { repositoryId: number }) { - const environments = await this.getEnvironmentsToBuild(environmentId, repositoryId); + const environments = await this.getEnvironmentsToBuild(environmentId); if (!environments.length) { getLogger().debug('Build: no matching environments'); @@ -3214,19 +3214,15 @@ export default class BuildService extends BaseService { /** * Returns an array of environments to build. * @param environmentId the default environmentId (if one exists) - * @param repositoryId the repository to use for finding relevant environments, if needed */ - private async getEnvironmentsToBuild(environmentId: number, repositoryId: number) { - let environments: Environment[] = []; - if (environmentId != null) { - environments.push(await this.db.models.Environment.findOne({ id: environmentId })); - } else { - environments = environments.concat( - await this.db.models.Environment.find().withGraphJoined('services').where('services.repositoryId', repositoryId) - ); + private async getEnvironmentsToBuild(environmentId: number): Promise { + if (environmentId == null) { + return []; } - return environments; + const environment = await this.db.models.Environment.findOne({ id: environmentId }); + + return environment != null ? [environment] : []; } private async updateDeploysImageDetails(build: Build, githubRepositoryId?: number, sourceBranch?: string | null) {