@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
224 lines (213 loc) • 7.29 kB
text/typescript
/*
* 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 { MutationNoCacheOptions } from "@atomist/automation-client/lib/spi/graph/GraphClient";
import * as fs from "fs-extra";
import * as _ from "lodash";
import * as path from "path";
import { SdmContext } from "../../../api/context/SdmContext";
import { ExecuteGoalResult } from "../../../api/goal/ExecuteGoalResult";
import { GoalInvocation } from "../../../api/goal/GoalInvocation";
import { SdmGoalEvent } from "../../../api/goal/SdmGoalEvent";
import { K8sNamespaceFile } from "../../../pack/k8s/support/namespace";
import {
OnBuildCompleteForDryRun,
UpdateSdmVersionMutation,
UpdateSdmVersionMutationVariables,
} from "../../../typings/types";
import { getGoalVersion } from "../../delivery/build/local/projectVersioner";
import { SdmVersion } from "../../ingesters/sdmVersionIngester";
import { postBuildWebhook } from "../../util/webhook/ImageLink";
import { ContainerInput, ContainerOutput, ContainerProjectHome } from "./container";
import Build = OnBuildCompleteForDryRun.Build;
/**
* 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;
}
}
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 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;
}