Module: VivlioStarter::CLI::ScaffoldBase
- Extended by:
- ScaffoldBase
- Included in:
- ScaffoldBase
- Defined in:
- lib/vivlio_starter/cli/scaffold_base.rb
Constant Summary collapse
- BASE_RELATIVE =
保存基準の置き場所(
.cache/vs/scaffold-base/)。 キャッシュなのでvs clean --cacheで消えるが、経路 A と 2-way が控えている。 File.join(Common::CACHE_DIR, 'scaffold-base')
- MERGEABLE_EXTENSIONS =
3-way マージの材料になり得る拡張子。著者が手で編集する設定・スタイルだけを 対象にする。SVG は文字列としてはテキストだが、雛形の 3,714 個はすべて twemoji の 生成物で、著者が編集して競合することはない(保存量が 1MB → 数十 MB に跳ねる)。
%w[.css .js .json .md .txt .yaml .yml].freeze
- MERGEABLE_BASENAMES =
拡張子を持たない設定ファイル
%w[Gemfile .gitignore].freeze
- MAX_MERGEABLE_BYTES =
マージ対象の上限。設定ファイルとしては十分に大きく、事故的な巨大ファイルは弾く
1_048_576
Instance Attribute Summary collapse
Instance Method Summary collapse
-
#ancestor_path(relative, expected_digest, roots) ⇒ String?
lock が記録したハッシュと中身が一致する祖先ファイルのパスを返す。.
-
#ancestor_roots(project_root = '.') ⇒ Array<String>
祖先の探索先を優先順に返す。プロジェクト内の保存基準を先に見るのは 単に近いからで、正しさは中身のハッシュ照合が担保する(順序に依存しない)。.
- #base_dir(project_root = '.') ⇒ Object
-
#installed_scaffold_dirs ⇒ Object
インストール済み vivlio-starter の雛形ディレクトリ(新しい版から順に)。 Bundler 実行下でもバンドル外の版を見たいので Gem.path を直接 glob する (
Gem::Specification.find_by_nameはバンドル内に限定されるため使えない)。. -
#merge(base_path, mine_path, theirs_path) ⇒ String?
git merge-fileによる 3-way マージ。衝突なく合流できたときだけ中身を返す。 衝突・git 不在・その他の異常はすべて nil で、呼び出し側は 2-way の競合へ落とす (衝突マーカーが著者のファイルへ紛れ込むことは無い)。. -
#mergeable?(relative) ⇒ Boolean
3-way マージの材料になり得る雛形ファイルか。 著者データ領域(contents/ 等)はそもそも競合に分類されないため対象外。.
-
#sync!(project_root, scaffold_source:, lock_files:) ⇒ Object
次回の祖先として雛形を保管する。複製するのは lock が「現在の雛形と一致」と 記録したファイルだけ——記録が進まなかったファイル(競合をスキップした等)の 基準を新版で上書きすると、次回のマージが雛形側の変更を黙って捨てる。.
Instance Attribute Details
#gem_paths ⇒ Object
80 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 80 def gem_paths = @gem_paths || Gem.path |
Instance Method Details
#ancestor_path(relative, expected_digest, roots) ⇒ String?
lock が記録したハッシュと中身が一致する祖先ファイルのパスを返す。
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 98 def ancestor_path(relative, expected_digest, roots) return nil if expected_digest.nil? || !mergeable?(relative) roots.each do |root| path = File.join(root, relative) next unless File.file?(path) next unless ScaffoldLock.file_digest(path) == expected_digest return path end nil end |
#ancestor_roots(project_root = '.') ⇒ Array<String>
祖先の探索先を優先順に返す。プロジェクト内の保存基準を先に見るのは 単に近いからで、正しさは中身のハッシュ照合が担保する(順序に依存しない)。
71 72 73 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 71 def ancestor_roots(project_root = '.') [base_dir(project_root), *installed_scaffold_dirs].select { Dir.exist?(it) } end |
#base_dir(project_root = '.') ⇒ Object
75 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 75 def base_dir(project_root = '.') = File.join(project_root, BASE_RELATIVE) |
#installed_scaffold_dirs ⇒ Object
インストール済み vivlio-starter の雛形ディレクトリ(新しい版から順に)。
Bundler 実行下でもバンドル外の版を見たいので Gem.path を直接 glob する
(Gem::Specification.find_by_name はバンドル内に限定されるため使えない)。
85 86 87 88 89 90 91 92 93 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 85 def installed_scaffold_dirs gem_paths .flat_map { Dir.glob(File.join(it, 'gems', 'vivlio-starter-*', 'lib', 'project_scaffold')) } .select { Dir.exist?(it) } .sort .reverse rescue StandardError [] end |
#merge(base_path, mine_path, theirs_path) ⇒ String?
git merge-file による 3-way マージ。衝突なく合流できたときだけ中身を返す。
衝突・git 不在・その他の異常はすべて nil で、呼び出し側は 2-way の競合へ落とす
(衝突マーカーが著者のファイルへ紛れ込むことは無い)。
自前の diff3 は書かない——マージは境界条件が多く、枯れた実装に任せるほうが安い。
git は Xcode Command Line Tools に同梱で、vs doctor が CLT の有無を既に見ている。
118 119 120 121 122 123 124 125 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 118 def merge(base_path, mine_path, theirs_path) merged, _err, status = Open3.capture3( 'git', 'merge-file', '-p', '-q', mine_path, base_path, theirs_path ) status.success? ? merged : nil rescue StandardError nil end |
#mergeable?(relative) ⇒ Boolean
3-way マージの材料になり得る雛形ファイルか。 著者データ領域(contents/ 等)はそもそも競合に分類されないため対象外。
60 61 62 63 64 65 66 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 60 def mergeable?(relative) return false if ScaffoldLock.(relative) basename = File.basename(relative) MERGEABLE_BASENAMES.include?(basename) || MERGEABLE_EXTENSIONS.include?(File.extname(basename).downcase) end |
#sync!(project_root, scaffold_source:, lock_files:) ⇒ Object
次回の祖先として雛形を保管する。複製するのは lock が「現在の雛形と一致」と 記録したファイルだけ——記録が進まなかったファイル(競合をスキップした等)の 基準を新版で上書きすると、次回のマージが雛形側の変更を黙って捨てる。
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/vivlio_starter/cli/scaffold_base.rb', line 131 def sync!(project_root, scaffold_source:, lock_files:) destination_root = base_dir(project_root) ScaffoldLock.scaffold_files(scaffold_source).each do |relative| next unless mergeable?(relative) source = File.join(scaffold_source, relative) next if File.size(source) > MAX_MERGEABLE_BYTES next unless lock_files[relative] == ScaffoldLock.file_digest(source) destination = File.join(destination_root, relative) FileUtils.mkdir_p(File.dirname(destination)) FileUtils.cp(source, destination) end rescue StandardError => e # 基準の保管は「次回が楽になる」だけの処理。失敗しても upgrade 自体は成功させる Common.log_debug("[upgrade] 雛形の基準を保管できませんでした: #{e.}") end |