mirrorfile 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 513175ce2e6de6e4e0a0a810d844ce5a280fd3e899cf490b289249054a8c37f9
4
- data.tar.gz: 9a8ca82bdd368ef020c97fbf156aba1e5d3cad6860e5eef4e02226165c167277
3
+ metadata.gz: 4216911dce60b274bfaf08354c6ebedba8a3e226750a655236fbed0dbbaf8d13
4
+ data.tar.gz: c7b0bd4bc994641810ea9ff115e198171ef81b929d539f1d9942f198fad0ebb5
5
5
  SHA512:
6
- metadata.gz: 0046b1bde5286137d682244bf597e8f72a0d43a206c6013b4dc470b08b9dead8ebbe0d7aa390c63437330b9ccf15c93dbefc99ca8e5a0bd346606166232f38b9
7
- data.tar.gz: fa70c541f5cdaa6e17c2a654bf07a094cb2037a405409a9fda00fe8f7d725331cb6b66737915fe1df8b39a2f7c8bdbe4bcd887a1a536fd7c1420fa8bcbbdd971
6
+ metadata.gz: d45d8d2a3c7eb6cd6e39df812c499b5a595daa22949bbbbaeedcfac03ffa0c05483ecd1579fa599ba11e7c816fcef452b111f965afab81c520e30059e08c3b91
7
+ data.tar.gz: 53e4c4554638cbd36571be7a7ef852803b5c10f6e714d3bb7c872405320681413dbb8cb7dbddbc633609bae6ea5887f9f1af6b4822086db1ee0f0b429e374c3f
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.1.0] - 2026-10-21
11
+
12
+ ### Added
13
+
14
+ - `mirror init` now declares `mirrorfile` in an existing `Gemfile`: when the
15
+ project root has a `Gemfile` that does not already depend on the gem,
16
+ `Mirrorfile::Mirror#init` runs `bundle add mirrorfile` before creating the
17
+ Mirrorfile. Projects without a `Gemfile` are unaffected, and an existing
18
+ declaration is left alone. A failing `bundle add` raises `Mirrorfile::Error`.
19
+
20
+ ### Changed
21
+
22
+ - `gem_kit` (`~> 0.2`) is now a runtime dependency of the gem, not just a
23
+ development one.
24
+
10
25
  ## [0.1.0] - 2024-01-01
11
26
 
12
27
  ### Added
data/README.md CHANGED
@@ -28,6 +28,9 @@ mirror update # pulls existing repos
28
28
  mirror list # shows defined mirrors
