Refactor configuration and scripts for Archipelago backend and ISO build
- Updated Cargo.toml to remove unnecessary package backtrace optimizations. - Changed default bind host and port in config.rs for broader accessibility. - Renamed state_manager to _state_manager in server.rs for clarity. - Updated user field to _user in PodmanClient and DockerRuntime for consistency. - Modified build-debian-iso.sh to enhance welcome message and backend startup instructions. - Improved archipelago-menu.sh to display backend status and updated Web UI URL. - Enhanced install-to-disk.sh for better package management and user creation during installation.
This commit is contained in:
parent
c9722a34f6
commit
66c823e2fd
136
.cursor/rules/Development-Workflow.mdc
Normal file
136
.cursor/rules/Development-Workflow.mdc
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# Archipelago Development Workflow
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Development happens on Mac (editing in Cursor), with the HP ProDesk running Archipelago as the live test target via SSH.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────┐ SSH/rsync ┌─────────────────────┐
|
||||||
|
│ Mac (Dev Host) │ ──────────────────────────▶│ HP ProDesk (Target)│
|
||||||
|
│ │ │ │
|
||||||
|
│ • Cursor IDE │ │ • Archipelago OS │
|
||||||
|
│ • Source code │ │ • Live testing │
|
||||||
|
│ • ISO builds │ │ • Vue.js dev server│
|
||||||
|
│ │ │ • Rust backend │
|
||||||
|
└─────────────────────┘ └─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Target Machine Setup
|
||||||
|
|
||||||
|
**SSH Access:**
|
||||||
|
```bash
|
||||||
|
ssh archipelago@192.168.1.228
|
||||||
|
# Password: archipelago
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required packages on target (install once):**
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt install -y \
|
||||||
|
nodejs npm \
|
||||||
|
rustc cargo \
|
||||||
|
git \
|
||||||
|
build-essential
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
### Sync Code to Target
|
||||||
|
```bash
|
||||||
|
# From Mac - sync entire project
|
||||||
|
rsync -avz --exclude 'node_modules' --exclude 'target' --exclude 'dist' \
|
||||||
|
/Users/dorian/Projects/archy/ \
|
||||||
|
archipelago@192.168.1.228:/home/archipelago/archy/
|
||||||
|
|
||||||
|
# Or just the frontend
|
||||||
|
rsync -avz --exclude 'node_modules' \
|
||||||
|
/Users/dorian/Projects/archy/neode-ui/ \
|
||||||
|
archipelago@192.168.1.228:/home/archipelago/archy/neode-ui/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Development (Vue.js)
|
||||||
|
```bash
|
||||||
|
# On target via SSH
|
||||||
|
cd ~/archy/neode-ui
|
||||||
|
npm install
|
||||||
|
npm run dev -- --host 0.0.0.0
|
||||||
|
|
||||||
|
# Access from Mac browser: http://192.168.1.228:5173
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend Development (Rust)
|
||||||
|
```bash
|
||||||
|
# On target via SSH
|
||||||
|
cd ~/archy/core
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# Test run
|
||||||
|
./target/release/archipelago
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quick Deploy Script
|
||||||
|
Create `~/deploy.sh` on Mac:
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
TARGET="archipelago@192.168.1.228"
|
||||||
|
PROJECT="/Users/dorian/Projects/archy"
|
||||||
|
|
||||||
|
# Sync code
|
||||||
|
rsync -avz --exclude 'node_modules' --exclude 'target' --exclude 'dist' \
|
||||||
|
"$PROJECT/" "$TARGET:/home/archipelago/archy/"
|
||||||
|
|
||||||
|
# Rebuild on target
|
||||||
|
ssh $TARGET "cd ~/archy/neode-ui && npm install && npm run build"
|
||||||
|
ssh $TARGET "cd ~/archy/core && cargo build --release"
|
||||||
|
|
||||||
|
# Deploy to live system
|
||||||
|
ssh $TARGET "sudo cp ~/archy/core/target/release/archipelago /usr/local/bin/"
|
||||||
|
ssh $TARGET "sudo cp -r ~/archy/neode-ui/dist/* /opt/archipelago/web-ui/"
|
||||||
|
ssh $TARGET "sudo systemctl restart archipelago"
|
||||||
|
|
||||||
|
echo "Deployed! Check http://192.168.1.228"
|
||||||
|
```
|
||||||
|
|
||||||
|
## ISO Builds
|
||||||
|
|
||||||
|
ISO builds still happen on Mac (requires Docker Desktop for creating rootfs):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/dorian/Projects/archy/image-recipe
|
||||||
|
./build-auto-installer-iso.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Docker Desktop is required for:**
|
||||||
|
- Building the Debian rootfs tarball
|
||||||
|
- Creating squashfs overlay modules
|
||||||
|
- Pulling/saving container images for bundling
|
||||||
|
|
||||||
|
## File Locations
|
||||||
|
|
||||||
|
| Component | Mac (Source) | Target (Dev) | Target (Live) |
|
||||||
|
|-----------|--------------|--------------|---------------|
|
||||||
|
| Frontend | `neode-ui/` | `~/archy/neode-ui/` | `/opt/archipelago/web-ui/` |
|
||||||
|
| Backend | `core/` | `~/archy/core/` | `/usr/local/bin/archipelago` |
|
||||||
|
| App manifests | `apps/` | `~/archy/apps/` | `/etc/archipelago/apps/` |
|
||||||
|
|
||||||
|
## What You Can Remove from Mac
|
||||||
|
|
||||||
|
**Keep:**
|
||||||
|
- Docker Desktop (needed for ISO builds)
|
||||||
|
- Node.js/npm (for local editing/linting)
|
||||||
|
- Cursor IDE
|
||||||
|
|
||||||
|
**Can remove:**
|
||||||
|
- Any local test containers
|
||||||
|
- Podman (if installed)
|
||||||
|
- Local development servers (test on target instead)
|
||||||
|
|
||||||
|
## Workflow Summary
|
||||||
|
|
||||||
|
1. **Edit** code in Cursor on Mac
|
||||||
|
2. **Sync** to HP ProDesk with rsync
|
||||||
|
3. **Test** on target (run dev server or deploy to live)
|
||||||
|
4. **Iterate** until working
|
||||||
|
5. **Build ISO** on Mac when ready for distribution
|
||||||
|
6. **Flash & test** ISO on HP ProDesk
|
||||||
@ -19,11 +19,5 @@ opt-level = 0
|
|||||||
[profile.test]
|
[profile.test]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[profile.dev.package.backtrace]
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
[profile.dev.package.sqlx-macros]
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
# Archipelago workspace - no StartOS dependencies
|
# Archipelago workspace - no StartOS dependencies
|
||||||
# All patches removed - we use standard crates.io dependencies
|
# All patches removed - we use standard crates.io dependencies
|
||||||
|
|||||||
@ -142,8 +142,8 @@ impl Default for Config {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data_dir: PathBuf::from("/var/lib/archipelago"),
|
data_dir: PathBuf::from("/var/lib/archipelago"),
|
||||||
bind_host: "127.0.0.1".to_string(),
|
bind_host: "0.0.0.0".to_string(),
|
||||||
bind_port: 5959,
|
bind_port: 5678,
|
||||||
log_level: "info".to_string(),
|
log_level: "info".to_string(),
|
||||||
dev_mode: false,
|
dev_mode: false,
|
||||||
container_runtime: ContainerRuntime::Auto,
|
container_runtime: ContainerRuntime::Auto,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ use tracing::{error, info};
|
|||||||
pub struct Server {
|
pub struct Server {
|
||||||
_config: Config,
|
_config: Config,
|
||||||
api_handler: Arc<ApiHandler>,
|
api_handler: Arc<ApiHandler>,
|
||||||
state_manager: Arc<StateManager>,
|
_state_manager: Arc<StateManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
@ -48,7 +48,7 @@ impl Server {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
_config: config,
|
_config: config,
|
||||||
api_handler,
|
api_handler,
|
||||||
state_manager,
|
_state_manager: state_manager,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -49,14 +49,14 @@ impl From<&str> for ContainerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct PodmanClient {
|
pub struct PodmanClient {
|
||||||
user: String,
|
_user: String,
|
||||||
rootless: bool,
|
rootless: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PodmanClient {
|
impl PodmanClient {
|
||||||
pub fn new(user: String) -> Self {
|
pub fn new(user: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user,
|
_user: user,
|
||||||
rootless: true,
|
rootless: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,12 +82,12 @@ impl ContainerRuntime for PodmanRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct DockerRuntime {
|
pub struct DockerRuntime {
|
||||||
user: String,
|
_user: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DockerRuntime {
|
impl DockerRuntime {
|
||||||
pub fn new(user: String) -> Self {
|
pub fn new(user: String) -> Self {
|
||||||
Self { user }
|
Self { _user: user }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn docker_async(&self) -> TokioCommand {
|
fn docker_async(&self) -> TokioCommand {
|
||||||
|
|||||||
@ -96,12 +96,24 @@ main_menu() {
|
|||||||
show_banner
|
show_banner
|
||||||
show_status
|
show_status
|
||||||
|
|
||||||
# Show Web UI URL
|
# Show Web UI URL prominently
|
||||||
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
[ -z "$IP" ] && IP=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -1)
|
||||||
|
|
||||||
|
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||||
if [ -n "$IP" ]; then
|
if [ -n "$IP" ]; then
|
||||||
echo " 🌐 Web UI: http://$IP"
|
# Check if backend is running
|
||||||
echo ""
|
if pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||||
|
echo " │ 🌐 Web UI: http://$IP:5678 (running) │"
|
||||||
|
else
|
||||||
|
echo " │ 🌐 Web UI: http://$IP:5678 (not started) │"
|
||||||
|
fi
|
||||||
|
echo " │ 📡 SSH: ssh user@$IP (password: archipelago) │"
|
||||||
|
else
|
||||||
|
echo " │ 🌐 Web UI: (no network) │"
|
||||||
fi
|
fi
|
||||||
|
echo " └─────────────────────────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
|
|
||||||
echo " Main Menu:"
|
echo " Main Menu:"
|
||||||
echo " ─────────────────────────────────────────────────────────────"
|
echo " ─────────────────────────────────────────────────────────────"
|
||||||
@ -122,17 +134,34 @@ main_menu() {
|
|||||||
|
|
||||||
case $choice in
|
case $choice in
|
||||||
w|W)
|
w|W)
|
||||||
if [ -f "$SCRIPT_DIR/start-web-ui.sh" ]; then
|
echo ""
|
||||||
sudo bash "$SCRIPT_DIR/start-web-ui.sh" 80
|
# Start the real backend on port 5678
|
||||||
else
|
if command -v archipelago >/dev/null 2>&1; then
|
||||||
# Try to find and start web UI
|
if pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||||
for path in /opt/archipelago/scripts /run/live/medium/archipelago/scripts; do
|
echo " ✅ Archipelago backend already running on port 5678"
|
||||||
if [ -f "$path/start-web-ui.sh" ]; then
|
else
|
||||||
sudo bash "$path/start-web-ui.sh" 80
|
echo " 🚀 Starting Archipelago backend on port 5678..."
|
||||||
break
|
nohup archipelago >/tmp/archipelago.log 2>&1 &
|
||||||
|
sleep 2
|
||||||
|
if pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||||
|
echo " ✅ Backend started!"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Failed to start backend. Check /tmp/archipelago.log"
|
||||||
fi
|
fi
|
||||||
done
|
fi
|
||||||
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
echo ""
|
||||||
|
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||||
|
echo " │ 🌐 Open in browser: http://$IP:5678 │"
|
||||||
|
echo " └─────────────────────────────────────────────────────────────┘"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Archipelago binary not found at /usr/local/bin/archipelago"
|
||||||
|
echo ""
|
||||||
|
echo " Try running:"
|
||||||
|
echo " sudo cp /run/live/medium/archipelago/bin/archipelago /usr/local/bin/"
|
||||||
fi
|
fi
|
||||||
|
echo ""
|
||||||
|
read -p " Press Enter to continue..."
|
||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
if [ -f "$SCRIPT_DIR/install-to-disk.sh" ]; then
|
if [ -f "$SCRIPT_DIR/install-to-disk.sh" ]; then
|
||||||
|
|||||||
@ -117,10 +117,13 @@ fi
|
|||||||
|
|
||||||
echo "⚙️ Configuring system..."
|
echo "⚙️ Configuring system..."
|
||||||
|
|
||||||
# Mount virtual filesystems
|
# Mount virtual filesystems for chroot
|
||||||
mount --bind /dev /mnt/archipelago/dev
|
mount --bind /dev /mnt/archipelago/dev
|
||||||
|
mount --bind /dev/pts /mnt/archipelago/dev/pts
|
||||||
mount --bind /proc /mnt/archipelago/proc
|
mount --bind /proc /mnt/archipelago/proc
|
||||||
mount --bind /sys /mnt/archipelago/sys
|
mount --bind /sys /mnt/archipelago/sys
|
||||||
|
mount --bind /sys/firmware/efi/efivars /mnt/archipelago/sys/firmware/efi/efivars 2>/dev/null || true
|
||||||
|
mount --bind /run /mnt/archipelago/run
|
||||||
|
|
||||||
# Create fstab
|
# Create fstab
|
||||||
cat > /mnt/archipelago/etc/fstab <<EOF
|
cat > /mnt/archipelago/etc/fstab <<EOF
|
||||||
@ -140,56 +143,64 @@ cat > /mnt/archipelago/etc/hosts <<EOF
|
|||||||
::1 localhost ip6-localhost ip6-loopback
|
::1 localhost ip6-localhost ip6-loopback
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Install bootloader and essential packages
|
# Install bootloader and essential packages in chroot
|
||||||
chroot /mnt/archipelago /bin/bash <<'CHROOT_EOF'
|
echo "📦 Configuring package sources..."
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
# Add sources
|
# Create sources.list
|
||||||
cat > /etc/apt/sources.list <<EOF
|
cat > /mnt/archipelago/etc/apt/sources.list <<EOF
|
||||||
deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
|
deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
|
||||||
deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
|
deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
|
||||||
deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
|
deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
apt-get update
|
echo "📥 Updating package lists..."
|
||||||
|
chroot /mnt/archipelago apt-get update
|
||||||
|
|
||||||
# Install kernel, bootloader, and essentials
|
echo "📦 Installing kernel and bootloader..."
|
||||||
apt-get install -y \
|
chroot /mnt/archipelago apt-get install -y linux-image-amd64 grub-efi-amd64 grub-efi-amd64-signed shim-signed
|
||||||
linux-image-amd64 \
|
|
||||||
grub-efi-amd64 \
|
echo "📦 Installing essential packages..."
|
||||||
|
chroot /mnt/archipelago apt-get install -y \
|
||||||
sudo \
|
sudo \
|
||||||
networkmanager \
|
networkmanager \
|
||||||
openssh-server \
|
openssh-server \
|
||||||
curl \
|
curl \
|
||||||
wget \
|
wget \
|
||||||
htop \
|
htop \
|
||||||
vim \
|
vim-tiny \
|
||||||
git \
|
ca-certificates
|
||||||
podman \
|
|
||||||
podman-compose \
|
|
||||||
buildah \
|
|
||||||
skopeo
|
|
||||||
|
|
||||||
# Install GRUB
|
echo "📦 Installing container tools..."
|
||||||
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago
|
chroot /mnt/archipelago apt-get install -y podman || echo "⚠️ Podman not available in base repos, will use containers.io later"
|
||||||
update-grub
|
|
||||||
|
|
||||||
# Create archipelago user
|
echo "🔧 Installing GRUB bootloader..."
|
||||||
useradd -m -s /bin/bash -G sudo,podman archipelago
|
# Need to run grub-install inside chroot with proper environment
|
||||||
echo "archipelago:archipelago" | chpasswd
|
chroot /mnt/archipelago /bin/bash -c "
|
||||||
|
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago --recheck 2>&1 || \
|
||||||
|
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago --removable 2>&1 || \
|
||||||
|
echo '⚠️ GRUB install had issues, trying alternative...'
|
||||||
|
"
|
||||||
|
chroot /mnt/archipelago update-grub
|
||||||
|
|
||||||
# Enable services
|
echo "👤 Creating archipelago user..."
|
||||||
systemctl enable NetworkManager
|
# Create user first, then add to groups that exist
|
||||||
systemctl enable ssh
|
chroot /mnt/archipelago useradd -m -s /bin/bash archipelago || echo "User may already exist"
|
||||||
systemctl enable podman
|
chroot /mnt/archipelago usermod -aG sudo archipelago || true
|
||||||
|
chroot /mnt/archipelago usermod -aG podman archipelago 2>/dev/null || true
|
||||||
|
|
||||||
# Create Archipelago directories
|
# Set password using chpasswd
|
||||||
mkdir -p /var/lib/archipelago/{data,config,containers}
|
echo "archipelago:archipelago" | chroot /mnt/archipelago chpasswd
|
||||||
mkdir -p /etc/archipelago
|
|
||||||
chown -R archipelago:archipelago /var/lib/archipelago
|
|
||||||
|
|
||||||
echo "✅ Base system installed"
|
echo "⚙️ Enabling services..."
|
||||||
CHROOT_EOF
|
chroot /mnt/archipelago systemctl enable NetworkManager || true
|
||||||
|
chroot /mnt/archipelago systemctl enable ssh || chroot /mnt/archipelago systemctl enable sshd || true
|
||||||
|
|
||||||
|
echo "📁 Creating Archipelago directories..."
|
||||||
|
chroot /mnt/archipelago mkdir -p /var/lib/archipelago/{data,config,containers}
|
||||||
|
chroot /mnt/archipelago mkdir -p /etc/archipelago
|
||||||
|
chroot /mnt/archipelago chown -R archipelago:archipelago /var/lib/archipelago
|
||||||
|
|
||||||
|
echo "✅ Base system configured"
|
||||||
|
|
||||||
# Copy Archipelago files
|
# Copy Archipelago files
|
||||||
echo "📋 Installing Archipelago components..."
|
echo "📋 Installing Archipelago components..."
|
||||||
@ -228,7 +239,7 @@ if [ -n "$BOOT_MEDIA" ]; then
|
|||||||
cp -r "$BOOT_MEDIA/archipelago/apps" /mnt/archipelago/etc/archipelago/
|
cp -r "$BOOT_MEDIA/archipelago/apps" /mnt/archipelago/etc/archipelago/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create profile.d script for auto-menu on login
|
# Create profile.d script for welcome message on login
|
||||||
mkdir -p /mnt/archipelago/etc/profile.d
|
mkdir -p /mnt/archipelago/etc/profile.d
|
||||||
cat > /mnt/archipelago/etc/profile.d/archipelago.sh <<'PROFILE_EOF'
|
cat > /mnt/archipelago/etc/profile.d/archipelago.sh <<'PROFILE_EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
@ -237,26 +248,60 @@ if [ -t 0 ] && [ -z "$ARCHIPELAGO_WELCOMED" ]; then
|
|||||||
export ARCHIPELAGO_WELCOMED=1
|
export ARCHIPELAGO_WELCOMED=1
|
||||||
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
echo ""
|
echo ""
|
||||||
echo " 🏝️ Welcome to Archipelago Bitcoin Node OS"
|
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
if [ -n "$IP" ]; then
|
if [ -n "$IP" ]; then
|
||||||
echo " Web UI: http://$IP"
|
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||||
|
echo " │ 🌐 Web UI: http://$IP:5678 │"
|
||||||
|
echo " │ 📡 SSH: ssh archipelago@$IP │"
|
||||||
|
echo " └─────────────────────────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
fi
|
fi
|
||||||
echo " Menu: Type 'archipelago' to open setup menu"
|
echo " Commands:"
|
||||||
|
echo " archipelago - Start backend server"
|
||||||
|
echo " archipelago-menu - Open setup menu"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
PROFILE_EOF
|
PROFILE_EOF
|
||||||
chmod +x /mnt/archipelago/etc/profile.d/archipelago.sh
|
chmod +x /mnt/archipelago/etc/profile.d/archipelago.sh
|
||||||
|
|
||||||
|
# Create systemd service to auto-start archipelago backend
|
||||||
|
mkdir -p /mnt/archipelago/etc/systemd/system
|
||||||
|
cat > /mnt/archipelago/etc/systemd/system/archipelago.service <<'SERVICE_EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Archipelago Backend Server
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=archipelago
|
||||||
|
ExecStart=/usr/local/bin/archipelago
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SERVICE_EOF
|
||||||
|
|
||||||
|
# Enable the service
|
||||||
|
chroot /mnt/archipelago systemctl enable archipelago.service 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🧹 Cleaning up..."
|
echo "🧹 Cleaning up..."
|
||||||
|
|
||||||
# Unmount
|
# Unmount in reverse order
|
||||||
umount /mnt/archipelago/dev
|
sync
|
||||||
umount /mnt/archipelago/proc
|
umount /mnt/archipelago/run 2>/dev/null || true
|
||||||
umount /mnt/archipelago/sys
|
umount /mnt/archipelago/sys/firmware/efi/efivars 2>/dev/null || true
|
||||||
umount /mnt/archipelago/boot/efi
|
umount /mnt/archipelago/sys 2>/dev/null || true
|
||||||
umount /mnt/archipelago
|
umount /mnt/archipelago/proc 2>/dev/null || true
|
||||||
|
umount /mnt/archipelago/dev/pts 2>/dev/null || true
|
||||||
|
umount /mnt/archipelago/dev 2>/dev/null || true
|
||||||
|
umount /mnt/archipelago/boot/efi 2>/dev/null || true
|
||||||
|
umount /mnt/archipelago 2>/dev/null || true
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
|||||||
962
image-recipe/build-auto-installer-iso.sh
Executable file
962
image-recipe/build-auto-installer-iso.sh
Executable file
@ -0,0 +1,962 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build Archipelago Auto-Installer ISO (StartOS-like)
|
||||||
|
#
|
||||||
|
# This creates an ISO that automatically installs to the internal disk
|
||||||
|
# with minimal user interaction - similar to StartOS experience.
|
||||||
|
#
|
||||||
|
# Features:
|
||||||
|
# - Pre-built root filesystem (no network needed during install)
|
||||||
|
# - Auto-detects internal disk (skips USB boot drive)
|
||||||
|
# - Automatic installation with progress display
|
||||||
|
# - Boots directly to web UI after install
|
||||||
|
#
|
||||||
|
# Usage: ./build-auto-installer-iso.sh
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
WORK_DIR="$SCRIPT_DIR/build/auto-installer"
|
||||||
|
OUTPUT_DIR="$SCRIPT_DIR/results"
|
||||||
|
ROOTFS_DIR="$WORK_DIR/rootfs"
|
||||||
|
INSTALLER_DIR="$WORK_DIR/installer"
|
||||||
|
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ Building Archipelago Auto-Installer ISO (StartOS-like) ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check for required tools
|
||||||
|
check_tools() {
|
||||||
|
local missing=""
|
||||||
|
for tool in docker xorriso; do
|
||||||
|
if ! command -v $tool >/dev/null 2>&1; then
|
||||||
|
missing="$missing $tool"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -n "$missing" ]; then
|
||||||
|
echo "❌ Missing required tools:$missing"
|
||||||
|
echo " Install with: brew install$missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_tools
|
||||||
|
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 1: Build complete root filesystem using Docker
|
||||||
|
# =============================================================================
|
||||||
|
echo "📦 Step 1: Building root filesystem..."
|
||||||
|
|
||||||
|
ROOTFS_TAR="$WORK_DIR/archipelago-rootfs.tar"
|
||||||
|
|
||||||
|
if [ ! -f "$ROOTFS_TAR" ] || [ "$1" == "--rebuild" ]; then
|
||||||
|
echo " Using Docker to create Debian root filesystem..."
|
||||||
|
|
||||||
|
# Create a Dockerfile for building the rootfs
|
||||||
|
cat > "$WORK_DIR/Dockerfile.rootfs" <<'DOCKERFILE'
|
||||||
|
FROM debian:bookworm
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install all packages we need including nginx and podman
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
linux-image-amd64 \
|
||||||
|
grub-efi-amd64 \
|
||||||
|
grub-efi-amd64-signed \
|
||||||
|
shim-signed \
|
||||||
|
systemd \
|
||||||
|
systemd-sysv \
|
||||||
|
dbus \
|
||||||
|
sudo \
|
||||||
|
network-manager \
|
||||||
|
openssh-server \
|
||||||
|
nginx \
|
||||||
|
podman \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
htop \
|
||||||
|
vim-tiny \
|
||||||
|
ca-certificates \
|
||||||
|
locales \
|
||||||
|
console-setup \
|
||||||
|
keyboard-configuration \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Configure locale
|
||||||
|
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
|
||||||
|
|
||||||
|
# Create archipelago user
|
||||||
|
RUN useradd -m -s /bin/bash -G sudo archipelago && \
|
||||||
|
echo "archipelago:archipelago" | chpasswd && \
|
||||||
|
echo "archipelago ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/archipelago
|
||||||
|
|
||||||
|
# Set hostname
|
||||||
|
RUN echo "archipelago" > /etc/hostname
|
||||||
|
|
||||||
|
# Configure SSH
|
||||||
|
RUN mkdir -p /etc/ssh && \
|
||||||
|
sed -i 's/#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config || true && \
|
||||||
|
sed -i 's/#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config || true
|
||||||
|
|
||||||
|
# Configure nginx for Archipelago
|
||||||
|
RUN rm -f /etc/nginx/sites-enabled/default
|
||||||
|
COPY nginx-archipelago.conf /etc/nginx/sites-available/archipelago
|
||||||
|
RUN ln -sf /etc/nginx/sites-available/archipelago /etc/nginx/sites-enabled/archipelago
|
||||||
|
|
||||||
|
# Create archipelago systemd service
|
||||||
|
COPY archipelago.service /etc/systemd/system/archipelago.service
|
||||||
|
|
||||||
|
# Enable services
|
||||||
|
RUN systemctl enable NetworkManager || true && \
|
||||||
|
systemctl enable ssh || true && \
|
||||||
|
systemctl enable nginx || true && \
|
||||||
|
systemctl enable archipelago || true
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
RUN mkdir -p /var/lib/archipelago/{data,config,containers} && \
|
||||||
|
mkdir -p /etc/archipelago && \
|
||||||
|
mkdir -p /opt/archipelago/{bin,scripts,web-ui} && \
|
||||||
|
chown -R archipelago:archipelago /var/lib/archipelago /opt/archipelago
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
RUN apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
DOCKERFILE
|
||||||
|
|
||||||
|
# Create nginx config for the Docker build
|
||||||
|
cat > "$WORK_DIR/nginx-archipelago.conf" <<'NGINXCONF'
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /opt/archipelago/web-ui;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Serve static files (Vue.js SPA)
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API requests to backend
|
||||||
|
location /rpc/ {
|
||||||
|
proxy_pass http://127.0.0.1:5678;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy WebSocket
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://127.0.0.1:5678;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NGINXCONF
|
||||||
|
|
||||||
|
# Create systemd service for the Docker build
|
||||||
|
cat > "$WORK_DIR/archipelago.service" <<'SYSTEMDSERVICE'
|
||||||
|
[Unit]
|
||||||
|
Description=Archipelago Backend
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=archipelago
|
||||||
|
Environment="ARCHIPELAGO_BIND=0.0.0.0:5678"
|
||||||
|
Environment="ARCHIPELAGO_DEV_MODE=true"
|
||||||
|
ExecStart=/usr/local/bin/archipelago
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SYSTEMDSERVICE
|
||||||
|
|
||||||
|
echo " Building Docker image (this may take a few minutes)..."
|
||||||
|
docker build --platform linux/amd64 -t archipelago-rootfs -f "$WORK_DIR/Dockerfile.rootfs" "$WORK_DIR"
|
||||||
|
|
||||||
|
echo " Exporting filesystem..."
|
||||||
|
docker create --platform linux/amd64 --name archipelago-rootfs-tmp archipelago-rootfs
|
||||||
|
docker export archipelago-rootfs-tmp > "$ROOTFS_TAR"
|
||||||
|
docker rm archipelago-rootfs-tmp
|
||||||
|
|
||||||
|
echo "✅ Root filesystem created: $(du -h "$ROOTFS_TAR" | cut -f1)"
|
||||||
|
else
|
||||||
|
echo "✅ Using cached root filesystem: $(du -h "$ROOTFS_TAR" | cut -f1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 2: Create installer environment
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "📦 Step 2: Creating installer environment..."
|
||||||
|
|
||||||
|
# Download Debian Live as our installer base
|
||||||
|
BASE_ISO="$WORK_DIR/debian-live-installer.iso"
|
||||||
|
if [ ! -f "$BASE_ISO" ] || [ $(stat -f%z "$BASE_ISO" 2>/dev/null || echo 0) -lt 100000000 ]; then
|
||||||
|
echo " Downloading Debian Live base..."
|
||||||
|
rm -f "$BASE_ISO"
|
||||||
|
curl -L -o "$BASE_ISO" \
|
||||||
|
"https://sourceforge.net/projects/debian-live-respin-iso/files/standard/live-image-debian12.11-standard-20250522-amd64.hybrid.iso/download"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Extracting installer base..."
|
||||||
|
INSTALLER_ISO="$WORK_DIR/installer-iso"
|
||||||
|
rm -rf "$INSTALLER_ISO"
|
||||||
|
mkdir -p "$INSTALLER_ISO"
|
||||||
|
cd "$INSTALLER_ISO"
|
||||||
|
7z x -y "$BASE_ISO" >/dev/null 2>&1 || 7z x -y "$BASE_ISO"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 3: Add Archipelago components
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "📦 Step 3: Adding Archipelago components..."
|
||||||
|
|
||||||
|
ARCH_DIR="$INSTALLER_ISO/archipelago"
|
||||||
|
mkdir -p "$ARCH_DIR"
|
||||||
|
mkdir -p "$ARCH_DIR/bin"
|
||||||
|
mkdir -p "$ARCH_DIR/scripts"
|
||||||
|
|
||||||
|
# Copy the pre-built rootfs
|
||||||
|
echo " Including root filesystem..."
|
||||||
|
cp "$ROOTFS_TAR" "$ARCH_DIR/rootfs.tar"
|
||||||
|
|
||||||
|
# Copy backend binary
|
||||||
|
if [ -f "$SCRIPT_DIR/../core/target/release/archipelago" ]; then
|
||||||
|
echo " Including backend binary..."
|
||||||
|
cp "$SCRIPT_DIR/../core/target/release/archipelago" "$ARCH_DIR/bin/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy web UI (check both possible locations)
|
||||||
|
if [ -d "$SCRIPT_DIR/../web/dist/neode-ui" ]; then
|
||||||
|
echo " Including web UI from web/dist/neode-ui..."
|
||||||
|
cp -r "$SCRIPT_DIR/../web/dist/neode-ui" "$ARCH_DIR/web-ui"
|
||||||
|
elif [ -d "$SCRIPT_DIR/../neode-ui/dist" ]; then
|
||||||
|
echo " Including web UI from neode-ui/dist..."
|
||||||
|
cp -r "$SCRIPT_DIR/../neode-ui/dist" "$ARCH_DIR/web-ui"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Web UI not found - build it first with: cd neode-ui && npm run build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy app manifests
|
||||||
|
if [ -d "$SCRIPT_DIR/../apps" ]; then
|
||||||
|
echo " Including app manifests..."
|
||||||
|
cp -r "$SCRIPT_DIR/../apps" "$ARCH_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 3b: Bundle container images for offline installation
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "📦 Step 3b: Bundling container images for offline use..."
|
||||||
|
|
||||||
|
IMAGES_DIR="$ARCH_DIR/container-images"
|
||||||
|
mkdir -p "$IMAGES_DIR"
|
||||||
|
|
||||||
|
# Define images to bundle (space-separated: image filename)
|
||||||
|
CONTAINER_IMAGES="
|
||||||
|
bitcoinknots/bitcoin:29 bitcoin-knots.tar
|
||||||
|
lightninglabs/lnd:v0.18.4-beta lnd.tar
|
||||||
|
ghcr.io/home-assistant/home-assistant:stable homeassistant.tar
|
||||||
|
"
|
||||||
|
|
||||||
|
# Pull and save each image (force AMD64 for x86_64 target)
|
||||||
|
echo "$CONTAINER_IMAGES" | while read -r image filename; do
|
||||||
|
[ -z "$image" ] && continue
|
||||||
|
tarpath="$IMAGES_DIR/$filename"
|
||||||
|
|
||||||
|
if [ -f "$tarpath" ]; then
|
||||||
|
echo " ✅ Using cached: $filename"
|
||||||
|
else
|
||||||
|
echo " Pulling $image (linux/amd64)..."
|
||||||
|
if docker pull --platform linux/amd64 "$image"; then
|
||||||
|
echo " Saving $filename..."
|
||||||
|
docker save "$image" -o "$tarpath"
|
||||||
|
echo " ✅ Saved: $(du -h "$tarpath" | cut -f1)"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Failed to pull $image - skipping"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create first-boot service to load images into Podman
|
||||||
|
echo " Creating first-boot image loader service..."
|
||||||
|
cat > "$WORK_DIR/archipelago-load-images.service" <<'LOADSERVICE'
|
||||||
|
[Unit]
|
||||||
|
Description=Load Archipelago Container Images
|
||||||
|
After=network.target podman.service
|
||||||
|
ConditionPathExists=/opt/archipelago/container-images
|
||||||
|
ConditionPathExists=!/var/lib/archipelago/.images-loaded
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/opt/archipelago/scripts/load-container-images.sh
|
||||||
|
ExecStartPost=/usr/bin/touch /var/lib/archipelago/.images-loaded
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
LOADSERVICE
|
||||||
|
|
||||||
|
cat > "$WORK_DIR/load-container-images.sh" <<'LOADSCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
# Load pre-bundled container images into Podman
|
||||||
|
|
||||||
|
IMAGES_DIR="/opt/archipelago/container-images"
|
||||||
|
LOG_FILE="/var/log/archipelago-images.log"
|
||||||
|
|
||||||
|
echo "$(date): Starting container image load" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
if [ ! -d "$IMAGES_DIR" ]; then
|
||||||
|
echo "$(date): No images directory found" >> "$LOG_FILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tarfile in "$IMAGES_DIR"/*.tar; do
|
||||||
|
if [ -f "$tarfile" ]; then
|
||||||
|
echo "$(date): Loading $(basename "$tarfile")..." >> "$LOG_FILE"
|
||||||
|
podman load -i "$tarfile" >> "$LOG_FILE" 2>&1 && \
|
||||||
|
echo "$(date): Successfully loaded $(basename "$tarfile")" >> "$LOG_FILE" || \
|
||||||
|
echo "$(date): Failed to load $(basename "$tarfile")" >> "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "$(date): Container image load complete" >> "$LOG_FILE"
|
||||||
|
echo "$(date): Available images:" >> "$LOG_FILE"
|
||||||
|
podman images >> "$LOG_FILE" 2>&1
|
||||||
|
LOADSCRIPT
|
||||||
|
|
||||||
|
chmod +x "$WORK_DIR/load-container-images.sh"
|
||||||
|
|
||||||
|
# Copy scripts to ISO
|
||||||
|
mkdir -p "$ARCH_DIR/scripts"
|
||||||
|
cp "$WORK_DIR/load-container-images.sh" "$ARCH_DIR/scripts/"
|
||||||
|
cp "$WORK_DIR/archipelago-load-images.service" "$ARCH_DIR/scripts/"
|
||||||
|
|
||||||
|
echo " ✅ Container images bundled"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 4: Create auto-installer script
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "📦 Step 4: Creating auto-installer..."
|
||||||
|
|
||||||
|
cat > "$ARCH_DIR/auto-install.sh" <<'INSTALLER_SCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Archipelago Auto-Installer
|
||||||
|
# Automatically installs to internal disk (StartOS-like experience)
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BLUE}║ ║${NC}"
|
||||||
|
echo -e "${BLUE}║ ${GREEN}🏝️ ARCHIPELAGO BITCOIN NODE OS${BLUE} ║${NC}"
|
||||||
|
echo -e "${BLUE}║ ║${NC}"
|
||||||
|
echo -e "${BLUE}║ ${NC}Automatic Installation${BLUE} ║${NC}"
|
||||||
|
echo -e "${BLUE}║ ║${NC}"
|
||||||
|
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check required tools are present (should be bundled in ISO)
|
||||||
|
echo -e "${YELLOW}🔧 Checking installer tools...${NC}"
|
||||||
|
MISSING=""
|
||||||
|
command -v parted >/dev/null 2>&1 || MISSING="parted $MISSING"
|
||||||
|
command -v mkfs.vfat >/dev/null 2>&1 || MISSING="mkfs.vfat $MISSING"
|
||||||
|
command -v mkfs.ext4 >/dev/null 2>&1 || MISSING="mkfs.ext4 $MISSING"
|
||||||
|
|
||||||
|
if [ -n "$MISSING" ]; then
|
||||||
|
echo " Missing tools: $MISSING"
|
||||||
|
echo " Attempting to install (requires network)..."
|
||||||
|
if apt-get update -qq >/dev/null 2>&1; then
|
||||||
|
apt-get install -y -qq parted dosfstools e2fsprogs >/dev/null 2>&1 && echo " ✅ Tools installed" || {
|
||||||
|
echo -e "${RED}❌ Failed to install required tools. No network?${NC}"
|
||||||
|
echo " Required: parted, mkfs.vfat, mkfs.ext4"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo -e "${RED}❌ No network available and tools not bundled.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ✅ All tools present"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find boot media
|
||||||
|
BOOT_MEDIA=""
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom; do
|
||||||
|
if [ -d "$dev/archipelago" ]; then
|
||||||
|
BOOT_MEDIA="$dev"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$BOOT_MEDIA" ]; then
|
||||||
|
echo -e "${RED}❌ Boot media not found${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ROOTFS_TAR="$BOOT_MEDIA/archipelago/rootfs.tar"
|
||||||
|
if [ ! -f "$ROOTFS_TAR" ]; then
|
||||||
|
echo -e "${RED}❌ Root filesystem not found: $ROOTFS_TAR${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the boot USB device to exclude it
|
||||||
|
BOOT_DEV=$(findmnt -n -o SOURCE "$BOOT_MEDIA" 2>/dev/null | sed 's/[0-9]*$//' | sed 's/p[0-9]*$//')
|
||||||
|
BOOT_DEV_NAME=$(basename "$BOOT_DEV" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📋 Detecting disks...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find internal disk (prefer NVMe, then SATA, skip USB)
|
||||||
|
TARGET_DISK=""
|
||||||
|
TARGET_SIZE=""
|
||||||
|
|
||||||
|
# Check NVMe drives first
|
||||||
|
for disk in /dev/nvme*n1; do
|
||||||
|
if [ -b "$disk" ]; then
|
||||||
|
disk_name=$(basename "$disk")
|
||||||
|
if [ "$disk_name" != "$BOOT_DEV_NAME" ]; then
|
||||||
|
size=$(lsblk -dn -o SIZE "$disk" 2>/dev/null)
|
||||||
|
model=$(cat /sys/block/$disk_name/device/model 2>/dev/null || echo "NVMe SSD")
|
||||||
|
echo " Found: $disk ($size) - $model"
|
||||||
|
TARGET_DISK="$disk"
|
||||||
|
TARGET_SIZE="$size"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If no NVMe, check SATA drives
|
||||||
|
if [ -z "$TARGET_DISK" ]; then
|
||||||
|
for disk in /dev/sd[a-z]; do
|
||||||
|
if [ -b "$disk" ]; then
|
||||||
|
disk_name=$(basename "$disk")
|
||||||
|
if [ "$disk_name" != "$BOOT_DEV_NAME" ]; then
|
||||||
|
# Skip USB drives (check removable flag)
|
||||||
|
removable=$(cat /sys/block/$disk_name/removable 2>/dev/null || echo "0")
|
||||||
|
if [ "$removable" = "0" ]; then
|
||||||
|
size=$(lsblk -dn -o SIZE "$disk" 2>/dev/null)
|
||||||
|
model=$(cat /sys/block/$disk_name/device/model 2>/dev/null || echo "SATA Drive")
|
||||||
|
echo " Found: $disk ($size) - $model"
|
||||||
|
TARGET_DISK="$disk"
|
||||||
|
TARGET_SIZE="$size"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$TARGET_DISK" ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}❌ No suitable internal disk found${NC}"
|
||||||
|
echo " Please ensure an internal drive is connected."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✅ Target disk: $TARGET_DISK ($TARGET_SIZE)${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}⚠️ WARNING: ALL DATA ON $TARGET_DISK WILL BE ERASED${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Press Enter to install Archipelago, or Ctrl+C to cancel..."
|
||||||
|
read
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}🔧 Installing Archipelago...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Unmount any existing partitions
|
||||||
|
umount ${TARGET_DISK}* 2>/dev/null || true
|
||||||
|
umount ${TARGET_DISK}p* 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create partition table
|
||||||
|
echo " [1/6] Creating partitions..."
|
||||||
|
parted -s "$TARGET_DISK" mklabel gpt
|
||||||
|
parted -s "$TARGET_DISK" mkpart primary fat32 1MiB 513MiB
|
||||||
|
parted -s "$TARGET_DISK" set 1 esp on
|
||||||
|
parted -s "$TARGET_DISK" mkpart primary ext4 513MiB 100%
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Determine partition names
|
||||||
|
if [[ "$TARGET_DISK" == *nvme* ]]; then
|
||||||
|
EFI_PART="${TARGET_DISK}p1"
|
||||||
|
ROOT_PART="${TARGET_DISK}p2"
|
||||||
|
else
|
||||||
|
EFI_PART="${TARGET_DISK}1"
|
||||||
|
ROOT_PART="${TARGET_DISK}2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format partitions
|
||||||
|
echo " [2/6] Formatting partitions..."
|
||||||
|
mkfs.vfat -F32 -n EFI "$EFI_PART"
|
||||||
|
mkfs.ext4 -F -L archipelago "$ROOT_PART"
|
||||||
|
|
||||||
|
# Mount
|
||||||
|
echo " [3/6] Mounting filesystems..."
|
||||||
|
mkdir -p /mnt/target
|
||||||
|
mount "$ROOT_PART" /mnt/target
|
||||||
|
mkdir -p /mnt/target/boot/efi
|
||||||
|
mount "$EFI_PART" /mnt/target/boot/efi
|
||||||
|
|
||||||
|
# Extract rootfs
|
||||||
|
echo " [4/6] Installing system (this may take a few minutes)..."
|
||||||
|
tar -xf "$ROOTFS_TAR" -C /mnt/target
|
||||||
|
|
||||||
|
# Create fstab
|
||||||
|
echo " [5/6] Configuring system..."
|
||||||
|
cat > /mnt/target/etc/fstab <<EOF
|
||||||
|
# Archipelago Bitcoin Node OS
|
||||||
|
UUID=$(blkid -s UUID -o value "$ROOT_PART") / ext4 errors=remount-ro 0 1
|
||||||
|
UUID=$(blkid -s UUID -o value "$EFI_PART") /boot/efi vfat umask=0077 0 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Configure hostname
|
||||||
|
echo "archipelago" > /mnt/target/etc/hostname
|
||||||
|
cat > /mnt/target/etc/hosts <<EOF
|
||||||
|
127.0.0.1 localhost
|
||||||
|
127.0.1.1 archipelago
|
||||||
|
::1 localhost ip6-localhost ip6-loopback
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Copy Archipelago binaries and files
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/bin" ]; then
|
||||||
|
cp -r "$BOOT_MEDIA/archipelago/bin/"* /mnt/target/usr/local/bin/ 2>/dev/null || true
|
||||||
|
chmod +x /mnt/target/usr/local/bin/* 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/web-ui" ]; then
|
||||||
|
cp -r "$BOOT_MEDIA/archipelago/web-ui" /mnt/target/opt/archipelago/
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/apps" ]; then
|
||||||
|
cp -r "$BOOT_MEDIA/archipelago/apps" /mnt/target/etc/archipelago/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy pre-bundled container images
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/container-images" ]; then
|
||||||
|
echo " Copying container images (this may take a moment)..."
|
||||||
|
mkdir -p /mnt/target/opt/archipelago/container-images
|
||||||
|
cp -r "$BOOT_MEDIA/archipelago/container-images/"*.tar /mnt/target/opt/archipelago/container-images/ 2>/dev/null || true
|
||||||
|
|
||||||
|
# Copy first-boot loader script and service
|
||||||
|
mkdir -p /mnt/target/opt/archipelago/scripts
|
||||||
|
if [ -f "$BOOT_MEDIA/archipelago/scripts/load-container-images.sh" ]; then
|
||||||
|
cp "$BOOT_MEDIA/archipelago/scripts/load-container-images.sh" /mnt/target/opt/archipelago/scripts/
|
||||||
|
chmod +x /mnt/target/opt/archipelago/scripts/load-container-images.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$BOOT_MEDIA/archipelago/scripts/archipelago-load-images.service" ]; then
|
||||||
|
cp "$BOOT_MEDIA/archipelago/scripts/archipelago-load-images.service" /mnt/target/etc/systemd/system/
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " ✅ Container images staged for first-boot loading"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure correct ownership (use numeric UID:GID 1000:1000 since we're outside chroot)
|
||||||
|
chown -R 1000:1000 /mnt/target/opt/archipelago 2>/dev/null || true
|
||||||
|
chown -R 1000:1000 /mnt/target/var/lib/archipelago 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create welcome profile (nginx serves on port 80)
|
||||||
|
cat > /mnt/target/etc/profile.d/archipelago.sh <<'PROFILE'
|
||||||
|
#!/bin/bash
|
||||||
|
if [ -t 0 ] && [ -z "$ARCHIPELAGO_WELCOMED" ]; then
|
||||||
|
export ARCHIPELAGO_WELCOMED=1
|
||||||
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
echo ""
|
||||||
|
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
if [ -n "$IP" ]; then
|
||||||
|
echo " 🌐 Web UI: http://$IP"
|
||||||
|
echo " 📡 SSH: ssh archipelago@$IP"
|
||||||
|
echo " 🔑 Password: archipelago (SSH) / password123 (Web UI)"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
PROFILE
|
||||||
|
chmod +x /mnt/target/etc/profile.d/archipelago.sh
|
||||||
|
|
||||||
|
# Systemd service is already in rootfs, but ensure it has correct config
|
||||||
|
cat > /mnt/target/etc/systemd/system/archipelago.service <<'SERVICE'
|
||||||
|
[Unit]
|
||||||
|
Description=Archipelago Backend
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=archipelago
|
||||||
|
Environment="ARCHIPELAGO_BIND=0.0.0.0:5678"
|
||||||
|
Environment="ARCHIPELAGO_DEV_MODE=true"
|
||||||
|
ExecStart=/usr/local/bin/archipelago
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SERVICE
|
||||||
|
|
||||||
|
# Install GRUB
|
||||||
|
echo " [6/6] Installing bootloader..."
|
||||||
|
mount --bind /dev /mnt/target/dev
|
||||||
|
mount --bind /dev/pts /mnt/target/dev/pts
|
||||||
|
mount --bind /proc /mnt/target/proc
|
||||||
|
mount --bind /sys /mnt/target/sys
|
||||||
|
mount --bind /run /mnt/target/run
|
||||||
|
|
||||||
|
chroot /mnt/target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago --removable 2>/dev/null || \
|
||||||
|
chroot /mnt/target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=archipelago 2>/dev/null || \
|
||||||
|
echo " Warning: GRUB install had issues, trying alternative..."
|
||||||
|
|
||||||
|
chroot /mnt/target update-grub
|
||||||
|
|
||||||
|
# Enable services
|
||||||
|
chroot /mnt/target systemctl enable archipelago.service 2>/dev/null || true
|
||||||
|
chroot /mnt/target systemctl enable nginx.service 2>/dev/null || true
|
||||||
|
chroot /mnt/target systemctl enable archipelago-load-images.service 2>/dev/null || true
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
sync
|
||||||
|
umount /mnt/target/run 2>/dev/null || true
|
||||||
|
umount /mnt/target/sys 2>/dev/null || true
|
||||||
|
umount /mnt/target/proc 2>/dev/null || true
|
||||||
|
umount /mnt/target/dev/pts 2>/dev/null || true
|
||||||
|
umount /mnt/target/dev 2>/dev/null || true
|
||||||
|
umount /mnt/target/boot/efi 2>/dev/null || true
|
||||||
|
umount /mnt/target 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${GREEN}║ ║${NC}"
|
||||||
|
echo -e "${GREEN}║ ✅ INSTALLATION COMPLETE! ║${NC}"
|
||||||
|
echo -e "${GREEN}║ ║${NC}"
|
||||||
|
echo -e "${GREEN}║ Remove the USB drive and press Enter to reboot. ║${NC}"
|
||||||
|
echo -e "${GREEN}║ ║${NC}"
|
||||||
|
echo -e "${GREEN}║ After reboot: ║${NC}"
|
||||||
|
echo -e "${GREEN}║ • Web UI: http://<IP> ║${NC}"
|
||||||
|
echo -e "${GREEN}║ • SSH: ssh archipelago@<IP> ║${NC}"
|
||||||
|
echo -e "${GREEN}║ • SSH Password: archipelago ║${NC}"
|
||||||
|
echo -e "${GREEN}║ • Web Password: password123 ║${NC}"
|
||||||
|
echo -e "${GREEN}║ ║${NC}"
|
||||||
|
echo -e "${GREEN}║ Pre-loaded apps (ready to start via Web UI): ║${NC}"
|
||||||
|
echo -e "${GREEN}║ • Bitcoin Knots • LND • Home Assistant ║${NC}"
|
||||||
|
echo -e "${GREEN}║ ║${NC}"
|
||||||
|
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter to reboot..."
|
||||||
|
reboot
|
||||||
|
INSTALLER_SCRIPT
|
||||||
|
|
||||||
|
chmod +x "$ARCH_DIR/auto-install.sh"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 5: Configure auto-start installer on boot
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "📦 Step 5: Configuring auto-start..."
|
||||||
|
|
||||||
|
# Create a squashfs overlay module that Debian Live will automatically load
|
||||||
|
# This is the proper way to add files to the live filesystem
|
||||||
|
OVERLAY_DIR="$WORK_DIR/overlay-root"
|
||||||
|
rm -rf "$OVERLAY_DIR"
|
||||||
|
mkdir -p "$OVERLAY_DIR/etc/profile.d"
|
||||||
|
mkdir -p "$OVERLAY_DIR/etc/skel"
|
||||||
|
mkdir -p "$OVERLAY_DIR/usr/local/bin"
|
||||||
|
mkdir -p "$OVERLAY_DIR/usr/sbin"
|
||||||
|
mkdir -p "$OVERLAY_DIR/sbin"
|
||||||
|
|
||||||
|
# Download and extract required tools AND their libraries for offline use
|
||||||
|
echo " Downloading partitioning tools for offline use..."
|
||||||
|
TOOLS_DIR="$WORK_DIR/tools-extract"
|
||||||
|
rm -rf "$TOOLS_DIR"
|
||||||
|
mkdir -p "$TOOLS_DIR"
|
||||||
|
|
||||||
|
docker run --rm --platform linux/amd64 \
|
||||||
|
-v "$TOOLS_DIR:/output" \
|
||||||
|
debian:bookworm \
|
||||||
|
bash -c '
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq parted dosfstools e2fsprogs
|
||||||
|
|
||||||
|
# Copy binaries
|
||||||
|
cp /usr/sbin/parted /output/
|
||||||
|
cp /usr/sbin/mkfs.vfat /output/ 2>/dev/null || cp /sbin/mkfs.vfat /output/ 2>/dev/null || true
|
||||||
|
cp /usr/sbin/mkfs.ext4 /output/ 2>/dev/null || cp /sbin/mkfs.ext4 /output/ 2>/dev/null || true
|
||||||
|
cp /usr/sbin/mke2fs /output/ 2>/dev/null || cp /sbin/mke2fs /output/ 2>/dev/null || true
|
||||||
|
cp /sbin/mkfs.fat /output/ 2>/dev/null || true
|
||||||
|
|
||||||
|
# Copy required shared libraries for parted
|
||||||
|
mkdir -p /output/lib
|
||||||
|
cp /lib/x86_64-linux-gnu/libparted.so* /output/lib/ 2>/dev/null || true
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libparted.so* /output/lib/ 2>/dev/null || true
|
||||||
|
cp /lib/x86_64-linux-gnu/libreadline.so* /output/lib/ 2>/dev/null || true
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libreadline.so* /output/lib/ 2>/dev/null || true
|
||||||
|
cp /lib/x86_64-linux-gnu/libdevmapper.so* /output/lib/ 2>/dev/null || true
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libdevmapper.so* /output/lib/ 2>/dev/null || true
|
||||||
|
|
||||||
|
# List what parted actually needs
|
||||||
|
ldd /usr/sbin/parted 2>/dev/null | grep "=>" | awk "{print \$3}" | while read lib; do
|
||||||
|
[ -f "$lib" ] && cp "$lib" /output/lib/ 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Libraries bundled:"
|
||||||
|
ls -la /output/lib/
|
||||||
|
'
|
||||||
|
|
||||||
|
# Copy tools to overlay
|
||||||
|
cp "$TOOLS_DIR/parted" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
|
||||||
|
cp "$TOOLS_DIR/mkfs.vfat" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
|
||||||
|
cp "$TOOLS_DIR/mkfs.fat" "$OVERLAY_DIR/sbin/" 2>/dev/null || true
|
||||||
|
cp "$TOOLS_DIR/mkfs.ext4" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
|
||||||
|
cp "$TOOLS_DIR/mke2fs" "$OVERLAY_DIR/usr/sbin/" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Copy shared libraries
|
||||||
|
mkdir -p "$OVERLAY_DIR/usr/lib/x86_64-linux-gnu"
|
||||||
|
cp "$TOOLS_DIR/lib/"*.so* "$OVERLAY_DIR/usr/lib/x86_64-linux-gnu/" 2>/dev/null || true
|
||||||
|
|
||||||
|
chmod +x "$OVERLAY_DIR/usr/sbin/"* 2>/dev/null || true
|
||||||
|
chmod +x "$OVERLAY_DIR/sbin/"* 2>/dev/null || true
|
||||||
|
echo " ✅ Partitioning tools and libraries bundled"
|
||||||
|
|
||||||
|
# Create the auto-start profile script
|
||||||
|
cat > "$OVERLAY_DIR/etc/profile.d/z99-archipelago-installer.sh" <<'AUTOSTART'
|
||||||
|
#!/bin/bash
|
||||||
|
# Auto-start Archipelago installer on login
|
||||||
|
|
||||||
|
# Only run once, only in interactive terminal, only for regular users
|
||||||
|
if [ -n "$INSTALLER_STARTED" ] || [ ! -t 0 ]; then
|
||||||
|
return 0 2>/dev/null || exit 0
|
||||||
|
fi
|
||||||
|
export INSTALLER_STARTED=1
|
||||||
|
|
||||||
|
# Give system a moment to settle
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo " ╔═══════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS - INSTALLER ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find boot media
|
||||||
|
BOOT_MEDIA=""
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom /media/cdrom; do
|
||||||
|
if [ -f "$dev/archipelago/auto-install.sh" ]; then
|
||||||
|
BOOT_MEDIA="$dev"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$BOOT_MEDIA" ]; then
|
||||||
|
echo " Found installer at: $BOOT_MEDIA"
|
||||||
|
echo ""
|
||||||
|
echo " Press Enter to start installation, or Ctrl+C for shell..."
|
||||||
|
read
|
||||||
|
sudo bash "$BOOT_MEDIA/archipelago/auto-install.sh"
|
||||||
|
else
|
||||||
|
echo " ⚠️ Installer not found on boot media."
|
||||||
|
echo ""
|
||||||
|
echo " Checked: /run/live/medium, /lib/live/mount/medium, /cdrom"
|
||||||
|
echo ""
|
||||||
|
echo " You can try manually:"
|
||||||
|
echo " sudo bash /path/to/archipelago/auto-install.sh"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
AUTOSTART
|
||||||
|
chmod +x "$OVERLAY_DIR/etc/profile.d/z99-archipelago-installer.sh"
|
||||||
|
|
||||||
|
# Also create .bashrc that sources the profile script (belt and suspenders)
|
||||||
|
cat > "$OVERLAY_DIR/etc/skel/.bashrc" <<'BASHRC'
|
||||||
|
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||||
|
|
||||||
|
# If not running interactively, don't do anything
|
||||||
|
case $- in
|
||||||
|
*i*) ;;
|
||||||
|
*) return;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Source profile.d scripts
|
||||||
|
if [ -d /etc/profile.d ]; then
|
||||||
|
for i in /etc/profile.d/*.sh; do
|
||||||
|
if [ -r "$i" ]; then
|
||||||
|
. "$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
BASHRC
|
||||||
|
|
||||||
|
# Create a wrapper script that can be called directly
|
||||||
|
cat > "$OVERLAY_DIR/usr/local/bin/archipelago-install" <<'WRAPPER'
|
||||||
|
#!/bin/bash
|
||||||
|
# Archipelago installer wrapper
|
||||||
|
|
||||||
|
BOOT_MEDIA=""
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom /media/cdrom; do
|
||||||
|
if [ -f "$dev/archipelago/auto-install.sh" ]; then
|
||||||
|
BOOT_MEDIA="$dev"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$BOOT_MEDIA" ]; then
|
||||||
|
exec sudo bash "$BOOT_MEDIA/archipelago/auto-install.sh"
|
||||||
|
else
|
||||||
|
echo "Installer not found. Searched:"
|
||||||
|
echo " /run/live/medium, /lib/live/mount/medium, /cdrom, /media/cdrom"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
WRAPPER
|
||||||
|
chmod +x "$OVERLAY_DIR/usr/local/bin/archipelago-install"
|
||||||
|
|
||||||
|
# Create the squashfs module
|
||||||
|
# Debian Live automatically loads all .squashfs files from live/ directory
|
||||||
|
echo " Creating overlay squashfs module..."
|
||||||
|
LIVE_DIR="$INSTALLER_ISO/live"
|
||||||
|
mkdir -p "$LIVE_DIR"
|
||||||
|
|
||||||
|
# Check if mksquashfs is available (may need to use Docker on macOS)
|
||||||
|
if command -v mksquashfs >/dev/null 2>&1; then
|
||||||
|
mksquashfs "$OVERLAY_DIR" "$LIVE_DIR/99-archipelago.squashfs" -comp xz -noappend
|
||||||
|
else
|
||||||
|
# Use Docker to create squashfs on macOS
|
||||||
|
echo " Using Docker to create squashfs..."
|
||||||
|
docker run --rm --platform linux/amd64 \
|
||||||
|
-v "$OVERLAY_DIR:/overlay:ro" \
|
||||||
|
-v "$LIVE_DIR:/output" \
|
||||||
|
debian:bookworm \
|
||||||
|
bash -c "apt-get update && apt-get install -y squashfs-tools && mksquashfs /overlay /output/99-archipelago.squashfs -comp xz -noappend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " ✅ Created overlay module: 99-archipelago.squashfs"
|
||||||
|
|
||||||
|
# Modify GRUB config - update branding and ensure components are loaded
|
||||||
|
if [ -f "$INSTALLER_ISO/boot/grub/grub.cfg" ]; then
|
||||||
|
echo " Configuring GRUB..."
|
||||||
|
sed -i.bak \
|
||||||
|
-e 's/Debian GNU\/Linux/Archipelago Installer/g' \
|
||||||
|
-e 's/Live system/Install Archipelago/g' \
|
||||||
|
"$INSTALLER_ISO/boot/grub/grub.cfg"
|
||||||
|
|
||||||
|
# Ensure 'components' parameter is present (loads additional squashfs modules)
|
||||||
|
# Also add 'username=user' to ensure consistent username
|
||||||
|
if ! grep -q "components" "$INSTALLER_ISO/boot/grub/grub.cfg"; then
|
||||||
|
sed -i 's/boot=live/boot=live components/' "$INSTALLER_ISO/boot/grub/grub.cfg"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$INSTALLER_ISO/isolinux/live.cfg" ]; then
|
||||||
|
echo " Configuring ISOLINUX..."
|
||||||
|
sed -i.bak \
|
||||||
|
-e 's/Debian GNU\/Linux/Archipelago Installer/g' \
|
||||||
|
-e 's/Live system/Install Archipelago/g' \
|
||||||
|
"$INSTALLER_ISO/isolinux/live.cfg"
|
||||||
|
|
||||||
|
# Add components parameter
|
||||||
|
if ! grep -q "components" "$INSTALLER_ISO/isolinux/live.cfg"; then
|
||||||
|
sed -i 's/boot=live/boot=live components/' "$INSTALLER_ISO/isolinux/live.cfg"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$INSTALLER_ISO/isolinux/menu.cfg" ]; then
|
||||||
|
sed -i.bak \
|
||||||
|
-e 's/Debian GNU\/Linux/Archipelago Installer/g' \
|
||||||
|
"$INSTALLER_ISO/isolinux/menu.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# STEP 6: Create final ISO
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "📦 Step 6: Creating bootable ISO..."
|
||||||
|
|
||||||
|
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-x86_64.iso"
|
||||||
|
|
||||||
|
# Get MBR for hybrid boot
|
||||||
|
ISOHDPFX=""
|
||||||
|
for path in \
|
||||||
|
"/usr/local/share/syslinux/isohdpfx.bin" \
|
||||||
|
"/usr/share/syslinux/isohdpfx.bin" \
|
||||||
|
"/opt/homebrew/share/syslinux/isohdpfx.bin" \
|
||||||
|
"$INSTALLER_ISO/isolinux/isohdpfx.bin"; do
|
||||||
|
if [ -f "$path" ]; then
|
||||||
|
ISOHDPFX="$path"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$ISOHDPFX" ]; then
|
||||||
|
echo " Extracting MBR from isolinux.bin..."
|
||||||
|
dd if="$INSTALLER_ISO/isolinux/isolinux.bin" of="$WORK_DIR/isohdpfx.bin" bs=432 count=1 2>/dev/null
|
||||||
|
ISOHDPFX="$WORK_DIR/isohdpfx.bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
xorriso -as mkisofs -o "$OUTPUT_ISO" \
|
||||||
|
-volid "ARCHIPELAGO" \
|
||||||
|
-J -R \
|
||||||
|
-isohybrid-mbr "$ISOHDPFX" \
|
||||||
|
-c isolinux/boot.cat \
|
||||||
|
-b isolinux/isolinux.bin \
|
||||||
|
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||||
|
-eltorito-alt-boot \
|
||||||
|
-e boot/grub/efi.img \
|
||||||
|
-no-emul-boot \
|
||||||
|
-isohybrid-gpt-basdat \
|
||||||
|
"$INSTALLER_ISO"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ✅ AUTO-INSTALLER ISO CREATED! ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo "📀 Output: $OUTPUT_ISO"
|
||||||
|
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
||||||
|
echo ""
|
||||||
|
echo "🔥 This is a StartOS-like automatic installer!"
|
||||||
|
echo ""
|
||||||
|
echo "Features:"
|
||||||
|
echo " • Pre-built system (no internet needed during install)"
|
||||||
|
echo " • Auto-detects internal disk"
|
||||||
|
echo " • One-button installation"
|
||||||
|
echo " • Boots directly to Archipelago after install"
|
||||||
|
echo " • Pre-bundled container apps:"
|
||||||
|
echo " - Bitcoin Knots v29"
|
||||||
|
echo " - LND v0.18.4"
|
||||||
|
echo " - Home Assistant"
|
||||||
|
echo ""
|
||||||
|
echo "To create USB:"
|
||||||
|
echo " 1. Flash with: sudo dd if=$OUTPUT_ISO of=/dev/rdiskX bs=4m"
|
||||||
|
echo " Or use Balena Etcher"
|
||||||
|
echo " 2. Boot from USB"
|
||||||
|
echo " 3. Press Enter to install"
|
||||||
|
echo " 4. Remove USB and reboot"
|
||||||
|
echo ""
|
||||||
@ -314,9 +314,9 @@ ln -sf ../archipelago-ssh.service "$ISO_CUSTOM/etc/systemd/system/multi-user.tar
|
|||||||
|
|
||||||
# Create system-wide profile script that runs on ANY login
|
# Create system-wide profile script that runs on ANY login
|
||||||
mkdir -p "$ISO_CUSTOM/etc/profile.d"
|
mkdir -p "$ISO_CUSTOM/etc/profile.d"
|
||||||
cat > "$ISO_CUSTOM/etc/profile.d/archipelago.sh" <<'PROFILE_EOF'
|
cat > "$ISO_CUSTOM/etc/profile.d/z99-archipelago.sh" <<'PROFILE_EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Archipelago auto-start - runs on login
|
# Archipelago auto-start - runs on login (z99 = runs last)
|
||||||
|
|
||||||
# Only run once per session and only in interactive shells
|
# Only run once per session and only in interactive shells
|
||||||
if [ -n "$ARCHIPELAGO_STARTED" ] || [ ! -t 0 ]; then
|
if [ -n "$ARCHIPELAGO_STARTED" ] || [ ! -t 0 ]; then
|
||||||
@ -333,23 +333,97 @@ for dev in /run/live/medium /lib/live/mount/medium /cdrom; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Get IP address
|
||||||
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
[ -z "$IP" ] && IP=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -1)
|
||||||
|
|
||||||
|
# Show welcome banner ALWAYS
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ Your sovereign Bitcoin infrastructure ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
if [ -n "$IP" ]; then
|
||||||
|
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||||
|
echo " │ 🌐 Web UI: http://$IP:5678 │"
|
||||||
|
echo " │ 📡 SSH: ssh user@$IP (password: archipelago) │"
|
||||||
|
echo " └─────────────────────────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$BOOT_MEDIA" ]; then
|
if [ -z "$BOOT_MEDIA" ]; then
|
||||||
|
echo " ⚠️ Boot media not found at /run/live/medium"
|
||||||
|
echo ""
|
||||||
|
echo " Manual commands:"
|
||||||
|
echo " archipelago - Start backend server"
|
||||||
|
echo " archipelago-menu - Open setup menu"
|
||||||
|
echo ""
|
||||||
return 0 2>/dev/null || exit 0
|
return 0 2>/dev/null || exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install archipelago command if not present
|
echo " 📍 Boot media: $BOOT_MEDIA"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Install archipelago commands if not present
|
||||||
if [ ! -f /usr/local/bin/archipelago ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago" ]; then
|
if [ ! -f /usr/local/bin/archipelago ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago" ]; then
|
||||||
sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago" /usr/local/bin/ 2>/dev/null
|
sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago" /usr/local/bin/ 2>/dev/null
|
||||||
sudo chmod +x /usr/local/bin/archipelago 2>/dev/null
|
sudo chmod +x /usr/local/bin/archipelago 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run auto-start
|
if [ ! -f /usr/local/bin/archipelago-menu ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago-menu" ]; then
|
||||||
if [ -f "$BOOT_MEDIA/archipelago/auto-start.sh" ]; then
|
sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago-menu" /usr/local/bin/ 2>/dev/null
|
||||||
source "$BOOT_MEDIA/archipelago/auto-start.sh"
|
sudo chmod +x /usr/local/bin/archipelago-menu 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy scripts to /opt
|
||||||
|
if [ ! -d /opt/archipelago/scripts ] && [ -d "$BOOT_MEDIA/archipelago/scripts" ]; then
|
||||||
|
sudo mkdir -p /opt/archipelago
|
||||||
|
sudo cp -r "$BOOT_MEDIA/archipelago/scripts" /opt/archipelago/ 2>/dev/null
|
||||||
|
sudo chmod +x /opt/archipelago/scripts/*.sh 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start web backend in background if available
|
||||||
|
if command -v archipelago >/dev/null 2>&1; then
|
||||||
|
if ! pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||||
|
echo " 🚀 Starting Archipelago backend on port 5678..."
|
||||||
|
nohup archipelago >/tmp/archipelago.log 2>&1 &
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
echo " ✅ Archipelago backend already running"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " Commands:"
|
||||||
|
echo " archipelago-menu - Open interactive setup menu"
|
||||||
|
echo " archipelago - Start/restart backend server"
|
||||||
|
echo ""
|
||||||
|
read -p " Press Enter to open the setup menu (or Ctrl+C to skip)... "
|
||||||
|
|
||||||
|
# Launch the menu
|
||||||
|
if [ -f /opt/archipelago/scripts/archipelago-menu.sh ]; then
|
||||||
|
exec bash /opt/archipelago/scripts/archipelago-menu.sh
|
||||||
|
elif [ -f "$BOOT_MEDIA/archipelago/scripts/archipelago-menu.sh" ]; then
|
||||||
|
exec bash "$BOOT_MEDIA/archipelago/scripts/archipelago-menu.sh"
|
||||||
fi
|
fi
|
||||||
PROFILE_EOF
|
PROFILE_EOF
|
||||||
|
|
||||||
chmod +x "$ISO_CUSTOM/etc/profile.d/archipelago.sh"
|
chmod +x "$ISO_CUSTOM/etc/profile.d/z99-archipelago.sh"
|
||||||
|
|
||||||
|
# Also add to /etc/skel/.bashrc as fallback for new user sessions
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/skel"
|
||||||
|
cat >> "$ISO_CUSTOM/etc/skel/.bashrc" <<'BASHRC_EOF'
|
||||||
|
|
||||||
|
# Archipelago auto-start fallback
|
||||||
|
if [ -z "$ARCHIPELAGO_STARTED" ] && [ -t 0 ]; then
|
||||||
|
[ -f /etc/profile.d/z99-archipelago.sh ] && source /etc/profile.d/z99-archipelago.sh
|
||||||
|
fi
|
||||||
|
BASHRC_EOF
|
||||||
|
|
||||||
# Modify GRUB config for Archipelago branding
|
# Modify GRUB config for Archipelago branding
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
511
image-recipe/build-debian-live-iso.sh
Executable file
511
image-recipe/build-debian-live-iso.sh
Executable file
@ -0,0 +1,511 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build Archipelago Bitcoin Node OS ISO - Debian Live Edition
|
||||||
|
# Based on Debian Live for reliable USB boot (like StartOS)
|
||||||
|
#
|
||||||
|
# Usage: ./build-debian-iso.sh
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
WORK_DIR="$SCRIPT_DIR/build/debian-iso"
|
||||||
|
OUTPUT_DIR="$SCRIPT_DIR/results"
|
||||||
|
DEBIAN_VERSION="bookworm"
|
||||||
|
ARCH="amd64"
|
||||||
|
|
||||||
|
echo "╔════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ Building Archipelago - Debian Live Edition ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
# Download Debian Live ISO if not exists
|
||||||
|
BASE_ISO="$WORK_DIR/debian-live-12-${ARCH}-standard.iso"
|
||||||
|
if [ ! -f "$BASE_ISO" ] || [ $(stat -f%z "$BASE_ISO" 2>/dev/null || echo 0) -lt 100000000 ]; then
|
||||||
|
echo "📥 Downloading Debian Live 12 (Bookworm) Standard ISO..."
|
||||||
|
rm -f "$BASE_ISO"
|
||||||
|
# Use SourceForge respin which has up-to-date Debian 12 Live
|
||||||
|
curl -L -o "$BASE_ISO" \
|
||||||
|
"https://sourceforge.net/projects/debian-live-respin-iso/files/standard/live-image-debian12.11-standard-20250522-amd64.hybrid.iso/download"
|
||||||
|
echo "✅ Downloaded Debian Live ISO"
|
||||||
|
else
|
||||||
|
echo "✅ Using cached Debian Live ISO"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract ISO
|
||||||
|
echo ""
|
||||||
|
echo "📦 Extracting Debian Live ISO..."
|
||||||
|
ISO_CUSTOM="$WORK_DIR/custom"
|
||||||
|
rm -rf "$ISO_CUSTOM"
|
||||||
|
mkdir -p "$ISO_CUSTOM"
|
||||||
|
|
||||||
|
cd "$ISO_CUSTOM"
|
||||||
|
7z x -y "$BASE_ISO" >/dev/null 2>&1 || 7z x -y "$BASE_ISO"
|
||||||
|
|
||||||
|
echo "✅ Extracted ISO"
|
||||||
|
|
||||||
|
# Add Archipelago files
|
||||||
|
echo ""
|
||||||
|
echo "📋 Adding Archipelago files..."
|
||||||
|
|
||||||
|
ARCHIPELAGO_DIR="$ISO_CUSTOM/archipelago"
|
||||||
|
mkdir -p "$ARCHIPELAGO_DIR"
|
||||||
|
mkdir -p "$ARCHIPELAGO_DIR/bin"
|
||||||
|
mkdir -p "$ARCHIPELAGO_DIR/scripts"
|
||||||
|
|
||||||
|
# Copy the pre-built backend if it exists
|
||||||
|
if [ -d "$SCRIPT_DIR/../core/target/release" ]; then
|
||||||
|
echo "🦀 Including Archipelago backend..."
|
||||||
|
cp "$SCRIPT_DIR/../core/target/release/archipelago" "$ARCHIPELAGO_DIR/bin/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy the frontend build if it exists
|
||||||
|
if [ -d "$SCRIPT_DIR/../web/dist/neode-ui" ]; then
|
||||||
|
echo "🎨 Including Archipelago Web UI..."
|
||||||
|
cp -r "$SCRIPT_DIR/../web/dist/neode-ui" "$ARCHIPELAGO_DIR/web-ui"
|
||||||
|
elif [ -d "$SCRIPT_DIR/../neode-ui/dist" ]; then
|
||||||
|
echo "🎨 Including Archipelago frontend..."
|
||||||
|
cp -r "$SCRIPT_DIR/../neode-ui/dist" "$ARCHIPELAGO_DIR/web-ui" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy app manifests
|
||||||
|
if [ -d "$SCRIPT_DIR/../apps" ]; then
|
||||||
|
echo "📦 Including app manifests..."
|
||||||
|
cp -r "$SCRIPT_DIR/../apps" "$ARCHIPELAGO_DIR/apps" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy setup scripts
|
||||||
|
if [ -d "$SCRIPT_DIR/archipelago-scripts" ]; then
|
||||||
|
echo "📜 Including setup scripts..."
|
||||||
|
cp "$SCRIPT_DIR/archipelago-scripts/"*.sh "$ARCHIPELAGO_DIR/scripts/" 2>/dev/null || true
|
||||||
|
chmod +x "$ARCHIPELAGO_DIR/scripts/"*.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create main setup script for Archipelago
|
||||||
|
cat > "$ARCHIPELAGO_DIR/setup-archipelago.sh" <<'SETUP_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Archipelago Setup Script for Debian Live
|
||||||
|
#
|
||||||
|
echo ""
|
||||||
|
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ 🏝️ ARCHIPELAGO BITCOIN NODE OS - Debian Edition ║"
|
||||||
|
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find the boot media
|
||||||
|
BOOT_MEDIA=""
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom /media/cdrom; do
|
||||||
|
if [ -d "$dev/archipelago" ]; then
|
||||||
|
BOOT_MEDIA="$dev"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$BOOT_MEDIA" ]; then
|
||||||
|
echo "❌ Could not find Archipelago files on boot media"
|
||||||
|
echo " Looking in: /run/live/medium, /lib/live/mount/medium, /cdrom"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📍 Found Archipelago at: $BOOT_MEDIA/archipelago"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy files to system
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/bin" ]; then
|
||||||
|
echo "📋 Installing Archipelago binaries..."
|
||||||
|
sudo cp -r "$BOOT_MEDIA/archipelago/bin/"* /usr/local/bin/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/apps" ]; then
|
||||||
|
echo "📋 Installing app manifests..."
|
||||||
|
sudo mkdir -p /etc/archipelago
|
||||||
|
sudo cp -r "$BOOT_MEDIA/archipelago/apps" /etc/archipelago/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$BOOT_MEDIA/archipelago/scripts" ]; then
|
||||||
|
echo "📋 Installing setup scripts..."
|
||||||
|
sudo mkdir -p /opt/archipelago
|
||||||
|
sudo cp -r "$BOOT_MEDIA/archipelago/scripts" /opt/archipelago/ 2>/dev/null || true
|
||||||
|
sudo chmod +x /opt/archipelago/scripts/*.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Archipelago installed!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Automatically launch the menu
|
||||||
|
sleep 1
|
||||||
|
exec /opt/archipelago/scripts/archipelago-menu.sh
|
||||||
|
SETUP_EOF
|
||||||
|
|
||||||
|
chmod +x "$ARCHIPELAGO_DIR/setup-archipelago.sh"
|
||||||
|
|
||||||
|
# Create auto-start script that runs on login
|
||||||
|
cat > "$ARCHIPELAGO_DIR/auto-start.sh" <<'AUTOSTART_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Archipelago Auto-Start - Runs on first login
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find boot media
|
||||||
|
BOOT_MEDIA=""
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom; do
|
||||||
|
if [ -d "$dev/archipelago" ]; then
|
||||||
|
BOOT_MEDIA="$dev"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Install essential tools on first boot (required for disk installer)
|
||||||
|
if [ ! -f /tmp/.archipelago-tools-installed ]; then
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO - First Boot Setup ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo " 📦 Installing required tools..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Update and install essential tools
|
||||||
|
sudo apt-get update -qq 2>/dev/null
|
||||||
|
sudo apt-get install -y parted debootstrap dosfstools e2fsprogs 2>/dev/null
|
||||||
|
|
||||||
|
echo " ✅ Tools installed"
|
||||||
|
touch /tmp/.archipelago-tools-installed
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get IP address
|
||||||
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
|
||||||
|
# Start web UI in background if not already running
|
||||||
|
if [ -n "$BOOT_MEDIA" ] && ! pgrep -f "http.server" >/dev/null; then
|
||||||
|
WEB_UI_DIR="$BOOT_MEDIA/archipelago/web-ui"
|
||||||
|
if [ -d "$WEB_UI_DIR" ]; then
|
||||||
|
cd "$WEB_UI_DIR"
|
||||||
|
nohup python3 -m http.server 80 --bind 0.0.0.0 >/dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ Your sovereign Bitcoin infrastructure ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
if [ -n "$IP" ]; then
|
||||||
|
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||||
|
echo " │ 🌐 Web UI: http://$IP "
|
||||||
|
echo " └─────────────────────────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
echo " Type 'archipelago' to open the setup menu"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if already set up
|
||||||
|
if [ -f ~/.archipelago-setup-done ]; then
|
||||||
|
return 2>/dev/null || exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# First time - run setup automatically
|
||||||
|
if [ -n "$BOOT_MEDIA" ] && [ -f "$BOOT_MEDIA/archipelago/setup-archipelago.sh" ]; then
|
||||||
|
echo ""
|
||||||
|
read -p " Press Enter to continue to setup menu..."
|
||||||
|
bash "$BOOT_MEDIA/archipelago/setup-archipelago.sh"
|
||||||
|
fi
|
||||||
|
AUTOSTART_EOF
|
||||||
|
|
||||||
|
chmod +x "$ARCHIPELAGO_DIR/auto-start.sh"
|
||||||
|
|
||||||
|
# Create a simple 'archipelago-menu' command wrapper (don't overwrite the backend binary!)
|
||||||
|
cat > "$ARCHIPELAGO_DIR/bin/archipelago-menu" <<'CMD_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Archipelago menu command - launches the setup menu
|
||||||
|
|
||||||
|
if [ -f /opt/archipelago/scripts/archipelago-menu.sh ]; then
|
||||||
|
exec /opt/archipelago/scripts/archipelago-menu.sh
|
||||||
|
else
|
||||||
|
# Find on boot media
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom; do
|
||||||
|
if [ -f "$dev/archipelago/scripts/archipelago-menu.sh" ]; then
|
||||||
|
exec bash "$dev/archipelago/scripts/archipelago-menu.sh"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Archipelago menu not found. Run setup first:"
|
||||||
|
echo " sh /run/live/medium/archipelago/setup-archipelago.sh"
|
||||||
|
fi
|
||||||
|
CMD_EOF
|
||||||
|
|
||||||
|
chmod +x "$ARCHIPELAGO_DIR/bin/archipelago-menu"
|
||||||
|
|
||||||
|
# Verify the real backend binary is there
|
||||||
|
if [ -f "$ARCHIPELAGO_DIR/bin/archipelago" ]; then
|
||||||
|
echo " Backend binary: $(file "$ARCHIPELAGO_DIR/bin/archipelago" | grep -o 'ELF.*' || echo 'included')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create SSH auto-start script for live environment
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/live/config.conf.d"
|
||||||
|
cat > "$ISO_CUSTOM/etc/live/config.conf.d/archipelago.conf" <<'LIVE_CONF_EOF'
|
||||||
|
# Archipelago live config
|
||||||
|
LIVE_HOSTNAME="archipelago"
|
||||||
|
LIVE_USER_DEFAULT_GROUPS="audio cdrom dip floppy video plugdev netdev powerdev scanner bluetooth sudo"
|
||||||
|
LIVE_CONF_EOF
|
||||||
|
|
||||||
|
# Create rc.local to start SSH on boot (works in live environment)
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/rc.local.d"
|
||||||
|
cat > "$ISO_CUSTOM/etc/rc.local.d/archipelago-ssh.sh" <<'RCLOCAL_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Enable SSH in live environment
|
||||||
|
|
||||||
|
# Install and start SSH if not running
|
||||||
|
if ! systemctl is-active --quiet ssh 2>/dev/null; then
|
||||||
|
# Try to start SSH (may already be installed)
|
||||||
|
systemctl start ssh 2>/dev/null || true
|
||||||
|
|
||||||
|
# If SSH not installed, install it
|
||||||
|
if ! command -v sshd >/dev/null 2>&1; then
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y openssh-server
|
||||||
|
systemctl start ssh
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set password for user (live user is typically 'user' with empty password)
|
||||||
|
echo "user:archipelago" | chpasswd 2>/dev/null || true
|
||||||
|
echo "root:archipelago" | chpasswd 2>/dev/null || true
|
||||||
|
|
||||||
|
# Allow password auth
|
||||||
|
sed -i 's/#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config 2>/dev/null
|
||||||
|
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config 2>/dev/null
|
||||||
|
systemctl restart ssh 2>/dev/null || true
|
||||||
|
RCLOCAL_EOF
|
||||||
|
chmod +x "$ISO_CUSTOM/etc/rc.local.d/archipelago-ssh.sh"
|
||||||
|
|
||||||
|
# Also create a systemd service that runs early
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/systemd/system"
|
||||||
|
cat > "$ISO_CUSTOM/etc/systemd/system/archipelago-ssh.service" <<'SERVICE_EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Archipelago SSH Setup
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/bash -c 'apt-get update -qq && apt-get install -y openssh-server && echo "user:archipelago" | chpasswd && echo "root:archipelago" | chpasswd && sed -i "s/#PasswordAuthentication.*/PasswordAuthentication yes/" /etc/ssh/sshd_config && sed -i "s/PasswordAuthentication no/PasswordAuthentication yes/" /etc/ssh/sshd_config && systemctl restart ssh'
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SERVICE_EOF
|
||||||
|
|
||||||
|
# Enable the service
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/systemd/system/multi-user.target.wants"
|
||||||
|
ln -sf ../archipelago-ssh.service "$ISO_CUSTOM/etc/systemd/system/multi-user.target.wants/archipelago-ssh.service"
|
||||||
|
|
||||||
|
# Create system-wide profile script that runs on ANY login
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/profile.d"
|
||||||
|
cat > "$ISO_CUSTOM/etc/profile.d/z99-archipelago.sh" <<'PROFILE_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Archipelago auto-start - runs on login (z99 = runs last)
|
||||||
|
|
||||||
|
# Only run once per session and only in interactive shells
|
||||||
|
if [ -n "$ARCHIPELAGO_STARTED" ] || [ ! -t 0 ]; then
|
||||||
|
return 0 2>/dev/null || exit 0
|
||||||
|
fi
|
||||||
|
export ARCHIPELAGO_STARTED=1
|
||||||
|
|
||||||
|
# Find archipelago directory
|
||||||
|
BOOT_MEDIA=""
|
||||||
|
for dev in /run/live/medium /lib/live/mount/medium /cdrom; do
|
||||||
|
if [ -d "$dev/archipelago" ]; then
|
||||||
|
BOOT_MEDIA="$dev"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Get IP address
|
||||||
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
[ -z "$IP" ] && IP=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -1)
|
||||||
|
|
||||||
|
# Show welcome banner ALWAYS
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo " ╔═══════════════════════════════════════════════════════════╗"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ 🏝️ ARCHIPELAGO BITCOIN NODE OS ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ║ Your sovereign Bitcoin infrastructure ║"
|
||||||
|
echo " ║ ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
if [ -n "$IP" ]; then
|
||||||
|
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||||
|
echo " │ 🌐 Web UI: http://$IP:5678 │"
|
||||||
|
echo " │ 📡 SSH: ssh user@$IP (password: archipelago) │"
|
||||||
|
echo " └─────────────────────────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$BOOT_MEDIA" ]; then
|
||||||
|
echo " ⚠️ Boot media not found at /run/live/medium"
|
||||||
|
echo ""
|
||||||
|
echo " Manual commands:"
|
||||||
|
echo " archipelago - Start backend server"
|
||||||
|
echo " archipelago-menu - Open setup menu"
|
||||||
|
echo ""
|
||||||
|
return 0 2>/dev/null || exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " 📍 Boot media: $BOOT_MEDIA"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Install archipelago commands if not present
|
||||||
|
if [ ! -f /usr/local/bin/archipelago ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago" ]; then
|
||||||
|
sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago" /usr/local/bin/ 2>/dev/null
|
||||||
|
sudo chmod +x /usr/local/bin/archipelago 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/bin/archipelago-menu ] && [ -f "$BOOT_MEDIA/archipelago/bin/archipelago-menu" ]; then
|
||||||
|
sudo cp "$BOOT_MEDIA/archipelago/bin/archipelago-menu" /usr/local/bin/ 2>/dev/null
|
||||||
|
sudo chmod +x /usr/local/bin/archipelago-menu 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy scripts to /opt
|
||||||
|
if [ ! -d /opt/archipelago/scripts ] && [ -d "$BOOT_MEDIA/archipelago/scripts" ]; then
|
||||||
|
sudo mkdir -p /opt/archipelago
|
||||||
|
sudo cp -r "$BOOT_MEDIA/archipelago/scripts" /opt/archipelago/ 2>/dev/null
|
||||||
|
sudo chmod +x /opt/archipelago/scripts/*.sh 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start web backend in background if available
|
||||||
|
if command -v archipelago >/dev/null 2>&1; then
|
||||||
|
if ! pgrep -f "archipelago" >/dev/null 2>&1; then
|
||||||
|
echo " 🚀 Starting Archipelago backend on port 5678..."
|
||||||
|
nohup archipelago >/tmp/archipelago.log 2>&1 &
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
echo " ✅ Archipelago backend already running"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " Commands:"
|
||||||
|
echo " archipelago-menu - Open interactive setup menu"
|
||||||
|
echo " archipelago - Start/restart backend server"
|
||||||
|
echo ""
|
||||||
|
read -p " Press Enter to open the setup menu (or Ctrl+C to skip)... "
|
||||||
|
|
||||||
|
# Launch the menu
|
||||||
|
if [ -f /opt/archipelago/scripts/archipelago-menu.sh ]; then
|
||||||
|
exec bash /opt/archipelago/scripts/archipelago-menu.sh
|
||||||
|
elif [ -f "$BOOT_MEDIA/archipelago/scripts/archipelago-menu.sh" ]; then
|
||||||
|
exec bash "$BOOT_MEDIA/archipelago/scripts/archipelago-menu.sh"
|
||||||
|
fi
|
||||||
|
PROFILE_EOF
|
||||||
|
|
||||||
|
chmod +x "$ISO_CUSTOM/etc/profile.d/z99-archipelago.sh"
|
||||||
|
|
||||||
|
# Also add to /etc/skel/.bashrc as fallback for new user sessions
|
||||||
|
mkdir -p "$ISO_CUSTOM/etc/skel"
|
||||||
|
cat >> "$ISO_CUSTOM/etc/skel/.bashrc" <<'BASHRC_EOF'
|
||||||
|
|
||||||
|
# Archipelago auto-start fallback
|
||||||
|
if [ -z "$ARCHIPELAGO_STARTED" ] && [ -t 0 ]; then
|
||||||
|
[ -f /etc/profile.d/z99-archipelago.sh ] && source /etc/profile.d/z99-archipelago.sh
|
||||||
|
fi
|
||||||
|
BASHRC_EOF
|
||||||
|
|
||||||
|
# Modify GRUB config for Archipelago branding
|
||||||
|
echo ""
|
||||||
|
echo "⚙️ Configuring boot..."
|
||||||
|
|
||||||
|
if [ -f "$ISO_CUSTOM/boot/grub/grub.cfg" ]; then
|
||||||
|
sed -i.bak \
|
||||||
|
-e 's/Debian GNU\/Linux/Archipelago Bitcoin Node OS/g' \
|
||||||
|
-e 's/Live system/Archipelago Live/g' \
|
||||||
|
"$ISO_CUSTOM/boot/grub/grub.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$ISO_CUSTOM/isolinux/menu.cfg" ]; then
|
||||||
|
sed -i.bak \
|
||||||
|
-e 's/Debian GNU\/Linux/Archipelago Bitcoin Node OS/g' \
|
||||||
|
-e 's/Live system/Archipelago Live/g' \
|
||||||
|
"$ISO_CUSTOM/isolinux/menu.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$ISO_CUSTOM/isolinux/live.cfg" ]; then
|
||||||
|
sed -i.bak \
|
||||||
|
-e 's/Live system/Archipelago Live/g' \
|
||||||
|
"$ISO_CUSTOM/isolinux/live.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create final ISO
|
||||||
|
OUTPUT_ISO="$OUTPUT_DIR/archipelago-debian-12-x86_64.iso"
|
||||||
|
echo ""
|
||||||
|
echo "🔥 Creating final bootable ISO..."
|
||||||
|
|
||||||
|
if command -v xorriso >/dev/null 2>&1; then
|
||||||
|
# Create proper hybrid ISO with MBR for USB boot
|
||||||
|
# Need to extract MBR from isolinux or use system syslinux
|
||||||
|
|
||||||
|
# Check for isohdpfx.bin in various locations
|
||||||
|
ISOHDPFX=""
|
||||||
|
for path in \
|
||||||
|
"/usr/local/share/syslinux/isohdpfx.bin" \
|
||||||
|
"/usr/share/syslinux/isohdpfx.bin" \
|
||||||
|
"/opt/homebrew/share/syslinux/isohdpfx.bin" \
|
||||||
|
"$ISO_CUSTOM/isolinux/isohdpfx.bin"; do
|
||||||
|
if [ -f "$path" ]; then
|
||||||
|
ISOHDPFX="$path"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$ISOHDPFX" ]; then
|
||||||
|
echo "⚠️ No isohdpfx.bin found, extracting from isolinux.bin..."
|
||||||
|
# Extract first 432 bytes from isolinux.bin as MBR
|
||||||
|
dd if="$ISO_CUSTOM/isolinux/isolinux.bin" of="$WORK_DIR/isohdpfx.bin" bs=432 count=1 2>/dev/null
|
||||||
|
ISOHDPFX="$WORK_DIR/isohdpfx.bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Using MBR: $ISOHDPFX"
|
||||||
|
|
||||||
|
xorriso -as mkisofs -o "$OUTPUT_ISO" \
|
||||||
|
-volid "ARCHIPELAGO" \
|
||||||
|
-J -R \
|
||||||
|
-isohybrid-mbr "$ISOHDPFX" \
|
||||||
|
-c isolinux/boot.cat \
|
||||||
|
-b isolinux/isolinux.bin \
|
||||||
|
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||||
|
-eltorito-alt-boot \
|
||||||
|
-e boot/grub/efi.img \
|
||||||
|
-no-emul-boot \
|
||||||
|
-isohybrid-gpt-basdat \
|
||||||
|
"$ISO_CUSTOM"
|
||||||
|
else
|
||||||
|
echo "❌ xorriso not found. Please install it: brew install xorriso"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ ISO created successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "📀 Output: $OUTPUT_ISO"
|
||||||
|
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
||||||
|
echo ""
|
||||||
|
echo "🔥 This is Debian Live-based - reliable USB boot like StartOS!"
|
||||||
|
echo ""
|
||||||
|
echo "To create USB:"
|
||||||
|
echo " 1. Use Balena Etcher to flash the ISO"
|
||||||
|
echo " 2. Or: sudo dd if=$OUTPUT_ISO of=/dev/sdX bs=4M status=progress"
|
||||||
|
echo ""
|
||||||
99
scripts/deploy-to-target.sh
Executable file
99
scripts/deploy-to-target.sh
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Deploy Archipelago code to the HP ProDesk target
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/deploy-to-target.sh # Sync and rebuild
|
||||||
|
# ./scripts/deploy-to-target.sh --quick # Sync only, no rebuild
|
||||||
|
# ./scripts/deploy-to-target.sh --live # Deploy to live system
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
TARGET_HOST="${ARCHIPELAGO_TARGET:-archipelago@192.168.1.228}"
|
||||||
|
TARGET_DIR="/home/archipelago/archy"
|
||||||
|
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ Deploying to Archipelago Target ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo "Target: $TARGET_HOST"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
QUICK=false
|
||||||
|
LIVE=false
|
||||||
|
for arg in "$@"; do
|
||||||
|
case $arg in
|
||||||
|
--quick) QUICK=true ;;
|
||||||
|
--live) LIVE=true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sync code
|
||||||
|
echo "📦 Syncing code..."
|
||||||
|
rsync -avz --delete \
|
||||||
|
--exclude 'node_modules' \
|
||||||
|
--exclude 'target' \
|
||||||
|
--exclude 'dist' \
|
||||||
|
--exclude '.git' \
|
||||||
|
--exclude 'image-recipe/build' \
|
||||||
|
--exclude 'image-recipe/results' \
|
||||||
|
"$PROJECT_DIR/" "$TARGET_HOST:$TARGET_DIR/"
|
||||||
|
|
||||||
|
if [ "$QUICK" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "✅ Quick sync complete!"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build on target
|
||||||
|
echo ""
|
||||||
|
echo "🔨 Building on target..."
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
echo " Building frontend..."
|
||||||
|
ssh "$TARGET_HOST" "cd $TARGET_DIR/neode-ui && npm install --silent && npm run build" 2>&1 | sed 's/^/ /'
|
||||||
|
|
||||||
|
# Backend (if Rust is installed)
|
||||||
|
if ssh "$TARGET_HOST" "command -v cargo" >/dev/null 2>&1; then
|
||||||
|
echo " Building backend..."
|
||||||
|
ssh "$TARGET_HOST" "cd $TARGET_DIR/core && cargo build --release 2>&1" | tail -5 | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
echo " ⚠️ Rust not installed on target, skipping backend build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$LIVE" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "🚀 Deploying to live system..."
|
||||||
|
|
||||||
|
# Deploy backend
|
||||||
|
ssh "$TARGET_HOST" "sudo cp $TARGET_DIR/core/target/release/archipelago /usr/local/bin/ 2>/dev/null || true"
|
||||||
|
|
||||||
|
# Deploy frontend
|
||||||
|
ssh "$TARGET_HOST" "sudo rm -rf /opt/archipelago/web-ui/*"
|
||||||
|
ssh "$TARGET_HOST" "sudo cp -r $TARGET_DIR/neode-ui/dist/* /opt/archipelago/web-ui/"
|
||||||
|
ssh "$TARGET_HOST" "sudo chown -R 1000:1000 /opt/archipelago/web-ui"
|
||||||
|
|
||||||
|
# Restart services
|
||||||
|
ssh "$TARGET_HOST" "sudo systemctl restart archipelago nginx"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Deployed to live system!"
|
||||||
|
echo " Web UI: http://$(echo $TARGET_HOST | cut -d@ -f2)"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "✅ Build complete!"
|
||||||
|
echo ""
|
||||||
|
echo "To test frontend dev server:"
|
||||||
|
echo " ssh $TARGET_HOST"
|
||||||
|
echo " cd ~/archy/neode-ui && npm run dev -- --host 0.0.0.0"
|
||||||
|
echo " Then open: http://$(echo $TARGET_HOST | cut -d@ -f2):5173"
|
||||||
|
echo ""
|
||||||
|
echo "To deploy to live system:"
|
||||||
|
echo " ./scripts/deploy-to-target.sh --live"
|
||||||
|
fi
|
||||||
93
scripts/setup-target-dev.sh
Executable file
93
scripts/setup-target-dev.sh
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Setup development environment on Archipelago target machine
|
||||||
|
#
|
||||||
|
# Run this ON the HP ProDesk via SSH:
|
||||||
|
# curl -sSL https://raw.githubusercontent.com/.../setup-target-dev.sh | bash
|
||||||
|
# Or copy and run locally:
|
||||||
|
# scp scripts/setup-target-dev.sh archipelago@192.168.1.228:~/
|
||||||
|
# ssh archipelago@192.168.1.228 'bash ~/setup-target-dev.sh'
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ Setting up Archipelago Development Environment ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Update packages
|
||||||
|
echo "📦 Updating packages..."
|
||||||
|
sudo apt update
|
||||||
|
|
||||||
|
# Install Node.js (for Vue.js frontend)
|
||||||
|
echo ""
|
||||||
|
echo "📦 Installing Node.js..."
|
||||||
|
if ! command -v node &> /dev/null; then
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||||
|
sudo apt install -y nodejs
|
||||||
|
else
|
||||||
|
echo " Node.js already installed: $(node --version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install Rust (for backend)
|
||||||
|
echo ""
|
||||||
|
echo "📦 Installing Rust..."
|
||||||
|
if ! command -v cargo &> /dev/null; then
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
source ~/.cargo/env
|
||||||
|
else
|
||||||
|
echo " Rust already installed: $(rustc --version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install build tools
|
||||||
|
echo ""
|
||||||
|
echo "📦 Installing build tools..."
|
||||||
|
sudo apt install -y \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
git
|
||||||
|
|
||||||
|
# Create development directory
|
||||||
|
echo ""
|
||||||
|
echo "📁 Creating development directory..."
|
||||||
|
mkdir -p ~/archy
|
||||||
|
|
||||||
|
# Fix XDG_RUNTIME_DIR for rootless Podman (add to bashrc)
|
||||||
|
if ! grep -q "XDG_RUNTIME_DIR" ~/.bashrc; then
|
||||||
|
echo ""
|
||||||
|
echo "🔧 Fixing Podman rootless setup..."
|
||||||
|
cat >> ~/.bashrc << 'EOF'
|
||||||
|
|
||||||
|
# Fix for rootless Podman
|
||||||
|
if [ -z "$XDG_RUNTIME_DIR" ]; then
|
||||||
|
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||||
|
if [ ! -d "$XDG_RUNTIME_DIR" ]; then
|
||||||
|
sudo mkdir -p "$XDG_RUNTIME_DIR"
|
||||||
|
sudo chown $(whoami):$(whoami) "$XDG_RUNTIME_DIR"
|
||||||
|
sudo chmod 700 "$XDG_RUNTIME_DIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable user lingering for Podman
|
||||||
|
sudo loginctl enable-linger archipelago 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ✅ Development environment ready! ║"
|
||||||
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo "Installed:"
|
||||||
|
echo " • Node.js: $(node --version 2>/dev/null || echo 'not found')"
|
||||||
|
echo " • npm: $(npm --version 2>/dev/null || echo 'not found')"
|
||||||
|
echo " • Rust: $(rustc --version 2>/dev/null || echo 'not found')"
|
||||||
|
echo " • Cargo: $(cargo --version 2>/dev/null || echo 'not found')"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Log out and back in (or run: source ~/.bashrc)"
|
||||||
|
echo " 2. From your Mac, run: ./scripts/deploy-to-target.sh"
|
||||||
|
echo " 3. To start Vue.js dev server: cd ~/archy/neode-ui && npm run dev -- --host 0.0.0.0"
|
||||||
|
echo ""
|
||||||
Loading…
x
Reference in New Issue
Block a user