tetra 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. data/.gitignore +27 -0
  2. data/.rubocop.yml +14 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +28 -0
  5. data/MOTIVATION.md +27 -0
  6. data/README.md +106 -0
  7. data/Rakefile +9 -0
  8. data/SPECIAL_CASES.md +130 -0
  9. data/bin/tetra +29 -0
  10. data/integration-tests/commons.sh +55 -0
  11. data/lib/template/gitignore +2 -0
  12. data/lib/template/kit.spec +64 -0
  13. data/lib/template/kit/CONTENTS +8 -0
  14. data/lib/template/kit/jars/CONTENTS +1 -0
  15. data/lib/template/kit/m2/settings.xml +10 -0
  16. data/lib/template/output/CONTENTS +3 -0
  17. data/lib/template/package.spec +65 -0
  18. data/lib/template/src/CONTENTS +6 -0
  19. data/lib/tetra.rb +63 -0
  20. data/lib/tetra/ant_runner.rb +27 -0
  21. data/lib/tetra/archiver.rb +95 -0
  22. data/lib/tetra/commands/ant.rb +23 -0
  23. data/lib/tetra/commands/base.rb +89 -0
  24. data/lib/tetra/commands/download_maven_source_jars.rb +29 -0
  25. data/lib/tetra/commands/dry_run.rb +17 -0
  26. data/lib/tetra/commands/finish.rb +22 -0
  27. data/lib/tetra/commands/generate_all.rb +38 -0
  28. data/lib/tetra/commands/generate_kit_archive.rb +18 -0
  29. data/lib/tetra/commands/generate_kit_spec.rb +16 -0
  30. data/lib/tetra/commands/generate_package_archive.rb +19 -0
  31. data/lib/tetra/commands/generate_package_script.rb +21 -0
  32. data/lib/tetra/commands/generate_package_spec.rb +22 -0
  33. data/lib/tetra/commands/get_pom.rb +33 -0
  34. data/lib/tetra/commands/get_source.rb +30 -0
  35. data/lib/tetra/commands/init.rb +15 -0
  36. data/lib/tetra/commands/list_kit_missing_sources.rb +21 -0
  37. data/lib/tetra/commands/move_jars_to_kit.rb +18 -0
  38. data/lib/tetra/commands/mvn.rb +23 -0
  39. data/lib/tetra/git.rb +140 -0
  40. data/lib/tetra/kit_checker.rb +104 -0
  41. data/lib/tetra/kit_runner.rb +43 -0
  42. data/lib/tetra/kit_spec_adapter.rb +28 -0
  43. data/lib/tetra/logger.rb +28 -0
  44. data/lib/tetra/main.rb +102 -0
  45. data/lib/tetra/maven_runner.rb +47 -0
  46. data/lib/tetra/maven_website.rb +59 -0
  47. data/lib/tetra/package_spec_adapter.rb +59 -0
  48. data/lib/tetra/pom.rb +55 -0
  49. data/lib/tetra/pom_getter.rb +104 -0
  50. data/lib/tetra/project.rb +245 -0
  51. data/lib/tetra/script_generator.rb +57 -0
  52. data/lib/tetra/source_getter.rb +41 -0
  53. data/lib/tetra/spec_generator.rb +60 -0
  54. data/lib/tetra/template_manager.rb +33 -0
  55. data/lib/tetra/version.rb +6 -0
  56. data/lib/tetra/version_matcher.rb +90 -0
  57. data/spec/data/ant-super-simple-code/build.xml +133 -0
  58. data/spec/data/ant-super-simple-code/build/HW.class +0 -0
  59. data/spec/data/ant-super-simple-code/build/mypackage/HW.class +0 -0
  60. data/spec/data/ant-super-simple-code/dist/antsimple-20130618.jar +0 -0
  61. data/spec/data/ant-super-simple-code/lib/junit-4.11.jar +0 -0
  62. data/spec/data/ant-super-simple-code/lib/log4j-1.2.13.jar +0 -0
  63. data/spec/data/ant-super-simple-code/src/mypackage/HW.java +15 -0
  64. data/spec/data/antlr/antlr-2.7.2.jar +0 -0
  65. data/spec/data/antlr/pom.xml +6 -0
  66. data/spec/data/commons-logging/commons-logging-1.1.1.jar +0 -0
  67. data/spec/data/commons-logging/parent_pom.xml +420 -0
  68. data/spec/data/commons-logging/pom.xml +504 -0
  69. data/spec/data/nailgun/nailgun-0.7.1.jar +0 -0
  70. data/spec/data/nailgun/pom.xml +153 -0
  71. data/spec/data/struts-apps/pom.xml +228 -0
  72. data/spec/data/tomcat/pom.xml +33 -0
  73. data/spec/lib/ant_runner_spec.rb +45 -0
  74. data/spec/lib/archiver_spec.rb +106 -0
  75. data/spec/lib/git_spec.rb +105 -0
  76. data/spec/lib/kit_checker_spec.rb +119 -0
  77. data/spec/lib/maven_runner_spec.rb +68 -0
  78. data/spec/lib/maven_website_spec.rb +56 -0
  79. data/spec/lib/pom_getter_spec.rb +36 -0
  80. data/spec/lib/pom_spec.rb +69 -0
  81. data/spec/lib/project_spec.rb +254 -0
  82. data/spec/lib/script_generator_spec.rb +67 -0
  83. data/spec/lib/source_getter_spec.rb +36 -0
  84. data/spec/lib/spec_generator_spec.rb +130 -0
  85. data/spec/lib/template_manager_spec.rb +54 -0
  86. data/spec/lib/version_matcher_spec.rb +64 -0
  87. data/spec/spec_helper.rb +37 -0
  88. data/spec/support/kit_runner_examples.rb +15 -0
  89. data/tetra.gemspec +31 -0
  90. data/utils/delete_nonet_user.sh +8 -0
  91. data/utils/setup_nonet_user.sh +8 -0
  92. metadata +267 -0
