ad_licenselint 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/ad_licenselint +4 -0
- data/lib/ad_licenselint.rb +23 -0
- data/lib/ad_licenselint/ad_logger.rb +33 -0
- data/lib/ad_licenselint/constant.rb +8 -0
- data/lib/ad_licenselint/license_entry.rb +31 -0
- data/lib/ad_licenselint/markdown_formatter.rb +46 -0
- data/lib/ad_licenselint/option_handler.rb +52 -0
- data/lib/ad_licenselint/report.rb +17 -0
- data/lib/ad_licenselint/runner.rb +97 -0
- data/lib/ad_licenselint/terminal_formatter.rb +21 -0
- metadata +199 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3865f9b58fea6e2da9af8009f08e8ad04b979c7c9b2b2974e3fba37aeb589509
|
4
|
+
data.tar.gz: 7f7555f6efbc66a8af23cb5d851c341211669b07a1cc7e5f3846b847f9e9f278
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 90ca6ecf7176ef3bbefc45fe10450916356aeb9d08b1d57fb5e0080026d5ba50fb1f742242ef29b59fde41a890b78a815992d81162f1c7ff8361041757737110
|
7
|
+
data.tar.gz: 76266a5595f2ed1482328a7c2a6c7be940eb48348a904486612e02e230ea7c6e4ed5ae4ec9e4f5cd5998b45fc9a56f82ef3ad8a6b16c6a4f838deeb617fc404e
|
data/bin/ad_licenselint
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'logger'
|
3
|
+
require 'colorize'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'json'
|
6
|
+
require 'terminal-table'
|
7
|
+
require 'cocoapods-core'
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
require 'ad_licenselint/ad_logger'
|
11
|
+
require 'ad_licenselint/option_handler'
|
12
|
+
require 'ad_licenselint/runner'
|
13
|
+
require 'ad_licenselint/license_entry'
|
14
|
+
require 'ad_licenselint/constant'
|
15
|
+
require 'ad_licenselint/report'
|
16
|
+
require 'ad_licenselint/terminal_formatter'
|
17
|
+
require 'ad_licenselint/markdown_formatter'
|
18
|
+
|
19
|
+
module ADLicenseLint
|
20
|
+
class Error < StandardError; end
|
21
|
+
|
22
|
+
LOGGER = ADLogger.new
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ADLicenseLint
|
2
|
+
class ADLogger
|
3
|
+
SEVERITY = { debug: Logger::DEBUG , info: Logger::INFO, warn: Logger::WARN, error: Logger::ERROR, fatal: Logger::FATAL, unknown: Logger::UNKNOWN }
|
4
|
+
AUTHORIZED_COLORS = [:black, :yellow, :red, :blue, :green]
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@logger = Logger.new(STDOUT)
|
8
|
+
@logger.level = Logger::INFO
|
9
|
+
end
|
10
|
+
|
11
|
+
def log(level, color, text)
|
12
|
+
exit unless text and level and color
|
13
|
+
|
14
|
+
level, color = [level, color].map(&:to_sym)
|
15
|
+
raise "Color not supported" unless AUTHORIZED_COLORS.include? color
|
16
|
+
raise "Invalid severity" unless SEVERITY.dig(level)
|
17
|
+
|
18
|
+
@logger.add(SEVERITY.dig(level), text.send(color))
|
19
|
+
end
|
20
|
+
|
21
|
+
def debug!
|
22
|
+
@logger.level = Logger::DEBUG
|
23
|
+
end
|
24
|
+
|
25
|
+
def debug?
|
26
|
+
@logger.debug?
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
@logger.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class LicenseEntry
|
2
|
+
attr_reader :license_content, :license_name, :pod_name, :source_url
|
3
|
+
attr_writer :source_url
|
4
|
+
|
5
|
+
def initialize(hash)
|
6
|
+
@pod_name = hash["Title"] || ""
|
7
|
+
@license_name = hash["License"] || ""
|
8
|
+
@license_content = hash["FooterText"] || ""
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_valid
|
12
|
+
!pod_name.empty? && !license_name.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_accepted
|
16
|
+
ADLicenseLint::Constant::ACCEPTED_LICENSES.include?(license_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def copyright
|
20
|
+
(/Copyright(.*)$/.match license_content)[0]
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_hash
|
24
|
+
{
|
25
|
+
pod_name: @pod_name,
|
26
|
+
license_content: @license_content,
|
27
|
+
license_name: @license_name,
|
28
|
+
source_url: @source_url
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ADLicenseLint
|
2
|
+
class MarkdownFormatter
|
3
|
+
def initialize(report)
|
4
|
+
@entries = report.entries.sort_by(&:pod_name)
|
5
|
+
end
|
6
|
+
|
7
|
+
def formatted_content
|
8
|
+
"#{table}\n\n#{foldable_content}"
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def table
|
14
|
+
rows = [
|
15
|
+
"| Pod | License | Source |",
|
16
|
+
"| --- | --- | --- |",
|
17
|
+
] + @entries.map { |entry|
|
18
|
+
"| #{entry.pod_name} | #{entry.license_name} | #{entry.source_url} |"
|
19
|
+
}
|
20
|
+
rows.join("\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def foldable_content
|
24
|
+
[
|
25
|
+
"<details>",
|
26
|
+
"<summary>Licenses</summary>",
|
27
|
+
"",
|
28
|
+
licenses_content,
|
29
|
+
"</details>"
|
30
|
+
].join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def licenses_content
|
34
|
+
@entries
|
35
|
+
.map { |entry|
|
36
|
+
[
|
37
|
+
"### #{entry.pod_name}",
|
38
|
+
"```",
|
39
|
+
entry.license_content,
|
40
|
+
"```",
|
41
|
+
].join("\n")
|
42
|
+
}
|
43
|
+
.join("\n")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ADLicenseLint
|
2
|
+
|
3
|
+
class OptionHandler
|
4
|
+
|
5
|
+
def self.parse
|
6
|
+
|
7
|
+
options = {
|
8
|
+
format: ADLicenseLint::Constant::TERMINAL_FORMAT_OPTION,
|
9
|
+
path: ".",
|
10
|
+
all: false
|
11
|
+
}
|
12
|
+
available_formats = ADLicenseLint::Constant::AVAILABLE_OPTIONS
|
13
|
+
|
14
|
+
parser = OptionParser.new do |p|
|
15
|
+
p.banner = "Usage: ad_licenselint -f [md|term]"
|
16
|
+
|
17
|
+
p.on("-f", "--format [FORMAT]", "[Optional] Select output format [#{available_formats.join(", ")}] (default to #{options[:format]})") do |arg|
|
18
|
+
options[:format] = arg
|
19
|
+
end
|
20
|
+
|
21
|
+
p.on("-p", "--path [PATH]", "[Optional] Sources directory (default to \"#{options[:path]}\")") do |arg|
|
22
|
+
options[:path] = arg
|
23
|
+
end
|
24
|
+
|
25
|
+
p.on("-a", "--all", "[Optional] Display all licenses (default to #{options[:all]})") do |arg|
|
26
|
+
options[:all] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
p.on("-h", "--help", "Prints this help") do
|
30
|
+
puts p
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
parser.parse!
|
37
|
+
raise OptionParser::InvalidArgument, "--format" unless available_formats.include?(options[:format])
|
38
|
+
rescue OptionParser::MissingArgument => e
|
39
|
+
LOGGER.log(:error, :red, "Missing argument for option #{e.args.join(",")}")
|
40
|
+
exit
|
41
|
+
rescue OptionParser::InvalidArgument => e
|
42
|
+
LOGGER.log(:error, :red, "Invalid argument for option #{e.args.join(",")}")
|
43
|
+
exit
|
44
|
+
rescue ArgumentError => e
|
45
|
+
LOGGER.log(:error, :red, e.message)
|
46
|
+
raise e
|
47
|
+
end
|
48
|
+
|
49
|
+
options
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module ADLicenseLint
|
2
|
+
|
3
|
+
class Runner
|
4
|
+
attr_accessor :options, :path
|
5
|
+
|
6
|
+
POD_SOURCE = Pod::Source.new("~/.cocoapods/repos/master")
|
7
|
+
|
8
|
+
def initialize(options = nil)
|
9
|
+
@options = options || OptionHandler.parse
|
10
|
+
@path = File.expand_path(@options[:path])
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_report
|
14
|
+
raise "Folder #{target_support_path} does not exist" if Dir[target_support_path].empty?
|
15
|
+
|
16
|
+
plist_files = Dir[acknowledgements_plist_path] # one plist for each target
|
17
|
+
json_contents = plist_files.map { |plist_file| acknowledgement_json(plist_file) }
|
18
|
+
entries = all_entries(json_contents)
|
19
|
+
warning_entries = entries.reject(&:is_accepted)
|
20
|
+
displayed_entries = options[:all] ? entries : warning_entries
|
21
|
+
|
22
|
+
Report.new(displayed_entries)
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
report = create_report
|
27
|
+
return "" if report.empty?
|
28
|
+
|
29
|
+
case options[:format]
|
30
|
+
when ADLicenseLint::Constant::MARKDOWN_FORMAT_OPTION
|
31
|
+
MarkdownFormatter.new(report).formatted_content
|
32
|
+
when ADLicenseLint::Constant::TERMINAL_FORMAT_OPTION
|
33
|
+
TerminalFormatter.new(report).formatted_content
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def acknowledgement_json(plist_file)
|
40
|
+
tmp_file = Tempfile.new('license')
|
41
|
+
begin
|
42
|
+
system "plutil -convert json -o #{tmp_file.path} \"#{plist_file}\"" # convert plist to json
|
43
|
+
JSON.parse(File.read(tmp_file.path))
|
44
|
+
ensure
|
45
|
+
tmp_file.close
|
46
|
+
tmp_file.unlink # deletes the temp file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def all_entries(json_contents)
|
51
|
+
pod_names = pod_names_from_podfile
|
52
|
+
entries = json_contents
|
53
|
+
.map { |json| json["PreferenceSpecifiers"].map { |hash| LicenseEntry.new hash } }
|
54
|
+
.flatten
|
55
|
+
.select(&:is_valid)
|
56
|
+
.select { |e| pod_names.include?(e.pod_name) }
|
57
|
+
.uniq(&:pod_name)
|
58
|
+
entries.each { |e| e.source_url = source_url(e.pod_name) }
|
59
|
+
entries
|
60
|
+
end
|
61
|
+
|
62
|
+
def source_url(pod_name)
|
63
|
+
url = git_url(pod_name)
|
64
|
+
url&.gsub(".git", "")
|
65
|
+
end
|
66
|
+
|
67
|
+
def git_url(pod_name)
|
68
|
+
spec = pod_specification(pod_name)
|
69
|
+
spec["source"]["git"] || spec["homepage"] unless spec.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
def pod_specification(pod_name)
|
73
|
+
set = POD_SOURCE.set(pod_name)
|
74
|
+
return nil if set.highest_version.nil? # access to specification crashes if highest_version is nil
|
75
|
+
set.specification.to_hash
|
76
|
+
end
|
77
|
+
|
78
|
+
def pod_names_from_podfile
|
79
|
+
Pod::Podfile.from_file(podfile_path).dependencies
|
80
|
+
.map(&:name)
|
81
|
+
.map { |e| e.split("/").first } # ex: convert CocoaLumberjack/Swift to CocoaLumberjack
|
82
|
+
.uniq
|
83
|
+
end
|
84
|
+
|
85
|
+
def podfile_path
|
86
|
+
File.join(@path, 'Podfile')
|
87
|
+
end
|
88
|
+
|
89
|
+
def target_support_path
|
90
|
+
File.join(@path, 'Pods', 'Target\ Support\ Files')
|
91
|
+
end
|
92
|
+
|
93
|
+
def acknowledgements_plist_path
|
94
|
+
File.join(target_support_path, "Pods-*/*acknowledgements.plist")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ADLicenseLint
|
2
|
+
class TerminalFormatter
|
3
|
+
|
4
|
+
def initialize(report)
|
5
|
+
@report = report
|
6
|
+
end
|
7
|
+
|
8
|
+
def formatted_content
|
9
|
+
rows = @report
|
10
|
+
.entries
|
11
|
+
.sort_by(&:pod_name)
|
12
|
+
.map { |entry|
|
13
|
+
[entry.pod_name, entry.license_name, entry.source_url]
|
14
|
+
}
|
15
|
+
Terminal::Table.new({
|
16
|
+
headings: ['Pod', 'License', 'Source'],
|
17
|
+
rows: rows
|
18
|
+
}).to_s
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ad_licenselint
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pierre Felgines
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.12.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.0
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.12.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '12.3'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '12.3'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: minitest
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '5.11'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '5.11'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: byebug
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '11.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '11.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: minitest-reporters
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.3'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '1.3'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: minitest-hooks
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.5'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.5'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: cocoapods
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.9'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '1.9'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: colorize
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0.8'
|
124
|
+
type: :runtime
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0.8'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: terminal-table
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '1.8'
|
138
|
+
type: :runtime
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '1.8'
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: cocoapods-core
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '1.9'
|
152
|
+
type: :runtime
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '1.9'
|
159
|
+
description: Lint the licenses for iOS projects
|
160
|
+
email: pierre.felgines@fabernovel.com
|
161
|
+
executables:
|
162
|
+
- ad_licenselint
|
163
|
+
extensions: []
|
164
|
+
extra_rdoc_files: []
|
165
|
+
files:
|
166
|
+
- bin/ad_licenselint
|
167
|
+
- lib/ad_licenselint.rb
|
168
|
+
- lib/ad_licenselint/ad_logger.rb
|
169
|
+
- lib/ad_licenselint/constant.rb
|
170
|
+
- lib/ad_licenselint/license_entry.rb
|
171
|
+
- lib/ad_licenselint/markdown_formatter.rb
|
172
|
+
- lib/ad_licenselint/option_handler.rb
|
173
|
+
- lib/ad_licenselint/report.rb
|
174
|
+
- lib/ad_licenselint/runner.rb
|
175
|
+
- lib/ad_licenselint/terminal_formatter.rb
|
176
|
+
homepage: https://rubygems.org/gems/ad_licenselint
|
177
|
+
licenses:
|
178
|
+
- MIT
|
179
|
+
metadata: {}
|
180
|
+
post_install_message:
|
181
|
+
rdoc_options: []
|
182
|
+
require_paths:
|
183
|
+
- lib
|
184
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "~>"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '2.3'
|
189
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
requirements: []
|
195
|
+
rubygems_version: 3.0.3
|
196
|
+
signing_key:
|
197
|
+
specification_version: 4
|
198
|
+
summary: Lint the licenses for iOS projects
|
199
|
+
test_files: []
|