autoproj 1.12.6 → 1.13.0.b1

Sign up to get free protection for your applications and to get access to all the features.
@@ -210,13 +210,108 @@ module Autoproj
210
210
  end
211
211
  end
212
212
 
213
+ # Returns true if packages and prefixes should be auto-generated, based
214
+ # on the SHA of the package names. This is meant to be used for build
215
+ # services that want to check that dependencies are properly set
216
+ #
217
+ # The default is false (disabled)
218
+ #
219
+ # @return [Boolean]
213
220
  def randomize_layout?
214
221
  get('randomize_layout', false)
215
222
  end
216
223
 
224
+ # Sets whether the layout should be randomized
225
+ #
226
+ # @return [Boolean]
227
+ # @see randomize_layout?
217
228
  def randomize_layout=(value)
218
229
  set('randomize_layout', value, true)
219
230
  end
231
+
232
+ DEFAULT_UTILITY_SETUP = Hash[
233
+ 'doc' => true,
234
+ 'test' => false]
235
+
236
+ # The configuration key that should be used to store the utility
237
+ # enable/disable information
238
+ #
239
+ # @param [String] the utility name
240
+ # @return [String] the config key
241
+ def utility_key(utility)
242
+ "autoproj_#{utility}_utility"
243
+ end
244
+
245
+ # Returns whether a given utility is enabled for the package
246
+ #
247
+ # If there is no specific configuration for the package, uses the global
248
+ # default set with utility_enable_all or utility_disable_all. If none of
249
+ # these methods has been called, uses the default in
250
+ # {DEFAULT_UTILITY_SETUP}
251
+ #
252
+ # @param [String] utility the utility name (e.g. 'doc' or 'test')
253
+ # @param [String] package the package name
254
+ # @return [Boolean] true if the utility should be enabled for the
255
+ # requested package and false otherwise
256
+ def utility_enabled_for?(utility, package)
257
+ utility_config = get(utility_key(utility), Hash.new)
258
+ if utility_config.has_key?(package)
259
+ utility_config[package]
260
+ else get("#{utility_key(utility)}_default", DEFAULT_UTILITY_SETUP[utility])
261
+ end
262
+ end
263
+
264
+ # Enables a utility for all packages
265
+ #
266
+ # This both sets the default value for all packages and resets all
267
+ # package-specific values set with {utility_enable_for} and
268
+ # {utility_disable_for}
269
+ #
270
+ # @param [String] utility the utility name (e.g. 'doc' or 'test')
271
+ # @return [void]
272
+ def utility_enable_all(utility)
273
+ reset(utility_key(utility))
274
+ set("#{utility_key(utility)}_default", true)
275
+ end
276
+
277
+ # Enables a utility for a specific package
278
+ #
279
+ # Note that if the default for this utility is to be enabled, this is
280
+ # essentially a no-op.
281
+ #
282
+ # @param [String] utility the utility name (e.g. 'doc' or 'test')
283
+ # @param [String] package the package name
284
+ # @return [void]
285
+ def utility_enable_for(utility, package)
286
+ utility_config = get(utility_key(utility), Hash.new)
287
+ set(utility_key(utility), utility_config.merge(package => true))
288
+ end
289
+
290
+ # Disables a utility for all packages
291
+ #
292
+ # This both sets the default value for all packages and resets all
293
+ # package-specific values set with {utility_enable_for} and
294
+ # {utility_disable_for}
295
+ #
296
+ # @param [String] utility the utility name (e.g. 'doc' or 'test')
297
+ # @return [void]
298
+ def utility_disable_all(utility)
299
+ reset(utility_key(utility))
300
+ set("#{utility_key(utility)}_default", false)
301
+ end
302
+
303
+ # Disables a utility for a specific package
304
+ #
305
+ # Note that if the default for this utility is to be disabled, this is
306
+ # essentially a no-op.
307
+ #
308
+ # @param [String] utility the utility name (e.g. 'doc' or 'test')
309
+ # @param [String] package the package name
310
+ # @return [void]
311
+ def utility_disable_for(utility, package)
312
+ utility_config = get(utility_key(utility), Hash.new)
313
+ set(utility_key(utility), utility_config.merge(package => false))
314
+ end
220
315
  end
