capistrano-sidekiq 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41a4b345da5a4f9019cca07d226270be72a796a743b03dc9cfeb0cf13a7c0dc3
4
- data.tar.gz: bea4261d462c586ededff2ef99bf0311a463810a334cf73fc128f82041ed3a03
3
+ metadata.gz: 61bb07339f25aae4a39fa10cb0dad5b696b13d89bb29e2c626d4d930cab246dc
4
+ data.tar.gz: fca70af00d2a413d6bd84d7ab9f40ff678000f32f6699b5bbb221704ab72eccc
5
5
  SHA512:
6
- metadata.gz: 0b8e9df1839123c5cef2a3f15fbc7512bfc1d328f9c79ae141f9f48ff87d0d95e60a3a310dc14b47bc9ef4a01de18e73c14000706fd92c09a7d995380c421760
7
- data.tar.gz: d31d7765c396d32d987b6f70b5de3e2bf5eeb3a43e7528187bb4c1d862b59235772a171353e9611d4d7bc151e7c8a24fecc5855fa459c1e48187b67a91c084e6
6
+ metadata.gz: 3e5ebaa925990b9e6896328a3d78e0a187c3f794870ef5ed47fb0828f7a709236817020918aee2cd0cb7601e7b3bc739b36060a686abebd0988ed81f611d7a1e
7
+ data.tar.gz: 9325d11d20b43003c085dd645e66d9028b30d87ed8c8422bdd01996afdd7fb6b35cc9e34ecf794f410c10d6499e426620f44a1ec1bd3f34ec53e641fcffb082f
data/CHANGELOG.md CHANGED
@@ -1,11 +1,43 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased](https://github.com/seuros/capistrano-sidekiq/compare/v2.3.0...master)
3
+ ## [Unreleased](https://github.com/seuros/capistrano-sidekiq/compare/v3.1.0...master)
4
+
5
+ ## [3.1.0](https://github.com/seuros/capistrano-sidekiq/compare/v3.0.0...v3.1.0) - 2025-06-22
6
+
7
+ ### Fixed
8
+ - Fix undefined method 'as' error by properly calling backend.as (#327)
9
+ - Fix inconsistent systemd variable naming (systemctl_user vs sidekiq_systemctl_user)
10
+ - Fix systemd lingering configuration issues (#311)
11
+ - Fix sidekiq:uninstall to work without sudo for user services (#307)
12
+ - Align configuration pattern with capistrano-puma for consistency
13
+
14
+ ### Added
15
+ - Add sidekiqswarm support for Sidekiq Enterprise
16
+ - Add deployment tracking for Sidekiq 7+ metrics (`sidekiq_mark_deploy`)
17
+ - Add login shell option for systemd (`sidekiq_use_login_shell`)
18
+ - Add configurable sidekiq command (`sidekiq_command`, `sidekiq_command_args`)
19
+ - Add helper tasks for generating multiple config files
20
+ - Add comprehensive documentation for per-server configuration (#312)
21
+ - Add detailed systemd integration guide (#240)
22
+ - Add GitHub Actions CI workflow with Ruby 3.2+ support
23
+ - Add COSS specification compliance
24
+ - Add shared configuration support with other Capistrano service gems
25
+
26
+ ### Changed
27
+ - Update minimum Ruby version to 3.2+ (required by Sidekiq 7)
28
+ - Use consistent sidekiq-prefixed variables for systemd configuration
29
+ - Improve README documentation with shared configuration examples
30
+ - Update CI to test with Ruby 3.2 and 3.3
31
+ - Enhance systemd service template with configurable features
32
+
33
+ ## [3.0.0](https://github.com/seuros/capistrano-sidekiq/compare/v2.3.0...v3.0.0)
4
34
  - Rewrite to match capistrano3-puma api
5
35
  - Add support for multiple sidekiq configs (processes and queues can be configured with erb)
6
36
  - Add support to multiple EnvironmentFile
7
37
  - Add support to multiple EnvironmentVariables
8
38
  - Default role for sidekiq is now :worker instead of :app
39
+ - Remove monit support
40
+ - Remove deprecated --index argument for Sidekiq 6.0+ compatibility (#234)
9
41
 
10
42
  ## [2.3.0](https://github.com/seuros/capistrano-sidekiq/tree/2.3.0) (2022-05-17)
11
43
 
data/README.md CHANGED
@@ -1,54 +1,375 @@
1
- [![Gem Version](https://badge.fury.io/rb/capistrano-sidekiq.svg)](http://badge.fury.io/rb/capistrano-sidekiq)
2
-
3
1
  # Capistrano::Sidekiq
4
2
 
5
- Sidekiq integration for Capistrano
3
+ [![Gem Version](https://badge.fury.io/rb/capistrano-sidekiq.svg)](http://badge.fury.io/rb/capistrano-sidekiq)
4
+ [![COSS Compliant](https://img.shields.io/badge/COSS-compliant-green.svg)](https://github.com/contriboss/coss_spec)
5
+
6
+ Sidekiq integration for Capistrano - providing systemd service management and deployment coordination for Sidekiq 7+.
6
7
 
7
8
  ## Installation
8
9
 
9
- gem 'capistrano-sidekiq', group: :development
10
+ Add to your Gemfile:
11
+
12
+ ```ruby
13
+ gem 'capistrano-sidekiq', group: :development
14
+ ```
15
+
16
+ Then execute:
17
+
18
+ ```bash
19
+ $ bundle install
20
+ ```
21
+
22
+ ## Setup
23
+
24
+ Add to your Capfile:
25
+
26
+ ```ruby
27
+ # Capfile
28
+ require 'capistrano/sidekiq'
29
+ install_plugin Capistrano::Sidekiq # Default sidekiq tasks
30
+ install_plugin Capistrano::Sidekiq::Systemd # Systemd integration
31
+ ```
32
+
33
+ ## Configuration Options
34
+
35
+ ### Basic Settings
36
+
37
+ ```ruby
38
+ # config/deploy.rb
39
+ set :sidekiq_roles, :worker # Default role for Sidekiq processes
40
+ set :sidekiq_default_hooks, true # Enable default deployment hooks
41
+ set :sidekiq_env, fetch(:rack_env, fetch(:rails_env, fetch(:stage))) # Environment for Sidekiq processes
42
+
43
+ # Single config file
44
+ set :sidekiq_config_files, ['sidekiq.yml']
45
+
46
+ # Multiple config files for different Sidekiq processes
47
+ set :sidekiq_config_files, ['sidekiq.yml', 'sidekiq-high-priority.yml']
48
+ ```
49
+
50
+ ### Shared Configuration with Other Gems
51
+
52
+ This gem follows the Capistrano convention of sharing common settings across multiple service gems (e.g., capistrano-puma, capistrano-sidekiq). The following settings are shared:
53
+
54
+ - `:service_unit_user` - Determines if services run as system or user services
55
+ - `:systemctl_bin` - Path to the systemctl binary
56
+ - `:lingering_user` - User for systemd lingering (for user services)
57
+ - `:service_unit_env_files` - Shared environment files
58
+ - `:service_unit_env_vars` - Shared environment variables
59
+
60
+ Each gem can override these with prefixed versions (e.g., `:sidekiq_systemctl_user`).
61
+
62
+ ### Advanced Configuration
63
+
64
+ ```ruby
65
+ # Shared systemd settings (can be used by multiple Capistrano gems like capistrano-puma)
66
+ set :service_unit_user, :user # Run as user or system service (:system or :user)
67
+ set :systemctl_bin, '/bin/systemctl' # Path to systemctl binary
68
+ set :lingering_user, 'deploy' # User to enable lingering for (defaults to :user)
69
+
70
+ # Sidekiq-specific overrides (optional - defaults to shared settings above)
71
+ set :sidekiq_systemctl_user, :system # Override service_unit_user for Sidekiq only
72
+ set :sidekiq_systemctl_bin, '/usr/bin/systemctl' # Override systemctl_bin for Sidekiq only
73
+ set :sidekiq_service_unit_name, "custom_sidekiq_#{fetch(:stage)}" # Custom service name
74
+ set :sidekiq_lingering_user, 'sidekiq' # Override lingering user for Sidekiq only
75
+
76
+ # Environment configuration
77
+ set :sidekiq_service_unit_env_files, ['/etc/environment'] # Environment files
78
+ set :sidekiq_service_unit_env_vars, [ # Environment variables
79
+ 'RAILS_ENV=production',
80
+ 'MALLOC_ARENA_MAX=2'
81
+ ]
82
+
83
+ # Logging configuration
84
+ set :sidekiq_log, -> { File.join(shared_path, 'log', 'sidekiq.log') }
85
+ set :sidekiq_error_log, -> { File.join(shared_path, 'log', 'sidekiq.error.log') }
86
+
87
+ # Command customization
88
+ set :sidekiq_command, 'sidekiq' # Use 'sidekiq' or 'sidekiqswarm' for Enterprise
89
+ set :sidekiq_command_args, -> { "-e #{fetch(:sidekiq_env)}" }
90
+
91
+ # Use login shell to load environment (for rbenv, rvm, etc.)
92
+ set :sidekiq_use_login_shell, true
93
+
94
+ # Sidekiq Enterprise - Swarm configuration
95
+ set :sidekiq_command, 'sidekiqswarm'
96
+ set :sidekiq_swarm_env_vars, [
97
+ 'SIDEKIQ_COUNT=5', # Number of processes
98
+ 'SIDEKIQ_MAXMEM_MB=768', # Memory limit per process
99
+ 'SIDEKIQ_PRELOAD_APP=1' # Preload app for memory efficiency
100
+ ]
101
+ # Merge swarm env vars with regular env vars
102
+ set :sidekiq_service_unit_env_vars, -> {
103
+ fetch(:sidekiq_service_unit_env_vars, []) + fetch(:sidekiq_swarm_env_vars, [])
104
+ }
105
+ ```
106
+
107
+ ### Per-Server Configuration
108
+
109
+ You can configure Sidekiq differently for specific servers, allowing you to run different Sidekiq processes with different configurations on different servers.
110
+
111
+ #### Basic Per-Server Setup
112
+
113
+ ```ruby
114
+ # config/deploy/production.rb
115
+ server 'worker1.example.com',
116
+ roles: [:worker],
117
+ sidekiq_config_files: ['sidekiq_high_priority.yml']
118
+
119
+ server 'worker2.example.com',
120
+ roles: [:worker],
121
+ sidekiq_config_files: ['sidekiq_low_priority.yml']
122
+ ```
123
+
124
+ #### Advanced Per-Server Configuration
125
+
126
+ ```ruby
127
+ # Different users and multiple processes per server
128
+ server 'worker1.example.com',
129
+ roles: [:worker],
130
+ sidekiq_config_files: ['sidekiq_critical.yml', 'sidekiq_default.yml'],
131
+ sidekiq_user: 'sidekiq_critical',
132
+ sidekiq_systemctl_user: :system # Run as system service on this server
133
+
134
+ server 'worker2.example.com',
135
+ roles: [:worker],
136
+ sidekiq_config_files: ['sidekiq_batch.yml'],
137
+ sidekiq_user: 'sidekiq_batch',
138
+ sidekiq_service_unit_env_vars: ['MALLOC_ARENA_MAX=4'] # Server-specific env vars
139
+ ```
140
+
141
+ #### How It Works
142
+
143
+ 1. **Configuration Files**: Each server can have its own set of `sidekiq_config_files`
144
+ 2. **Service Creation**: A separate systemd service is created for each config file
145
+ 3. **Service Naming**: Services are named as `<app>_sidekiq_<stage>` for the default `sidekiq.yml`, or `<app>_sidekiq_<stage>.<config_name>` for additional configs
146
+ 4. **Independent Control**: Each service can be started, stopped, and restarted independently
147
+
148
+ #### Example Configurations
149
+
150
+ **config/sidekiq_high_priority.yml:**
151
+ ```yaml
152
+ :concurrency: 10
153
+ :queues:
154
+ - [critical, 2]
155
+ - [high, 1]
156
+ ```
157
+
158
+ **config/sidekiq_low_priority.yml:**
159
+ ```yaml
160
+ :concurrency: 5
161
+ :queues:
162
+ - [low, 1]
163
+ - [default, 1]
164
+ ```
165
+
166
+ #### Deployment Commands
167
+
168
+ When using per-server configurations, Capistrano will:
169
+ - Install the appropriate services on each server during `cap production sidekiq:install`
170
+ - Start only the configured services on each server during `cap production sidekiq:start`
171
+ - Manage each server's services independently
172
+
173
+ You can also target specific servers:
174
+ ```bash
175
+ cap production sidekiq:restart --hosts=worker1.example.com
176
+ ```
177
+
178
+ ## Multiple Processes
179
+
180
+ There are two ways to run multiple Sidekiq processes:
181
+
182
+ ### Option 1: Multiple Config Files (Recommended)
183
+
184
+ Create separate config files for different workloads:
185
+
186
+ ```yaml
187
+ # config/sidekiq_critical.yml
188
+ :concurrency: 5
189
+ :queues:
190
+ - [critical, 2]
191
+
192
+ # config/sidekiq_default.yml
193
+ :concurrency: 10
194
+ :queues:
195
+ - [default, 1]
196
+ - [low, 1]
197
+ ```
198
+
199
+ Configure in deploy.rb:
200
+ ```ruby
201
+ set :sidekiq_config_files, ['sidekiq_critical.yml', 'sidekiq_default.yml']
202
+ ```
203
+
204
+ This creates separate services:
205
+ - `myapp_sidekiq_production.sidekiq_critical`
206
+ - `myapp_sidekiq_production.sidekiq_default`
207
+
208
+ Benefits:
209
+ - Clear purpose for each process
210
+ - Independent scaling and management
211
+ - Different configurations per process
212
+ - Meaningful service names
213
+
214
+ ### Option 2: Sidekiq Enterprise (Sidekiqswarm)
215
+
216
+ For simple scaling with identical processes:
217
+
218
+ ```ruby
219
+ set :sidekiq_command, 'sidekiqswarm'
220
+ set :sidekiq_service_unit_env_vars, ['SIDEKIQ_COUNT=10']
221
+ ```
222
+
223
+ This runs 10 identical processes with a single service.
224
+
225
+ ### Quick Setup for Multiple Processes
226
+
227
+ Use the helper task to generate config files:
228
+
229
+ ```bash
230
+ # Generate 5 config files
231
+ bundle exec cap production sidekiq:helpers:generate_configs[5]
232
+
233
+ # This creates:
234
+ # - config/sidekiq.yml (critical, high queues)
235
+ # - config/sidekiq_1.yml (default, medium queues)
236
+ # - config/sidekiq_2.yml (low, background queues)
237
+ # - config/sidekiq_3.yml (low, background queues)
238
+ # - config/sidekiq_4.yml (low, background queues)
239
+ ```
240
+
241
+ Then add to your deploy.rb:
242
+ ```ruby
243
+ set :sidekiq_config_files, ['sidekiq.yml', 'sidekiq_1.yml', 'sidekiq_2.yml', 'sidekiq_3.yml', 'sidekiq_4.yml']
244
+ ```
245
+
246
+ ## Available Tasks
247
+
248
+ ```bash
249
+ # View all available tasks
250
+ cap -T sidekiq
10
251
 
11
- And then execute:
252
+ # Common commands
253
+ cap sidekiq:start # Start Sidekiq
254
+ cap sidekiq:stop # Stop Sidekiq (graceful shutdown)
255
+ cap sidekiq:restart # Restart Sidekiq
256
+ cap sidekiq:quiet # Quiet Sidekiq (stop processing new jobs)
257
+ cap sidekiq:install # Install Sidekiq systemd service
258
+ cap sidekiq:uninstall # Remove Sidekiq systemd service
259
+ cap sidekiq:enable # Enable Sidekiq systemd service
260
+ cap sidekiq:disable # Disable Sidekiq systemd service
261
+ cap sidekiq:mark_deploy # Mark deployment in Sidekiq metrics (Sidekiq 7+)
12
262
 
13
- $ bundle
263
+ # Helper tasks
264
+ cap sidekiq:helpers:generate_configs[3] # Generate 3 config files
265
+ cap sidekiq:helpers:show_config # Show current configuration
266
+ ```
267
+
268
+ ## Systemd Integration
269
+
270
+ For detailed information about systemd integration, see [Systemd Integration Guide](docs/SYSTEMD_INTEGRATION.md).
271
+
272
+ ## Deployment Tracking (Sidekiq 7+)
14
273
 
274
+ Sidekiq 7 introduced metrics with deployment tracking. Enable it to see deployment markers in your Sidekiq metrics graphs:
275
+
276
+ ### Configuration
15
277
 
16
- ## Usage
17
278
  ```ruby
18
- # Capfile
19
- require 'capistrano/sidekiq'
20
- install_plugin Capistrano::Sidekiq # Default sidekiq tasks
21
- # Then select your service manager
22
- install_plugin Capistrano::Sidekiq::Systemd
279
+ # config/deploy.rb
280
+
281
+ # Enable deployment marking
282
+ set :sidekiq_mark_deploy, true
283
+
284
+ # Optional: Custom deploy label (defaults to git commit description)
285
+ set :sidekiq_deploy_label, "v#{fetch(:current_revision)[0..6]} deployed to #{fetch(:stage)}"
23
286
  ```
24
287
 
25
- Configurable options - Please ensure you check your version's branch for the available settings - shown here with defaults:
288
+ ### How It Works
289
+
290
+ When enabled, the gem will:
291
+ 1. Automatically mark deployments after successful deploys
292
+ 2. Show vertical red lines in Sidekiq's metrics graphs
293
+ 3. Display the deploy label when hovering over the marker
294
+
295
+ This helps correlate performance changes with deployments.
296
+
297
+ ## Sidekiq Enterprise (Swarm) Support
298
+
299
+ This gem supports Sidekiq Enterprise's `sidekiqswarm` command for managing multiple processes with a single service.
300
+
301
+ ### Configuration
26
302
 
27
303
  ```ruby
28
- :sidekiq_roles => :worker
29
- :sidekiq_default_hooks => true
30
- :sidekiq_env => fetch(:rack_env, fetch(:rails_env, fetch(:stage)))
31
- # single config
32
- :sidekiq_config_files, ['sidekiq.yml']
33
- # multiple configs
34
- :sidekiq_config_files, ['sidekiq.yml', 'sidekiq-2.yml'] # you can also set it per server
304
+ # config/deploy.rb
305
+
306
+ # Use sidekiqswarm instead of sidekiq
307
+ set :sidekiq_command, 'sidekiqswarm'
308
+
309
+ # Configure swarm-specific environment variables
310
+ set :sidekiq_service_unit_env_vars, [
311
+ 'SIDEKIQ_COUNT=10', # Number of processes to spawn
312
+ 'SIDEKIQ_MAXMEM_MB=512', # Memory limit per process
313
+ 'SIDEKIQ_PRELOAD_APP=1', # Preload Rails app for memory efficiency
314
+ 'RAILS_ENV=production'
315
+ ]
316
+
317
+ # Optional: Configure shutdown timeout for graceful stops
318
+ set :sidekiq_command_args, -> { "-e #{fetch(:sidekiq_env)} -t 30" }
35
319
  ```
36
320
 
37
- ## Example
321
+ ### Benefits of Sidekiqswarm
322
+
323
+ - **Single Service**: Manage N processes with one systemd service
324
+ - **Memory Efficiency**: Preload app once, fork processes
325
+ - **Auto-restart**: Processes that exceed memory limits are automatically restarted
326
+ - **Simplified Management**: No need for multiple service files
327
+
328
+ ### Per-Server Swarm Configuration
329
+
330
+ ```ruby
331
+ server 'worker1.example.com',
332
+ roles: [:worker],
333
+ sidekiq_service_unit_env_vars: ['SIDEKIQ_COUNT=20', 'SIDEKIQ_MAXMEM_MB=1024']
334
+
335
+ server 'worker2.example.com',
336
+ roles: [:worker],
337
+ sidekiq_service_unit_env_vars: ['SIDEKIQ_COUNT=5', 'SIDEKIQ_MAXMEM_MB=512']
338
+ ```
339
+
340
+ ## Working with Systemd Logs
341
+
342
+ View Sidekiq service logs using journalctl:
343
+
344
+ ```bash
345
+ # View last 100 lines of logs
346
+ journalctl -u sidekiq_myapp_production -n 100
347
+
348
+ # Follow logs in real-time
349
+ journalctl -u sidekiq_myapp_production -f
350
+ ```
351
+
352
+ ## Log File Configuration
353
+
354
+ ### Modern Systemd (v240+, e.g., Ubuntu 20.04+)
355
+
356
+ Log files are configured automatically using the `append:` functionality in the systemd service file.
38
357
 
39
- A sample application is provided to show how to use this gem at https://github.com/seuros/capistrano-example-app
358
+ ### Legacy Systemd Systems
40
359
 
41
- ## Configuring the log files on systems with less recent Systemd versions
360
+ For systems with older Systemd versions where `append:` is not supported:
42
361
 
43
- The template used by this project assumes a recent version of Systemd (v240+, e.g. Ubuntu 20.04).
362
+ 1. Sidekiq messages are sent to syslog by default
363
+ 2. Configure system logger to filter Sidekiq messages
44
364
 
45
- On systems with a less recent version, the `append:` functionality is not supported, and the Sidekiq log messages are sent to the syslog.
365
+ ## Example Application
46
366
 
47
- It's possible to workaround this limitation by configuring the system logger to filter the Sidekiq messages; see [wiki](/../../wiki/Configuring-append-mode-log-files-via-Syslog-NG).
367
+ A complete example application demonstrating the usage of this gem is available at:
368
+ https://github.com/seuros/capistrano-example-app
48
369
 
49
370
  ## Contributing
50
371
 
51
- 1. Fork it
372
+ 1. Fork the repository
52
373
  2. Create your feature branch (`git checkout -b my-new-feature`)
53
374
  3. Commit your changes (`git commit -am 'Add some feature'`)
54
375
  4. Push to the branch (`git push origin my-new-feature`)
@@ -1,46 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capistrano
4
- class Sidekiq::Systemd < Capistrano::Plugin
5
- include SidekiqCommon
6
- def define_tasks
7
- eval_rakefile File.expand_path('../tasks/systemd.rake', __dir__)
8
- end
9
- def set_defaults
10
- set_if_empty :systemctl_bin, '/bin/systemctl'
11
- set_if_empty :service_unit_user, :user
12
- set_if_empty :systemctl_user, fetch(:service_unit_user, :user) == :user
4
+ class Sidekiq
5
+ class Systemd < Capistrano::Plugin
6
+ include SidekiqCommon
7
+ def define_tasks
8
+ eval_rakefile File.expand_path('../tasks/systemd.rake', __dir__)
9
+ eval_rakefile File.expand_path('../tasks/helpers.rake', __dir__)
10
+ end
13
11
 
14
- set_if_empty :sidekiq_service_unit_name, -> { "#{fetch(:application)}_sidekiq_#{fetch(:stage)}" }
15
- set_if_empty :sidekiq_lingering_user, -> { fetch(:lingering_user, fetch(:user)) }
12
+ def set_defaults
13
+ set_if_empty :systemctl_bin, '/bin/systemctl'
14
+ set_if_empty :service_unit_user, :user
15
+ set_if_empty :systemctl_user, -> { fetch(:service_unit_user, :user) == :user }
16
16
 
17
- ## Sidekiq could have a stripped down or more complex version of the environment variables
18
- set_if_empty :sidekiq_service_unit_env_files, -> { fetch(:service_unit_env_files, []) }
19
- set_if_empty :sidekiq_service_unit_env_vars, -> { fetch(:service_unit_env_vars, []) }
17
+ set_if_empty :sidekiq_systemctl_bin, -> { fetch(:systemctl_bin) }
18
+ set_if_empty :sidekiq_service_unit_name, -> { "#{fetch(:application)}_sidekiq_#{fetch(:stage)}" }
20
19
 
21
- set_if_empty :sidekiq_service_templates_path, fetch(:service_templates_path, 'config/deploy/templates')
22
- end
20
+ set_if_empty :sidekiq_systemctl_user, -> { fetch(:service_unit_user) }
21
+ set_if_empty :sidekiq_enable_lingering, -> { fetch(:sidekiq_systemctl_user) != :system }
22
+ set_if_empty :sidekiq_lingering_user, -> { fetch(:lingering_user, fetch(:user)) }
23
+
24
+ ## Sidekiq could have a stripped down or more complex version of the environment variables
25
+ set_if_empty :sidekiq_service_unit_env_files, -> { fetch(:service_unit_env_files, []) }
26
+ set_if_empty :sidekiq_service_unit_env_vars, -> { fetch(:service_unit_env_vars, []) }
23
27
 
24
- def systemd_command(*args)
25
- command = [fetch(:systemctl_bin)]
28
+ set_if_empty :sidekiq_service_templates_path, fetch(:service_templates_path, 'config/deploy/templates')
26
29
 
27
- unless fetch(:service_unit_user) == :system
28
- command << "--user"
30
+ # Allow customization of the sidekiq command
31
+ set_if_empty :sidekiq_command, 'sidekiq'
32
+ set_if_empty :sidekiq_command_args, -> { "-e #{fetch(:sidekiq_env)}" }
33
+
34
+ # Deployment tracking for Sidekiq 7+ metrics
35
+ set_if_empty :sidekiq_mark_deploy, false
36
+ set_if_empty :sidekiq_deploy_label, nil
37
+
38
+ # Login shell option for loading environment
39
+ set_if_empty :sidekiq_use_login_shell, false
29
40
  end
30
41
 
31
- command + args
32
- end
42
+ def fetch_systemd_unit_path
43
+ if fetch(:sidekiq_systemctl_user) == :system
44
+ '/etc/systemd/system/'
45
+ else
46
+ home_dir = backend.capture :pwd
47
+ File.join(home_dir, '.config', 'systemd', 'user')
48
+ end
49
+ end
50
+
51
+ def systemd_command(*args)
52
+ command = [fetch(:sidekiq_systemctl_bin)]
33
53
 
34
- def sudo_if_needed(*command)
35
- if fetch(:service_unit_user) == :system
36
- backend.sudo command.map(&:to_s).join(" ")
37
- else
38
- backend.execute(*command)
54
+ command << '--user' unless fetch(:sidekiq_systemctl_user) == :system
55
+
56
+ command + args
39
57
  end
40
- end
41
58
 
42
- def execute_systemd(*args)
43
- sudo_if_needed(*systemd_command(*args))
59
+ def sudo_if_needed(*command)
60
+ if fetch(:sidekiq_systemctl_user) == :system
61
+ backend.sudo command.map(&:to_s).join(' ')
62
+ else
63
+ backend.execute(*command)
64
+ end
65
+ end
66
+
67
+ def execute_systemd(*)
68
+ sudo_if_needed(*systemd_command(*))
69
+ end
44
70
  end
45
71
  end
46
72
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capistrano
4
- SidekiqVERSION = '3.0.0'
4
+ SIDEKIQ_VERSION = '3.1.0'
5
5
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'capistrano/bundler'
3
+ require 'capistrano/bundler' unless ENV['TEST']
4
4
  require 'capistrano/plugin'
5
5
 
6
6
  module Capistrano
7
7
  module SidekiqCommon
8
- def compiled_template(config_file = "sidekiq.yml")
8
+ def compiled_template(config_file = 'sidekiq.yml')
9
9
  @config_file = config_file
10
10
  local_template_directory = fetch(:sidekiq_service_templates_path)
11
11
  search_paths = [
@@ -25,15 +25,15 @@ module Capistrano
25
25
  end
26
26
 
27
27
  def sidekiq_config
28
- "--config config/#{@config_file}" if @config_file != "sidekiq.yml"
28
+ "--config config/#{@config_file}" if @config_file != 'sidekiq.yml'
29
29
  end
30
30
 
31
- def switch_user(role, &block)
31
+ def switch_user(role, &)
32
32
  su_user = sidekiq_user(role)
33
33
  if su_user == role.user
34
34
  yield
35
35
  else
36
- as su_user, &block
36
+ backend.as(su_user, &)
37
37
  end
38
38
  end
39
39
 
@@ -42,13 +42,14 @@ module Capistrano
42
42
  fetch(:sidekiq_user)
43
43
  else
44
44
  properties = role.properties
45
- properties.fetch(:sidekiq_user) || # local property for sidekiq only
46
- fetch(:sidekiq_user) ||
47
- properties.fetch(:run_as) || # global property across multiple capistrano gems
45
+ properties.fetch(:sidekiq_user, nil) || # local property for sidekiq only
46
+ fetch(:sidekiq_user, nil) ||
47
+ properties.fetch(:run_as, nil) || # global property across multiple capistrano gems
48
48
  role.user
49
49
  end
50
50
  end
51
51
  end
52
+
52
53
  class Sidekiq < Capistrano::Plugin
53
54
  def define_tasks
54
55
  eval_rakefile File.expand_path('tasks/sidekiq.rake', __dir__)
@@ -59,7 +60,7 @@ module Capistrano
59
60
 
60
61
  set_if_empty :sidekiq_env, -> { fetch(:rack_env, fetch(:rails_env, fetch(:rake_env, fetch(:stage)))) }
61
62
  set_if_empty :sidekiq_roles, fetch(:sidekiq_role, :worker)
62
- set_if_empty :sidekiq_configs, %w[sidekiq] # sidekiq.yml
63
+ set_if_empty :sidekiq_configs, %w[sidekiq] # sidekiq.yml
63
64
 
64
65
  set_if_empty :sidekiq_log, -> { File.join(shared_path, 'log', 'sidekiq.log') }
65
66
  set_if_empty :sidekiq_error_log, -> { File.join(shared_path, 'log', 'sidekiq.log') }
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :sidekiq do
4
+ namespace :helpers do
5
+ desc 'Generate multiple Sidekiq config files'
6
+ task :generate_configs, :count do |_task, args|
7
+ count = (args[:count] || 3).to_i
8
+
9
+ puts "Generating #{count} Sidekiq config files..."
10
+
11
+ # Base template
12
+ base_config = {
13
+ concurrency: 10,
14
+ timeout: 25,
15
+ verbose: false,
16
+ strict: true
17
+ }
18
+
19
+ # Generate config files
20
+ count.times do |i|
21
+ config = base_config.dup
22
+
23
+ # Assign queues based on index
24
+ config[:queues] = case i
25
+ when 0
26
+ [['critical', 2], ['high', 1]]
27
+ when 1
28
+ [['default', 1], ['medium', 1]]
29
+ else
30
+ [['low', 1], ['background', 1]]
31
+ end
32
+
33
+ filename = i.zero? ? 'sidekiq.yml' : "sidekiq_#{i}.yml"
34
+ filepath = "config/#{filename}"
35
+
36
+ # Generate YAML content
37
+ content = <<~YAML
38
+ # Sidekiq configuration file #{i + 1}/#{count}
39
+ :concurrency: #{config[:concurrency]}
40
+ :timeout: #{config[:timeout]}
41
+ :verbose: #{config[:verbose]}
42
+ :strict: #{config[:strict]}
43
+
44
+ :queues:
45
+ YAML
46
+
47
+ config[:queues].each do |queue, priority|
48
+ content += " - [#{queue}, #{priority}]\n"
49
+ end
50
+
51
+ puts " Creating #{filepath}..."
52
+ File.write(filepath, content)
53
+ end
54
+
55
+ puts "\nAdd to your deploy.rb:"
56
+ puts "set :sidekiq_config_files, #{count.times.map do |i|
57
+ i.zero? ? "'sidekiq.yml'" : "'sidekiq_#{i}.yml'"
58
+ end.join(', ')}"
59
+ end
60
+
61
+ desc 'Show current Sidekiq configuration'
62
+ task :show_config do
63
+ on roles(fetch(:sidekiq_roles)) do
64
+ within current_path do
65
+ config_files = fetch(:sidekiq_config_files, ['sidekiq.yml'])
66
+
67
+ config_files.each do |config_file|
68
+ puts "\n=== #{config_file} ==="
69
+ if test("[ -f config/#{config_file} ]")
70
+ puts capture(:cat, "config/#{config_file}")
71
+ else
72
+ puts ' File not found'
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :deploy do
2
4
  before :starting, :check_sidekiq_hooks do
3
5
  invoke 'sidekiq:add_default_hooks' if fetch(:sidekiq_default_hooks)
@@ -8,7 +10,36 @@ namespace :sidekiq do
8
10
  task :add_default_hooks do
9
11
  after 'deploy:starting', 'sidekiq:quiet' if Rake::Task.task_defined?('sidekiq:quiet')
10
12
  after 'deploy:updated', 'sidekiq:stop'
13
+ after 'deploy:reverted', 'sidekiq:stop'
11
14
  after 'deploy:published', 'sidekiq:start'
15
+ after 'deploy:published', 'sidekiq:mark_deploy' if fetch(:sidekiq_mark_deploy, false)
12
16
  after 'deploy:failed', 'sidekiq:restart'
13
17
  end
18
+
19
+ desc 'Mark deployment in Sidekiq metrics'
20
+ task :mark_deploy do
21
+ if fetch(:sidekiq_mark_deploy, false)
22
+ on roles(fetch(:sidekiq_roles)) do
23
+ within current_path do
24
+ # Get deploy label - use custom label or git description
25
+ deploy_label = fetch(:sidekiq_deploy_label) || begin
26
+ capture(:git, 'log', '-1', '--format="%h %s"').strip
27
+ rescue StandardError
28
+ "#{fetch(:application)} #{fetch(:stage)} deploy"
29
+ end
30
+
31
+ info "Marking deployment in Sidekiq metrics: #{deploy_label}"
32
+
33
+ # Create a Ruby script to mark the deployment
34
+ mark_deploy_script = <<~RUBY
35
+ require 'sidekiq/deploy'
36
+ Sidekiq::Deploy.mark!(ARGV[0])
37
+ RUBY
38
+
39
+ # Execute the script with the deploy label
40
+ execute :bundle, :exec, :ruby, '-e', mark_deploy_script, '--', deploy_label
41
+ end
42
+ end
43
+ end
44
+ end
14
45
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  git_plugin = self
@@ -56,11 +55,11 @@ namespace :sidekiq do
56
55
  task :enable do
57
56
  on roles(fetch(:sidekiq_roles)) do |role|
58
57
  git_plugin.config_files(role).each do |config_file|
59
- git_plugin.execute_systemd("enable", git_plugin.sidekiq_service_file_name(config_file))
58
+ git_plugin.execute_systemd('enable', git_plugin.sidekiq_service_file_name(config_file))
60
59
  end
61
60
 
62
- if fetch(:systemctl_user) && fetch(:sidekiq_lingering_user)
63
- execute :loginctl, "enable-linger", fetch(:sidekiq_lingering_user)
61
+ if fetch(:sidekiq_enable_lingering) && fetch(:sidekiq_lingering_user)
62
+ execute :loginctl, 'enable-linger', fetch(:sidekiq_lingering_user)
64
63
  end
65
64
  end
66
65
  end
@@ -69,36 +68,36 @@ namespace :sidekiq do
69
68
  task :disable do
70
69
  on roles(fetch(:sidekiq_roles)) do |role|
71
70
  git_plugin.config_files(role).each do |config_file|
72
- git_plugin.execute_systemd("disable", git_plugin.sidekiq_service_file_name(config_file))
71
+ git_plugin.execute_systemd('disable', git_plugin.sidekiq_service_file_name(config_file))
73
72
  end
74
73
  end
75
74
  end
76
75
 
77
76
  def fetch_systemd_unit_path
78
77
  if fetch(:sidekiq_systemctl_user) == :system
79
- "/etc/systemd/system/"
78
+ '/etc/systemd/system/'
80
79
  else
81
80
  home_dir = backend.capture :pwd
82
- File.join(home_dir, ".config", "systemd", "user")
81
+ File.join(home_dir, '.config', 'systemd', 'user')
83
82
  end
84
83
  end
85
84
 
86
85
  def create_systemd_template(role)
87
86
  systemd_path = fetch(:service_unit_path, fetch_systemd_unit_path)
88
- backend.execute :mkdir, '-p', systemd_path if fetch(:systemctl_user)
87
+ backend.execute :mkdir, '-p', systemd_path if fetch(:sidekiq_systemctl_user) != :system
89
88
 
90
89
  config_files(role).each do |config_file|
91
- ctemplate = compiled_template(config_file)
92
- temp_file_name = File.join('/tmp', "sidekiq.#{config_file}.service")
93
- systemd_file_name = File.join(systemd_path, sidekiq_service_file_name(config_file))
94
- backend.upload!(StringIO.new(ctemplate), temp_file_name)
95
- if fetch(:systemctl_user)
96
- warn "Moving #{temp_file_name} to #{systemd_file_name}"
97
- backend.execute :mv, temp_file_name, systemd_file_name
98
- else
99
- warn "Installing #{systemd_file_name} as root"
100
- backend.execute :sudo, :mv, temp_file_name, systemd_file_name
101
- end
90
+ ctemplate = compiled_template(config_file)
91
+ temp_file_name = File.join('/tmp', "sidekiq.#{config_file}.service")
92
+ systemd_file_name = File.join(systemd_path, sidekiq_service_file_name(config_file))
93
+ backend.upload!(StringIO.new(ctemplate), temp_file_name)
94
+ if fetch(:sidekiq_systemctl_user) == :system
95
+ warn "Installing #{systemd_file_name} as root"
96
+ backend.execute :sudo, :mv, temp_file_name, systemd_file_name
97
+ else
98
+ warn "Moving #{temp_file_name} to #{systemd_file_name}"
99
+ backend.execute :mv, temp_file_name, systemd_file_name
100
+ end
102
101
  end
103
102
  end
104
103
 
@@ -107,12 +106,12 @@ namespace :sidekiq do
107
106
 
108
107
  config_files(role).each do |config_file|
109
108
  systemd_file_name = File.join(systemd_path, sidekiq_service_file_name(config_file))
110
- if fetch(:systemctl_user)
111
- warn "Deleting #{systemd_file_name}"
112
- backend.execute :rm, "-f", systemd_file_name
113
- else
109
+ if fetch(:sidekiq_systemctl_user) == :system
114
110
  warn "Deleting #{systemd_file_name} as root"
115
- backend.execute :sudo, :rm, "-f", systemd_file_name
111
+ backend.execute :sudo, :rm, '-f', systemd_file_name
112
+ else
113
+ warn "Deleting #{systemd_file_name}"
114
+ backend.execute :rm, '-f', systemd_file_name
116
115
  end
117
116
  end
118
117
  end
@@ -121,21 +120,21 @@ namespace :sidekiq do
121
120
  config_files(role).each do |config_file|
122
121
  sidekiq_service = sidekiq_service_unit_name(config_file)
123
122
  warn "Quieting #{sidekiq_service}"
124
- execute_systemd("kill -s TSTP", sidekiq_service)
123
+ execute_systemd('kill -s TSTP', sidekiq_service)
125
124
  end
126
125
  end
127
126
 
128
127
  def sidekiq_service_unit_name(config_file)
129
- if config_file != "sidekiq.yml"
130
- fetch(:sidekiq_service_unit_name) + "." + config_file.split(".")[0..-2].join(".")
131
- else
128
+ if config_file == 'sidekiq.yml'
132
129
  fetch(:sidekiq_service_unit_name)
130
+ else
131
+ "#{fetch(:sidekiq_service_unit_name)}.#{config_file.split('.')[0..-2].join('.')}"
133
132
  end
134
133
  end
135
134
 
136
135
  def sidekiq_service_file_name(config_file)
137
136
  ## Remove the extension
138
- config_file = config_file.split('.')[0..-1].join('.')
137
+ config_file = config_file.split('.').join('.')
139
138
 
140
139
  "#{sidekiq_service_unit_name(config_file)}.service"
141
140
  end
@@ -20,7 +20,11 @@ Type=notify
20
20
  WatchdogSec=10
21
21
  <%="User=#{sidekiq_user}" if fetch(:sidekiq_systemctl_user) == :system %>
22
22
  WorkingDirectory=<%= current_path %>
23
- ExecStart=<%= expanded_bundle_path %> exec sidekiq -e <%= fetch(:sidekiq_env) %> <%= sidekiq_config %>
23
+ <% if fetch(:sidekiq_use_login_shell) %>
24
+ ExecStart=/bin/bash -lc '<%= expanded_bundle_path %> exec <%= fetch(:sidekiq_command) %> <%= fetch(:sidekiq_command_args) %> <%= sidekiq_config %>'
25
+ <% else %>
26
+ ExecStart=<%= expanded_bundle_path %> exec <%= fetch(:sidekiq_command) %> <%= fetch(:sidekiq_command_args) %> <%= sidekiq_config %>
27
+ <% end %>
24
28
 
25
29
  # Use `systemctl kill -s TSTP <% sidekiq_service_unit_name(@config_file) %>` to quiet the Sidekiq process
26
30
  UMask=0002
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: capistrano
@@ -43,14 +43,84 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 6.0.6
46
+ version: '7.0'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 6.0.6
53
+ version: '7.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: minitest
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '5.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '5.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.50'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.50'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-minitest
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.30'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.30'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rubocop-rake
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.6'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.6'
54
124
  description: Sidekiq integration for Capistrano
55
125
  email:
56
126
  - terminale@gmail.com
@@ -65,15 +135,15 @@ files:
65
135
  - lib/capistrano/sidekiq.rb
66
136
  - lib/capistrano/sidekiq/systemd.rb
67
137
  - lib/capistrano/sidekiq/version.rb
138
+ - lib/capistrano/tasks/helpers.rake
68
139
  - lib/capistrano/tasks/sidekiq.rake
69
140
  - lib/capistrano/tasks/systemd.rake
70
141
  - lib/capistrano/templates/sidekiq.service.capistrano.erb
71
- - lib/generators/capistrano/sidekiq/monit/template_generator.rb
72
- - lib/generators/capistrano/sidekiq/monit/templates/sidekiq_monit.conf.erb
73
142
  homepage: https://github.com/seuros/capistrano-sidekiq
74
143
  licenses:
75
144
  - LGPL-3.0
76
- metadata: {}
145
+ metadata:
146
+ rubygems_mfa_required: 'true'
77
147
  post_install_message: "\n Version 3.0.0 is a major release. Please see README.md,
78
148
  breaking changes are listed in CHANGELOG.md\n "
79
149
  rdoc_options: []
@@ -83,14 +153,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
153
  requirements:
84
154
  - - ">="
85
155
  - !ruby/object:Gem::Version
86
- version: 2.5.0
156
+ version: 3.2.0
87
157
  required_rubygems_version: !ruby/object:Gem::Requirement
88
158
  requirements:
89
159
  - - ">="
90
160
  - !ruby/object:Gem::Version
91
161
  version: '0'
92
162
  requirements: []
93
- rubygems_version: 3.6.2
163
+ rubygems_version: 3.6.7
94
164
  specification_version: 4
95
165
  summary: Sidekiq integration for Capistrano
96
166
  test_files: []
@@ -1,24 +0,0 @@
1
- require 'rails/generators/base'
2
-
3
- module Capistrano
4
- module Sidekiq
5
- module Monit
6
- module Generators
7
- class TemplateGenerator < Rails::Generators::Base
8
-
9
- namespace "capistrano:sidekiq:monit:template"
10
- desc "Create local monitrc.erb, and erb files for monitored processes for customization"
11
- source_root File.expand_path('../templates', __FILE__)
12
- argument :templates_path, type: :string,
13
- default: "config/deploy/templates",
14
- banner: "path to templates"
15
-
16
- def copy_template
17
- copy_file "sidekiq_monit.conf.erb", "#{templates_path}/sidekiq_monit.erb"
18
- end
19
-
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,8 +0,0 @@
1
- # Monit configuration for Sidekiq
2
- # Service name: <%= sidekiq_service_name %>
3
- #
4
- check process <%= sidekiq_service_name %>
5
- matching 'sidekiq .* <%= fetch(:application) %>'
6
- start program = "/bin/su - <%= sidekiq_user(role) %> -c 'cd <%= current_path %> && <%= SSHKit.config.command_map[:bundle] %> exec sidekiq -e <%= fetch(:sidekiq_env) %> <%= sidekiq_config %> <%= sidekiq_concurrency %> <%= sidekiq_require %> <%= sidekiq_queues %> <%= sidekiq_logfile ? ">> #{sidekiq_logfile} 2>&1" : nil %> &'" with timeout <%= fetch(:sidekiq_timeout).to_i + 10 %> seconds
7
- stop program = "/bin/su - <%= sidekiq_user(role) %> -c 'ps ax | grep "<%= "sidekiq .* #{fetch(:application)}" %>" | grep -v grep | awk "{print \$1}" | xargs --no-run-if-empty kill'" with timeout <%= fetch(:sidekiq_timeout).to_i + 10 %> seconds
8
- group <%= fetch(:sidekiq_monit_group) || fetch(:application) %>-sidekiq