blob: 8b034e2c3d902a7e0899dfb79a1a81acbefe99b4 [file] [log] [blame]
Allen Li24bf8182017-03-02 16:41:20 -08001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Git repo metrics."""
6
7from __future__ import absolute_import
Allen Li24bf8182017-03-02 16:41:20 -08008
Chris McDonald59650c32021-07-20 15:29:28 -06009import logging
Allen Li24bf8182017-03-02 16:41:20 -080010import os
11import subprocess
12
Allen Lia9c6e802017-07-11 15:42:47 -070013from chromite.lib import metrics
Allen Li24bf8182017-03-02 16:41:20 -080014
Chris McDonald59650c32021-07-20 15:29:28 -060015
Allen Li24bf8182017-03-02 16:41:20 -080016logger = logging.getLogger(__name__)
17
18
19class _GitRepo(object):
Alex Klein1699fab2022-09-08 08:46:06 -060020 """Helper class for running git commands."""
Allen Li24bf8182017-03-02 16:41:20 -080021
Alex Klein1699fab2022-09-08 08:46:06 -060022 def __init__(self, gitdir):
23 self._gitdir = gitdir
Allen Li24bf8182017-03-02 16:41:20 -080024
Alex Klein1699fab2022-09-08 08:46:06 -060025 def _get_git_command(self):
26 return ["git", "--git-dir", self._gitdir]
Allen Li24bf8182017-03-02 16:41:20 -080027
Alex Klein1699fab2022-09-08 08:46:06 -060028 def _check_output(self, args, **kwargs):
29 return subprocess.check_output(
30 self._get_git_command() + list(args), **kwargs
31 ).decode("utf-8")
Allen Li24bf8182017-03-02 16:41:20 -080032
Alex Klein1699fab2022-09-08 08:46:06 -060033 def get_commit_hash(self):
34 """Return commit hash string."""
35 return self._check_output(["rev-parse", "HEAD"]).strip()
Allen Li24bf8182017-03-02 16:41:20 -080036
Alex Klein1699fab2022-09-08 08:46:06 -060037 def get_commit_time(self):
38 """Return commit time as UNIX timestamp int."""
39 return int(
40 self._check_output(["show", "-s", "--format=%ct", "HEAD"]).strip()
41 )
Allen Li24bf8182017-03-02 16:41:20 -080042
Alex Klein1699fab2022-09-08 08:46:06 -060043 def get_unstaged_changes(self):
44 """Return number of unstaged changes as (added, deleted)."""
45 added_total, deleted_total = 0, 0
46 # output looks like:
47 # '1\t2\tfoo\n3\t4\tbar\n'
48 # '-\t-\tbinary_file\n'
49 output = self._check_output(["diff-index", "--numstat", "HEAD"])
50 stats_strings = (line.split() for line in output.splitlines())
51 for added, deleted, _path in stats_strings:
52 if added != "-":
53 added_total += int(added)
54 if deleted != "-":
55 deleted_total += int(deleted)
56 return added_total, deleted_total
Allen Li8934e042017-06-21 15:54:42 -070057
Allen Li24bf8182017-03-02 16:41:20 -080058
59class _GitMetricCollector(object):
Alex Klein1699fab2022-09-08 08:46:06 -060060 """Class for collecting metrics about a git repository.
Allen Libdb9f042017-04-10 13:25:47 -070061
Alex Klein1699fab2022-09-08 08:46:06 -060062 The constructor takes the arguments: `gitdir`, `metric_path`.
63 `gitdir` is the path to the Git directory to collect metrics for and
64 may start with a tilde (expanded to a user's home directory).
65 `metric_path` is the Monarch metric path to report to.
66 """
Allen Li24bf8182017-03-02 16:41:20 -080067
Alex Klein1699fab2022-09-08 08:46:06 -060068 _commit_hash_metric = metrics.StringMetric(
69 "git/hash", description="Current Git commit hash."
70 )
Allen Li24bf8182017-03-02 16:41:20 -080071
Alex Klein1699fab2022-09-08 08:46:06 -060072 _timestamp_metric = metrics.GaugeMetric(
73 "git/timestamp",
74 description="Current Git commit time as seconds since Unix Epoch.",
75 )
Allen Li24bf8182017-03-02 16:41:20 -080076
Alex Klein1699fab2022-09-08 08:46:06 -060077 _unstaged_changes_metric = metrics.GaugeMetric(
78 "git/unstaged_changes", description="Unstaged Git changes."
79 )
Allen Li8934e042017-06-21 15:54:42 -070080
Alex Klein1699fab2022-09-08 08:46:06 -060081 def __init__(self, gitdir, metric_path):
82 self._gitdir = gitdir
83 self._gitrepo = _GitRepo(os.path.expanduser(gitdir))
84 self._fields = {"repo": gitdir}
85 self._metric_path = metric_path
Allen Li24bf8182017-03-02 16:41:20 -080086
Alex Klein1699fab2022-09-08 08:46:06 -060087 def collect(self):
88 """Collect metrics."""
89 try:
90 self._collect_commit_hash_metric()
91 self._collect_timestamp_metric()
92 self._collect_unstaged_changes_metric()
93 except subprocess.CalledProcessError as e:
94 logger.warning(
95 "Error collecting git metrics for %s: %s", self._gitdir, e
96 )
Allen Li24bf8182017-03-02 16:41:20 -080097
Alex Klein1699fab2022-09-08 08:46:06 -060098 def _collect_commit_hash_metric(self):
99 commit_hash = self._gitrepo.get_commit_hash()
100 logger.debug("Collecting Git hash %r for %r", commit_hash, self._gitdir)
101 self._commit_hash_metric.set(commit_hash, self._fields)
Allen Li24bf8182017-03-02 16:41:20 -0800102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 def _collect_timestamp_metric(self):
104 commit_time = self._gitrepo.get_commit_time()
105 logger.debug(
106 "Collecting Git timestamp %r for %r", commit_time, self._gitdir
107 )
108 self._timestamp_metric.set(commit_time, self._fields)
Allen Li24bf8182017-03-02 16:41:20 -0800109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 def _collect_unstaged_changes_metric(self):
111 added, deleted = self._gitrepo.get_unstaged_changes()
112 self._unstaged_changes_metric.set(
113 added, fields=dict(change_type="added", **self._fields)
114 )
115 self._unstaged_changes_metric.set(
116 deleted, fields=dict(change_type="deleted", **self._fields)
117 )
Allen Li8934e042017-06-21 15:54:42 -0700118
Allen Li24bf8182017-03-02 16:41:20 -0800119
Alex Klein1699fab2022-09-08 08:46:06 -0600120_CHROMIUMOS_DIR = "~chromeos-test/chromiumos/"
Allen Li24bf8182017-03-02 16:41:20 -0800121
122_repo_collectors = (
Allen Lia02d34a2017-04-04 17:13:46 -0700123 # TODO(ayatane): We cannot access chromeos-admin because we are
124 # running as non-root.
Alex Klein1699fab2022-09-08 08:46:06 -0600125 _GitMetricCollector(
126 gitdir="/root/chromeos-admin/.git", metric_path="chromeos-admin"
127 ),
128 _GitMetricCollector(
129 gitdir=_CHROMIUMOS_DIR + "chromite/.git", metric_path="chromite"
130 ),
131 _GitMetricCollector(
132 gitdir="/usr/local/autotest/.git", metric_path="installed_autotest"
133 ),
Allen Li24bf8182017-03-02 16:41:20 -0800134)
135
136
137def collect_git_metrics():
Alex Klein1699fab2022-09-08 08:46:06 -0600138 """Collect metrics for Git repository state."""
139 for collector in _repo_collectors:
140 collector.collect()