gjp 0.19.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -93,40 +93,19 @@ Success! Now we have to tell gjp to return in normal mode:
93
93
 
94
94
  At this point `gjp` restored `src/` as it was before the build and listed outputs in `file_lists/commons-collections_output`. Those will be used later to compile the `%install` and `%files` sections of the project spec.
95
95
 
96
- This should be sufficient to be able to repeat the build on a machine with no Internet access, but what if we wanted to be 100% sure of that?
96
+ Note that, if the build was unsusccesful, the following command can be used to cancel it and return to pre-dry running state:
97
97
 
98
- #### Second, networkless, dry-run build
98
+ gjp finish --failed
99
99
 
100
- `gjp` has a subcommand to setup a `nonet` user without Internet access, courtesy of `iptables`. You can simply retry the build using that user to see if it works. Note that the following commands will alter group permissions to allow both your current user and `nonet` to work on the same files.
100
+ #### Generating a build script
101
101
 
102
- gjp set-up-nonet-user
103
- chmod -R g+rw ../../..
104
- gjp dry-run
105
- su nonet
106
- ping www.google.com #this should fail!
107
- gjp mvn package
108
- chmod -R g+rw .
109
- exit
110
- gjp finish
111
-
112
- The above is obviously not mandatory, but it can be useful for debugging purposes.
113
-
114
- #### Adding a build.sh file
115
-
116
- One last thing before generating packages is to setup a build script. By default `gjp` will generate a spec file which assumes a `build.sh` script in the source folder of your project that contains all commands needed to build the package itself. At the moment, this needs to be done manually, but it will hopefully be automated in a future release.
117
-
118
- Let's just create it:
119
-
120
- cd ../../..
121
- vi src/commons-collections/build.sh
102
+ `gjp` expects that all commands needed to build a package are put in a `build.sh` script in `src/<package name>`. If you are a Bash user you are lucky - `gjp` can do this for you by looking at your command history! Just type:
122
103
 
123
- Add the following lines:
104
+ gjp generate-build-script commons-collections
124
105
 
125
- #!/bin/sh
126
- cd src/commons-collections/commons-collections-3.2.1-src/
127
- ../../../kit/apache-maven-3.1.0/bin/mvn -Dmaven.repo.local=`readlink -e ../../../kit/m2` -s`readlink -e ../../../kit/m2/settings.xml` package
106
+ Note that `gjp` will substitute the `gjp mvn` calls with equivalent lines that are actually runnable on a build host without `gjp` itself.
128
107
 
129
- Note that `build.sh` gets called from the `gjp` project root, hence the `cd` line, and the Maven line was taken directly from `gjp mvn` output above and pasted verbatim.
108
+ Of course this script can also be manually modified, and it must be in more difficult cases. You don't even have to be afraid of regenerating it later. `gjp` will run a three-way merge and warn if conflicts arise!
130
109
 
131
110
  #### Generating archives and spec files
132
111
 
@@ -154,7 +133,23 @@ commons-collection BuldRequires galaxy-kit, its archive contains only source fil
154
133
 
155
134
  Packages are ready to be submitted to an OBS project. As OBS integration is not yet implemented, refer to OBS documentation to do that.
156
135
 
157
- #### Kit sources
136
+ #### Optional: running networkless dry-run builds
137
+
138
+ `gjp` has a subcommand to setup a `nonet` user without Internet access, courtesy of `iptables`. You can simply retry the build using that user to see if it works. Note that the following commands will alter group permissions to allow both your current user and `nonet` to work on the same files.
139
+
140
+ gjp set-up-nonet-user
141
+ chmod -R g+rw ../../..
142
+ gjp dry-run
143
+ su nonet
144
+ ping www.google.com #this should fail!
145
+ gjp mvn package
146
+ chmod -R g+rw .
147
+ exit
148
+ gjp finish
149
+
150
+ The above is not mandatory, but it can be useful for debugging purposes.
151
+
152
+ #### Optional: kit sources
158
153
 
159
154
  If kit sources are needed for license compliance, some extra work is needed. Fortunately, finding jar source files and adding them to the kit is much easier than packaging its contents in proper RPMs!
160
155
 
data/lib/gjp.rb CHANGED
@@ -2,6 +2,7 @@ require "gjp/version"
2
2
  require "gjp/logger"
3
3
  require "gjp/template_manager"
4
4
  require "gjp/git"
5
+ require "gjp/build_script_generator"
5
6
  require "gjp/project"
6
7
  require "gjp/pom"
