rails-worktrees 0.7.0 → 0.7.1

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: e86a789b2e56f07d95a626b67c9fafbf01862e9c8496043107914b4446e13702
4
- data.tar.gz: 9f0dce854a6c7a20dfedb00dd181f7ecc5699ae6cf86ff61d24d60bce80be11e
3
+ metadata.gz: 36b6c2a6e2e481803b035ba28c575c1619ba5fb1d158f0e4c6e4180d69aeafee
4
+ data.tar.gz: 9265e0459e03d34cbc4962b181bdd62836ddb4433c392d8aac43b65f445992a1
5
5
  SHA512:
6
- metadata.gz: 16513989c4364e4cf37d4eb0cf1fbde3fc95d8aa246223b5e9cca20da0035266d8f33e81a115d83b95b359ff93292796ba88889f06495c9d8b39fde11eb8d5b4
7
- data.tar.gz: 1bcdfe219ef78dadeeaf7a8de8cc71662de4e0066b6bebf9941aa59d86c9349a38e3caf0671693dc321b54b672618e7b9b772994efb48511628f77718345aceb
6
+ metadata.gz: 7cfecdcffdaeb1e229332c1a8a20ed80f2600886a58deb4f2f9b186c70957f2b70224a950e1c9109b3d9d234db3c18fe7789074ba1109e58d6e1da35236e2923
7
+ data.tar.gz: 977ebc76e219d9e31d05595852ab830355c6ca30f2617d2ead869b4d5ccce18b0bbc0360dd4e533aff6ffab53c7eee7c20480ec2f380881c69d5387e859526b0
@@ -1 +1 @@
1
- {".":"0.7.0"}
1
+ {".":"0.7.1"}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.1](https://github.com/asjer/rails-worktrees/compare/v0.7.0...v0.7.1) (2026-07-06)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **wt:** bootstrap mise before bundler ([24720a1](https://github.com/asjer/rails-worktrees/commit/24720a1cf51ce55676277c478f3b86bbb98e72eb))
9
+
3
10
  ## [0.7.0](https://github.com/asjer/rails-worktrees/compare/v0.6.0...v0.7.0) (2026-04-05)
4
11
 
5
12
 
@@ -3,6 +3,8 @@ module Rails
3
3
  module Generators
4
4
  # Detects common mise.toml setups and suggests loading the worktree-local .env.
5
5
  module MiseFollowUp
6
+ FILE_ENCODING = 'UTF-8'.freeze
7
+
6
8
  private
7
9
 
8
10
  def follow_up_notes_text
@@ -26,7 +28,7 @@ module Rails
26
28
  def mise_env_file_configured?
27
29
  return false unless mise_toml_path
28
30
 
29
- File.read(mise_toml_path).match?(/^\s*_.file\s*=\s*["']\.env["']\s*(?:#.*)?\s*$/)
31
+ File.read(mise_toml_path, encoding: FILE_ENCODING).match?(/^\s*_.file\s*=\s*["']\.env["']\s*(?:#.*)?\s*$/)
30
32
  end
31
33
 
32
34
  def mise_toml_path
@@ -3,6 +3,8 @@ module Rails
3
3
  module Generators
4
4
  # Detects config/puma.rb setups that should prefer the worktree-local DEV_PORT.
5
5
  module PumaFollowUp
6
+ FILE_ENCODING = 'UTF-8'.freeze
7
+
6
8
  private
7
9
 
8
10
  def puma_follow_up_notes_text
@@ -24,7 +26,9 @@ module Rails
24
26
  def puma_update_result
25
27
  return missing_puma_update_result unless existing_puma_config_path
26
28
 
27
- ::Rails::Worktrees::PumaConfigUpdater.new(content: File.read(existing_puma_config_path)).call
29
+ ::Rails::Worktrees::PumaConfigUpdater.new(
30
+ content: File.read(existing_puma_config_path, encoding: FILE_ENCODING)
31
+ ).call
28
32
  end
29
33
 
30
34
  def missing_puma_update_result
@@ -1,5 +1,65 @@
1
- #!/usr/bin/env ruby
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
1
+ #!/usr/bin/env bash
2
+ set -e
3
3
 
4
- require 'bundler/setup'
5
- load Gem.bin_path('rails-worktrees', 'wt')
4
+ wt_prepend_path() {
5
+ case ":${PATH:-}:" in
6
+ *":$1:"*) ;;
7
+ *) PATH="$1${PATH:+:$PATH}" ;;
8
+ esac
9
+ }
10
+
11
+ wt_prepend_path "/usr/local/bin"
12
+ wt_prepend_path "/opt/homebrew/bin"
13
+ if [ -n "${HOME:-}" ]; then
14
+ wt_prepend_path "$HOME/.local/bin"
15
+ fi
16
+ export PATH
17
+
18
+ wt_locale_needs_utf8() {
19
+ case "${1:-}" in
20
+ ""|C|POSIX) return 0 ;;
21
+ *) return 1 ;;
22
+ esac
23
+ }
24
+
25
+ wt_utf8_locale() {
26
+ if command -v locale >/dev/null 2>&1 && locale -a 2>/dev/null | grep -Eiq '^(C|c)[_.-]UTF-?8$'; then
27
+ printf '%s' 'C.UTF-8'
28
+ else
29
+ printf '%s' 'en_US.UTF-8'
30
+ fi
31
+ }
32
+
33
+ if wt_locale_needs_utf8 "${LANG:-}" || { [ -n "${LC_ALL:-}" ] && wt_locale_needs_utf8 "${LC_ALL:-}"; }; then
34
+ WT_UTF8_LOCALE="${LANG:-}"
35
+ if wt_locale_needs_utf8 "$WT_UTF8_LOCALE"; then
36
+ WT_UTF8_LOCALE="$(wt_utf8_locale)"
37
+ fi
38
+
39
+ if wt_locale_needs_utf8 "${LANG:-}"; then
40
+ export LANG="$WT_UTF8_LOCALE"
41
+ fi
42
+ if [ -n "${LC_ALL:-}" ] && wt_locale_needs_utf8 "${LC_ALL:-}"; then
43
+ export LC_ALL="$WT_UTF8_LOCALE"
44
+ fi
45
+ fi
46
+
47
+ WT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
48
+ WT_SCRIPT_PATH="$WT_SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}")"
49
+ WT_APP_ROOT="$(cd "$WT_SCRIPT_DIR/.." && pwd -P)"
50
+ WT_MISE_CONFIG=""
51
+
52
+ if [ -f "$WT_APP_ROOT/mise.toml" ]; then
53
+ WT_MISE_CONFIG="$WT_APP_ROOT/mise.toml"
54
+ elif [ -f "$WT_APP_ROOT/.mise.toml" ]; then
55
+ WT_MISE_CONFIG="$WT_APP_ROOT/.mise.toml"
56
+ fi
57
+
58
+ if [ -n "$WT_MISE_CONFIG" ] && [ -z "${RAILS_WORKTREES_BOOTSTRAPPED:-}" ] && command -v mise >/dev/null 2>&1; then
59
+ mise trust "$WT_MISE_CONFIG" >/dev/null || true
60
+ export RAILS_WORKTREES_BOOTSTRAPPED=1
61
+ exec mise exec -- "$WT_SCRIPT_PATH" "$@"
62
+ fi
63
+
64
+ export BUNDLE_GEMFILE="${BUNDLE_GEMFILE:-$WT_APP_ROOT/Gemfile}"
65
+ exec ruby -rbundler/setup -e 'load Gem.bin_path("rails-worktrees", "wt")' -- "$@"
@@ -36,6 +36,8 @@ module Worktrees
36
36
  'follow-up edits when safe; also generate bin/ob'
