@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
164 lines (132 loc) • 5.33 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 { BaseContext, GitHubStatusContext } from "./GitHubContext";
import { GoalEnvironment, IndependentOfEnvironment } from "./support/environment";
/**
* Core data for a goal
*/
export interface GoalDefinition {
/**
* Must be unique among goals
* Should be camel case
*/
uniqueName: string;
/**
* Optional environment for this goal to run in.
* This is meant to allow for logical grouping of goals from code, testing and production etc.
*/
environment?: GoalEnvironment;
displayName?: string;
plannedDescription?: string;
requestedDescription?: string;
completedDescription?: string;
workingDescription?: string;
failedDescription?: string;
waitingForApprovalDescription?: string;
waitingForPreApprovalDescription?: string;
canceledDescription?: string;
stoppedDescription?: string;
skippedDescription?: string;
// when set to true, this goal will execute in its own container/client
isolated?: boolean;
// when set to true, this goal requires approval before it is marked success
approvalRequired?: boolean;
// when set to true, this goal requires pre approval before it is requested
preApprovalRequired?: boolean;
// when set to true, this goal can be retried in case of failure
retryFeasible?: boolean;
}
/**
* Represents a delivery action, such as Build or Deploy.
*/
export class Goal {
public readonly context: GitHubStatusContext;
public readonly definition: GoalDefinition;
get environment(): string {
return this.definition.environment;
}
get successDescription(): string {
return this.definition.completedDescription || `Complete: ${this.name}`;
}
get inProcessDescription(): string {
return this.definition.workingDescription || `Working: ${this.name}`;
}
get failureDescription(): string {
return this.definition.failedDescription || `Failed: ${this.name}`;
}
get plannedDescription(): string {
return this.definition.plannedDescription || `Planned: ${this.name}`;
}
get requestedDescription(): string {
return this.definition.requestedDescription || `Ready: ${this.name}`;
}
get waitingForApprovalDescription(): string {
return this.definition.waitingForApprovalDescription || `Approval required: ${this.name}`;
}
get waitingForPreApprovalDescription(): string {
return this.definition.waitingForPreApprovalDescription || `Start required: ${this.name}`;
}
get canceledDescription(): string {
return this.definition.canceledDescription || `Canceled: ${this.name}`;
}
get stoppedDescription(): string {
return this.definition.stoppedDescription || `Stopped: ${this.name}`;
}
get skippedDescription(): string {
return this.definition.skippedDescription || `Skipped: ${this.name}`;
}
get name(): string {
return this.definition.displayName || this.definition.uniqueName;
}
get uniqueName(): string {
return this.definition.uniqueName;
}
constructor(definition: GoalDefinition) {
this.definition = validateGoalDefinition(definition);
// Default environment if hasn't been provided
if (!this.definition.environment) {
this.definition.environment = IndependentOfEnvironment;
}
this.context = BaseContext + this.definition.environment + this.definition.uniqueName;
}
}
export class GoalWithPrecondition extends Goal {
public readonly dependsOn: Goal[];
constructor(definition: GoalDefinition, ...dependsOn: Goal[]) {
super(definition);
this.dependsOn = dependsOn;
}
}
export function isGoalDefinition(f: Goal | GoalDefinition): f is GoalDefinition {
return !!(f as GoalDefinition).uniqueName;
}
export function hasPreconditions(goal: Goal): goal is GoalWithPrecondition {
return !!(goal as GoalWithPrecondition).dependsOn;
}
const UniqueNameRegExp = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/i;
function validateGoalDefinition(gd: GoalDefinition): GoalDefinition {
// First replace all spaces and _ with -
const uniqueName = gd.uniqueName.replace(/ /g, "-").replace(/_/g, "-");
// Now validate the part in front of # against regexp
if (!UniqueNameRegExp.test(uniqueName.split("#")[0])) {
throw new Error(
`Goal uniqueName '${gd.uniqueName}' must consist of lower case alphanumeric characters or '-', and must start and ` +
`end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '${UniqueNameRegExp}')`,
);
}
gd.uniqueName = uniqueName;
return gd;
}