licensed 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c0f8ca7a1d4607f4051c9347ce5d9de2b42fa46f
4
- data.tar.gz: 9a0aee1b3d112ca9c57ea0ec74db1c91e62048cd
3
+ metadata.gz: c7f39c048283d853075674325f0d371edaf124a8
4
+ data.tar.gz: 60530129f47ed34f51cac3152be83aab27e32a01
5
5
  SHA512:
6
- metadata.gz: 3e9c5fcd573bbe2bd7a275ac60ae90423d207b87673d900e95322404081f2aefd7db237aef8f582a5bf51b9e007e271e49a28b8cb73e7df612c5e5559b8c9954
7
- data.tar.gz: ea703164f15583a9eb0728055a2443ddde2e14e53a0b93fae2dcada23ca64b926f8e4821ad1c76edf0397a3af8abd0540790084e3d93e741b2cb1fadaff7f4d8
6
+ metadata.gz: 2b2bc431aea2a64f3f12139d533914ad2de740cc5eb00310166dbfa6160fc5a6821d73faf214ceb49fcb6207d9c695561fc44a9ff21e146d88cc4f4eb7de0d38
7
+ data.tar.gz: 53db8e3e0272e976428471bbe0f5a2c6e54cdb7743b2e309ac8b2d839a352073fb8a81026e8bc19838f155a09d0b2a74097736ee6e68bc4152eb2124177ceb7e
data/.licensed.yml ADDED
@@ -0,0 +1,7 @@
1
+ allowed:
2
+ - mit
3
+ - apache-2.0
4
+
5
+ reviewed:
6
+ bundler:
7
+ - pathname-common_prefix
data/.travis.yml CHANGED
@@ -93,5 +93,10 @@ matrix:
93
93
  script: ./script/test git_submodule
94
94
  env: NAME="git_submodule"
95
95
 
96
+ - language: java
97
+ before_script: ./test/fixtures/gradle/gradlew --quiet --version
98
+ script: ./script/test gradle
99
+ env: NAME="gradle"
100
+
96
101
  notifications:
97
102
  disable: true
