bard 1.7.4 → 2.0.0.beta

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.
data/README.md ADDED
@@ -0,0 +1,489 @@
1
+ # Bard
2
+
3
+ A modular deployment tool for Ruby applications that makes deployment simple and extensible.
4
+
5
+ ## Quick Start
6
+
7
+ ```ruby
8
+ # bard.rb
9
+ target :production do
10
+ ssh "deploy@example.com:22"
11
+ end
12
+ ```
13
+
14
+ ```bash
15
+ bard deploy production
16
+ ```
17
+
18
+ ## Features
19
+
20
+ - **Modular Capabilities**: Enable only the features you need
21
+ - **Pluggable Strategies**: SSH, GitHub Pages, or create your own
22
+ - **Default Targets**: Pre-configured for common Bot and Rose workflows
23
+ - **Git Integration**: Built-in branch management and safety checks
24
+ - **CI Integration**: Auto-detects GitHub Actions or Jenkins
25
+ - **Data Syncing**: Copy databases and assets between targets
26
+
27
+ ## Installation
28
+
29
+ Add to your Gemfile:
30
+
31
+ ```ruby
32
+ gem 'bard'
33
+ ```
34
+
35
+ Or install globally:
36
+
37
+ ```bash
38
+ gem install bard
39
+ ```
40
+
41
+ ## Core Concepts
42
+
43
+ ### Targets
44
+
45
+ A **target** is a deployment destination. Targets can be servers, serverless environments, static hosting, or anything else you can deploy to.
46
+
47
+ ```ruby
48
+ target :production do
49
+ ssh "deploy@example.com:22", path: "app"
50
+ end
51
+ ```
52
+
53
+ ### Capabilities
54
+
55
+ Capabilities are features enabled on targets. Common capabilities include:
56
+
57
+ - **SSH**: Remote command execution and file transfer
58
+ - **Ping**: Health check URLs
59
+ - **Data**: Database and file syncing
60
+ - **Backup**: Automatic backups during deployment
61
+
62
+ ### Deployment Strategies
63
+
64
+ Strategies determine how code gets deployed. Built-in strategies:
65
+
66
+ - **SSH**: Deploy via git pull on remote server
67
+ - **GitHub Pages**: Deploy static site to gh-pages branch
68
+ - **Custom**: Define your own (Jets, Docker, Kubernetes, etc.)
69
+
70
+ ## Configuration
71
+
72
+ Create a `bard.rb` file in your project root:
73
+
74
+ ```ruby
75
+ # Simple SSH deployment
76
+ target :production do
77
+ ssh "deploy@example.com:22",
78
+ path: "app",
79
+ gateway: "bastion@example.com:22"
80
+ end
81
+
82
+ # GitHub Pages static site
83
+ target :production do
84
+ github_pages "https://example.com"
85
+ end
86
+
87
+ # Custom strategy (Jets serverless)
88
+ require_relative 'lib/jets_deploy_strategy'
89
+
90
+ target :production do
91
+ jets "https://api.example.com", run_tests: true
92
+ end
93
+ ```
94
+
95
+ ## Default Targets
96
+
97
+ Bard ships with default targets for Bot and Rose workflows. Override any in your `bard.rb`:
98
+
99
+ - **:local** - Local development (no SSH)
100
+ - **:ci** - Jenkins CI at staging.botandrose.com
101
+ - **:staging** - Staging server at staging.botandrose.com
102
+ - **:gubs** - Bot and Rose cloud server
103
+
104
+ ```ruby
105
+ # Override default staging to use Jets
106
+ target :staging do
107
+ jets "https://staging-api.example.com"
108
+ end
109
+
110
+ # Keep :ci, :gubs, :local as defaults
111
+ ```
112
+
113
+ ## Commands
114
+
115
+ ### Deployment
116
+
117
+ ```bash
118
+ # Deploy to production
119
+ bard deploy production
120
+
121
+ # Deploy to staging
122
+ bard deploy staging
123
+
124
+ # Deploy feature branch to staging (no merge)
125
+ bard stage feature-branch
126
+
127
+ # Skip CI checks
128
+ bard deploy production --skip-ci
129
+ ```
130
+
131
+ ### Data Management
132
+
133
+ ```bash
134
+ # Copy database and assets from production to local
135
+ bard data --from=production --to=local
136
+
137
+ # Copy staging data to local
138
+ bard data --from=staging --to=local
139
+
140
+ # Configure additional paths to sync
141
+ # bard.rb:
142
+ data "public/uploads", "public/system"
143
+ ```
144
+
145
+ ### SSH Commands
146
+
147
+ ```bash
148
+ # SSH into a target
149
+ bard ssh production
150
+
151
+ # Run a command on a target
152
+ bard run production "bundle exec rails console"
153
+ ```
154
+
155
+ ### CI
156
+
157
+ ```bash
158
+ # Run CI for current branch
159
+ bard ci
160
+
161
+ # Run tests locally
162
+ bard ci --local-ci
163
+
164
+ # Check CI status
165
+ bard ci --status
166
+ ```
167
+
168
+ ### Utilities
169
+
170
+ ```bash
171
+ # Open target URL in browser
172
+ bard open production
173
+
174
+ # Ping target to check health
175
+ bard ping production
176
+
177
+ # Show uncommitted changes
178
+ bard hurt
179
+
180
+ # Open changed files in vim
181
+ bard vim
182
+ ```
183
+
184
+ ### Provisioning
185
+
186
+ ```bash
187
+ # Full server provisioning
188
+ bard provision deploy@new-server.com:22
189
+
190
+ # Configure nginx for current app
191
+ bard setup
192
+ ```
193
+
194
+ ## SSH Capability
195
+
196
+ Enable SSH to run commands and transfer files:
197
+
198
+ ```ruby
199
+ target :production do
200
+ ssh "user@host:port",
201
+ path: "deploy/path",
202
+ gateway: "bastion@host:port",
203
+ ssh_key: "/path/to/key",
204
+ env: "RAILS_ENV=production"
205
+ end
206
+ ```
207
+
208
+ This provides:
209
+ - `target.run!(command)` - Execute remote command (raises on error)
210
+ - `target.run(command)` - Execute remote command (silent on error)
211
+ - `target.exec!(command)` - Replace process with remote command
212
+ - `target.copy_file(path, to: target)` - Copy file to another target
213
+ - `target.copy_dir(path, to: target)` - Rsync directory to another target
214
+
215
+ ## Deployment Strategies
216
+
217
+ ### SSH Strategy
218
+
219
+ Deploy by running git pull on remote server:
220
+
221
+ ```ruby
222
+ target :production do
223
+ ssh "deploy@example.com:22"
224
+ end
225
+ ```
226
+
227
+ Deployment runs: `git pull origin master && bin/setup`
228
+
229
+ ### GitHub Pages Strategy
230
+
231
+ Deploy static site to GitHub Pages:
232
+
233
+ ```ruby
234
+ target :production do
235
+ github_pages "https://example.com"
236
+ end
237
+ ```
238
+
239
+ Deployment:
240
+ 1. Starts Rails server locally
241
+ 2. Mirrors site with wget
242
+ 3. Creates orphan commit with static assets
243
+ 4. Force-pushes to `gh-pages` branch
244
+
245
+ ### Custom Strategies
246
+
247
+ Create your own deployment strategy:
248
+
249
+ ```ruby
250
+ # lib/jets_deploy_strategy.rb
251
+ module Bard
252
+ class DeployStrategy
253
+ class Jets < DeployStrategy
254
+ def deploy
255
+ target_name = target.key.to_s
256
+ options = target.strategy_options(:jets)
257
+
258
+ run! "rake vips:build:#{target_name}" unless options[:skip_build]
259
+ run! "bundle exec rspec" if should_run_tests?(target_name, options)
260
+ run! "jets deploy #{options[:env] || target_name}"
261
+ end
262
+
263
+ private
264
+
265
+ def should_run_tests?(target_name, options)
266
+ return options[:run_tests] if options.key?(:run_tests)
267
+ target_name == "production"
268
+ end
269
+ end
270
+ end
271
+ end
272
+ ```
273
+
274
+ Use it in your `bard.rb`:
275
+
276
+ ```ruby
277
+ require_relative 'lib/jets_deploy_strategy'
278
+
279
+ target :production do
280
+ jets "https://api.example.com", run_tests: true
281
+ end
282
+
283
+ target :staging do
284
+ jets "https://staging-api.example.com", skip_build: true
285
+ end
286
+ ```
287
+
288
+ Strategies auto-register via Ruby's `inherited` hook - no manual registration needed!
289
+
290
+ ## CI Configuration
291
+
292
+ Bard auto-detects your CI system:
293
+ - Finds `.github/workflows/ci.yml` → GitHub Actions
294
+ - Otherwise → Jenkins (legacy)
295
+
296
+ Override via DSL:
297
+
298
+ ```ruby
299
+ # Force specific CI system
300
+ ci :github_actions
301
+ ci :jenkins
302
+ ci :local
303
+
304
+ # Disable CI
305
+ ci false
306
+ ```
307
+
308
+ ## Ping Configuration
309
+
310
+ Configure health check URLs:
311
+
312
+ ```ruby
313
+ target :production do
314
+ ssh "deploy@example.com:22"
315
+ ping "https://example.com", "/health", "/status"
316
+ end
317
+ ```
318
+
319
+ Auto-configured by deployment strategies:
320
+
321
+ ```ruby
322
+ # Ping URL automatically set from Jets URL
323
+ target :production do
324
+ jets "https://api.example.com"
325
+ end
326
+ ```
327
+
328
+ ## Data Syncing
329
+
330
+ Database syncing is enabled by default when SSH is configured. Add file paths to sync:
331
+
332
+ ```ruby
333
+ # Global configuration
334
+ data "public/uploads", "public/system"
335
+
336
+ target :production do
337
+ ssh "deploy@example.com:22"
338
+ end
339
+ ```
340
+
341
+ Then sync:
342
+
343
+ ```bash
344
+ bard data --from=production --to=local
345
+ ```
346
+
347
+ This:
348
+ 1. Runs `bin/rake db:dump` on source
349
+ 2. Copies `db/data.sql.gz` via SCP
350
+ 3. Runs `bin/rake db:load` on destination
351
+ 4. Rsyncs configured data paths
352
+
353
+ ## Backup Configuration
354
+
355
+ Control whether backups are created during deployment:
356
+
357
+ ```ruby
358
+ # Enable backups (default for SSH deployments)
359
+ backup true
360
+
361
+ # Disable backups (typical for serverless/static)
362
+ backup false
363
+ ```
364
+
365
+ ## Example Configurations
366
+
367
+ ### Traditional Rails App
368
+
369
+ ```ruby
370
+ target :staging do
371
+ ssh "deploy@staging.example.com:22"
372
+ end
373
+
374
+ target :production do
375
+ ssh "deploy@production.example.com:22"
376
+ end
377
+
378
+ data "public/uploads"
379
+ backup true
380
+ ```
381
+
382
+ ### Serverless API (Jets)
383
+
384
+ ```ruby
385
+ require_relative 'lib/jets_deploy_strategy'
386
+
387
+ target :staging do
388
+ jets "https://staging-api.example.com"
389
+ end
390
+
391
+ target :production do
392
+ jets "https://api.example.com", run_tests: true
393
+ end
394
+
395
+ backup false
396
+ ```
397
+
398
+ ### Hybrid (Jets + SSH for debugging)
399
+
400
+ ```ruby
401
+ require_relative 'lib/jets_deploy_strategy'
402
+
403
+ target :staging do
404
+ jets "https://staging-api.example.com"
405
+ ssh "deploy@bastion.example.com:22" # Enables SSH commands
406
+ end
407
+
408
+ target :production do
409
+ jets "https://api.example.com"
410
+ # No SSH in production
411
+ end
412
+
413
+ backup false
414
+ ```
415
+
416
+ ### Static Site
417
+
418
+ ```ruby
419
+ target :production do
420
+ github_pages "https://example.com"
421
+ end
422
+
423
+ backup false
424
+ ```
425
+
426
+ ### Override Default Targets
427
+
428
+ ```ruby
429
+ # Override default staging to use Jets
430
+ target :staging do
431
+ jets "https://staging-api.example.com"
432
+ end
433
+
434
+ # Keep :ci, :gubs, :local targets as defaults
435
+ ```
436
+
437
+ ## Migration from v1.x
438
+
439
+ See [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for detailed migration instructions.
440
+
441
+ Key changes in v2.0:
442
+ - `server` renamed to `target`
443
+ - SSH configuration uses hash options
444
+ - Strategy-first configuration
445
+ - Capability-based commands
446
+
447
+ ## Architecture
448
+
449
+ Bard follows a modular, capability-based architecture:
450
+
451
+ - **Core**: Minimal git workflow and configuration
452
+ - **Capabilities**: Features enabled via DSL methods
453
+ - **Strategies**: Pluggable deployment strategies with auto-registration
454
+ - **Subsystems**: Independent and composable
455
+
456
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture documentation.
457
+
458
+ ## Custom Strategy Guide
459
+
460
+ See [CUSTOM_STRATEGIES.md](CUSTOM_STRATEGIES.md) for a step-by-step guide to creating custom deployment strategies.
461
+
462
+ ## Development
463
+
464
+ ```bash
465
+ # Clone repository
466
+ git clone https://github.com/botandrose/bard.git
467
+ cd bard
468
+
469
+ # Install dependencies
470
+ bundle install
471
+
472
+ # Run tests
473
+ bundle exec rspec
474
+
475
+ # Run bard from source
476
+ bundle exec bin/bard
477
+ ```
478
+
479
+ ## License
480
+
481
+ MIT License. Copyright (c) 2018 Micah Geisel. See LICENSE for details.
482
+
483
+ ## Contributing
484
+
485
+ 1. Fork it
486
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
487
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
488
+ 4. Push to the branch (`git push origin my-new-feature`)
489
+ 5. Create new Pull Request
@@ -44,10 +44,19 @@ module Bard::CLI::Deploy
44
44
  invoke :master_key, [], from: "local", to: to
45
45
  config[to].run! "bin/setup && bard setup"
46
46
  else
47
- if config[to].github_pages
48
- Bard::GithubPages.new(self).deploy(config[to])
47
+ # Use deployment strategy for v2.0 Targets, or fallback for v1.x Servers
48
+ target = config[to]
49
+ if target.respond_to?(:deploy_strategy) && target.deploy_strategy
50
+ require "bard/deploy_strategy/#{target.deploy_strategy}"
51
+ strategy = target.deploy_strategy_instance
52
+ strategy.deploy
53
+ elsif target.respond_to?(:github_pages) && target.github_pages
54
+ # Old v1.x github_pages support
55
+ require "bard/github_pages"
56
+ Bard::GithubPages.new(self).deploy(target)
49
57
  else
50
- config[to].run! "git pull origin master && bin/setup"
58
+ # Default SSH deployment
59
+ target.run! "git pull origin master && bin/setup"
51
60
  end
52
61
  end
53
62
 
data/lib/bard/command.rb CHANGED
@@ -24,8 +24,14 @@ module Bard
24
24
 
25
25
  def run verbose: false, quiet: false
26
26
  # no-op if server doesn't really exist
27
- if on.to_sym != :local && on.respond_to?(:ssh) && on.ssh == false
28
- return true
27
+ if on.to_sym != :local
28
+ # Check for new Target architecture
29
+ if on.respond_to?(:server) && on.server.nil?
30
+ return true
31
+ # Check for old Server architecture
32
+ elsif on.respond_to?(:ssh) && on.ssh == false
33
+ return true
34
+ end
29
35
  end
30
36
  if verbose
31
37
  system full_command(quiet: quiet)
@@ -55,18 +61,28 @@ module Bard
55
61
  end
56
62
 
57
63
  def remote_command quiet: false
64
+ # Support both new Target (with server attribute) and old Server architecture
65
+ ssh_server = on.respond_to?(:server) ? on.server : on
66
+
58
67
  cmd = command
59
- if on.env
60
- cmd = "#{on.env} #{command}"
68
+ if ssh_server.env
69
+ cmd = "#{ssh_server.env} #{command}"
61
70
  end
62
71
  unless home
63
- cmd = "cd #{on.path} && #{cmd}"
72
+ path = on.respond_to?(:path) ? on.path : ssh_server.path
73
+ cmd = "cd #{path} && #{cmd}" if path
64
74
  end
65
- ssh_key = on.ssh_key ? "-i #{on.ssh_key} " : ""
66
- cmd = "ssh -tt #{ssh_key} #{on.ssh_uri} '#{cmd}'"
67
- if on.gateway
68
- cmd = "ssh -tt #{on.ssh_uri(:gateway)} \"#{cmd}\""
75
+
76
+ ssh_key = ssh_server.ssh_key ? "-i #{ssh_server.ssh_key} " : ""
77
+ ssh_uri = ssh_server.respond_to?(:ssh_uri) ? ssh_server.ssh_uri : ssh_server.ssh_uri(:ssh)
78
+
79
+ cmd = "ssh -tt #{ssh_key} #{ssh_uri} '#{cmd}'"
80
+
81
+ if ssh_server.gateway
82
+ gateway_uri = ssh_server.respond_to?(:ssh_uri) ? ssh_server.gateway : ssh_server.ssh_uri(:gateway)
83
+ cmd = "ssh -tt #{gateway_uri} \"#{cmd}\""
69
84
  end
85
+
70
86
  cmd += " 2>&1" if quiet
71
87
  cmd
72
88
  end