37
37
  ].join(' ')
38
38
 
39
+ FILE_ENCODING = 'UTF-8'.freeze
40
+
39
41
  FOLLOW_UP_TEMPLATE = <<~TEXT.freeze
40
42
  ============================================
41
43
  rails-worktrees installed successfully! 🚂
@@ -130,7 +132,7 @@ module Worktrees
130
132
 
131
133
  def database_update_result
132
134
  result = ::Rails::Worktrees::DatabaseConfigUpdater.new(
133
- content: File.read(database_config_path)
135
+ content: File.read(database_config_path, encoding: FILE_ENCODING)
134
136
  ).call
135
137
 
136
138
  File.write(database_config_path, result.content) if result.changed?
@@ -181,7 +183,9 @@ module Worktrees
181
183
  return
182
184
  end
183
185
 
184
- result = ::Rails::Worktrees::ProcfileUpdater.new(content: File.read(procfile_path)).call
186
+ result = ::Rails::Worktrees::ProcfileUpdater.new(
187
+ content: File.read(procfile_path, encoding: FILE_ENCODING)
188
+ ).call
185
189
  File.write(procfile_path, result.content) if result.changed?
186
190
  announce_updater_result('Procfile.dev', result)
187
191
  end
@@ -193,7 +197,9 @@ module Worktrees
193
197
  return
