Skip to content

Commit 1ec34ff

Browse files
committed
release-only downloads, autocomplete
1 parent 15c5cd8 commit 1ec34ff

19 files changed

Lines changed: 566 additions & 59 deletions

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ jobs:
7474
7575
- name: Package release bundle
7676
run: |
77+
cp -a ansible release/ansible
78+
rm -rf release/ansible/.ansible
7779
cd release
78-
zip -rq "../terrarium-linux-${{ matrix.arch }}.zip" dist
80+
zip -rq "../terrarium-linux-${{ matrix.arch }}.zip" dist ansible
7981
8082
- uses: actions/upload-artifact@v4
8183
with:

.github/workflows/validate.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
bun install
2020
bun run build
2121
bun run docs:build
22+
cd ansible
2223
for attempt in 1 2 3 4; do
2324
if ansible-galaxy collection install -r requirements.yml; then
2425
break
@@ -34,4 +35,6 @@ jobs:
3435
test -x dist/terrariumctl
3536
bun run tests/integration/index.ts --help
3637
- name: Ansible syntax
37-
run: ansible-playbook -i ansible/inventory.ini ansible/site.yml --syntax-check
38+
run: |
39+
cd ansible
40+
ansible-playbook -i inventory.ini site.yml --syntax-check

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ node_modules/
33
bun.lockb
44
docs/.vitepress/cache/
55
docs/.vitepress/dist/
6+
ansible/.ansible/
67
tests/integration/output/
78
tests/integration/.env

ansible.cfg renamed to ansible/ansible.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[defaults]
2-
inventory = ansible/inventory.ini
3-
roles_path = ansible/roles
2+
inventory = inventory.ini
3+
roles_path = roles
44
collections_path = .ansible/collections:~/.ansible/collections:/usr/share/ansible/collections
55
host_key_checking = False
66
retry_files_enabled = False

ansible/roles/base/tasks/main.yml

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
- "{{ terrarium_state_dir }}/restore"
2929
- /usr/local/lib/terrarium
3030

31-
- name: Assert Terrarium compiled binaries are staged in the repo checkout
31+
- name: Assert Terrarium compiled binaries are staged in the install bundle
3232
ansible.builtin.stat:
3333
path: "{{ terrarium_repo_dir }}/dist/terrariumctl"
3434
register: terrarium_compiled_cli
@@ -75,6 +75,88 @@
7575
and terrarium_trm_alias.stat.lnk_source == "/usr/local/bin/terrariumctl"
7676
)
7777
78+
- name: Create shell completion directories
79+
ansible.builtin.file:
80+
path: "{{ item }}"
81+
state: directory
82+
mode: "0755"
83+
loop:
84+
- /usr/share/bash-completion/completions
85+
- /usr/local/share/zsh/site-functions
86+
- /usr/share/fish/vendor_completions.d
87+
88+
- name: Generate terrariumctl bash completion
89+
ansible.builtin.command: /usr/local/bin/terrariumctl completion bash
90+
register: terrariumctl_completion_bash
91+
changed_when: false
92+
93+
- name: Generate terrariumctl zsh completion
94+
ansible.builtin.command: /usr/local/bin/terrariumctl completion zsh
95+
register: terrariumctl_completion_zsh
96+
changed_when: false
97+
98+
- name: Generate terrariumctl fish completion
99+
ansible.builtin.command: /usr/local/bin/terrariumctl completion fish
100+
register: terrariumctl_completion_fish
101+
changed_when: false
102+
103+
- name: Install terrariumctl bash completion
104+
ansible.builtin.copy:
105+
content: "{{ terrariumctl_completion_bash.stdout }}\n"
106+
dest: /usr/share/bash-completion/completions/terrariumctl
107+
mode: "0644"
108+
109+
- name: Install terrariumctl zsh completion
110+
ansible.builtin.copy:
111+
content: "{{ terrariumctl_completion_zsh.stdout }}\n"
112+
dest: /usr/local/share/zsh/site-functions/_terrariumctl
113+
mode: "0644"
114+
115+
- name: Install terrariumctl fish completion
116+
ansible.builtin.copy:
117+
content: "{{ terrariumctl_completion_fish.stdout }}\n"
118+
dest: /usr/share/fish/vendor_completions.d/terrariumctl.fish
119+
mode: "0644"
120+
121+
- name: Install trm bash completion alias
122+
ansible.builtin.file:
123+
src: /usr/share/bash-completion/completions/terrariumctl
124+
dest: /usr/share/bash-completion/completions/trm
125+
state: link
126+
force: true
127+
when: >-
128+
not terrarium_trm_alias.stat.exists
129+
or (
130+
terrarium_trm_alias.stat.islnk | default(false)
131+
and terrarium_trm_alias.stat.lnk_source == "/usr/local/bin/terrariumctl"
132+
)
133+
134+
- name: Install trm zsh completion alias
135+
ansible.builtin.file:
136+
src: /usr/local/share/zsh/site-functions/_terrariumctl
137+
dest: /usr/local/share/zsh/site-functions/_trm
138+
state: link
139+
force: true
140+
when: >-
141+
not terrarium_trm_alias.stat.exists
142+
or (
143+
terrarium_trm_alias.stat.islnk | default(false)
144+
and terrarium_trm_alias.stat.lnk_source == "/usr/local/bin/terrariumctl"
145+
)
146+
147+
- name: Install trm fish completion alias
148+
ansible.builtin.file:
149+
src: /usr/share/fish/vendor_completions.d/terrariumctl.fish
150+
dest: /usr/share/fish/vendor_completions.d/trm.fish
151+
state: link
152+
force: true
153+
when: >-
154+
not terrarium_trm_alias.stat.exists
155+
or (
156+
terrarium_trm_alias.stat.islnk | default(false)
157+
and terrarium_trm_alias.stat.lnk_source == "/usr/local/bin/terrariumctl"
158+
)
159+
78160
- name: Ensure root has a local password for Cockpit
79161
ansible.builtin.user:
80162
name: root

