tetra 0.51.0 → 0.52.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +16 -10
- data/SPECIAL_CASES.md +3 -3
- data/lib/tetra.rb +1 -0
- data/lib/tetra/facades/bash.rb +28 -0
- data/lib/tetra/facades/process_runner.rb +8 -1
- data/lib/tetra/packages/package.rb +2 -2
- data/lib/tetra/packages/scriptable.rb +9 -16
- data/lib/tetra/project.rb +14 -3
- data/lib/tetra/ui/dry_run_subcommand.rb +16 -41
- data/lib/tetra/ui/generate_script_subcommand.rb +1 -2
- data/lib/tetra/ui/init_subcommand.rb +1 -1
- data/lib/tetra/ui/subcommand.rb +2 -2
- data/lib/tetra/version.rb +1 -1
- data/spec/lib/package_spec.rb +2 -2
- data/spec/lib/project_spec.rb +6 -6
- data/spec/lib/scriptable_spec.rb +9 -10
- metadata +3 -2
data/README.md
CHANGED
@@ -12,6 +12,7 @@ You need:
|
|
12
12
|
|
13
13
|
* [Ruby 1.9](https://www.ruby-lang.org/en/);
|
14
14
|
* [git](http://git-scm.com/);
|
15
|
+
* bash;
|
15
16
|
* a JDK that can compile whatever software you need to package;
|
16
17
|
|
17
18
|
Install `tetra` via RubyGems:
|
@@ -23,15 +24,20 @@ Install `tetra` via RubyGems:
|
|
23
24
|
Building a package with `tetra` is quite unusual — this is a deliberate choice, so don't worry. Basic steps are:
|
24
25
|
|
25
26
|
* `tetra init` a new project;
|
26
|
-
* add sources to `src/<package name>` and anything else needed for the build in `kit/` in binary form (
|
27
|
-
* execute `tetra dry-run
|
28
|
-
* execute `tetra generate-all`: tetra will
|
27
|
+
* add sources to `src/<package name>` and anything else needed for the build in `kit/` in binary form (Ant and Maven are already included);
|
28
|
+
* execute `tetra dry-run`, which will open a bash subshell. In there, build your project, and when you are done conclude quitting it with `Ctrl+D`;
|
29
|
+
* execute `tetra generate-all`: tetra will scaffold spec files and tarballs.
|
29
30
|
|
30
31
|
Done!
|
31
32
|
|
32
33
|
### How can that possibly work?
|
33
34
|
|
34
|
-
|
35
|
+
During the dry-run `tetra`:
|
36
|
+
- saves your bash history, so that it can use it later to scaffold a build script;
|
37
|
+
- keeps track of changed files, in particular produced jars, which are included in the spec's `%files` section;
|
38
|
+
- saves files downloaded from the Internet (eg. by Maven) and packs them to later allow networkless builds.
|
39
|
+
|
40
|
+
Note that with `tetra` you are not building all dependencies from source - build dependencies are aggregated in a binary-only "blob" package. While this is not ideal it is sufficient to fulfill most open source licenses and to have a repeatable, networkless build, while being a lot easier to automate.
|
35
41
|
|
36
42
|
## A commons-collections walkthrough
|
37
43
|
|
@@ -48,18 +54,18 @@ Second, place source files in the `src/` folder:
|
|
48
54
|
unzip commons-collections-3.2.1-src.zip
|
49
55
|
rm commons-collections-3.2.1-src.zip
|
50
56
|
|
51
|
-
Third, you need to show `tetra` how to build your package
|
57
|
+
Third, you need to show `tetra` how to build your package. Run `tetra dry-run` a new subshell will open, in there do anything you would normally do to build the package:
|
52
58
|
|
53
59
|
cd ../src
|
54
|
-
tetra dry-run
|
60
|
+
tetra dry-run
|
55
61
|
|
56
62
|
cd commons-collections-3.2.1-src/
|
57
63
|
tetra mvn package
|
58
64
|
|
59
|
-
|
65
|
+
^D
|
60
66
|
|
61
67
|
Note that we used `tetra mvn package` instead of `mvn package`: this will use a preloaded Maven bundled in `kit/` by default and the repository in `kit/m2`.
|
62
|
-
Also note that this being a dry-run build, sources will be brought back to their original state after
|
68
|
+
Also note that this being a dry-run build, sources will be brought back to their original state after it finishes to ensure repeatability.
|
63
69
|
|
64
70
|
Finally, generate build scripts, spec files and tarballs in the `packages/` directory:
|
65
71
|
|
@@ -75,9 +81,9 @@ An in-depth discussion of this project's motivation is available in the [MOTIVAT
|
|
75
81
|
|
76
82
|
## Status
|
77
83
|
|
78
|
-
`tetra`
|
84
|
+
`tetra` will soon hit 1.0. If you are a packager you can try to use it, any feedback would be **very** welcome!
|
79
85
|
|
80
|
-
At the moment `tetra` is tested on openSUSE.
|
86
|
+
At the moment `tetra` is tested on openSUSE and Ubuntu.
|
81
87
|
|
82
88
|
## Sources
|
83
89
|
|
data/SPECIAL_CASES.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Special cases
|
2
2
|
|
3
|
-
## Failing
|
3
|
+
## Failing dry-runs
|
4
4
|
|
5
|
-
If your build fails for whatever reason, abort it with `
|
5
|
+
If your build fails for whatever reason during a dry run, abort it with `^C^D` (`Ctrl+C` followed by `Ctrl+D`). `tetra` will restore all project files as they were before the build.
|
6
6
|
|
7
7
|
## Generating files multiple times
|
8
8
|
|
@@ -12,7 +12,7 @@ You can edit any file generated by tetra - regenerating it later will not overwr
|
|
12
12
|
|
13
13
|
You can generate single files with the following commands:
|
14
14
|
|
15
|
-
* `tetra generate-script`: (re)generates the `build.sh` file from
|
15
|
+
* `tetra generate-script`: (re)generates the `build.sh` file from commands used in the latest successful dry-run;
|
16
16
|
* `tetra generate-archive`: (re)generates the package tarball;
|
17
17
|
* `tetra generate-spec`: (re)generates the package spec;
|
18
18
|
* `tetra generate-kit`: (re)generates the kit tarball and spec;
|
data/lib/tetra.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Tetra
|
4
|
+
# runs Bash with tetra-specific options
|
5
|
+
class Bash
|
6
|
+
include ProcessRunner
|
7
|
+
|
8
|
+
# runs bash in a subshell, returns list of
|
9
|
+
# commands that were run in the session
|
10
|
+
def bash
|
11
|
+
Tempfile.open("tetra-history") do |temp_file|
|
12
|
+
temp_path = temp_file.path
|
13
|
+
|
14
|
+
env = {
|
15
|
+
"HISTFILE" => temp_path, # use temporary file for history
|
16
|
+
"HISTFILESIZE" => "-1", # don't limit file size
|
17
|
+
"HISTSIZE" => "-1", # don't limit history size
|
18
|
+
"HISTTIMEFORMAT" => nil, # don't keep timestamps
|
19
|
+
"HISTCONTROL" => "", # don't skip any command
|
20
|
+
"PS1" => "\e[1;33mdry-running\e[m:\\\w\$ " # change prompt
|
21
|
+
}
|
22
|
+
|
23
|
+
run_interactive("bash --norc", env)
|
24
|
+
File.read(temp_path).split("\n").map(&:strip)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -5,7 +5,7 @@ module Tetra
|
|
5
5
|
module ProcessRunner
|
6
6
|
include Logging
|
7
7
|
|
8
|
-
# runs
|
8
|
+
# runs a noninteractive executable and returns its output as a string
|
9
9
|
# raises ExecutionFailed if the exit status is not 0
|
10
10
|
# optionally echoes the executable's output/error to standard output/error
|
11
11
|
def run(commandline, echo = false, stdin = nil)
|
@@ -34,6 +34,13 @@ module Tetra
|
|
34
34
|
out_recorder.record
|
35
35
|
end
|
36
36
|
|
37
|
+
# runs an interactive executable in a subshell
|
38
|
+
# changing environment variables
|
39
|
+
def run_interactive(command, env)
|
40
|
+
success = system(env, command)
|
41
|
+
fail ExecutionFailed.new(command, $CHILD_STATUS, nil, nil) unless success
|
42
|
+
end
|
43
|
+
|
37
44
|
# records bytes sent via "<<" for later use
|
38
45
|
# optionally echoes to another IO object
|
39
46
|
class RecordingIO
|
@@ -4,23 +4,14 @@ module Tetra
|
|
4
4
|
# generates a package build script from bash_history
|
5
5
|
module Scriptable
|
6
6
|
# returns a build script for this package
|
7
|
-
def _to_script(project
|
7
|
+
def _to_script(project)
|
8
8
|
project.from_directory do
|
9
|
-
history_lines = File.readlines(history_path).map(&:strip)
|
10
|
-
relevant_lines =
|
11
|
-
history_lines
|
12
|
-
.reverse
|
13
|
-
.take_while { |e| e.match(/tetra +dry-run +start/).nil? }
|
14
|
-
.reverse
|
15
|
-
.take_while { |e| e.match(/tetra +dry-run +finish/).nil? }
|
16
|
-
.select { |e| e.match(/^#/).nil? }
|
17
|
-
|
18
9
|
script_lines = [
|
19
10
|
"#!/bin/bash",
|
20
11
|
"set -xe",
|
21
12
|
"PROJECT_PREFIX=`readlink -e .`",
|
22
13
|
"cd #{project.latest_dry_run_directory}"
|
23
|
-
] + script_body(project
|
14
|
+
] + script_body(project)
|
24
15
|
|
25
16
|
new_content = script_lines.join("\n") + "\n"
|
26
17
|
|
@@ -34,19 +25,21 @@ module Tetra
|
|
34
25
|
end
|
35
26
|
end
|
36
27
|
|
37
|
-
# returns the script body
|
38
|
-
|
39
|
-
|
28
|
+
# returns the script body by taking the last dry-run's
|
29
|
+
# build script lines and adjusting mvn and ant's paths
|
30
|
+
def script_body(project)
|
31
|
+
lines = project.build_script_lines
|
32
|
+
ant = if lines.any? { |e| e.match(/tetra +ant/) }
|
40
33
|
path = Tetra::Kit.new(project).find_executable("ant")
|
41
34
|
Tetra::Ant.new(project.full_path, path).ant(@options)
|
42
35
|
end
|
43
36
|
|
44
|
-
mvn = if
|
37
|
+
mvn = if lines.any? { |e| e.match(/tetra +mvn/) }
|
45
38
|
mvn_path = Tetra::Kit.new(project).find_executable("mvn")
|
46
39
|
mvn = Tetra::Mvn.new("$PROJECT_PREFIX", mvn_path)
|
47
40
|
end
|
48
41
|
|
49
|
-
|
42
|
+
lines.map do |line|
|
50
43
|
if line =~ /tetra +mvn/
|
51
44
|
line.gsub(/tetra +mvn/, "#{mvn.get_mvn_commandline(['-o'])}")
|
52
45
|
elsif line =~ /tetra +ant/
|
data/lib/tetra/project.rb
CHANGED
@@ -114,9 +114,11 @@ module Tetra
|
|
114
114
|
!latest_comment.nil? && !(latest_comment =~ /tetra: dry-run-finished/)
|
115
115
|
end
|
116
116
|
|
117
|
-
# ends a dry-run assuming a successful build
|
118
|
-
# reverts sources
|
119
|
-
|
117
|
+
# ends a dry-run assuming a successful build:
|
118
|
+
# - reverts sources as before dry-run
|
119
|
+
# - saves the list of generated files in git comments
|
120
|
+
# - saves the build script lines in git comments
|
121
|
+
def finish(build_script_lines)
|
120
122
|
# keep track of changed files
|
121
123
|
start_id = @git.latest_id("tetra: dry-run-started")
|
122
124
|
changed_files = @git.changed_files("src", start_id)
|
@@ -127,6 +129,7 @@ module Tetra
|
|
127
129
|
# prepare commit comments
|
128
130
|
comments = ["Dry run finished\n", "tetra: dry-run-finished"]
|
129
131
|
comments += changed_files.map { |f| "tetra: file-changed: #{f}" }
|
132
|
+
comments += build_script_lines.map { |l| "tetra: build-script-line: #{l}" }
|
130
133
|
|
131
134
|
# if this is the first dry-run, mark sources as tarball
|
132
135
|
if @git.latest_id("tetra: dry-run-finished").nil?
|
@@ -228,6 +231,14 @@ module Tetra
|
|
228
231
|
.sort
|
229
232
|
end
|
230
233
|
|
234
|
+
def build_script_lines
|
235
|
+
@git.latest_comment("tetra: dry-run-finished")
|
236
|
+
.split("\n")
|
237
|
+
.map { |line| line[/^tetra: build-script-line: (.+)$/, 1] }
|
238
|
+
.compact
|
239
|
+
.sort
|
240
|
+
end
|
241
|
+
|
231
242
|
# archives a tarball of src/ in packages/
|
232
243
|
# the latest commit marked as tarball is taken as the version
|
233
244
|
def archive_sources
|
@@ -3,55 +3,30 @@
|
|
3
3
|
module Tetra
|
4
4
|
# tetra dry-run
|
5
5
|
class DryRunSubcommand < Tetra::Subcommand
|
6
|
-
parameter "COMMAND", "\"start\" to begin, \"finish\" to end or \"abort\" to undo changes" do |command|
|
7
|
-
if %w(start finish abort).include?(command)
|
8
|
-
command
|
9
|
-
else
|
10
|
-
fail ArgumentError, "\"#{command}\" is not valid, must be one of \"start\", \"finish\" or \"abort\""
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
6
|
def execute
|
15
7
|
checking_exceptions do
|
16
8
|
project = Tetra::Project.new(".")
|
17
|
-
send(command, project)
|
18
|
-
end
|
19
|
-
end
|
20
9
|
|
21
|
-
def start(project)
|
22
|
-
if !project.dry_running?
|
23
10
|
if project.src_patched?
|
24
|
-
puts "Some files in src/ were changed since last dry-run
|
25
|
-
puts "
|
26
|
-
puts "Dry run not started
|
11
|
+
puts "Some files in src/ were changed since last dry-run,"
|
12
|
+
puts "use \"tetra patch message\" to include changes in a patch before dry-running."
|
13
|
+
puts "Dry run not started"
|
27
14
|
else
|
28
15
|
project.dry_run
|
29
|
-
puts "
|
30
|
-
puts "
|
31
|
-
puts "If the build succeedes end this dry run with
|
32
|
-
puts "
|
33
|
-
end
|
34
|
-
else
|
35
|
-
puts "Dry-run already in progress."
|
36
|
-
puts "Use \"tetra dry-run finish\" to end it or \"tetra dry-run abort\" to undo changes."
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def finish(project)
|
41
|
-
if project.dry_running?
|
42
|
-
project.finish
|
43
|
-
puts "Dry-run finished."
|
44
|
-
else
|
45
|
-
puts "No dry-run is in progress."
|
46
|
-
end
|
47
|
-
end
|
16
|
+
puts "Dry-run started in a new bash shell."
|
17
|
+
puts "Build your project now, you can use \"tetra mvn\" and \"tetra ant\"."
|
18
|
+
puts "If the build succeedes end this dry run with ^D (Ctrl+D),"
|
19
|
+
puts "if the build does not succeed use ^C^D to abort and undo any change"
|
48
20
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
21
|
+
begin
|
22
|
+
history = Bash.new.bash
|
23
|
+
project.finish(history)
|
24
|
+
puts "Dry-run finished"
|
25
|
+
rescue ExecutionFailed
|
26
|
+
project.abort
|
27
|
+
puts "Project reverted as before dry-run"
|
28
|
+
end
|
29
|
+
end
|
55
30
|
end
|
56
31
|
end
|
57
32
|
end
|
@@ -7,9 +7,8 @@ module Tetra
|
|
7
7
|
checking_exceptions do
|
8
8
|
project = Tetra::Project.new(".")
|
9
9
|
ensure_dry_running(:has_finished, project) do
|
10
|
-
history = File.join(Dir.home, ".bash_history")
|
11
10
|
result_path, conflict_count =
|
12
|
-
Tetra::Package.new(project).to_script
|
11
|
+
Tetra::Package.new(project).to_script
|
13
12
|
print_generation_result(project, result_path, conflict_count)
|
14
13
|
end
|
15
14
|
end
|
@@ -8,7 +8,7 @@ module Tetra
|
|
8
8
|
Tetra::Project.init(".")
|
9
9
|
puts "Project inited."
|
10
10
|
puts "Add sources to src/, binary dependencies to kit/."
|
11
|
-
puts "When you are ready to test a build, use \"tetra dry-run\"
|
11
|
+
puts "When you are ready to test a build, use \"tetra dry-run\""
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/tetra/ui/subcommand.rb
CHANGED
@@ -59,10 +59,10 @@ module Tetra
|
|
59
59
|
else
|
60
60
|
if (state == :is_in_progress) ||
|
61
61
|
(state == :has_finished && !dry_running && !has_finished)
|
62
|
-
puts "Please start a dry-run first, use \"tetra dry-run
|
62
|
+
puts "Please start a dry-run first, use \"tetra dry-run\""
|
63
63
|
elsif (state == :is_not_in_progress) ||
|
64
64
|
(state == :has_finished && dry_running)
|
65
|
-
puts "
|
65
|
+
puts "There is a dry-run in progress, please finish it (^D) or abort it (^C^D)"
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
data/lib/tetra/version.rb
CHANGED
data/spec/lib/package_spec.rb
CHANGED
@@ -11,7 +11,7 @@ describe Tetra::Package do
|
|
11
11
|
Dir.chdir(@project_path) do
|
12
12
|
FileUtils.touch(File.join("kit", "jars", "test.jar"))
|
13
13
|
end
|
14
|
-
@project.finish
|
14
|
+
@project.finish([])
|
15
15
|
|
16
16
|
@project.from_directory do
|
17
17
|
FileUtils.mkdir_p(File.join("src", "out"))
|
@@ -28,7 +28,7 @@ describe Tetra::Package do
|
|
28
28
|
FileUtils.touch(File.join("src", "out", "test#{i}.jar"))
|
29
29
|
end
|
30
30
|
|
31
|
-
@project.finish
|
31
|
+
@project.finish([])
|
32
32
|
end
|
33
33
|
|
34
34
|
FileUtils.copy(File.join("spec", "data", "nailgun", "pom.xml"), @project_path)
|
data/spec/lib/project_spec.rb
CHANGED
@@ -20,7 +20,7 @@ describe Tetra::Project do
|
|
20
20
|
|
21
21
|
it "returns a project version after dry-run" do
|
22
22
|
@project.dry_run
|
23
|
-
@project.finish
|
23
|
+
@project.finish([])
|
24
24
|
expect(@project.version).to be
|
25
25
|
end
|
26
26
|
end
|
@@ -77,7 +77,7 @@ describe Tetra::Project do
|
|
77
77
|
expect(@project.dry_running?).to be_falsey
|
78
78
|
@project.dry_run
|
79
79
|
expect(@project.dry_running?).to be_truthy
|
80
|
-
@project.finish
|
80
|
+
@project.finish([])
|
81
81
|
expect(@project.dry_running?).to be_falsey
|
82
82
|
end
|
83
83
|
end
|
@@ -87,7 +87,7 @@ describe Tetra::Project do
|
|
87
87
|
it "checks whether src is dirty" do
|
88
88
|
@project.from_directory do
|
89
89
|
@project.dry_run
|
90
|
-
@project.finish
|
90
|
+
@project.finish([])
|
91
91
|
expect(@project.src_patched?).to be_falsey
|
92
92
|
|
93
93
|
FileUtils.touch(File.join("src", "test"))
|
@@ -129,7 +129,7 @@ describe Tetra::Project do
|
|
129
129
|
FileUtils.touch(File.join("src", "test2"))
|
130
130
|
end
|
131
131
|
|
132
|
-
expect(@project.finish).to be_truthy
|
132
|
+
expect(@project.finish([])).to be_truthy
|
133
133
|
expect(@project.dry_running?).to be_falsey
|
134
134
|
|
135
135
|
@project.from_directory do
|
@@ -200,13 +200,13 @@ describe Tetra::Project do
|
|
200
200
|
File.open(File.join("src", "added_in_first_dry_run"), "w") { |f| f.write("A") }
|
201
201
|
File.open("added_outside_directory", "w") { |f| f.write("A") }
|
202
202
|
end
|
203
|
-
expect(@project.finish).to be_truthy
|
203
|
+
expect(@project.finish([])).to be_truthy
|
204
204
|
|
205
205
|
expect(@project.dry_run).to be_truthy
|
206
206
|
@project.from_directory do
|
207
207
|
File.open(File.join("src", "added_in_second_dry_run"), "w") { |f| f.write("A") }
|
208
208
|
end
|
209
|
-
expect(@project.finish).to be_truthy
|
209
|
+
expect(@project.finish([])).to be_truthy
|
210
210
|
|
211
211
|
list = @project.produced_files
|
212
212
|
expect(list).to include("added_in_second_dry_run")
|
data/spec/lib/scriptable_spec.rb
CHANGED
@@ -9,17 +9,16 @@ describe Tetra::Scriptable do
|
|
9
9
|
create_mock_project
|
10
10
|
|
11
11
|
@project.from_directory do
|
12
|
-
File.open("history", "w") do |io|
|
13
|
-
io.puts "some earlier command"
|
14
|
-
io.puts "tetra dry-run start --unwanted-options"
|
15
|
-
io.puts "cd somewhere significant"
|
16
|
-
io.puts "tetra mvn --options"
|
17
|
-
io.puts "tetra dry-run finish -a"
|
18
|
-
io.puts "some later command"
|
19
|
-
end
|
20
|
-
|
21
12
|
FileUtils.mkdir_p(File.join("src", "test-package"))
|
22
13
|
@project.dry_run
|
14
|
+
|
15
|
+
history = ["tetra dry-run start --unwanted-options",
|
16
|
+
"cd somewhere significant",
|
17
|
+
"tetra mvn --options",
|
18
|
+
"tetra dry-run finish -a"
|
19
|
+
]
|
20
|
+
|
21
|
+
@project.finish(history)
|
23
22
|
end
|
24
23
|
|
25
24
|
create_mock_executable("ant")
|
@@ -34,7 +33,7 @@ describe Tetra::Scriptable do
|
|
34
33
|
it "generates a build script from the history" do
|
35
34
|
@project.from_directory do
|
36
35
|
@package = Tetra::Package.new(@project)
|
37
|
-
@package.to_script
|
36
|
+
@package.to_script
|
38
37
|
|
39
38
|
lines = File.readlines(File.join("packages", "test-project", "build.sh"))
|
40
39
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tetra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.52.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-03-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -352,6 +352,7 @@ files:
|
|
352
352
|
- lib/template/src/CONTENTS
|
353
353
|
- lib/tetra.rb
|
354
354
|
- lib/tetra/facades/ant.rb
|
355
|
+
- lib/tetra/facades/bash.rb
|
355
356
|
- lib/tetra/facades/git.rb
|
356
357
|
- lib/tetra/facades/mvn.rb
|
357
358
|
- lib/tetra/facades/process_runner.rb
|