194
198
  end
195
199
 
196
- result = ::Rails::Worktrees::PumaConfigUpdater.new(content: File.read(puma_config_path)).call
200
+ result = ::Rails::Worktrees::PumaConfigUpdater.new(
201
+ content: File.read(puma_config_path, encoding: FILE_ENCODING)
202
+ ).call
197
203
  File.write(puma_config_path, result.content) if result.changed?
198
204
  announce_updater_result('config/puma.rb', result)
199
205
  end
@@ -203,7 +209,7 @@ module Worktrees
203
209
  return announce_missing_mise_toml unless path
204
210
 
205
211
  result = ::Rails::Worktrees::MiseTomlUpdater.new(
206
- content: File.read(path),
212
+ content: File.read(path, encoding: FILE_ENCODING),
207
213
  file_name: File.basename(path)
208
214
  ).call
209
215
 
@@ -249,13 +255,19 @@ module Worktrees
249
255
 
250
256
  def git_repo?
251
257
  _stdout_str, _stderr_str, status = Open3.capture3(
252
- 'git', 'rev-parse', '--is-inside-work-tree', chdir: destination_root
258
+ git_process_env, 'git', 'rev-parse', '--is-inside-work-tree',
259
+ chdir: destination_root,
260
+ unsetenv_others: true
253
261
  )
254
262
  status.success?
255
263
  rescue Errno::ENOENT
256
264
  false
257
265
  end
258
266
 
267
+ def git_process_env
268
+ ENV.to_h.slice('HOME', 'PATH')
269
+ end
270
+
259
271
  def conductor_workspace_root
260
272
  "File.expand_path('~/Sites/conductor/workspaces')"
261
273
  end
@@ -8,6 +8,7 @@ module Rails
8
8
  class BrowserCommand
9
9
  APP_ROOT_ENV_KEY = 'RAILS_WORKTREES_APP_ROOT'.freeze
10
10
  ENV_FILE_NAME = '.env'.freeze
11
+ FILE_ENCODING = 'UTF-8'.freeze
11
12
 
12
13
  def initialize(argv:, io:, env:, cwd:, host_os: RbConfig::CONFIG['host_os'])
13
14
  @argv = argv.dup
@@ -127,7 +128,7 @@ module Rails
127
128
  def dev_port_from_env_file
128
129
  return unless File.file?(env_path)
129
130
 
130
- lines = File.readlines(env_path, chomp: true)
131
+ lines = File.readlines(env_path, chomp: true, encoding: FILE_ENCODING)
131
132
  env_value(lines, 'DEV_PORT')
132
133
  rescue StandardError => e
133
134
  raise Error, "Could not read #{env_path}: #{e.message}"
@@ -7,6 +7,26 @@ module Rails
7
7
  # Shell-level git helpers, branch/worktree queries, and worktree creation.
8
8
  # rubocop:disable Metrics/ModuleLength
9
9
  module GitOperations
10
+ GIT_REPOSITORY_ENV_KEYS = %w[
11
+ GIT_ALTERNATE_OBJECT_DIRECTORIES
12
+ GIT_COMMON_DIR
13
+ GIT_CONFIG
14
+ GIT_CONFIG_COUNT
15
+ GIT_CONFIG_PARAMETERS
16
+ GIT_DIR
17
+ GIT_GRAFT_FILE
18
+ GIT_IMPLICIT_WORK_TREE
19
+ GIT_INDEX_FILE
20
+ GIT_NAMESPACE
21
+ GIT_NO_REPLACE_OBJECTS
22
+ GIT_OBJECT_DIRECTORY
23
+ GIT_PREFIX
24
+ GIT_QUARANTINE_PATH
25
+ GIT_REPLACE_REF_BASE
26
+ GIT_SHALLOW_FILE
27
+ GIT_WORK_TREE
28
+ ].freeze
29
+
10
30
  private
11
31
 
12
32
  def require_git_repo
@@ -162,24 +182,42 @@ module Rails
162
182
  end
163
183
 
164
184
  def git!(*)
165
- stdout_str, stderr_str, status = Open3.capture3(@env.to_h, 'git', *, chdir: @cwd)
185
+ stdout_str, stderr_str, status = Open3.capture3(
186
+ git_process_env, 'git', *, chdir: @cwd, unsetenv_others: true
187
+ )
188
+ stdout_str = command_output(stdout_str)
189
+ stderr_str = command_output(stderr_str)
166
190
  return stdout_str if status.success?