221
316
  end
222
317
 
@@ -195,5 +195,8 @@ pip:
195
195
  opensuse: python-pip
196
196
  fedora: python-pip
197
197
 
198
+ sudo:
199
+ default: sudo
200
+
198
201
  # vim: expandtab
199
202
 
@@ -25,51 +25,35 @@ module Autoproj
25
25
  :http_url => "https://git.#{base_url}",
26
26
  :ssh_url => "git@#{base_url}:",
27
27
  :fallback_to_http => true,
28
- :default => 'http,ssh',
29
- :disabled_methods => []
30
-
31
- disabled_methods = Array(options[:disabled_methods])
28
+ :default => 'git,ssh'
29
+
30
+ gitorious_long_doc = [
31
+ "How should I interact with #{base_url} (git, http or ssh)",
32
+ "If you give two values, comma-separated, the first one will be",
33
+ "used for pulling and the second one for pushing"]
32
34
 
33
35
  access_methods = Hash[
34
36
  'git' => 'git,ssh',
35
37
  'ssh' => 'ssh,ssh',
36
38
  'http' => 'http,http']
37
39
 
38
- gitorious_long_doc = [
39
- "How should I interact with #{base_url} (#{(access_methods.keys - disabled_methods).sort.join(", ")})",
40
- "If you give two values, comma-separated, the first one will be",
41
- "used for pulling and the second one for pushing"]
42
-
43
- validator = lambda do |value|
44
- if value =~ /,/
45
- value.split(',').each do |method|
46
- if !access_methods.has_key?(method)
47
- raise Autoproj::InputError, "#{method} is not a known access method"
48
- elsif disabled_methods.include?(method)
49
- raise Autoproj::InputError, "#{method} is disabled on #{base_url}"
40
+ configuration_option name, 'string',
41
+ :default => options[:default],
42
+ :doc => gitorious_long_doc do |value|
43
+ if value =~ /,/
44
+ value.split(',').each do |method|
45
+ if !access_methods.has_key?(method)
46
+ raise Autoproj::InputError, "#{method} is not a known access method"
47
+ end
50
48
  end
49
+ elsif !access_methods.has_key?(value)
50
+ raise Autoproj::InputError, "#{value} is not a known access method"
51
51
  end
52
- elsif !access_methods.has_key?(value)
53
- raise Autoproj::InputError, "#{value} is not a known access method"
54
- elsif disabled_methods.include?(value)
55
- raise Autoproj::InputError, "#{method} is disabled on #{base_url}"
56
- end
57
-
58
- value
59
- end
60
52
 
61
- configuration_option name, 'string',
62
- :default => options[:default],
63
- :doc => gitorious_long_doc, &validator
53
+ value
54
+ end
64
55
 
65
- access_mode = Autoproj.config.get(name)
66
- begin
67
- validator[access_mode]
68
- rescue Autoproj::InputError => e
69
- Autoproj.warn e.message
70
- Autoproj.config.reset(name)
71
- access_mode = Autoproj.config.get(name)
72
- end
56
+ access_mode = Autoproj.user_config(name)
73
57
  access_mode = access_methods[access_mode] || access_mode
74
58
  pull, push = access_mode.split(',')
75
59
  [[pull, "_ROOT"], [push, "_PUSH_ROOT"]].each do |method, var_suffix|
@@ -89,11 +73,15 @@ module Autoproj
89
73
  end
90
74
  pull_base_url = Autoproj.user_config("#{name}_ROOT")
91
75
  push_base_url = Autoproj.user_config("#{name}_PUSH_ROOT")