data/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 2.1.0 - 2019-04-16
10
+
11
+ ### Added
12
+ - New Gradle dependency source enumerator (:tada: @dbussink https://github.com/github/licensed/pull/150, @jandersson-svt https://github.com/github/licensed/pull/159)
13
+ - Metadata added to distributed packages (https://github.com/github/licensed/pull/160)
14
+
15
+ ### Changes
16
+ - Bundler dependency source loads license key from a gem's cached gemspec file as a fallback (https://github.com/github/licensed/pull/154)
17
+ - Licensed will only raise errors on an empty dependency path when caching records (https://github.com/github/licensed/pull/149)
18
+
19
+ ### Fixed
20
+ - Migrating to v2 will no longer crash trying to migrate cached records that don't exist (https://github.com/github/licensed/pull/148)
21
+ - Reported warnings will no longer crash licensed when caching records (https://github.com/github/licensed/pull/147)
22
+
9
23
  ## 2.0.1 - 2019-02-14
10
24
 
11
25
  ### Changes
data/Rakefile CHANGED
@@ -44,11 +44,7 @@ namespace :test do
44
44
  t.description = "Run #{source} tests"
45
45
  t.libs << "test"
46
46
  t.libs << "lib"
47
-
48
- # use negative lookahead to exclude all source tests except
49
- # the tests for `source`
50
- t.test_files = FileList["test/**/*_test.rb"].exclude(/test\/sources\/(?!#{source}).*?_test.rb/,
51
- "test/fixtures/**/*_test.rb")
47
+ t.test_files = FileList["test/commands/*_test.rb", "test/sources/#{source}_test.rb"]
52
48
  end
53
49
  end
54
50
  end
@@ -61,6 +57,7 @@ end
61
57
 
62
58
  packages_search = File.expand_path("script/packages/*", __dir__)
63
59
  platforms = Dir[packages_search].map { |f| File.basename(f, ".*") }
60
+ .reject { |f| f == "build" }
64
61
 
65
62
  namespace :package do
66
63
  platforms.each do |platform|
@@ -1,11 +1,14 @@
1
1
  FROM ruby:2.4-slim-stretch
2
2
 
3
3
  RUN apt-get update \
4
- && apt-get install -y --no-install-recommends cmake make gcc pkg-config squashfs-tools git curl bison \
4
+ && apt-get install -y --no-install-recommends cmake make gcc pkg-config squashfs-tools git curl bison rsync \
5
5
  && rm -rf /var/lib/apt/lists/*
6
6
 
7
7
  RUN curl -L http://enclose.io/rubyc/rubyc-linux-x64.gz | gunzip > /usr/local/bin/rubyc \
8
8
  && chmod +x /usr/local/bin/rubyc
9
9
 
10
+ RUN gem update --system && gem update bundler
11
+
10
12
  ENV CPPFLAGS="-P"
11
13
  ENV RUBYC="/usr/local/bin/rubyc"
14
+ ENV LANG=C.UTF-8
@@ -71,6 +71,17 @@ Creating a new `Licensed::Dependency` object requires name, version, and path ar
71
71
  6. errors (optional)
72
72
  - Any errors found when loading dependency information.
73
73
 
74
+ ##### Creating specialized Dependency objects
75
+
76
+ `Licensed::Dependency` objects inherit from `Licensee::Projects::FsProject` and can override or extend the default `Licensee` behavior to find files for a dependency.
77
+
78
+ If a dependency source requires customized logic when finding or loading license or legal content, the source should define and use a `Licensed::Dependency` subclass to implement the required logic.
79
+
80
+ For examples of this see:
81
+
82
+ - [Manifest::Dependency](../../lib/licensed/sources/manifest.rb) which finds license text from C-style comments
83
+ - [Gradle::Dependency](../../lib/licensed/sources/gradle.rb) which loads license text from a URI
84
+
74
85
  #### Finding licenses
75
86
 
76
87
  In some cases, license content will be in a parent directory of the specified location. For instance, this can happen with Golang packages
@@ -0,0 +1,16 @@
1
+ # Gradle
2
+
3
+ The gradle source will detect dependencies when a `build.gradle` file is found along with either `gradle` or `gradlew` executables being available. The source uses the [Gradle-License-Report](https://github.com/jk1/Gradle-License-Report) plugin to enumerate dependencies and locate their licensing information.
4
+
5
+ An active network connection is required when running the `licensed cache` command with the gradle source. Gradle packages generally do not include license text or other legal notices, and additional network requests are needed to find and pull the necessary license content.
6
+
7
+ ### Setting dependency configurations to load
8
+
9
+ The `gradle.configurations` property is used to determine which dependencies are fetched to load license information. The default configurations are `"runtime"` and `"runtimeClassPath"`.
10
+
11
+ ```yml
12
+ gradle:
13
+ configurations:
14
+ - runtime
15
+ - runtimeClassPath
16
+ ```
@@ -32,6 +32,11 @@ module Licensed
32
32
  #
33
33
  # Returns true.
34
34
  def evaluate_dependency(app, source, dependency, report)
35
+ if dependency.path.empty?
36
+ report.errors << "dependency path not found"
37
+ return false
38
+ end
39
+
35
40
  filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
36
41
  cached_record = Licensed::DependencyRecord.read(filename)
37
42
  if options[:force] || save_dependency_record?(dependency, cached_record)
@@ -8,6 +8,7 @@ module Licensed
8
8
  attr_reader :name
9
9
  attr_reader :version
10
10
  attr_reader :errors
11
+ attr_reader :path
11
12
 
12
13
  # Create a new project dependency
13
14
  #
@@ -21,23 +22,16 @@ module Licensed
21
22
  # Returns a new dependency object. Dependency metadata and license contents
22
23
  # are available if no errors are set on the dependency.
23
24
  def initialize(name:, version:, path:, search_root: nil, metadata: {}, errors: [])
24
- # check the path for default errors if no other errors
25
- # were found when loading the dependency
26
- if errors.empty? && path.to_s.empty?
27
- errors.push("dependency path not found")
28
- end
29
-
30
25
  @name = name
31
26
  @version = version
32
27
  @metadata = metadata
33
28
  @errors = errors
34
-
35
- # if there are any errors, don't evaluate any dependency contents
36
- return if errors.any?
29
+ path = path.to_s
30
+ @path = path
37
31
 
38
32
  # enforcing absolute paths makes life much easier when determining
39
33
  # an absolute file path in #notices
40
- if !Pathname.new(path).absolute?
34
+ if File.exist?(path) && !Pathname.new(path).absolute?
41
35
  # this is an internal error related to source implementation and
42
36
  # should be raised, not stored to be handled by reporters
43
37
  raise ArgumentError, "dependency path #{path} must be absolute"
@@ -52,13 +46,7 @@ module Licensed
52
46
  # but they can still find license contents between the given path and
53
47
  # the search root
54
48
  # @root is defined
55
- path.exist? || File.exist?(@root)
56
- end
57
-
58
- # Returns the location of this dependency on the local disk
59
- def path
60
- # exposes the private method Licensee::Projects::FSProject#dir_path
61
- dir_path
49
+ File.exist?(path) || File.exist?(@root)
62
50
  end
63
51
 
64
52
  # Returns true if the dependency has any errors, false otherwise
@@ -68,7 +56,6 @@ module Licensed
68
56
 
69
57
  # Returns a record for this dependency including metadata and legal contents
70
58
  def record
71
- return nil if errors?
72
59
  @record ||= DependencyRecord.new(
73
60
  metadata: license_metadata,
74
61
  licenses: license_contents,
@@ -78,62 +65,56 @@ module Licensed
78
65
 
79
66
  # Returns a string representing the dependencys license
80
67
  def license_key
81
- return "none" if errors? || !license
68
+ return "none" unless license
82
69
  license.key
83
70
  end
84
71
 
85
72
  # Returns the license text content from all matched sources
86
73
  # except the package file, which doesn't contain license text.
87
74
  def license_contents
88
- return [] if errors?
89
75
  matched_files.reject { |f| f == package_file }
90
76
  .group_by(&:content)
91
- .map { |content, files| { "sources" => content_sources(files), "text" => content } }
77
+ .map { |content, files| { "sources" => license_content_sources(files), "text" => content } }
92
78
  end
93
79
 
94
80
  # Returns legal notices found at the dependency path
95
81
  def notice_contents
96
- return [] if errors?
97
- notice_files.sort # sorted by the path
98
- .map { |file| { "sources" => content_sources(file), "text" => File.read(file).rstrip } }
99
- .select { |text| text.length > 0 } # files with content only
100
- end
101
-
102
- # Returns an array of file paths used to locate legal notices
103
- def notice_files
104
- return [] if errors?
105
-
106
82
  Dir.glob(dir_path.join("*"))
107
83
  .grep(LEGAL_FILES_PATTERN)
108
84
  .select { |path| File.file?(path) }
85
+ .sort # sorted by the path
86
+ .map { |path| { "sources" => normalize_source_path(path), "text" => File.read(path).rstrip } }
87
+ .select { |notice| notice["text"].length > 0 } # files with content only
109
88
  end
110
89
 
111
90
  private
112
91
 
113
- # Returns the sources for a group of license or notice file contents
92
+ # Returns the sources for a group of license file contents
114
93
  #
115
94
  # Sources are returned as a single string with sources separated by ", "
116
- def content_sources(files)
95
+ def license_content_sources(files)
117
96
  paths = Array(files).map do |file|
118
- path = if file.is_a?(Licensee::ProjectFiles::ProjectFile)
119
- dir_path.join(file[:dir], file[:name])
120
- else
121
- Pathname.new(file).expand_path(dir_path)
122
- end
123
-
124
- if path.fnmatch?(dir_path.join("**").to_path)
125
- # files under the dependency path return the relative path to the file
126
- path.relative_path_from(dir_path).to_path
127
- else
128
- # otherwise return the source_path as the immediate parent folder name
129
- # joined with the file name
130
- path.dirname.basename.join(path.basename).to_path
131
- end
97
+ next file[:uri] if file[:uri]
98
+
99
+ path = dir_path.join(file[:dir], file[:name])
100
+ normalize_source_path(path)
132
101
  end
133
102
 
134
103
  paths.join(", ")
135
104
  end
136
105
 
106
+ def normalize_source_path(path)
107
+ path = Pathname.new(path) unless path.is_a?(Pathname)
108
+ if path.fnmatch?(dir_path.join("**").to_path)
109
+ # files under the dependency path return the relative path to the file
110
+ path.relative_path_from(dir_path).to_path
111
+ else
112
+ # otherwise return the source_path as the immediate parent folder name
113
+ # joined with the file name
114
+ path.dirname.basename.join(path.basename).to_path
115
+ end
116
+ end
117
+
137
118
  # Returns the metadata that represents this dependency. This metadata
138
119
  # is written to YAML in the dependencys cached text file
139
120
  def license_metadata
@@ -28,7 +28,9 @@ module Licensed
28
28
  end
29
29
 
30
30
  app.sources.each do |source|
31
- Dir.chdir app.cache_path.join(source.class.type) do
31
+ cache_path = app.cache_path.join(source.class.type)
32
+ next unless File.exist?(cache_path)
33
+ Dir.chdir cache_path do
32
34
  # licensed v1 cached records were stored as .txt files with YAML frontmatter
33
35
  Dir["**/*.txt"].each do |file|
34
36
  yaml, licenses, notices = parse_file(file)
@@ -11,5 +11,6 @@ module Licensed
11
11
  require "licensed/sources/manifest"
12
12
  require "licensed/sources/npm"
13
13
  require "licensed/sources/pip"
14
+ require "licensed/sources/gradle"
14
15
  end
15
16
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "delegate"
2
3
  begin
3
4
  require "bundler"
4
5
  rescue LoadError
@@ -36,6 +37,43 @@ module Licensed
36
37
  end
37
38
  end
38
39
 
40
+ class BundlerSpecification < ::SimpleDelegator
41
+ def gem_dir
42
+ dir = super
43
+ return dir if File.exist?(dir)
44
+
45
+ File.join(Gem.dir, "gems", full_name)
46
+ end
47
+ end
48
+
49
+ class Dependency < Licensed::Dependency
50
+ attr_reader :loaded_from
51
+
52
+ def initialize(name:, version:, path:, loaded_from:, errors: [], metadata: {})
53
+ @loaded_from = loaded_from
54
+ super name: name, version: version, path: path, errors: errors, metadata: metadata
55
+ end
56
+
57
+ # Load a package manager file from the base Licensee::Projects::FsProject
58
+ # or from a gem specification file.
59
+ def package_file
60
+ super || spec_file
61
+ end
62
+
63
+ private
64
+
65
+ # Find a package manager file from the given bundler specification's
66
+ # `loaded_from` if available.
67
+ def spec_file
68
+ return @spec_file if defined?(@spec_file)
69
+ return @spec_file = nil unless loaded_from && File.exist?(loaded_from)
70
+ @spec_file = begin
71
+ file = { name: File.basename(loaded_from), dir: File.dirname(loaded_from) }
72
+ Licensee::ProjectFiles::PackageManagerFile.new(File.read(loaded_from), file)
73
+ end
74
+ end
75
+ end
76
+
39
77
  GEMFILES = %w{Gemfile gems.rb}.freeze
40
78
  DEFAULT_WITHOUT_GROUPS = %i{development test}
41
79
 
@@ -50,10 +88,11 @@ module Licensed
50
88
  with_local_configuration do
51
89
  specs.map do |spec|
52
90
  error = spec.error if spec.respond_to?(:error)
53
- Licensed::Dependency.new(
91
+ Dependency.new(
54
92
  name: spec.name,
55
93
  version: spec.version.to_s,
56
94
  path: spec.gem_dir,
95
+ loaded_from: spec.loaded_from,
57
96
  errors: Array(error),
58
97
  metadata: {
59
98
  "type" => Bundler.type,
@@ -112,7 +151,7 @@ module Licensed
112
151
  spec = definition.resolve.find { |s| s.satisfies?(dependency) }
113
152
 
114
153
  # a nil spec should be rare, generally only seen from bundler
115
- return bundle_exec_gem_spec(dependency.name) if spec.nil?
154
+ return matching_spec(dependency) || bundle_exec_gem_spec(dependency.name) if spec.nil?
116
155
 
117
156
  # try to find a non-lazy specification that matches `spec`
118
157
  # spec.source.specs gives access to specifications with more
@@ -184,6 +223,21 @@ module Licensed
184
223
  end
185
224
  end
186
225
 
226
+ # Loads a dependency specification using rubygems' built-in
227
+ # `Dependency#matching_specs` and `Dependency#to_spec`, from the original
228
+ # gem environment
229
+ def matching_spec(dependency)
230
+ begin
231
+ ::Bundler.with_original_env do
232
+ ::Bundler.rubygems.clear_paths
233
+ return unless dependency.matching_specs(true).any?
234
+ BundlerSpecification.new(dependency.to_spec)
235
+ end
236
+ ensure
237
+ ::Bundler.configure
238
+ end
239
+ end
240
+
187
241
  # Build the bundler definition
188
242
  def definition
189
243
  @definition ||= ::Bundler::Definition.build(gemfile_path, lockfile_path, nil)
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+ require "tempfile"
3
+ require "csv"
4
+ require "uri"
5
+ require "json"
6
+ require "net/http"
7
+ require "fileutils"
8
+
9
+ module Licensed
10
+ module Sources
11
+ class Gradle < Source
12
+
13
+ DEFAULT_CONFIGURATIONS = ["runtime", "runtimeClasspath"].freeze
14
+ GRADLE_LICENSES_PATH = ".gradle-licenses".freeze
15
+
16
+ class Dependency < Licensed::Dependency
17
+ GRADLE_LICENSES_CSV_NAME = "licenses.csv".freeze
18
+
19
+ class << self
20
+ # Returns a key to uniquely identify a name and version in the obtained CSV content
21
+ def csv_key(name:, version:)
22
+ "#{name}-#{version}"
23
+ end
24
+
25
+ # Loads and caches license report CSV data as a hash of :name-:version => :url pairs
26
+ #
27
+ # executable - The gradle executable to run to generate the license report
28
+ # configurations - The gradle configurations to generate license report for
29
+ #
30
+ # Returns a hash of dependency identifiers to their license content URL
31
+ def load_csv(path, executable, configurations)
32
+ @csv ||= begin
33
+ gradle_licenses_dir = File.join(path, GRADLE_LICENSES_PATH)
34
+ Licensed::Sources::Gradle.gradle_command("generateLicenseReport", path: path, executable: executable, configurations: configurations)
35
+ CSV.foreach(File.join(gradle_licenses_dir, GRADLE_LICENSES_CSV_NAME), headers: true).each_with_object({}) do |row, hsh|
36
+ name, _, version = row["artifact"].rpartition(":")
37
+ key = csv_key(name: name, version: version)
38
+ hsh[key] = row["moduleLicenseUrl"]
39
+ end
40
+ ensure
41
+ FileUtils.rm_rf(gradle_licenses_dir)
42
+ end
43
+ end
44
+
45
+ # Returns the cached url for the given dependency
46
+ def url_for(dependency)
47
+ @csv[csv_key(name: dependency.name, version: dependency.version)]
48
+ end
49
+
50
+ # Cache and return the results of getting the license content.
51
+ def retrieve_license(url)
52
+ (@licenses ||= {})[url] ||= Net::HTTP.get(URI(url))
53
+ end
54
+ end
55
+
56
+ def initialize(name:, version:, path:, executable:, configurations:, metadata: {})
57
+ @configurations = configurations
58
+ @executable = executable
59
+ super(name: name, version: version, path: path, metadata: metadata)
60
+ end
61
+
62
+ # Returns whether the dependency content exists
63
+ def exist?
64
+ # shouldn't force network connections just to check if content exists
65
+ # only check that the path is not empty
66
+ !path.to_s.empty?
67
+ end
68
+
69
+ # Returns a Licensee::ProjectFiles::LicenseFile for the dependency
70
+ def project_files
71
+ self.class.load_csv(path, @executable, @configurations)
72
+ url = self.class.url_for(self)
73
+
74
+ return [] if url.nil?
75
+
76
+ license_data = self.class.retrieve_license(url)
77
+
78
+ Array(Licensee::ProjectFiles::LicenseFile.new(license_data, { uri: url }))
79
+ end
80
+ end
81
+
82
+ def enabled?
83
+ !gradle_executable.to_s.empty? && File.exist?(config.pwd.join("build.gradle"))
84
+ end
85
+
86
+ def enumerate_dependencies
87
+ JSON.parse(self.class.gradle_command("printDependencies", path: config.pwd, executable: gradle_executable, configurations: configurations)).map do |package|
88
+ name = "#{package["group"]}:#{package["name"]}"
89
+ Dependency.new(
90
+ name: name,
91
+ version: package["version"],
92
+ path: config.pwd,
93
+ executable: gradle_executable,
94
+ configurations: configurations,
95
+ metadata: {
96
+ "type" => Gradle.type
97
+ }
98
+ )
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def gradle_executable
105
+ return @gradle_executable if defined?(@gradle_executable)
106
+ @gradle_executable = begin
107
+ gradlew = File.join(config.pwd, "gradlew")
108
+ return gradlew if File.executable?(gradlew)
109
+ "gradle" if Licensed::Shell.tool_available?("gradle")
110
+ end
111
+ end
112
+
113
+ # Returns the configurations to include in license generation.
114
+ # Defaults to ["runtime", "runtimeClasspath"]
115
+ def configurations
116
+ @configurations ||= begin
117
+ if configurations = config.dig("gradle", "configurations")
118
+ Array(configurations)
119
+ else
120
+ DEFAULT_CONFIGURATIONS
121
+ end
122
+ end
123
+ end
124
+
125
+ def self.add_gradle_license_report_plugins_block(gradle_build_file)
126
+
127
+ if gradle_build_file.include? "plugins"
128
+ gradle_build_file.gsub(/(?<=plugins)\s+{/, " { id 'com.github.jk1.dependency-license-report' version '1.6'")
129
+ else
130
+
131
+ gradle_build_file = " plugins { id 'com.github.jk1.dependency-license-report' version '1.6' }" + gradle_build_file
132
+ end
133
+ end
134
+
135
+ def self.gradle_command(*args, path:, executable:, configurations:)
136
+ gradle_build_file = File.read("build.gradle")
137
+
138
+ if !gradle_build_file.include? "dependency-license-report"
139
+ gradle_build_file = Licensed::Sources::Gradle.add_gradle_license_report_plugins_block(gradle_build_file)
140
+ end
141
+
142
+ Dir.chdir(path) do
143
+ Tempfile.create(["license-", ".gradle"], path) do |f|
144
+ f.write(gradle_build_file)
145
+ f.write gradle_file(configurations)
146
+ f.close
147
+ Licensed::Shell.execute(executable, "-q", "-b", f.path, *args)
148
+ end
149
+ end
150
+ end
151
+
152
+ def self.gradle_file(configurations)
153
+ <<~EOF
154
+
155
+ import com.github.jk1.license.render.CsvReportRenderer
156
+ import com.github.jk1.license.filter.LicenseBundleNormalizer
157
+
158
+ final configs = #{configurations.inspect}
159
+
160
+ licenseReport {
161
+ configurations = configs
162
+ outputDir = "$projectDir/#{GRADLE_LICENSES_PATH}"
163
+ renderers = [new CsvReportRenderer()]
164
+ filters = [new LicenseBundleNormalizer()]
165
+ }
166
+
167
+ task printDependencies {
168
+ doLast {
169
+ def dependencies = []
170
+ configs.each {
171
+ configurations[it].resolvedConfiguration.resolvedArtifacts.each { artifact ->
172
+ def id = artifact.moduleVersion.id
173
+ dependencies << " { \\"group\\": \\"${id.group}\\", \\"name\\": \\"${id.name}\\", \\"version\\": \\"${id.version}\\" }"
174
+ }
175
+ }
176
+ println "[\\n${dependencies.join(", ")}\\n]"
177
+ }
178
+ }
179
+ EOF
180
+ end
181
+ end
182
+ end
183
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "2.0.1".freeze
3
+ VERSION = "2.1.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
data/licensed.gemspec CHANGED
@@ -31,7 +31,8 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_development_dependency "rake", "~> 10.0"
33
33
  spec.add_development_dependency "minitest", "~> 5.8"
34
- spec.add_development_dependency "rubocop", "~> 0.49"
34
+ spec.add_development_dependency "mocha", "~> 1.0"
35
+ spec.add_development_dependency "rubocop", "~> 0.49", "< 0.67"
35
36
  spec.add_development_dependency "rubocop-github", "~> 0.6"
36
37
  spec.add_development_dependency "byebug", "~> 10.0.0"
37
38
  end
@@ -0,0 +1,92 @@
1
+ #!/bin/bash
2
+ #/ Usage: script/packages/build <RUBYC> [VERSION]
3
+ #/
4
+ #/ WARNING: Do not call this directly. Please create packages using
5
+ #/ `script/package [platform]` or `bundle exec rake package[platform]`
6
+ #/
7
+ #/ Builds a distributable package for licensed for a given RUBYC compiler and licensed VERSION.
8
+ #/ Packages are of the form licensed-$VERSION-$PLATFORM-x64.tar.gz and contain a `./licensed` executable
9
+ #/ Built Packages are placed in the <root>/pkg/$VERSION directory.
10
+ #/
11
+ #/ OPTIONS:
12
+ #/ <RUBYC> The path to a rubyc compiler that should be used to compile the target executable
13
+ #/ [VERSION] (optional, default to current git branch or SHA1) version of licensed to build exe at
14
+ #/
15
+ #/ EXAMPLES:
16
+ #/
17
+ #/ Builds a package for version 1.1.0 using a local rubyc compiler
18
+ #/ $ script/packages/build RUBYC="./rubyc-darwin" VERSION="1.1.0"
19
+ #/
20
+
21
+ set -euo pipefail
22
+
23
+ BASE_DIR="$(cd "$(dirname $0)/../.." && pwd)"
24
+ RUBYC=${RUBYC:=""}
25
+ if [ ! -f "$RUBYC" ]; then
26
+ echo "Specify a rubyc compiler using the RUBYC environment variable" >&2
27
+ exit 127
28
+ fi
29
+
30
+ # if a version is not provided, get an identifier from the current HEAD
31
+ VERSION=${VERSION:="$(git rev-parse --abbrev-ref HEAD)"}
32
+
33
+ BUILD_DIR="$(mktemp -d)"
34
+ COPY_DIR="$(mktemp -d)"
35
+ trap "rm -rf $BUILD_DIR; rm -rf $COPY_DIR" EXIT
36
+
37
+ # copy the repo to a separate directory. determining license metadata
38
+ # will require a clean environment with no Gemfile.lock. using a work location
39
+ # is preferred to messing with a development repository
40
+ rsync -r --exclude="test/" --exclude=".licenses/" --exclude="vendor/" --exclude="Gemfile.lock" --exclude="pkg/" $BASE_DIR/ $COPY_DIR
41
+ cd $COPY_DIR
42
+
43
+ # ensure repo is at $VERSION and build executable, restoring the repo to previous
44
+ # state after build.
45
+ (
46
+ # run in a subshell for ease of returning to the current branch after building
47
+ # the executable
48
+ CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
49
+ if [[ "$VERSION" != "$CURRENT_BRANCH" ]]; then
50
+ git checkout "$VERSION"
51
+ trap "git checkout $CURRENT_BRANCH" EXIT
52
+ fi
53
+
54
+ # build the licensed rubyc executable
55
+ "$RUBYC" --clean-tmpdir -o "$BUILD_DIR/licensed" "$COPY_DIR/exe/licensed"
56
+ chmod +x $BUILD_DIR/licensed
57
+ )
58
+
59
+ # non-executable content will be stored in a `meta` directory
60
+ mkdir -p "$BUILD_DIR/meta"
61
+
62
+ # include dependency license data in package; run bundle update in temp work dir
63
+ # and then run licensed on itself in that directory to grab license data
64
+ # NOTE: this does not produce accurate license information if any licensed
65
+ # depends on any platform-specific gems.
66
+ (
67
+ # run in a subshell so that `unset BUNDLER_VERSION` is contained. the ENV var
68
+ # is required to successfully build a distributable executable on
69
+ # dockerized linux
70
+ unset BUNDLER_VERSION
71
+
72
+ # determining dependency license data needs to be done from a clean environment
73
+ # without previous dependencies or a Gemfile.lock so as to pull in the latest
74
+ # bundler version, which is what the distributed executable uses
75
+ script/bootstrap
76
+ bundle exec exe/licensed cache
77
+ cp -R "$COPY_DIR/.licenses" "$BUILD_DIR/meta"
78
+ )
79
+
80
+ # copy static metadata to build directory
81
+ mkdir -p "$BUILD_DIR/meta/ruby"
82
+ curl -o "$BUILD_DIR/meta/ruby/license.txt" "https://www.ruby-lang.org/en/about/license.txt"
83
+ cp "$BASE_DIR/LICENSE" "$BUILD_DIR/meta"
84
+ cp "$BASE_DIR/README.md" "$BUILD_DIR/meta"
85
+
86
+ # create release archive
87
+ PLATFORM="$(uname -s | tr '[:upper:]' '[:lower:]')"
88
+ TARGET="$BASE_DIR/pkg/$VERSION/licensed-$VERSION-$PLATFORM-x64.tar.gz"
89
+ mkdir -p "$(dirname $TARGET)"
90
+ tar -C "$BUILD_DIR" -czf "$TARGET" .
91
+
92
+ echo "licensed package built to $TARGET"
@@ -27,21 +27,23 @@ build_linux_docker() {
27
27
  -v "$BASE_DIR":/var/licensed \
28
28
  -w /var/licensed \
29
29
  "$IMAGE" \
30
- "script/build-rubyc-exe"
30
+ "script/packages/build"
31
31
  }
32
32
 
33
33
  build_linux_local() {
34
34
  sudo apt-get update && \
35
35
  apt-get install -y --no-install-recommends cmake make gcc pkg-config squashfs-tools curl bison git
36
36
 
37
- if [ ! -f "$BASE_DIR/bin/rubyc-linux" ]; then
38
- mkdir -p "$BASE_DIR/bin"
39
- curl -L http://enclose.io/rubyc/rubyc-linux-x64.gz | gunzip > "$BASE_DIR/bin/rubyc-linux"
40
- chmod +x "$BASE_DIR/bin/rubyc-linux"
37
+ RUBYC="$BASE_DIR/bin/rubyc-linux"
38
+ if [ ! -f "$RUBYC" ]; then
39
+ mkdir -p "$(dirname "$RUBYC")"
40
+ curl -L http://enclose.io/rubyc/rubyc-linux-x64.gz | gunzip > "$RUBYC"
41
+ chmod +x "$RUBYC"
41
42
  fi
42
43
 
43
44
  export CPPFLAGS="-P"
44
- RUBYC="$BASE_DIR/bin/rubyc-linux" "$BASE_DIR"/script/build-rubyc-exe
45
+ export RUBYC
46
+ "$BASE_DIR"/script/packages/build
45
47
  }
46
48
 
47
49
  if [[ "$(uname -s)" != "Linux" ]]; then
data/script/packages/mac CHANGED
@@ -22,14 +22,16 @@ if [[ "$(uname -s)" != "Darwin" ]]; then
22
22
  fi
23
23
 
24
24
  BASE_DIR="$(cd "$(dirname $0)/../.." && pwd)"
25
+ RUBYC="$BASE_DIR/bin/rubyc-darwin"
25
26
 
26
27
  brew update
27
28
  brew list "squashfs" &>/dev/null || brew install "squashfs"
28
29
 
29
- if [ ! -f "$BASE_DIR/bin/rubyc-darwin" ]; then
30
- mkdir -p "$BASE_DIR/bin"
31
- curl -L http://enclose.io/rubyc/rubyc-darwin-x64.gz | gunzip > "$BASE_DIR/bin/rubyc-darwin"
32
- chmod +x "$BASE_DIR/bin/rubyc-darwin"
30
+ if [ ! -f "$RUBYC" ]; then
31
+ mkdir -p "$(dirname "$RUBYC")"
32
+ curl -L http://enclose.io/rubyc/rubyc-darwin-x64.gz | gunzip > "$RUBYC"
33
+ chmod +x "$RUBYC"
33
34
  fi
34
35
 
35
- RUBYC="$BASE_DIR/bin/rubyc-darwin" "$BASE_DIR"/script/build-rubyc-exe
36
+ export RUBYC
37
+ "$BASE_DIR"/script/packages/build
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: licensed
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-02-14 00:00:00.000000000 Z
11
+ date: 2019-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: licensee
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '5.8'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mocha
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rubocop
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -115,6 +129,9 @@ dependencies:
115
129
  - - "~>"
116
130
  - !ruby/object:Gem::Version
117
131
  version: '0.49'
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '0.67'
118
135
  type: :development
119
136
  prerelease: false
120
137
  version_requirements: !ruby/object:Gem::Requirement
@@ -122,6 +139,9 @@ dependencies:
122
139
  - - "~>"
123
140
  - !ruby/object:Gem::Version
124
141
  version: '0.49'
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.67'
125
145
  - !ruby/object:Gem::Dependency
126
146
  name: rubocop-github
127
147
  requirement: !ruby/object:Gem::Requirement
@@ -159,6 +179,7 @@ extensions: []
159
179
  extra_rdoc_files: []
160
180
  files:
161
181
  - ".gitignore"
182
+ - ".licensed.yml"
162
183
  - ".rubocop.yml"
163
184
  - ".ruby-version"
164
185
  - ".travis.yml"
@@ -182,6 +203,7 @@ files:
182
203
  - docs/sources/dep.md
183
204
  - docs/sources/git_submodule.md
184
205
  - docs/sources/go.md
206
+ - docs/sources/gradle.md
185
207
  - docs/sources/manifests.md
186
208
  - docs/sources/npm.md
187
209
  - docs/sources/pip.md
@@ -213,6 +235,7 @@ files:
213
235
  - lib/licensed/sources/dep.rb
214
236
  - lib/licensed/sources/git_submodule.rb
215
237
  - lib/licensed/sources/go.rb
238
+ - lib/licensed/sources/gradle.rb
216
239
  - lib/licensed/sources/manifest.rb
217
240
  - lib/licensed/sources/npm.rb
218
241
  - lib/licensed/sources/pip.rb
@@ -221,10 +244,10 @@ files:
221
244
  - lib/licensed/version.rb
222
245
  - licensed.gemspec
223
246
  - script/bootstrap
224
- - script/build-rubyc-exe
225
247
  - script/cibuild
226
248
  - script/console
227
249
  - script/package
250
+ - script/packages/build
228
251
  - script/packages/linux
229
252
  - script/packages/mac
230
253
  - script/setup
@@ -1,58 +0,0 @@
1
- #!/bin/bash
2
- #/ Usage: script/build-rubyc-exe <RUBYC> [VERSION]
3
- #/
4
- #/ WARNING: You should not need to call this directly. Please create packages using
5
- #/ `script/package [platform]` or `bundle exec rake package[platform]`
6
- #/
7
- #/ Builds a distributable package for licensed for a given RUBYC compiler and licensed VERSION.
8
- #/ Packages are of the form licensed-$VERSION-$PLATFORM-x64.tar.gz and contain a `./licensed` executable
9
- #/ Built Packages are placed in the <root>/pkg directory.
10
- #/
11
- #/ OPTIONS:
12
- #/ <RUBYC> The path to a rubyc compiler that should be used to compile the target executable
13
- #/ [VERSION] (optional, default to current git branch or SHA1) version of licensed to build exe at
14
- #/
15
- #/ EXAMPLES:
16
- #/
17
- #/ Builds a package for version 1.1.0 using a local rubyc compiler
18
- #/ $ build-rubyc-exe RUBYC="./rubyc-darwin" VERSION="1.1.0"
19
- #/
20
-
21
- set -euo pipefail
22
-
23
- usage(){
24
- grep "^#/" <"$0" | cut -c3-
25
- }
26
-
27
- BASE_DIR="$(cd "$(dirname $0)/.." && pwd)"
28
- VERSION=${VERSION:=""}
29
- RUBYC=${RUBYC:=""}
30
- if [ ! -f "$RUBYC" ]; then
31
- echo "Please specify a rubyc compiler" >&2
32
- usage
33
- exit 127
34
- fi
35
-
36
- BUILD_DEST="$(mktemp -d)"
37
- trap "rm -rf $BUILD_DEST" EXIT
38
-
39
- if [ -n "$VERSION" ]; then
40
- CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
41
- git checkout "$VERSION"
42
- trap "rm -rf $BUILD_DEST; git checkout $CURRENT_BRANCH" EXIT
43
- else
44
- VERSION="$(git rev-parse --abbrev-ref HEAD)"
45
- fi
46
-
47
- PACKAGE_DEST="$BASE_DIR/pkg"
48
- mkdir -p "$PACKAGE_DEST"
49
-
50
- PLATFORM="$(uname -s | tr '[:upper:]' '[:lower:]')"
51
- TARGET="licensed-$VERSION-$PLATFORM-x64.tar.gz"
52
-
53
- "$RUBYC" --clean-tmpdir -o "$BUILD_DEST/licensed" "$BASE_DIR/exe/licensed"
54
-
55
- chmod +x $BUILD_DEST/licensed
56
- tar -C "$BUILD_DEST" -czf "$PACKAGE_DEST/$TARGET" "./licensed"
57
-
58
- echo "licensed built to $PACKAGE_DEST/$TARGET"