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 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 (typically a copy of Maven and maybe some other dependency jars);
27
- * execute `tetra dry-run start`, then any command needed to compile your software, then `tetra dry-run finish`;
28
- * execute `tetra generate-all`: tetra will look at changed files and Bash history to scaffold spec files and tarballs.
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
- With `tetra` you are not building all dependencies from source, just your package. Everything else is shipped already compiled with attached source files, which is much easier to implement and automate. Yet, it is sufficient to fulfill open source licenses and to have a repeatable, networkless build.
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 by running appropriate commands between `tetra dry-run start` and `tetra dry-run finish`. Bash history will be recorded to generate a "starting-point" build script (that will be sufficient in simple cases like this):
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 start
60
+ tetra dry-run
55
61
 
56
62
  cd commons-collections-3.2.1-src/
57
63
  tetra mvn package
58
64
 
59
- tetra dry-run finish
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 `tetra finish`, as this ensures build repeatability.
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` is a research project currently in beta state. If you are a packager you can try to use it, any feedback would be **very** welcome!
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 builds
3
+ ## Failing dry-runs
4
4
 
5
- If your build fails for whatever reason, abort it with `tetra finish abort`. `tetra` will restore all project files as they were before build.
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 the latest bash history (assumes `tetra dry-run start` and `tetra dry-run finish` have been used);
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
@@ -28,6 +28,7 @@ require "tetra/facades/process_runner"
28
28
  require "tetra/facades/git"
29
29
  require "tetra/facades/ant"
30
30
  require "tetra/facades/mvn"
31
+ require "tetra/facades/bash"
31
32
 
32
33
  # main internal classes
33
34
  require "tetra/version"
@@ -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 an external executable and returns its output as a string
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
@@ -57,8 +57,8 @@ module Tetra
57
57
  _to_spec(@project, name, "package.spec", @project.packages_dir)
58
58
  end
59
59
 
60
- def to_script(history)
61
- _to_script(@project, history)
60
+ def to_script
61
+ _to_script(@project)
62
62
  end
63
63
  end
64
64
  end
@@ -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, history_path)
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, relevant_lines)
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
- def script_body(project, relevant_lines)
39
- ant = if relevant_lines.any? { |e| e.match(/tetra +ant/) }
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 relevant_lines.any? { |e| e.match(/tetra +mvn/) }
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
- relevant_lines.map do |line|
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 and updates output file lists
119
- def finish
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 "Use \"tetra patch message\" to include changes in a patch before dry-running."
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 "Now dry-running, please start your build."
30
- puts "To run a Maven installation from the kit, use \"tetra mvn\"."
31
- puts "If the build succeedes end this dry run with \"tetra dry-run finish\"."
32
- puts "If the build does not succeed use \"tetra dry-run abort\" to undo any change."
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
- def abort(project)
50
- if project.dry_running?
51
- project.abort
52
- puts "Project reverted as before dry-run."
53
- else
54
- puts "No dry-run is in progress."
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(history)
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
@@ -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 start\""
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 "Please finish or abort this dry-run first, use \"tetra dry-run finish\" or \"tetra dry-run abort\""
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  # base module for tetra
4
4
  module Tetra
5
- VERSION = "0.51.0"
5
+ VERSION = "0.52.0"
6
6
  end
@@ -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)
@@ -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")
@@ -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("history")
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.51.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-02-27 00:00:00.000000000 Z
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