ghamma 0.1.1 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad500d312e1ec3068bcebd224b215cc53cefb71c70ec88f765d3dda38c1605fa
4
- data.tar.gz: 26265dd2d7e18b24d6649d535b2546c42ea41404238e15117f65045621da1f31
3
+ metadata.gz: 26ed59a6dae9c41b8fe6a3aa7b24f7ba2cc430696acfa061094c2e1e690a437d
4
+ data.tar.gz: 7bc89adfb0d48a8c4cfed55c1633771fcf57889e56e67efcbe2cc4f5d4ab5d7c
5
5
  SHA512:
6
- metadata.gz: c7cd7a72afe17f051550b28fd8b3de74227d38c1ef02e24a62242d355efcff1bf183103188b365201cec19e2506d1a2c3d6cbfb7c3e288e78abd612d08fc1174
7
- data.tar.gz: 3749db0be1011a9998684e5f9d66a1d990a97233e892581655136b576916e3becee978bc17002b3617b2536401339ba0ed4ce6c0d107e5c5a65035f0f3f997af
6
+ metadata.gz: a0df963608fd632710848105a954214aa5c6fbc33a7a789230d372567d0de534f5320e82ce2cd0d4b5854b960528faa5f1ca64962e2ed5c7f56219b19a0bfcb4
7
+ data.tar.gz: 7817262789b374aa634ac3c5087d88c3e9672bcb50b783928c2bf349461278c41da2b4b9702c062b4bed7116940b1eea4e584cd67f6ba72d1774a9286be53fcb
data/CHANGELOG.md CHANGED
@@ -1,4 +1,17 @@
1
- ## [Unreleased]
1
+ ## Unreleased
2
+
3
+ ## [0.3.0] - 2023-07-04
4
+
5
+ - Feature: Allow optionally specifying an output file for the CSV
6
+ - Change: Add ID of the workflow run to the output
7
+ - Feature: Support restricting workflow runs examined since a given date
8
+ - Feature: Fetch all the workflow runs, not just the first 100
9
+ - Breaking: Removed the `list-workflows` command and renamed the `duration-history` to just `duration`
10
+ - Breaking: The `duration` command now accepts the workflow file name (or ID) instead of the name
11
+
12
+ ## [0.2.0] - 2023-06-01
13
+
14
+ - Breaking: Add commands to the CLI: `version`, `list-worklows`, `duration-history`
2
15
 
3
16
  ## [0.1.1] - 2023-06-01
4
17
 
data/Gemfile.lock CHANGED
@@ -1,8 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ghamma (0.1.1)
4
+ ghamma (0.3.0)
5
+ dry-cli
5
6
  http
7
+ tty-progressbar
6
8
 
7
9
  GEM
8
10
  remote: https://rubygems.org/
@@ -12,6 +14,7 @@ GEM
12
14
  ast (2.4.2)
13
15
  domain_name (0.5.20190701)
14
16
  unf (>= 0.0.5, < 1.0.0)
17
+ dry-cli (1.0.0)
15
18
  ffi (1.15.5)
16
19
  ffi-compiler (1.0.1)
17
20
  ffi (>= 1.0.0)
@@ -66,8 +69,16 @@ GEM
66
69
  standard-performance (1.0.1)
67
70
  lint_roller (~> 1.0)
68
71
  rubocop-performance (~> 1.16.0)
72
+ strings-ansi (0.2.0)
69
73
  test-unit (3.5.9)
70
74
  power_assert
75
+ tty-cursor (0.7.1)
76
+ tty-progressbar (0.18.2)
77
+ strings-ansi (~> 0.2)
78
+ tty-cursor (~> 0.7)
79
+ tty-screen (~> 0.8)
80
+ unicode-display_width (>= 1.6, < 3.0)
81
+ tty-screen (0.8.1)
71
82
  unf (0.1.4)
72
83
  unf_ext
73
84
  unf_ext (0.0.8.2)
data/README.md CHANGED
@@ -13,17 +13,43 @@ If bundler is not being used to manage dependencies, install the gem by executin
13
13
 
14
14
  ## Usage
15
15
 
