gemfilelint 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: '08f43710d45876141d5a462ef860d5d52f3b782af673be0f8cf5bde39ea269bc'
4
+ data.tar.gz: a81951955e4d2a60b4771ebc45f7adbd9a6150f14de2f34f162497ef7fbb2d00
5
+ SHA512:
6
+ metadata.gz: fb7f7ebdd672f505a5a1dbb91cc36dfc2ddafabfa470101931b05253f51a3e33df82175a5f0c06789d93455aefdce3cff1cbc839178b0a27f2c9c83354442237
7
+ data.tar.gz: 6aac9050ccf83d666dc9f640e0f0bc95fbd56ecec061d90f7da8ef2b8ae958406ec41b4e7bcc3de3df787046e68c596750225895fd86f96dc7ae181ca37c6578
@@ -0,0 +1,23 @@
1
+ name: Main
2
+ on: push
3
+ jobs:
4
+ ci:
5
+ name: CI
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@master
9
+ - uses: ruby/setup-ruby@v1
10
+ with:
11
+ ruby-version: 2.7
12
+ - uses: actions/cache@v1
13
+ with:
14
+ path: vendor/bundle
15
+ key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
16
+ restore-keys: |
17
+ ${{ runner.os }}-gems-
18
+ - run: bundle config path vendor/bundle
19
+ - run: bundle install --jobs 4 --retry 3
20
+ - run: bundle exec rubocop --parallel
21
+ - run: bundle exec rake test
22
+ env:
23
+ CI: true
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.mergify.yml ADDED
@@ -0,0 +1,10 @@
1
+ pull_request_rules:
2
+ - name: Automatically merge dependencies
3
+ conditions:
4
+ - base=master
5
+ - label=dependencies
6
+ - status-success=CI
7
+ actions:
8
+ merge:
9
+ strict: true
10
+ delete_head_branch: {}
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+ DisplayStyleGuide: true
4
+ TargetRubyVersion: 2.7
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+
8
+ Style/Documentation:
9
+ Enabled: false
10
+
11
+ Style/StructInheritance:
12
+ Enabled: false
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at kevin.deisz@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gemfilelint (0.1.0)
5
+ bundler
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.0)
11
+ jaro_winkler (1.5.4)
12
+ minitest (5.14.0)
13
+ parallel (1.19.1)
14
+ parser (2.7.0.2)
15
+ ast (~> 2.4.0)
16
+ rainbow (3.0.0)
17
+ rake (12.3.3)
18
+ rexml (3.2.4)
19
+ rubocop (0.80.0)
20
+ jaro_winkler (~> 1.5.1)
21
+ parallel (~> 1.10)
22
+ parser (>= 2.7.0.1)
23
+ rainbow (>= 2.2.2, < 4.0)
24
+ rexml
25
+ ruby-progressbar (~> 1.7)
26
+ unicode-display_width (>= 1.4.0, < 1.7)
27
+ ruby-progressbar (1.10.1)
28
+ unicode-display_width (1.6.1)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ gemfilelint!
35
+ minitest
36
+ rake
37
+ rubocop
38
+
39
+ BUNDLED WITH
40
+ 2.1.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020-present Kevin Deisz
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,41 @@
1
+ # Gemfile lint
2
+
3
+ Lint your Gemfile! This will find common spelling mistakes in gems and remote sources so that you don't accidentally download code from places that you don't mean to.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'gemfilelint'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install gemfilelint
20
+
21
+ ## Usage
22
+
23
+ Run the `gemfilelint` executable either in the root of your repository that contains a Gemfile or specify a path to one.
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kddeisz/gemfilelint. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/kddeisz/gemfilelint/blob/master/CODE_OF_CONDUCT.md).
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
38
+
39
+ ## Code of Conduct
40
+
41
+ Everyone interacting in the Gemfilelint project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/kddeisz/gemfilelint/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'gemfilelint'
6
+
7
+ require 'irb'
8
+ IRB.start(__FILE__)
data/bin/load-pg-dump ADDED
@@ -0,0 +1,98 @@
1
+ #!/bin/sh
2
+
3
+ ##############################################################################
4
+ # This script will download the most recently weekly dump listed on
5
+ # https://rubygems.org/pages/data and load it into a postgresql database.
6
+ #
7
+ # Assumptions:
8
+ #
9
+ # 1) a 'postgres' user exists your postgresql instance
10
+ # The dump script explicitly assigns ownership to the 'postgres' user, and
11
+ # so the 'postgres' user should exist
12
+ # 2) the user you pass with the -u option is a postgres super user
13
+ #
14
+ # Notes:
15
+ #
16
+ # * This script will drop and create the database, so buyer beware.
17
+ #
18
+ ##############################################################################
19
+
20
+ # variables
21
+ public_tar=
22
+ pg_database=rubygems
23
+ pg_user=postgres
24
+ download=false
25
+
26
+ base_url="https://s3-us-west-2.amazonaws.com/rubygems-dumps/"
27
+ prefix="production/public_postgresql"
28
+
29
+ ## Usage info
30
+ show_help() {
31
+ cat << EOF
32
+ Usage: ${0##*/} [-h] [-c] [-d DATABASE] [-u USER] FILE
33
+ Load a rubygems.org postgresql dump into a datatbase.
34
+ -h display this help and exit
35
+ -c download the latest file to FILE
36
+ -d DATABASE load the data into this database (default: rubygems)
37
+ -u USER connect to postgresql using this username (default: postgres)
38
+ Example: ./load-pg-dump -d rubygems_development ~/Downloads/public_postgresql.tar
39
+ EOF
40
+ }
41
+
42
+ OPTIND=1
43
+ while getopts "hcd:u:" opt; do
44
+ case "$opt" in
45
+ h)
46
+ show_help
47
+ exit 0
48
+ ;;
49
+ c) download=true
50
+ ;;
51
+ d) pg_database=$OPTARG
52
+ ;;
53
+ u) pg_user=$OPTARG
54
+ ;;
55
+ '?')
56
+ show_help >&2
57
+ exit 1
58
+ ;;
59
+ esac
60
+ done
61
+ shift "$((OPTIND-1))" # Shift off the options and optional
62
+
63
+ public_tar=$1
64
+ if [ -z "$public_tar" ]; then
65
+ show_help >&2
66
+ exit 1
67
+ fi
68
+
69
+ if $download; then
70
+ key=$(curl -s "${base_url}?prefix=${prefix}" | sed -ne 's/.*<Key>\(.*\)<\/Key>.*/\1/p')
71
+ latest_url="${base_url}${key}"
72
+ echo "Downloading ${latest_url} to ${public_tar}"
73
+ curl --progress-bar "${latest_url}" > ${public_tar}
74
+ fi
75
+
76
+ printf 'Loading "%s" into database "%s" as user "%s"\n', "$public_tar", "$pg_database", "$pg_user"
77
+
78
+ echo "Droppping database $pg_database"
79
+ dropdb -U $pg_user $pg_database
80
+
81
+ echo "Creating database $pg_database"
82
+ createdb -U $pg_user $pg_database
83
+
84
+ echo "Adding hstore extension"
85
+ psql -q -U $pg_user -d $pg_database -c "CREATE EXTENSION IF NOT EXISTS hstore;"
86
+
87
+ echo "Loading the data from $public_tar"
88
+ tar xOf "$public_tar" public_postgresql/databases/PostgreSQL.sql.gz | \
89
+ gunzip -c | \
90
+ psql --username $pg_user --dbname $pg_database
91
+
92
+ echo "Extracting the names of the top downloaded gems"
93
+ psql \
94
+ --username $pg_user \
95
+ -dbname $pg_database \
96
+ -c "COPY (SELECT name FROM rubygems INNER JOIN gem_downloads ON rubygems.id = gem_downloads.rubygem_id GROUP BY rubygems.id ORDER BY SUM(count) DESC NULLS LAST LIMIT 1000) TO '$PWD/lib/gems.txt' (format text);"
97
+
98
+ echo "Done."
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
data/exe/gemfilelint ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), __dir__))
5
+ require 'gemfilelint'
6
+
7
+ exit Gemfilelint.lint(ARGV[0] || 'Gemfile')
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/gemfilelint/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'gemfilelint'
7
+ spec.version = Gemfilelint::VERSION
8
+ spec.authors = ['Kevin Deisz']
9
+ spec.email = ['kevin.deisz@gmail.com']
10
+
11
+ spec.summary = 'Lint your Gemfile!'
12
+ spec.homepage = 'https://github.com/kddeisz/gemfilelint'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = Dir.chdir(__dir__) do
16
+ `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ end
20
+
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency 'bundler'
26
+
27
+ spec.add_development_dependency 'minitest'
28
+ spec.add_development_dependency 'rake'
29
+ spec.add_development_dependency 'rubocop'
30
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ require 'bundler'
6
+ require 'bundler/similarity_detector'
7
+
8
+ require 'gemfilelint/version'
9
+
10
+ module Gemfilelint
11
+ module Offenses
12
+ class Dependency < Struct.new(:name, :suggestions)
13
+ def to_s
14
+ <<~ERR
15
+ Gem \"#{name}\" is possibly misspelled, suggestions:
16
+ #{suggestions.map { |suggestion| " * #{suggestion}" }.join("\n")}"
17
+ ERR
18
+ end
19
+ end
20
+
21
+ class Remote < Struct.new(:name, :suggestions)
22
+ def to_s
23
+ <<~ERR
24
+ Source \"#{name}\" is possibly misspelled, suggestions:
25
+ #{suggestions.map { |suggestion| " * #{suggestion}" }.join("\n")}
26
+ ERR
27
+ end
28
+ end
29
+ end
30
+
31
+ class Linter
32
+ class SpellChecker
33
+ attr_reader :detector, :haystack
34
+
35
+ def initialize(haystack)
36
+ @detector = Bundler::SimilarityDetector.new(haystack)
37
+ @haystack = haystack
38
+ end
39
+
40
+ def correct(needle)
41
+ return [] if haystack.include?(needle)
42
+
43
+ detector.similar_words(needle)
44
+ end
45
+ end
46
+
47
+ module ANSIColor
48
+ CODES = { green: 32, magenta: 35 }.freeze
49
+
50
+ refine String do
51
+ def colorize(code)
52
+ "\033[#{CODES[code]}m#{self}\033[0m"
53
+ end
54
+ end
55
+ end
56
+
57
+ using ANSIColor
58
+
59
+ attr_reader :dependency_checker, :remote_checker, :logger
60
+
61
+ def initialize
62
+ common_gems = File.read(File.expand_path('gems.txt', __dir__)).split("\n")
63
+
64
+ @dependency_checker = SpellChecker.new(common_gems)
65
+ @remote_checker = SpellChecker.new(['https://rubygems.org/'])
66
+ end
67
+
68
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
69
+ def lint(path, logger: nil)
70
+ logger ||= make_logger
71
+
72
+ logger.info("Inspecting gemfile at #{path}\n")
73
+ offenses = []
74
+
75
+ each_offense_for(path) do |offense|
76
+ if offense
77
+ offenses << offense
78
+ logger.info('W'.colorize(:magenta))
79
+ else
80
+ logger.info('.'.colorize(:green))
81
+ end
82
+ end
83
+
84
+ logger.info("\n")
85
+ return 0 if offenses.empty?
86
+
87
+ prefix = 'W'.colorize(:magenta)
88
+ messages = offenses.map { |offense| "#{prefix}: #{offense}\n" }
89
+ logger.info("\nOffenses:\n\n#{messages.join}\n")
90
+
91
+ 1
92
+ end
93
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
94
+
95
+ private
96
+
97
+ def make_logger
98
+ Logger.new(STDOUT).tap do |logger|
99
+ logger.level = :info
100
+ logger.formatter = ->(*, message) { message }
101
+ end
102
+ end
103
+
104
+ def each_offense_for(path)
105
+ dsl = Bundler::Dsl.new
106
+ dsl.eval_gemfile(path)
107
+
108
+ # Lol wut, there has got to be a better way to do this
109
+ source_list = dsl.instance_variable_get(:@sources)
110
+ rubygems = source_list.instance_variable_get(:@rubygems_aggregate)
111
+
112
+ dsl.dependencies.each do |dependency|
113
+ yield dependency_offense_for(dependency.name)
114
+ end
115
+
116
+ rubygems.remotes.each do |remote|
117
+ yield remote_offense_for(remote.to_s)
118
+ end
119
+ end
120
+
121
+ def dependency_offense_for(name)
122
+ corrections = dependency_checker.correct(name)
123
+ Offenses::Dependency.new(name, corrections.first(5)) if corrections.any?
124
+ end
125
+
126
+ def remote_offense_for(uri)
127
+ corrections = remote_checker.correct(uri)
128
+ Offenses::Remote.new(uri, corrections) if corrections.any?
129
+ end
130
+ end
131
+
132
+ def self.lint(path, logger: nil)
133
+ Linter.new.lint(path, logger: logger)
134
+ end
135
+ end