92
- Hash[:type => 'git', :url => "#{pull_base_url}#{url}", :push_to => "#{push_base_url}#{url}", :retry_count => 10].merge(vcs_options)
76
+ Hash[type: 'git',
77
+ url: "#{pull_base_url}#{url}",
78
+ push_to: "#{push_base_url}#{url}",
79
+ retry_count: 10,
80
+ repository_id: "#{name.downcase}:#{url}"].merge(vcs_options)
93
81
  end
94
82
  end
95
83
  end
96
84
 
97
- Autoproj.gitorious_server_configuration('GITORIOUS', 'gitorious.org', :default => 'http,ssh', :disabled_methods => 'git')
85
+ Autoproj.gitorious_server_configuration('GITORIOUS', 'gitorious.org')
98
86
  Autoproj.gitorious_server_configuration('GITHUB', 'github.com', :http_url => 'https://github.com', :default => 'http,ssh')
99
87
 
@@ -40,7 +40,8 @@ module Autoproj
40
40
  # @return [Array<PackageSet>]
41
41
  attr_writer :package_sets
42
42
 
43
- # Returns true if +pkg_name+ has been explicitely selected
43
+ # Returns true if +pkg_name+ has been explicitely selected, either by
44
+ # the command line or through the layout
44
45
  def explicitly_selected_package?(pkg_name)
45
46
  explicit_selection && explicit_selection.include?(pkg_name)
46
47
  end
@@ -65,8 +66,14 @@ module Autoproj
65
66
 
66
67
  @file = file
67
68
  @data = data
68
- @ignored_packages |= (data['ignored_packages'] || Set.new)
69
- data['exclude_packages'] ||= Set.new
69
+ @ignored_packages |= (data['ignored_packages'] || Set.new).to_set
70
+ @manifest_exclusions |= (data['exclude_packages'] || Set.new).to_set
71
+
72
+ @normalized_layout = Hash.new
73
+ compute_normalized_layout(
74
+ normalized_layout,
75
+ '/',
76
+ data['layout'] || Hash.new)
70
77
 
71
78
  if data['constants']
72
79
  @constant_definitions = Autoproj.resolve_constant_definitions(data['constants'])
@@ -128,6 +135,7 @@ module Autoproj
128
135
  @ignored_os_dependencies = Set.new
129
136
  @reused_installations = Array.new
130
137
  @ignored_packages = Set.new
138
+ @manifest_exclusions = Set.new
131
139
 
132
140
  @constant_definitions = Hash.new
133
141
  if Autoproj.has_config_key?('manifest_source')
@@ -139,7 +147,7 @@ module Autoproj
139
147
  # Call this method to ignore a specific package. It must not be used in
140
148
  # init.rb, as the manifest is not yet loaded then
141
149
  def ignore_package(package_name)
142
- @ignored_packages << package_name
150
+ @ignored_packages << package_name.to_str
143
151
  end
144
152
 
145
153
  # True if the given package should not be built, with the packages that
@@ -185,7 +193,7 @@ module Autoproj
185
193
  # The set of package names that are listed in the excluded_packages
186
194
  # section of the manifest
187
195
  def manifest_exclusions
188
- data['exclude_packages']
196
+ @manifest_exclusions
189
197
  end
190
198
 
191
199
  # A package_name => reason map of the exclusions added with #add_exclusion.
@@ -198,6 +206,17 @@ module Autoproj
198
206
  automatic_exclusions[package_name] = reason
199
207
  end
200
208
 
209
+ # Tests whether the given package is excluded in the manifest
210
+ def excluded_in_manifest?(package_name)
211
+ manifest_exclusions.any? do |matcher|
212
+ if (pkg_set = metapackages[matcher]) && pkg_set.include?(package_name)
213
+ true
214
+ else
215
+ Regexp.new(matcher) === package_name
216
+ end
217
+ end
218
+ end
219
+
201
220
  # If +package_name+ is excluded from the build, returns a string that
202
221
  # tells why. Otherwise, returns nil
203
222
  #
