Class: Tetra::Project

Inherits:
Object
  • Object
show all
Includes:
Logging, ProjectIniter
Defined in:
lib/tetra/project.rb

Overview

encapsulates a Tetra project directory

Constant Summary collapse

TEMPLATE_PATH =

path of the project template files

File.join(__dir__, "..", "template")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

#log

Methods included from ProjectIniter

#commit_source_archive, included, #template_files

Constructor Details

#initialize(path) ⇒ Project

Returns a new instance of Project.



14
15
16
17
# File 'lib/tetra/project.rb', line 14

def initialize(path)
  @full_path = Tetra::Project.find_project_dir(File.expand_path(path))
  @git = Tetra::Git.new(@full_path)
end

Instance Attribute Details

#full_pathObject (readonly)

Returns the value of attribute full_path.



12
13
14
# File 'lib/tetra/project.rb', line 12

def full_path
  @full_path
end

Class Method Details

.find_project_dir(starting_dir) ⇒ Object

finds the project directory up in the tree, like git does



32
33
34
35
36
37
38
39
# File 'lib/tetra/project.rb', line 32

def self.find_project_dir(starting_dir)
  result = starting_dir
  result = File.expand_path("..", result) while project?(result) == false && result != "/"

  raise NoProjectDirectoryError, starting_dir if result == "/"

  result
end

Instance Method Details

#abortObject

ends a dry-run assuming the build went wrong reverts src/ and kit/ directories



88
89
90
91
# File 'lib/tetra/project.rb', line 88

def abort
  @git.revert_directories(%w[src kit], @git.latest_id("tetra: dry-run-started"))
  @git.undo_last_commit
end

#archive_kitObject

archives a tarball of kit/ in packages/ the latest commit marked as dry-run-finished is taken as the version



172
173
174
175
176
177
178
# File 'lib/tetra/project.rb', line 172

def archive_kit
  from_directory do
    id = @git.latest_id("tetra: dry-run-finished")
    destination_path = File.join(full_path, packages_dir, "#{name}-kit", "#{name}-kit.tar.xz")
    @git.archive("kit", id, destination_path)
  end
end

#build_script_linesObject



163
164
165
166
167
168
# File 'lib/tetra/project.rb', line 163

def build_script_lines
  @git.latest_comment("tetra: dry-run-finished")
      .split("\n")
      .filter_map { |line| line[/^tetra: build-script-line: (.+)$/, 1] }
      .sort
end

#commit_sources(message, new_tarball) ⇒ Object

commits files in the src/ dir as a patch or tarball update



94
95
96
97
98
99
100
101
102
103
# File 'lib/tetra/project.rb', line 94

def commit_sources(message, new_tarball)
  from_directory do
    comments = "#{message}\n"
    if new_tarball
      comments << "\ntetra: sources-tarball"
      @git.disable_special_files("src")
    end
    @git.commit_directories(["src"], comments)
  end
end

#dry_runObject

starts a dry running phase: files added to kit/ will be added to the kit package, src/ will be reset at the current state when finished



52
53
54
55
56
# File 'lib/tetra/project.rb', line 52

def dry_run
  current_directory = Pathname.new(Dir.pwd).relative_path_from(Pathname.new(@full_path))

  @git.commit_directories(%w[src kit], "Dry-run started\n\ntetra: dry-run-started: #{current_directory}")
end

#dry_running?Boolean

returns true iff we are currently dry-running

Returns:

  • (Boolean)


59
60
61
62
63
# File 'lib/tetra/project.rb', line 59

def dry_running?
  latest_comment = @git.latest_comment("tetra: dry-run-")
  # MODERNIZATION: Use match? instead of =~
  !latest_comment.nil? && !latest_comment.include?("tetra: dry-run-finished")
end

#finish(build_script_lines) ⇒ Object

ends a dry-run assuming a successful build:

  • reverts sources as before dry-run
  • saves the list of generated files in git comments
  • saves the build script lines in git comments


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/tetra/project.rb', line 69

def finish(build_script_lines)
  # keep track of changed files
  start_id = @git.latest_id("tetra: dry-run-started")
  changed_files = @git.changed_files("src", start_id)

  # revert to pre-dry-run status
  @git.revert_directories(["src"], start_id)

  # prepare commit comments
  comments = ["Dry run finished\n", "tetra: dry-run-finished"]
  comments += changed_files.map { |f| "tetra: file-changed: #{f}" }
  comments += build_script_lines.map { |l| "tetra: build-script-line: #{l}" }

  # commit end of dry run
  @git.commit_directories(["kit"], comments.join("\n"))
