UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

254 lines (229 loc) 8.38 kB
/* * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import * as changeCase from "change-case"; import { hasCommit } from "../../../api-helper/pushtest/commit"; import { isMaterialChange } from "../../../api-helper/pushtest/materialChangeTest"; import { StatefulPushListenerInvocation } from "../../../api/dsl/goalContribution"; import { isGoal } from "../../../api/mapping/goalTest"; import { pushTest, PushTest, } from "../../../api/mapping/PushTest"; import { hasFile, hasFileContaining, hasResourceProvider, isBranch, isRepo, ToDefaultBranch, } from "../../../api/mapping/support/commonPushTests"; import { and, not, or, } from "../../../api/mapping/support/pushTestUtils"; import { SdmGoalState } from "../../../typings/types"; import { isSkillConfigured } from "../../mapping/pushtest/isSkillConfigured"; import { toArray } from "../../util/misc/array"; import { camelCase } from "./util"; export type PushTestMaker<G extends Record<string, any> = any> = (params: G) => ((pli: StatefulPushListenerInvocation) => Promise<boolean>) | Promise<PushTest> | PushTest; export async function mapTests(tests: any, additionalTests: Record<string, PushTest>, extensionTests: Record<string, PushTestMaker>): Promise<PushTest | PushTest[]> { const newTests = []; for (const t of toArray(tests || [])) { const test = typeof t !== "string" && !Array.isArray(t) ? camelCase(t) : t as any; newTests.push(await mapTest(test, additionalTests, extensionTests)); } return newTests; } type CreatePushTest = (test: any, additionalTests: Record<string, PushTest>, extensionTests: Record<string, PushTestMaker>) => Promise<PushTest | undefined>; const HasFile: CreatePushTest = async test => { if (!!test.hasFile) { return hasFile(test.hasFile); } return undefined; }; const IsRepo: CreatePushTest = async test => { if (!!test.isRepo) { return isRepo(typeof test.isRepo === "string" ? new RegExp(test.isRepo) : test.isRepo); } return undefined; }; const IsBranch: CreatePushTest = async test => { if (!!test.isBranch) { return isBranch(typeof test.isBranch === "string" ? new RegExp(test.isBranch) : test.isBranch); } return undefined; }; const IsDefaultBranch: CreatePushTest = async test => { if (["isDefaultBranch", "toDefaultBranch"].includes(changeCase.camel(test))) { return ToDefaultBranch; } return undefined; }; const IsSkillConfigured: CreatePushTest = async test => { if (!!test.isSkillConfigured) { const sc = test.isSkillConfigured; return isSkillConfigured({ hasCommit: sc.hasCommit, hasFile: sc.hasFile, isBranch: sc.isBranch, isDefaultBranch: sc.isDefaultBranch, }); } return undefined; }; const IsGoal: CreatePushTest = async (test, additionalTests, extensionTests) => { const onGoal = test.isGoal || test.onGoal; if (!!onGoal) { return isGoal({ name: getStringOrRegexp(onGoal.name), state: onGoal.state || SdmGoalState.success, pushTest: onGoal.test ? await mapTest(onGoal.test, additionalTests, extensionTests) : undefined, output: getStringOrRegexp(onGoal.output), data: getStringOrRegexp(onGoal.data), }); } return undefined; }; const IsMaterialChange: CreatePushTest = async test => { if (!!test.isMaterialChange) { return isMaterialChange({ directories: toArray(test.isMaterialChange.directories), extensions: toArray(test.isMaterialChange.extensions), files: toArray(test.isMaterialChange.files), globs: getGlobPatterns(test.isMaterialChange), }); } return undefined; }; const HasFileContaining: CreatePushTest = async test => { if (!!test.hasFileContaining) { if (!test.hasFileContaining.content) { throw new Error("Push test 'hasFileContaining' can't be used without 'content' property"); } return hasFileContaining( getGlobPatterns(test.hasFileContaining) || "**/*", typeof test.hasFileContaining.content === "string" ? new RegExp(test.hasFileContaining.content) : test.hasFileContaining.content); } return undefined; }; const HasResourceProvider: CreatePushTest = async test => { if (!!test.hasResourceProvider) { if (!test.hasResourceProvider.type) { throw new Error("Push test 'hasResourceProvider' can't be used without 'type' property"); } return hasResourceProvider(test.hasResourceProvider.type, test.hasResourceProvider.name); } return undefined; }; const HasCommit: CreatePushTest = async test => { if (!!test.hasCommit) { return hasCommit(typeof test.hasCommit === "string" ? new RegExp(test.hasCommit) : test.hasCommit); } return undefined; }; const Not: CreatePushTest = async (test, additionalTests, extensionTests) => { if (!!test.not) { return not(await mapTest(test.not, additionalTests, extensionTests)); } return undefined; }; const And: CreatePushTest = async (test, additionalTests, extensionTests) => { if (!!test.and) { return and(...toArray(await mapTests(test.and, additionalTests, extensionTests))); } return undefined; }; const Or: CreatePushTest = async (test, additionalTests, extensionTests) => { if (!!test.or) { return or(...toArray(await mapTests(test.or, additionalTests, extensionTests))); } return undefined; }; const AdditionalTest: CreatePushTest = async (test, additionalTests) => { if (!!test.use && !!additionalTests[test.use]) { return additionalTests[test.use]; } return undefined; }; const FunctionTest: CreatePushTest = async test => { if (typeof test === "function") { return pushTest(test.toString(), test); } return undefined; }; const ExtensionTest = async (test, additionalTests, extensionTests) => { for (const extTestName in extensionTests) { if (test.use === extTestName) { const extTest = await extensionTests[extTestName](test.parameters || {}); if (!!extTest.name && !!extTest.mapping) { return extTest; } else { return pushTest(extTestName, extTest); } } } return undefined; }; export const CreatePushTests = [ HasFile, IsRepo, IsBranch, IsDefaultBranch, IsGoal, IsSkillConfigured, IsMaterialChange, HasFileContaining, HasResourceProvider, HasCommit, Not, And, Or, AdditionalTest, FunctionTest, ExtensionTest, ]; export async function mapTest(test: any, additionalTests: Record<string, PushTest>, extensionTests: Record<string, PushTestMaker>): Promise<PushTest> { for (const createPushTest of CreatePushTests) { const pt = await createPushTest(test, additionalTests, extensionTests); if (!!pt) { return pt; } } throw new Error(`Unable to construct push test from '${JSON.stringify(test)}'`); } function getGlobPatterns(test: any): string[] { const pattern = test.globPattern || test.pattern || test.globPatterns || test.patterns; return toArray(pattern); } function getStringOrRegexp(toTest: string | { regexp: string }): string | RegExp | undefined { if (!toTest) { return undefined; } else if (typeof toTest === "string") { return toTest; } else if (toTest.regexp !== undefined) { return new RegExp(toTest.regexp); } else { return undefined; } }