docs/getting-started/installation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Most users should use the interactive installer. Just run this single command:
2929
curl -fsSL https://github.com/terion-name/terrarium/releases/latest/download/install.sh | bash
3030
```
3131

32-
The published `install.sh` is intentionally thin. It downloads the matching compiled `terrariumctl` bundle from GitHub Releases, stages it into `/opt/terrarium`, and runs the real installer there. Default and tag-like release installs fail closed if the release cannot be resolved or downloaded; source builds require an explicit branch-like `--ref`, for example `main`.
32+
The published `install.sh` is intentionally thin. It downloads the matching release bundle from GitHub Releases, unpacks the compiled `terrariumctl` binary plus the Ansible provisioning assets into `/opt/terrarium`, and runs the real installer there. Default and tag-like release installs fail closed if the release cannot be resolved or downloaded; source builds require an explicit branch-like `--ref`, for example `main`.
3333

3434
If you want to pin a specific release instead of `latest`, use the tagged asset directly:
3535

@@ -120,7 +120,7 @@ For non-interactive automation, use generated or file-based secret inputs so sec
120120

121121
Terrarium keeps:
122122

123-
- the repo checkout at `/opt/terrarium`
123+
- the installed Terrarium bundle at `/opt/terrarium`
124124
- the canonical config in LXD's dqlite-backed `terrarium-system` project after LXD is initialized
125125
- a local config export at `/etc/terrarium/config.yaml`
126126

docs/reference/services-and-endpoints.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ If you ever need to dig into the server's internals, here is where everything li
5252
| `/etc/terrarium/config.yaml` | Your human-readable configuration backup. |
5353
| `/etc/terrarium/secrets/` | Generated passwords (like your Cockpit root login). |
5454
| `/var/lib/terrarium/` | General state files, S3 backup manifests, and OAuth proxy configs. |
55-
| `/opt/terrarium/` | The Terrarium source code repository. |
55+
| `/opt/terrarium/` | The installed Terrarium bundle: compiled `terrariumctl` and Ansible provisioning assets. |
5656

5757
*Note: The **canonical** configuration is actually stored inside LXD's `dqlite` database. Do not edit `/etc/terrarium/config.yaml` by hand; always use `terrariumctl set` commands.*
5858

@@ -70,4 +70,4 @@ If you link multiple Terrarium servers into a cluster, they communicate over a h
7070
- **`6641/tcp` & `6642/tcp`**: OVN database traffic (which Terrarium secures with mutual TLS certificates).
7171
- **`6081/udp`**: OVN overlay traffic (container-to-container communication).
7272

73-
You never need to open these internal ports on your hosting provider's firewall. Terrarium handles all the complex routing securely through the single WireGuard connection.
73+
You never need to open these internal ports on your hosting provider's firewall. Terrarium handles all the complex routing securely through the single WireGuard connection.

docs/reference/terrariumctl.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@
3939
| `terrariumctl set idp local|oidc` | mode plus optional flags | n/a | Switches between self-hosted ZITADEL and external OIDC, verifies external OIDC settings when applicable, and reconfigures oauth2-proxy plus LXD management auth together. |
4040
| `terrariumctl set s3` | optional flags | keeps current enable/disable state unless `--enable` or `--disable` is passed | Updates S3 backup settings, verifies the target with a real test operation, and can enable or disable S3 exports. |
4141
| `terrariumctl set syncoid` | optional flags | keeps current enable/disable state unless `--enable` or `--disable` is passed | Updates syncoid replication settings and can enable or disable syncoid. |
42+
| `terrariumctl completion` | `bash`, `zsh`, or `fish` | n/a | Prints a shell completion script. Installed hosts register completion for both `terrariumctl` and `trm` automatically. |
4243

4344
## install
4445

4546
| Flag | Argument | Required | Default | Meaning |
4647
| --- | --- | --- | --- | --- |
4748
| `--non-interactive` | none | no | interactive mode if omitted | Disables prompts and requires all needed config through flags. |
4849
| `--yes` | none | no | prompt before destructive actions | Auto-confirms destructive or confirmation prompts. |
49-
| `--ref` | git branch or tag | no | `main` | Checks out a specific Terrarium ref in `/opt/terrarium`. |
50+
| `--ref` | git branch or tag | no | `main` when invoking `terrariumctl` directly; release-selected tag when run through `install.sh` | Installs a specific Terrarium release tag, or builds from a branch-like source ref such as `main`. |
5051
| `--email` | email address | yes in non-interactive mode; no in interactive mode | prompted in interactive mode | Sets the Terrarium contact/admin email and default ZITADEL bootstrap admin email. |
5152
| `--acme-email` | email address | no | falls back to `--email` | Sets the ACME account identity for Traefik and LXD certificate automation. |
5253
| `--domain` | root domain | no | service domains default to `<service>.<dashed-public-ip>.traefik.me` when omitted | Sets the root domain used to derive service subdomains. |

install.sh

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,16 @@ ensure_os() {
5353
ensure_bootstrap_deps() {
5454
export DEBIAN_FRONTEND=noninteractive
5555
apt-get update -y
56-
apt-get install -y ca-certificates curl unzip git python3
56+
apt-get install -y ca-certificates curl unzip python3
57+
}
58+
59+
ensure_git() {
60+
if command -v git >/dev/null 2>&1; then
61+
return
62+
fi
63+
export DEBIAN_FRONTEND=noninteractive
64+
apt-get update -y
65+
apt-get install -y git
5766
}
5867

5968
ensure_bun() {
@@ -138,7 +147,7 @@ download_release_bundle() {
138147
curl -fsSL "${asset_url}" -o "${bundle_dir}/terrarium.zip" || return 1
139148
unzip -q "${bundle_dir}/terrarium.zip" -d "${bundle_dir}"
140149
[[ -x "${bundle_dir}/dist/terrariumctl" ]] || return 1
141-
TERRARIUM_BUNDLE_DIR="${bundle_dir}" TERRARIUM_REPO_URL="${REPO_URL}" "${bundle_dir}/dist/terrariumctl" install --ref "${resolved_ref}" "${FORWARD_ARGS[@]}"
150+
run_terrariumctl_install "${bundle_dir}" "${bundle_dir}/dist/terrariumctl" "${resolved_ref}" "${FORWARD_ARGS[@]}"
142151
}
143152

144153
build_from_source() {
@@ -152,6 +161,7 @@ build_from_source() {
152161
mkdir -p "${build_dir}/repo"
153162
cp -a "${source_path}/." "${build_dir}/repo/"
154163
else
164+
ensure_git
155165
git clone --depth 1 --branch "${source_ref}" "${REPO_URL}" "${build_dir}/repo"
156166
fi
157167
ensure_bun
@@ -160,7 +170,37 @@ build_from_source() {
160170
/opt/bun/bin/bun install --frozen-lockfile || /opt/bun/bin/bun install --no-progress
161171
/opt/bun/bin/bun scripts/build.ts
162172
)
163-
TERRARIUM_BUNDLE_DIR="${build_dir}/repo" TERRARIUM_REPO_URL="${REPO_URL}" "${build_dir}/repo/dist/terrariumctl" install --ref "${source_ref}" "${FORWARD_ARGS[@]}"
173+
run_terrariumctl_install "${build_dir}/repo" "${build_dir}/repo/dist/terrariumctl" "${source_ref}" "${FORWARD_ARGS[@]}"
174+
}
175+
176+
is_non_interactive_install() {
177+
local arg
178+
for arg in "${FORWARD_ARGS[@]}"; do
179+
case "${arg}" in
180+
--non-interactive|--help|-h)
181+
return 0
182+
;;
183+
esac
184+
done
185+
return 1
186+
}
187+
188+
run_terrariumctl_install() {
189+
local bundle_dir="$1"
190+
local terrariumctl="$2"
191+
local ref="$3"
192+
shift 3
193+
194+
if [[ -r /dev/tty ]]; then
195+
TERRARIUM_BUNDLE_DIR="${bundle_dir}" TERRARIUM_REPO_URL="${REPO_URL}" "${terrariumctl}" install --ref "${ref}" "$@" </dev/tty
196+
return
197+
fi
198+
199+
if ! is_non_interactive_install; then
200+
die "interactive install requires a TTY; run this from an interactive shell or pass --non-interactive with full configuration"
201+
fi
202+
203+
TERRARIUM_BUNDLE_DIR="${bundle_dir}" TERRARIUM_REPO_URL="${REPO_URL}" "${terrariumctl}" install --ref "${ref}" "$@"
164204
}
165205

166206
main() {

0 commit comments

Comments
 (0)