UNPKG

@atomist/sdm-core

Version:

Atomist Software Delivery Machine - Implementation

247 lines (235 loc) 7.89 kB
/* * Copyright © 2019 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 { LeveledLogMethod, MutationNoCacheOptions, } from "@atomist/automation-client"; import { Build, ExecuteGoalResult, GoalInvocation, ProgressLog, SdmContext, SdmGoalEvent, } from "@atomist/sdm"; import * as fs from "fs-extra"; import * as _ from "lodash"; import * as path from "path"; import { SdmVersion } from "../../ingesters/sdmVersionIngester"; import { getGoalVersion } from "../../internal/delivery/build/local/projectVersioner"; import { K8sNamespaceFile } from "../../pack/k8s/KubernetesGoalScheduler"; import { PushFields, UpdateSdmVersionMutation, UpdateSdmVersionMutationVariables, } from "../../typings/types"; import { postBuildWebhook, postLinkImageWebhook, } from "../../util/webhook/ImageLink"; import { ContainerInput, ContainerOutput, ContainerProjectHome, } from "./container"; import Images = PushFields.Images; /** * Simple test to see if SDM is running in Kubernetes. It is called * from a non-async function, so it must be non-async. * * @return `true` if process is running in Kubernetes, `false` otherwise. */ export function runningInK8s(): boolean { return fs.pathExistsSync(K8sNamespaceFile); } /** * Simple test to see if SDM is running as a Google Cloud Function. * * @return `true` if process is running as Google Cloud Function, * `false` otherwise. */ export function runningAsGoogleCloudFunction(): boolean { return !!process.env.K_SERVICE && !!process.env.K_REVISION; } /** * Return environment variables required by the container goal * execution machinery. * * @param goalEvent SDM goal event being executed as a container goal * @param ctx SDM context for goal execution * @return SDM goal environment variables */ export async function containerEnvVars(goalEvent: SdmGoalEvent, ctx: SdmContext, projectDir: string = ContainerProjectHome, inputDir: string = ContainerInput, outputDir: string = ContainerOutput): Promise<Array<{ name: string, value: string }>> { const version = await getGoalVersion({ owner: goalEvent.repo.owner, repo: goalEvent.repo.name, providerId: goalEvent.repo.providerId, sha: goalEvent.sha, branch: goalEvent.branch, context: ctx.context, }); // This should probably go into a different place but ok for now if (!!version) { _.set(goalEvent, "push.after.version", version); } return [{ name: "ATOMIST_WORKSPACE_ID", value: ctx.context.workspaceId, }, { name: "ATOMIST_SLUG", value: `${goalEvent.repo.owner}/${goalEvent.repo.name}`, }, { name: "ATOMIST_OWNER", value: goalEvent.repo.owner, }, { name: "ATOMIST_REPO", value: goalEvent.repo.name, }, { name: "ATOMIST_SHA", value: goalEvent.sha, }, { name: "ATOMIST_BRANCH", value: goalEvent.branch, }, { name: "ATOMIST_VERSION", value: version, }, { name: "ATOMIST_GOAL", value: `${inputDir}/goal.json`, }, { name: "ATOMIST_RESULT", value: `${outputDir}/result.json`, }, { name: "ATOMIST_INPUT_DIR", value: inputDir, }, { name: "ATOMIST_OUTPUT_DIR", value: outputDir, }, { name: "ATOMIST_PROJECT_DIR", value: projectDir, }].filter(e => !!e.value); } export async function prepareInputAndOutput(input: string, output: string, gi: GoalInvocation): Promise<void> { try { await fs.emptyDir(input); } catch (e) { e.message = `Failed to empty directory '${input}'`; throw e; } try { await fs.writeJson(path.join(input, "goal.json"), gi.goalEvent, { spaces: 2 }); } catch (e) { e.message = `Failed to write metadata to '${input}'`; try { await fs.remove(input); } catch (err) { e.message += `; Failed to clean up '${input}': ${err.message}`; } throw e; } try { await fs.emptyDir(output); } catch (e) { e.message = `Failed to empty directory '${output}'`; throw e; } } /** * Write to client and progress logs. Add newline to progress log. * * @param msg Message to write, should not have newline at end * @param l Logger method, e.g., `logger.warn` * @param p Progress log */ export function loglog(msg: string, l: LeveledLogMethod, p: ProgressLog): void { l(msg); p.write(msg + "\n"); } export async function processResult(result: any, gi: GoalInvocation): Promise<ExecuteGoalResult | undefined> { const { goalEvent, context } = gi; if (!!result) { if (result.SdmGoal) { const goal = result.SdmGoal as SdmGoalEvent; const r = { state: goal.state, phase: goal.phase, description: goal.description, externalUrls: goal.externalUrls, data: convertData(goal.data), }; const builds = _.get(goal, "push.builds") as Build[]; if (!!builds) { for (const build of builds) { await postBuildWebhook( goalEvent.repo.owner, goalEvent.repo.name, goalEvent.branch, goalEvent.sha, build.status as any, context.workspaceId); } } const images = _.get(goal, "push.after.images") as Images[]; if (!!images) { for (const image of images) { await postLinkImageWebhook( goalEvent.repo.owner, goalEvent.repo.name, goalEvent.sha, image.imageName, context.workspaceId, ); } } const version = _.get(goal, "push.after.version"); if (!!version) { const sdmVersion: SdmVersion = { sha: goalEvent.sha, branch: gi.goalEvent.branch, version, repo: { owner: goalEvent.repo.owner, name: goalEvent.repo.name, providerId: goalEvent.repo.providerId, }, }; await gi.context.graphClient.mutate<UpdateSdmVersionMutation, UpdateSdmVersionMutationVariables>({ name: "UpdateSdmVersion", variables: { version: sdmVersion, }, options: MutationNoCacheOptions, }); } return r; } else { return { ...result, data: convertData(result.data), }; } } return undefined; } function convertData(data: any): string { return !!data && typeof data !== "string" ? JSON.stringify(data) : data; }