end

#from_directory(subdirectory = "") ⇒ Object

runs a block from the project directory or a subdirectory



146
147
148
# File 'lib/tetra/project.rb', line 146

def from_directory(subdirectory = "", &)
  Dir.chdir(File.join(@full_path, subdirectory), &)
end

#latest_dry_run_directoryObject

returns the latest dry run start directory



151
152
153
# File 'lib/tetra/project.rb', line 151

def latest_dry_run_directory
  @git.latest_comment("tetra: dry-run-started")[/tetra: dry-run-started: (.*)$/, 1]
end

#merge_new_content(new_content, path, comment, kind) ⇒ Object

replaces content in path with new_content, commits using comment and 3-way merges new and old content with the previous version of file of the same kind, if it exists. returns the number of conflicts



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/tetra/project.rb', line 109

def merge_new_content(new_content, path, comment, kind)
  from_directory do
    log.debug "merging new content to #{path} of kind #{kind}"
    already_existing = File.exist?(path)

    generated_comment = "tetra: generated-#{kind}"
    whole_comment = [comment, generated_comment].join("\n\n")

    if already_existing
      unless @git.latest_id(generated_comment)
        log.debug "committing new file"
        @git.commit_file(path, whole_comment)
      end
      log.debug "moving #{path} to #{path}.tetra_user_edited"
      File.rename(path, "#{path}.tetra_user_edited")
    end

    previous_id = @git.latest_id(generated_comment)

    File.write(path, new_content)
    log.debug "committing new content: #{comment}"
    @git.commit_file(path, whole_comment)

    if already_existing
      # 3-way merge
      conflict_count = @git.merge_with_id(path, "#{path}.tetra_user_edited", previous_id)
      File.delete("#{path}.tetra_user_edited")

      @git.commit_file(path, "User changes merged back") if conflict_count.zero?

      return conflict_count
    end
    return 0
  end
end

#nameObject



19
20
21
# File 'lib/tetra/project.rb', line 19

def name
  File.basename(@full_path)
end

#packages_dirObject



27
28
29
# File 'lib/tetra/project.rb', line 27

def packages_dir
  "packages"
end

#produced_filesObject

returns a list of files produced during the last dry-run



156
157
158
159
160
161
# File 'lib/tetra/project.rb', line 156

def produced_files
  @git.latest_comment("tetra: dry-run-finished")
      .split("\n")
      .filter_map { |line| line[%r{^tetra: file-changed: src/(.+)$}, 1] }
      .sort
end

#purge_jarsObject

moves any .jar from src/ to kit/ and links it back



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/tetra/project.rb', line 202

def purge_jars
  from_directory do
    result = []
    Dir.glob("src/**/*.jar").each do |file|
      next if File.symlink?(file)

      new_location = File.join("kit", "jars", File.basename(file))
      FileUtils.mv(file, new_location)

      # Calculate relative path for symlink
      link_target = Pathname.new(new_location)
                            .relative_path_from(Pathname.new(file).dirname)
                            .to_s

      File.symlink(link_target, file)
      result << [file, new_location]
    end

    result
  end
end

#src_archiveObject

returns the name of the source archive file, if any



181
182
183
184
185
186
187
# File 'lib/tetra/project.rb', line 181

def src_archive
  from_directory do
    Dir.glob(File.join("packages", name, "*")).find do |file|
      File.file?(file) && !file.match?(/\.(spec|sh|patch)$/)
    end&.then { |f| File.basename(f) }
  end
end

#src_patched?Boolean

checks whether there were edits to src/ since last mark

Returns:

  • (Boolean)


43
44
45
46
47
# File 'lib/tetra/project.rb', line 43

def src_patched?
  from_directory do
    @git.changed_files("src", "HEAD").any?
  end
end

#versionObject



23
24
25
# File 'lib/tetra/project.rb', line 23

def version
  @git.latest_id("tetra: dry-run-finished")
end

#write_source_patchesObject

generates patches of src/ in packages/ the latest commit marked as tarball is taken as the base version, other commits are assumed to be patches on top returns filenames



193
194
195
196
197
198
199
# File 'lib/tetra/project.rb', line 193

def write_source_patches
  from_directory do
    id = @git.latest_id("tetra: sources-tarball")
    destination_path = File.join(full_path, packages_dir, name)
    @git.format_patch("src", id, destination_path)
  end
end