Class: Licensed::Sources::Go

Inherits:
Source
  • Object
show all
Includes:
ContentVersioning
Defined in:
lib/licensed/sources/go.rb

Constant Summary

Constants included from ContentVersioning

ContentVersioning::CONTENTS, ContentVersioning::GIT

Instance Attribute Summary

Attributes inherited from Source

#config

Instance Method Summary collapse

Methods included from ContentVersioning

#contents_hash, #contents_version, #git_version, #version_strategy

Methods inherited from Source

#dependencies, #ignored?, inherited, #initialize, type

Constructor Details

This class inherits a constructor from Licensed::Sources::Source

Instance Method Details

#contents_version_argumentsObject

Determines the arguments to pass to contents_version based on which version strategy is selected

Returns an array of arguments to pass to contents version



132
133
134
135
136
137
138
# File 'lib/licensed/sources/go.rb', line 132

def contents_version_arguments
  if version_strategy == Licensed::Sources::ContentVersioning::GIT
    ["."]
  else
    Dir["*"]
  end
end

#enabled?Boolean



11
12
13
# File 'lib/licensed/sources/go.rb', line 11

def enabled?
  Licensed::Shell.tool_available?("go") && go_source?
end

#enumerate_dependenciesObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/licensed/sources/go.rb', line 15

def enumerate_dependencies
  with_configured_gopath do
    packages.map do |package|
      import_path = non_vendored_import_path(package["ImportPath"])
      error = package.dig("Error", "Err") if package["Error"]

      Dependency.new(
        name: import_path,
        version: package_version(package),
        path: package["Dir"],
        search_root: search_root(package),
        errors: Array(error),
        metadata: {
          "type"        => Go.type,
          "summary"     => package["Doc"],
          "homepage"    => homepage(import_path)
        }
      )
    end
  end
end

#go_list_depsObject

Returns the list of dependencies as returned by “go list -json -deps” available in go 1.11



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/licensed/sources/go.rb', line 61

def go_list_deps
  args = ["-deps"]
  args << "-mod=vendor" if config.dig("go", "mod") == "vendor"

  # the CLI command returns packages in a pretty-printed JSON format but
  # not separated by commas. this gsub adds commas after all non-indented
  # "}" that close root level objects.
  # (?!\z) uses negative lookahead to not match the final "}"
  deps = package_info_command(*args).gsub(/^}(?!\z)$/m, "},")
  JSON.parse("[#{deps}]")
end

#go_source?Boolean

Returns whether go source is found



203
204
205
# File 'lib/licensed/sources/go.rb', line 203

def go_source?
  with_configured_gopath { Licensed::Shell.success?("go", "doc") }
end

#go_std_package?(package) ⇒ Boolean

Returns whether the given package import path belongs to the go std library or not

package - package to check as part of the go standard library



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/licensed/sources/go.rb', line 77

def go_std_package?(package)
  return false unless package

  # return true if package self-identifies
  return true if package["Standard"]

  import_path = package["ImportPath"]
  return false unless import_path

  # true if go standard packages includes the import path as given
  return true if go_std_packages.include?(import_path)
  return true if go_std_packages.include?("vendor/#{import_path}")

  # additional checks are only for vendored dependencies - return false
  # if package isn't vendored
  return false unless vendored_path?(import_path)

  # return true if any of the go standard packages matches against
  # the non-vendored import path
  return true if go_std_packages.include?(non_vendored_import_path(import_path))

  # modify the import path to look like the import path `go list` returns for vendored std packages
  vendor_path = import_path.sub("#{root_package["ImportPath"]}/", "")
  go_std_packages.include?(vendor_path) || go_std_packages.include?(vendor_path.sub("golang.org", "golang_org"))
end

#go_std_packagesObject

Returns a list of go standard packages



208
209
210
# File 'lib/licensed/sources/go.rb', line 208

def go_std_packages
  @std_packages ||= Licensed::Shell.execute("go", "list", "std").lines.map(&:strip)
end

#go_versionObject

Returns the current version of go available, as a Gem::Version



226
227
228
229
230
231
232
# File 'lib/licensed/sources/go.rb', line 226

def go_version
  @go_version ||= begin
    full_version = Licensed::Shell.execute("go", "version").strip
    version_string = full_version.gsub(%r{.*go(\d+\.\d+(\.\d+)?).*}, "\\1")
    Gem::Version.new(version_string)
  end
end

#gopathObject

Returns a GOPATH value from either a configuration value or ENV, with the configuration value preferred over the ENV var