@@ -205,7 +224,7 @@ module Autoproj
205
224
  # exclude_packages section of the manifest, or because they are
206
225
  # disabled on this particular operating system.
207
226
  def exclusion_reason(package_name)
208
- if manifest_exclusions.any? { |l| Regexp.new(l) =~ package_name }
227
+ if excluded_in_manifest?(package_name)
209
228
  "#{package_name} is listed in the exclude_packages section of the manifest"
210
229
  else
211
230
  automatic_exclusions[package_name]
@@ -218,7 +237,11 @@ module Autoproj
218
237
  # This is useful to avoid building packages that are of no use for the
219
238
  # user.
220
239
  def excluded?(package_name)
221
- if manifest_exclusions.any? { |l| Regexp.new(l) =~ package_name }
240
+ package_name = package_name.to_str
241
+
242
+ if normalized_layout.has_key?(package_name)
243
+ false
244
+ elsif excluded_in_manifest?(package_name)
222
245
  true
223
246
  elsif automatic_exclusions.any? { |pkg_name, | pkg_name == package_name }
224
247
  true
@@ -375,6 +398,12 @@ module Autoproj
375
398
  packages[name]
376
399
  end
377
400
 
401
+ def find_autobuild_package(name)
402
+ if pkg = packages[name]
403
+ pkg.autobuild
404
+ end
405
+ end
406
+
378
407
  def package(name)
379
408
  packages[name]
380
409
  end
@@ -500,6 +529,10 @@ module Autoproj
500
529
  set
501
530
  end
502
531
 
532
+ def main_package_set
533
+ each_package_set.find(&:main?)
534
+ end
535
+
503
536
  # Exception raised when a caller requires to use an excluded package
504
537
  class ExcludedPackage < ConfigError
505
538
  attr_reader :name
@@ -813,7 +846,11 @@ module Autoproj
813
846
  end
814
847
  end
815
848
 
816
- def normalized_layout(result = Hash.new, layout_level = '/', layout_data = (data['layout'] || Hash.new))
849
+ # A mapping from names to layout placement, as found in the layout
850
+ # section of the manifest
851
+ attr_reader :normalized_layout
852
+
853
+ def compute_normalized_layout(result, layout_level, layout_data)
817
854
  layout_data.each do |value|
818
855
  if value.kind_of?(Hash)
819
856
  subname, subdef = value.find { true }
@@ -877,7 +914,7 @@ module Autoproj
877
914
  pkg.autobuild.description = manifest
878
915
  package_manifests[package.name] = manifest
879
916
 
880
- manifest.each_dependency do |name, is_optional|
917
+ manifest.each_dependency(pkg.modes) do |name, is_optional|
881
918
  begin
882
919
  if is_optional
883
920
  package.optional_dependency name
@@ -37,7 +37,7 @@ module Autoproj
37
37
  FileUtils.mkdir_p File.dirname(pkg.importdir)
38
38
  Autobuild::Subprocess.run("autoproj-cache", "import", Autobuild.tool(:git), "--git-dir", pkg.importdir, 'init', "--bare")
39
39
  end
40
- pkg.importer.update_remotes_configuration(pkg, 'autoproj-cache')
40
+ pkg.importer.update_remotes_configuration(pkg)
41
41
 
42
42
  with_retry(10) do
43
43
  Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'remote', 'update', 'autobuild')
@@ -96,7 +96,7 @@ module Autoproj
96
96
  fake_package.update = false
97
97
  end
98
98
  end
99
- fake_package.import(only_local)
99
+ fake_package.import(only_local: only_local)
100
100
 
101
101
  rescue Autobuild::ConfigException => e
102
102
  raise ConfigError.new, "cannot import #{name}: #{e.message}", e.backtrace
@@ -177,9 +177,11 @@ module Autoproj
177
177
  pkg_set
178
178
  end
179
179
 
180
- def queue_auto_imports_if_needed(queue, pkg_set)
180
+ def queue_auto_imports_if_needed(queue, pkg_set, root_set)
181
181
  if pkg_set.auto_imports?
