gitgreen 0.1.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchCloudWatchTimeseries = void 0;
4
+ const client_cloudwatch_1 = require("@aws-sdk/client-cloudwatch");
5
+ const toTimeseries = (result) => {
6
+ if (!result)
7
+ return [];
8
+ const timestamps = result.Timestamps || [];
9
+ const values = result.Values || [];
10
+ const points = [];
11
+ const len = Math.min(timestamps.length, values.length);
12
+ for (let i = 0; i < len; i++) {
13
+ const value = values[i];
14
+ if (value === undefined || value === null)
15
+ continue;
16
+ const ts = timestamps[i];
17
+ points.push({
18
+ timestamp: ts instanceof Date ? ts.toISOString() : new Date(ts).toISOString(),
19
+ value: Number(value)
20
+ });
21
+ }
22
+ return points.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
23
+ };
24
+ const fetchCloudWatchTimeseries = async (opts) => {
25
+ const cw = new client_cloudwatch_1.CloudWatchClient({ region: opts.region });
26
+ const periodSeconds = opts.periodSeconds ?? 60;
27
+ const command = new client_cloudwatch_1.GetMetricDataCommand({
28
+ StartTime: opts.startTime,
29
+ EndTime: opts.endTime,
30
+ ScanBy: 'TimestampAscending',
31
+ MetricDataQueries: [
32
+ {
33
+ Id: 'cpu',
34
+ MetricStat: {
35
+ Metric: {
36
+ Namespace: 'AWS/EC2',
37
+ MetricName: 'CPUUtilization',
38
+ Dimensions: [{ Name: 'InstanceId', Value: opts.instanceId }]
39
+ },
40
+ Period: periodSeconds,
41
+ Stat: 'Average'
42
+ },
43
+ ReturnData: true
44
+ },
45
+ {
46
+ Id: 'ramUsed',
47
+ MetricStat: {
48
+ Metric: {
49
+ Namespace: 'CWAgent',
50
+ MetricName: 'mem_used',
51
+ Dimensions: [{ Name: 'InstanceId', Value: opts.instanceId }]
52
+ },
53
+ Period: periodSeconds,
54
+ Stat: 'Average'
55
+ },
56
+ ReturnData: true
57
+ },
58
+ {
59
+ Id: 'ramTotal',
60
+ MetricStat: {
61
+ Metric: {
62
+ Namespace: 'CWAgent',
63
+ MetricName: 'mem_total',
64
+ Dimensions: [{ Name: 'InstanceId', Value: opts.instanceId }]
65
+ },
66
+ Period: periodSeconds,
67
+ Stat: 'Average'
68
+ },
69
+ ReturnData: true
70
+ },
71
+ {
72
+ Id: 'ramUsedPercent',
73
+ MetricStat: {
74
+ Metric: {
75
+ Namespace: 'CWAgent',
76
+ MetricName: 'mem_used_percent',
77
+ Dimensions: [{ Name: 'InstanceId', Value: opts.instanceId }]
78
+ },
79
+ Period: periodSeconds,
80
+ Stat: 'Average'
81
+ },
82
+ ReturnData: true
83
+ }
84
+ ]
85
+ });
86
+ const resp = await cw.send(command);
87
+ const byId = new Map();
88
+ (resp.MetricDataResults || []).forEach(result => {
89
+ if (result.Id)
90
+ byId.set(result.Id, result);
91
+ });
92
+ const cpuUtilization = toTimeseries(byId.get('cpu'));
93
+ const ramUsed = toTimeseries(byId.get('ramUsed'));
94
+ let ramTotal = toTimeseries(byId.get('ramTotal'));
95
+ const ramUsedPercent = toTimeseries(byId.get('ramUsedPercent'));
96
+ let ramUsedSeries = ramUsed;
97
+ const fallbackTotal = ramTotal[0]?.value || opts.memoryBytesFallback;
98
+ if (!ramUsedSeries.length && ramUsedPercent.length && fallbackTotal) {
99
+ ramUsedSeries = ramUsedPercent.map(point => ({
100
+ timestamp: point.timestamp,
101
+ value: (point.value / 100) * fallbackTotal
102
+ }));
103
+ }
104
+ if (!ramTotal.length && fallbackTotal) {
105
+ const ts = ramUsedSeries[0]?.timestamp || new Date().toISOString();
106
+ ramTotal = [{ timestamp: ts, value: fallbackTotal }];
107
+ }
108
+ return { cpuUtilization, ramUsed: ramUsedSeries, ramTotal };
109
+ };
110
+ exports.fetchCloudWatchTimeseries = fetchCloudWatchTimeseries;
@@ -36,17 +36,19 @@ class CarbonCalculator {
36
36
  const cpuSorted = [...job.cpuTimeseries].sort((a, b) => this.parseTimestamp(a.timestamp) - this.parseTimestamp(b.timestamp));
37
37
  const ramUsedSorted = [...job.ramUsedTimeseries].sort((a, b) => this.parseTimestamp(a.timestamp) - this.parseTimestamp(b.timestamp));
38
38
  const ramSizeSorted = [...job.ramSizeTimeseries].sort((a, b) => this.parseTimestamp(a.timestamp) - this.parseTimestamp(b.timestamp));
39
- // GCP reports metrics every 60 seconds
40
- const GCP_INTERVAL_SECONDS = 60;
39
+ const DEFAULT_INTERVAL_SECONDS = 60;
41
40
  // Integrate CPU energy over timeseries
42
41
  let cpuEnergyKwh = 0;
43
42
  for (let i = 0; i < cpuSorted.length; i++) {
44
- const cpuPercent = cpuSorted[i].value * 100; // GCP returns 0-1, convert to 0-100
43
+ const rawCpuValue = cpuSorted[i].value;
44
+ const cpuPercent = job.provider === 'aws'
45
+ ? (rawCpuValue <= 1 ? rawCpuValue * 100 : rawCpuValue)
46
+ : rawCpuValue * 100;
45
47
  const powerWatts = this.interpolatePower(cpuProfile, cpuPercent);
46
48
  // Calculate interval from timestamps, or use GCP interval for single point
47
49
  let intervalSeconds;
48
50
  if (cpuSorted.length === 1) {
49
- intervalSeconds = GCP_INTERVAL_SECONDS;
51
+ intervalSeconds = DEFAULT_INTERVAL_SECONDS;
50
52
  }
51
53
  else if (i < cpuSorted.length - 1) {
52
54
  intervalSeconds = this.parseTimestamp(cpuSorted[i + 1].timestamp) - this.parseTimestamp(cpuSorted[i].timestamp);
@@ -65,7 +67,7 @@ class CarbonCalculator {
65
67
  const powerWatts = ramUsedGb * RAM_WATTS_PER_GB;
66
68
  let intervalSeconds;
67
69
  if (ramUsedSorted.length === 1) {
68
- intervalSeconds = GCP_INTERVAL_SECONDS;
70
+ intervalSeconds = DEFAULT_INTERVAL_SECONDS;
69
71
  }
70
72
  else if (i < ramUsedSorted.length - 1) {
71
73
  intervalSeconds = this.parseTimestamp(ramUsedSorted[i + 1].timestamp) - this.parseTimestamp(ramUsedSorted[i].timestamp);
@@ -78,8 +80,8 @@ class CarbonCalculator {
78
80
  // Calculate total runtime from timeseries
79
81
  const firstTs = Math.min(cpuSorted.length > 0 ? this.parseTimestamp(cpuSorted[0].timestamp) : Infinity, ramUsedSorted.length > 0 ? this.parseTimestamp(ramUsedSorted[0].timestamp) : Infinity);
80
82
  const lastTs = Math.max(cpuSorted.length > 0 ? this.parseTimestamp(cpuSorted[cpuSorted.length - 1].timestamp) : 0, ramUsedSorted.length > 0 ? this.parseTimestamp(ramUsedSorted[ramUsedSorted.length - 1].timestamp) : 0);
81
- // If single point, use GCP interval; otherwise use actual span
82
- const runtimeSeconds = lastTs === firstTs ? GCP_INTERVAL_SECONDS : (lastTs - firstTs);
83
+ // If single point, use default interval; otherwise use actual span
84
+ const runtimeSeconds = lastTs === firstTs ? DEFAULT_INTERVAL_SECONDS : (lastTs - firstTs);
83
85
  const runtimeHours = runtimeSeconds / 3600;
84
86
  // Calculate emissions
85
87
  const cpuEmissions = cpuEnergyKwh * pue * carbonIntensity;
@@ -64,7 +64,12 @@ const buildMarkdownReport = (result, job, budget) => {
64
64
  }
65
65
  // Add CPU chart (only if more than 1 point)
66
66
  if (cpuSorted.length > 1) {
67
- const cpuValues = cpuSorted.map(p => p.value * 100);
67
+ const cpuValues = cpuSorted.map(p => {
68
+ if (job.provider === 'aws') {
69
+ return p.value <= 1 ? p.value * 100 : p.value;
70
+ }
71
+ return p.value * 100;
72
+ });
68
73
  lines.push('', '## CPU Utilization (%)', '```', asciichart_1.default.plot(cpuValues, { height: 6 }), '```');
69
74
  }
70
75
  // Add RAM chart (only if more than 1 point)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitgreen",
3
- "version": "0.1.1",
3
+ "version": "1.0.1",
4
4
  "description": "GitGreen CLI for carbon reporting in GitLab pipelines (GCP/AWS)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",