still_active 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a57769b549262b76ee15993dd903d607864d5a1cb4cefec348c4a0d2f2892b44
4
+ data.tar.gz: 11212c25c8c4897b8883401e9a6d21f5c76f3275c79d76f48c36dc47c09c2037
5
+ SHA512:
6
+ metadata.gz: cec9d044f6e894659ae8bb30883fc98e6829d31a87612b0fc8660e2e56165deaeae606e1927fa7e60c6fc51ccd068998444543f9d89f50b2f910f4cc4410dc93
7
+ data.tar.gz: 5d082234f0e9491b8498b93dd3134546342297869218fe8e9b6d1ca913226aca5e00ad4b6299015f8f11747f59956153804de4b19730107b73a0b08d51e1dc8a
@@ -0,0 +1,16 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.0.2
14
+ bundler-cache: true
15
+ - name: Run the default task
16
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0.2
3
+
4
+ inherit_gem:
5
+ rubocop-shopify: rubocop.yml
6
+
7
+ Layout/LineLength:
8
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-11-06
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in still_active.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,124 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ still_active (0.1.0)
5
+ async
6
+ gems
7
+ github_api
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.8.0)
13
+ public_suffix (>= 2.0.2, < 5.0)
14
+ ast (2.4.2)
15
+ async (1.30.1)
16
+ console (~> 1.10)
17
+ nio4r (~> 2.3)
18
+ timers (~> 4.1)
19
+ console (1.13.1)
20
+ fiber-local
21
+ debug (1.3.4)
22
+ irb (>= 1.3.6)
23
+ reline (>= 0.2.7)
24
+ descendants_tracker (0.0.4)
25
+ thread_safe (~> 0.3, >= 0.3.1)
26
+ diff-lcs (1.4.4)
27
+ faraday (1.8.0)
28
+ faraday-em_http (~> 1.0)
29
+ faraday-em_synchrony (~> 1.0)
30
+ faraday-excon (~> 1.1)
31
+ faraday-httpclient (~> 1.0.1)
32
+ faraday-net_http (~> 1.0)
33
+ faraday-net_http_persistent (~> 1.1)
34
+ faraday-patron (~> 1.0)
35
+ faraday-rack (~> 1.0)
36
+ multipart-post (>= 1.2, < 3)
37
+ ruby2_keywords (>= 0.0.4)
38
+ faraday-em_http (1.0.0)
39
+ faraday-em_synchrony (1.0.0)
40
+ faraday-excon (1.1.0)
41
+ faraday-httpclient (1.0.1)
42
+ faraday-net_http (1.0.1)
43
+ faraday-net_http_persistent (1.2.0)
44
+ faraday-patron (1.0.0)
45
+ faraday-rack (1.0.0)
46
+ fiber-local (1.0.0)
47
+ gems (1.2.0)
48
+ github_api (0.19.0)
49
+ addressable (~> 2.4)
50
+ descendants_tracker (~> 0.0.4)
51
+ faraday (>= 0.8, < 2)
52
+ hashie (~> 3.5, >= 3.5.2)
53
+ oauth2 (~> 1.0)
54
+ hashie (3.6.0)
55
+ io-console (0.5.9)
56
+ irb (1.3.7)
57
+ reline (>= 0.2.7)
58
+ jwt (2.3.0)
59
+ multi_json (1.15.0)
60
+ multi_xml (0.6.0)
61
+ multipart-post (2.1.1)
62
+ nio4r (2.5.8)
63
+ oauth2 (1.4.7)
64
+ faraday (>= 0.8, < 2.0)
65
+ jwt (>= 1.0, < 3.0)
66
+ multi_json (~> 1.3)
67
+ multi_xml (~> 0.5)
68
+ rack (>= 1.2, < 3)
69
+ parallel (1.21.0)
70
+ parser (3.0.2.0)
71
+ ast (~> 2.4.1)
72
+ public_suffix (4.0.6)
73
+ rack (2.2.3)
74
+ rainbow (3.0.0)
75
+ rake (13.0.6)
76
+ regexp_parser (2.1.1)
77
+ reline (0.2.7)
78
+ io-console (~> 0.5)
79
+ rexml (3.2.5)
80
+ rspec (3.10.0)
81
+ rspec-core (~> 3.10.0)
82
+ rspec-expectations (~> 3.10.0)
83
+ rspec-mocks (~> 3.10.0)
84
+ rspec-core (3.10.1)
85
+ rspec-support (~> 3.10.0)
86
+ rspec-expectations (3.10.1)
87
+ diff-lcs (>= 1.2.0, < 2.0)
88
+ rspec-support (~> 3.10.0)
89
+ rspec-mocks (3.10.2)
90
+ diff-lcs (>= 1.2.0, < 2.0)
91
+ rspec-support (~> 3.10.0)
92
+ rspec-support (3.10.3)
93
+ rubocop (1.22.3)
94
+ parallel (~> 1.10)
95
+ parser (>= 3.0.0.0)
96
+ rainbow (>= 2.2.2, < 4.0)
97
+ regexp_parser (>= 1.8, < 3.0)
98
+ rexml
99
+ rubocop-ast (>= 1.12.0, < 2.0)
100
+ ruby-progressbar (~> 1.7)
101
+ unicode-display_width (>= 1.4.0, < 3.0)
102
+ rubocop-ast (1.12.0)
103
+ parser (>= 3.0.1.1)
104
+ rubocop-shopify (2.3.0)
105
+ rubocop (~> 1.22)
106
+ ruby-progressbar (1.11.0)
107
+ ruby2_keywords (0.0.5)
108
+ thread_safe (0.3.6)
109
+ timers (4.3.3)
110
+ unicode-display_width (2.1.0)
111
+
112
+ PLATFORMS
113
+ x86_64-darwin-21
114
+
115
+ DEPENDENCIES
116
+ debug
117
+ rake (~> 13.0)
118
+ rspec (~> 3.0)
119
+ rubocop
120
+ rubocop-shopify
121
+ still_active!
122
+
123
+ BUNDLED WITH
124
+ 2.2.22
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Sean Floyd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # StillActive
2
+
3
+ Identify which of your dependencies are no longer under active development.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ gem install still_active
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ The most important flag is the GitHub OAuth token, without which you will most certainly get rate limited.
14
+
15
+ ```text
16
+ Usage: still_active [options]
17
+
18
+ all flags are optional
19
+
20
+ --gemfile=GEMFILE path to gemfile
21
+ --gems=GEM,GEM2,... Gem(s)
22
+ --markdown Prints output in markdown format
23
+ --json Prints output in JSON format
24
+ --github-oauth-token=TOKEN GitHub OAuth token to make API calls
25
+ --safe-range-end=YEARS maximum number of years since last activity to be considered safe
26
+ --warning-range-start=YEARS minimum number of years since last activity to be considered worrying
27
+ --warning-range-end=YEARS maximum number of years since last activity to be considered worrying
28
+ --critical-warning-emoji=EMOJI
29
+ --futurist-emoji=EMOJI
30
+ --success-emoji=EMOJI
31
+ --unsure-emoji=EMOJI
32
+ --warning-emoji=EMOJI
33
+ -h, --help Show this message
34
+ -v, --version Show version
35
+ ```
36
+
37
+ ### Examples
38
+
39
+ ```bash
40
+ still_active --json --gems=rails,nokogiri
41
+ ```
42
+
43
+ Will output:
44
+
45
+ ```json
46
+ {"rails":{"latest_version":"6.1.4.1","latest_version_release_date":"2021-08-19 16:27:05 UTC","latest_pre_release_version":"7.0.0.alpha2","latest_pre_release_version_release_date":"2021-09-15 23:16:26 UTC","repository_url":"https://github.com/rails/rails","last_commit_date":"2021-11-06 09:16:40 UTC","ruby_gems_url":"https://rubygems.org/gems/rails"},"nokogiri":{"latest_version":"1.12.5","latest_version_release_date":"2021-09-27 19:03:57 UTC","latest_pre_release_version":"1.12.0.rc1","latest_pre_release_version_release_date":"2021-07-09 20:00:11 UTC","repository_url":"https://github.com/sparklemotion/nokogiri","last_commit_date":"2021-11-06 16:44:55 UTC","ruby_gems_url":"https://rubygems.org/gems/nokogiri"}}
47
+ ```
48
+
49
+ ```bash
50
+ # for this gem
51
+ stll_active --github-oauth-token=my-token-here
52
+ ```
53
+
54
+ Outputs:
55
+
56
+ | gem activity old? | up to date? | name | version used | release date | latest version | release date | latest pre-release version | release date | last commit date |
57
+ | ----------------- | ----------- | -------------------------------------------------------------- | ----------------------------------------------------------------- | ------------ | ----------------------------------------------------------------- | ------------ | -------------------------------------------------------------------- | ------------ | ------------------------------------------------------ |
58
+ | | ✅ | [debug](https://github.com/ruby/debug) | [1.3.4](https://rubygems.org/gems/debug/versions/1.3.4) | 2021/10 | [1.3.4](https://rubygems.org/gems/debug/versions/1.3.4) | 2021/10 | [1.0.0.rc2](https://rubygems.org/gems/debug/versions/1.0.0.rc2) | 2021/09 | [2021/11](https://github.com/ruby/debug) |
59
+ | | ✅ | [rake](https://github.com/ruby/rake) | [13.0.6](https://rubygems.org/gems/rake/versions/13.0.6) | 2021/07 | [13.0.6](https://rubygems.org/gems/rake/versions/13.0.6) | 2021/07 | [13.0.0.pre.1](https://rubygems.org/gems/rake/versions/13.0.0.pre.1) | 2019/09 | [2021/07](https://github.com/ruby/rake) |
60
+ | ⚠️ | ✅ | [rspec](https://github.com/rspec/rspec) | [3.10.0](https://rubygems.org/gems/rspec/versions/3.10.0) | 2020/10 | [3.10.0](https://rubygems.org/gems/rspec/versions/3.10.0) | 2020/10 | [3.6.0.beta2](https://rubygems.org/gems/rspec/versions/3.6.0.beta2) | 2016/12 | [2021/10](https://github.com/rspec/rspec) |
61
+ | | ✅ | [rubocop](https://github.com/rubocop/rubocop) | [1.22.3](https://rubygems.org/gems/rubocop/versions/1.22.3) | 2021/10 | [1.22.3](https://rubygems.org/gems/rubocop/versions/1.22.3) | 2021/10 | ❓ | ❓ | [2021/11](https://github.com/rubocop/rubocop) |
62
+ | | ✅ | [rubocop-shopify](https://github.com/Shopify/ruby-style-guide) | [2.3.0](https://rubygems.org/gems/rubocop-shopify/versions/2.3.0) | 2021/10 | [2.3.0](https://rubygems.org/gems/rubocop-shopify/versions/2.3.0) | 2021/10 | ❓ | ❓ | [2021/11](https://github.com/Shopify/ruby-style-guide) |
63
+ | ❓ | ❓ | still_active | 0.1.0 | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ |
64
+
65
+ ### Configuration options
66
+
67
+ - `gemfile_path` uses bundler to detect the Gemfile in your working directory
68
+ - `output_format` is markdown, can be configured to be JSON
69
+ - `critical_warning_emoji` 🚩
70
+ - `futurist_emoji` 🔮
71
+ - `success_emoji` ✅
72
+ - `unsure_emoji` ❓
73
+ - `warning_emoji` ⚠️
74
+ - `safe_range_end` 1 (safe range is by default [0..1])
75
+ - `warning_range_start` 2 (warning range is by default [2..3])
76
+ - `warning_range_end` 3
77
+
78
+ ## Development
79
+
80
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
81
+
82
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
83
+
84
+ ## Contributing
85
+
86
+ Bug reports and pull requests are welcome.
87
+
88
+ ## License
89
+
90
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: [:spec, :rubocop]
data/bin/console ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "still_active"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "debug"
11
+
12
+ require "irb"
13
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/still_active ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "../lib"))
5
+
6
+ require "still_active/cli"
7
+ require "still_active"
8
+
9
+ StillActive::CLI.new.run(ARGV)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillActive
4
+ module BundlerHelper
5
+ extend self
6
+
7
+ def gemfile_dependencies(gemfile_path: StillActive.config.gemfile_path)
8
+ Bundler::SharedHelpers.set_env("BUNDLE_GEMFILE", File.expand_path(gemfile_path))
9
+ gemfile_gems = Bundler.definition.dependencies.map(&:name)
10
+ Bundler
11
+ .definition
12
+ .locked_gems
13
+ .specs
14
+ .select { |spec| gemfile_gems.include?(spec.name) }
15
+ .each_with_object([]) { |spec, array| array << { name: spec.name, version: spec.version.version } }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillActive
4
+ module EmojiHelper
5
+ extend self
6
+ def inactive_gem_emoji(result_hash)
7
+ most_recent_activity = [result_hash.dig(:version_used_release_date), result_hash.dig(:latest_version_release_date),
8
+ result_hash.dig(:latest_pre_release_version_release_date),].compact.sort.last
9
+ return "❓" if most_recent_activity.nil?
10
+
11
+ case ((Time.now - most_recent_activity) / ONE_YEAR_IN_SECONDS).ceil
12
+ when 0..1
13
+ ""
14
+ when 2..3
15
+ "\u26A0\uFE0F"
16
+ else
17
+ "\u{1F6A9}"
18
+ end
19
+ end
20
+
21
+ def using_latest_emoji(using_last_release:, using_last_pre_release:)
22
+ if using_last_release.nil? && using_last_pre_release.nil?
23
+ "❓"
24
+ elsif using_last_release
25
+ "✅"
26
+ elsif using_last_pre_release
27
+ "🔮"
28
+ else
29
+ "⚠️"
30
+ end
31
+ end
32
+
33
+ ONE_YEAR_IN_SECONDS = 3.154e+7
34
+ end
35
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillActive
4
+ module MarkdownHelper
5
+ extend self
6
+ def markdown_table_header_line
7
+ "| gem activity old? | up to date? | name | version used | release date | latest version | release date | latest pre-release version | release date | last commit date |\n" \
8
+ "| ----------------- | ----------- | ---- | ------------ | ------------ | -------------- | ------------ | --------------------------- | ------------ | ---------------- |"
9
+ end
10
+
11
+ def markdown_table_body_line(gem_name:, data:)
12
+ repository_url = data[:repository_url]
13
+ ruby_gems_url = data[:ruby_gems_url]
14
+
15
+ version_used = data.dig(:version_used)
16
+ version_used_url =
17
+ if version_used && ruby_gems_url
18
+ "#{ruby_gems_url}/versions/#{version_used}"
19
+ end
20
+ version_used_release_date = data.dig(:version_used_release_date)
21
+
22
+ latest_version = data[:latest_version]
23
+ latest_version_url =
24
+ if latest_version && ruby_gems_url
25
+ "#{ruby_gems_url}/versions/#{latest_version}"
26
+ end
27
+ latest_version_release_date = data.dig(:latest_version_release_date)
28
+
29
+ latest_version_prerelease = data.dig(:latest_pre_release_version)
30
+ latest_version_prerelease_url =
31
+ if latest_version_prerelease && ruby_gems_url
32
+ "#{ruby_gems_url}/versions/#{latest_version_prerelease}"
33
+ end
34
+ latest_version_prerelease_date = data.dig(:latest_pre_release_version_release_date)
35
+
36
+ last_commit_date = data.dig(:last_commit_date)
37
+ last_commit_url = repository_url
38
+
39
+ inactive_repository_emoji = data.dig(:up_to_date_emoji)
40
+ using_latest_version_emoji = data.dig(:last_activity_worrying_emoji)
41
+
42
+ formatted_name = markdown_url(text: gem_name, url: repository_url)
43
+
44
+ formatted_version_used = markdown_url(text: version_used, url: version_used_url)
45
+ formatted_version_used_date = year_month(version_used_release_date)
46
+
47
+ formatted_latest_release_version = markdown_url(text: latest_version, url: latest_version_url)
48
+ formatted_latest_release_date = year_month(latest_version_release_date)
49
+
50
+ formatted_latest_pre_release_version = markdown_url(text: latest_version_prerelease,
51
+ url: latest_version_prerelease_url)
52
+ formatted_latest_pre_release_date = year_month(latest_version_prerelease_date)
53
+
54
+ formatted_last_commit_date = markdown_url(text: year_month(last_commit_date), url: last_commit_url)
55
+
56
+ formatted_markdown_table_line =
57
+ [
58
+ inactive_repository_emoji || StillActive.config.unsure_emoji,
59
+ using_latest_version_emoji || StillActive.config.unsure_emoji,
60
+ formatted_name,
61
+ formatted_version_used,
62
+ formatted_version_used_date || StillActive.config.unsure_emoji,
63
+ formatted_latest_release_version || StillActive.config.unsure_emoji,
64
+ formatted_latest_release_date || StillActive.config.unsure_emoji,
65
+ formatted_latest_pre_release_version || StillActive.config.unsure_emoji,
66
+ formatted_latest_pre_release_date || StillActive.config.unsure_emoji,
67
+ formatted_last_commit_date || StillActive.config.unsure_emoji,
68
+ ]
69
+ .join(" | ")
70
+
71
+ "| #{formatted_markdown_table_line} |"
72
+ end
73
+
74
+ private
75
+
76
+ def markdown_url(text:, url:)
77
+ return text if url.nil?
78
+
79
+ "[#{text}](#{url})"
80
+ end
81
+
82
+ def year_month(time_object)
83
+ return if time_object.nil?
84
+
85
+ time_object.strftime("%Y/%m")
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillActive
4
+ module VersionHelper
5
+ def find_version(versions:, version_string: nil, pre_release: false)
6
+ if !version_string.nil?
7
+ versions.find { |v| v["number"] == version_string }
8
+ else
9
+ versions.find { |v| v["prerelease"] == pre_release }
10
+ end
11
+ end
12
+
13
+ def up_to_date?(version_used:, latest_version: nil, latest_pre_release_version: nil)
14
+ return nil if latest_version.nil? && latest_pre_release_version.nil?
15
+
16
+ version_used = if version_used.is_a?(String)
17
+ version_used
18
+ else
19
+ gem_version(version_hash: version_used)
20
+ end
21
+
22
+ latest_version = if latest_version.is_a?(String)
23
+ latest_version
24
+ else
25
+ gem_version(version_hash: latest_version)
26
+ end
27
+ latest_pre_release_version = if latest_pre_release_version.is_a?(String)
28
+ latest_pre_release_version
29
+ else
30
+ gem_version(version_hash: latest_pre_release_version)
31
+ end
32
+
33
+ [latest_version, latest_pre_release_version].include?(version_used)
34
+ end
35
+
36
+ def gem_version(version_hash:)
37
+ version_hash&.dig("number")
38
+ end
39
+
40
+ def release_date(version_hash:)
41
+ release_date = version_hash&.dig("created_at")
42
+
43
+ Time.parse(release_date) unless release_date.nil?
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "options"
4
+ require_relative "../helpers/bundler_helper"
5
+ require_relative "../helpers/emoji_helper"
6
+ require_relative "../helpers/markdown_helper"
7
+ require_relative "../helpers/version_helper"
8
+ require_relative "workflow"
9
+ require "debug"
10
+ # require "cli/ui"
11
+
12
+ module StillActive
13
+ class CLI
14
+ include VersionHelper
15
+ def run(args)
16
+ options = Options.new.parse!(args)
17
+ if options[:provided_gems]
18
+ StillActive.config.gems.map! { |gem| { name: gem } }
19
+ else
20
+ StillActive.config.gems = BundlerHelper.gemfile_dependencies
21
+ end
22
+
23
+ result = Workflow.call
24
+ case StillActive.config.output_format
25
+ when :json
26
+ puts result.to_json
27
+ when :markdown
28
+ puts MarkdownHelper.markdown_table_header_line
29
+ result.keys.sort.each do |name|
30
+ result[name].merge!({
31
+ up_to_date_emoji: EmojiHelper.inactive_gem_emoji(result[name]),
32
+ last_activity_worrying_emoji: EmojiHelper.using_latest_emoji(
33
+ using_last_release: up_to_date?(
34
+ version_used: result[name].dig(:version_used), latest_version: result[name].dig(:latest_version)
35
+ ),
36
+ using_last_pre_release: up_to_date?(
37
+ version_used: result[name].dig(:version_used), latest_version: result[name].dig(:latest_pre_release_version)
38
+ ),
39
+ ),
40
+ })
41
+
42
+ puts MarkdownHelper.markdown_table_body_line(gem_name: name, data: result[name])
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler"
4
+
5
+ module StillActive
6
+ class Config
7
+ attr_accessor :critical_warning_emoji, :futurist_emoji, :gemfile_path, :gems, :github_oauth_token, :output_format,
8
+ :safe_range_end, :safe_range_start, :success_emoji, :unsure_emoji, :warning_emoji, :warning_range_end,
9
+ :warning_range_start
10
+
11
+ def initialize
12
+ @gemfile_path = Bundler.default_gemfile.to_s
13
+ @gems = []
14
+
15
+ @output_format = :markdown
16
+
17
+ @critical_warning_emoji = "🚩"
18
+ @futurist_emoji = "🔮"
19
+ @success_emoji = "✅"
20
+ @unsure_emoji = "❓"
21
+ @warning_emoji = "⚠️"
22
+
23
+ @safe_range_start = 0
24
+ @safe_range_end = 1
25
+
26
+ @warning_range_start = 2
27
+ @warning_range_end = 3
28
+ end
29
+
30
+ def github_client
31
+ @github_client ||=
32
+ Github.new(oauth_token: github_oauth_token)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler"
4
+
5
+ module StillActive
6
+ module Gemfile
7
+ extend self
8
+
9
+ def dependencies(gemfile_path:)
10
+ Bundler::SharedHelpers.set_env("BUNDLE_GEMFILE", File.expand_path(gemfile_path))
11
+ Bundler.definition.dependencies.map(&:name)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module StillActive
6
+ class Options
7
+ attr_accessor :options, :options_parser
8
+
9
+ def initialize
10
+ @options = {}
11
+ @options_parser = OptionParser.new do |opts|
12
+ add_banner(opts)
13
+ add_tail_options(opts)
14
+ add_gemfile_option(opts)
15
+ add_gems_option(opts)
16
+ add_output_options(opts)
17
+ add_token_options(opts)
18
+ add_range_options(opts)
19
+ add_emoji_options(opts)
20
+ end
21
+ end
22
+
23
+ def parse!(args)
24
+ options_parser.parse!(args)
25
+ validate_options
26
+ options
27
+ end
28
+
29
+ private
30
+
31
+ def validate_options
32
+ raise ArgumentError, "provide gemfile or gems, not both" if options[:provided_gemfile] && options[:provided_gems]
33
+ end
34
+
35
+ def add_gemfile_option(opts)
36
+ opts.on("--gemfile=GEMFILE", String, "path to gemfile") do |value|
37
+ options[:provided_gemfile] = true
38
+ StillActive.config { |config| config.gemfile_path = value }
39
+ end
40
+ end
41
+
42
+ def add_gems_option(opts)
43
+ opts.on("--gems=GEM,GEM2,...", Array, "Gem(s)") do |value|
44
+ options[:provided_gems] = true
45
+ StillActive.config { |config| config.gems = value }
46
+ end
47
+ end
48
+
49
+ def add_output_options(opts)
50
+ opts.on("--markdown", "Prints output in markdown format") { StillActive.config { |config| config.output_format = :markdown } }
51
+ opts.on("--json", "Prints output in JSON format") { StillActive.config { |config| config.output_format = :json } }
52
+ end
53
+
54
+ def add_token_options(opts)
55
+ opts.on("--github-oauth-token=TOKEN", String, "GitHub OAuth token to make API calls") do |value|
56
+ StillActive.config { |config| config.github_oauth_token = value }
57
+ end
58
+ end
59
+
60
+ def add_range_options(opts)
61
+ opts.on("--safe-range-end=YEARS", Integer,
62
+ "maximum number of years since last activity to be considered safe") do |value|
63
+ StillActive.config { |config| config.safe_range_end = value }
64
+ end
65
+ opts.on("--warning-range-start=YEARS", Integer,
66
+ "minimum number of years since last activity to be considered worrying") do |value|
67
+ StillActive.config { |config| config.warning_range_start = value }
68
+ end
69
+ opts.on("--warning-range-end=YEARS", Integer,
70
+ "maximum number of years since last activity to be considered worrying") do |value|
71
+ StillActive.config { |config| config.warning_range_end = value }
72
+ end
73
+ end
74
+
75
+ def add_emoji_options(opts)
76
+ opts.on("--critical-warning-emoji=EMOJI") { |value| StillActive.config { |config| config.critical_warning_emoji = value } }
77
+ opts.on("--futurist-emoji=EMOJI") { |value| StillActive.config { |config| config.futurist_emoji = value } }
78
+ opts.on("--success-emoji=EMOJI") { |value| StillActive.config { |config| config.success_emoji = value } }
79
+ opts.on("--unsure-emoji=EMOJI") { |value| StillActive.config { |config| config.unsure_emoji = value } }
80
+ opts.on("--warning-emoji=EMOJI") { |value| StillActive.config { |config| config.warning_emoji = value } }
81
+ end
82
+
83
+ def add_banner(opts)
84
+ opts.banner = <<-BANNER.gsub(/\A\s{8}/, "")
85
+ Usage: #{opts.program_name} [options]
86
+
87
+ all flags are optional
88
+
89
+ BANNER
90
+ end
91
+
92
+ def add_tail_options(opts)
93
+ opts.on_tail("-h", "--help", "Show this message") do
94
+ puts opts
95
+ exit
96
+ end
97
+ opts.on_tail("-v", "--version", "Show version") do
98
+ puts StillActive::VERSION
99
+ exit
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillActive
4
+ module Repository
5
+ GITHUB_REGEX = %r{(http(?:s)?://(?:www\.)?(github)\.com/((?:\w|_|-)+)/((?:\w|_|-)+))}i
6
+ GITLAB_REGEX = %r{(http(?:s)?://(?:www\.)?(gitlab)\.com/((?:\w|_|-)+)/((?:\w|_|-)+))}i
7
+
8
+ extend self
9
+
10
+ def valid?(url:)
11
+ [GITHUB_REGEX, GITLAB_REGEX].any? do |regex|
12
+ !url.scan(regex)&.first.nil?
13
+ end
14
+ end
15
+
16
+ def url_with_owner_and_name(url:)
17
+ [GITHUB_REGEX, GITLAB_REGEX].each do |regex|
18
+ values = url&.scan(regex)&.first
19
+ next if values.nil? || values.empty?
20
+
21
+ return [:url, :source, :owner, :name].zip(values).to_h
22
+ end
23
+ { source: :unhandled, owner: nil, name: nil }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StillActive
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "repository"
4
+ require_relative "../helpers/version_helper"
5
+ require "async"
6
+ require "async/barrier"
7
+ require "gems"
8
+ require "github_api"
9
+
10
+ module StillActive
11
+ module Workflow
12
+ extend self
13
+ include VersionHelper
14
+
15
+ def call
16
+ task = Async do
17
+ barrier = Async::Barrier.new
18
+ result_object = {}
19
+ StillActive.config.gems.each_with_object(result_object) do |gem, hash|
20
+ barrier.async do
21
+ gem_info(gem_name: gem[:name], result_object: hash, gem_version: gem.dig(:version))
22
+ end
23
+ end
24
+ barrier.wait
25
+ result_object
26
+ end
27
+ task.wait
28
+ end
29
+
30
+ private
31
+
32
+ def gem_info(gem_name:, result_object:, gem_version: nil)
33
+ result_object[gem_name] = {}
34
+ result_object[gem_name].merge!({
35
+ version_used: gem_version,
36
+ }) if gem_version
37
+
38
+ vs = versions(gem_name: gem_name)
39
+ repo_info = repository_info(gem_name: gem_name, versions: vs)
40
+ last_commit_date = last_commit_date(source: repo_info[:source], repository_owner: repo_info[:owner],
41
+ repository_name: repo_info[:name])
42
+ last_release = find_version(versions: vs, pre_release: false)
43
+ last_pre_release = find_version(versions: vs, pre_release: true)
44
+ result_object[gem_name].merge!({
45
+ latest_version: gem_version(version_hash: last_release),
46
+ latest_version_release_date: release_date(version_hash: last_release),
47
+
48
+ latest_pre_release_version: gem_version(version_hash: last_pre_release),
49
+ latest_pre_release_version_release_date: release_date(version_hash: last_pre_release),
50
+
51
+ repository_url: repo_info[:url],
52
+ last_commit_date: last_commit_date,
53
+ })
54
+
55
+ unless vs.empty?
56
+ result_object[gem_name].merge!({ ruby_gems_url: "https://rubygems.org/gems/#{gem_name}" })
57
+ end
58
+
59
+ if gem_version
60
+ version_used = find_version(versions: vs, version_string: gem_version)
61
+ result_object[gem_name].merge!({
62
+ # global_warning: global_warning,
63
+ up_to_date:
64
+ up_to_date?(
65
+ version_used: version_used,
66
+ latest_version: last_release,
67
+ latest_pre_release_version: last_pre_release
68
+ ),
69
+
70
+ version_used_release_date: release_date(version_hash: version_used),
71
+ })
72
+ end
73
+ rescue StandardError => e
74
+ puts "error occured for #{gem_name}: #{e.class}\n\t#{e.message}"
75
+ end
76
+
77
+ # makes network request
78
+ def versions(gem_name:)
79
+ Gems.versions(gem_name)
80
+ rescue Gems::NotFound
81
+ []
82
+ end
83
+
84
+ def repository_info(gem_name:, versions:)
85
+ # binding.break # if gem_name == "still_active"
86
+ valid_repository_url =
87
+ installed_gem_urls(gem_name: gem_name).find { |url| Repository.valid?(url: url) } ||
88
+ rubygems_versions_repository_url(versions: versions).find { |url| Repository.valid?(url: url) } ||
89
+ rubygems_gem_repository_url(gem_name: gem_name).find { |url| Repository.valid?(url: url) }
90
+ Repository.url_with_owner_and_name(url: valid_repository_url)
91
+ end
92
+
93
+ # does not make network requests
94
+ def installed_gem_urls(gem_name:)
95
+ info = Gem::Dependency.new(gem_name).matching_specs.first
96
+ return [] if info.nil?
97
+
98
+ [
99
+ info&.metadata&.dig("source_code_uri"),
100
+ info&.homepage,
101
+ ].compact.uniq
102
+ end
103
+
104
+ # does not make network requests
105
+ def rubygems_versions_repository_url(versions:)
106
+ versions
107
+ .map { |version| version.dig("metadata", "source_code_uri") }
108
+ .compact
109
+ .uniq
110
+ end
111
+
112
+ # makes network request
113
+ def rubygems_gem_repository_url(gem_name:)
114
+ info = Gems.info(gem_name)
115
+ return [] if info.nil?
116
+
117
+ [
118
+ info["homepage_uri"],
119
+ info["source_code_uri"],
120
+ ].compact.uniq
121
+ rescue Gems::NotFound
122
+ []
123
+ end
124
+
125
+ # makes network request
126
+ def last_commit(source:, repository_owner:, repository_name:)
127
+ case source.to_sym
128
+ when :github
129
+ StillActive.config.github_client.repos.commits.all(repository_owner, repository_name, per_page: 1)&.first
130
+ # when :gitlab
131
+ # Gitlab.commits(name, per_page: 1)
132
+ end
133
+ end
134
+
135
+ # makes network request
136
+ def last_commit_date(source:, repository_owner:, repository_name:)
137
+ commit = last_commit(source: source, repository_owner: repository_owner, repository_name: repository_name)
138
+ case source.to_sym
139
+ when :github
140
+ commit&.dig("commit", "author", "date").then { |date| Time.parse(date) unless date.nil? }
141
+ # when :gitlab
142
+ # commit
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "still_active/version"
4
+ require_relative "still_active/config"
5
+ require_relative "still_active/cli"
6
+
7
+ module StillActive
8
+ class Error < StandardError; end
9
+
10
+ class << self
11
+ def config
12
+ @config ||= Config.new
13
+ if block_given?
14
+ yield @config
15
+ else
16
+ @config
17
+ end
18
+ end
19
+
20
+ def reset
21
+ @config = Config.new
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/still_active/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "still_active"
7
+ spec.version = StillActive::VERSION
8
+ spec.authors = ["Sean Floyd"]
9
+ spec.email = ["contact@seanfloyd.dev"]
10
+
11
+ spec.summary = "Check if gems are under active development."
12
+ spec.description = "Obtain last release, pre-release, and last commit date to determine if a gem is still under active development."
13
+ spec.homepage = "https://github.com/SeanLF/still_active."
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.4.0"
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'https://mygemserver.com'"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = spec.homepage
21
+ spec.metadata["changelog_uri"] = "#{spec.metadata["source_code_uri"]}/blob/main/CHANGELOG.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "bin"
29
+ spec.executables = spec.files.grep(%r{\Abin/still_active}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # For more information and examples about making a new gem, checkout our
33
+ # guide at: https://bundler.io/guides/creating_gem.html
34
+ spec.add_development_dependency("debug")
35
+ spec.add_development_dependency("rubocop")
36
+ spec.add_development_dependency("rubocop-shopify")
37
+
38
+ spec.add_runtime_dependency("async")
39
+ # spec.add_runtime_dependency("cli-ui")
40
+ # spec.add_runtime_dependency("async-http")
41
+ spec.add_runtime_dependency("gems")
42
+ spec.add_runtime_dependency("github_api")
43
+ # spec.add_runtime_dependency("gitlab")
44
+ # spec.add_runtime_dependency("tty-progressbar")
45
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: still_active
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Floyd
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-11-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: debug
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop-shopify
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: async
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gems
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: github_api
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Obtain last release, pre-release, and last commit date to determine if
98
+ a gem is still under active development.
99
+ email:
100
+ - contact@seanfloyd.dev
101
+ executables:
102
+ - still_active
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".github/workflows/main.yml"
107
+ - ".gitignore"
108
+ - ".rspec"
109
+ - ".rubocop.yml"
110
+ - CHANGELOG.md
111
+ - Gemfile
112
+ - Gemfile.lock
113
+ - LICENSE.txt
114
+ - README.md
115
+ - Rakefile
116
+ - bin/console
117
+ - bin/setup
118
+ - bin/still_active
119
+ - lib/helpers/bundler_helper.rb
120
+ - lib/helpers/emoji_helper.rb
121
+ - lib/helpers/markdown_helper.rb
122
+ - lib/helpers/version_helper.rb
123
+ - lib/still_active.rb
124
+ - lib/still_active/cli.rb
125
+ - lib/still_active/config.rb
126
+ - lib/still_active/gemfile.rb
127
+ - lib/still_active/options.rb
128
+ - lib/still_active/repository.rb
129
+ - lib/still_active/version.rb
130
+ - lib/still_active/workflow.rb
131
+ - still_active.gemspec
132
+ homepage: https://github.com/SeanLF/still_active.
133
+ licenses:
134
+ - MIT
135
+ metadata:
136
+ homepage_uri: https://github.com/SeanLF/still_active.
137
+ source_code_uri: https://github.com/SeanLF/still_active.
138
+ changelog_uri: https://github.com/SeanLF/still_active./blob/main/CHANGELOG.md
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 2.4.0
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubygems_version: 3.2.30
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: Check if gems are under active development.
158
+ test_files: []