UNPKG

@atomist/sdm-pack-aspect

Version:

an Atomist SDM Extension Pack for visualizing drift across an organization

152 lines (137 loc) 4.35 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 { LocalProject, } from "@atomist/automation-client"; import { Aspect, ExtractFingerprint, fingerprintOf, } from "@atomist/sdm-pack-fingerprint"; import * as child_process from "child_process"; import * as util from "util"; import { bandFor, Default, } from "../../util/bands"; import { AgeBands, SizeBands, } from "../../util/commonBands"; import { showTiming } from "../../util/showTiming"; import { CountAspect, CountData, } from "../compose/commonTypes"; import { daysSince } from "./dateUtils"; const exec = util.promisify(child_process.exec); const gitLastCommitCommand = "git log -1 --format=%cd --date=short"; export const GitRecencyType = "git-recency"; export interface GitRecencyData { lastCommitTime: number; } const gitRecencyExtractor: ExtractFingerprint<GitRecencyData> = async p => { const r = await exec(gitLastCommitCommand, { cwd: (p as LocalProject).baseDir }); if (!r.stdout) { return undefined; } const data = { lastCommitTime: new Date(r.stdout.trim()).getTime() }; return fingerprintOf({ type: GitRecencyType, data, }); }; /** * Classify since last commit */ export const GitRecency: Aspect<GitRecencyData> = { name: GitRecencyType, displayName: "Recency of git activity", baseOnly: true, extract: gitRecencyExtractor, toDisplayableFingerprintName: () => "Recency of git activity", toDisplayableFingerprint: fp => { const date = new Date(fp.data.lastCommitTime); return lastDateToActivityBand(date); }, stats: { defaultStatStatus: { entropy: false, }, }, }; function committersCommands(commitDepth: number): string[] { return [ `git fetch --depth=${commitDepth}`, `git shortlog -s -n --all --max-count ${commitDepth}`, ]; } export const GitActivesType = "git-actives"; function activeCommittersExtractor(commitDepth: number): ExtractFingerprint<CountData> { return async p => { const cwd = (p as LocalProject).baseDir; const cmds = committersCommands(commitDepth); const r = await showTiming(`commands ${cmds} in ${cwd}`, async () => { exec(cmds[0], { cwd }); return exec(cmds[1], { cwd }); }); if (!r.stdout) { return undefined; } const count = r.stdout.trim().split("\n").length; const data = { count }; return fingerprintOf({ type: GitActivesType, data, }); }; } /** * Active committers. This is expensive as it requires cloning the * last commitDepth commits */ export function gitActiveCommitters(opts: { commitDepth: number}): CountAspect { return { name: GitActivesType, displayName: "Active git committers", baseOnly: true, extract: activeCommittersExtractor(opts.commitDepth), toDisplayableFingerprintName: () => `Active git committers to ${opts.commitDepth} commits`, toDisplayableFingerprint: fp => { return bandFor<SizeBands>({ low: { upTo: 4 }, medium: { upTo: 12 }, high: Default, }, fp.data.count, { includeNumber: true }); }, stats: { defaultStatStatus: { entropy: false, }, basicStatsPath: "count", }, }; } function lastDateToActivityBand(date: Date): string { const days = daysSince(date); return bandFor<AgeBands>({ current: { upTo: 30 }, recent: { upTo: 200 }, ancient: { upTo: 500 }, prehistoric: Default, }, days, { includeNumber: true }); }