Class: Autoproj::Ops::Snapshot

Inherits:
Object
  • Object
show all
Defined in:
lib/autoproj/ops/snapshot.rb

Constant Summary collapse

DEFAULT_VERSIONS_FILE_BASENAME =
"50-versions.yml"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(manifest, keep_going: false) ⇒ Snapshot

Returns a new instance of Snapshot.



81
82
83
84
# File 'lib/autoproj/ops/snapshot.rb', line 81

def initialize(manifest, keep_going: false)
    @manifest = manifest
    @keep_going = keep_going
end

Instance Attribute Details

#manifestObject (readonly)

Returns the value of attribute manifest.



68
69
70
# File 'lib/autoproj/ops/snapshot.rb', line 68

def manifest
  @manifest
end

Class Method Details

.create_commit(pkg, path, message, parent_id = nil, real_author: true) {|io| ... } ⇒ String

Create a git commit in which a file contains provided content

The target git repository’s current index and history is left unmodified. The only modification is the creation of a new dangling commit.

It creates a temporary file and gives it to the block so that the file gets filled with the new content

Parameters:

  • a (Autobuild::Package)

    package object whose importer is a git importer. The git commit is created in this repository

  • path (String)

    the file to be created or updated, relative to the root of the git repository

  • the (String)

    commit message

Yield Parameters:

  • io (Tempfile)

    a temporary file

Returns:

  • (String)

    the commit ID



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/autoproj/ops/snapshot.rb', line 254

def self.create_commit(pkg, path, message, parent_id = nil, real_author: true)
    importer = pkg.importer
    object_id = Tempfile.open "autoproj-versions" do |io|
        yield(io)
        io.flush
        importer.run_git_bare(
            pkg, "hash-object", "-w",
            "--path", path, io.path
        ).first
    end

    cacheinfo = ["100644", object_id, path]
    cacheinfo = cacheinfo.join(",") if Autobuild::Git.at_least_version(2, 1)

    parent_id ||= importer.rev_parse(pkg, "HEAD")

    env = Hash.new
    unless real_author
        env["GIT_AUTHOR_NAME"] = "autoproj"
        env["GIT_AUTHOR_EMAIL"] = "autoproj"
        env["GIT_COMMITTER_NAME"] = "autoproj"
        env["GIT_COMMITTER_EMAIL"] = "autoproj"
    end

    # Create the tree using a temporary index in order to not mess with
    # the user's index state. read-tree initializes the new index and
    # then we add the overrides file with update-index / write-tree
    our_index = File.join(importer.git_dir(pkg, false), "index.autoproj")
    FileUtils.rm_f our_index
    begin
        ENV["GIT_INDEX_FILE"] = our_index
        importer.run_git_bare(pkg, "read-tree", parent_id)
        # And add the new file
        importer.run_git_bare(
            pkg, "update-index",
            "--add", "--cacheinfo", *cacheinfo
        )
        tree_id = importer.run_git_bare(pkg, "write-tree").first
    ensure
        ENV.delete("GIT_INDEX_FILE")
        FileUtils.rm_f our_index
    end

    importer.run_git_bare(
        pkg, "commit-tree",
        tree_id, "-p", parent_id, env: env, input_streams: [message]
    ).first
end

.import_state_log_refObject



187
188
189
# File 'lib/autoproj/ops/snapshot.rb', line 187

def self.import_state_log_ref
    "refs/autoproj"
end

.merge_packets(overrides, state) ⇒ Object

Update version control information with new choices

The two parameters are formatted as expected in the version_control and overrides fields in source.yml / overrides.yml, that is (in YAML)

- package_name:
  version: '10'
  control: '20'
  info: '30'

The two parameters are expected to only use full package names, and not regular expressions

Parameters:

  • overrides (Array<String=>Hash>)

    the information that should augment the current state

  • state (Array<String=>Hash>)

    the current state

  • the (Hash)

    updated information



