bard-new 0.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 +7 -0
- data/.github/workflows/ci.yml +42 -0
- data/.gitignore +4 -0
- data/CLAUDE.md +55 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +179 -0
- data/README.md +107 -0
- data/Rakefile +8 -0
- data/bard-new.gemspec +25 -0
- data/features/new.feature +12 -0
- data/features/provision.feature +10 -0
- data/features/step_definitions/bard_new_steps.rb +64 -0
- data/features/support/bard-coverage +16 -0
- data/features/support/env.rb +22 -0
- data/features/support/new_server.rb +136 -0
- data/features/support/provision_server.rb +282 -0
- data/lib/bard/new/cli/new.rb +102 -0
- data/lib/bard/new/cli/provision.rb +32 -0
- data/lib/bard/new/provision/app.rb +9 -0
- data/lib/bard/new/provision/apt.rb +15 -0
- data/lib/bard/new/provision/authorizedkeys.rb +23 -0
- data/lib/bard/new/provision/base.rb +17 -0
- data/lib/bard/new/provision/data.rb +23 -0
- data/lib/bard/new/provision/deploy.rb +9 -0
- data/lib/bard/new/provision/http.rb +15 -0
- data/lib/bard/new/provision/logrotation.rb +27 -0
- data/lib/bard/new/provision/masterkey.rb +17 -0
- data/lib/bard/new/provision/mysql.rb +21 -0
- data/lib/bard/new/provision/nginx.rb +31 -0
- data/lib/bard/new/provision/repo.rb +71 -0
- data/lib/bard/new/provision/rvm.rb +20 -0
- data/lib/bard/new/provision/ssh.rb +79 -0
- data/lib/bard/new/provision/swapfile.rb +21 -0
- data/lib/bard/new/provision/user.rb +43 -0
- data/lib/bard/new/rails_template.rb +213 -0
- data/lib/bard/new/version.rb +5 -0
- data/lib/bard/plugins/new.rb +2 -0
- data/spec/acceptance/docker/Dockerfile.new +68 -0
- data/spec/acceptance/docker/Dockerfile.provision +41 -0
- data/spec/acceptance/docker/entrypoint-new.sh +3 -0
- data/spec/acceptance/docker/test_key +27 -0
- data/spec/acceptance/docker/test_key.pub +1 -0
- data/spec/bard/new/cli/new_spec.rb +85 -0
- data/spec/bard/new/cli/provision_spec.rb +40 -0
- data/spec/bard/new/provision/app_spec.rb +33 -0
- data/spec/bard/new/provision/apt_spec.rb +39 -0
- data/spec/bard/new/provision/authorizedkeys_spec.rb +40 -0
- data/spec/bard/new/provision/base_spec.rb +34 -0
- data/spec/bard/new/provision/data_spec.rb +54 -0
- data/spec/bard/new/provision/deploy_spec.rb +33 -0
- data/spec/bard/new/provision/http_spec.rb +57 -0
- data/spec/bard/new/provision/logrotation_spec.rb +34 -0
- data/spec/bard/new/provision/masterkey_spec.rb +62 -0
- data/spec/bard/new/provision/mysql_spec.rb +55 -0
- data/spec/bard/new/provision/nginx_spec.rb +81 -0
- data/spec/bard/new/provision/repo_spec.rb +208 -0
- data/spec/bard/new/provision/rvm_spec.rb +49 -0
- data/spec/bard/new/provision/ssh_spec.rb +242 -0
- data/spec/bard/new/provision/swapfile_spec.rb +33 -0
- data/spec/bard/new/provision/user_spec.rb +103 -0
- data/spec/spec_helper.rb +19 -0
- metadata +214 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8201fa5a621130b02195491b4639865adcc88eb94ec86bceb3f3c2008cb7ece9
|
|
4
|
+
data.tar.gz: f4d6efff097acd5ca4e20d54d07be348b4d8616928860a39d09cca688b4ba81a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0f1e688442793919d59477e855cf2db2b03ab09b7b8118a6a8a686acb8f412e13609fba9bf793729af83efd6424a2b66000422b9796fa543e56ac6a1ee3d72b9
|
|
7
|
+
data.tar.gz: 146fa6ba13c75198b1fabda558860a8faf1c3ee3d22732ecc51f43ea0eb2938573ddb1fdbd6f2608e090132b9d08da9c54c043923ecf5bb8b9a009757e862ba7
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
strategy:
|
|
6
|
+
fail-fast: false
|
|
7
|
+
matrix:
|
|
8
|
+
ruby: [ "3.3", "3.4", "4.0" ]
|
|
9
|
+
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout code
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Ruby
|
|
16
|
+
uses: ruby/setup-ruby@v1
|
|
17
|
+
with:
|
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
|
19
|
+
bundler-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Install Podman
|
|
22
|
+
run: |
|
|
23
|
+
sudo apt-get update -y
|
|
24
|
+
sudo apt-get install -y podman
|
|
25
|
+
sudo nohup podman system service --time=0 tcp://127.0.0.1:8080 > /tmp/podman-service.log 2>&1 &
|
|
26
|
+
for i in {1..30}; do
|
|
27
|
+
if curl --silent --fail http://127.0.0.1:8080/v1.40/version >/dev/null 2>&1; then
|
|
28
|
+
echo "Podman REST API ready at tcp://127.0.0.1:8080"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
sleep 1
|
|
32
|
+
done
|
|
33
|
+
echo "DOCKER_HOST=tcp://127.0.0.1:8080" >> $GITHUB_ENV
|
|
34
|
+
|
|
35
|
+
- name: Build test container images
|
|
36
|
+
run: |
|
|
37
|
+
sudo podman pull ubuntu:24.04
|
|
38
|
+
sudo podman build -t bard-test-provision -f spec/acceptance/docker/Dockerfile.provision spec/acceptance/docker
|
|
39
|
+
sudo podman build -t bard-test-new -f spec/acceptance/docker/Dockerfile.new .
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: bundle exec rake
|
data/.gitignore
ADDED
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this gem is
|
|
6
|
+
|
|
7
|
+
`bard-new` is a plugin for the `bard` CLI gem ([botandrose/bard](https://github.com/botandrose/bard)). It adds two subcommands:
|
|
8
|
+
|
|
9
|
+
- `bard new <project-name>` — scaffolds a new Rails app using `lib/bard/new/rails_template.rb`, optionally pushes to GitHub and stages a deploy.
|
|
10
|
+
- `bard provision [ssh_url]` — idempotently provisions a fresh Ubuntu 24.04 host into a production target, step by step.
|
|
11
|
+
|
|
12
|
+
The plugin entry point is `lib/bard/plugins/new.rb`. The parent `bard` gem's plugin loader auto-requires it (by convention); the gemspec depends on `bard`.
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
### CLI layer (`lib/bard/new/cli/`)
|
|
17
|
+
Reopens `Bard::CLI` (a Thor subclass from the `bard` gem) and adds `new` and `provision` commands. `new.rb` shells out to `env -i bash -lc` + RVM to run `rails new` with the template; `provision.rb` iterates over `PROVISION_STEPS` and dispatches each to a `Bard::Provision::<Step>` class.
|
|
18
|
+
|
|
19
|
+
### Provision steps (`lib/bard/new/provision/`)
|
|
20
|
+
Each step is a `Struct.new(:config, :ssh_url)` subclass of `Bard::Provision` (see `base.rb`) implementing `#call`. Steps are **idempotent** — they check current state before acting (e.g. `Nginx#http_responding?`, `SSH#password_auth_enabled?`) and print ` ✓` at the end. `provision_server` returns a duped target whose `ssh` points at the raw `ssh_url` being provisioned; `target` is the final production target from `bard.rb`. The order in `PROVISION_STEPS` matters (SSH → User → AuthorizedKeys → ... → Data).
|
|
21
|
+
|
|
22
|
+
### Rails template (`lib/bard/new/rails_template.rb`)
|
|
23
|
+
Drives `rails new -m` — generates Gemfile (bard-rails, solid_*, sprockets+dartsass, importmap/turbo/stimulus, exception_notification, puma), writes `Procfile`, adds a `bootstrap` rake task that does `db:prepare` + (in production) `assets:precompile` + `foreman export systemd-user` + `systemctl --user restart`, and runs `bard install && bin/setup && bard setup` after bundle. Reads `ruby_version` and `project_name` from the current `rvm current name`.
|
|
24
|
+
|
|
25
|
+
### Test infrastructure
|
|
26
|
+
Both cucumber features spin up a real **Podman** container (via `docker-api` against the podman socket) using `spec/acceptance/docker/Dockerfile.{new,provision}`, then SSH in with `spec/acceptance/docker/test_key`. The `@new` tag gives a deploy-ready Ubuntu + RVM + pre-installed bard/bard-new gems for exercising `bard new` — `Dockerfile.new` clones `bard` from GitHub (branch `v2.0`) during the build and `COPY`s the bard-new working tree in (build context is the repo root). The `@provision` tag gives a raw-ish Ubuntu + systemd for exercising the provision steps end-to-end (build context is just `spec/acceptance/docker/`).
|
|
27
|
+
|
|
28
|
+
Coverage is written by both cucumber (`features/support/env.rb`) and rspec (`spec/spec_helper.rb`) via SimpleCov — the cucumber runs shell out through `features/support/bard-coverage`, a wrapper that starts SimpleCov before exec'ing the `bard` binary so the subprocess's line coverage is merged in.
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Full suite (default rake task runs both)
|
|
34
|
+
bundle exec rake
|
|
35
|
+
|
|
36
|
+
# Unit specs only
|
|
37
|
+
bundle exec rspec
|
|
38
|
+
bundle exec rspec spec/bard/new/provision/nginx_spec.rb # single file
|
|
39
|
+
|
|
40
|
+
# Cucumber — tags select which container gets built/started
|
|
41
|
+
bundle exec cucumber features/new.feature # @new tag
|
|
42
|
+
bundle exec cucumber features/provision.feature # @provision tag
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Cucumber runs are slow (they build and boot containers). Per global rules: **always** pipe cucumber output to a uniquely-named file in `/tmp/` and grep it, and never run the full cucumber suite speculatively.
|
|
46
|
+
|
|
47
|
+
Podman must be installed and `systemctl --user start podman.socket` must succeed — the test harness auto-starts it and sets `DOCKER_HOST` to the user socket.
|
|
48
|
+
|
|
49
|
+
## Conventions specific to this repo
|
|
50
|
+
|
|
51
|
+
- Provision step output format is `print "Name:"` → one `print " action,"` per side effect → `puts " ✓"`. Preserve this when adding/editing steps.
|
|
52
|
+
- Provision steps MUST be idempotent and check state before acting — they are re-run against already-provisioned servers.
|
|
53
|
+
- `provision_server` (raw `ssh_url`) vs `target` (configured production target) is a load-bearing distinction; the SSH step rewrites `ssh_url` to the target port mid-flight so later steps connect to the reconfigured port.
|
|
54
|
+
- New provision steps: add the class name to `PROVISION_STEPS` in `lib/bard/new/cli/provision.rb` in the correct order, create `lib/bard/new/provision/<name>.rb` with a `Bard::Provision::<Name>` class, and add `spec/bard/new/provision/<name>_spec.rb`.
|
|
55
|
+
- The rails template's Ruby version is hardcoded to `ruby-4.0.2` in `cli/new.rb#new_ruby_version`.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
GIT
|
|
2
|
+
remote: https://github.com/botandrose/bard.git
|
|
3
|
+
revision: eed2de978df9a6e26bd98c2ca1e02da5586e22a0
|
|
4
|
+
branch: v2.0
|
|
5
|
+
specs:
|
|
6
|
+
bard (2.0.0)
|
|
7
|
+
base64
|
|
8
|
+
rbnacl
|
|
9
|
+
rvm
|
|
10
|
+
term-ansicolor (>= 1.0.3)
|
|
11
|
+
thor (>= 0.19.0)
|
|
12
|
+
|
|
13
|
+
PATH
|
|
14
|
+
remote: .
|
|
15
|
+
specs:
|
|
16
|
+
bard-new (0.1.0)
|
|
17
|
+
bard
|
|
18
|
+
|
|
19
|
+
GEM
|
|
20
|
+
remote: http://rubygems.org/
|
|
21
|
+
specs:
|
|
22
|
+
addressable (2.8.9)
|
|
23
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
24
|
+
base64 (0.3.0)
|
|
25
|
+
bigdecimal (4.1.0)
|
|
26
|
+
builder (3.3.0)
|
|
27
|
+
crack (1.0.1)
|
|
28
|
+
bigdecimal
|
|
29
|
+
rexml
|
|
30
|
+
cucumber (10.2.0)
|
|
31
|
+
base64 (~> 0.2)
|
|
32
|
+
builder (~> 3.2)
|
|
33
|
+
cucumber-ci-environment (> 9, < 12)
|
|
34
|
+
cucumber-core (> 15, < 17)
|
|
35
|
+
cucumber-cucumber-expressions (> 17, < 20)
|
|
36
|
+
cucumber-html-formatter (> 21, < 23)
|
|
37
|
+
diff-lcs (~> 1.5)
|
|
38
|
+
logger (~> 1.6)
|
|
39
|
+
mini_mime (~> 1.1)
|
|
40
|
+
multi_test (~> 1.1)
|
|
41
|
+
sys-uname (~> 1.3)
|
|
42
|
+
cucumber-ci-environment (11.0.0)
|
|
43
|
+
cucumber-core (16.2.0)
|
|
44
|
+
cucumber-gherkin (> 36, < 40)
|
|
45
|
+
cucumber-messages (> 31, < 33)
|
|
46
|
+
cucumber-tag-expressions (> 6, < 9)
|
|
47
|
+
cucumber-cucumber-expressions (19.0.0)
|
|
48
|
+
bigdecimal
|
|
49
|
+
cucumber-gherkin (39.0.0)
|
|
50
|
+
cucumber-messages (>= 31, < 33)
|
|
51
|
+
cucumber-html-formatter (22.3.0)
|
|
52
|
+
cucumber-messages (> 23, < 33)
|
|
53
|
+
cucumber-messages (32.2.0)
|
|
54
|
+
cucumber-tag-expressions (8.1.0)
|
|
55
|
+
date (3.5.1)
|
|
56
|
+
debug (1.11.1)
|
|
57
|
+
irb (~> 1.10)
|
|
58
|
+
reline (>= 0.3.8)
|
|
59
|
+
diff-lcs (1.6.2)
|
|
60
|
+
docile (1.4.1)
|
|
61
|
+
docker-api (2.4.0)
|
|
62
|
+
excon (>= 0.64.0)
|
|
63
|
+
multi_json
|
|
64
|
+
erb (6.0.2)
|
|
65
|
+
excon (1.4.2)
|
|
66
|
+
logger
|
|
67
|
+
ffi (1.17.4)
|
|
68
|
+
ffi (1.17.4-aarch64-linux-gnu)
|
|
69
|
+
ffi (1.17.4-aarch64-linux-musl)
|
|
70
|
+
ffi (1.17.4-arm-linux-gnu)
|
|
71
|
+
ffi (1.17.4-arm-linux-musl)
|
|
72
|
+
ffi (1.17.4-arm64-darwin)
|
|
73
|
+
ffi (1.17.4-x86-linux-gnu)
|
|
74
|
+
ffi (1.17.4-x86-linux-musl)
|
|
75
|
+
ffi (1.17.4-x86_64-darwin)
|
|
76
|
+
ffi (1.17.4-x86_64-linux-gnu)
|
|
77
|
+
ffi (1.17.4-x86_64-linux-musl)
|
|
78
|
+
hashdiff (1.2.1)
|
|
79
|
+
io-console (0.8.2)
|
|
80
|
+
irb (1.17.0)
|
|
81
|
+
pp (>= 0.6.0)
|
|
82
|
+
prism (>= 1.3.0)
|
|
83
|
+
rdoc (>= 4.0.0)
|
|
84
|
+
reline (>= 0.4.2)
|
|
85
|
+
logger (1.7.0)
|
|
86
|
+
memoist3 (1.0.0)
|
|
87
|
+
mini_mime (1.1.5)
|
|
88
|
+
mize (0.6.1)
|
|
89
|
+
multi_json (1.19.1)
|
|
90
|
+
multi_test (1.1.0)
|
|
91
|
+
pp (0.6.3)
|
|
92
|
+
prettyprint
|
|
93
|
+
prettyprint (0.2.0)
|
|
94
|
+
prism (1.9.0)
|
|
95
|
+
psych (5.3.1)
|
|
96
|
+
date
|
|
97
|
+
stringio
|
|
98
|
+
public_suffix (7.0.5)
|
|
99
|
+
rake (13.3.1)
|
|
100
|
+
rbnacl (7.1.2)
|
|
101
|
+
ffi (~> 1)
|
|
102
|
+
rdoc (7.2.0)
|
|
103
|
+
erb
|
|
104
|
+
psych (>= 4.0.0)
|
|
105
|
+
tsort
|
|
106
|
+
readline (0.0.4)
|
|
107
|
+
reline
|
|
108
|
+
reline (0.6.3)
|
|
109
|
+
io-console (~> 0.5)
|
|
110
|
+
rexml (3.4.4)
|
|
111
|
+
rspec (3.13.2)
|
|
112
|
+
rspec-core (~> 3.13.0)
|
|
113
|
+
rspec-expectations (~> 3.13.0)
|
|
114
|
+
rspec-mocks (~> 3.13.0)
|
|
115
|
+
rspec-core (3.13.6)
|
|
116
|
+
rspec-support (~> 3.13.0)
|
|
117
|
+
rspec-expectations (3.13.5)
|
|
118
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
119
|
+
rspec-support (~> 3.13.0)
|
|
120
|
+
rspec-mocks (3.13.8)
|
|
121
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
122
|
+
rspec-support (~> 3.13.0)
|
|
123
|
+
rspec-support (3.13.7)
|
|
124
|
+
rvm (1.11.3.9)
|
|
125
|
+
simplecov (0.22.0)
|
|
126
|
+
docile (~> 1.1)
|
|
127
|
+
simplecov-html (~> 0.11)
|
|
128
|
+
simplecov_json_formatter (~> 0.1)
|
|
129
|
+
simplecov-html (0.13.2)
|
|
130
|
+
simplecov_json_formatter (0.1.4)
|
|
131
|
+
stringio (3.2.0)
|
|
132
|
+
sync (0.5.0)
|
|
133
|
+
sys-uname (1.5.1)
|
|
134
|
+
ffi (~> 1.1)
|
|
135
|
+
memoist3 (~> 1.0.0)
|
|
136
|
+
term-ansicolor (1.11.3)
|
|
137
|
+
tins (~> 1)
|
|
138
|
+
testcontainers (0.2.0)
|
|
139
|
+
testcontainers-core (= 0.2.0)
|
|
140
|
+
testcontainers-core (0.2.0)
|
|
141
|
+
docker-api (~> 2.2)
|
|
142
|
+
thor (1.5.0)
|
|
143
|
+
tins (1.52.0)
|
|
144
|
+
bigdecimal
|
|
145
|
+
mize (~> 0.6)
|
|
146
|
+
readline
|
|
147
|
+
sync
|
|
148
|
+
tsort (0.2.0)
|
|
149
|
+
webmock (3.26.2)
|
|
150
|
+
addressable (>= 2.8.0)
|
|
151
|
+
crack (>= 0.3.2)
|
|
152
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
153
|
+
|
|
154
|
+
PLATFORMS
|
|
155
|
+
aarch64-linux-gnu
|
|
156
|
+
aarch64-linux-musl
|
|
157
|
+
arm-linux-gnu
|
|
158
|
+
arm-linux-musl
|
|
159
|
+
arm64-darwin
|
|
160
|
+
ruby
|
|
161
|
+
x86-linux-gnu
|
|
162
|
+
x86-linux-musl
|
|
163
|
+
x86_64-darwin
|
|
164
|
+
x86_64-linux-gnu
|
|
165
|
+
x86_64-linux-musl
|
|
166
|
+
|
|
167
|
+
DEPENDENCIES
|
|
168
|
+
bard!
|
|
169
|
+
bard-new!
|
|
170
|
+
cucumber
|
|
171
|
+
debug
|
|
172
|
+
rake
|
|
173
|
+
rspec
|
|
174
|
+
simplecov
|
|
175
|
+
testcontainers
|
|
176
|
+
webmock
|
|
177
|
+
|
|
178
|
+
BUNDLED WITH
|
|
179
|
+
2.7.2
|
data/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# bard-new
|
|
2
|
+
|
|
3
|
+
A plugin for the [bard](https://github.com/botandrose/bard) CLI that scaffolds new Rails projects and provisions Ubuntu servers into Bot & Rose production targets.
|
|
4
|
+
|
|
5
|
+
Adds two subcommands:
|
|
6
|
+
|
|
7
|
+
- `bard new <project-name>` — scaffold a new Rails app, push it to GitHub, and stage a deploy.
|
|
8
|
+
- `bard provision [ssh_url]` — idempotently provision a fresh Ubuntu 24.04 host into a production target.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
gem install bard-new
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The parent `bard` gem auto-requires installed plugins by convention, so the new commands appear under `bard --help` once the gem is installed.
|
|
17
|
+
|
|
18
|
+
## `bard new`
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bard new my_project
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Steps:
|
|
25
|
+
|
|
26
|
+
1. Validates the project name (lowercase letters and digits only, must start with a letter).
|
|
27
|
+
2. Creates an RVM gemset (`ruby-4.0.2@<project-name>`) and installs Rails (`~> 8.1.0`).
|
|
28
|
+
3. Runs `rails new` against [`lib/bard/new/rails_template.rb`](lib/bard/new/rails_template.rb), which:
|
|
29
|
+
- writes a Gemfile with `bard-rails`, `solid_*`, sprockets + dartsass, importmap/turbo/stimulus, `exception_notification`, and `puma`
|
|
30
|
+
- writes a `Procfile`
|
|
31
|
+
- adds a `bootstrap` rake task (runs `db:prepare`, and in production runs `assets:precompile`, exports systemd-user units via `foreman`, and restarts the service)
|
|
32
|
+
- runs `bard install && bin/setup && bard setup`
|
|
33
|
+
4. Creates a private GitHub repo, pushes the initial commit, uploads `config/master.key` as an Actions secret, and enables branch protection on `master`.
|
|
34
|
+
5. Stages the new project (`bard deploy --clone`).
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
|
|
38
|
+
- `--skip-github` — skip GitHub repo creation and push.
|
|
39
|
+
- `--skip-stage` — skip the staging deploy.
|
|
40
|
+
|
|
41
|
+
## `bard provision`
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bard provision deploy@new-server.example.com:22
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If `ssh_url` is omitted, the `:production` target's SSH URL from `bard.rb` is used.
|
|
48
|
+
|
|
49
|
+
The command iterates through the provisioning steps in order and dispatches each to a class under `Bard::Provision`. Every step is **idempotent** — it inspects current state and skips work that's already done — so re-running against a partially or fully provisioned host is safe.
|
|
50
|
+
|
|
51
|
+
| Step | What it does |
|
|
52
|
+
|------------------|------------------------------------------------------------------------------|
|
|
53
|
+
| `SSH` | Hardens sshd, switches to the port configured on the production target. |
|
|
54
|
+
| `User` | Creates the deploy user. |
|
|
55
|
+
| `AuthorizedKeys` | Installs SSH keys. |
|
|
56
|
+
| `Swapfile` | Allocates a swapfile. |
|
|
57
|
+
| `Apt` | Installs base system packages. |
|
|
58
|
+
| `MySQL` | Installs and configures MySQL, creates the app database and user. |
|
|
59
|
+
| `Repo` | Clones the application repo into the deploy path. |
|
|
60
|
+
| `MasterKey` | Installs `config/master.key`. |
|
|
61
|
+
| `RVM` | Installs RVM and the project's Ruby version. |
|
|
62
|
+
| `App` | Runs `bin/setup` / app bootstrap. |
|
|
63
|
+
| `Nginx` | Installs and configures nginx + TLS. |
|
|
64
|
+
| `Deploy` | Performs the initial deploy. |
|
|
65
|
+
| `HTTP` | Verifies the site responds. |
|
|
66
|
+
| `LogRotation` | Configures logrotate. |
|
|
67
|
+
| `Data` | Syncs initial data. |
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
|
|
71
|
+
- `--steps step1 step2 …` — run only a subset of steps (names match the table above).
|
|
72
|
+
|
|
73
|
+
## Development
|
|
74
|
+
|
|
75
|
+
The test suite uses real Podman containers to exercise both commands end-to-end. You need:
|
|
76
|
+
|
|
77
|
+
- Podman with the user socket enabled: `systemctl --user start podman.socket`
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bundle install
|
|
81
|
+
|
|
82
|
+
# Full suite (rspec + cucumber)
|
|
83
|
+
bundle exec rake
|
|
84
|
+
|
|
85
|
+
# Unit specs
|
|
86
|
+
bundle exec rspec
|
|
87
|
+
bundle exec rspec spec/bard/new/provision/nginx_spec.rb
|
|
88
|
+
|
|
89
|
+
# Cucumber — tags select which container image gets built
|
|
90
|
+
bundle exec cucumber features/new.feature # @new
|
|
91
|
+
bundle exec cucumber features/provision.feature # @provision
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Cucumber runs are slow because they build and boot containers; run individual features rather than the full suite.
|
|
95
|
+
|
|
96
|
+
## Adding a provision step
|
|
97
|
+
|
|
98
|
+
1. Add the class name to `PROVISION_STEPS` in [`lib/bard/new/cli/provision.rb`](lib/bard/new/cli/provision.rb) at the correct position — ordering matters (e.g. `SSH` must run before `User`, which must run before `AuthorizedKeys`).
|
|
99
|
+
2. Create `lib/bard/new/provision/<name>.rb` defining `Bard::Provision::<Name>` as a subclass of `Bard::Provision` (a `Struct.new(:config, :ssh_url)`) with a `#call` method.
|
|
100
|
+
3. Make `#call` idempotent — check current state before making changes.
|
|
101
|
+
4. Follow the output convention: `print "Name:"`, then one `print " action,"` per side effect, then `puts " ✓"`.
|
|
102
|
+
5. Use `provision_server` to SSH to the host being provisioned (raw `ssh_url`) and `target` for the final production target from `bard.rb`. The `SSH` step rewrites `ssh_url` to the configured port mid-flight, so later steps connect to the reconfigured host.
|
|
103
|
+
6. Add `spec/bard/new/provision/<name>_spec.rb`.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT. Copyright (c) Micah Geisel.
|
data/Rakefile
ADDED
data/bard-new.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "bard/new/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "bard-new"
|
|
7
|
+
spec.version = Bard::New::VERSION
|
|
8
|
+
spec.authors = ["Micah Geisel"]
|
|
9
|
+
spec.email = ["micah@botandrose.com"]
|
|
10
|
+
spec.summary = "Project creation and server provisioning for bard."
|
|
11
|
+
spec.homepage = "http://github.com/botandrose/bard-new"
|
|
12
|
+
spec.license = "MIT"
|
|
13
|
+
|
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
16
|
+
spec.require_paths = ["lib"]
|
|
17
|
+
|
|
18
|
+
spec.add_dependency "bard"
|
|
19
|
+
|
|
20
|
+
spec.add_development_dependency "rake"
|
|
21
|
+
spec.add_development_dependency "rspec"
|
|
22
|
+
spec.add_development_dependency "debug"
|
|
23
|
+
spec.add_development_dependency "cucumber"
|
|
24
|
+
spec.add_development_dependency "testcontainers"
|
|
25
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@new
|
|
2
|
+
Feature: bard new
|
|
3
|
+
Create a new bard-managed Rails project.
|
|
4
|
+
|
|
5
|
+
Background:
|
|
6
|
+
Given a bard new server is running
|
|
7
|
+
|
|
8
|
+
Scenario: creates a new Rails project
|
|
9
|
+
When I run bard new "testproject"
|
|
10
|
+
Then the output should contain "Project testproject created!"
|
|
11
|
+
And the project "testproject" should run successfully
|
|
12
|
+
And the project "testproject" should respond to http://testproject.localhost
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@provision
|
|
2
|
+
Feature: bard provision
|
|
3
|
+
Background:
|
|
4
|
+
Given a provision server is running
|
|
5
|
+
|
|
6
|
+
Scenario: provisions a server with nginx reverse proxy
|
|
7
|
+
When I provision the system
|
|
8
|
+
And I set up the test project
|
|
9
|
+
And I provision the app
|
|
10
|
+
Then the site should be running
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Then /^the output should contain "([^\"]+)"$/ do |expected|
|
|
2
|
+
expect(@stdout).to include(expected)
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
# bard new steps
|
|
6
|
+
Given /^a bard new server is running$/ do
|
|
7
|
+
raise "New server failed to start" unless @new_container && @new_ssh_port
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
When /^I run bard new "([^"]+)"$/ do |project_name|
|
|
11
|
+
run_bard_remote("new #{project_name} --skip-github --skip-stage")
|
|
12
|
+
unless @status.success?
|
|
13
|
+
raise "bard new failed with status: #{@status}\nOutput: #{@stdout}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Then /^the project "([^"]+)" should run successfully$/ do |project_name|
|
|
18
|
+
stdout, status = run_new_ssh("cd /tmp/bardwork/#{project_name} && bin/rails runner 'puts :bard_test_ok'")
|
|
19
|
+
expect(status).to be_success, "rails runner failed:\n#{stdout}"
|
|
20
|
+
expect(stdout).to include("bard_test_ok")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Then /^the project "([^"]+)" should respond to http:\/\/(.+)$/ do |project_name, hostname|
|
|
24
|
+
Open3.capture2e(
|
|
25
|
+
"timeout", "3",
|
|
26
|
+
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null",
|
|
27
|
+
"-p", @new_ssh_port.to_s, "-i", new_ssh_key_path,
|
|
28
|
+
"deploy@localhost",
|
|
29
|
+
"bash -lc 'cd /tmp/bardwork/#{project_name} && setsid -f bundle exec puma -p 3000 </dev/null >/tmp/puma.log 2>&1'"
|
|
30
|
+
)
|
|
31
|
+
sleep 5
|
|
32
|
+
stdout, status = run_new_ssh("curl -sf -H 'Host: #{hostname}' http://localhost/")
|
|
33
|
+
expect(status).to be_success, "HTTP request to #{hostname} failed:\n#{stdout}"
|
|
34
|
+
expect(stdout).to include(project_name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# bard provision steps
|
|
38
|
+
Given /^a provision server is running$/ do
|
|
39
|
+
raise "Provision server failed to start" unless @container && @ssh_port
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
When /^I provision the system$/ do
|
|
43
|
+
run_provision_phase1
|
|
44
|
+
unless @status.success?
|
|
45
|
+
raise "Provision phase 1 failed:\n#{@stdout}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
When /^I set up the test project$/ do
|
|
50
|
+
setup_test_project
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
When /^I provision the app$/ do
|
|
54
|
+
run_provision_phase2
|
|
55
|
+
unless @status.success?
|
|
56
|
+
raise "Provision phase 2 failed:\n#{@stdout}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Then /^the site should be running$/ do
|
|
61
|
+
stdout, status = Open3.capture2e("curl -sf http://127.0.0.1:#{@http_port}/")
|
|
62
|
+
expect(status).to be_success, "Site not responding on port #{@http_port}"
|
|
63
|
+
expect(stdout).to include("hello from testproject")
|
|
64
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
root = File.expand_path("../../..", __FILE__)
|
|
3
|
+
|
|
4
|
+
require "simplecov"
|
|
5
|
+
SimpleCov.root(root)
|
|
6
|
+
SimpleCov.start do
|
|
7
|
+
command_name "Cucumber (subprocess #{$$})"
|
|
8
|
+
track_files "lib/**/*.rb"
|
|
9
|
+
add_filter "spec/"
|
|
10
|
+
add_filter "features/"
|
|
11
|
+
coverage_dir "#{root}/coverage"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
$LOAD_PATH.unshift("#{root}/lib")
|
|
15
|
+
require "bard/cli"
|
|
16
|
+
Bard::CLI.start ARGV
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "simplecov"
|
|
2
|
+
SimpleCov.start do
|
|
3
|
+
command_name "Cucumber"
|
|
4
|
+
track_files "lib/**/*.rb"
|
|
5
|
+
add_filter "spec/"
|
|
6
|
+
add_filter "features/"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
|
10
|
+
require "bard/cli"
|
|
11
|
+
require 'rspec/expectations'
|
|
12
|
+
require 'fileutils'
|
|
13
|
+
|
|
14
|
+
ENV["PATH"] = "#{File.dirname(File.expand_path(__FILE__))}:#{ENV['PATH']}"
|
|
15
|
+
ENV["GIT_DIR"] = nil
|
|
16
|
+
ENV["GIT_WORK_TREE"] = nil
|
|
17
|
+
ENV["GIT_INDEX_FILE"] = nil
|
|
18
|
+
|
|
19
|
+
ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
|
|
20
|
+
|
|
21
|
+
# Ensure tmp directory exists
|
|
22
|
+
FileUtils.mkdir_p(File.join(ROOT, "tmp"))
|