UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

126 lines 4.75 kB
"use strict"; /* * 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. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.parseNameStatusDiff = exports.diffPush = exports.k8sSpecRegExp = exports.k8sSpecGlob = void 0; const child_process_1 = require("@atomist/automation-client/lib/util/child_process"); /** * Glob pattern used to identify Kubernetes spec files in a * repository. It only matched JSON and YAML files at the root of the * repository. */ exports.k8sSpecGlob = "*.@(json|yaml|yml)"; /** * Regular expression used to identify Kubernetes spec files in a * repository. It only matched JSON and YAML files at the root of the * repository. */ exports.k8sSpecRegExp = /^[^/]+\.(?:json|ya?ml)$/; /** * Determine all changed Kubernetes resource spec files in a push. A * file is considered a Kubernetes resource spec if it matches * [[k8sSpecRegExp]]. Changes are returned in commit order, with the * commit order unchanged from the push event sent by cortex, which it * typically chronologically with the oldest commit first. Within a * commit, the changes are sorted first by operation, with deletes * before applies, and then by path using `sort(localCompare)`. * Deletes are sorted first because renames are processed as adds and * deletes and we want to avoid a rename resulting in a resource being * deleted. If you want to control the order of operations, spread * the operations across multiple commits in the same push. * * @param project project with the changed Kubernetes resource specs. * @param push git push with changes * @param tag git commit message tag indicating automated commits that should be ignored * @param log goal execution progress log * @return sorted resource spec changes */ async function diffPush(project, push, tag, log) { const changes = []; const commits = push.commits.filter(c => !c.message.includes(tag)); for (const commit of commits) { try { const sha = commit.sha; const args = ["diff", "-z", "--no-renames", "--name-status", "--diff-filter=ADM", `${sha}~1`, sha, "--"]; const opts = { cwd: project.baseDir }; const diffResult = await child_process_1.execPromise("git", args, opts); const newChanges = parseNameStatusDiff(sha, diffResult.stdout); changes.push(...newChanges); } catch (e) { e.message = `Failed to diff commit ${commit.sha}, skipping: ${e.message}`; log.write(e.message); } } return changes; } exports.diffPush = diffPush; /** * Convert output of `git diff -z --name-status` to a list of file * changes. The returned changes are sorted by file path. * * @param sha git commit SHA * @param diff output from git diff command * @return sorted resource spec changes */ function parseNameStatusDiff(sha, diff) { const changes = []; const diffContent = diff.trim().replace(/\0$/, ""); if (!diffContent) { return changes; } const fields = diffContent.split("\0"); for (let i = 0; i < fields.length; i += 2) { const f = fields[i]; if (!f) { throw new Error(`Empty git diff status`); } const path = fields[i + 1]; if (!path) { throw new Error(`Missing path from git diff status: ${f}`); } if (!exports.k8sSpecRegExp.test(path)) { continue; } if (f === "A" || f.startsWith("M")) { changes.push({ change: "apply", path, sha }); } else if (f === "D") { changes.push({ change: "delete", path, sha }); } else { throw new Error(`Unexpected git diff status for path '${path}' in commit ${sha}: ${f}`); } } return changes.sort(compareDiff); } exports.parseNameStatusDiff = parseNameStatusDiff; /** * Sort "delete" changes before "apply". Within each category, sort * by path. */ function compareDiff(a, b) { if (a.change === b.change) { return a.path.localeCompare(b.path); } else if (a.change === "delete") { return -1; } else { return 1; } } //# sourceMappingURL=diff.js.map