29
29
  ```
30
30
 
31
+ When a Gemfile exists, `mirror init` adds and installs mirrorfile
32
+ via Bundler unless it is already declared as a dependency.
33
+
31
34
  ## Example Mirrorfile
32
35
 
33
36
  You can change sources mid-file:
@@ -21,7 +21,7 @@ module Mirrorfile
21
21
  class CLI
22
22
  # Available CLI commands
23
23
  # @return [Array<String>] list of valid commands
24
- COMMANDS = %w[init install update list git help].freeze
24
+ COMMANDS = %w[init install update list git migrate-to-v1 help].freeze
25
25
 
26
26
  # Creates a new CLI instance.
27
27
  #
@@ -70,8 +70,9 @@ module Mirrorfile
70
70
  when 'install' then install
71
71
  when 'update' then update
72
72
  when 'list' then list
73
- when 'git' then git(args.drop(1))
74
- when 'help' then help
73
+ when 'git' then git(args.drop(1))
74
+ when 'migrate-to-v1' then migrate_to_v1
75
+ when 'help' then help
75
76
  when '-h', '--help' then help
76
77
  when '-v', '--version' then version
77
78
  else usage
@@ -152,6 +153,14 @@ module Mirrorfile
152
153
  system('git', '--git-dir=.git.mirror', *args)
153
154
  end
154
155
 
156
+ # Executes the migrate-to-v1 command.
157
+ #
158
+ # @return [void]
159
+ # @api private
160
+ def migrate_to_v1
161
+ Mirror.new.migrate_to_v1
162
+ end
163
+
155
164
  # Displays help information.
156
165
  #
157
166
  # @return [void]
@@ -163,13 +172,15 @@ module Mirrorfile
163
172
  Usage: mirror <command>
164
173
 
165
174
  Commands:
166
- init Initialize project with Mirrorfile, .gitignore entry,
167
- and Zeitwerk initializer for Rails projects
168
- install Clone repositories that don't exist locally
169
- update Pull latest changes for existing repositories
170
- list Show all defined mirrors
171
- git Run git commands against a .git.mirror directory
172
- help Show this help message
175
+ init Initialize project with Mirrorfile, .gitignore entry,
176
+ and Zeitwerk initializer for Rails projects;
177
+ add mirrorfile to an existing Gemfile
178
+ install Clone repositories that don't exist locally
179
+ update Pull latest changes for existing repositories
180
+ list Show all defined mirrors
181
+ git Run git commands against a .git.mirror directory
182
+ migrate-to-v1 Upgrade legacy mirrors to v1.0 format
183
+ help Show this help message
173
184
 
174
185
  Options:
175
186
  -h, --help Show this help message
@@ -212,12 +223,14 @@ module Mirrorfile
212
223
  Usage: mirror <command>
213
224
 
214
225
  Commands:
215
- init Create Mirrorfile, .gitignore entry, and Zeitwerk initializer (Rails only)
216
- install Clone repositories that don't exist locally
217
- update Pull latest changes for existing repositories
218
- list Show all defined mirrors
219
- git Run git commands against a .git.mirror directory
220
- help Show detailed help
226
+ init Create Mirrorfile, .gitignore entry, and Zeitwerk initializer (Rails only)
227
+ Add mirrorfile to an existing Gemfile
228
+ install Clone repositories that don't exist locally
229
+ update Pull latest changes for existing repositories
230
+ list Show all defined mirrors
231
+ git Run git commands against a .git.mirror directory
232
+ migrate-to-v1 Upgrade legacy mirrors to v1.0 format
233
+ help Show detailed help
221
234
 
222
235
  Run 'mirror help' for more information.
223
236
  USAGE
@@ -103,6 +103,7 @@ module Mirrorfile
103
103
  # - Creates a Mirrorfile with example syntax
104
104
  # - Adds /mirrors to .gitignore (creates file if needed)
105
105
  # - Creates a Rails initializer for Zeitwerk autoloading (Rails projects only)
106
+ # - Adds mirrorfile as a dependency if a Gemfile exists
106
107
  #
107
108
  # Existing files are not overwritten.
108
109
  #
@@ -117,6 +118,7 @@ module Mirrorfile
117
118
  # @see #setup_gitignore
118
119
  # @see #setup_zeitwerk
119
120
  def init
121
+ setup_dependency
120
122
  create_mirrorfile
121
123
  setup_gitignore
122
124
  setup_templates
@@ -136,6 +138,23 @@ module Mirrorfile
136
138
  @mirrorfile.entries.to_a
137
139
  end
138
140
 
141
+ # Migrates the project to mirrorfile v1.0.
142
+ #
143
+ # Performs the following steps:
144
+ # - Updates Gemfile version constraint to ~> 1.0 (if Gemfile exists)
145
+ # - Updates the system gem if installed
146
+ # - Installs template files (.envrc, README.md) into mirrors/
147
+ # - Renames .git directories to .git.mirror in existing mirrors
148
+ #
149
+ # @return [void]
150
+ def migrate_to_v1
151
+ update_gemfile
152
+ update_system_gem
153
+ setup_templates
154
+ rename_git_dirs
155
+ puts 'Migration to v1.0 complete.'
156
+ end
157
+
139
158
  # Returns whether any mirrors use legacy .git directories.
140
159
  #
141
160
  # A project is considered legacy if any subdirectory of mirrors/
@@ -152,6 +171,28 @@ module Mirrorfile
152
171
 
153
172
  private
154
173
 
174
+ # Adds mirrorfile to the project's bundle, preserving existing declarations.
175
+ #
176
+ # @return [void]
177
+ # @raise [Error] if Bundler cannot add the dependency
178
+ # @api private
179
+ def setup_dependency
180
+ gemfile = root.join('Gemfile').expand_path
181
+ return unless gemfile.file?
182
+
183
+ require 'bundler'
184
+
185
+ Bundler.with_unbundled_env do
186
+ definition = Bundler::Dsl.new
187
+ definition.eval_gemfile(gemfile)
188
+ next if definition.dependencies.any? { |dependency| dependency.name == 'mirrorfile' }
189
+
190
+ unless system('bundle', 'add', 'mirrorfile', chdir: root.to_s)
191
+ raise Error, 'Failed to run bundle add mirrorfile'
192
+ end
193
+ end
194
+ end
195
+
155
196
  # Loads and parses the Mirrorfile.
156
197
  #
157
198
  # @return [Mirrorfile] the parsed Mirrorfile
@@ -273,6 +314,63 @@ module Mirrorfile
273
314
  readme_dest.exist? || FileUtils.cp(templates_dir.join('README.md.template'), readme_dest)
274
315
  end
275
316
 
317
+ # Updates the Gemfile to use mirrorfile ~> 1.0.
318
+ #
319
+ # Replaces any existing mirrorfile gem declaration with one
320
+ # constrained to ~> 1.0. Does nothing if Gemfile doesn't exist
321
+ # or doesn't reference mirrorfile.
322
+ #
323
+ # @return [void]
324
+ # @api private
325
+ def update_gemfile
326
+ gemfile = root.join('Gemfile')
327
+ return unless gemfile.exist?
328
+
329
+ content = gemfile.read
330
+ updated = content.gsub(/^(\s*gem\s+["']mirrorfile["']).*$/, '\1, "~> 1.0"')
331
+
332
+ return unless content != updated
333
+
334
+ gemfile.write(updated)
335
+ puts 'Updated Gemfile to mirrorfile ~> 1.0'
336
+ end
337
+
338
+ # Updates the system-installed mirrorfile gem.
339
+ #
340
+ # Checks if the gem is installed globally and runs gem update
341
+ # if so.
342
+ #
343
+ # @return [void]
344
+ # @api private
345
+ def update_system_gem
346
+ return if ENV['MIRRORFILE_SKIP_GEM_UPDATE']
347
+ return unless system('gem', 'list', '-i', 'mirrorfile', out: File::NULL, err: File::NULL)
348
+
349
+ puts 'Updating system gem...'
350
+ system('gem', 'update', 'mirrorfile')
351
+ end
352
+
353
+ # Renames .git directories to .git.mirror in existing mirrors.
354
+ #
355
+ # Scans all subdirectories of mirrors/ for .git directories
356
+ # and renames them. Skips entries that already have .git.mirror.
357
+ #
358
+ # @return [void]
359
+ # @api private
360
+ def rename_git_dirs
361
+ return unless mirrors_dir.exist?
362
+
363
+ mirrors_dir.children.select(&:directory?).each do |mirror_path|
364
+ git_dir = mirror_path.join('.git')
365
+ git_mirror_dir = mirror_path.join('.git.mirror')
366
+
367
+ if git_dir.exist? && !git_mirror_dir.exist?
368
+ File.rename(git_dir.to_s, git_mirror_dir.to_s)
369
+ puts "Renamed #{mirror_path.basename}/.git to .git.mirror"
370
+ end
371
+ end
372
+ end
373
+
276
374
  # Determines if the current project is a Rails application by checking
277
375
  # for the standard Rails application entrypoint.
278
376
  #
@@ -3,5 +3,5 @@
3
3
  module Mirrorfile
4
4
  # Current version of the Mirrorfile gem
5
5
  # @return [String] the semantic version string
6
- VERSION = '1.0.0'
6
+ VERSION = '1.1.0'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mirrorfile
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Kidd
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-01 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: gem_kit
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.2'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: minitest
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -77,15 +91,10 @@ executables:
77
91
  extensions: []
78
92
  extra_rdoc_files: []
79
93
  files:
80
- - ".envrc"
81
- - ".yardopts"
82
94
  - CHANGELOG.md
83
95
  - LICENSE.txt
84
96
  - README.md
85
- - Rakefile
86
97
  - exe/mirror
87
- - flake.lock
88
- - flake.nix
89
98
  - lib/mirrorfile.rb
90
99
  - lib/mirrorfile/cli.rb
91
100
  - lib/mirrorfile/cli_legacy.rb
@@ -93,7 +102,6 @@ files:
93
102
  - lib/mirrorfile/mirror.rb
94
103
  - lib/mirrorfile/mirrorfile.rb
95
104
  - lib/mirrorfile/version.rb
96
- - mirrorfile.gemspec
97
105
  - templates/README.md.template
98
106
  - templates/envrc.template
99
107
  homepage: https://github.com/n-at-han-k/mirrorfile
data/.envrc DELETED
@@ -1 +0,0 @@
1
- use flake
data/.yardopts DELETED
@@ -1,7 +0,0 @@
1
- --markup markdown
2
- --markup-provider redcarpet
3
- --charset utf-8
4
- --output-dir doc
5
- --readme README.md
6
- --title "GitMirror Documentation"
7
- lib/**/*.rb
data/Rakefile DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
5
- require "yard"
6
-
7
- Rake::TestTask.new(:test) do |t|
8
- t.libs << "test"
9
- t.libs << "lib"
10
- t.test_files = FileList["test/**/*_test.rb"]
11
- end
12
-
13
- YARD::Rake::YardocTask.new do |t|
14
- t.files = ["lib/**/*.rb"]
15
- t.options = ["--markup", "markdown"]
16
- end
17
-
18
- task default: :test
data/flake.lock DELETED
@@ -1,61 +0,0 @@
1
- {
2
- "nodes": {
3
- "flake-utils": {
4
- "inputs": {
5
- "systems": "systems"
6
- },
7
- "locked": {
8
- "lastModified": 1731533236,
9
- "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
10
- "owner": "numtide",
11
- "repo": "flake-utils",
12
- "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
13
- "type": "github"
14
- },
15
- "original": {
16
- "owner": "numtide",
17
- "repo": "flake-utils",
18
- "type": "github"
19
- }
20
- },
21
- "nixpkgs": {
22
- "locked": {
23
- "lastModified": 1772773019,
24
- "narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=",
25
- "owner": "nixos",
26
- "repo": "nixpkgs",
27
- "rev": "aca4d95fce4914b3892661bcb80b8087293536c6",
28
- "type": "github"
29
- },
30
- "original": {
31
- "owner": "nixos",
32
- "ref": "nixos-unstable",
33
- "repo": "nixpkgs",
34
- "type": "github"
35
- }
36
- },
37
- "root": {
38
- "inputs": {
39
- "flake-utils": "flake-utils",
40
- "nixpkgs": "nixpkgs"
41
- }
42
- },
43
- "systems": {
44
- "locked": {
45
- "lastModified": 1681028828,
46
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47
- "owner": "nix-systems",
48
- "repo": "default",
49
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50
- "type": "github"
51
- },
52
- "original": {
53
- "owner": "nix-systems",
54
- "repo": "default",
55
- "type": "github"
56
- }
57
- }
58
- },
59
- "root": "root",
60
- "version": 7
61
- }
data/flake.nix DELETED
@@ -1,34 +0,0 @@
1
- {
2
- inputs = {
3
- nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4
-
5
- flake-utils.url = "github:numtide/flake-utils";
6
- flake-utils.inputs.nixpkgs.follows = "nixpkgs";
7
- };
8
-
9
- outputs = { self, nixpkgs, flake-utils }:
10
- flake-utils.lib.eachDefaultSystem (system:
11
- let
12
- pkgs = nixpkgs.legacyPackages.${system};
13
- in
14
- {
15
- devShells.default = pkgs.mkShell {
16
- packages = with pkgs; [
17
- ruby_3_4
18
- bundler
19
- ];
20
-
21
- shellHook = ''
22
- export BUNDLE_PATH=".bundler"
23
- export GEM_PATH=".bundler/ruby/3.4.0"
24
-
25
- export PATH="$PWD/bin:$PATH"
26
- export PATH=".bundler/ruby/3.4.0/bin:$PATH"
27
- '';
28
-
29
- };
30
-
31
- packages.default = pkgs.hello;
32
- }
33
- );
34
- }
data/mirrorfile.gemspec DELETED
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/mirrorfile/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "mirrorfile"
7
- spec.version = Mirrorfile::VERSION
8
- spec.authors = ["Nathan Kidd"]
9
- spec.email = ["nathankidd@hey.com"]
10
-
11
- spec.summary = "Manage local mirrors of git repositories"
12
-
13
- spec.description = <<~DESC
14
- Mirrorfile provides a DSL similar to Bundler's Gemfile for managing local
15
- mirrors of git repositories. Clone and keep repositories updated with
16
- simple commands. Includes Rails/Zeitwerk integration for autoloading
17
- mirrored code.
18
- DESC
19
-
20
- spec.homepage = "https://github.com/n-at-han-k/mirrorfile"
21
- spec.license = "MIT"
22
- spec.required_ruby_version = ">= 3.2.0"
23
-
24
- spec.metadata["homepage_uri"] = spec.homepage
25
- spec.metadata["source_code_uri"] = spec.homepage
26
- spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
27
- spec.metadata["documentation_uri"] = spec.homepage
28
- spec.metadata["rubygems_mfa_required"] = "true"
29
-
30
- spec.files = Dir.chdir(__dir__) do
31
- `git ls-files -z`.split("\x0").reject do |f|
32
- (File.expand_path(f) == __FILE__) ||
33
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
34
- end
35
- end
36
-
37
- spec.bindir = "exe"
38
- spec.executables = ["mirror"]
39
- spec.require_paths = ["lib"]
40
-
41
- spec.add_development_dependency "minitest", "~> 5.0"
42
- spec.add_development_dependency "rake", "~> 13.0"
43
- spec.add_development_dependency "rubocop", "~> 1.21"
44
- spec.add_development_dependency "yard", "~> 0.9"
45
- end