167
191
 
168
192
  raise Error, combined_output(stdout_str, stderr_str)
169
193
  end
170
194
 
171
195
  def git_capture(*, allow_failure: false)
172
- stdout_str, stderr_str, status = Open3.capture3(@env.to_h, 'git', *, chdir: @cwd)
196
+ stdout_str, stderr_str, status = Open3.capture3(
197
+ git_process_env, 'git', *, chdir: @cwd, unsetenv_others: true
198
+ )
199
+ stdout_str = command_output(stdout_str)
200
+ stderr_str = command_output(stderr_str)
173
201
  return stdout_str if status.success? || allow_failure
174
202
 
175
203
  raise Error, combined_output(stdout_str, stderr_str)
176
204
  end
177
205
 
178
206
  def git_success?(*)
179
- _stdout_str, _stderr_str, status = Open3.capture3(@env.to_h, 'git', *, chdir: @cwd)
207
+ _stdout_str, _stderr_str, status = Open3.capture3(
208
+ git_process_env, 'git', *, chdir: @cwd, unsetenv_others: true
209
+ )
180
210
  status.success?
181
211
  end
182
212
 
213
+ def git_process_env
214
+ @env.to_h.except(*GIT_REPOSITORY_ENV_KEYS)
215
+ end
216
+
217
+ def command_output(output)
218
+ output.to_s.dup.force_encoding(Encoding::UTF_8).scrub
219
+ end
220
+
183
221
  def combined_output(stdout_str, stderr_str)
184
222
  output = [stdout_str, stderr_str].map(&:strip).reject(&:empty?).join("\n")
185
223
  return output unless output.empty?
@@ -4,6 +4,8 @@ module Rails
4
4
  # Auto-picking names from bundled .txt files and tracking retired names.
5
5
  # rubocop:disable Metrics/ModuleLength
6
6
  module NamePicking
7
+ FILE_ENCODING = 'UTF-8'.freeze
8
+
7
9
  private
8
10
 
9
11
  def resolve_worktree_name(project_name, workspaces)
@@ -48,7 +50,7 @@ module Rails
48
50
  end
49
51
 
50
52
  def source_names(source_file)
51
- File.readlines(source_file, chomp: true).filter_map do |line|
53
+ File.readlines(source_file, chomp: true, encoding: FILE_ENCODING).filter_map do |line|
52
54
  name = line.delete_suffix("\r").strip
53
55
  next if name.empty? || name.start_with?('#')
54
56
 
@@ -74,7 +76,7 @@ module Rails
74
76
  state_file = retired_names_file
75
77
  return false unless state_file
76
78
 
77
- File.foreach(state_file).any? { |line| line.split("\t", 2).first == candidate }
79
+ File.foreach(state_file, encoding: FILE_ENCODING).any? { |line| line.split("\t", 2).first == candidate }
78
80
  end
79
81
 
80
82
  def record_retired_name(worktree_name, project_name)
@@ -13,6 +13,7 @@ module Rails
13
13
  end
14
14
 
15
15
  ENV_FILE_NAME = '.env'.freeze
16
+ FILE_ENCODING = 'UTF-8'.freeze
16
17
 
17
18
  def initialize(target_dir:, worktree_name:, configuration:, peer_roots: nil)
18
19
  @target_dir = target_dir
@@ -44,7 +45,9 @@ module Rails
44
45
  result(false, [message], values)
45
46
  end
46
47
 
47
- def existing_env_lines = File.exist?(env_path) ? File.readlines(env_path, chomp: true) : []
48
+ def existing_env_lines
49
+ File.exist?(env_path) ? File.readlines(env_path, chomp: true, encoding: FILE_ENCODING) : []
50
+ end
48
51
 
49
52
  def resolved_values(lines)
50
53
  dev_port = (env_value(lines, 'DEV_PORT') || allocate_dev_port).to_s
@@ -123,7 +126,7 @@ module Rails
123
126
 
124
127
  def peer_env_lines(path)
125
128
  env_file = File.join(path, ENV_FILE_NAME)
126
- File.exist?(env_file) ? File.readlines(env_file, chomp: true) : []
129
+ File.exist?(env_file) ? File.readlines(env_file, chomp: true, encoding: FILE_ENCODING) : []
127
130
  end
128
131
 
129
132
  def peers_root = File.dirname(@target_dir)
@@ -12,6 +12,7 @@ module Rails
12
12
  end
13
13
 
