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



136
137
138
139
140
141
142
# File 'lib/licensed/sources/go.rb', line 136

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

#enabled?Boolean

Returns:

  • (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
36
# File 'lib/licensed/sources/go.rb', line 15

def enumerate_dependencies
  with_configured_gopath do
    packages.map do |package|
      import_path = non_vendored_path(package["ImportPath"], root_package["ImportPath"])
      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



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

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

Returns:

  • (Boolean)


210
211
212
# File 'lib/licensed/sources/go.rb', line 210

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

Returns:

  • (Boolean)


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

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}")
  return true if go_std_packages.include?(import_path.sub("golang.org", "internal"))

  # additional checks are only for vendored dependencies - return false
  # if package isn't vendored
  non_vendored_import_path = non_vendored_path(import_path, root_package["ImportPath"])
  return false unless non_vendored_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)
  return true if go_std_packages.include?(non_vendored_import_path.sub("golang.org", "internal"))

  # 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



215
216
217
# File 'lib/licensed/sources/go.rb', line 215

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



233
234
235
236
237
238
239
# File 'lib/licensed/sources/go.rb', line 233

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



221
222
223
224
225
226
227
228
229
230
# File 'lib/licensed/sources/go.rb', line 221

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.



145
146
147
148
# File 'lib/licensed/sources/go.rb', line 145

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

Returns:

  • (Boolean)


108
109
110
111
112
# File 'lib/licensed/sources/go.rb', line 108

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

#non_vendored_path(path, base) ⇒ Object

Returns the path parameter without the vendor component if one is found

path - Package path with vendor component base - The base path that the input must start with



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

def non_vendored_path(path, base)
  return unless path
  return unless vendored_path?(path, base)
  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



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

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



200
201
202
# File 'lib/licensed/sources/go.rb', line 200

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



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/licensed/sources/go.rb', line 117

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 && File.exist?(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



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

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



205
206
207
# File 'lib/licensed/sources/go.rb', line 205

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

#root_package_depsObject

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



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

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



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/licensed/sources/go.rb', line 153

def search_root(package)
  return if package.nil?

  # search root choices:
  # 1. module directory if using go modules and directory is available
  # 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
  module_dir = package.dig("Module", "Dir")
  return module_dir if module_dir
  return package["Dir"].match("^(.*/vendor)/.*$")[1] if vendored_path?(package["Dir"], config.root)
  return package["Root"] if package["Root"]
  return gopath if package["Dir"]&.start_with?(gopath)
  nil
end

#vendored_path?(path, base) ⇒ Boolean

Returns whether a package is vendored or not based on a base path and whether the path contains a vendor component

path - Package path to test base - The base path that the input must start with

Returns:

  • (Boolean)


175
176
177
178
# File 'lib/licensed/sources/go.rb', line 175

def vendored_path?(path, base)
  return false if path.nil? || base.nil?
  path.start_with?(base.to_s) && path.include?("vendor/")
end