Skip to content

Commit a0a5b53

Browse files
authored
FEATURE: Add Ruby-based setup wizard using Gum (#1018)
* FEATURE: Add Ruby-based setup wizard using Gum Introduces a new interactive setup wizard written in Ruby that provides a modern terminal UI experience using the Gum and Glamour gems. Key features: - Runs in a lightweight Docker container (no Ruby needed on host) - Uses docker-api gem to communicate with Docker socket directly - Interactive prompts with styled input fields and confirmations - Catppuccin-inspired color scheme with ASCII banner - Progress indicators and spinners for system checks - Full feature parity with bash discourse-setup for standalone installs Usage: ./discourse-setup-ruby [--skip-rebuild] [--skip-connection-test] The wrapper script builds the wizard image and handles the rebuild on the host after configuration completes.
1 parent 98e81aa commit a0a5b53

File tree

16 files changed

+1809
-1
lines changed

16 files changed

+1809
-1
lines changed

.github/workflows/build.yml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,36 @@ jobs:
160160
run: |
161161
docker tag discourse/base:2.0.${{ env.TIMESTAMP }}-main-${{ matrix.arch }} discourse/base:aarch64
162162
docker push discourse/base:aarch64
163+
164+
setup_wizard:
165+
runs-on: ${{ (matrix.arch == 'amd64' && 'debian-12-8core') || 'ubuntu-24.04-8core-arm' }}
166+
strategy:
167+
matrix:
168+
arch: [amd64, arm64]
169+
timeout-minutes: 15
170+
steps:
171+
- uses: actions/checkout@v4
172+
with:
173+
fetch-depth: 1
174+
175+
- name: build setup-wizard image
176+
working-directory: image
177+
run: |
178+
ruby auto_build.rb setup_wizard_${{ matrix.arch }}
179+
180+
- name: Login to Docker Hub
181+
if: github.ref == 'refs/heads/main'
182+
uses: docker/login-action@v3
183+
with:
184+
username: discoursebuild
185+
password: ${{ secrets.DOCKERHUB_PASSWORD }}
186+
187+
- name: push to dockerhub
188+
if: github.ref == 'refs/heads/main'
189+
run: |
190+
docker tag discourse/setup-wizard:build_${{ matrix.arch }} discourse/setup-wizard:release-${{ matrix.arch }}
191+
docker push discourse/setup-wizard:release-${{ matrix.arch }}
192+
163193
ruby_3_4:
164194
runs-on: debian-12-8core
165195
timeout-minutes: 30
@@ -232,7 +262,7 @@ jobs:
232262
docker push discourse/base:release-ruby-3.3.8-and-3.4.7
233263
push_multi_arch_manifests:
234264
runs-on: ubuntu-latest
235-
needs: [base, timestamp]
265+
needs: [base, setup_wizard, timestamp]
236266
env:
237267
TIMESTAMP: ${{ needs.timestamp.outputs.timestamp }}
238268
if: github.ref == 'refs/heads/main'
@@ -311,6 +341,11 @@ jobs:
311341
discourse/discourse_dev:${{ env.TIMESTAMP }}-amd64 \
312342
discourse/discourse_dev:${{ env.TIMESTAMP }}-arm64
313343
344+
# Setup wizard release
345+
docker buildx imagetools create -t discourse/setup-wizard:release \
346+
discourse/setup-wizard:release-amd64 \
347+
discourse/setup-wizard:release-arm64
348+
314349
test:
315350
runs-on: debian-12-8core
316351
timeout-minutes: 30

discourse-setup-ruby

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Discourse Setup Wizard (Ruby version)
4+
#
5+
# This script runs the Ruby-based setup wizard inside a Docker container.
6+
# It provides the same functionality as discourse-setup but with a better UI.
7+
#
8+
9+
set -e
10+
11+
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12+
cd "$DIR"
13+
14+
IMAGE_NAME="discourse/setup-wizard:release"
15+
16+
# Colors for output
17+
RED='\033[0;31m'
18+
GREEN='\033[0;32m'
19+
YELLOW='\033[1;33m'
20+
NC='\033[0m' # No Color
21+
22+
log_info() {
23+
echo -e "${GREEN}${NC} $1"
24+
}
25+
26+
log_warn() {
27+
echo -e "${YELLOW}!${NC} $1"
28+
}
29+
30+
log_error() {
31+
echo -e "${RED}${NC} $1"
32+
}
33+
34+
# Check if running as root
35+
check_root() {
36+
if [[ $EUID -ne 0 ]]; then
37+
log_error "This script must be run as root. Please sudo or log in as root first."
38+
exit 1
39+
fi
40+
}
41+
42+
# Check if Docker is available
43+
check_docker() {
44+
if ! command -v docker &> /dev/null; then
45+
log_error "Docker is not installed. Please install Docker first."
46+
exit 1
47+
fi
48+
}
49+
50+
# Pull the setup wizard Docker image
51+
pull_image() {
52+
if docker image inspect "$IMAGE_NAME" &> /dev/null; then
53+
log_info "Using cached setup wizard image"
54+
else
55+
log_info "Pulling setup wizard image..."
56+
docker pull "$IMAGE_NAME"
57+
fi
58+
}
59+
60+
# Run the setup wizard container
61+
run_wizard() {
62+
log_info "Starting Discourse Setup Wizard..."
63+
echo
64+
65+
# Clean up any previous signal files
66+
rm -f "$DIR/.wizard_rebuild_needed"
67+
rm -f "$DIR/.wizard_swap_needed"
68+
69+
docker run -it --rm \
70+
--name discourse-setup-wizard \
71+
--network host \
72+
-v "$DIR:/discourse_docker" \
73+
-v /var/run/docker.sock:/var/run/docker.sock \
74+
-e DISCOURSE_DOCKER_DIR=/discourse_docker \
75+
"$IMAGE_NAME" \
76+
"$@" < /dev/tty
77+
78+
local wizard_exit=$?
79+
80+
# Check if wizard signaled that swap creation is needed (exit code 42)
81+
if [[ $wizard_exit -eq 42 ]] && [[ -f "$DIR/.wizard_swap_needed" ]]; then
82+
rm -f "$DIR/.wizard_swap_needed"
83+
if create_swap; then
84+
log_info "Resuming setup wizard..."
85+
echo
86+
# Re-run wizard after creating swap
87+
run_wizard "$@"
88+
return $?
89+
else
90+
log_error "Failed to create swap. Please create swap manually and re-run the wizard."
91+
return 1
92+
fi
93+
fi
94+
95+
# Check if wizard signaled that a rebuild is needed
96+
if [[ -f "$DIR/.wizard_rebuild_needed" ]]; then
97+
local app_name
98+
app_name=$(cat "$DIR/.wizard_rebuild_needed")
99+
rm -f "$DIR/.wizard_rebuild_needed"
100+
101+
if [[ $wizard_exit -eq 0 ]]; then
102+
run_rebuild "$app_name"
103+
fi
104+
fi
105+
106+
return $wizard_exit
107+
}
108+
109+
# Run the launcher rebuild on the host
110+
run_rebuild() {
111+
local app_name="$1"
112+
113+
log_info "Rebuilding $app_name in 5 seconds (Ctrl+C to cancel)..."
114+
sleep 5
115+
116+
log_info "Running: ./launcher rebuild $app_name"
117+
"$DIR/launcher" rebuild "$app_name"
118+
}
119+
120+
# Create swap on the host system
121+
create_swap() {
122+
log_info "Creating 2GB swapfile on host..."
123+
124+
if ! install -o root -g root -m 0600 /dev/null /swapfile; then
125+
log_error "Failed to create swapfile"
126+
return 1
127+
fi
128+
129+
if ! fallocate -l 2G /swapfile; then
130+
log_error "Failed to allocate swap space"
131+
rm -f /swapfile
132+
return 1
133+
fi
134+
135+
if ! mkswap /swapfile; then
136+
log_error "Failed to format swapfile"
137+
rm -f /swapfile
138+
return 1
139+
fi
140+
141+
if ! swapon /swapfile; then
142+
log_error "Failed to enable swap"
143+
rm -f /swapfile
144+
return 1
145+
fi
146+
147+
echo '/swapfile swap swap auto 0 0' >> /etc/fstab
148+
sysctl -w vm.swappiness=10
149+
echo 'vm.swappiness = 10' > /etc/sysctl.d/30-discourse-swap.conf
150+
151+
log_info "Swap created successfully"
152+
return 0
153+
}
154+
155+
# Show help
156+
show_help() {
157+
cat <<EOF
158+
Discourse Setup Wizard (Ruby version)
159+
160+
Usage: $0 [OPTIONS]
161+
162+
Options:
163+
--debug Enable debug mode (skips rebuild)
164+
--skip-rebuild Skip the container rebuild after configuration
165+
--skip-connection-test Skip DNS/port connectivity tests
166+
--help, -h Show this help message
167+
168+
This script runs the interactive setup wizard to configure your Discourse
169+
installation. It will:
170+
171+
1. Check system requirements (memory, disk space, Docker)
172+
2. Prompt for configuration (hostname, email, SMTP settings)
173+
3. Generate/update containers/app.yml
174+
4. Optionally rebuild and start Discourse
175+
176+
For more information, visit: https://github.com/discourse/discourse_docker
177+
178+
EOF
179+
}
180+
181+
# Parse arguments
182+
WIZARD_ARGS=()
183+
184+
while [[ $# -gt 0 ]]; do
185+
case "$1" in
186+
--help|-h)
187+
show_help
188+
exit 0
189+
;;
190+
*)
191+
WIZARD_ARGS+=("$1")
192+
shift
193+
;;
194+
esac
195+
done
196+
197+
# Main
198+
check_root
199+
check_docker
200+
pull_image
201+
run_wizard "${WIZARD_ARGS[@]}"

image/auto_build.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@
122122
tag: "discourse/discourse_dev:build_arm64",
123123
extra_args: "--platform linux/arm64 --build-arg=\"from_tag=build_slim_main_arm64\"",
124124
},
125+
setup_wizard_amd64: {
126+
name: "setup_wizard",
127+
tag: "discourse/setup-wizard:build_amd64",
128+
extra_args: "",
129+
},
130+
setup_wizard_arm64: {
131+
name: "setup_wizard",
132+
tag: "discourse/setup-wizard:build_arm64",
133+
extra_args: "--platform linux/arm64",
134+
},
125135
}
126136