14
14
  TEMPLATE_PATH = File.expand_path('../../generators/rails/worktrees/templates/rails_worktrees.rb.tt', __dir__)
15
+ FILE_ENCODING = 'UTF-8'.freeze
15
16
  CURRENT_WRAPPER_CALL = 'Rails.application.config.x.rails_worktrees.tap do |config|'.freeze
16
17
  CONFIGURE_CALL = 'Rails::Worktrees.configure do |config|'.freeze
17
18
  KNOWN_GUARD_FRAGMENTS = [
@@ -135,7 +136,9 @@ module Rails
135
136
  end
136
137
 
137
138
  def render_default_template
138
- ERB.new(File.read(TEMPLATE_PATH), trim_mode: '-').result(template_context.instance_eval { binding })
139
+ ERB.new(File.read(TEMPLATE_PATH, encoding: FILE_ENCODING), trim_mode: '-').result(
140
+ template_context.instance_eval { binding }
141
+ )
139
142
  end
140
143
 
141
144
  def template_context
@@ -22,7 +22,7 @@ module Rails
22
22
  def call
23
23
  return configuration unless initializer_path && File.file?(initializer_path)
24
24
 
25
- body = extract_configuration_body(File.read(initializer_path))
25
+ body = extract_configuration_body(File.read(initializer_path, encoding: 'UTF-8'))
26
26
  return configuration unless body
27
27
 
28
28
  recorder = AssignmentRecorder.new(configuration)
@@ -49,6 +49,7 @@ module Rails
49
49
  # rubocop:enable Style/RedundantStructKeywordInit
50
50
 
51
51
  TEMPLATE_ROOT = File.expand_path('../../generators/rails/worktrees/templates', __dir__)
52
+ FILE_ENCODING = 'UTF-8'.freeze
52
53
 
53
54
  def initialize(root:)
54
55
  @root = root
@@ -100,10 +101,10 @@ module Rails
100
101
  path = absolute_path(config.fetch(:relative_path))
101
102
  return nil if config.fetch(:optional) && !File.exist?(path)
102
103
 
103
- desired_content = File.read(config.fetch(:template_path))
104
+ desired_content = read_text(config.fetch(:template_path))
104
105
  return template_missing_check(config, desired_content) unless File.exist?(path)
105
106
 
106
- current_content = File.read(path)
107
+ current_content = read_text(path)
107
108
  if current_content == desired_content
108
109
  if executable_mode_current?(config, path)
109
110
  return ok_check(config, "#{config.fetch(:relative_path)} is up to date.")
@@ -168,7 +169,7 @@ module Rails
168
169
  )
169
170
  end
170
171
 
171
- updater_result_check(config, InitializerUpdater.new(content: File.read(path)).call)
172
+ updater_result_check(config, InitializerUpdater.new(content: read_text(path)).call)
172
173
  end
173
174
 
174
175
  def database_check
@@ -187,7 +188,7 @@ module Rails
187
188
  )
188
189
  end
189
190
 
190
- result = DatabaseConfigUpdater.new(content: File.read(path)).call
191
+ result = DatabaseConfigUpdater.new(content: read_text(path)).call
191
192
  return updated_database_check(config, result) if result.changed?
192
193
 
193
194
  if database_configured?(result)
@@ -244,7 +245,7 @@ module Rails
244
245
  path = absolute_path(config.fetch(:relative_path))
245
246
  return unless File.exist?(path)
246
247
 
247
- updater_result_check(config, yield(File.read(path)))
248
+ updater_result_check(config, yield(read_text(path)))
248
249
  end
249
250
 
250
251
  def mise_check
@@ -265,7 +266,7 @@ module Rails
265
266
 
266
267
  def mise_updater_result(relative_path)
267
268
  MiseTomlUpdater.new(
268
- content: File.read(absolute_path(relative_path)),
269
+ content: read_text(absolute_path(relative_path)),
269
270
  file_name: File.basename(relative_path)
270
271
  ).call
271
272
  end
@@ -319,6 +320,10 @@ module Rails
319
320
  )
320
321
  end
321
322
 
323
+ def read_text(path)
324
+ File.read(path, encoding: FILE_ENCODING)
325
+ end
326
+
322
327
  def absolute_path(relative_path)
323
328
  File.join(root, relative_path)
324
329
  end
@@ -1,5 +1,5 @@
1
1
  module Rails
2
2
  module Worktrees
3
- VERSION = '0.7.0'.freeze
3
+ VERSION = '0.7.1'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-worktrees
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Asjer Querido