frontend/app/getbuilds.ts (188 lines of code) (raw):
import axios from "axios";
import {
SystemNotification,
SystemNotifcationKind,
} from "@guardian/pluto-headers";
function doubleEncode(str: string): string {
return encodeURIComponent(encodeURIComponent(str));
}
async function getLatestBuildInternal(
projectId: string,
buildJob: string,
branchName: string
): Promise<BuildInfo> {
//the branch name may contain a /; in this case it must be _double_ encoded or the / is considered part of the html path
const url = `/api/project/${doubleEncode(projectId)}/${doubleEncode(
branchName
)}/${buildJob}/buildinfo`;
const response = await axios.get<BuildInfo>(url, {
validateStatus: () => true,
});
switch (response.status) {
case 200:
return response.data;
default:
throw `Server returned ${response.status}`;
}
}
/**
* finds the value of the "gitlab-project-id" label
* @param deploymentInfo
*/
function getGLProjectId(deploymentInfo: DeployedImageInfo): string | undefined {
if (deploymentInfo.labels.hasOwnProperty("gitlab-project-id")) {
return deploymentInfo.labels["gitlab-project-id"];
} else {
return undefined;
}
}
function getGHProjectId(deploymentInfo: DeployedImageInfo): string | undefined {
if (deploymentInfo.labels.hasOwnProperty("github-project-name")) {
return deploymentInfo.labels["github-project-name"];
} else {
return undefined;
}
}
/**
* finds the value of the "gitlab-publishing-job" label
* @param deploymentInfo
*/
function getGHPublishingJob(
deploymentInfo: DeployedImageInfo
): string | undefined {
if (deploymentInfo.labels.hasOwnProperty("gitlab-publishing-job")) {
return deploymentInfo.labels["gitlab-publishing-job"];
} else {
return undefined;
}
}
function getGLPublishingJob(
deploymentInfo: DeployedImageInfo
): string | undefined {
if (deploymentInfo.labels.hasOwnProperty("github-publishing-job")) {
return deploymentInfo.labels["github-publishing-job"];
} else {
return undefined;
}
}
async function getLatestMainlineBuild(deploymentInfo: DeployedImageInfo) {
try {
const mainBuild = await getLatestBuild(deploymentInfo, "main");
return mainBuild;
} catch (err) {
if (err == "Server returned 404") {
return getLatestBuild(deploymentInfo, "master");
} else {
throw err;
}
}
}
async function getLatestBuild(
deploymentInfo: DeployedImageInfo,
branchName: string
) {
const maybeProjectId =
getGLProjectId(deploymentInfo) ?? getGHProjectId(deploymentInfo);
if (!maybeProjectId) {
console.log(
`${deploymentInfo.deploymentName}: can't get build info because there is no gitlab-project-id set in the labels`
);
return;
}
const jobName =
getGLPublishingJob(deploymentInfo) ?? getGHPublishingJob(deploymentInfo);
if (!jobName) {
console.log(
`${deploymentInfo.deploymentName}: can't get build info because there is not gitlab-publishing-job set in the labels`
);
return;
}
return getLatestBuildInternal(maybeProjectId, jobName, branchName);
}
const CompareVersionResult = {
SAME: "same",
NEEDS_UPDATE: "needs_update",
DEPLOYMENT_AHEAD: "deployment_ahead",
NON_NUMERIC: "non_numeric",
NOTHING_AVAILABLE: "nothing_available",
};
/**
* checks whether the deployed image is ahead or behind the one that is available.
* this first finds an image in DeployedImageInfo that has the same name as in `available` and does a version comparison on
* that. If there is no such image, then CompareVersionResult.NOTHING_AVAILABLE is returned.
*
* @param deployment `DeployedImageInfo` representing the format that is deployed
* @param available `BuildInfo` representing the image that is available from the server
* @returns a value from CompareVersionResult
*/
function compareVersionResults(
deployment: DeployedImageInfo,
available: BuildInfo
): string {
if (!available.built_image) {
return CompareVersionResult.NOTHING_AVAILABLE;
}
try {
const numericVersionAvailable = parseInt(available.built_image.version);
if (Number.isNaN(numericVersionAvailable))
return CompareVersionResult.NON_NUMERIC;
const relevantDeployedImages = deployment.deployedImages.filter(
(img) => img.imageName == available.built_image?.imageName
);
if (relevantDeployedImages.length == 0)
return CompareVersionResult.NOTHING_AVAILABLE;
const numericVersionDeployed = parseInt(relevantDeployedImages[0].version);
if (Number.isNaN(numericVersionDeployed))
return CompareVersionResult.NON_NUMERIC;
if (numericVersionAvailable == numericVersionDeployed) {
return CompareVersionResult.SAME;
} else if (numericVersionDeployed < numericVersionAvailable) {
return CompareVersionResult.NEEDS_UPDATE;
} else {
return CompareVersionResult.DEPLOYMENT_AHEAD;
}
} catch (e) {
return CompareVersionResult.NON_NUMERIC;
}
}
function getCookie(key: string) {
const b = document.cookie.match("(^|;)\\s*" + key + "\\s*=\\s*([^;]+)");
return b ? b.pop() : "";
}
async function requestUpdate(to: DockerImage, deploymentName: string) {
const requestBody: UpdateDeploymentRequest = {
to: to,
deploymentName: deploymentName,
};
const result = await axios.post("/api/deployment/update", requestBody, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Csrf-Token": getCookie("pvm-csrf"), //this must match the value of play.filters.csrf.cookie.name in application.conf
},
validateStatus: (status) => true,
});
switch (result.status) {
case 200:
SystemNotification.open(
SystemNotifcationKind.Success,
"Update is now underway. Use the Refresh button to check when the deployment completes."
);
break;
case 403 | 401:
SystemNotification.open(
SystemNotifcationKind.Error,
"Permission denied. You are either not an administrator or your login expired."
);
break;
case 409:
const response = result.data as ConflictError;
console.error(
`Server expected one of ${response.deployed} but we requested ${response.expected}`
);
SystemNotification.open(
SystemNotifcationKind.Error,
"Could not perform the update because the deployed image has a different name to the requested one"
);
break;
default:
const responseText =
result.data && result.data.hasOwnProperty("detail")
? result.data.detail
: result.data.toString();
SystemNotification.open(
SystemNotifcationKind.Error,
`Could not perform update: ${responseText}`
);
break;
}
}
export {
getLatestMainlineBuild,
getLatestBuild,
compareVersionResults,
CompareVersionResult,
requestUpdate,
};