214
215
216
217
218
219
220
221
222
223
# File 'lib/licensed/sources/go.rb', line 214

def gopath
  return @gopath if defined?(@gopath)

  @gopath = begin
    path = config.dig("go", "GOPATH")
    return File.expand_path(path, config.root) unless path.to_s.empty?
    return ENV["GOPATH"] if ENV["GOPATH"]
    Licensed::Shell.execute("go", "env", "GOPATH")
  end
end

#homepage(import_path) ⇒ Object

Returns the godoc.org page for a package.



141
142
143
144
# File 'lib/licensed/sources/go.rb', line 141

def homepage(import_path)
  return unless import_path
  "https://godoc.org/#{import_path}"
end

#local_package?(package) ⇒ Boolean

Returns whether the package is local to the current project



104
105
106
107
108
# File 'lib/licensed/sources/go.rb', line 104

def local_package?(package)
  return false unless package && package["ImportPath"]
  import_path = package["ImportPath"]
  import_path.start_with?(root_package["ImportPath"]) && !vendored_path?(import_path)
end

#non_vendored_import_path(import_path) ⇒ Object

Returns the import path parameter without the vendor component

import_path - Package import path with vendor component



177
178
179
180
181
# File 'lib/licensed/sources/go.rb', line 177

def non_vendored_import_path(import_path)
  return unless import_path
  return import_path unless vendored_path?(import_path)
  import_path.split("vendor/")[1]
end

#package_info(import_path) ⇒ Object

Returns a hash of information about the package with a given import path

import_path - Go package import path



186
187
188
# File 'lib/licensed/sources/go.rb', line 186

def package_info(import_path)
  JSON.parse(package_info_command(import_path))
end

#package_info_command(*args) ⇒ Object

Returns package information as a JSON string

args - additional arguments to ‘go list`, e.g. Go package import path



193
194
195
# File 'lib/licensed/sources/go.rb', line 193

def package_info_command(*args)
  Licensed::Shell.execute("go", "list", "-e", "-json", *Array(args)).strip
end

#package_version(package) ⇒ Object

Returns the version for a given package

package - package to get version of



113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/licensed/sources/go.rb', line 113

def package_version(package)
  # use module version if it exists
  go_mod = package["Module"]
  return go_mod["Version"] if go_mod

  package_directory = package["Dir"]
  return unless package_directory

  # find most recent git SHA for a package, or nil if SHA is
  # not available
  Dir.chdir package_directory do
    contents_version *contents_version_arguments
  end
end

#packagesObject

Returns an array of dependency package import paths



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/licensed/sources/go.rb', line 38

def packages
  dependency_packages = if go_version < Gem::Version.new("1.11.0")
    root_package_deps
  else
    go_list_deps
  end

  # don't include go std packages
  # don't include packages under the root project that aren't vendored
  dependency_packages
    .reject { |pkg| go_std_package?(pkg) }
    .reject { |pkg| local_package?(pkg) }
end

#root_packageObject

Returns the info for the package under test



198
199
200
# File 'lib/licensed/sources/go.rb', line 198

def root_package
  @root_package ||= package_info(".")
end

#root_package_depsObject

Returns non-ignored packages found from the root packages “Deps” property



53
54
55
56
57
# File 'lib/licensed/sources/go.rb', line 53

def root_package_deps
  # check for ignored packages to avoid raising errors calling `go list`
  # when ignored package is not found
  Parallel.map(Array(root_package["Deps"])) { |name| package_info(name) }
end

#search_root(package) ⇒ Object

Returns the root directory to search for a package license

package - package object obtained from package_info



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/licensed/sources/go.rb', line 149

def search_root(package)
  return if package.nil?

  # search root choices:
  # 1. module directory if using go modules
  # 2. vendor folder if package is vendored
  # 3. package root value if available
  # 4. GOPATH if the package directory is under the gopath
  # 5. nil
  return package.dig("Module", "Dir") if package["Module"]
  return package["Dir"].match("^(.*/vendor)/.*$")[1] if vendored_path?(package["Dir"])
  return package["Root"] if package["Root"]
  return gopath if package["Dir"]&.start_with?(gopath)
  nil
end

#vendored_path?(path) ⇒ Boolean

Returns whether a package is vendored or not based on the package import_path

path - Package path to test



169
170
171
172
# File 'lib/licensed/sources/go.rb', line 169

def vendored_path?(path)
  return false if path.nil?
  path.start_with?(root_package["ImportPath"]) && path.include?("vendor/")
end