182
182
  pkg_set.each_raw_imported_set do |import_vcs, import_options|
183
+ repository_id = repository_id_of(import_vcs)
184
+ import_vcs = root_set.overrides_for("pkg_set:#{repository_id}", import_vcs)
183
185
  queue << [import_vcs, import_options, pkg_set]
184
186
  end
185
187
  end
@@ -191,10 +193,7 @@ module Autoproj
191
193
  return "local:#{vcs.url}"
192
194
  end
193
195
 
194
- name = PackageSet.name_of(manifest, vcs)
195
- raw_local_dir = PackageSet.raw_local_dir_of(vcs)
196
- fake_package = Tools.create_autobuild_package(vcs, name, raw_local_dir)
197
- fake_package.importer.repository_id
196
+ vcs.create_autobuild_importer.repository_id
198
197
  end
199
198
 
200
199
  # Load the package set information
@@ -210,7 +209,7 @@ module Autoproj
210
209
  by_repository_id = Hash.new
211
210
  by_name = Hash.new
212
211
 
213
- queue = queue_auto_imports_if_needed(Array.new, root_pkg_set)
212
+ queue = queue_auto_imports_if_needed(Array.new, root_pkg_set, root_pkg_set)
214
213
  while !queue.empty?
215
214
  vcs, options, imported_from = queue.shift
216
215
  repository_id = repository_id_of(vcs)
@@ -258,7 +257,7 @@ module Autoproj
258
257
  by_name[pkg_set.name] = [pkg_set, vcs, options, imported_from]
259
258
 
260
259
  # Finally, queue the imports
261
- queue_auto_imports_if_needed(queue, pkg_set)
260
+ queue_auto_imports_if_needed(queue, pkg_set, root_pkg_set)
262
261
  end
263
262
 
264
263
  cleanup_remotes_dir(package_sets)