@@ -0,0 +1,104 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # checks kits for errors
5
+ class KitChecker
6
+ include Logging
7
+
8
+ def initialize(project)
9
+ @project = project
10
+ end
11
+
12
+ # returns an array of [path, archive] couples found in kit/
13
+ # archive is not nil if path is inside a zip file
14
+ def kit_file_paths
15
+ @project.from_directory("kit") do
16
+ plain_file_paths = Dir[File.join("**", "*")].select do |path|
17
+ File.file?(path)
18
+ end.map do |path|
19
+ [path, nil]
20
+ end
21
+
22
+ archived_file_paths = plain_file_paths.select do |path, _archive|
23
+ path. =~ (/\.(zip)|([jwe]ar)$/)
24
+ end.map do |path, _archive|
25
+ result = []
26
+ Zip::File.foreach(path) do |entry|
27
+ result << [entry.to_s, path] if entry.file?
28
+ end
29
+ result
30
+ end.flatten(1)
31
+
32
+ plain_file_paths + archived_file_paths
33
+ end
34
+ end
35
+
36
+ # returns a list of class names for which
37
+ # we have source files in kit/
38
+ def source_class_names(paths)
39
+ source_paths = paths.select do |path, _archive|
40
+ path =~ /\.java$/
41
+ end
42
+
43
+ # heuristically add all possible package names, walking
44
+ # back the directory tree all the way back to root.
45
+ # This could add non-existent names, but allows not looking
46
+ # in the file at all
47
+ class_names = source_paths.map do |path, _archive|
48
+ class_name = path_to_class(path)
49
+ parts = class_name.split(".")
50
+ last_index = parts.length - 1
51
+ (0..last_index).map do |i|
52
+ parts[i..last_index].join(".")
53
+ end
54
+ end.flatten
55
+
56
+ Set.new(class_names)
57
+ end
58
+
59
+ # returns a list of class names for which
60
+ # we have binary files in kit/
61
+ def compiled_classes(paths)
62
+ result = {}
63
+ paths.select do |path, _archive|
64
+ path =~ /\.class$/
65
+ end.each do |path, archive|
66
+ class_name = path_to_class(path)
67
+ if result[archive].nil?
68
+ result[archive] = [class_name]
69
+ else
70
+ result[archive] << class_name
71
+ end
72
+ end
73
+
74
+ result
75
+ end
76
+
77
+ # returns a hash that associates archive names and
78
+ # the unsourced classes within them
79
+ def unsourced_archives
80
+ paths = kit_file_paths
81
+ source_class_names = source_class_names(paths)
82
+ archive_paths_to_class_names = compiled_classes(paths)
83
+
84
+ archive_paths_to_class_names.map do |archive, class_names|
85
+ unsourced_class_names = class_names.select do |class_name|
86
+ source_class_names.include?(class_name) == false
87
+ end
88
+ { archive: archive, class_names: class_names, unsourced_class_names: unsourced_class_names }
89
+ end.select do |archive|
90
+ archive[:unsourced_class_names].any?
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # attempts to turn a .java/.class file name into a class name
97
+ def path_to_class(path)
98
+ path
99
+ .gsub(/\$.+?\./, ".") # remove $InnerClasses
100
+ .gsub(/\..+?$/, "") # remove .extension
101
+ .gsub("/", ".") # convert paths in package names
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # runs programs from a tetra kit with tetra-specific options
5
+ class KitRunner
6
+ include Logging
7
+
8
+ def initialize(project)
9
+ @project = project
10
+ end
11
+
12
+ # finds an executable in a bin/ subdirectory of kit
13
+ def find_executable(name)
14
+ @project.from_directory do
15
+ Find.find("kit") do |path|
16
+ next unless path =~ /bin\/#{name}$/
17
+
18
+ log.debug("found #{name} executable: #{path}")
19
+ return path
20
+ end
21
+ end
22
+
23
+ log.debug("#{name} executable not found")
24
+ nil
25
+ end
26
+
27
+ # runs an external executable, returns true on success
28
+ def run_executable(full_commandline)
29
+ log.debug "running #{full_commandline}"
30
+ Process.wait(Process.spawn(full_commandline))
31
+ $CHILD_STATUS.exitstatus == 0
32
+ end
33
+ end
34
+
35
+ # an executable from the kit was not found
36
+ class ExecutableNotFoundError < Exception
37
+ attr_reader :executable
38
+
39
+ def initialize(executable)
40
+ @executable = executable
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # encapsulates details of a kit needed by the spec file
5
+ # retrieving them from other objects
6
+ class KitSpecAdapter
7
+ attr_reader :name
8
+ attr_reader :version
9
+ attr_reader :archives
10
+
11
+ def initialize(project)
12
+ @name = project.name
13
+ @version = project.version
14
+
15
+ @archives =
16
+ project.from_directory do
17
+ ["#{name}-kit.tar.xz"] +
18
+ Dir.entries("output/#{name}-kit")
19
+ .select { |f| f =~ /_[0-9]+.tar.xz$/ }
20
+ .sort
21
+ end
22
+ end
23
+
24
+ def public_binding
25
+ binding
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # configures logging for this application
5
+ class Logger
6
+ include Singleton
7
+ extend Forwardable
8
+
9
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal, :level=
10
+
11
+ def initialize
12
+ @logger = ::Logger.new(STDERR)
13
+ @logger.datetime_format = "%Y-%m-%d %H:%M "
14
+ @logger.level = ::Logger::ERROR
15
+ @logger.formatter = proc do |severity, _datetime, _progname, msg|
16
+ "#{severity.chars.first}: #{msg}\n"
17
+ end
18
+ end
19
+ end
20
+
21
+ # convenience methods
22
+ module Logging
23
+ # convenience instance method
24
+ def log
25
+ Tetra::Logger.instance
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # program entry point
5
+ class MainCommand < Clamp::Command
6
+ subcommand(
7
+ "init",
8
+ "Inits a tetra project in the current directory",
9
+ Tetra::InitCommand
10
+ )
11
+
12
+ subcommand(
13
+ "dry-run",
14
+ "Starts a dry-run build",
15
+ Tetra::DryRunCommand
16
+ )
17
+
18
+ subcommand(
19
+ "mvn",
20
+ "Locates and runs Maven from any directory in kit/",
21
+ Tetra::MavenCommand
22
+ )
23
+
24
+ subcommand(
25
+ "ant",
26
+ "Locates and runs Ant from any directory in kit/",
27
+ Tetra::AntCommand
28
+ )
29
+
30
+ subcommand(
31
+ "finish",
32
+ "Ends the current dry-run",
33
+ Tetra::FinishCommand
34
+ )
35
+
36
+ subcommand(
37
+ "generate-kit-archive",
38
+ "Create or refresh the kit tarball",
39
+ Tetra::GenerateKitArchiveCommand
40
+ )
41
+
42
+ subcommand(
43
+ "generate-kit-spec",
44
+ "Create or refresh a spec file for the kit",
45
+ Tetra::GenerateKitSpecCommand
46
+ )
47
+
48
+ subcommand(
49
+ "generate-package-script",
50
+ "Create or refresh a build.sh file for a package",
51
+ Tetra::GeneratePackageScriptCommand
52
+ )
53
+
54
+ subcommand(
55
+ "generate-package-archive",
56
+ "Create or refresh a package tarball",
57
+ Tetra::GeneratePackageArchiveCommand
58
+ )
59
+
60
+ subcommand(
61
+ "generate-package-spec",
62
+ "Create or refresh a spec file for a package",
63
+ Tetra::GeneratePackageSpecCommand
64
+ )
65
+
66
+ subcommand(
67
+ "generate-all",
68
+ "Create or refresh specs, archives, scripts for a package and the kit",
69
+ Tetra::GenerateAllCommand
70
+ )
71
+
72
+ subcommand(
73
+ "move-jars-to-kit",
74
+ "Locates jars in src/ and moves them to kit/",
75
+ Tetra::MoveJarsToKitCommand
76
+ )
77
+
78
+ subcommand(
79
+ "download-maven-source-jars",
80
+ "Attempts to download Maven kit/ sources",
81
+ Tetra::DownloadMavenSourceJarsCommand
82
+ )
83
+
84
+ subcommand(
85
+ "get-pom",
86
+ "Retrieves a pom file",
87
+ Tetra::GetPomCommand
88
+ )
89
+
90
+ subcommand(
91
+ "get-source",
92
+ "Attempts to retrieve a project's sources",
93
+ Tetra::GetSourceCommand
94
+ )
95
+
96
+ subcommand(
97
+ "list-kit-missing-sources",
98
+ "Locates jars in kit/ that have no source files",
99
+ Tetra::ListKitMissingSourcesCommand
100
+ )
101
+ end
102
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # runs Maven with tetra-specific options
5
+ class MavenRunner < KitRunner
6
+ include Logging
7
+
8
+ # runs Maven in a subprocess
9
+ def mvn(options)
10
+ run_executable(get_maven_commandline(@project.full_path, options))
11
+ end
12
+
13
+ # runs Maven to attempt getting a source jar
14
+ # returns true if successful
15
+ def get_source_jar(group_id, artifact_id, version)
16
+ mvn(["dependency:get", "-Dartifact=#{group_id}:#{artifact_id}:#{version}:jar:sources", "-Dtransitive=false"])
17
+ end
18
+
19
+ # runs Maven to get the effective POM from an existing POM
20
+ # returns effective pom path or nil if not found
21
+ def get_effective_pom(pom_path)
22
+ effective_pom_path = "#{pom_path}.effective"
23
+ success = mvn(["help:effective-pom", "-f#{pom_path}", "-Doutput=#{File.split(effective_pom_path)[1]}"])
24
+ if success
25
+ effective_pom_path
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ # returns a command line for running Maven from the specified
32
+ # prefix
33
+ def get_maven_commandline(prefix, options)
34
+ executable = find_executable("mvn")
35
+
36
+ if !executable.nil?
37
+ mvn_path = File.join(prefix, executable)
38
+ repo_path = File.join(prefix, "kit", "m2")
39
+ config_path = File.join(prefix, "kit", "m2", "settings.xml")
40
+
41
+ "#{mvn_path} -Dmaven.repo.local=#{repo_path} -s#{config_path} #{options.join(" ")}"
42
+ else
43
+ fail ExecutableNotFoundError, "mvn"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # Facade to search.maven.org
5
+ class MavenWebsite
6
+ include Logging
7
+
8
+ # returns a search result object from search.maven.com
9
+ # searching by a jar sha1 hash
10
+ # see output format at http://search.maven.org/#api
11
+ def search_by_sha1(sha1)
12
+ search(q: "1:\"#{sha1}\"")
13
+ end
14
+
15
+ # returns a search result object from search.maven.com
16
+ # searching by keyword (name)
17
+ # see output format at http://search.maven.org/#api
18
+ def search_by_name(name)
19
+ search(q: name)
20
+ end
21
+
22
+ # returns a search result object from search.maven.com
23
+ # searching by Maven's group id and artifact id
24
+ # see output format at http://search.maven.org/#api
25
+ def search_by_group_id_and_artifact_id(group_id, artifact_id)
26
+ search(q: "g:\"#{group_id}\" AND a:\"#{artifact_id}\"", core: "gav")
27
+ end
28
+
29
+ # returns a search result object from search.maven.com
30
+ # searching by Maven's id (group id, artifact id and version)
31
+ # see output format at http://search.maven.org/#api
32
+ def search_by_maven_id(group_id, artifact_id, version)
33
+ search(q: "g:\"#{group_id}\" AND a:\"#{artifact_id}\" AND v:\"#{version}\"")
34
+ end
35
+
36
+ # returns a search result object from search.maven.com
37
+ # see input and output format at http://search.maven.org/#api
38
+ def search(params)
39
+ response = RestClient.get("http://search.maven.org/solrsearch/select",
40
+ params: params.merge("rows" => "100", "wt" => "json")
41
+ )
42
+ json = JSON.parse(response.to_s)
43
+ json["response"]["docs"]
44
+ end
45
+
46
+ # returns a Maven's triple (artifactId, groupId, version)
47
+ # from a result object
48
+ def get_maven_id_from(result)
49
+ [result["g"], result["a"], result["v"]]
50
+ end
51
+
52
+ # downloads a POM from a search.maven.com search result
53
+ def download_pom(group_id, artifact_id, version)
54
+ path = "#{group_id.gsub(".", "/")}/#{artifact_id}/#{version}/#{artifact_id}-#{version}.pom"
55
+ log.debug("downloading #{path}...")
56
+ (RestClient.get "http://search.maven.org/remotecontent", params: { filepath: path }).to_s
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tetra
4
+ # encapsulates details of a package needed by the spec file
5
+ # retrieving them from other objects
6
+ class PackageSpecAdapter
7
+ attr_reader :name
8
+ attr_reader :version
9
+ attr_reader :license
10
+ attr_reader :summary
11
+ attr_reader :url
12
+ attr_reader :project_name
13
+ attr_reader :project_version
14
+ attr_reader :group_id
15
+ attr_reader :artifact_id
16
+ attr_reader :version
17
+ attr_reader :runtime_dependency_ids
18
+ attr_reader :description
19
+ attr_reader :outputs
20
+
21
+ def initialize(project, package_name, pom, filter)
22
+ @name = package_name
23
+ @version = pom.version
24
+ @license = (
25
+ if pom.license_name != ""
26
+ pom.license_name
27
+ else
28
+ "Apache-2.0"
29
+ end
30
+ )
31
+ @summary = cleanup_description(pom.description, 60)
32
+ @url = pom.url
33
+ @project_name = project.name
34
+ @project_version = project.version
35
+ @group_id = pom.group_id
36
+ @artifact_id = pom.artifact_id
37
+ @version = pom.version
38
+ @runtime_dependency_ids = pom.runtime_dependency_ids
39
+ @description = cleanup_description(pom.description, 1500)
40
+
41
+ @outputs = project.get_produced_files(package_name).select do |file|
42
+ File.fnmatch? filter, File.basename(file)
43
+ end
44
+ end
45
+
46
+ def public_binding
47
+ binding
48
+ end
49
+
50
+ def cleanup_description(raw, max_length)
51
+ raw
52
+ .gsub(/[\s]+/, " ")
53
+ .strip
54
+ .slice(0..max_length - 1)
55
+ .sub(/\s\w+$/, "")
56
+ .sub(/\.+$/, "")
57
+ end
58
+ end
59
+ end