21
22
23
24
25
26
27
28
# File 'lib/autoproj/ops/snapshot.rb', line 21

def self.merge_packets(overrides, state)
    overriden = overrides.map { |entry| entry.keys.first }.to_set
    filtered_state = state.find_all do |pkg|
        name, = pkg.first
        !overriden.include?(name)
    end
    filtered_state + overrides
end

.snapshot(packages, target_dir) ⇒ Object



64
65
66
# File 'lib/autoproj/ops/snapshot.rb', line 64

def self.snapshot(packages, target_dir)
    # todo
end

.update_log_available?(manifest) ⇒ Boolean

Returns:

  • (Boolean)


30
31
32
33
34
35
# File 'lib/autoproj/ops/snapshot.rb', line 30

def self.update_log_available?(manifest)
    new(manifest).import_state_log_package
    true
rescue ArgumentError
    false
end

Instance Method Details

#current_import_stateObject



197
198
199
200
201
202
203
204
205
206
# File 'lib/autoproj/ops/snapshot.rb', line 197

def current_import_state
    main = import_state_log_package
    # Try to resolve the log ref, and extract the version file from it
    begin
        yaml = main.importer.show(main, self.class.import_state_log_ref, import_state_log_file)
        YAML.load(yaml) || Array.new
    rescue Autobuild::PackageException
        Array.new
    end
end

#error_or_warn(package, error) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/autoproj/ops/snapshot.rb', line 107

def error_or_warn(package, error)
    if error.kind_of?(Interrupt)
        raise
    elsif keep_going?
        error = error.message unless error.respond_to?(:to_str)
        Autoproj.warn error
    elsif error.respond_to?(:to_str)
        raise Autobuild::PackageException.new(package, "snapshot"), error
    else
        raise
    end
end

#import_state_log_fileObject



193
194
195
# File 'lib/autoproj/ops/snapshot.rb', line 193

def import_state_log_file
    File.join(Workspace::OVERRIDES_DIR, DEFAULT_VERSIONS_FILE_BASENAME)
end

#import_state_log_packageAutobuild::Package

Returns a package that is used to store this installs import history

Its importer is guaranteed to be a git importer

Returns:

  • (Autobuild::Package)

    a package whose importer is Autobuild::Git



172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/autoproj/ops/snapshot.rb', line 172

def import_state_log_package
    pkg = manifest.main_package_set.create_autobuild_package
    unless pkg.importer
        if Autobuild::Git.can_handle?(pkg.srcdir)
            pkg.importer = Autobuild.git(pkg.srcdir)
        end
    end

    unless pkg.importer.kind_of?(Autobuild::Git)
        raise ArgumentError, "cannot use autoproj auto-import feature if the main configuration is not managed under git"
    end

    pkg
end

#keep_going?Boolean

Control what happens if a package fails to be snapshotted

If true, the failure to snapshot a package should lead to a warning. Otherwise (the default), it leads to an error.

Returns:

  • (Boolean)

See Also:



77
78
79
# File 'lib/autoproj/ops/snapshot.rb', line 77

def keep_going?
    !!@keep_going
end

#save_import_state(name, versions) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/autoproj/ops/snapshot.rb', line 223

def save_import_state(name, versions)
    versions = sort_versions(versions)

    main = import_state_log_package
    git_dir = main.importer.git_dir(main, false)
    # Ensure that our ref is being logged
    FileUtils.touch File.join(git_dir, "logs", *self.class.import_state_log_ref.split("/"))
    # Create the commit with the versions info
    commit_id = Snapshot.create_commit(main, import_state_log_file, name, real_author: false) do |io|
        YAML.dump(versions, io)
    end
    # And save it in our reflog
    main.importer.run_git_bare(main, "update-ref", "-m", name, self.class.import_state_log_ref, commit_id)
end

#save_versions(versions, versions_file, replace: false) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/autoproj/ops/snapshot.rb', line 43

