tetra 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
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