capistrano-ops 1.1.0 → 1.2.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 +4 -4
- data/.rubocop.yml +0 -1
- data/CHANGELOG.md +12 -1
- data/README.md +87 -0
- data/lib/capistrano/ops/capistrano.rb +1 -0
- data/lib/capistrano/ops/local/helpers.rb +71 -0
- data/lib/capistrano/ops/local/tasks/console.rake +59 -0
- data/lib/capistrano/ops/local/tasks/run.rake +40 -0
- data/lib/capistrano/ops/local.rb +9 -0
- data/lib/capistrano/ops/version.rb +1 -1
- data/spec/capistrano/ops/local/helpers_spec.rb +121 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fe9dc820d50c226c74d26afad1f5031651bdf9758c220268cfe1d8721416b011
|
|
4
|
+
data.tar.gz: 5ac2da6ebbd2a6d7ba4bebf031b2bd3b690f30367ce48437a8c2656a2b65ec44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de3334c09fe3b2f7cb113d13d8a309d5466464c438499e684531e015d888f73cdc8e2f7d033bbe96cf9bebab64c89ba8c18d1b74cae48ed6df5172ea47741302
|
|
7
|
+
data.tar.gz: 3d177c55f3fcf475f030f67222a0a14b715f712f4627ae6c96db27e8f75a2af162015080410d7bbd0f822b854cbfd88e5e16df668cca21132ddd4cbccd462177
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.0] - 2026-09-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add `local:run` and `local:console` tasks (module `capistrano/ops/local`) to upload a local Ruby script from `scripts/local/` to the server and execute it — once via `rails runner`, or interactively with a Rails console
|
|
15
|
+
- Helpers module `Capistrano::Ops::Local::Helpers` with script resolution (auto `.rb`), listing, TTY-aware coloring, and dangerous-stage confirmation
|
|
16
|
+
- Configurable settings: `:local_scripts_dir`, `:local_dangerous_stages`; existing `:console_role`, `:console_env`, `:console_user`, `:console_shell` respected
|
|
17
|
+
- Specs for helpers (script resolution, TTY coloring, tmp-path generation)
|
|
18
|
+
- README section documenting the new tasks, including `capistrano-rails` prerequisite for `local:console`
|
|
19
|
+
|
|
10
20
|
## [1.1.0] - 2026-09-18
|
|
11
21
|
|
|
12
22
|
### Added
|
|
@@ -311,7 +321,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
311
321
|
- Figaro YAML management
|
|
312
322
|
- Basic Capistrano integration
|
|
313
323
|
|
|
314
|
-
[Unreleased]: https://github.com/zauberware/capistrano-ops/compare/v1.
|
|
324
|
+
[Unreleased]: https://github.com/zauberware/capistrano-ops/compare/v1.2.0...HEAD
|
|
325
|
+
[1.2.0]: https://github.com/zauberware/capistrano-ops/releases/tag/v1.2.0
|
|
315
326
|
[1.1.0]: https://github.com/zauberware/capistrano-ops/releases/tag/v1.1.0
|
|
316
327
|
[1.0.11]: https://github.com/zauberware/capistrano-ops/releases/tag/v1.0.11
|
|
317
328
|
[v1.0.10]: https://github.com/zauberware/capistrano-ops/compare/v1.0.9...v1.0.10
|
data/README.md
CHANGED
|
@@ -24,6 +24,7 @@ The capistrano-ops gem is a valuable library, tailor-made for Rails DevOps profe
|
|
|
24
24
|
- [Configuration](#configuration-1)
|
|
25
25
|
- [Usage](#usage-1)
|
|
26
26
|
- [Wkhtmltopdf Setup](#wkhtmltopdf-setup)
|
|
27
|
+
- [Local scripts (run / console)](#local-scripts-run--console)
|
|
27
28
|
- [Contributing](#contributing)
|
|
28
29
|
- [License](#license)
|
|
29
30
|
|
|
@@ -170,6 +171,8 @@ require 'capistrano/ops'
|
|
|
170
171
|
| `cap <environment> logrotate:enable` | enable logrotate for logfiles on server |
|
|
171
172
|
| `cap <environment> logrotate:disable` | disable logrotate for logfiles on server |
|
|
172
173
|
| `cap <environment> logrotate:check` | show logrotate status for logfiles on server |
|
|
174
|
+
| `cap <environment> local:run SCRIPT=<file>` | upload local script and execute it once via `rails runner` on the server |
|
|
175
|
+
| `cap <environment> local:console SCRIPT=<file>` | open a remote Rails console with a local script preloaded |
|
|
173
176
|
| `rake pg:dump` | creates postgres database backup |
|
|
174
177
|
| `rake pg:remove_old_dumps` | remove old postgres backups |
|
|
175
178
|
| `rake storage:backup` | creates backup of storage |
|
|
@@ -390,6 +393,90 @@ require 'capistrano/ops/wkhtmltopdf'
|
|
|
390
393
|
|
|
391
394
|
[↑](#)
|
|
392
395
|
|
|
396
|
+
## Local scripts (run / console)
|
|
397
|
+
|
|
398
|
+
Ship a Ruby script from your workstation onto the server and execute it there —
|
|
399
|
+
either once via `rails runner`, or with a Rails console attached and the
|
|
400
|
+
script's methods preloaded. Useful for one-off data patches, ad-hoc audits, or
|
|
401
|
+
debugging sessions where you want your local scratch code to have full access
|
|
402
|
+
to the production Rails environment.
|
|
403
|
+
|
|
404
|
+
### Requirements
|
|
405
|
+
|
|
406
|
+
`local:console` uses the interactive console runner shipped in `capistrano-rails`.
|
|
407
|
+
Make sure both are loaded in your `Capfile`:
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
# Capfile
|
|
411
|
+
require 'capistrano/rails/console' # provides run_interactively (needed by local:console)
|
|
412
|
+
require 'capistrano/ops' # auto-loads capistrano/ops/local
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
If `capistrano-rails` is not in your Gemfile, the `local:*` tasks will emit a
|
|
416
|
+
warning at load time and skip registering. `local:run` alone technically only
|
|
417
|
+
needs core Capistrano, but for simplicity both tasks share the same gate.
|
|
418
|
+
|
|
419
|
+
### Setup
|
|
420
|
+
|
|
421
|
+
Create the scripts directory in your app root (default `scripts/local/`) and
|
|
422
|
+
drop `.rb` files in it:
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
scripts/local/
|
|
426
|
+
├── check_stuck_jobs.rb
|
|
427
|
+
└── set_welcome_email_template.rb
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The directory is configurable:
|
|
431
|
+
|
|
432
|
+
```ruby
|
|
433
|
+
# config/deploy.rb
|
|
434
|
+
set :local_scripts_dir, 'ops/scripts' # default: 'scripts/local'
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Tasks
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
cap staging local:run SCRIPT=check_stuck_jobs # execute once via rails runner
|
|
441
|
+
cap staging local:console SCRIPT=check_stuck_jobs # open remote console with script preloaded
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
- `.rb` extension is optional (`SCRIPT=foo` and `SCRIPT=foo.rb` both work)
|
|
445
|
+
- Omitting `SCRIPT=` prints the list of available scripts
|
|
446
|
+
- Interactive `[y/N]` confirmation on "dangerous" stages (default: `production`)
|
|
447
|
+
- Uploaded temp files are cleaned up automatically after execution
|
|
448
|
+
|
|
449
|
+
### Writing scripts
|
|
450
|
+
|
|
451
|
+
Define methods rather than top-level code — this way the same script works for
|
|
452
|
+
both `local:run` (direct execution) and `local:console` (interactive access):
|
|
453
|
+
|
|
454
|
+
```ruby
|
|
455
|
+
# scripts/local/check_stuck_jobs.rb
|
|
456
|
+
def show_stuck_jobs(hours_ago: 24)
|
|
457
|
+
cutoff = hours_ago.hours.ago
|
|
458
|
+
Sidekiq::Workers.new.select { |_, _, w| Time.at(w['run_at']) < cutoff }.each do |process, _, work|
|
|
459
|
+
puts "#{work['payload']['class']} on #{process}"
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# auto-run when executed via local:run, dormant in the console
|
|
464
|
+
show_stuck_jobs unless defined?(IRB)
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Configuration options
|
|
468
|
+
|
|
469
|
+
| Setting | Default | Purpose |
|
|
470
|
+
| -------------------------- | ------------------------------- | ---------------------------------------------- |
|
|
471
|
+
| `:local_scripts_dir` | `'scripts/local'` | Where local scripts are read from |
|
|
472
|
+
| `:local_dangerous_stages` | `%w[production]` | Stages that trigger the `[y/N]` confirmation |
|
|
473
|
+
| `:console_role` | `:app` | Capistrano role used to pick the target host |
|
|
474
|
+
| `:console_env` | `fetch(:rails_env) \|\| :stage` | Rails env passed to `rails runner` / `console` |
|
|
475
|
+
| `:console_user` | (unset) | `sudo -u` target user for the remote command |
|
|
476
|
+
| `:console_shell` | (unset) | Shell override for the interactive session |
|
|
477
|
+
|
|
478
|
+
[↑](#)
|
|
479
|
+
|
|
393
480
|
## Contributing
|
|
394
481
|
|
|
395
482
|
1. Fork it ( https://github.com/zauberware/capistrano-ops/fork )
|
|
@@ -7,6 +7,7 @@ require 'capistrano/ops/logrotate'
|
|
|
7
7
|
require 'capistrano/ops/logs'
|
|
8
8
|
require 'capistrano/ops/invoke'
|
|
9
9
|
require 'capistrano/ops/backup'
|
|
10
|
+
require 'capistrano/ops/local'
|
|
10
11
|
|
|
11
12
|
unless defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
|
|
12
13
|
puts 'Capistrano 3 is required to use this gem'
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Capistrano
|
|
6
|
+
module Ops
|
|
7
|
+
module Local
|
|
8
|
+
module Helpers
|
|
9
|
+
def scripts_dir
|
|
10
|
+
File.expand_path(fetch(:local_scripts_dir, 'scripts/local'), Dir.pwd)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def available_scripts
|
|
14
|
+
Dir.glob(File.join(scripts_dir, '*.rb')).map { |f| File.basename(f) }.sort
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def resolve_script(name)
|
|
18
|
+
return nil if name.nil? || name.empty?
|
|
19
|
+
|
|
20
|
+
candidates = [name, "#{name}.rb"].map { |n| File.join(scripts_dir, n) }
|
|
21
|
+
candidates.find { |p| File.exist?(p) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def print_available_scripts
|
|
25
|
+
scripts = available_scripts
|
|
26
|
+
if scripts.empty?
|
|
27
|
+
puts " (no scripts found in #{fetch(:local_scripts_dir, 'scripts/local')}/)"
|
|
28
|
+
else
|
|
29
|
+
scripts.each { |s| puts " • #{s}" }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def abort_script_not_given(command)
|
|
34
|
+
puts "\n#{color('Usage:', :bold)} cap <stage> local:#{command} SCRIPT=<filename>"
|
|
35
|
+
puts "\n#{color('Available scripts:', :bold)}"
|
|
36
|
+
print_available_scripts
|
|
37
|
+
abort
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def abort_script_not_found(name)
|
|
41
|
+
puts "\n#{color("Script not found: #{name}", :red)}"
|
|
42
|
+
puts "\n#{color('Available scripts:', :bold)}"
|
|
43
|
+
print_available_scripts
|
|
44
|
+
abort
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def confirm_dangerous_stage!(action)
|
|
48
|
+
stage = fetch(:stage).to_s
|
|
49
|
+
dangerous = fetch(:local_dangerous_stages, %w[production])
|
|
50
|
+
return unless dangerous.include?(stage)
|
|
51
|
+
|
|
52
|
+
print "\n#{color("⚠ #{action} on #{stage.upcase}. Continue? [y/N] ", :yellow)}"
|
|
53
|
+
abort 'Aborted.' unless $stdin.gets.to_s.strip.casecmp('y').zero?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def remote_tmp_path(script_path)
|
|
57
|
+
"/tmp/cap_local_#{SecureRandom.hex(6)}_#{File.basename(script_path)}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def color(text, style)
|
|
61
|
+
return text unless $stdout.tty?
|
|
62
|
+
|
|
63
|
+
code = { bold: '1', red: '31', green: '32', yellow: '33', cyan: '36', dim: '2' }[style]
|
|
64
|
+
return text unless code
|
|
65
|
+
|
|
66
|
+
"\e[#{code}m#{text}\e[0m"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :local do
|
|
4
|
+
include Capistrano::Ops::Local::Helpers
|
|
5
|
+
|
|
6
|
+
desc 'Rails console with a local script pre-loaded: cap <stage> local:console SCRIPT=<filename>'
|
|
7
|
+
task :console do
|
|
8
|
+
script = ENV.fetch('SCRIPT', nil)
|
|
9
|
+
abort_script_not_given('console') unless script
|
|
10
|
+
|
|
11
|
+
script_path = resolve_script(script)
|
|
12
|
+
abort_script_not_found(script) unless script_path
|
|
13
|
+
|
|
14
|
+
stage = fetch(:stage).to_s
|
|
15
|
+
puts "\n#{color('Script:', :bold)} #{File.basename(script_path)}"
|
|
16
|
+
puts "#{color('Target:', :bold)} #{stage} (interactive console)"
|
|
17
|
+
|
|
18
|
+
confirm_dangerous_stage!('Opening console')
|
|
19
|
+
|
|
20
|
+
remote_tmp = remote_tmp_path(script_path)
|
|
21
|
+
remote_irbrc = "/tmp/cap_local_irbrc_#{SecureRandom.hex(6)}.rb"
|
|
22
|
+
|
|
23
|
+
on primary(fetch(:console_role, :app)) do
|
|
24
|
+
upload! script_path, remote_tmp
|
|
25
|
+
# Wrapper loads the script into the console's main context and deletes
|
|
26
|
+
# both temp files server-side when IRB exits. The at_exit puts often
|
|
27
|
+
# gets swallowed by Sentry/etc during shutdown; the local puts in the
|
|
28
|
+
# ensure block below is what the user actually sees.
|
|
29
|
+
wrapper = <<~RUBY
|
|
30
|
+
load '#{remote_tmp}'
|
|
31
|
+
at_exit do
|
|
32
|
+
begin; File.delete('#{remote_tmp}'); rescue StandardError; end
|
|
33
|
+
begin; File.delete('#{remote_irbrc}'); rescue StandardError; end
|
|
34
|
+
end
|
|
35
|
+
RUBY
|
|
36
|
+
upload! StringIO.new(wrapper), remote_irbrc
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts "#{color("▶ Opening console with #{File.basename(script_path)} on #{stage}…", :cyan)}\n\n"
|
|
40
|
+
|
|
41
|
+
begin
|
|
42
|
+
run_interactively primary(fetch(:console_role, :app)), shell: fetch(:console_shell, nil) do
|
|
43
|
+
within current_path do
|
|
44
|
+
as user: fetch(:console_user, nil) do
|
|
45
|
+
args = ['-e', fetch(:console_env, fetch(:rails_env, fetch(:stage)))]
|
|
46
|
+
args.push('--', '-r', remote_irbrc)
|
|
47
|
+
execute(:rails, :console, *args)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
ensure
|
|
52
|
+
# Local puts — no `on {}` block, since Capistrano's Configuration is not
|
|
53
|
+
# safely Marshal-dumpable after run_interactively (fails with
|
|
54
|
+
# "no _dump_data is defined for class Thread::Mutex"). Server-side
|
|
55
|
+
# cleanup is handled by the wrapper's at_exit hook.
|
|
56
|
+
puts color("\n\u{1F9F9} Console closed on #{stage} — server temp files cleaned up on exit.", :dim)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :local do
|
|
4
|
+
include Capistrano::Ops::Local::Helpers
|
|
5
|
+
|
|
6
|
+
desc 'Run a local script on remote via rails runner: cap <stage> local:run SCRIPT=<filename>'
|
|
7
|
+
task :run do
|
|
8
|
+
script = ENV.fetch('SCRIPT', nil)
|
|
9
|
+
abort_script_not_given('run') unless script
|
|
10
|
+
|
|
11
|
+
script_path = resolve_script(script)
|
|
12
|
+
abort_script_not_found(script) unless script_path
|
|
13
|
+
|
|
14
|
+
stage = fetch(:stage).to_s
|
|
15
|
+
puts "\n#{color('Script:', :bold)} #{File.basename(script_path)}"
|
|
16
|
+
puts "#{color('Target:', :bold)} #{stage}"
|
|
17
|
+
|
|
18
|
+
confirm_dangerous_stage!('Running script')
|
|
19
|
+
|
|
20
|
+
on roles(fetch(:console_role, :app)) do
|
|
21
|
+
remote_tmp = remote_tmp_path(script_path)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
upload! script_path, remote_tmp
|
|
25
|
+
puts "#{color("▶ Running #{File.basename(script_path)} on #{stage}…", :cyan)}\n\n"
|
|
26
|
+
|
|
27
|
+
within current_path do
|
|
28
|
+
with rails_env: fetch(:console_env, fetch(:rails_env, fetch(:stage))) do
|
|
29
|
+
execute :bundle, :exec, :rails, 'runner', remote_tmp
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
ensure
|
|
33
|
+
execute :rm, '-f', remote_tmp
|
|
34
|
+
puts color("\u{1F9F9} Cleaned up temp file on server", :dim)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts "\n#{color('✔ Done.', :green)}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'capistrano/ops/task_loader'
|
|
4
|
+
require 'capistrano/ops/local/helpers'
|
|
5
|
+
|
|
6
|
+
TaskLoader.load_tasks_if_gem_present(
|
|
7
|
+
'capistrano-rails', 'local/tasks',
|
|
8
|
+
'WARNING: Gemfile does not include capistrano-rails gem which is required for local:console (provides capistrano/rails/console)'
|
|
9
|
+
)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'capistrano/ops/local/helpers'
|
|
6
|
+
|
|
7
|
+
RSpec.describe Capistrano::Ops::Local::Helpers do
|
|
8
|
+
let(:host) do
|
|
9
|
+
Class.new do
|
|
10
|
+
include Capistrano::Ops::Local::Helpers
|
|
11
|
+
|
|
12
|
+
def initialize(scripts_dir_override)
|
|
13
|
+
@scripts_dir_override = scripts_dir_override
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fetch(key, default = nil)
|
|
17
|
+
return @scripts_dir_override if key == :local_scripts_dir
|
|
18
|
+
|
|
19
|
+
default
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
around do |example|
|
|
25
|
+
Dir.mktmpdir do |dir|
|
|
26
|
+
@tmp = dir
|
|
27
|
+
Dir.chdir(dir) { example.run }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def write_script(name)
|
|
32
|
+
dir = File.join(@tmp, 'ops', 'scripts')
|
|
33
|
+
FileUtils.mkdir_p(dir)
|
|
34
|
+
File.write(File.join(dir, name), '# example')
|
|
35
|
+
dir
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#available_scripts' do
|
|
39
|
+
it 'returns sorted basenames of .rb files' do
|
|
40
|
+
write_script('b.rb')
|
|
41
|
+
write_script('a.rb')
|
|
42
|
+
write_script('note.txt')
|
|
43
|
+
|
|
44
|
+
instance = host.new('ops/scripts')
|
|
45
|
+
|
|
46
|
+
expect(instance.available_scripts).to eq(%w[a.rb b.rb])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'is empty when the directory does not exist' do
|
|
50
|
+
instance = host.new('does/not/exist')
|
|
51
|
+
|
|
52
|
+
expect(instance.available_scripts).to eq([])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe '#resolve_script' do
|
|
57
|
+
it 'returns the full path when the exact filename exists' do
|
|
58
|
+
write_script('foo.rb')
|
|
59
|
+
|
|
60
|
+
instance = host.new('ops/scripts')
|
|
61
|
+
|
|
62
|
+
expect(instance.resolve_script('foo.rb')).to end_with('/ops/scripts/foo.rb')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'auto-appends .rb when omitted' do
|
|
66
|
+
write_script('foo.rb')
|
|
67
|
+
|
|
68
|
+
instance = host.new('ops/scripts')
|
|
69
|
+
|
|
70
|
+
expect(instance.resolve_script('foo')).to end_with('/ops/scripts/foo.rb')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'returns nil for unknown names' do
|
|
74
|
+
write_script('foo.rb')
|
|
75
|
+
|
|
76
|
+
instance = host.new('ops/scripts')
|
|
77
|
+
|
|
78
|
+
expect(instance.resolve_script('bar')).to be_nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'returns nil for empty input' do
|
|
82
|
+
instance = host.new('ops/scripts')
|
|
83
|
+
|
|
84
|
+
expect(instance.resolve_script(nil)).to be_nil
|
|
85
|
+
expect(instance.resolve_script('')).to be_nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe '#remote_tmp_path' do
|
|
90
|
+
it 'produces a unique /tmp path preserving the basename' do
|
|
91
|
+
instance = host.new('ops/scripts')
|
|
92
|
+
|
|
93
|
+
path = instance.remote_tmp_path('/some/where/foo.rb')
|
|
94
|
+
|
|
95
|
+
expect(path).to match(%r{\A/tmp/cap_local_[0-9a-f]{12}_foo\.rb\z})
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#color' do
|
|
100
|
+
it 'returns plain text when stdout is not a TTY' do
|
|
101
|
+
instance = host.new('ops/scripts')
|
|
102
|
+
allow($stdout).to receive(:tty?).and_return(false)
|
|
103
|
+
|
|
104
|
+
expect(instance.color('hello', :red)).to eq('hello')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'wraps text in ANSI escape when stdout is a TTY and style is known' do
|
|
108
|
+
instance = host.new('ops/scripts')
|
|
109
|
+
allow($stdout).to receive(:tty?).and_return(true)
|
|
110
|
+
|
|
111
|
+
expect(instance.color('hello', :red)).to eq("\e[31mhello\e[0m")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'passes text through unchanged for unknown styles' do
|
|
115
|
+
instance = host.new('ops/scripts')
|
|
116
|
+
allow($stdout).to receive(:tty?).and_return(true)
|
|
117
|
+
|
|
118
|
+
expect(instance.color('hello', :magenta)).to eq('hello')
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capistrano-ops
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Crusius
|
|
@@ -234,6 +234,10 @@ files:
|
|
|
234
234
|
- lib/capistrano/ops/helper.rb
|
|
235
235
|
- lib/capistrano/ops/invoke.rb
|
|
236
236
|
- lib/capistrano/ops/invoke/tasks/invoke.rake
|
|
237
|
+
- lib/capistrano/ops/local.rb
|
|
238
|
+
- lib/capistrano/ops/local/helpers.rb
|
|
239
|
+
- lib/capistrano/ops/local/tasks/console.rake
|
|
240
|
+
- lib/capistrano/ops/local/tasks/run.rake
|
|
237
241
|
- lib/capistrano/ops/logrotate.rb
|
|
238
242
|
- lib/capistrano/ops/logrotate/helpers.rb
|
|
239
243
|
- lib/capistrano/ops/logrotate/paths.rb
|
|
@@ -272,6 +276,7 @@ files:
|
|
|
272
276
|
- lib/capistrano/ops/wkhtmltopdf/helpers.rb
|
|
273
277
|
- lib/capistrano/ops/wkhtmltopdf/tasks/setup.rake
|
|
274
278
|
- scripts/bump_version.rb
|
|
279
|
+
- spec/capistrano/ops/local/helpers_spec.rb
|
|
275
280
|
- spec/capistrano/ops/version_spec.rb
|
|
276
281
|
- spec/spec_helper.rb
|
|
277
282
|
homepage: https://github.com/zauberware/capistrano-ops
|