16
- TODO: Write usage instructions here
16
+ ### Authentication
17
+
18
+ The Github workflows API requires an authenticated user, even for public repos.
19
+ To use this tool, you will need a
20
+ [Github API Token](https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api#authenticating-with-a-personal-access-token)
21
+ with at least `read` access to the repo you want to look at.
22
+
23
+ Once you have that, make it available.
24
+
25
+ ```
26
+ $ export GH_TOKEN="YOUR_API_TOKEN"
27
+ ```
28
+
29
+ Then fetch the list of available workflows for the repo. The name will be necessary in the next step.
30
+
31
+ ```
32
+ $ ghamma list-workflows tony-rowan ghamma
33
+ Found 1 workflows
34
+ Ruby
35
+ ```
36
+
37
+ Then fetch a CSV of the durations of the latest workflow runs.
38
+
39
+ ```
40
+ $ ghamma duration-history tony-rowan ghamma Ruby
41
+ Date,Duration
42
+ 2023-06-01T15:44:49Z,21000
43
+ 2023-06-01T15:37:21Z,17000
44
+ 2023-06-01T15:35:16Z,23000
45
+ 2023-06-01T15:31:33Z,19000
46
+ ```
17
47
 
18
48
  ## Development
19
49
 
20
50
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test-unit` to run the tests.
21
51
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
22
52
 
23
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update
24
- the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the
25
- version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
26
-
27
53
  ## Contributing
28
54
 
29
55
  Bug reports and pull requests are welcome on GitHub at https://github.com/tony-rowan/ghamma. This project is
data/exe/ghamma CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'ghamma'
3
+ require "ghamma"
4
4
 
5
- Ghamma::Cli.new.print_workflow_duration_metrics
5
+ Dry::CLI.new(Ghamma::CLI).call
@@ -0,0 +1,61 @@
1
+ require "csv"
2
+ require "dry/cli"
3
+ require "http"
4
+ require "tty/progressbar"
5
+
6
+ require_relative "../github_api_client"
7
+
8
+ module Ghamma
9
+ module CLI
10
+ class Duration < Dry::CLI::Command
11
+ desc "Extract the durations of successful runs of a workflow over time"
12
+
13
+ example [
14
+ "tony-rowan ghamma main.yml" \
15
+ " # Prints durations of all workflow runs of main.yml to STDOUT",
16
+ "tony-rowan ghamma main.yml --since='2023-07-01' --output='/tmp/main.csv'" \
17
+ " # Saves durations of all workflow runs of main.yml since 2023-07-01 to /tmp/main.csv"
18
+ ]
19
+
20
+ argument :owner, required: true, desc: "The user or organisation to whom the repo belongs"
21
+ argument :repo, required: true, desc: "The repo to which the workflow belongs"
22
+ argument :workflow, required: true, desc: "The filename for the desired workflow, e.g. main.yml"
23
+
24
+ option :since, desc: "Optionally restrict workflows to those created since this date"
25
+ option :output, desc: "Optional file to which to output results, defaults to STDOUT"
26
+
27
+ def call(owner:, repo:, workflow:, since: nil, output: nil)
28
+ workflow_statement = "Fetching the durations of all workflow runs of #{workflow}"
29
+ since_statment = "since #{since}" if since
30
+
31
+ puts [workflow_statement, since_statment].join(" ")
32
+
33
+ progressbar = TTY::ProgressBar.new("Fetching workflow runs [:bar] :current/:total ETA: :eta", total: nil)
34
+
35
+ durations = GithubApiClient.new(owner: owner, repo: repo, token: ENV["GH_TOKEN"])
36
+ .fetch_workflow_duration_history(workflow, since, progressbar)
37
+
38
+ progressbar.finish
39
+
40
+ puts "Generating output"
41
+
42
+ durations_output = CSV.generate do |csv|
43
+ csv << ["ID", "Date", "Duration"]
44
+ durations.each do |duration|
45
+ csv << duration
46
+ end
47
+ end
48
+
49
+ if output
50
+ File.write(output, durations_output)
51
+ else
52
+ puts
53
+ puts durations_output
54
+ puts
55
+ end
56
+
57
+ puts "Done"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,15 @@
1
+ require "dry/cli"
2
+
3
+ require_relative "../version"
4
+
5
+ module Ghamma
6
+ module CLI
7
+ class Version < Dry::CLI::Command
8
+ desc "Print version"
9
+
10
+ def call(*)
11
+ puts VERSION
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/ghamma/cli.rb CHANGED
@@ -1,80 +1,13 @@
1
- require "csv"
2
- require "http"
1
+ require "dry/cli"
3
2
 
4
- module Ghamma
5
- class Cli
6
-
7
- BASE_URL = "https://api.github.com".freeze
8
- OWNER = ARGV[0].freeze
9
- REPOSITORY = ARGV[1].freeze
10
-
11
- def api_url(resource)
12
- "#{BASE_URL}#{resource}"
13
- end
14
-
15
- def get(resource, params = {})
16
- response = HTTP
17
- .auth("Bearer #{ENV["GH_TOKEN"]}")
18
- .get(api_url(resource), params: params)
19
- .body
20
- JSON.parse(response)
21
- end
22
-
23
- def fetch_workflows
24
- response = get "/repos/#{OWNER}/#{REPOSITORY}/actions/workflows"
25
- response["workflows"].map { |json| {id: json["id"], name: json["name"]} }.tap do |workflows|
26
- puts "Found #{workflows.size} workflows"
27
- end
28
- end
29
-
30
- def fetch_workflows_with_runs
31
- fetch_workflows.map do |workflow|
32
- response = get "/repos/#{OWNER}/#{REPOSITORY}/actions/workflows/#{workflow[:id]}/runs",
33
- {per_page: 100, status: "success", exclude_pull_requests: true, created: ">2023-05-01"}
34
- puts "Fetched runs for #{workflow[:name]}"
3
+ require_relative "./cli/version"
4
+ require_relative "./cli/duration"
35
5
 
36
- next if response["total_count"].zero?
37
-
38
- {
39
- id: workflow[:id],
40
- name: workflow[:name],
41
- runs: response["workflow_runs"].map { |json| {id: json["id"], date: json["created_at"]} }
42
- }
43
- end.compact
44
- end
45
-
46
- def fetch_workflow_duration_metrics
47
- fetch_workflows_with_runs.map do |workflow|
48
- runs_with_timing = workflow[:runs].map do |workflow_run|
49
- response = get "/repos/#{OWNER}/#{REPOSITORY}/actions/runs/#{workflow_run[:id]}/timing"
50
-
51
- {
52
- duration: response["run_duration_ms"],
53
- date: workflow_run[:date]
54
- }
55
- end
56
-
57
- puts "Fetched timings for #{workflow[:name]}"
58
-
59
- {
60
- name: workflow[:name],
61
- runs: runs_with_timing
62
- }
63
- end
64
- end
6
+ module Ghamma
7
+ module CLI
8
+ extend Dry::CLI::Registry
65
9
 
66
- def print_workflow_duration_metrics
67
- fetch_workflow_duration_metrics.each do |workflow_metrics|
68
- puts workflow_metrics[:name]
69
- metrics_table = CSV.generate do |csv|
70
- csv << ["Date", "Duration"]
71
- workflow_metrics[:runs].each do |metric_datum|
72
- csv << [metric_datum[:date], metric_datum[:duration]]
73
- end
74
- end
75
- puts metrics_table
76
- puts
77
- end
78
- end
10
+ register "version", Version, aliases: ["v", "-v", "--version"]
11
+ register "duration", Duration
79
12
  end
80
13
  end
@@ -0,0 +1,61 @@
1
+ require "http"
2
+
3
+ module Ghamma
4
+ class GithubApiClient
5
+ BASE_URL = "https://api.github.com".freeze
6
+
7
+ def initialize(owner:, repo:, token:)
8
+ @repo_base_url = "#{BASE_URL}/repos/#{owner}/#{repo}"
9
+ @authorization_header = "Bearer #{token}"
10
+ end
11
+
12
+ def fetch_workflow_duration_history(workflow_id, since, progressbar)
13
+ pages = 2 # enough to do the first loop
14
+ page = 1
15
+ timings = []
16
+
17
+ while page <= pages
18
+ since_param = ">#{since}" if since
19
+
20
+ response = get(
21
+ "/workflows/#{workflow_id}/runs",
22
+ {page: page, per_page: 100, status: "success", exclude_pull_requests: true, created: since_param}.compact
23
+ )
24
+
25
+ total_count = response["total_count"]
26
+ progressbar.update(total: total_count)
27
+ pages = total_count / 100
28
+ page += 1
29
+
30
+ response.fetch("workflow_runs").each do |workflow_run|
31
+ run_timing = get("/runs/#{workflow_run["id"]}/timing")
32
+
33
+ timings << [
34
+ workflow_run["id"],
35
+ workflow_run["created_at"],
36
+ run_timing["run_duration_ms"]
37
+ ]
38
+
39
+ progressbar.advance
40
+
41
+ sleep 0.1 # Try to keep under the Github rate limit
42
+ end
43
+
44
+ sleep 1 # Try to keep under the Github rate limit
45
+ end
46
+
47
+ timings
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :repo_base_url, :authorization_header
53
+
54
+ def get(resource, params = {})
55
+ HTTP
56
+ .auth(authorization_header)
57
+ .get("#{repo_base_url}/actions#{resource}", params: params)
58
+ .parse
59
+ end
60
+ end
61
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ghamma
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ghamma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Rowan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-01 00:00:00.000000000 Z
11
+ date: 2023-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-cli
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: http
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -24,6 +38,20 @@ dependencies:
24
38
  - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: tty-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rake
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -85,6 +113,9 @@ files:
85
113
  - exe/ghamma
86
114
  - lib/ghamma.rb
87
115
  - lib/ghamma/cli.rb
116
+ - lib/ghamma/cli/duration.rb
117
+ - lib/ghamma/cli/version.rb
118
+ - lib/ghamma/github_api_client.rb
88
119
  - lib/ghamma/version.rb
89
120
  - sig/ghamma.rbs
90
121
  homepage: https://github.com/tony-rowan/ghamma