7
8
  require "gjp/version_matcher"
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+
3
+ module Gjp
4
+ # generates build scripts from bash_history
5
+ class BuildScriptGenerator
6
+ include Logger
7
+
8
+ def initialize(project, history_path)
9
+ @project = project
10
+ @maven_runner = Gjp::MavenRunner.new(project)
11
+ @history_path = history_path
12
+ end
13
+
14
+ def generate_build_script(name)
15
+ @project.from_directory do
16
+ history_lines = File.readlines(@history_path).map { |e| e.strip }
17
+ relevant_lines =
18
+ history_lines
19
+ .reverse
20
+ .take_while { |e| e.match(/gjp +dry-run/) == nil }
21
+ .reverse
22
+ .take_while { |e| e.match(/gjp +finish/) == nil }
23
+ .select { |e| e.match(/^#/) == nil }
24
+
25
+ script_lines = [
26
+ "#!/bin/bash",
27
+ "PROJECT_PREFIX=`readlink -e .`",
28
+ "cd #{@project.latest_dry_run_directory}"
29
+ ] +
30
+ relevant_lines.map do |line|
31
+ if line =~ /gjp +mvn/
32
+ line.gsub(/gjp +mvn/, "#{@maven_runner.get_maven_commandline("$PROJECT_PREFIX")}")
33
+ else
34
+ line
35
+ end
36
+ end
37
+
38
+ new_content = script_lines.join("\n") + "\n"
39
+
40
+ result_path = File.join("src", name, "build.sh")
41
+ conflict_count = @project.merge_new_content(new_content, result_path, "Build script generated", "generate_#{name}_build_script")
42
+ [result_path, conflict_count]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -41,8 +41,7 @@ module Gjp
41
41
  checking_exceptions do
42
42
  Gjp::Project.init(".")
43
43
  puts "Project inited."
44
- puts "Any file added to kit/ will be added to the kit package."
45
- puts "Any file added to src/<name> will be added to the corresponding package."
44
+ puts "Add sources to src/<package name>, binary dependencies to kit/."
46
45
  puts "When you are ready to test a build, use \"gjp dry-run\"."
47
46
  end
48
47
  end
@@ -53,10 +52,9 @@ module Gjp
53
52
  checking_exceptions do
54
53
  if Gjp::Project.new(".").dry_run
55
54
  puts "Now dry-running, please start your build."
56
- puts "Any file added to kit/, presumably downloaded dependencies, will be added to the kit."
57
- puts "The src/ directory and all files in it will be brought back to the current state when finished."
58
- puts "To run Maven from the kit, use \"gjp mvn\"."
59
- puts "To end this dry run, use \"gjp finish\"."
55
+ puts "To run a Maven installation from the kit, use \"gjp mvn\"."
56
+ puts "If the build succeedes end this dry run with \"gjp finish\"."
57
+ puts "If the build does not succeed use \"gjp finish --failed\" to restore files."
60
58
  end
61
59
  end
62
60
  end
@@ -73,18 +71,21 @@ module Gjp
73
71
  def execute
74
72
  checking_exceptions do
75
73
  project = Gjp::Project.new(".")
76
- result = Gjp::MavenRunner.new(project).mvn(@maven_options)
77
- puts "Real commandline was:"
78
- puts "#{result}"
74
+ Gjp::MavenRunner.new(project).mvn(@maven_options)
79
75
  end
80
76
  end
81
77
  end
82
78
 
83
79
  subcommand "finish", "Ends the current dry-run" do
80
+ option ["-f", "--failed"], :flag, "build failed, restore files as before dry-run"
84
81
  def execute
85
82
  checking_exceptions do
86
- if Gjp::Project.new(".").finish
87
- puts "Dry-run finished."
83
+ if Gjp::Project.new(".").finish(failed?)
84
+ if failed?
85
+ puts "Project reverted as before dry-run."
86
+ else
87
+ puts "Dry-run finished."
88
+ end
88
89
  else
89
90
  puts "No dry-run is in progress."
90
91
  end
@@ -92,12 +93,30 @@ module Gjp
92
93
  end
93
94
  end
94
95
 
96
+ subcommand "generate-build-script", "Creates or refreshes a build.sh file" do
97
+ parameter "NAME", "name of a package, that is, an src/ subdirectory name"
98
+ def execute
99
+ checking_exceptions do
100
+ project = Gjp::Project.new(".")
101
+ history_file = File.join(Dir.home, ".bash_history")
102
+ result_path, conflict_count = Gjp::BuildScriptGenerator.new(project, history_file).generate_build_script(name)
103
+ puts "#{result_path} generated"
104
+ if conflict_count > 0
105
+ puts "Warning: #{conflict_count} unresolved conflicts"
106
+ end
107
+ end
108
+ end
109
+ end
110
+
95
111
  subcommand "generate-kit-spec", "Creates or refreshes a spec file for the kit" do
96
112
  def execute
97
113
  checking_exceptions do
98
114
  project = Gjp::Project.new(".")
99
- result_path = Gjp::SpecGenerator.new(project).generate_kit_spec
115
+ result_path, conflict_count = Gjp::SpecGenerator.new(project).generate_kit_spec
100
116
  puts "#{result_path} generated"
117
+ if conflict_count > 0
118
+ puts "Warning: #{conflict_count} unresolved conflicts"
119
+ end
101
120
  end
102
121
  end
103
122
  end
@@ -119,9 +138,12 @@ module Gjp
119
138
  def execute
120
139
  checking_exceptions do
121
140
  project = Gjp::Project.new(".")
122
- result_path = Gjp::SpecGenerator.new(project).generate_package_spec name, pom, filter
141
+ result_path, conflict_count = Gjp::SpecGenerator.new(project).generate_package_spec name, pom, filter
123
142
  if result_path != nil
124
143
  puts "#{result_path} generated"
144
+ if conflict_count > 0
145
+ puts "Warning: #{conflict_count} unresolved conflicts"
146
+ end
125
147
  else
126
148
  $stderr.puts "file_list/#{name}_output not found. Ensure you have already run a" +
127
149
  "\"gjp dry run\" and \"gjp finish\"."
@@ -1,65 +1,103 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Gjp
4
- # encapsulates git, all methods refer to the current directory
4
+ # facade to git, currently implemented with calls to the git command
5
+ # prefixes all tags with "gjp_"
5
6
  class Git
6
7
  include Logger
7
8
 
9
+ # inits a new git manager object pointing to the specified
10
+ # directory
11
+ def initialize(directory)
12
+ @directory = directory
13
+ end
14
+
8
15
  # inits a repo
9
- def self.init
10
- if Dir.exists?(".git") == false
11
- `git init`
12
- else
13
- raise GitAlreadyInitedException
16
+ def init
17
+ Dir.chdir(@directory) do
18
+ if Dir.exists?(".git") == false
19
+ `git init`
20
+ else
21
+ raise GitAlreadyInitedException
22
+ end
14
23
  end
15
24
  end
16
25
 
17
26
  # returns a list of filenames that changed in the repo
18
27
  # since the specified tag
19
28
  def changed_files_since(tag)
20
- `git diff-tree --no-commit-id --name-only -r gjp_#{tag} HEAD`.split("\n")
29
+ Dir.chdir(@directory) do
30
+ `git diff-tree --no-commit-id --name-only -r gjp_#{tag} HEAD`.split("\n")
31
+ end
21
32
  end
22
33
 
23
34
  # adds all files in the current directory and removes
24
35
  # all files not in the current directory.
25
36
  # if tag is given, commit is also tagged
26
- def commit_whole_directory(message, tag = nil)
27
- log.debug "committing with message: #{message}"
37
+ def commit_whole_directory(message, tag = nil, tag_message = nil)
38
+ Dir.chdir(@directory) do
39
+ log.debug "committing with message: #{message}"
28
40
 
29
- `git rm -r --cached --ignore-unmatch .`
30
- `git add .`
31
- `git commit -m "#{message}"`
41
+ `git rm -r --cached --ignore-unmatch .`
42
+ `git add .`
43
+ `git commit -m "#{message}"`
32
44
 
33
- if tag != nil
34
- `git tag gjp_#{tag}`
45
+ if tag != nil
46
+ if tag_message != nil
47
+ `git tag gjp_#{tag} -m "#{tag_message}"`
48
+ else
49
+ `git tag gjp_#{tag}`
50
+ end
51
+ end
35
52
  end
36
53
  end
37
54
 
38
55
  # returns the highest suffix found in tags with the given prefix
39
56
  def get_tag_maximum_suffix(prefix)
40
- `git tag`.split.map do |tag|
41
- if tag =~ /^gjp_#{prefix}_([0-9]+)$/
42
- $1.to_i
43
- else
44
- 0
45
- end
46
- end.max or 0
57
+ Dir.chdir(@directory) do
58
+ `git tag`.split.map do |tag|
59
+ if tag =~ /^gjp_#{prefix}_([0-9]+)$/
60
+ $1.to_i
61
+ else
62
+ 0
63
+ end
64
+ end.max or 0
65
+ end
47
66
  end
48
67
 
49
68
  # reverts path contents as per specified tag
50
69
  def revert_whole_directory(path, tag)
51
- `git rm -rf --ignore-unmatch #{path}`
52
- `git checkout -f gjp_#{tag} -- #{path}`
70
+ Dir.chdir(@directory) do
71
+ `git rm -rf --ignore-unmatch #{path}`
72
+ `git checkout -f gjp_#{tag} -- #{path}`
53
73
 
54
- `git clean -f -d #{path}`
74
+ `git clean -f -d #{path}`
75
+ end
55
76
  end
56
77
 
57
78
  # 3-way merges the git file at path with the one in new_path
58
79
  # assuming they have a common ancestor at the specified tag
80
+ # returns the conflict count
59
81
  def merge_with_tag(path, new_path, tag)
60
- `git show gjp_#{tag}:#{path} > #{path}.old_version`
61
- `git merge-file --ours #{path} #{path}.old_version #{new_path}`
62
- File.delete "#{path}.old_version"
82
+ Dir.chdir(@directory) do
83
+ `git show gjp_#{tag}:#{path} > #{path}.old_version`
84
+ `git merge-file #{path} #{path}.old_version #{new_path} -L "newly generated" -L "previously generated" -L "user edited"`
85
+ conflict_count = $?.exitstatus
86
+ File.delete "#{path}.old_version"
87
+ return conflict_count
88
+ end
89
+ end
90
+
91
+ # deletes a tag
92
+ def delete_tag(tag)
93
+ Dir.chdir(@directory) do
94
+ `git tag -d gjp_#{tag}`
95
+ end
96
+ end
97
+
98
+ # returns the tag message
99
+ def get_message(tag)
100
+ `git cat-file tag gjp_#{tag}`.split.last
63
101
  end
64
102
  end
65
103
 
@@ -27,9 +27,9 @@ module Gjp
27
27
  nil
28
28
  end
29
29
 
30
- # returns a command line for running Maven
31
- def get_maven_commandline(kit_full_path, running_full_path)
32
- prefix = path_from running_full_path, kit_full_path
30
+ # returns a command line for running Maven from the specified
31
+ # prefix
32
+ def get_maven_commandline(prefix)
33
33
  maven_executable = find_maven_executable
34
34
 
35
35
  if maven_executable != nil
@@ -37,29 +37,18 @@ module Gjp
37
37
  repo_path = File.join(prefix, "kit", "m2")
38
38
  config_path = File.join(prefix, "kit", "m2", "settings.xml")
39
39
 
40
- "#{mvn_path} -Dmaven.repo.local=`readlink -e #{repo_path}` -s`readlink -e #{config_path}`"
40
+ "#{mvn_path} -Dmaven.repo.local=#{repo_path} -s#{config_path}"
41
+ else
42
+ raise MavenNotFoundException
41
43
  end
42
44
  end
43
45
 
44
- # returns a path from origin to destination, provided they are both absolute
45
- def path_from(origin, destination)
46
- (Pathname.new(destination).relative_path_from Pathname.new(origin)).split.first
47
- end
48
-
49
46
  # runs mvn in a subprocess
50
47
  def mvn(options)
51
- kit_full_path = File.join(@project.full_path, "kit")
52
- running_full_path = File.expand_path(".")
53
- maven_commandline = get_maven_commandline(kit_full_path, running_full_path)
54
- if maven_commandline == nil
55
- raise MavenNotFoundException
56
- end
57
-
58
- full_commandline = "#{maven_commandline} #{options.join(' ')}"
48
+ full_commandline = "#{get_maven_commandline(@project.full_path)} #{options.join(' ')}"
59
49
  log.debug full_commandline
60
50
 
61
51
  Process.wait(Process.spawn(full_commandline))
62
- full_commandline
63
52
  end
64
53
  end
65
54
 
@@ -11,7 +11,7 @@ module Gjp
11
11
 
12
12
  def initialize(path)
13
13
  @full_path = Gjp::Project.find_project_dir(File.expand_path(path))
14
- @git = Gjp::Git.new
14
+ @git = Gjp::Git.new(@full_path)
15
15
  end
16
16
 
17
17
  # finds the project directory up in the tree, like git does
@@ -36,7 +36,7 @@ module Gjp
36
36
  # inits a new project directory structure
37
37
  def self.init(dir)
38
38
  Dir.chdir(dir) do
39
- Gjp::Git.init
39
+ Gjp::Git.new(".").init
40
40
 
41
41
  Dir.mkdir "src"
42
42
  Dir.mkdir "kit"
@@ -50,6 +50,7 @@ module Gjp
50
50
  template_manager.copy "kit", "."
51
51
  template_manager.copy "specs", "."
52
52
  template_manager.copy "src", "."
53
+ template_manager.copy ".gitignore", ".gitignore"
53
54
 
54
55
  project.take_snapshot "Template files added", :init
55
56
  end
@@ -59,14 +60,13 @@ module Gjp
59
60
  # to the kit package, src/ will be reset at the current state
60
61
  # when finished
61
62
  def dry_run
62
- from_directory do
63
- if is_dry_running
64
- return false
65
- end
66
-
67
- take_snapshot "Dry-run started", :dry_run_started
63
+ if is_dry_running
64
+ return false
68
65
  end
69
66
 
67
+ current_directory = Pathname.new(Dir.pwd).relative_path_from Pathname.new(@full_path)
68
+
69
+ take_snapshot("Dry-run started", :dry_run_started, current_directory)
70
70
  true
71
71
  end
72
72
 
@@ -75,11 +75,15 @@ module Gjp
75
75
  latest_tag_count(:dry_run_started) > latest_tag_count(:dry_run_finished)
76
76
  end
77
77
 
78
- # ends any phase that was previously started,
79
- # generating file lists
80
- def finish
81
- from_directory do
82
- if is_dry_running
78
+ # ends a dry-run.
79
+ # if failed is true, reverts the whole directory
80
+ # if failed is false, reverts sources and updates output file lists
81
+ def finish(failed)
82
+ if is_dry_running
83
+ if failed
84
+ @git.revert_whole_directory(".", latest_tag(:dry_run_started))
85
+ @git.delete_tag(latest_tag(:dry_run_started))
86
+ else
83
87
  take_snapshot "Changes during dry-run"
84
88
 
85
89
  update_output_file_lists
@@ -89,9 +93,8 @@ module Gjp
89
93
  take_snapshot "Sources reverted as before dry-run"
90
94
 
91
95
  take_snapshot "Dry run finished", :dry_run_finished
92
-
93
- return true
94
96
  end
97
+ return true
95
98
  end
96
99
  false
97
100
  end
@@ -99,58 +102,74 @@ module Gjp
99
102
  # updates files that contain lists of the output files produced by
100
103
  # the build of each package
101
104
  def update_output_file_lists
102
- Dir.foreach("src") do |entry|
103
- if File.directory?(File.join(Dir.getwd, "src", entry)) and entry != "." and entry != ".."
104
- directory = File.join("src", entry)
105
- file_name = "#{entry}_output"
106
- list_file = File.join("file_lists", file_name)
107
- tracked_files = if File.exists?(list_file)
108
- File.readlines(list_file).map { |line| line.strip }
109
- else
110
- []
111
- end
112
-
113
- new_tracked_files = (
114
- @git.changed_files_since(latest_tag(:dry_run_started))
115
- .select { |file| file.start_with?(directory) }
116
- .map { |file|file[directory.length + 1, file.length] }
117
- .concat(tracked_files)
118
- .uniq
119
- .sort
120
- )
121
-
122
- log.debug("writing file list for #{directory}: #{new_tracked_files.to_s}")
123
-
124
- File.open(list_file, "w+") do |file_list|
125
- new_tracked_files.each do |file|
126
- file_list.puts file
127
- end
105
+ each_package_directory do |directory|
106
+ files = (
107
+ @git.changed_files_since(latest_tag(:dry_run_started))
108
+ .select { |file| file.start_with?(directory) }
109
+ .map { |file|file[directory.length + 1, file.length] }
110
+ .sort
111
+ )
112
+
113
+ log.debug("writing file list for #{directory}: #{files.to_s}")
114
+
115
+ list_path = File.join("file_lists", "#{Pathname.new(directory).basename}_output")
116
+ File.open(list_path, "w+") do |file_list|
117
+ files.each do |file|
118
+ file_list.puts file
128
119
  end
129
120
  end
130
121
  end
131
122
  end
132
123
 
133
124
  # takes a revertable snapshot of this project
134
- def take_snapshot(message, prefix = nil)
135
- tag = if prefix
136
- "#{prefix}_#{latest_tag_count(prefix) + 1}"
125
+ def take_snapshot(message, tag_prefix = nil, tag_message = nil)
126
+ tag = if tag_prefix
127
+ "#{tag_prefix}_#{latest_tag_count(tag_prefix) + 1}"
137
128
  else
138
129
  nil
139
130
  end
140
131
 
141
- @git.commit_whole_directory(message, tag)
132
+ @git.commit_whole_directory(message, tag, tag_message)
133
+ end
134
+
135
+ # replaces content in path with new_content, takes a snapshot using
136
+ # snapshot_message and tag_prefix and 3-way merges new and old content
137
+ # with a previous snapshotted file same path tag_prefix, if it exists.
138
+ # returns the number of conflicts
139
+ def merge_new_content(new_content, path, snapshot_message, tag_prefix)
140
+ from_directory do
141
+ already_existing = File.exist? path
142
+ previous_tag = latest_tag(tag_prefix)
143
+
144
+ if already_existing
145
+ File.rename path, "#{path}.gjp_user_edited"
146
+ end
147
+
148
+ File.open(path, "w") { |io| io.write(new_content) }
149
+ take_snapshot(snapshot_message, tag_prefix)
150
+
151
+ if already_existing
152
+ if previous_tag == ""
153
+ previous_tag = latest_tag(tag_prefix)
154
+ end
155
+
156
+ # 3-way merge
157
+ conflict_count = @git.merge_with_tag("#{path}", "#{path}.gjp_user_edited", previous_tag)
158
+ File.delete "#{path}.gjp_user_edited"
159
+ return conflict_count
160
+ end
161
+ return 0
162
+ end
142
163
  end
143
164
 
144
- # returns the last tag of its type corresponding to a
145
- # gjp snapshot
146
- def latest_tag(tag_type)
147
- "#{tag_type}_#{latest_tag_count(tag_type)}"
165
+ # returns the tag with maximum count for a given tag prefix
166
+ def latest_tag(prefix)
167
+ "#{prefix}_#{latest_tag_count(prefix)}"
148
168
  end
149
169
 
150
- # returns the last tag count of its type corresponding
151
- # to a gjp snapshot
152
- def latest_tag_count(tag_type)
153
- @git.get_tag_maximum_suffix(tag_type)
170
+ # returns the maximum tag count for a given tag prefix
171
+ def latest_tag_count(prefix)
172
+ @git.get_tag_maximum_suffix(prefix)
154
173
  end
155
174
 
156
175
  # runs a block from the project directory or a subdirectory
@@ -160,6 +179,24 @@ module Gjp
160
179
  end
161
180
  end
162
181
 
182
+ # runs a block for each package directory in src/
183
+ def each_package_directory
184
+ from_directory do
185
+ Dir.foreach("src") do |entry|
186
+ if File.directory?(File.join(Dir.getwd, "src", entry)) and entry != "." and entry != ".."
187
+ directory = File.join("src", entry)
188
+ yield directory
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ # returns the latest dry run start directory
195
+ def latest_dry_run_directory
196
+ @git.get_message(latest_tag(:dry_run_started))
197
+ end
198
+
199
+
163
200
  # helpers for ERB
164
201
 
165
202
  def name
@@ -7,13 +7,12 @@ module Gjp
7
7
 
8
8
  def initialize(project)
9
9
  @project = project
10
- @git = Gjp::Git.new
11
10
  end
12
11
 
13
12
  def generate_kit_spec
14
13
  spec_path = File.join("specs", "#{@project.name}-kit.spec")
15
- generate_merging "kit.spec", @project.get_binding, spec_path, :generate_kit_spec
16
- spec_path
14
+ conflict_count = generate_merging("kit.spec", @project.get_binding, spec_path, :generate_kit_spec)
15
+ [spec_path, conflict_count]
17
16
  end
18
17
 
19
18
  def generate_package_spec(name, pom, filter)
@@ -26,32 +25,14 @@ module Gjp
26
25
 
27
26
  adapter = Gjp::PackageSpecAdapter.new(@project, name, Gjp::Pom.new(pom), filter)
28
27
 
29
- generate_merging "package.spec", adapter.get_binding, spec_path, "generate_#{name}_spec"
30
-
31
- spec_path
28
+ conflict_count = generate_merging("package.spec", adapter.get_binding, spec_path, "generate_#{name}_spec")
29
+ [spec_path, conflict_count]
32
30
  end
33
31
 
34
- # generates a file in result_path from template together with binding.
35
- # if a file already exists at that position, 3-way merge it with the
36
- # common ancestor. Takes a snapshot in the end for future merges
37
- def generate_merging(template, binding, result_path, tag_prefix)
38
- @project.from_directory do
39
- TemplateManager.new.generate template, binding, "#{result_path}.new_version"
40
-
41
- already_generated = @project.latest_tag(tag_prefix) != ""
42
- already_existing = File.exist? result_path
43
-
44
- if already_generated and already_existing
45
- # 3-way merge
46
- @git.merge_with_tag("#{result_path}", "#{result_path}.new_version", @project.latest_tag(tag_prefix))
47
- File.delete "#{result_path}.new_version"
48
- else
49
- # just replace
50
- File.rename "#{result_path}.new_version", result_path
51
- end
52
-
53
- @project.take_snapshot("Spec generated", tag_prefix)
54
- end
32
+ # generates a spec file from a template and 3-way merges it
33
+ def generate_merging(template, binding, path, tag_prefix)
34
+ new_content = TemplateManager.new.generate(template, binding)
35
+ @project.merge_new_content(new_content, path, "Spec generated", tag_prefix)
55
36
  end
56
37
  end
57
38
  end
@@ -13,13 +13,23 @@ module Gjp
13
13
  @template_path = File.join(File.dirname(__FILE__), "..", "template")
14
14
  end
15
15
 
16
+ # copies a template file in a directory
16
17
  def copy(template_name, destination_dir)
17
18
  FileUtils.cp_r(File.join(template_path, template_name), destination_dir)
18
19
  end
19
20
 
20
- def generate(template_name, object_binding, destination_path)
21
+ # generates content from an ERB template and an object binding
22
+ # if destination_path is given, write it to that file, otherwise just
23
+ # return it
24
+ def generate(template_name, object_binding, destination_path = nil)
21
25
  erb = ERB.new File.read(File.join(template_path, template_name)), nil, "<>"
22
- File.open(destination_path, "w") { |io| io.write erb.result(object_binding) }
26
+ new_content = erb.result(object_binding)
27
+
28
+ if destination_path != nil
29
+ File.open(destination_path, "w") { |io| io.write new_content }
30
+ end
31
+
32
+ new_content
23
33
  end
24
34
  end
25
35
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Gjp
4
- VERSION = "0.19.0"
4
+ VERSION = "0.20.0"
5
5
  end
@@ -0,0 +1,2 @@
1
+ *.gjp_user_edited
2
+ archives/
@@ -26,6 +26,7 @@ Source0: %{name}.tar.xz
26
26
  BuildRoot: %{_tmppath}/%{name}-%{version}-build
27
27
  BuildArch: noarch
28
28
  BuildRequires: xz
29
+ BuildRequires: fdupes
29
30
  Provides: gjp(kit)
30
31
  # no two kits should ever be installed at any given time
31
32
  Conflicts: otherproviders(gjp(kit))
@@ -45,6 +46,7 @@ be installed on end users' systems.
45
46
  %install
46
47
  install -d -m 0755 %{buildroot}%{_datadir}/gjp/%{name}/
47
48
  cp -a * %{buildroot}%{_datadir}/gjp/%{name}/
49
+ %fdupes -s %{buildroot}%{_datadir}/gjp/%{name}/
48
50
 
49
51
  %files
50
52
  %defattr(-,root,root)
@@ -26,7 +26,7 @@ Source0: %{name}.tar.xz
26
26
  BuildRoot: %{_tmppath}/%{name}-%{version}-build
27
27
  BuildRequires: xz
28
28
  BuildRequires: java-devel
29
- BuildRequires: <%= project_name %>-kit == <%= project_version %>
29
+ BuildRequires: <%= project_name %>-kit >= <%= project_version %>
30
30
  BuildArch: noarch
31
31
  Provides: mvn(<%= group_id %>:<%= artifact_id %>) == <%= version %>
32
32
  <% runtime_dependency_ids.each do |dependency_id| %>
@@ -33,7 +33,7 @@ describe Gjp::Archiver do
33
33
  @project.from_directory do
34
34
  File.open(File.join("kit","kit_test"), "w") { |io| io.puts "test content" }
35
35
  end
36
- @project.finish
36
+ @project.finish(false)
37
37
 
38
38
  archiver.archive_kit
39
39
  @project.from_directory do
@@ -48,7 +48,7 @@ describe Gjp::Archiver do
48
48
  Dir.mkdir(File.join("src", "package-name"))
49
49
  File.open(File.join("src", "package-name", "src_test"), "w") { |io| io.puts "test content" }
50
50
  end
51
- @project.finish
51
+ @project.finish(false)
52
52
 
53
53
  archiver.archive_package "package-name"
54
54
  @project.from_directory do
@@ -0,0 +1,65 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Gjp::BuildScriptGenerator do
6
+ before(:each) do
7
+ @project_path = File.join("spec", "data", "test-project")
8
+ Dir.mkdir(@project_path)
9
+
10
+ Gjp::Project.init(@project_path)
11
+ @project = Gjp::Project.new(@project_path)
12
+
13
+ @project.from_directory do
14
+ File.open("history", "w") do |io|
15
+ io.puts "some earlier command"
16
+ io.puts "gjp dry-run --unwanted-options"
17
+ io.puts "cd somewhere significant"
18
+ io.puts "gjp mvn --options"
19
+ io.puts "gjp finish -a"
20
+ io.puts "some later command"
21
+ end
22
+
23
+ FileUtils.mkdir_p(File.join("src", "test-package"))
24
+
25
+ @generator = Gjp::BuildScriptGenerator.new(@project, "history")
26
+ end
27
+
28
+ mock_maven_executable
29
+ end
30
+
31
+ after(:each) do
32
+ FileUtils.rm_rf(@project_path)
33
+ end
34
+
35
+ describe "#generate_build_script" do
36
+ it "generates a build script from the history" do
37
+ @project.from_directory do
38
+ @generator.generate_build_script("test-package")
39
+
40
+ lines = File.readlines(File.join("src", "test-package", "build.sh"))
41
+
42
+ lines.should include("#!/bin/bash\n")
43
+ lines.should include("cd somewhere significant\n")
44
+ lines.should include("$PROJECT_PREFIX/kit/mvn/bin/mvn -Dmaven.repo.local=$PROJECT_PREFIX/kit/m2 -s$PROJECT_PREFIX/kit/m2/settings.xml --options\n")
45
+
46
+ lines.should_not include("some earlier command\n")
47
+ lines.should_not include("gjp dry-run --unwanted-options\n")
48
+ lines.should_not include("gjp dry-run --unwanted-options\n")
49
+ lines.should_not include("gjp finish -a\n")
50
+ lines.should_not include("some later command\n")
51
+ end
52
+ end
53
+ end
54
+
55
+ def mock_maven_executable
56
+ Dir.chdir(@project_path) do
57
+ @bin_dir = File.join("kit", "mvn", "bin")
58
+ FileUtils.mkdir_p(@bin_dir)
59
+ @maven_executable = File.join(@bin_dir, "mvn")
60
+ File.open(@maven_executable, "w") { |io| io.puts "echo $0 $*>test_out" }
61
+ File.chmod(0777, @maven_executable)
62
+ end
63
+ end
64
+ end
65
+
@@ -30,16 +30,14 @@ describe Gjp::MavenRunner do
30
30
  describe "#get_maven_commandline" do
31
31
  it "returns commandline options for running maven" do
32
32
  mock_maven_executable
33
- kit_full_path = File.join(@project.full_path, "kit")
34
- commandline = @maven_runner.get_maven_commandline(kit_full_path, @project.full_path)
35
33
 
36
- commandline.should eq "./#{@maven_executable} -Dmaven.repo.local=`readlink -e ./kit/m2` -s`readlink -e ./kit/m2/settings.xml`"
34
+ @project.from_directory do
35
+ commandline = @maven_runner.get_maven_commandline(".")
36
+ commandline.should eq "./#{@maven_executable} -Dmaven.repo.local=./kit/m2 -s./kit/m2/settings.xml"
37
+ end
37
38
  end
38
39
  it "doesn't return commandline options if Maven is not available" do
39
- kit_full_path = File.join(@project.full_path, "kit")
40
- commandline = @maven_runner.get_maven_commandline(kit_full_path, @project.full_path)
41
-
42
- commandline.should be_nil
40
+ expect { @maven_runner.get_maven_commandline(".") }.to raise_error(Gjp::MavenNotFoundException)
43
41
  end
44
42
  end
45
43
 
@@ -53,7 +51,7 @@ describe Gjp::MavenRunner do
53
51
  end
54
52
  it "doesn't run Maven if it is not available" do
55
53
  @project.from_directory do
56
- expect { @maven_runner.mvn []}.to raise_error(Gjp::MavenNotFoundException)
54
+ expect { @maven_runner.mvn([]) }.to raise_error(Gjp::MavenNotFoundException)
57
55
  end
58
56
  end
59
57
  end
@@ -57,7 +57,7 @@ describe Gjp::Project do
57
57
  @project.is_dry_running.should be_false
58
58
  @project.dry_run
59
59
  @project.is_dry_running.should be_true
60
- @project.finish
60
+ @project.finish(false)
61
61
  @project.is_dry_running.should be_false
62
62
  end
63
63
  end
@@ -77,13 +77,14 @@ describe Gjp::Project do
77
77
  end
78
78
 
79
79
  describe "#finish" do
80
- it "ends the current dry-run phase" do
80
+ it "ends the current dry-run phase after a successful build" do
81
81
  @project.from_directory do
82
82
  Dir.mkdir("src/abc")
83
83
  `echo A > src/abc/test`
84
84
  end
85
85
 
86
- @project.finish.should be_false
86
+ @project.finish(true).should be_false
87
+ @project.finish(false).should be_false
87
88
 
88
89
  @project.dry_run.should be_true
89
90
 
@@ -92,7 +93,7 @@ describe Gjp::Project do
92
93
  `touch src/abc/test2`
93
94
  end
94
95
 
95
- @project.finish.should be_true
96
+ @project.finish(false).should be_true
96
97
  @project.is_dry_running.should be_false
97
98
 
98
99
  @project.from_directory do
@@ -104,22 +105,58 @@ describe Gjp::Project do
104
105
  File.exists?("src/abc/test2").should be_false
105
106
  end
106
107
  end
108
+ it "ends the current dry-run phase after a failed build" do
109
+ @project.from_directory do
110
+ Dir.mkdir("src/abc")
111
+ `echo A > src/abc/test`
112
+ `echo A > kit/test`
113
+ end
114
+
115
+ @project.finish(true).should be_false
116
+ @project.finish(false).should be_false
117
+
118
+ @project.dry_run.should be_true
119
+
120
+ @project.from_directory do
121
+ `echo B > src/abc/test`
122
+ `touch src/abc/test2`
123
+ `echo B > kit/test`
124
+ `touch kit/test2`
125
+ end
126
+
127
+ @project.finish(true).should be_true
128
+ @project.is_dry_running.should be_false
129
+
130
+ @project.from_directory do
131
+ `git rev-list --all`.split("\n").length.should eq 2
132
+ File.read("src/abc/test").should eq "A\n"
133
+ File.exists?("src/abc/test2").should be_false
134
+
135
+ File.read("kit/test").should eq "A\n"
136
+ File.exists?("kit/test2").should be_false
137
+
138
+ File.exists?(File.join("file_lists", "abc_output")).should be_false
139
+ end
140
+ end
107
141
  end
108
142
 
109
143
  describe "#dry_run" do
110
144
  it "starts a dry running phase" do
111
- @project.finish.should be_false
145
+ @project.finish(false).should be_false
112
146
 
113
147
  @project.from_directory do
114
148
  `touch src/test`
115
149
  end
116
150
 
117
- @project.dry_run.should be_true
151
+ @project.from_directory("src") do
152
+ @project.dry_run.should be_true
153
+ end
118
154
 
119
155
  @project.from_directory do
120
156
  @project.is_dry_running.should be_true
121
157
  `git rev-list --all`.split("\n").length.should eq 2
122
158
  `git diff-tree --no-commit-id --name-only -r HEAD`.split("\n").should include("src/test")
159
+ `git cat-file tag gjp_dry_run_started_1 | tail -1`.should include("src")
123
160
  end
124
161
  end
125
162
  end
@@ -15,7 +15,7 @@ describe Gjp::SpecGenerator do
15
15
  test_file = File.join("kit", "test")
16
16
  File.open(test_file, "w") { |io| io.puts "kit content test file" }
17
17
  end
18
- @project.finish
18
+ @project.finish(false)
19
19
 
20
20
  @spec_generator = Gjp::SpecGenerator.new(@project)
21
21
  end
@@ -46,7 +46,7 @@ describe Gjp::SpecGenerator do
46
46
  io.write("nonconflicting line")
47
47
  end
48
48
  end
49
- @project.finish
49
+ @project.finish(false)
50
50
 
51
51
  @spec_generator.generate_kit_spec.should be_true
52
52
 
@@ -74,7 +74,7 @@ describe Gjp::SpecGenerator do
74
74
  io.write(spec_contents)
75
75
  end
76
76
  end
77
- @project.finish
77
+ @project.finish(false)
78
78
 
79
79
  @spec_generator.generate_kit_spec.should be_true
80
80
 
@@ -82,8 +82,11 @@ describe Gjp::SpecGenerator do
82
82
  spec_lines = File.readlines(File.join("specs", "test-project-kit.spec"))
83
83
  spec_lines.should include("Name: test-project-kit\n")
84
84
  spec_lines.should include("Source0: %{name}.tar.xz\n")
85
+ spec_lines.should include("<<<<<<< newly generated\n")
86
+ spec_lines.should include("Version: 2\n")
87
+ spec_lines.should include("=======\n")
85
88
  spec_lines.should include("CONFLICTING!\n")
86
- spec_lines.should_not include("Version: 2\n")
89
+ spec_lines.should include(">>>>>>> user edited\n")
87
90
  end
88
91
  end
89
92
  end
@@ -106,7 +109,7 @@ describe Gjp::SpecGenerator do
106
109
  `touch src/test/out/test#{i}.jar`
107
110
  end
108
111
 
109
- @project.finish
112
+ @project.finish(false)
110
113
  end
111
114
 
112
115
  @spec_generator.generate_package_spec "test", File.join("spec", "data", "nailgun", "pom.xml"), "*.jar"
@@ -117,7 +120,7 @@ describe Gjp::SpecGenerator do
117
120
  spec_lines.should include("License: The Apache Software License, Version 2.0\n")
118
121
  spec_lines.should include("Summary: Nailgun is a client, protocol, and server for running Java\n")
119
122
  spec_lines.should include("Url: http://martiansoftware.com/nailgun\n")
120
- spec_lines.should include("BuildRequires: #{@project.name}-kit\n")
123
+ spec_lines.should include("BuildRequires: #{@project.name}-kit >= 2\n")
121
124
  spec_lines.should include("Provides: mvn(com.martiansoftware:nailgun-all) == 0.9.1\n")
122
125
  spec_lines.should include("cp -a out/test3.jar %{buildroot}%{_javadir}/test3.jar\n")
123
126
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gjp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.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: 2013-10-07 00:00:00.000000000 Z
12
+ date: 2013-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -158,6 +158,7 @@ files:
158
158
  - integration-tests/commons-collections.sh
159
159
  - lib/gjp.rb
160
160
  - lib/gjp/archiver.rb
161
+ - lib/gjp/build_script_generator.rb
161
162
  - lib/gjp/cli.rb
162
163
  - lib/gjp/git.rb
163
164
  - lib/gjp/limited_network_user.rb
@@ -175,6 +176,7 @@ files:
175
176
  - lib/gjp/template_manager.rb
176
177
  - lib/gjp/version.rb
177
178
  - lib/gjp/version_matcher.rb
179
+ - lib/template/.gitignore
178
180
  - lib/template/archives/CONTENTS
179
181
  - lib/template/file_lists/CONTENTS
180
182
  - lib/template/kit.spec
@@ -200,6 +202,7 @@ files:
200
202
  - spec/data/struts-apps/pom.xml
201
203
  - spec/data/tomcat/pom.xml
202
204
  - spec/lib/archiver_spec.rb
205
+ - spec/lib/build_script_generator_spec.rb
203
206
  - spec/lib/limited_network_user_spec_interactive.rb
204
207
  - spec/lib/maven_runner_spec.rb
205
208
  - spec/lib/maven_website_spec.rb
@@ -256,6 +259,7 @@ test_files:
256
259
  - spec/data/struts-apps/pom.xml
257
260
  - spec/data/tomcat/pom.xml
258
261
  - spec/lib/archiver_spec.rb
262
+ - spec/lib/build_script_generator_spec.rb
259
263
  - spec/lib/limited_network_user_spec_interactive.rb
260
264
  - spec/lib/maven_runner_spec.rb
261
265
  - spec/lib/maven_website_spec.rb