From ea8a65694be2c8e19800c4aaef26852c92e3b306 Mon Sep 17 00:00:00 2001 From: Edaena Salinas Date: Wed, 25 Mar 2020 13:12:27 -0700 Subject: [PATCH] Check that repository exits for installing pipeline (#440) * Check that repository exits for installing pipeline * Add repository validation to other commands * Remove repositoryExists function * Update unit tests Co-authored-by: Edaena Salinas Co-authored-by: Edaena Salinas --- src/commands/hld/pipeline.test.ts | 2 +- src/commands/hld/pipeline.ts | 5 +-- src/commands/project/pipeline.test.ts | 10 ++---- src/commands/project/pipeline.ts | 11 +++--- src/commands/service/pipeline.test.ts | 2 +- src/commands/service/pipeline.ts | 5 +-- src/lib/azdoClient.test.ts | 52 +++++++++++++++++++++++++++ src/lib/azdoClient.ts | 27 ++++++++++++++ 8 files changed, 96 insertions(+), 18 deletions(-) diff --git a/src/commands/hld/pipeline.test.ts b/src/commands/hld/pipeline.test.ts index 7255ce74b..228c7e19b 100644 --- a/src/commands/hld/pipeline.test.ts +++ b/src/commands/hld/pipeline.test.ts @@ -59,7 +59,7 @@ afterAll(() => { disableVerboseLogging(); }); -jest.spyOn(azdo, "repositoryHasFile").mockReturnValue(Promise.resolve()); +jest.spyOn(azdo, "validateRepository").mockReturnValue(Promise.resolve()); describe("test emptyStringIfUndefined function", () => { it("pass in undefined", () => { diff --git a/src/commands/hld/pipeline.ts b/src/commands/hld/pipeline.ts index 40d5683ad..b7e122a06 100644 --- a/src/commands/hld/pipeline.ts +++ b/src/commands/hld/pipeline.ts @@ -6,7 +6,7 @@ import { } from "azure-devops-node-api/interfaces/BuildInterfaces"; import commander from "commander"; import { Config } from "../../config"; -import { repositoryHasFile } from "../../lib/azdoClient"; +import { validateRepository } from "../../lib/azdoClient"; import { build as buildCmd, exit as exitCmd } from "../../lib/commandBuilder"; import { BUILD_SCRIPT_URL, @@ -200,7 +200,8 @@ export const execute = async ( }; // By default the version descriptor is for the master branch - await repositoryHasFile( + await validateRepository( + opts.devopsProject, RENDER_HLD_PIPELINE_FILENAME, opts.yamlFileBranch ? opts.yamlFileBranch : "master", opts.hldName, diff --git a/src/commands/project/pipeline.test.ts b/src/commands/project/pipeline.test.ts index 596c93ffc..e0bf10bca 100644 --- a/src/commands/project/pipeline.test.ts +++ b/src/commands/project/pipeline.test.ts @@ -36,11 +36,11 @@ const mockValues: CommandOptions = { personalAccessToken: "PAT", pipelineName: "pipelineName", repoName: "repoName", - repoUrl: "repoUrl", + repoUrl: "https://dev.azure.com/myOrg/myProject/_git/myRepo", yamlFileBranch: "master", }; -jest.spyOn(azdo, "repositoryHasFile").mockReturnValue(Promise.resolve()); +jest.spyOn(azdo, "validateRepository").mockReturnValue(Promise.resolve()); const mockMissingValues: CommandOptions = { buildScriptUrl: undefined, @@ -123,12 +123,6 @@ describe("installLifecyclePipeline and execute tests", () => { expect(exitFn).toBeCalledTimes(1); expect(exitFn.mock.calls).toEqual([[0]]); }); - it("test execute function: missing repo url and pipeline name", async () => { - const exitFn = jest.fn(); - await execute(mockMissingValues, "", exitFn); - expect(exitFn).toBeCalledTimes(1); - expect(exitFn.mock.calls).toEqual([[1]]); - }); it("test execute function: github repos not supported", async () => { const exitFn = jest.fn(); await execute(nullValues, "", exitFn); diff --git a/src/commands/project/pipeline.ts b/src/commands/project/pipeline.ts index 22763f34d..5137242fa 100644 --- a/src/commands/project/pipeline.ts +++ b/src/commands/project/pipeline.ts @@ -6,7 +6,7 @@ import { } from "azure-devops-node-api/interfaces/BuildInterfaces"; import commander from "commander"; import { Config } from "../../config"; -import { repositoryHasFile } from "../../lib/azdoClient"; +import { validateRepository } from "../../lib/azdoClient"; import { fileInfo as bedrockFileInfo } from "../../lib/bedrockYaml"; import { build as buildCmd, @@ -77,6 +77,7 @@ export const fetchValidateValues = ( if (!opts.repoUrl) { throw Error(`Repo url not defined`); } + const values: CommandOptions = { buildScriptUrl: opts.buildScriptUrl || BUILD_SCRIPT_URL, devopsProject: opts.devopsProject || azureDevops?.project, @@ -84,7 +85,8 @@ export const fetchValidateValues = ( personalAccessToken: opts.personalAccessToken || azureDevops?.access_token, pipelineName: opts.pipelineName || getRepositoryName(gitOriginUrl) + "-lifecycle", - repoName: getRepositoryName(gitOriginUrl), + repoName: + getRepositoryName(opts.repoUrl) || getRepositoryName(gitOriginUrl), repoUrl: opts.repoUrl || getRepositoryUrl(gitOriginUrl), yamlFileBranch: opts.yamlFileBranch, }; @@ -233,10 +235,11 @@ export const execute = async ( personalAccessToken: values.personalAccessToken, project: values.devopsProject, }; - await repositoryHasFile( + await validateRepository( + values.devopsProject!, PROJECT_PIPELINE_FILENAME, values.yamlFileBranch ? opts.yamlFileBranch : "master", - values.repoName!, + values.repoName, accessOpts ); await installLifecyclePipeline(values); diff --git a/src/commands/service/pipeline.test.ts b/src/commands/service/pipeline.test.ts index f881e4c66..4cdf48108 100644 --- a/src/commands/service/pipeline.test.ts +++ b/src/commands/service/pipeline.test.ts @@ -46,7 +46,7 @@ const getMockedValues = (): CommandOptions => { return deepClone(MOCKED_VALUES); }; -jest.spyOn(azdo, "repositoryHasFile").mockReturnValue(Promise.resolve()); +jest.spyOn(azdo, "validateRepository").mockReturnValue(Promise.resolve()); describe("test fetchValues function", () => { it("with all values set", async () => { diff --git a/src/commands/service/pipeline.ts b/src/commands/service/pipeline.ts index a17ad10c4..939693370 100644 --- a/src/commands/service/pipeline.ts +++ b/src/commands/service/pipeline.ts @@ -8,7 +8,7 @@ import { import commander from "commander"; import path from "path"; import { Config } from "../../config"; -import { repositoryHasFile } from "../../lib/azdoClient"; +import { validateRepository } from "../../lib/azdoClient"; import { build as buildCmd, exit as exitCmd } from "../../lib/commandBuilder"; import { BUILD_SCRIPT_URL, @@ -90,7 +90,8 @@ export const execute = async ( path.join(serviceName, SERVICE_PIPELINE_FILENAME); // By default the version descriptor is for the master branch - await repositoryHasFile( + await validateRepository( + opts.devopsProject, pipelinesYamlPath, opts.yamlFileBranch ? opts.yamlFileBranch : "master", opts.repoName, diff --git a/src/lib/azdoClient.test.ts b/src/lib/azdoClient.test.ts index d00f6545b..ab3592790 100644 --- a/src/lib/azdoClient.test.ts +++ b/src/lib/azdoClient.test.ts @@ -12,6 +12,7 @@ import { getWebApi, invalidateWebApi, repositoryHasFile, + validateRepository, } from "./azdoClient"; import * as azdoClient from "./azdoClient"; import { AzureDevOpsOpts } from "./git"; @@ -144,6 +145,57 @@ describe("test getBuildApi function", () => { }); }); +describe("validateRepository", () => { + test("repository exists", async () => { + const getRepositoryFunc = jest.spyOn(azure, "GitAPI"); + getRepositoryFunc.mockReturnValueOnce( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Promise.resolve({ getRepository: () => ({ id: "3839fjfkj" }) } as any) + ); + const getItemFunc = jest.spyOn(azure, "GitAPI"); + getItemFunc.mockReturnValueOnce( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Promise.resolve({ getItem: () => ({ commitId: "3839fjfkj" }) } as any) + ); + const accessOpts: AzureDevOpsOpts = { + orgName: "testOrg", + personalAccessToken: "mytoken", + project: "testProject", + }; + + await expect( + validateRepository( + "my-project", + "myFile", + "master", + "my-repo", + accessOpts + ) + ).resolves.not.toThrow(); + }); + test("repository does not exist", async () => { + const createPullRequestFunc = jest.spyOn(azure, "GitAPI"); + createPullRequestFunc.mockReturnValueOnce( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Promise.resolve({ getRepository: () => null } as any) + ); + const accessOpts: AzureDevOpsOpts = { + orgName: "testOrg", + personalAccessToken: "mytoken", + project: "testProject", + }; + await expect( + validateRepository( + "my-project", + "myFile", + "master", + "my-repo", + accessOpts + ) + ).rejects.toThrow(); + }); +}); + describe("repositoryHasFile", () => { test("repository contains the given file", async () => { const createPullRequestFunc = jest.spyOn(azure, "GitAPI"); diff --git a/src/lib/azdoClient.ts b/src/lib/azdoClient.ts index 8f05de28b..bab4fcd94 100644 --- a/src/lib/azdoClient.ts +++ b/src/lib/azdoClient.ts @@ -157,3 +157,30 @@ export const repositoryHasFile = async ( ); } }; + +/** + * Validates if a repository exists and if it contains the given file + * @param project The Azure DevOps project name + * @param fileName The name of the file + * @param branch The branch name + * @param repoName The name of the repository + * @param accessOpts The Azure DevOps access options to the repository + */ +export const validateRepository = async ( + project: string, + fileName: string, + branch: string, + repoName: string, + accessOpts: AzureDevOpsOpts +): Promise => { + const gitApi = await GitAPI(accessOpts); + const repo = await gitApi.getRepository(repoName, project); + + if (!repo) { + throw Error( + `Project '${project}' does not contain repository '${repoName}'.` + ); + } + + await repositoryHasFile(fileName, branch, repoName, accessOpts); +};