minestrone 0.0.1 → 0.0.3

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: 6cd69ccb970305b22a5e19099df378e581cb3c15ff188ebdffcf2fef499e6bdd
4
- data.tar.gz: 7c01180ad806dd649ad13e5682d815179221c85c120aa60dc411d83fffd413de
3
+ metadata.gz: e3c8964c359e7dabb2d3d14bd09a093d4d4bbabbb8ce75bf47e2949eb2e711ea
4
+ data.tar.gz: 8772884a6badaf622abc7b794c5dc971bbab040fc403537402cfade082014a66
5
5
  SHA512:
6
- metadata.gz: e8ddd3e8f828248e489f7b22bb9476cb962ea1d04e5973b9cb4a2eed0d3558bd91bdaa8e7142a9151649b041148fa9cb60440a97fcd7bd737a4a7db13b304f25
7
- data.tar.gz: db1e39316df310daf33fd85aa3c11fecc671ad1ba19f9ccbe4d67e3d47aa370296cb182021e767f16c4b3b841a0cf72110d178c0bec54a69a7869435c044e26a
6
+ metadata.gz: 7c5aff5e3ac5ab932ac0d9f1adaf88b8fbdc646ee5056e219858a3c85574f90a60882aa35c9c38df1c293101eec28d91afbb844b2e9bb9cea13417ee12cebfd0
7
+ data.tar.gz: 587306f72ed2e29c81c29593e5a7cf7c765a184544b37fa94bbd5b45d0c1334bed073ed1928a38c539e737b220246c4506fa7ec8c06cfc044042ec8ca4a4a7af
data/CHANGELOG.md ADDED
@@ -0,0 +1,56 @@
1
+ ## [0.0.3] - 2026-10-24
2
+
3
+ - updated `capify` script to generate an updated and simplified deploy config
4
+ - bumped up `net-ssh`, `net-scp` and `net-sftp` required versions
5
+ - bumped up required Ruby version to 3.3+
6
+ - `ed25519` and `bcrypt_pbkdf` don't need to be separately included in the Gemfile
7
+
8
+ ## [0.0.2] - 2026-07-26
9
+
10
+ - added `upload_file` as an alias for `upload` (which in deploy tasks is shadowed by the `deploy:upload` task)
11
+ - added `append` & `remove` for adding/removing to array variables (from Capistrano 3)
12
+ - renamed `_cset` to `set_if_empty` and made it a public API (also from Capistrano 3)
13
+ - in migration and assets tasks, use `RACK_ENV` instead of `RAILS_ENV`, unless `rails_env` is explicitly defined (Rails also accepts `RACK_ENV`, but Sinatra/Rack doesn't accept `RAILS_ENV`)
14
+ - `:use_sudo` is now disabled by default
15
+ - renamed `version_dir` to `releases_dir` for consistency
16
+ - removed `:maintenance_basename`, `:maintenance_template_path` and the maintenance page tasks `deploy:web:disable` / `deploy:web:enable`
17
+ - removed `deploy:cold`, `deploy:start` and `deploy:stop` tasks
18
+ - removed `:normalize_asset_timestamps` and `:public_children`
19
+
20
+ ## Minestrone [0.0.1] - 2026-05-25
21
+
22
+ Changes from Capistrano 2.15.11:
23
+
24
+ Removed:
25
+
26
+ * server roles, multiple servers and "primary" server designation – you only define a single server hostname and everything happens there (`server` method in the DSL only accepts a single string + options, `role` is gone)
27
+ * support for any parallel execution
28
+ * multiple stages
29
+ * SCM handlers other than `:git` and `:none`
30
+ * most deploy strategies – only `:remote_cache` and `:copy` are left
31
+ * support for SSH gateways
32
+ * password authentication for SSH and Git
33
+ * REPL shell
34
+ * code supporting ancient versions of Ruby, Rails or Rake, or very old Capistrano API
35
+ * Windows support code
36
+
37
+ Changed:
38
+
39
+ * project name
40
+ * default deploy path is `/var/www/#{application}`
41
+ * `deploy:cleanup` now runs automatically, with 5 last releases
42
+ * bumped up minimum Ruby version to 3.0
43
+ * various refactoring and formatting changes
44
+
45
+ Added:
46
+
47
+ * `ENV['SERVER']` for overriding the host the tasks run on (`HOSTS` still works, but only accepts one hostname)
48
+ * set up CI on GitHub
49
+ * added `frozen_string_literal` directives everywhere
50
+ * added `benchmark` and `logger` to dependencies in gemspec
51
+ * added `deploy/bundler` recipe for Bundler integration
52
+
53
+
54
+ ## Capistrano 2.15.11 - 2023-06-17
55
+
56
+ Previous changelog available [here](https://github.com/capistrano/capistrano/blob/v2.15.11/CHANGELOG).
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in minestrone.gemspec
4
4
  gemspec
data/LICENSE.txt CHANGED
@@ -1,7 +1,8 @@
1
1
  MIT License (MIT)
2
2
 
3
- Copyright (c) 2012-2020 Tom Clements, Lee Hambley
4
3
  Copyright (c) 2005-2007 Jamis Buck <jamis@37signals.com>
4
+ Copyright (c) 2012-2020 Tom Clements, Lee Hambley
5
+ Copyright (c) 2026 Jakub Suder
5
6
 
6
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
8
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -19,14 +19,25 @@ Minestrone is a tool for deploying Ruby applications to a server, based on the c
19
19
  * REPL shell
20
20
  * legacy SCMs – only `:git` and `:none` are left
21
21
  * most deploy strategies – only `:remote_cache` and `:copy` are left
22
- * any legacy/deprecated code from Capistrano 1.x era or for ancient versions of Ruby
22
+ * maintenance page tasks
23
+ * normalizing asset timestamps
24
+ * `deploy:cold`, `deploy:start` and `deploy:stop` tasks
25
+ * any legacy/deprecated code from Capistrano 1.x era or for ancient versions of Ruby or Rails
23
26
 
24
27
 
25
28
  ### Other API changes
26
29
 
27
30
  * default deploy path is `/var/www/#{application}`
28
31
  * `server` method in the DSL only accepts a single string + options
29
- * Ruby 3.0+ is required
32
+ * `:use_sudo` is false by default
33
+ * `deploy:cleanup` runs automatically, with 5 last releases
34
+ * `deploy/bundler` recipe handles Bundler integration
35
+ * migration & asset tasks set a `RACK_ENV` instead of `RAILS_ENV` (which Rails also accepts), unless `:rails_env` is explicitly defined
36
+ * added `upload_file` as alias for `upload`
37
+ * added `append` and `remove` for adding/removing items from array variables
38
+ * renamed `_cset` to `set_if_empty`
39
+ * Ruby 3.3+ is required
40
+ * `ed25519` and `bcrypt_pbkdf` don't need to be separately included in the Gemfile
30
41
 
31
42
 
32
43
  ### Project status
data/SECURITY.md ADDED
@@ -0,0 +1,16 @@
1
+ # Security Policy
2
+
3
+ ## How To Report a Vulnerability
4
+
5
+ If you find a potential vulnerability in one of my projects, it's recommended not to report it through a standard publicly-readable issue report, in order to avoid giving possible clues to any potential attackers who could abuse it before it's fixed (see: ["Just a rumour of a bug is enough to find a security exploit these days"](https://anil.recoil.org/notes/rumour-is-the-exploit)).
6
+
7
+ You can use one of these to contact me instead:
8
+
9
+ - [@mackuba.eu](https://bsky.app/profile/did:plc:oio4hkxaop4ao4wz2pp3f4cr) on Bluesky (my DMs should be open, or mention me discretely with a request for contact)
10
+ - [@mackuba@martianbase.net](https://martianbase.net/@mackuba) on Mastodon (my personal instance)
11
+ - email: mackuba `@` protonmail.ch (optionally with [this PGP key](https://mail-api.proton.me/pks/lookup?op=get&search=mackuba@protonmail.ch))
12
+ - ["Report a vulnerability"](https://github.com/mackuba/minestrone/security/advisories/new) form on GitHub
13
+
14
+ I do not check my Twitter/X or LinkedIn accounts regularly.
15
+
16
+ Note: please don't submit AI-reported issues that have not been verified manually :)
data/bin/capify CHANGED
@@ -32,39 +32,40 @@ end
32
32
 
33
33
  def unindent(string)
34
34
  indentation = string[/\A\s*/]
35
- string.strip.gsub(/^#{indentation}/, "")
35
+ string.strip.gsub(/^#{indentation}/, "") + "\n"
36
36
  end
37
37
 
38
38
  files = {
39
39
  "Capfile" => unindent(<<-FILE),
40
-
41
40
  load 'deploy'
42
41
  load 'deploy/bundler'
43
42
 
44
- # Uncomment if you are using Rails' asset pipeline
43
+ # Uncomment if you are using Rails asset pipeline
45
44
  # load 'deploy/assets'
46
45
 
47
46
  load 'config/deploy'
48
47
  FILE
49
48
 
50
- "config/deploy.rb" => 'set :application, "set your application name here"
51
- set :repository, "set your repository location here"
52
-
53
- # set :scm, :git # :git is the default, or set :none to disable
49
+ "config/deploy.rb" => unindent(<<-FILE),
50
+ server "host.example.com"
54
51
 
55
- server "host.example.com"
52
+ set :application, "set your application name here"
53
+ set :repository, "set your git repository URL here"
56
54
 
57
- # if you want to clean up old releases on each deploy uncomment this:
58
- # after "deploy:restart", "deploy:cleanup"
55
+ # customize if needed
56
+ # set :keep_releases, 5
57
+ # set :rake, "RACK_ENV=production bundle exec rake"
58
+ # append :shared_children, "data"
59
59
 
60
- # If you are using Passenger, uncomment this:
61
- # namespace :deploy do
62
- # task :start do ; end
63
- # task :stop do ; end
64
- # task :restart do
65
- # run "#{try_sudo} touch #{File.join(current_path,\'tmp\',\'restart.txt\')}"
66
- # end
67
- # end'}
60
+ namespace :deploy do
61
+ task :restart do
62
+ # implement depending on the Ruby app server you use
63
+ # e.g. for Passenger:
64
+ # run "touch \#{current_path}/tmp/restart.txt"
65
+ end
66
+ end
67
+ FILE
68
+ }
68
69
 
69
70
  base = ARGV.shift
70
71
 
@@ -23,7 +23,7 @@ Built-in recipes live under `lib/minestrone/recipes`. The deploy recipe composes
23
23
  | `lib/minestrone/cli/options.rb` | `Minestrone::CLI::Options` | Defines `OptionParser` switches, default config discovery, environment variable extraction, and simple value coercion for `-s` and `-S`. | Mixed into `CLI`; requires `optparse`; uses `Logger`, `Version`, `CLI.password_prompt` |
24
24
  | `lib/minestrone/command.rb` | `Minestrone::Command` | Single-session remote command runner. Opens one SSH command channel, handles stdout/stderr callbacks, optional pty, environment injection, placeholder replacement, and remote command errors. | `errors`, `processable`, `Configuration.default_io_proc`, SSH session from `connections` |
25
25
  | `lib/minestrone/configuration.rb` | `Minestrone::Configuration` | Central DSL object. Owns logger/debug/dry-run state, explicitly calls each included module's initialization hook, mixes in all configuration and action modules, and unblocks namespace method shadowing. | `logger`; all `configuration/*`; `configuration/actions/*` |
26
- | `lib/minestrone/configuration/actions/file_transfer.rb` | `Minestrone::Configuration::Actions::FileTransfer` | Adds `put`, `get`, `upload`, `download`, and `transfer` DSL actions. Delegates actual work to `Minestrone::Transfer`. | `transfer`, `Connections#execute_on_server`, `Connections#session`, `run` |
26
+ | `lib/minestrone/configuration/actions/file_transfer.rb` | `Minestrone::Configuration::Actions::FileTransfer` | Adds `put`, `get`, `upload`/`upload_file`, `download`, and `transfer` DSL actions. `upload_file` avoids ambiguity with the `deploy:upload` task. Delegates actual work to `Minestrone::Transfer`. | `transfer`, `Connections#execute_on_server`, `Connections#session`, `run` |
27
27
  | `lib/minestrone/configuration/actions/inspect.rb` | `Minestrone::Configuration::Actions::Inspect` | Adds `stream` and `capture` actions for running commands and consuming streamed or captured single-server output. | `errors`, `Invocation#invoke_command`, `sudo` |
28
28
  | `lib/minestrone/configuration/actions/invocation.rb` | `Minestrone::Configuration::Actions::Invocation` | Adds `run`, `invoke_command`, `sudo`, command defaults, sudo prompt handling, and debug prompting. Converts DSL command calls into sequential `Command` execution. | `command`, `Servers#active_server`, `Connections#execute_on_server`, `CLI.debug_prompt`, `Variables` |
29
29
  | `lib/minestrone/configuration/alias_task.rb` | `Minestrone::Configuration::AliasTask` | Adds `alias_task`, duplicating an existing task under a new name. | `Namespaces#find_task`, `#define_task`, `NoSuchTaskError` |
@@ -33,15 +33,16 @@ Built-in recipes live under `lib/minestrone/recipes`. The deploy recipe composes
33
33
  | `lib/minestrone/configuration/loading.rb` | `Minestrone::Configuration::Loading`, `Loading::ClassMethods` | Loads recipes from files, strings, or procs. Provides a Minestrone-aware `require` that lets multiple configuration instances reload recipe DSL effects. | Used by `CLI`, recipes, extensions |
34
34
  | `lib/minestrone/configuration/log_formatters.rb` | `Minestrone::Configuration::LogFormatters` | DSL for adding logger formatters and disabling formatting. | `logger` |
35
35
  | `lib/minestrone/configuration/namespaces.rb` | `Minestrone::Configuration::Namespaces`, `Namespaces::Namespace`, `Kernel.method_added` hook | Implements `namespace`, `task`, `desc`, task lookup, namespace lookup, default tasks, and nested namespace forwarding to parent configuration. | `task_definition`, `alias_task`, `execution` |
36
- | `lib/minestrone/configuration/servers.rb` | `Minestrone::Configuration::Servers` | Adds the single-host `server` DSL and resolves the configured server, with `HOST` as a one-host environment override. | `ServerDefinition`, `errors` |
37
- | `lib/minestrone/configuration/variables.rb` | `Minestrone::Configuration::Variables` | Configuration variables with lazy proc evaluation, reset/unset, `fetch`, `[]`, and variable-backed `method_missing`. | Used by nearly every DSL and recipe file |
36
+ | `lib/minestrone/configuration/servers.rb` | `Minestrone::Configuration::Servers` | Adds the single-host `server` DSL and resolves the configured server, with `SERVER` as the preferred environment override and `HOSTS` as a compatibility fallback. | `ServerDefinition`, `errors` |
37
+ | `lib/minestrone/configuration/variables.rb` | `Minestrone::Configuration::Variables` | Configuration variables with lazy proc evaluation, conditional defaults through `set_if_empty`, array updates through `append`/`remove`, reset/unset, `fetch`, `[]`, and variable-backed `method_missing`. | Used by nearly every DSL and recipe file |
38
38
  | `lib/minestrone/errors.rb` | `Minestrone::Error`, `CaptureError`, `NoSuchTaskError`, `NoMatchingServersError`, `RemoteError`, `ConnectionError`, `TransferError`, `CommandError`, `LocalArgumentError` | Shared exception hierarchy. Remote errors carry affected hosts. | Used by command, transfer, connections, execution, recipes, deploy adapters |
39
39
  | `lib/minestrone/ext/string.rb` | reopens `String` | Adds `String#compact`, collapsing whitespace. Used to make heredoc shell commands one line. | Used by deploy assets and recipe command heredocs |
40
40
  | `lib/minestrone/extensions.rb` | `Minestrone::ExtensionProxy`, `Minestrone::EXTENSIONS`, plugin methods | Plugin registration system. Adds proxy methods to `Configuration` instances and delegates unknown plugin method calls back to the configuration. | `Configuration`, `Error` |
41
41
  | `lib/minestrone/logger.rb` | `Minestrone::Logger` | Level-based logger with TTY color/style formatters, timestamp/prepend/append/replace support, and default formatter registration. | Used by `Configuration`, CLI options, command/transfer/strategy/scm code |
42
42
  | `lib/minestrone/processable.rb` | `Minestrone::Processable`, `Processable::SessionAssociation` | Shared single-session Net::SSH event loop helper. Preprocesses/postprocesses the session with `IO.select` and associates raised errors with that session. | Included by `Command`, `Transfer` |
43
- | `lib/minestrone/recipes/deploy.rb` | recipe methods and tasks only | Primary deployment recipe. Defines deploy variables, helpers (`scm_default`, `depend`, `with_env`, `run_locally`, `try_sudo`, `try_runner`), and tasks for setup, update, update_code, symlinks, upload, rollback, migrations, cleanup, checks, cold deploy, start/stop, pending diff/log, and web maintenance mode. Defaults to Git when `.git` exists and `none` otherwise. | `deploy/scm`, `deploy/strategy`, `dependencies`, `Command`/`Transfer` through configuration actions |
43
+ | `lib/minestrone/recipes/deploy.rb` | recipe methods and tasks only | Primary deployment recipe. Defines deploy defaults, helpers (`scm_default`, `depend`, `with_env`, `r_env`, `run_locally`, `try_sudo`, `try_runner`), and tasks for setup, update, update_code, symlinks, upload, restart hooks, rollback, migrations, cleanup, checks, and pending diff/log. Defaults to Git when `.git` exists and `none` otherwise. | `deploy/scm`, `deploy/strategy`, `dependencies`, `Command`/`Transfer` through configuration actions |
44
44
  | `lib/minestrone/recipes/deploy/assets.rb` | recipe methods and tasks only | Rails asset-pipeline extension. Hooks into deploy lifecycle to symlink shared assets, precompile, maintain manifest mtimes, clean expired assets, clean all assets, and roll back manifests. | Loads `deploy`; `json`, `yaml` via deploy, `Set` via deploy, `String#compact`, callbacks/tasks/actions |
45
+ | `lib/minestrone/recipes/deploy/bundler.rb` | recipe methods and tasks only | Bundler integration. Configures bundle defaults, makes Rake run through Bundler, installs gems into the shared bundle path during deployment, and symlinks that path into each release. | Loads `deploy`; deploy variables, callbacks/tasks/actions |
45
46
  | `lib/minestrone/recipes/deploy/dependencies.rb` | `Minestrone::Deploy::Dependencies` | Aggregates local and remote dependency checks and reports pass/fail. | `local_dependency`, `remote_dependency` |
46
47
  | `lib/minestrone/recipes/deploy/local_dependency.rb` | `Minestrone::Deploy::LocalDependency` | Checks local command availability in `PATH`. | Used by `Dependencies`, `Strategy::Copy#check!` |
47
48
  | `lib/minestrone/recipes/deploy/remote_dependency.rb` | `Minestrone::Deploy::RemoteDependency` | Checks remote directories, files, writability, commands, gems, deb/rpm packages, and expected command output. | `errors`, `Configuration#invoke_command`, `CommandError` |
@@ -54,12 +55,11 @@ Built-in recipes live under `lib/minestrone/recipes`. The deploy recipe composes
54
55
  | `lib/minestrone/recipes/deploy/strategy/copy.rb` | `Minestrone::Deploy::Strategy::Copy`, `Copy::Compression` | Local-copy strategy. Checks out/export locally or refreshes a local cache, optionally builds, excludes files, archives, uploads, extracts remotely, and cleans staging artifacts. | `strategy/base`, `fileutils`, `tempfile`, `LocalDependency`, `FileTransfer#upload`, SCM local proxy |
55
56
  | `lib/minestrone/recipes/deploy/strategy/remote_cache.rb` | `Minestrone::Deploy::Strategy::RemoteCache` | Remote strategy maintaining a shared cached checkout under `shared_path`, then copying/rsyncing it to each release. Handles SCM prompt filtering and writes `REVISION`. | `strategy/base`, SCM `sync`/`checkout`/`handle_data`, remote `rsync` when exclusions exist |
56
57
  | `lib/minestrone/recipes/standard.rb` | recipe tasks only | Standard recipe loaded by CLI. Defines `invoke` for one-off remote commands. | `configuration/actions/invocation` |
57
- | `lib/minestrone/recipes/templates/maintenance.rhtml` | none | ERB/XHTML maintenance-page template used by `deploy:web:disable`. | Read by `recipes/deploy.rb` |
58
58
  | `lib/minestrone/server_definition.rb` | `Minestrone::ServerDefinition` | Parses and stores `user@host:port` plus server options. Provides comparison, equality/hash, default user, and string rendering. | Used by `Servers`, `SSH`, `Connections` |
59
59
  | `lib/minestrone/ssh.rb` | `Minestrone::SSH`, `SSH::Server` | SSH connection helper. Applies the originating `ServerDefinition` to the Net::SSH session and builds public-key-only connection options. | `net/ssh`, `ServerDefinition` |
60
60
  | `lib/minestrone/task_definition.rb` | `Minestrone::TaskDefinition` | Stores task metadata: name, namespace, options, body, description, and rollback behavior. Formats descriptions and computes fully qualified names. | `server_definition`; used by `configuration/namespaces` and `execution` |
61
61
  | `lib/minestrone/transfer.rb` | `Minestrone::Transfer`, `Transfer::SFTPTransferWrapper` | Single-session upload/download engine over SFTP or SCP. Normalizes host placeholders and IOs, tracks transfer failure, delegates event processing to `Processable`, and raises `TransferError`. | `net/scp`, `net/sftp`, `processable`, `errors`, SSH session |
62
- | `lib/minestrone/version.rb` | `Minestrone::Version`, `Minestrone::VERSION` | Version constants and string rendering for Minestrone 2.15.11. | Used by CLI `--version` |
62
+ | `lib/minestrone/version.rb` | `Minestrone::Version`, `Minestrone::VERSION` | Version constants and string rendering for Minestrone 0.0.2. | Used by CLI `--version` |
63
63
 
64
64
  ## Dependency Graph
65
65
 
@@ -105,6 +105,7 @@ flowchart TD
105
105
  deploy_recipe --> strategy_factory["Deploy::Strategy factory"]
106
106
  deploy_recipe --> config
107
107
  assets_recipe["recipes/deploy/assets.rb"] --> deploy_recipe
108
+ bundler_recipe["recipes/deploy/bundler.rb"] --> deploy_recipe
108
109
 
109
110
  scm_factory --> scm_base["SCM::Base"]
110
111
  scm_base --> scm_adapters["Git, None"]
@@ -137,7 +138,8 @@ These are the code-level edges visible from `require` and `load`, excluding Ruby
137
138
  | `lib/minestrone/configuration/namespaces.rb` | `minestrone/configuration/alias_task`, `minestrone/task_definition` |
138
139
  | `lib/minestrone/configuration/servers.rb` | `minestrone/server_definition`, `minestrone/errors` |
139
140
  | `lib/minestrone/recipes/deploy.rb` | `minestrone/recipes/deploy/scm`, `minestrone/recipes/deploy/strategy` |
140
- | `lib/minestrone/recipes/deploy/assets.rb` | `load 'deploy'` unless `_cset` already exists |
141
+ | `lib/minestrone/recipes/deploy/assets.rb` | `load 'deploy'` unless the `try_sudo` deploy helper already exists |
142
+ | `lib/minestrone/recipes/deploy/bundler.rb` | `load 'deploy'` unless the `try_sudo` deploy helper already exists |
141
143
  | `lib/minestrone/recipes/deploy/dependencies.rb` | `minestrone/recipes/deploy/local_dependency`, `minestrone/recipes/deploy/remote_dependency` |
142
144
  | `lib/minestrone/recipes/deploy/remote_dependency.rb` | `minestrone/errors` |
143
145
  | `lib/minestrone/recipes/deploy/scm.rb` | dynamically `minestrone/recipes/deploy/scm/<name>` |
@@ -154,9 +156,9 @@ These are the code-level edges visible from `require` and `load`, excluding Ruby
154
156
 
155
157
  - `Configuration` is the central dependency hub. Most recipe and strategy code calls into it through DSL methods mixed in from its modules.
156
158
  - `Command` and `Transfer` depend on `Processable` because they share the same single-session Net::SSH event-loop pattern.
157
- - `ServerDefinition` is the single configured server identity object. It carries host/user/port/options into the `HOST` override path, SSH connection setup, command placeholders, transfer logging, and error reporting.
159
+ - `ServerDefinition` is the single configured server identity object. It carries host/user/port/options into the `SERVER`/`HOSTS` override path, SSH connection setup, command placeholders, transfer logging, and error reporting.
158
160
  - `Callbacks` wraps `Execution#invoke_task_directly`; task lifecycle hooks therefore apply to all direct task execution paths, including CLI actions and recipe-invoked task aliases.
159
161
  - `Deploy::SCM` and `Deploy::Strategy` are factories. They load adapter files by name and instantiate classes from generated constant names.
160
162
  - SCM adapters generally do not execute commands. They return shell command strings and prompt handlers. Strategies decide whether those commands run locally, remotely, through caches, or through uploaded archives.
161
163
  - `Strategy::Copy` is the main bridge from local SCM commands to remote deployment: it uses the SCM's local proxy, local filesystem staging, compression utilities, `upload`, and remote decompression.
162
- - `recipes/deploy/assets.rb` is not a class extension. It is a recipe that mutates the current configuration by setting defaults, registering callbacks, adding helper methods, and defining `deploy:assets:*` tasks.
164
+ - `recipes/deploy/assets.rb` and `recipes/deploy/bundler.rb` are not class extensions. They are recipes that mutate the current configuration by setting defaults, registering callbacks, adding helper methods, and defining deployment tasks.
@@ -34,6 +34,8 @@ module Minestrone
34
34
  end
35
35
  end
36
36
 
37
+ alias upload_file upload
38
+
37
39
  def download(from, to, options = {}, &block)
38
40
  transfer(:down, from, to, options, &block)
39
41
  end
@@ -36,6 +36,13 @@ module Minestrone
36
36
 
37
37
  alias :[]= :set
38
38
 
39
+ # Sets a variable only when it has not already been defined. Existing
40
+ # values, including nil and false, are preserved. Accepts either a value
41
+ # or a block, following the same rules as #set.
42
+ def set_if_empty(variable, *args, &block)
43
+ set(variable, *args, &block) unless exists?(variable)
44
+ end
45
+
39
46
  # Removes any trace of the given variable.
40
47
  def unset(variable)
41
48
  sym = variable.to_sym
@@ -43,6 +50,20 @@ module Minestrone
43
50
  @variables.delete(sym)
44
51
  end
45
52
 
53
+ # Appends one or more values to an array variable. Missing variables are
54
+ # treated as empty arrays, and existing scalar values are wrapped in an
55
+ # array before the new values are appended.
56
+ def append(variable, *values)
57
+ set(variable, Array(fetch(variable, [])).concat(values))
58
+ end
59
+
60
+ # Removes all occurrences of the given values from an array variable.
61
+ # Missing variables are treated as empty arrays, and existing scalar
62
+ # values are wrapped in an array before removal.
63
+ def remove(variable, *values)
64
+ set(variable, Array(fetch(variable, [])) - values)
65
+ end
66
+
46
67
  # Returns true if the variable has been defined, and false otherwise.
47
68
  def exists?(variable)
48
69
  @variables.key?(variable.to_sym)
@@ -2,15 +2,13 @@
2
2
 
3
3
  require 'json'
4
4
 
5
- load 'deploy' unless defined?(_cset)
5
+ load 'deploy' unless defined?(try_sudo)
6
6
 
7
- _cset :asset_env, "RAILS_GROUPS=assets"
8
- _cset :assets_prefix, "assets"
9
- _cset :shared_assets_prefix, "assets"
10
- _cset :expire_assets_after, (3600 * 24 * 7)
11
- _cset(:asset_manifest_prefix) { (`sprockets -v`.chomp < "3.0" ? "manifest" : ".sprockets-manifest") rescue "manifest" }
12
-
13
- _cset :normalize_asset_timestamps, false
7
+ set_if_empty :asset_env, "RAILS_GROUPS=assets"
8
+ set_if_empty :assets_prefix, "assets"
9
+ set_if_empty :shared_assets_prefix, "assets"
10
+ set_if_empty :expire_assets_after, (3600 * 24 * 7)
11
+ set_if_empty(:asset_manifest_prefix) { (`sprockets -v`.chomp < "3.0" ? "manifest" : ".sprockets-manifest") rescue "manifest" }
14
12
 
15
13
  before 'deploy:finalize_update', 'deploy:assets:symlink'
16
14
  after 'deploy:update_code', 'deploy:assets:precompile'
@@ -57,13 +55,12 @@ namespace :deploy do
57
55
  asset_env variable. The defaults are:
58
56
 
59
57
  set :rake, "rake"
60
- set :rails_env, "production"
58
+ set :rack_env, "production"
61
59
  set :asset_env, "RAILS_GROUPS=assets"
62
60
  DESC
63
61
  task :precompile do
64
62
  run <<-CMD.compact
65
- cd -- #{latest_release} &&
66
- RAILS_ENV=#{rails_env.to_s.shellescape} #{asset_env} #{rake} assets:precompile
63
+ cd -- #{latest_release} && #{r_env} #{asset_env} #{rake} assets:precompile
67
64
  CMD
68
65
 
69
66
  if capture("ls -1 #{shared_path.shellescape}/#{shared_assets_prefix}/#{asset_manifest_prefix}* | wc -l").to_i > 1
@@ -112,11 +109,11 @@ namespace :deploy do
112
109
  asset_env variable. The defaults are:
113
110
 
114
111
  set :rake, "rake"
115
- set :rails_env, "production"
112
+ set :rack_env, "production"
116
113
  set :asset_env, "RAILS_GROUPS=assets"
117
114
  DESC
118
115
  task :clean do
119
- run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:clean"
116
+ run "cd #{latest_release} && #{rake} #{r_env} #{asset_env} assets:clean"
120
117
  end
121
118
 
122
119
  desc <<-DESC
@@ -186,7 +183,7 @@ namespace :deploy do
186
183
  run <<-CMD.compact
187
184
  cd -- #{previous_release.shellescape} &&
188
185
  cp -f -- #{previous_manifest.shellescape} #{restored_manifest_path.shellescape} &&
189
- [ -z "$(#{rake} -P | grep assets:precompile:nondigest)" ] || #{rake} RAILS_ENV=#{rails_env.to_s.shellescape} #{asset_env} assets:precompile:nondigest
186
+ [ -z "$(#{rake} -P | grep assets:precompile:nondigest)" ] || #{rake} #{r_env} #{asset_env} assets:precompile:nondigest
190
187
  CMD
191
188
  end
192
189
  end
@@ -4,13 +4,13 @@
4
4
  # https://github.com/ruby/rubygems/blob/master/bundler/lib/bundler/minestrone.rb
5
5
  # and https://github.com/minestrone/bundler
6
6
 
7
- load 'deploy' unless defined?(_cset)
7
+ load 'deploy' unless defined?(try_sudo)
8
8
 
9
- _cset :bundle_env, ''
10
- _cset :bundle_cmd, 'bundle'
11
- _cset(:bundle_path) { "#{shared_path}/bundle" }
12
- _cset :bundle_without, [:development, :test]
13
- _cset :bundle_flags, '--quiet'
9
+ set_if_empty :bundle_env, ''
10
+ set_if_empty :bundle_cmd, 'bundle'
11
+ set_if_empty(:bundle_path) { "#{shared_path}/bundle" }
12
+ set_if_empty :bundle_without, [:development, :test]
13
+ set_if_empty :bundle_flags, '--quiet'
14
14
 
15
15
  set(:rake) { "#{bundle_cmd} exec rake" }
16
16
 
@@ -7,35 +7,29 @@ require "yaml"
7
7
  require "minestrone/recipes/deploy/scm"
8
8
  require "minestrone/recipes/deploy/strategy"
9
9
 
10
- def _cset(name, *args, &block)
11
- unless exists?(name)
12
- set(name, *args, &block)
13
- end
14
- end
15
-
16
10
  # =========================================================================
17
11
  # These variables MUST be set in the client capfiles. If they are not set,
18
12
  # the deploy will fail with an error.
19
13
  # =========================================================================
20
14
 
21
- _cset(:application) { abort "Please specify the name of your application, set :application, 'foo'" }
22
- _cset(:repository) { abort "Please specify the repository that houses your application's code, set :repository, 'foo'" }
15
+ set_if_empty(:application) { abort "Please specify the name of your application, set :application, 'foo'" }
16
+ set_if_empty(:repository) { abort "Please specify the repository that houses your application's code, set :repository, 'foo'" }
23
17
 
24
18
  # =========================================================================
25
19
  # These variables may be set in the client capfile if their default values
26
20
  # are not sufficient.
27
21
  # =========================================================================
28
22
 
29
- _cset(:scm) { scm_default }
30
- _cset :deploy_via, :remote_cache
23
+ set_if_empty(:scm) { scm_default }
24
+ set_if_empty :deploy_via, :remote_cache
31
25
 
32
- _cset(:deploy_to) { "/var/www/#{application}" }
33
- _cset(:revision) { source.head }
26
+ set_if_empty(:deploy_to) { "/var/www/#{application}" }
27
+ set_if_empty(:revision) { source.head }
34
28
 
35
- _cset :rails_env, "production"
36
- _cset :rake, "rake"
29
+ set_if_empty :rack_env, "production"
30
+ set_if_empty :rake, "rake"
37
31
 
38
- _cset :keep_releases, 5
32
+ set_if_empty :keep_releases, 5
39
33
 
40
34
  # =========================================================================
41
35
  # These variables should NOT be changed unless you are very confident in
@@ -43,43 +37,40 @@ _cset :keep_releases, 5
43
37
  # changes if you do decide to muck with these!
44
38
  # =========================================================================
45
39
 
46
- _cset(:source) { Minestrone::Deploy::SCM.new(scm, self) }
47
- _cset(:real_revision) { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") { run_locally(cmd) } } }
40
+ set_if_empty(:source) { Minestrone::Deploy::SCM.new(scm, self) }
41
+ set_if_empty(:real_revision) { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") { run_locally(cmd) } } }
48
42
 
49
- _cset(:strategy) { Minestrone::Deploy::Strategy.new(deploy_via, self) }
43
+ set_if_empty(:strategy) { Minestrone::Deploy::Strategy.new(deploy_via, self) }
50
44
 
51
45
  # If overriding release name, please also select an appropriate setting for :releases below.
52
- _cset(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") }
46
+ set_if_empty(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") }
53
47
 
54
- _cset :version_dir, "releases"
55
- _cset :shared_dir, "shared"
56
- _cset :shared_children, %w(public/system log tmp/pids)
57
- _cset :current_dir, "current"
48
+ set_if_empty :releases_dir, "releases"
49
+ set_if_empty :shared_dir, "shared"
50
+ set_if_empty :shared_children, %w(public/system log tmp/pids)
51
+ set_if_empty :current_dir, "current"
58
52
 
59
- _cset(:releases_path) { File.join(deploy_to, version_dir) }
60
- _cset(:shared_path) { File.join(deploy_to, shared_dir) }
61
- _cset(:current_path) { File.join(deploy_to, current_dir) }
62
- _cset(:release_path) { File.join(releases_path, release_name) }
53
+ set_if_empty(:releases_path) { File.join(deploy_to, releases_dir) }
54
+ set_if_empty(:shared_path) { File.join(deploy_to, shared_dir) }
55
+ set_if_empty(:current_path) { File.join(deploy_to, current_dir) }
56
+ set_if_empty(:release_path) { File.join(releases_path, release_name) }
63
57
 
64
- _cset(:releases) { capture("#{try_sudo} ls -x #{releases_path}").split.sort }
65
- _cset(:current_release) { releases.length > 0 ? File.join(releases_path, releases.last) : nil }
66
- _cset(:previous_release) { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil }
58
+ set_if_empty(:releases) { capture("#{try_sudo} ls -x #{releases_path}").split.sort }
59
+ set_if_empty(:current_release) { releases.length > 0 ? File.join(releases_path, releases.last) : nil }
60
+ set_if_empty(:previous_release) { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil }
67
61
 
68
- _cset(:current_revision) { capture("#{try_sudo} cat #{current_path}/REVISION").chomp }
69
- _cset(:latest_revision) { capture("#{try_sudo} cat #{current_release}/REVISION").chomp }
70
- _cset(:previous_revision) { capture("#{try_sudo} cat #{previous_release}/REVISION").chomp if previous_release }
62
+ set_if_empty(:current_revision) { capture("#{try_sudo} cat #{current_path}/REVISION").chomp }
63
+ set_if_empty(:latest_revision) { capture("#{try_sudo} cat #{current_release}/REVISION").chomp }
64
+ set_if_empty(:previous_revision) { capture("#{try_sudo} cat #{previous_release}/REVISION").chomp if previous_release }
71
65
 
72
- _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
66
+ set_if_empty(:run_method) { fetch(:use_sudo, false) ? :sudo : :run }
73
67
 
74
68
  # some tasks, like symlink, need to always point at the latest release, but
75
69
  # they can also (occassionally) be called standalone. In the standalone case,
76
70
  # the timestamped release_path will be inaccurate, since the directory won't
77
71
  # actually exist. This variable lets tasks like symlink work either in the
78
72
  # standalone case, or during deployment.
79
- _cset(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release }
80
-
81
- _cset :maintenance_basename, "maintenance"
82
- _cset(:maintenance_template_path) { File.join(__dir__, "templates", "maintenance.rhtml") }
73
+ set_if_empty(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release }
83
74
 
84
75
  # =========================================================================
85
76
  # These are helper methods that will be available to your recipes.
@@ -109,6 +100,15 @@ ensure
109
100
  ENV[name] = saved
110
101
  end
111
102
 
103
+ def r_env
104
+ if rails_env = fetch(:rails_env, nil)
105
+ "RAILS_ENV=#{rails_env.to_s.shellescape}"
106
+ else
107
+ rack_env = fetch(:rack_env, "production")
108
+ "RACK_ENV=#{rack_env.to_s.shellescape}"
109
+ end
110
+ end
111
+
112
112
  # Logs the command then executes it locally. Returns the command output as a string.
113
113
  def run_locally(cmd)
114
114
  if dry_run
@@ -179,10 +179,7 @@ end
179
179
 
180
180
  namespace :deploy do
181
181
  desc <<-DESC
182
- Deploys your project. This calls both `update' and `restart'. Note that \
183
- this will generally only work for applications that have already been deployed \
184
- once. For a "cold" deploy, you'll want to take a look at the `deploy:cold' \
185
- task, which handles the cold start specifically.
182
+ Deploys your project. This calls both `update' and `restart'.
186
183
  DESC
187
184
 
188
185
  task :default do
@@ -253,14 +250,9 @@ namespace :deploy do
253
250
  task with your own environment's requirements.
254
251
 
255
252
  This task will make the release group-writable (if the :group_writable \
256
- variable is set to true, which is the default). It will then set up \
253
+ variable is set to true, which is the default) and will set up \
257
254
  symlinks to the shared directory for the log, system, and tmp/pids \
258
- directories, and will lastly touch all assets in public/images, \
259
- public/stylesheets, and public/javascripts so that the times are \
260
- consistent (so that asset timestamping works). This touch process \
261
- is only carried out if the :normalize_asset_timestamps variable is \
262
- set to true, which is the default. The asset directories can be overridden \
263
- using the :public_children variable.
255
+ directories.
264
256
  DESC
265
257
 
266
258
  task :finalize_update do
@@ -286,18 +278,6 @@ namespace :deploy do
286
278
  end
287
279
 
288
280
  run commands.join(' && ') if commands.any?
289
-
290
- if fetch(:normalize_asset_timestamps, true)
291
- stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
292
-
293
- asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).
294
- map { |p| "#{latest_release}/public/#{p}" }.
295
- map { |p| p.shellescape }
296
-
297
- if asset_paths.any?
298
- run("find #{asset_paths.join(" ")} -exec touch -t #{stamp} -- {} ';'; true", :env => { "TZ" => "UTC" })
299
- end
300
- end
301
281
  end
302
282
 
303
283
  desc <<-DESC
@@ -418,14 +398,13 @@ namespace :deploy do
418
398
  rake executable by setting the rake variable. The defaults are:
419
399
 
420
400
  set :rake, "rake"
421
- set :rails_env, "production"
401
+ set :rack_env, "production"
422
402
  set :migrate_env, ""
423
403
  set :migrate_target, :latest
424
404
  DESC
425
405
 
426
406
  task :migrate do
427
407
  rake = fetch(:rake, "rake")
428
- rails_env = fetch(:rails_env, "production")
429
408
  migrate_env = fetch(:migrate_env, "")
430
409
  migrate_target = fetch(:migrate_target, :latest)
431
410
 
@@ -435,7 +414,7 @@ namespace :deploy do
435
414
  else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
436
415
  end
437
416
 
438
- run "cd #{directory} && #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
417
+ run "cd #{directory} && #{r_env} #{migrate_env} #{rake} db:migrate"
439
418
  end
440
419
 
441
420
  desc <<-DESC
@@ -458,9 +437,7 @@ namespace :deploy do
458
437
  desc <<-DESC
459
438
  Clean up old releases. By default, the last 5 releases are kept on each \
460
439
  server (though you can change this with the keep_releases variable). All \
461
- other deployed revisions are removed from the server. By default, this \
462
- will use sudo to clean up the old releases, but if sudo is not available \
463
- for your environment, set the :use_sudo variable to false instead.
440
+ other deployed revisions are removed from the server.
464
441
  DESC
465
442
 
466
443
  task :cleanup do
@@ -511,38 +488,6 @@ namespace :deploy do
511
488
  end
512
489
  end
513
490
 
514
- desc <<-DESC
515
- Deploys and starts a `cold' application. This is useful if you have not \
516
- deployed your application before, or if your application is (for some \
517
- other reason) not currently running. It will deploy the code, run any \
518
- pending migrations, and then instead of invoking `deploy:restart', it will \
519
- invoke `deploy:start' to fire up the application servers.
520
- DESC
521
-
522
- task :cold do
523
- update
524
- migrate
525
- start
526
- end
527
-
528
- desc <<-DESC
529
- Blank task exists as a hook into which to install your own environment \
530
- specific behaviour.
531
- DESC
532
-
533
- task :start do
534
- # Empty Task to overload with your platform specifics
535
- end
536
-
537
- desc <<-DESC
538
- Blank task exists as a hook into which to install your own environment \
539
- specific behaviour.
540
- DESC
541
-
542
- task :stop do
543
- # Empty Task to overload with your platform specifics
544
- end
545
-
546
491
  namespace :pending do
547
492
  desc <<-DESC
548
493
  Displays the `diff' since your last deploy. This is useful if you want \
@@ -565,75 +510,4 @@ namespace :deploy do
565
510
  system(source.local.log(from))
566
511
  end
567
512
  end
568
-
569
- namespace :web do
570
- desc <<-DESC
571
- Present a maintenance page to visitors. Disables your application's web \
572
- interface by writing a "#{maintenance_basename}.html" file to the web server. The \
573
- server must be configured to detect the presence of this file, and if \
574
- it is present, always display it instead of performing the request.
575
-
576
- By default, the maintenance page will just say the site is down for \
577
- "maintenance", and will be back "shortly", but you can customize the \
578
- page by specifying the REASON and UNTIL environment variables:
579
-
580
- $ min deploy:web:disable \\
581
- REASON="hardware upgrade" \\
582
- UNTIL="12pm Central Time"
583
-
584
- You can use a different template for the maintenance page by setting the \
585
- :maintenance_template_path variable in your deploy.rb file. The template file \
586
- should either be a plaintext or an erb file.
587
-
588
- Further customization will require that you write your own task.
589
- DESC
590
-
591
- task :disable do
592
- require 'erb'
593
- on_rollback { run "rm -f #{shared_path}/system/#{maintenance_basename}.html" }
594
-
595
- warn <<-EOHTACCESS
596
-
597
- # Please add something like this to your site's Apache htaccess to redirect users to the maintenance page.
598
- # More Info: http://www.shiftcommathree.com/articles/make-your-rails-maintenance-page-respond-with-a-503
599
-
600
- ErrorDocument 503 /system/#{maintenance_basename}.html
601
- RewriteEngine On
602
- RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
603
- RewriteCond %{DOCUMENT_ROOT}/system/#{maintenance_basename}.html -f
604
- RewriteCond %{SCRIPT_FILENAME} !#{maintenance_basename}.html
605
- RewriteRule ^.*$ - [redirect=503,last]
606
-
607
- # Or if you are using Nginx add this to your server config:
608
-
609
- if (-f $document_root/system/maintenance.html) {
610
- return 503;
611
- }
612
- error_page 503 @maintenance;
613
- location @maintenance {
614
- rewrite ^(.*)$ /system/maintenance.html break;
615
- break;
616
- }
617
- EOHTACCESS
618
-
619
- reason = ENV['REASON']
620
- deadline = ENV['UNTIL']
621
-
622
- template = File.read(maintenance_template_path)
623
- result = ERB.new(template).result(binding)
624
-
625
- put result, "#{shared_path}/system/#{maintenance_basename}.html", :mode => 0644
626
- end
627
-
628
- desc <<-DESC
629
- Makes the application web-accessible again. Removes the \
630
- "#{maintenance_basename}.html" page generated by deploy:web:disable, which (if your \
631
- web server is configured correctly) will make your application \
632
- web-accessible again.
633
- DESC
634
-
635
- task :enable do
636
- run "rm -f #{shared_path}/system/#{maintenance_basename}.html"
637
- end
638
- end
639
513
  end
@@ -3,7 +3,7 @@
3
3
  module Minestrone
4
4
  class Version
5
5
  def self.to_s
6
- "0.0.1"
6
+ "0.0.3"
7
7
  end
8
8
  end
9
9
 
data/minestrone.gemspec CHANGED
@@ -1,32 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- $LOAD_PATH.push File.expand_path("lib", __dir__)
4
- require "minestrone/version"
3
+ require_relative 'lib/minestrone/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
6
  spec.name = "minestrone"
8
7
  spec.version = Minestrone::Version.to_s
9
8
  spec.platform = Gem::Platform::RUBY
10
9
  spec.authors = ["Jamis Buck", "Lee Hambley", "Kuba Suder"]
11
- spec.homepage = "http://github.com/mackuba/minestrone"
12
- spec.summary = "Minestrone - Welcome to easy deployment with Ruby over SSH"
13
- spec.description = "Minestrone is a utility and framework for executing commands on a remote machine, via SSH."
10
+
11
+ spec.summary = "Simple deployment tool for Ruby apps (based on Capistrano)"
12
+ spec.description = %(
13
+ Minestrone is a simplified fork of old Capistrano from before the 3.0 rewrite. It retains most of the old, simpler API,
14
+ while also removing some more advanced features, and focusing on the relatively simplest but common scenario of a single
15
+ production server. It also removes a lot of old cruft, support for less commonly used or less recommended things,
16
+ and modernizes some of the code.
17
+ )
18
+
19
+ spec.homepage = "https://github.com/mackuba/minestrone"
20
+ spec.license = "MIT"
21
+
22
+ spec.metadata = {
23
+ "bug_tracker_uri" => "https://github.com/mackuba/minestrone/issues",
24
+ "changelog_uri" => "https://github.com/mackuba/minestrone/blob/master/CHANGELOG.md",
25
+ "source_code_uri" => "https://github.com/mackuba/minestrone",
26
+ "rubygems_mfa_required" => "true"
27
+ }
28
+
14
29
  spec.files = `git ls-files`.split("\n")
15
- spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
- spec.executables = `git ls-files -- bin/*`.split("\n").map { |file| File.basename(file) }
30
+ spec.executables = ['min', 'capify']
17
31
  spec.require_paths = ["lib"]
18
32
  spec.extra_rdoc_files = [
19
33
  "README.md"
20
34
  ]
21
35
 
22
- spec.required_ruby_version = ">= 3.0.0"
36
+ spec.required_ruby_version = ">= 3.3.0"
23
37
 
24
38
  spec.add_dependency 'benchmark', '~> 0.5'
25
- spec.add_dependency 'highline', '>= 0'
26
- spec.add_dependency 'net-ssh', '>= 7.2'
27
- spec.add_dependency 'net-sftp', '>= 3.0'
28
- spec.add_dependency 'net-scp', '>= 3.0'
29
-
30
- # used silently by net-ssh but undeclared
31
- spec.add_dependency 'logger'
39
+ spec.add_dependency 'highline', '~> 3.0'
40
+ spec.add_dependency 'net-ssh', '~> 8.0'
41
+ spec.add_dependency 'net-sftp', '~> 4.0'
42
+ spec.add_dependency 'net-scp', '~> 4.0'
43
+ spec.add_dependency 'bcrypt_pbkdf', '~> 1.1'
32
44
  end
@@ -47,6 +47,12 @@ class ConfigurationActionsFileTransferTest < Test::Unit::TestCase
47
47
  @config.upload("testl.txt", "testr.txt", :mode => "g+w", :foo => "bar")
48
48
  end
49
49
 
50
+ def test_upload_file_should_alias_upload
51
+ @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", { :foo => "bar" })
52
+ @config.expects(:run).with("chmod 775 testr.txt", { :foo => "bar" })
53
+ @config.upload_file("testl.txt", "testr.txt", :mode => 0775, :foo => "bar")
54
+ end
55
+
50
56
  def test_download_should_delegate_to_transfer
51
57
  @config.expects(:transfer).with(:down, "testr.txt", "testl.txt", { :foo => "bar" })
52
58
  @config.download("testr.txt", "testl.txt", :foo => "bar")
@@ -96,6 +96,60 @@ class ConfigurationVariablesTest < Test::Unit::TestCase
96
96
  end
97
97
  end
98
98
 
99
+ def test_set_if_empty_should_set_missing_variable
100
+ @config.set_if_empty :sample, :value
101
+ assert_equal :value, @config.fetch(:sample)
102
+ end
103
+
104
+ def test_set_if_empty_should_accept_lazy_block
105
+ @config.set_if_empty(:sample) { :value }
106
+ assert_instance_of Proc, @config.variables[:sample]
107
+ assert_equal :value, @config.fetch(:sample)
108
+ end
109
+
110
+ def test_set_if_empty_should_not_replace_existing_false_or_nil
111
+ @config.set :false_value, false
112
+ @config.set :nil_value, nil
113
+
114
+ @config.set_if_empty :false_value, :replacement
115
+ replacement_called = false
116
+ @config.set_if_empty(:nil_value) { replacement_called = true }
117
+
118
+ assert_equal false, @config.fetch(:false_value)
119
+ assert_nil @config.fetch(:nil_value)
120
+ assert_equal false, replacement_called
121
+ end
122
+
123
+ def test_append_should_add_values_to_missing_or_existing_array
124
+ @config.append :missing, :one, :two
125
+ @config.set :existing, [:one]
126
+ @config.append :existing, :two, :three
127
+
128
+ assert_equal [:one, :two], @config.fetch(:missing)
129
+ assert_equal [:one, :two, :three], @config.fetch(:existing)
130
+ end
131
+
132
+ def test_append_should_wrap_existing_scalar_var_in_array
133
+ @config.set :sample, :one
134
+ @config.append :sample, :two
135
+ assert_equal [:one, :two], @config.fetch(:sample)
136
+ end
137
+
138
+ def test_remove_should_remove_values_from_missing_or_existing_array
139
+ @config.remove :missing, :one
140
+ @config.set :existing, [:one, :two, :one, :three]
141
+ @config.remove :existing, :one, :three
142
+
143
+ assert_equal [], @config.fetch(:missing)
144
+ assert_equal [:two], @config.fetch(:existing)
145
+ end
146
+
147
+ def test_remove_should_wrap_existing_scalar
148
+ @config.set :sample, :one
149
+ @config.remove :sample, :two
150
+ assert_equal [:one], @config.fetch(:sample)
151
+ end
152
+
99
153
  def test_unset_should_remove_variable_from_hash
100
154
  @config.set :sample, :value
101
155
  assert @config.variables.key?(:sample)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minestrone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamis Buck
@@ -29,74 +29,78 @@ dependencies:
29
29
  name: highline
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
32
+ - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '0'
34
+ version: '3.0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ">="
39
+ - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '0'
41
+ version: '3.0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: net-ssh
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ">="
46
+ - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '7.2'
48
+ version: '8.0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ">="
53
+ - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '7.2'
55
+ version: '8.0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: net-sftp
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ">="
60
+ - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '3.0'
62
+ version: '4.0'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ">="
67
+ - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '3.0'
69
+ version: '4.0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: net-scp
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - ">="
74
+ - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '3.0'
76
+ version: '4.0'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - ">="
81
+ - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '3.0'
83
+ version: '4.0'
84
84
  - !ruby/object:Gem::Dependency
85
- name: logger
85
+ name: bcrypt_pbkdf
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - ">="
88
+ - - "~>"
89
89
  - !ruby/object:Gem::Version
90
- version: '0'
90
+ version: '1.1'
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - ">="
95
+ - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: '0'
98
- description: Minestrone is a utility and framework for executing commands on a remote
99
- machine, via SSH.
97
+ version: '1.1'
98
+ description: "\n Minestrone is a simplified fork of old Capistrano from before
99
+ the 3.0 rewrite. It retains most of the old, simpler API,\n while also removing
100
+ some more advanced features, and focusing on the relatively simplest but common
101
+ scenario of a single\n production server. It also removes a lot of old cruft,
102
+ support for less commonly used or less recommended things,\n and modernizes some
103
+ of the code.\n "
100
104
  executables:
101
105
  - capify
102
106
  - min
@@ -106,10 +110,12 @@ extra_rdoc_files:
106
110
  files:
107
111
  - ".github/workflows/main.yml"
108
112
  - ".gitignore"
113
+ - CHANGELOG.md
109
114
  - Gemfile
110
115
  - LICENSE.txt
111
116
  - README.md
112
117
  - Rakefile
118
+ - SECURITY.md
113
119
  - bin/capify
114
120
  - bin/min
115
121
  - docs/lib-codebase-map.md
@@ -154,7 +160,6 @@ files:
154
160
  - lib/minestrone/recipes/deploy/strategy/copy.rb
155
161
  - lib/minestrone/recipes/deploy/strategy/remote_cache.rb
156
162
  - lib/minestrone/recipes/standard.rb
157
- - lib/minestrone/recipes/templates/maintenance.rhtml
158
163
  - lib/minestrone/server_definition.rb
159
164
  - lib/minestrone/ssh.rb
160
165
  - lib/minestrone/task_definition.rb
@@ -198,9 +203,14 @@ files:
198
203
  - test/transfer_test.rb
199
204
  - test/utils.rb
200
205
  - test/version_test.rb
201
- homepage: http://github.com/mackuba/minestrone
202
- licenses: []
203
- metadata: {}
206
+ homepage: https://github.com/mackuba/minestrone
207
+ licenses:
208
+ - MIT
209
+ metadata:
210
+ bug_tracker_uri: https://github.com/mackuba/minestrone/issues
211
+ changelog_uri: https://github.com/mackuba/minestrone/blob/master/CHANGELOG.md
212
+ source_code_uri: https://github.com/mackuba/minestrone
213
+ rubygems_mfa_required: 'true'
204
214
  rdoc_options: []
205
215
  require_paths:
206
216
  - lib
@@ -208,51 +218,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
218
  requirements:
209
219
  - - ">="
210
220
  - !ruby/object:Gem::Version
211
- version: 3.0.0
221
+ version: 3.3.0
212
222
  required_rubygems_version: !ruby/object:Gem::Requirement
213
223
  requirements:
214
224
  - - ">="
215
225
  - !ruby/object:Gem::Version
216
226
  version: '0'
217
227
  requirements: []
218
- rubygems_version: 4.0.10
228
+ rubygems_version: 4.0.23
219
229
  specification_version: 4
220
- summary: Minestrone - Welcome to easy deployment with Ruby over SSH
221
- test_files:
222
- - test/cli/execute_test.rb
223
- - test/cli/help_test.rb
224
- - test/cli/options_test.rb
225
- - test/cli/ui_test.rb
226
- - test/cli_test.rb
227
- - test/command_test.rb
228
- - test/configuration/actions/file_transfer_test.rb
229
- - test/configuration/actions/inspect_test.rb
230
- - test/configuration/actions/invocation_test.rb
231
- - test/configuration/alias_task_test.rb
232
- - test/configuration/callbacks_test.rb
233
- - test/configuration/connections_test.rb
234
- - test/configuration/execution_test.rb
235
- - test/configuration/loading_test.rb
236
- - test/configuration/namespace_dsl_test.rb
237
- - test/configuration/servers_test.rb
238
- - test/configuration/variables_test.rb
239
- - test/configuration_test.rb
240
- - test/deploy/local_dependency_test.rb
241
- - test/deploy/remote_dependency_test.rb
242
- - test/deploy/scm/base_test.rb
243
- - test/deploy/scm/git_test.rb
244
- - test/deploy/scm/none_test.rb
245
- - test/deploy/strategy/copy_test.rb
246
- - test/extensions_test.rb
247
- - test/fixtures/cli_integration.rb
248
- - test/fixtures/config.rb
249
- - test/fixtures/custom.rb
250
- - test/logger_formatting_test.rb
251
- - test/logger_test.rb
252
- - test/recipes_test.rb
253
- - test/server_definition_test.rb
254
- - test/ssh_test.rb
255
- - test/task_definition_test.rb
256
- - test/transfer_test.rb
257
- - test/utils.rb
258
- - test/version_test.rb
230
+ summary: Simple deployment tool for Ruby apps (based on Capistrano)
231
+ test_files: []
@@ -1,53 +0,0 @@
1
-
2
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
-
5
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
-
7
- <head>
8
- <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
9
- <title>System down for maintenance</title>
10
-
11
- <style type="text/css">
12
- div.outer {
13
- position: absolute;
14
- left: 50%;
15
- top: 50%;
16
- width: 500px;
17
- height: 300px;
18
- margin-left: -260px;
19
- margin-top: -150px;
20
- }
21
-
22
- .DialogBody {
23
- margin: 0;
24
- padding: 10px;
25
- text-align: left;
26
- border: 1px solid #ccc;
27
- border-right: 1px solid #999;
28
- border-bottom: 1px solid #999;
29
- background-color: #fff;
30
- }
31
-
32
- body { background-color: #fff; }
33
- </style>
34
- </head>
35
-
36
- <body>
37
-
38
- <div class="outer">
39
- <div class="DialogBody" style="text-align: center;">
40
- <div style="text-align: center; width: 200px; margin: 0 auto;">
41
- <p style="color: red; font-size: 16px; line-height: 20px;">
42
- The system is down for <%= reason ? reason : "maintenance" %>
43
- as of <%= Time.now.strftime("%H:%M %Z") %>.
44
- </p>
45
- <p style="color: #666;">
46
- It'll be back <%= deadline ? deadline : "shortly" %>.
47
- </p>
48
- </div>
49
- </div>
50
- </div>
51
-
52
- </body>
53
- </html>