def save_versions(versions, versions_file, replace: false)
    existing_versions = Array.new
    if !replace && File.exist?(versions_file)
        existing_versions = YAML.load(File.read(versions_file)) ||
                            Array.new
    end

    # create direcotry for versions file first
    FileUtils.mkdir_p(File.dirname(versions_file))

    # augment the versions file with the updated versions
    versions = Snapshot.merge_packets(versions, existing_versions)

    versions = sort_versions(versions)

    # write the yaml file
    File.open(versions_file, "w") do |io|
        io.write YAML.dump(versions)
    end
end

#snapshot_package_sets(target_dir = nil, only_local: true) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/autoproj/ops/snapshot.rb', line 86

def snapshot_package_sets(target_dir = nil, only_local: true)
    result = Array.new
    manifest.each_package_set do |pkg_set|
        next if pkg_set.local?

        vcs_info =
            begin pkg_set.snapshot(target_dir, only_local: only_local)
            rescue Exception => e
                error_or_warn(pkg_set, e)
                next
            end

        if vcs_info
            result << Hash["pkg_set:#{pkg_set.repository_id}", vcs_info]
        else
            error_or_warn(pkg_set, "cannot snapshot package set #{pkg_set.name}: importer snapshot failed")
        end
    end
    result
end

#snapshot_packages(packages, target_dir = nil, only_local: true, fingerprint: false) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/autoproj/ops/snapshot.rb', line 120

def snapshot_packages(packages, target_dir = nil, only_local: true, fingerprint: false)
    result = Array.new
    fingerprint_memo = Hash.new
    packages.each do |package_name|
        package = manifest.find_package_definition(package_name)
        unless package
            raise ArgumentError, "#{package_name} is not a known package"
        end

        importer = package.autobuild.importer
        if !importer
            error_or_warn(package, "cannot snapshot #{package_name} as it has no importer")
            next
        elsif !importer.respond_to?(:snapshot)
            error_or_warn(package, "cannot snapshot #{package_name} as the #{importer.class} importer does not support it")
            next
        end

        vcs_info =
            begin importer.snapshot(package.autobuild, target_dir, only_local: only_local)
            rescue Exception => e
                error_or_warn(package, e)
                next
            end

        if fingerprint
            vcs_info["fingerprint"] = package.autobuild.fingerprint(memo: fingerprint_memo)
        end

        if vcs_info
            result << Hash[package_name, vcs_info]
        else
            error_or_warn(package, "cannot snapshot #{package_name}: importer snapshot failed")
        end
    end
    result
end

#sort_versions(versions) ⇒ Object



37
38
39
40
41
# File 'lib/autoproj/ops/snapshot.rb', line 37

def sort_versions(versions)
    pkg_sets, pkgs = versions.partition { |vcs| vcs.keys.first =~ /^pkg_set:/ }
    pkg_sets.sort_by { |vcs| vcs.keys.first } +
        pkgs.sort_by { |vcs| vcs.keys.first }
end

#tags(package) ⇒ Object

Returns the list of existing version tags



159
160
161
162
163
164
# File 'lib/autoproj/ops/snapshot.rb', line 159

def tags(package)
    importer = package.importer
    all_tags = importer.run_git_bare(package, "tag")
    all_tags.find_all do |tag_name|
    end
end

#update_package_import_state(name, packages) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/autoproj/ops/snapshot.rb', line 208

def update_package_import_state(name, packages)
    current_versions = current_import_state
    if current_versions.empty?
        # Do a full snapshot this time only
        Autoproj.message "  building initial autoproj import log, this may take a while"
        packages = manifest.all_selected_source_packages
                           .find_all { |pkg| File.directory?(pkg.autobuild.srcdir) }
                           .map(&:name)
    end
    versions  = snapshot_package_sets
    versions += snapshot_packages(packages)
    versions = Snapshot.merge_packets(versions, current_versions)
    save_import_state(name, versions)
end