127137
def run(command)

image/setup_wizard/.dockerignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.git
2+
.gitignore
3+
*.md
4+
README*
5+
Dockerfile*
6+
.dockerignore

image/setup_wizard/Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM discourse/ruby:3.4-trixie-slim
2+
3+
# Install system dependencies (curl, nc, lsof needed for network checks)
4+
# Note: No Docker CLI needed - we use docker-api gem to talk to the socket
5+
ARG TARGETARCH
6+
RUN apt-get update && apt-get install -y --no-install-recommends \
7+
curl \
8+
netcat-openbsd \
9+
lsof \
10+
&& curl -fsSL https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${TARGETARCH} -o /usr/local/bin/yq \
11+
&& chmod +x /usr/local/bin/yq \
12+
&& rm -rf /var/lib/apt/lists/*
13+
14+
WORKDIR /wizard
15+
16+
# Copy and install gems
17+
COPY Gemfile Gemfile.lock* ./
18+
RUN bundle config set without 'development test' && bundle install
19+
20+
# Copy the wizard logic
21+
COPY lib/ ./lib/
22+
COPY wizard.rb ./
23+
24+
ENTRYPOINT ["ruby", "wizard.rb"]

image/setup_wizard/Gemfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
5+
ruby ">= 3.2.0"
6+
7+
gem "gum" # Interactive terminal UI (Charm's gum wrapper)
8+
gem "glamour" # Markdown rendering for terminal
9+
gem "docker-api" # Docker socket communication (no CLI needed)
10+
gem "base64" # Required by docker-api, bundled gem in Ruby 3.4+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseSetup
4+
class CLI
5+
attr_reader :debug, :skip_rebuild, :skip_connection_test
6+
7+
def initialize(args = ARGV)
8+
@debug = false
9+
@skip_rebuild = false
10+
@skip_connection_test = false
11+
12+
parse(args)
13+
end
14+
15+
def parse(args)
16+
args.each do |arg|
17+
case arg
18+
when "--debug"
19+
@debug = true
20+
@skip_rebuild = true
21+
when "--skip-rebuild"
22+
@skip_rebuild = true
23+
when "--skip-connection-test"
24+
@skip_connection_test = true
25+
when "--help", "-h"
26+
print_help
27+
exit 0
28+
end
29+
end
30+
end
31+
32+
def print_help
33+
puts <<~HELP
34+
Discourse Setup Wizard
35+
36+
Usage: wizard.rb [OPTIONS]
37+
38+
Options:
39+
--debug Enable debug mode (implies --skip-rebuild)
40+
--skip-rebuild Skip the rebuild step after configuration
41+
--skip-connection-test Skip DNS/port connectivity tests
42+
--help, -h Show this help message
43+
44+
HELP
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)