@@ -0,0 +1,258 @@
1
+ module Autoproj
2
+ module Ops
3
+ class Snapshot
4
+ # Update version control information with new choices
5
+ #
6
+ # The two parameters are formatted as expected in the version_control
7
+ # and overrides fields in source.yml / overrides.yml, that is (in YAML)
8
+ #
9
+ # - package_name:
10
+ # version: '10'
11
+ # control: '20'
12
+ # info: '30'
13
+ #
14
+ # The two parameters are expected to only use full package names, and
15
+ # not regular expressions
16
+ #
17
+ # @param [Array<String=>Hash>] overrides the information that should augment
18
+ # the current state
19
+ # @param [Array<String=>Hash>] state the current state
20
+ # @param [Hash] the updated information
21
+ def self.merge_packets( overrides, state )
22
+ result = overrides.dup
23
+ overriden = overrides.map { |entry| entry.keys.first }.to_set
24
+ state.each do |pkg|
25
+ name, _ = pkg.first
26
+ if !overriden.include?(name)
27
+ result << pkg
28
+ end
29
+ end
30
+ result
31
+ end
32
+
33
+ def sort_versions(versions)
34
+ pkg_sets, pkgs = versions.partition { |n, _| n =~ /^pkg_set:/ }
35
+ pkg_sets.sort_by { |n, _| n.keys.first } +
36
+ pkgs.sort_by { |n, _| n.keys.first }
37
+ end
38
+
39
+ def save_versions( versions, versions_file, options = Hash.new )
40
+ options = Kernel.validate_options options,
41
+ replace: false
42
+
43
+ existing_versions = Array.new
44
+ if !options[:replace] && File.exists?(versions_file)
45
+ existing_versions = YAML.load( File.read( versions_file ) ) ||
46
+ Array.new
47
+ end
48
+
49
+ # create direcotry for versions file first
50
+ FileUtils.mkdir_p(File.dirname( versions_file ))
51
+
52
+ # augment the versions file with the updated versions
53
+ Snapshot.merge_packets( versions, existing_versions )
54
+
55
+ versions = sort_versions(versions)
56
+
57
+ # write the yaml file
58
+ File.open(versions_file, 'w') do |io|
59
+ io.write YAML.dump(versions)
60
+ end
61
+ end
62
+
63
+ def self.snapshot( packages, target_dir )
64
+ # todo
65
+ end
66
+
67
+ attr_reader :manifest
68
+
69
+ # Control what happens if a package fails to be snapshotted
70
+ #
71
+ # If true, the failure to snapshot a package should lead to a warning.
72
+ # Otherwise (the default), it leads to an error.
73
+ #
74
+ # @return [Boolean]
75
+ # @see initialize error_or_warn
76
+ def keep_going?; !!@keep_going end
77
+
78
+ def initialize(manifest, options = Hash.new)
79
+ @manifest = manifest
80
+ options = Kernel.validate_options options,
81
+ keep_going: false
82
+ @keep_going = options[:keep_going]
83
+ end
84
+
85
+ def snapshot_package_sets(target_dir = nil)
86
+ result = Array.new
87
+ manifest.each_package_set do |pkg_set|
88
+ next if pkg_set.local?
89
+
90
+ if vcs_info = pkg_set.snapshot(target_dir)
91
+ result << Hash["pkg_set:#{pkg_set.repository_id}", vcs_info]
92
+ else
93
+ error_or_warn(pkg_set, "cannot snapshot #{package_name}: importer snapshot failed")
94
+ end
95
+ end
96
+ result
97
+ end
98
+
99
+ def error_or_warn(package, error_msg)
100
+ if keep_going?
101
+ Autoproj.warn error_msg
102
+ else
103
+ raise Autobuild::PackageException.new(package, 'snapshot'), error_msg
104
+ end
105
+ end
106
+
107
+ def snapshot_packages(packages, target_dir = nil)
108
+ result = Array.new
109
+ packages.each do |package_name|
110
+ package = manifest.packages[package_name]
111
+ if !package
112
+ raise ArgumentError, "#{package_name} is not a known package"
113
+ end
114
+ importer = package.autobuild.importer
115
+ if !importer
116
+ error_or_warn(package, "cannot snapshot #{package_name} as it has no importer")
117
+ next
118
+ elsif !importer.respond_to?(:snapshot)
119
+ error_or_warn(package, "cannot snapshot #{package_name} as the #{importer.class} importer does not support it")
120
+ next
121
+ end
122
+
123
+ vcs_info = importer.snapshot(package.autobuild, target_dir)
124
+ if vcs_info
125
+ result << Hash[package_name, vcs_info]
126
+ else
127
+ error_or_warn(package, "cannot snapshot #{package_name}: importer snapshot failed")
128
+ end
129
+ end
130
+ result
131
+ end
132
+
133
+ # Returns the list of existing version tags
134
+ def tags(package)
135
+ importer = package.importer
136
+ all_tags = importer.run_git_bare(package, 'tag')
137
+ all_tags.find_all do |tag_name|
138
+ end
139
+ end
140
+
141
+ # Returns a package that is used to store this installs import history
142
+ #
143
+ # Its importer is guaranteed to be a git importer
144
+ #
145
+ # @return [Autobuild::Package] a package whose importer is
146
+ # {Autobuild::Git}
147
+ def import_state_log_package
148
+ manifest.main_package_set.create_autobuild_package
149
+ end
150
+
151
+ def import_state_log_ref
152
+ "refs/autoproj"
153
+ end
154
+
155
+ DEFAULT_VERSIONS_FILE_BASENAME = "50-versions.yml"
156
+
157
+ def import_state_log_file
158
+ File.join(OVERRIDES_DIR, DEFAULT_VERSIONS_FILE_BASENAME)
159
+ end
160
+
161
+ def current_import_state
162
+ main = import_state_log_package
163
+ # Try to resolve the log ref, and extract the version file from it
164
+ begin
165
+ yaml = main.importer.show(main, import_state_log_ref, import_state_log_file)
166
+ YAML.load(yaml) || Array.new
167
+ rescue Autobuild::PackageException
168
+ Array.new
169
+ end
170
+ end
171
+
172
+ def update_package_import_state(name, packages)
173
+ current_versions = current_import_state
174
+ if current_versions.empty?
175
+ # Do a full snapshot this time only
176
+ Autoproj.message " building initial autoproj import log, this may take a while"
177
+ packages = manifest.all_selected_packages.
178
+ find_all { |pkg| File.directory?(manifest.find_package(pkg).autobuild.srcdir) }
179
+ end
180
+ versions = snapshot_package_sets
181
+ versions += snapshot_packages(packages)
182
+ versions = Snapshot.merge_packets(versions, current_versions)
183
+ save_import_state(name, versions)
184
+ end
185
+
186
+ def save_import_state(name, versions)
187
+ versions = sort_versions(versions)
188
+
189
+ main = import_state_log_package
190
+ git_dir = main.importer.git_dir(main, false)
191
+ # Ensure that our ref is being logged
192
+ FileUtils.touch File.join(git_dir, 'logs', *import_state_log_ref.split("/"))
193
+ # Create the commit with the versions info
194
+ commit_id = Snapshot.create_commit(main, import_state_log_file, name) do |io|
195
+ YAML.dump(versions, io)
196
+ end
197
+ # And save it in our reflog
198
+ main.importer.run_git_bare(main, "update-ref", '-m', name, import_state_log_ref, commit_id)
199
+ end
200
+
201
+ # Create a git commit in which a file contains provided content
202
+ #
203
+ # The target git repository's current index and history is left
204
+ # unmodified. The only modification is the creation of a new dangling
205
+ # commit.
206
+ #
207
+ # It creates a temporary file and gives it to the block so that the file
208
+ # gets filled with the new content
209
+ #
210
+ # @yieldparam [Tempfile] io a temporary file
211
+ # @param [Autobuild::Package] a package object whose importer is a git
212
+ # importer. The git commit is created in this repository
213
+ # @param [String] path the file to be created or updated, relative to
214
+ # the root of the git repository
215
+ # @param [String] the commit message
216
+ # @return [String] the commit ID
217
+ def self.create_commit(pkg, path, message)
218
+ importer = pkg.importer
219
+ object_id = Tempfile.open 'autoproj-versions' do |io|
220
+ yield(io)
221
+ io.flush
222
+ importer.run_git_bare(
223
+ pkg, 'hash-object', '-w',
224
+ '--path', path, io.path).first
225
+ end
226
+
227
+ cacheinfo = ["100644", object_id, path]
228
+ if Autobuild::Git.at_least_version(2, 1)
229
+ cacheinfo = cacheinfo.join(",")
230
+ end
231
+
232
+ # Create the tree using a temporary index in order to not mess with
233
+ # the user's index state. read-tree initializes the new index and
234
+ # then we add the overrides file with update-index / write-tree
235
+ our_index = File.join(importer.git_dir(pkg, false), 'index.autoproj')
236
+ FileUtils.rm_f our_index
237
+ begin
238
+ ENV['GIT_INDEX_FILE'] = our_index
239
+ importer.run_git_bare(pkg, 'read-tree', 'HEAD')
240
+ # And add the new file
241
+ importer.run_git_bare(
242
+ pkg, 'update-index',
243
+ '--add', '--cacheinfo', *cacheinfo)
244
+ tree_id = importer.run_git_bare(pkg, 'write-tree').first
245
+ ensure
246
+ ENV.delete('GIT_INDEX_FILE')
247
+ FileUtils.rm_f our_index
248
+ end
249
+
250
+ head_id = importer.rev_parse(pkg, 'HEAD')
251
+
252
+ importer.run_git_bare(
253
+ pkg, 'commit-tree',
254
+ tree_id, '-p', head_id, input_streams: [message]).first
255
+ end
256
+ end
257
+ end
258
+ end