diff --git a/.github/workflows/build_install_folder.yml b/.github/workflows/build_install_folder.yml new file mode 100644 index 00000000..366fcfb9 --- /dev/null +++ b/.github/workflows/build_install_folder.yml @@ -0,0 +1,24 @@ +name: Build Deployment Zip + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Create deployment ZIP + run: | + cd install + zip -r ../deploy.zip . + + - name: Release + uses: softprops/action-gh-release@v2 + with: + files: deploy.zip diff --git a/Dockerfile b/Dockerfile index 6815b4a6..23d37f87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG base_image=ghcr.io/decidim/decidim-generator:latest FROM $base_image LABEL maintainer="hola@decidim.org" -RUN decidim . +RUN decidim --queue sidekiq . RUN bundle check || bundle install RUN bundle exec rake assets:precompile diff --git a/README.md b/README.md index 4b0e703a..5b070ce6 100644 --- a/README.md +++ b/README.md @@ -101,3 +101,65 @@ sudo chown -R $(whoami): ${APP_NAME} ``` From here on you can follow the steps on the [Getting Started](https://docs.decidim.org/en/install/) guide. + +## Using a production deploy script +We've been working on a script that you can use to have a fully functional, production-ready decidim instance. + +```bash +curl -fsSL https://decidim.org/install | bash +``` + +It will install the necessary tools to make decidim work on your server. + +- Docker +- unzip +- UFW + +The application will be hosted in the `/opt/decidim` directory by default, even though you can change it with `REPOSITORY_PATH` environment variable. + +## App - Main Decidim Web Application +The app itself will be the container with the base image you decide (By default is the latest Decidim version: `decidim/decidim:latest`). You can change it with the `DECIDIM_IMAGE` environment variable. + +This is the front-end web process users access in the browser. + +## Worker +The worker will be the one responsible for all the background jobs that the application needs to run. + +## Cache +The app needs a cache server. This will be a `redis:8-alpine` instance. This cache will be used both by the app and the worker. + +## Database +The application needs a database to run. Through the installation process you will be asked if you have an already working database, if not, you will have a postgres container with all the schema and migrations run (It will be a `postgres:17-alpine`) + +## Configuration +To configure the application you will have to answer some questions that will, at the end, generate a `.env` file. + +### Environment Variables Reference +To see the full list of Decidim Environment Variables, and that you can add to your generated `.env` file, you can take a look at the official [documentation](https://docs.decidim.org/en/develop/configure/environment_variables) + +| Variable | Default | Used In | Description | +|----------|---------|---------|-------------| +| **BUNDLE_GEMFILE** | `Gemfile.wrapper` | app, worker | Selects which Gemfile the container should use. | +| **DECIDIM_IMAGE** | `decidim/decidim:latest` | app, worker | Overrides the Decidim Docker image version. | +| **DECIDIM_DOMAIN** | — | app, traefik | Domain for HTTPS routing and URL generation. | +| **SECRET_KEY_BASE** | — | app, worker | Rails secret key used for sessions and cookies. | +| **DATABASE_NAME** | `decidim` | db | PostgreSQL database name. | +| **DATABASE_USER** | `decidim` | db | PostgreSQL username. | +| **DATABASE_HOST** | `db` | app, worker | Hostname of your PostgreSQL instance. | +| **DATABASE_PASSWORD** | `decidim` | db | PostgreSQL user password. | +| **DATABASE_URL** | — | app, worker | Full PostgreSQL connection URL (overrides other DB vars). | +| **SMTP_USERNAME** | — | app, worker | Username for SMTP authentication. | +| **SMTP_PASSWORD** | — | app, worker | Password for SMTP authentication. | +| **SMTP_ADDRESS** | — | app, worker | SMTP server hostname. | +| **SMTP_DOMAIN** | — | app, worker | SMTP domain. | +| **SMTP_PORT** | — | app, worker | SMTP port. | +| **SMTP_STARTTLS_AUTO** | `true` | app | Enables STARTTLS automatically. | +| **REDIS_URL** | `redis://cache:6379/0` | app | Redis URL for cache + sessions. | +| **VAPID_PUBLIC_KEY** | — | app | Web Push public key for browser notifications. | +| **VAPID_PRIVATE_KEY** | — | app | Web Push private key (keep secret). | +| **CERTIFICATE_EMAIL** | — | traefik | Email used by Let's Encrypt for certificate issues/renewals. | +| **WEB_CONCURRENCY** | `2` | app | Puma concurrency setting. | +| **LOG_LEVEL** | `info` | app | Log level for Rails. | +| **DECIDIM_FORCE_SSL** | `false` | app | Enforce HTTPS-only traffic. | +| **MAPS_API_KEY** | — | app | API key for maps provider. | +| **MAPS_PROVIDER** | `here` | app | Selects map provider (here, mapbox, google, etc). | diff --git a/docker-compose.yml b/docker-compose.yml index cf5bcd45..d4a9900e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,17 +2,9 @@ version: '3' services: decidim: image: decidim/decidim:latest - entrypoint: ["/code/vendor/hello-world.sh"] - # The entrypoint override above wipes out the CMD - # on the Dockerfile-deploy, so we need to declare it - # again here (https://github.com/docker/compose/issues/3140) command: ["bundle", "exec", "rails", "s", "-b", "0.0.0.0"] ports: - 3000:3000 - volumes: - # Makes our entrypoint scripts available to the container - # under /code/vendor - - ./scripts:/code/vendor environment: - RAILS_ENV=development - DATABASE_HOST=pg diff --git a/install/README.md b/install/README.md new file mode 100644 index 00000000..62cfee39 --- /dev/null +++ b/install/README.md @@ -0,0 +1,468 @@ +# Decidim Installation Guide 🗳️ + +This guide will help you install Decidim on your own server, even with minimal technical knowledge. We'll walk you through everything needed from, setting up a server to launching your digital democratic platform. + +## 📋 Table of Contents + +1. [Prerequisites](#prerequisites) +2. [Step 1: Create a Server](#step-1-create-a-server) +3. [Step 2: Connect to Your Server](#step-2-connect-to-your-server) +4. [Step 3: Configure DNS](#step-3-configure-dns) +5. [Step 4: Install Decidim](#step-4-install-decidim) +6. [Step 5: Configure Email (SMTP)](#step-5-configure-email-smtp) +7. [Step 6: Security & Firewall Setup](#step-6-security--firewall-setup) +8. [Step 7: Complete Setup](#step-7-complete-setup) +9. [Troubleshooting](#troubleshooting) + +--- + +## Prerequisites + +Before you start, you'll need: + +- **A domain name** (like `example.org`) - You can buy one from any domain registrar +- **Patience** - The installation can take about 20-30 minutes + +### Server Requirements + +**Minimum specifications:** +- **RAM**: 2GB minimum (4GB+ recommended for production) +- **Storage**: 20GB+ +- **OS**: Ubuntu 24.04 (required) + +**Recommended providers:** +- **Hetzner** - Used in this guide +- **Here Maps** - For geo-location features +- **SMTP Email Provider** - For sending emails (Gmail, Scaleway, Rapidmail, etc.) + +--- + +## Step 1: Create a Server + +We recommend using Hetzner for affordable, reliable hosting. Here's how to set up a server: + +### 1.1 Create a Hetzner Account + +1. Go to [hetzner.com](https://hetzner.com) +2. Click "Register" and create an account + +This process might take some time until Hetzner verifies your account. + +### 1.2 Create a New Server + +1. Log into your Hetzner account +2. Click "Servers" → "Create Server" +3. **Server Location**: Choose a location near your users +4. **Server Type**: + - Click "Shared" (cheaper option, perfect for small organizations) + - Choose "CAX21" +5. **Image**: Select **Ubuntu 24.04** (important! or a newer LTS if available) +6. **SSH Key** (Recommended): + - Create SSH key if you don't have one + - If unsure, skip this and use password +7. **Server Name**: Give it a name like "decidim-server" +8. Click "Create & Buy Now" + +**How to create an SSH key (if needed)**: + +If you don't have an SSH key, create one on your local machine: + + ```bash +ssh-keygen -t rsa -b 4096 -C "your_email@example.com" + ``` + +This will generate a public/private key pair. Copy the contents of `~/.ssh/id_rsa.pub` and paste it into Hetzner's SSH key field. + +### 1.3 Wait for Server to be Ready + +Your server will be ready in 1-2 minutes. You'll see: +- Server IP address (e.g., `123.45.67.89`) +- Root password (if you chose password instead of SSH key) + +**PLEASE SAVE THIS INFORMATION!! You'll need it immediately!!** + +--- + +## Step 2: Connect to Your Server + +### Option A: Using SSH Key (More Secure) + +If you created an SSH key: +1. Open your terminal (Terminal on Mac, PowerShell on Windows) +2. Type: `ssh root@YOUR_SERVER_IP` +3. Replace `YOUR_SERVER_IP` with your actual IP address + +### Option B: Using Password (Easier) + +1. Open your terminal (Terminal on Mac, PowerShell on Windows) +2. Type: `ssh root@YOUR_SERVER_IP` +3. When prompted for password, paste the root password from Hetzner +4. It won't show characters while typing - that's normal! + +**First time only**: You'll see a warning about "authenticity of host can't be established". Type `yes` and press Enter. + +--- + +## Step 3: Configure DNS + +Before installing Decidim, you need to point your domain to your server. + +### 3.1 Find Your Server's IP Address + +If you lost it, you can find it in the Hetzner dashboard for that server. + +### 3.2 Update DNS Settings + +Go to where you bought your domain (Namecheap, GoDaddy, etc.) and add these DNS records: + +| Type | Host/Name | Value | TTL | +|------|-----------|-------|-----| +| A | @ | YOUR_SERVER_IP | auto | +| CNAME | www | @ | auto | + +**Example**: If your server IP is `123.45.67.89` and your domain is `example.org` and you want the platform to be accessible in `decidim.example.org`, you would set: +- A record: `decidim.example.org` → `123.45.67.89` + +### 3.3 Wait for DNS Propagation + +DNS changes take 5-30 minutes to work worldwide. You can check if it's ready: +```bash +ping decidim.example.org +``` + +--- + +## Step 4: Install Decidim + +Now for the main installation! Run these commands one by one on your server. + +### 4.1 Download and Run the Installer + +```bash +curl -fsSL https://decidim.org/install | bash +``` + +### 4.2 Follow the Prompts + +The installer will ask for: + +**Instance Information:** +- **Organization Name**: e.g., "City Hall Democracy Platform" +- **Domain**: e.g., `decidim.example.org` (must match your DNS setup) + +**Database Configuration:** +- **Local Database** (Recommended): PostgreSQL with auto-generated credentials +- **External Database**: Only if you have your own database server + +**Important Database Notes:** +- The installer creates a secure database user with random password +- Database URL is stored in `/opt/decidim/.env` file +- For production, consider regular backups of PostgreSQL + +**File Storage:** +- **Local Storage** (Default): Perfect for most users +- **S3 Storage**: Only if you use Amazon S3 + +**Configuration Files Generated:** +- `.env`: Contains all your configuration (database, SMTP, secrets) +- `docker-compose.yml`: Defines services and networks +- **Important**: Never commit `.env` to version control - it contains passwords! + +**Email Configuration** (Step 5 covers this in detail) + +**SSL Certificate**: The installer will automatically set up free SSL certificates + +--- + +## Step 5: Configure Email (SMTP) + +Email is crucial for user notifications and password resets. You'll need an SMTP provider. + +### 5.1 Choose an Email Provider + +**Free Options** +- [Gmail SMTP](https://support.google.com/a/answer/176600) (500 emails/day limit) + +**Paid Options** +- [Scaleway](https://www.scaleway.com/en/transactional-email-tem/) +- [Mailgun](https://mailgun.com) + +### 5.2 Get SMTP Settings + +Each provider will give you: +- **SMTP Server**: e.g., `smtp.sendgrid.net` +- **Port**: e.g., `587` +- **Username**: Your email or API key +- **Password**: Your password or API key +- **From Address**: e.g., `noreply@decidim.example.org` + +### 5.3 Configure SMTP during Installation + +When the installer asks for email settings, enter: +- **SMTP Host**: Your provider's SMTP server +- **SMTP Port**: 587 (most common) +- **SMTP Username**: Your SMTP username +- **SMTP Password**: Your SMTP password +- **From Email**: noreply@decidim.example.org +- **From Name**: Your Organization Name + +--- + +## Step 6: Security & Firewall Setup + +### 6.1 Firewall Configuration + +The installer will configure firewall rules automatically. You can check the status: + +```bash +# Check firewall status +sudo ufw status + +# Allow SSH (don't lock yourself out!) +sudo ufw allow ssh + +# Enable firewall if not already active +sudo ufw enable +``` + +### 6.2 SSL Certificate (Automatic) + +The installer uses Traefik to handle SSL certificates automatically through Let's Encrypt: +- **Automatic renewal**: Certificates renew themselves +- **No manual intervention needed** +- **HTTPS enforced**: All traffic redirected to secure connections + +If you experience SSL issues: +- Ensure your domain correctly points to the server IP +- Wait 5-10 minutes for DNS propagation +- Check that ports 80 and 443 are accessible from the internet + +### 6.3 Security Best Practices + +Based on the traditional Decidim setup experience: + +1. **Never expose database**: Use firewalls and network segmentation +2. **Keep software updated**: Run updates regularly +3. **Use strong passwords**: For admin users and database +4. **Monitor logs**: Check for suspicious activity +5. **Backup regularly**: Database and configuration files + +### 6.3 Email Domain Authentication + +For better email deliverability, configure these DNS records (advanced): + +- **SPF Record**: `v=spf1 include:_spf.google.com ~all` (if using Gmail) +- **DKIM**: Generate keys from your email provider +- **DMARC**: `v=DMARC1; p=quarantine; rua=mailto:dmarc@decidim.example.org` + +## Step 7: Complete Setup + +### 7.1 Create System Administrator + +During installation, you'll be prompted to create a system admin: +- **Email**: Use your admin email +- **Password**: The installer will auto-generate a secure password +- **Save the password!** You'll need it to log in + +### 7.2 Access Your Decidim Instance + +1. Open your web browser +2. Go to `https://decidim.example.org/system` +3. Log in with: + - Email: Your system admin email + - Password: The password shown during installation + +### 7.3 Configure Your Organization + +Once logged in, you'll need to: +1. Set up your organization details +2. Create your first participatory space +3. Configure user registration settings + +### 7.4 Background Jobs & Maintenance + +The Docker setup includes automatic background job processing using Sidekiq. Here's what's running: + +**Background Processing**: +- Sidekiq handles email sending and other background tasks +- Automatically restarts if it crashes +- Monitored and managed through Docker Compose + +**To manually check Sidekiq jobs:** +```bash +# Check Sidekiq status +docker compose logs worker + +# Restart Sidekiq if needed +docker compose restart worker +``` + +To have a perfectly functional decidim instance, it's necessary to run some commands in a periodic way. To do so we have provided the installer with a `config/schedule.yml` that will be used by the worker and the `sidekiq-cron` gem. There you will see all the tasks that need to run. Here's a list of some of them: + +- decidim_participatory_processes:change_active_step +- decidim:open_data:export +- decidim:delete_download_your_data_files +- decidim:metrics:all + +In the `schedule.yml` you will find some environment variables that you can configure if you want to disable the execution of some of those jobs. They will be executed thanks to the job in `jobs/invoke_rake_task_job.rb`. + +You can modify the `schedule.yml` file if you want to include some other jobs to be executed, or if there are some new ones. It will still be taken care of from this repository. + +--- + +## 🔧 Useful Commands + +### Managing Your Decidim Instance + +```bash +# Go to the Decidim directory +cd /opt/decidim + +# View live logs +docker compose logs -f + +# Stop Decidim +docker compose down + +# Start Decidim +docker compose up -d + +# Restart services +docker compose restart + +# Check service status +docker compose ps +``` + +### Updating Decidim + +To update to the latest docker image available you can execute the following commands. + +```bash ++# Pull the latest Docker images ++docker compose pull ++# Restart with updated images ++docker compose up -d +``` + +In case there were new files and configurations in a release of the decidim installer, then you would have to execute the script again. + +--- + +## Troubleshooting + +### Common Issues + +**"Connection refused" when connecting to server:** +- Wait 2-3 minutes after server creation +- Check that you're using the correct IP address +- Try `ping YOUR_SERVER_IP` first + +**DNS not working:** +- Wait at least 30 minutes after changing DNS records +- Use `dig example.org` to check DNS +- Make sure A records point to your server IP +- Check records + +**Email not sending:** +- Double-check SMTP settings in `/opt/decidim/.env` +- Verify your firewall allows port 587 outbound +- Check with your email provider about authentication +- **Gmail issues**: May need "App Password" if 2FA enabled +- Check for IPv6 conflicts (installer handles this automatically) + +**Installation fails:** +- Run `curl -fsSL https://decidim.org/install | bash` again +- Check the error message carefully +- Check available disk space +- Verify internet connection + +**Performance issues on low-memory servers:** +- **Memory optimization**: Consider upgrading to 4GB RAM for production +- **Monitor memory**: `free -h` to check usage +- **Check Docker resource limits**: `docker stats` + +**SSL Certificate problems:** +- Domain must resolve to server IP before certificate issuance +- Port 80 and 443 must be accessible from internet +- Check Traefik logs: `docker compose logs traefik` +- Wait 5-10 minutes for certificate issuance + +### Getting Help + +- **Documentation**: [docs.decidim.org](https://docs.decidim.org) +- **Installation Issues**: [GitHub Issues](https://github.com/decidim/docker/issues) +- **Community**: [MetaDecidim](https://meta.decidim.org) + +## 🎯 Advanced Configuration Options + +### Geolocation & Maps + +For mapping features (meeting locations, proposals with addresses): + +1. **Get HERE Maps API Key**: + - Register at [HERE Developer Portal](https://developer.here.com/) + - Create free account and get API credentials + +2. **Configure in Decidim**: + ```bash + # Edit environment file + nano /opt/decidim/.env + # Add: + # MAPS_API_KEY=your-here-api-key + # MAPS_PROVIDER=here + ``` + +3. **Restart services**: + ```bash + docker compose restart + ``` + +### Important Files + +After installation, your configuration is stored in: +- `/opt/decidim/.env` - **All your settings** (database, email, secrets) 🔐 +- `/opt/decidim/docker-compose.yml` - Service definitions +- `/opt/decidim/storage/` - Persistent data (database, uploads, logs) + +**🚨 Security Warning**: +- **Never commit `.env` to version control** +- **Keep backup of `.env` file in secure location** +- **Contains database passwords, SMTP credentials, and secret keys** + +### Log Locations + +For troubleshooting, check these logs: +```bash +# Application logs +docker compose logs -f app + +# Database logs +docker compose logs -f db + +# Worker logs +docker compose logs -f worker + +# All services +docker compose logs -f +``` + +--- + +## 🎉 Congratulations! + +You now have a fully functional Decidim instance running on your own server! + +Your democracy platform is ready to: +- Accept user registrations +- Host discussions and debates +- Run participatory budgeting processes +- Enable collaborative decision-making + +Remember to: +- Regularly update your server (`apt update && apt upgrade`) +- Back up your data +- Monitor your email deliverability +- Engage with your community! + +Happy democracy building! 🗳️✨ diff --git a/install/config/schedule.yml b/install/config/schedule.yml new file mode 100644 index 00000000..9209aedd --- /dev/null +++ b/install/config/schedule.yml @@ -0,0 +1,64 @@ +hourly_active_step_job: + cron: "0 * * * *" + class: "InvokeRakeTaskJob" + queue: default + status: <%= ENV.fetch("CHANGE_ACTIVE_STEP", "enabled") %> # enabled by default + args: + task: decidim_participatory_processes:change_active_step + +daily_open_data_job: + cron: "3 3 * * *" + class: "InvokeRakeTaskJob" + queue: default + status: <%= ENV.fetch("EXPORT_OPEN_DATA", "enabled") %> # enabled by default + args: + task: decidim:open_data:export + +daily_delete_download_your_data_files_job: + cron: "33 3 * * *" + class: "InvokeRakeTaskJob" + queue: default + status: <%= ENV.fetch("DELETE_DOWNLOAD_YOUR_DATA_FILES", "enabled") %> # enabled by default + args: + task: decidim:delete_download_your_data_files + +daily_notifications_digest_job: + cron: "5 18 * * *" + class: "InvokeRakeTaskJob" + queue: mailers + status: <%= ENV.fetch("DAILY_NOTIFICATIONS_DIGEST", "enabled") %> # enabled by default + args: + task: decidim:mailers:notifications_digest_daily + +daily_reminders_job: + cron: "1 11 * * *" + class: "InvokeRakeTaskJob" + queue: default + status: <%= ENV.fetch("SEND_REMINDERS", "enabled") %> # enabled by default + args: + task: decidim:reminders:all + +daily_delete_inactive_participants_job: + cron: "30 1 * * *" + class: "InvokeRakeTaskJob" + queue: default + status: <%= ENV.fetch("DELETE_INACTIVE_PARTICIPANTS", "enabled") %> + args: + task: decidim:participants:delete_inactive_participants + +weekly_clean_registration_forms_job: + cron: "0 3 * * 0" + class: "InvokeRakeTaskJob" + queue: default + status: <%= ENV.fetch("CLEAN_REGISTRATION_FORMS", "enabled") %> # enabled by default + args: + task: decidim_meetings:clean_registration_forms + +weekly_notifications_digest_job: + cron: "5 18 * * 0" + class: "InvokeRakeTaskJob" + queue: mailers + status: <%= ENV.fetch("WEEKLY_NOTIFICATIONS_DIGEST", "enabled") %> # enabled by default + args: + task: decidim:mailers:notifications_digest_weekly + diff --git a/install/config/sidekiq.yml b/install/config/sidekiq.yml new file mode 100644 index 00000000..c9e659c4 --- /dev/null +++ b/install/config/sidekiq.yml @@ -0,0 +1,16 @@ +:concurrency: <%= ENV.fetch("SIDEKIQ_CONCURRENCY", 5) %> +:queues: + - [mailers, 4] + - [vote_reminder, 2] + - [reminders, 2] + - [default, 2] + - [newsletter, 2] + - [newsletters_opt_in, 2] + - [conference_diplomas, 2] + - [events, 2] + - [translations, 2] + - [user_report, 2] + - [block_user, 2] + - [metrics, 1] + - [exports, 1] + - [close_meeting_reminder, 1] diff --git a/install/dependencies/build_env.sh b/install/dependencies/build_env.sh new file mode 100644 index 00000000..3516a9aa --- /dev/null +++ b/install/dependencies/build_env.sh @@ -0,0 +1,289 @@ +#!/bin/bash +set -e +set -u +set -o pipefail + +if [ -z "${REPOSITORY_PATH:-}" ]; then + echo "❌ Error: REPOSITORY_PATH is not set." + exit 1 +fi + +BUILD_ENV_PATH="$REPOSITORY_PATH/.env" + +COMPOSE_PROFILES="" + +echo "───────────────────────────────────────────────" +echo "🔧 Environment Configuration Phase" +echo " We'll now collect all the information needed to configure your Decidim instance." +echo " All responses will be saved in a .env file that you can edit later." +echo +echo "📝 Information we'll collect:" +echo " • Instance details (name, domain)" +echo " • Database settings (local PostgreSQL or external)" +echo " • Email configuration (SMTP server)" +echo " • File storage (local filesystem or S3 bucket)" +echo " • Security keys (auto-generated)" +echo +echo "💡 Don't worry if you don't have all the details ready!" +echo " You can always modify the .env file after installation." +echo +echo "Press Enter to continue..." +read -r "$BUILD_ENV_PATH" <>"$BUILD_ENV_PATH" </dev/null 2>&1; then + echo "🐳 Docker not found. Installing Docker..." + echo "⚠️ After installation, you'll need to re-run this script for changes to take effect." + echo "💡 You can run 'newgrp docker' to activate Docker group membership." + + if ! curl -fsSL https://get.docker.com | bash; then + echo "❌ Failed to install Docker" + exit 1 + fi + + if ! sudo usermod -aG docker "${USER}"; then + echo "❌ Failed to add user to Docker group" + exit 1 + fi + + echo "" + echo "🔄 Docker installation completed!" + echo "📋 Next steps:" + echo " 1. Log out and log back in, OR run: 'newgrp docker'" + echo " 2. Re-run this installation script" + echo "" + echo "⏹️ Exiting for user session refresh..." + exit 1 +else + echo "✅ Docker is installed: $(docker --version)" + + # Check if user can run docker commands + if ! docker info >/dev/null 2>&1; then + echo "⚠️ Docker is installed but current user cannot run Docker commands." + echo "💡 Try running: 'newgrp docker' or log out and log back in." + echo " If that doesn't work, you may need to: 'sudo usermod -aG docker \$USER'" + exit 1 + fi + + echo "✅ Docker is accessible for current user!" +fi diff --git a/install/dependencies/create_system_admin.sh b/install/dependencies/create_system_admin.sh new file mode 100644 index 00000000..f7a51f04 --- /dev/null +++ b/install/dependencies/create_system_admin.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e +set -u +set -o pipefail + +generate_system_admin() { + read -r -p "email: " SYSTEM_EMAIL /dev/null; do + echo "Waiting for Rails server to start..." + sleep 10 +done + +echo "Container is running correctly... Now we are going to create the system admin." + +generate_system_admin + +if [ $? -eq 1 ]; then + echo "❌ Seems like there was a problem creating the system admin." + echo + echo "🔧 Troubleshooting:" + echo " • Try running the command manually:" + echo " docker exec -ti decidim bin/rails decidim_system:create_admin" + echo " • Review the logs for any errors:" + echo " docker compose logs decidim" +else + echo "✅ System administrator created successfully!" + echo "Your password to access is: ${SYSTEM_PASSWORD}" + echo "📍 You can now access the admin panel at: https://${DECIDIM_DOMAIN}/system" +fi diff --git a/install/dependencies/decidim_version.sh b/install/dependencies/decidim_version.sh new file mode 100644 index 00000000..fa8e170b --- /dev/null +++ b/install/dependencies/decidim_version.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +echo "───────────────────────────────────────────────" +echo "📦 Decidim version" +echo +echo "💡 About Decidim versions:" +echo " • latest - Most recent stable release (recommended)" +echo " • Custom images - Your own modified Decidim builds" +echo +echo "If you want, later on you can modify the 'docker-compose.yml' to change the Decidim version." +echo +echo "Default image: decidim/decidim:latest" +echo + +while true; do + read -r -p "👉 Press enter to continue with the download of the Decidim image" Gemfile.wrapper <Gemfile.local <>Gemfile.local +fi diff --git a/install/dependencies/generate_vapid_keys.sh b/install/dependencies/generate_vapid_keys.sh new file mode 100644 index 00000000..81c35e49 --- /dev/null +++ b/install/dependencies/generate_vapid_keys.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e +set -u +set -o pipefail + +echo "🔐 Generating VAPID keys..." + +# Check if DECIDIM_IMAGE is set +if [ -z "${DECIDIM_IMAGE:-}" ]; then + echo "❌ Error: DECIDIM_IMAGE is not set" + exit 1 +fi + +output=$(docker run --rm \ + "$DECIDIM_IMAGE" \ + bin/rails decidim:pwa:generate_vapid_keys) + +echo "✅ The VAPID keys have been generated correctly!" + +VAPID_PUBLIC_KEY=$(echo "$output" | grep 'VAPID_PUBLIC_KEY' | cut -d'=' -f2) +VAPID_PRIVATE_KEY=$(echo "$output" | grep 'VAPID_PRIVATE_KEY' | cut -d'=' -f2) + +# Export the keys for use by calling script +export VAPID_PUBLIC_KEY +export VAPID_PRIVATE_KEY + +echo "🔑 Keys successfully extracted!" diff --git a/install/dependencies/open_ports.sh b/install/dependencies/open_ports.sh new file mode 100644 index 00000000..6970e6ab --- /dev/null +++ b/install/dependencies/open_ports.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +open_ports() { + echo + echo "To handle the SSL certificate, we will have to open the port 80 and the port 443." + echo + + if ! command -v ufw; then + echo "UFW nos intalled. We are going to install it to allow openning ports 80 and 443 on this machine." + sudo apt install ufw + fi + + sudo ufw allow 22 + sudo ufw allow 80 + sudo ufw allow 443 + sudo ufw --force enable +} + +echo "───────────────────────────────────────────────" +echo "Now we are going to open the necessary ports for Decidim to work ussing UFW." +echo "This is a standard practice to protect your server." +echo +read -r -p "Can we proceed openning ports 22, 80 and 443? [Y/n] " yn /dev/null | grep -Eq "Ubuntu|Debian"; then + echo "This installation process must be run on a Debian/Ubuntu distribution." + exit 1 +fi + +echo "✅ Correct distribution." diff --git a/install/docker-compose.yml b/install/docker-compose.yml new file mode 100644 index 00000000..d6353491 --- /dev/null +++ b/install/docker-compose.yml @@ -0,0 +1,128 @@ +services: + app: + image: ${DECIDIM_IMAGE:-decidim/decidim:latest} + command: ["bin/rails", "server", "-b", "0.0.0.0"] + entrypoint: ["/code/entrypoint.sh"] + restart: always + depends_on: + - cache + expose: + - 3000 + volumes: + - ${PWD}/Gemfile.wrapper:/code/Gemfile.wrapper + - ${PWD}/Gemfile.local:/code/Gemfile.local + - ${PWD}/scripts/entrypoint.sh:/code/entrypoint.sh + - app_gems:/usr/local/bundle + - storage_data:/code/storage + - migrations_data:/code/db/migrate + labels: + - "traefik.enable=true" + - "traefik.http.routers.app.rule=Host(`$$DECIDIM_DOMAIN`)" + - "traefik.http.routers.app.entrypoints=websecure" + - "traefik.http.routers.app.tls=true" + - "traefik.http.routers.app.tls.certresolver=myresolver" + - "traefik.http.services.app.loadbalancer.server.port=3000" + env_file: + - .env + environment: + - BUNDLE_GEMFILE=Gemfile.wrapper + - DATABASE_URL + - SECRET_KEY_BASE + - DECIDIM_FORCE_SSL=${DECIDIM_FORCE_SSL:-false} + - REDIS_URL=${REDIS_URL:-redis://cache:6379/0} + - WEB_CONCURRENCY=${WEB_CONCURRENCY:-2} + - LOG_LEVEL=${LOG_LEVEL:-info} + - DECIDIM_ENABLE_HTML_HEADER_SNIPPETS + - SMTP_STARTTLS_AUTO=${SMTP_STARTTLS_AUTO:-true} + - QUEUE_ADAPTER=sidekiq + - SMTP_USERNAME + - SMTP_PASSWORD + - SMTP_ADDRESS + - SMTP_DOMAIN + - SMTP_PORT + - DECIDIM_MAILER_SENDER + - MAPS_API_KEY + - MAPS_PROVIDER=here + worker: + image: ${DECIDIM_IMAGE:-decidim/decidim:latest} + command: ["bundle", "exec", "sidekiq", "-C", "config/sidekiq.yml"] + entrypoint: ["/code/sidekiq_entrypoint.sh"] + pull_policy: always + restart: always + env_file: + - .env + volumes: + - ${PWD}/Gemfile.wrapper:/code/Gemfile.wrapper + - ${PWD}/Gemfile.local:/code/Gemfile.local + - ${PWD}/scripts/sidekiq_entrypoint.sh:/code/sidekiq_entrypoint.sh + - ${PWD}/config/sidekiq.yml:/code/config/sidekiq.yml + - ${PWD}/config/schedule.yml:/code/config/schedule.yml + - ${PWD}/jobs/invoke_rake_task_job.rb:/code/app/jobs/invoke_rake_task_job.rb + - worker_gems:/usr/local/bundle + - storage_data:/code/storage + - migrations_data:/code/db/migrate + environment: + - BUNDLE_GEMFILE=Gemfile.wrapper + - DATABASE_URL + - SECRET_KEY_BASE + - DECIDIM_FORCE_SSL=false + - QUEUE_ADAPTER=sidekiq + - REDIS_URL=${REDIS_URL:-redis://cache:6379/0} + - SMTP_USERNAME + - SMTP_PASSWORD + - SMTP_ADDRESS + - SMTP_DOMAIN + - SMTP_PORT + - DECIDIM_MAILER_SENDER + links: + - cache + traefik: + image: traefik:v3.6 + command: + - --api + - --api.dashboard=false + - --providers.docker=true + - --log.level=DEBUG + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --entrypoints.traefik.address=:8080 + - --entrypoints.web.http.redirections.entryPoint.to=websecure + - --entrypoints.web.http.redirections.entryPoint.scheme=https + - --certificatesresolvers.myresolver.acme.email=${CERTIFICATE_EMAIL} + - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json + - --certificatesresolvers.myresolver.acme.httpchallenge=true + - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web + ports: + - "80:80" + - "443:443" + restart: unless-stopped + volumes: + - traefik_data:/letsencrypt + - "/var/run/docker.sock:/var/run/docker.sock:ro" + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.service=api@internal" + db: + image: postgres:17-alpine + profiles: ["db"] + env_file: + - .env + environment: + POSTGRES_USER: ${DATABASE_USER:-decidim} + POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-decidim} + POSTGRES_DB: ${DATABASE_NAME:-decidim} + volumes: + - pg_data:/var/lib/postgresql/data + cache: + image: redis:8-alpine + volumes: + - cache_data:/data + restart: always +volumes: + app_gems: {} + worker_gems: {} + pg_data: {} + cache_data: {} + storage_data: {} + migrations_data: {} + traefik_data: {} diff --git a/install/install.sh b/install/install.sh new file mode 100644 index 00000000..a005459c --- /dev/null +++ b/install/install.sh @@ -0,0 +1,150 @@ +#!/bin/bash +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +set -e +set -u +set -o pipefail + +echo -e "***********************************************************************" +echo -e "* 🚀 Welcome to Decidim Installation Script! *" +echo -e "* *" +echo -e "* This script will install Decidim on this machine and guide you *" +echo -e "* through the complete configuration process. *" +echo -e "* *" +echo -e "* You'll need to provide: *" +echo -e "* • Instance name and domain *" +echo -e "* • Database configuration (local or external) *" +echo -e "* • SMTP server settings for emails *" +echo -e "* • File storage settings (local or S3) *" +echo -e "* *" +echo -e "* 💡 All dependencies (Docker, etc) will be installed for you. *" +echo -e "* *" +echo -e "* ⚠️ For production use, review security settings and documentation. *" +echo -e "* *" +echo -e "***********************************************************************" + +REPOSITORY_PATH=${REPOSITORY_PATH:-/opt/decidim} +REPOSITORY_URL="https://github.com/decidim/docker" + +export REPOSITORY_URL + +echo "📁 Installation directory: $REPOSITORY_PATH" + +trap 'echo "❌ Error occurred at line $LINENO. You can re-run this script to restart the installation."' ERR + +if [ ! -d "$REPOSITORY_PATH" ]; then + echo "📁 Creating installation directory: $REPOSITORY_PATH" + if ! sudo mkdir -p "$REPOSITORY_PATH"; then + echo "❌ Failed to create directory $REPOSITORY_PATH" + exit 1 + fi + if ! sudo chown "$USER":"$USER" "$REPOSITORY_PATH"; then + echo "❌ Failed to set ownership of $REPOSITORY_PATH" + exit 1 + fi +fi + +TMP="/tmp/decidim-docker-files" +if [ ! -d "$TMP" ]; then + mkdir "$TMP" +fi + +echo "📥 Downloading the necessary installation files." +curl -fsSL \ + --retry 3 \ + --retry-delay 2 \ + --connect-timeout 30 \ + --max-time 300 \ + --progress-bar \ + -o "$TMP/deploy.zip" \ + "$REPOSITORY_URL/releases/download/latest/deploy.zip" + +echo "📦 Installing unzip package..." +if ! (sudo apt update && sudo apt install unzip -y); then + echo "❌ Failed to install unzip package" + exit 1 +fi + +echo "📂 Extracting files to $REPOSITORY_PATH..." +if ! unzip -u -o "$TMP/deploy.zip" -d "$REPOSITORY_PATH" /dev/null +groupmod -g "$USER_GID" decidim 2>/dev/null +usermod -g "$USER_GID" decidim 2>/dev/null + +chown -R -h "$USER_UID" "$BUNDLE_PATH" +chgrp -R -h "$USER_GID" "$BUNDLE_PATH" + +# Check all the gems are installed or fails. +if ! bundle check; then + echo "❌ Gems in Gemfile are not installed. Installing them with \"bundle install\"..." + bundle install +else + echo "✅ Gems in Gemfile are installed!" +fi + +# Check to see if there are migrations to install +bundle exec rake railties:install:migrations + +# Check no migrations are pending migrations +if [ -z "$SKIP_MIGRATIONS" ]; then + bundle exec rails db:migrate + echo "✅ Migrations are all up!" +else + echo "⚠️ Skipping migrations!" +fi + +echo "🚀" "$@" +exec "$@" diff --git a/install/scripts/sidekiq_entrypoint.sh b/install/scripts/sidekiq_entrypoint.sh new file mode 100755 index 00000000..6b4d0bb4 --- /dev/null +++ b/install/scripts/sidekiq_entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh -x + +# Check all the gems are installed or fails. +if ! bundle check; then + echo "❌ Gems within the Gemfile are not installed. Installing them with \"bundle install\"..." + if ! bundle install; then + echo "❌ bundle install failed." + exit 1 + fi +else + echo "✅ Gems in Gemfile are installed!" +fi + +echo "🚀" "$@" +exec "$@" diff --git a/install/up.sh b/install/up.sh new file mode 100644 index 00000000..a7c39b56 --- /dev/null +++ b/install/up.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e +set -u +set -o pipefail + + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPOSITORY_PATH="${REPOSITORY_PATH:-$SCRIPT_DIR}" +ENV_FILE="${REPOSITORY_PATH}/.env" + +# Check if .env file exists +if [ ! -f "$ENV_FILE" ]; then + echo "❌ Error: .env file not found at $ENV_FILE" + echo "Please run the installation script first or create the .env file manually." + exit 1 +fi + +echo "🚀 Starting Decidim containers..." + +docker compose --env-file "$ENV_FILE" up -d + +echo "📋 Displaying recent container logs..." +docker compose logs --tail=30 + +echo "✅ Containers started successfully!" +echo "🔍 You can monitor logs with: docker compose logs -f" diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh deleted file mode 100644 index c48d1f87..00000000 --- a/scripts/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -x - -USER_UID=$(stat -c %u /code/Gemfile) -USER_GID=$(stat -c %g /code/Gemfile) - -export USER_UID -export USER_GID - -usermod -u "$USER_UID" decidim 2> /dev/null -groupmod -g "$USER_GID" decidim 2> /dev/null -usermod -g "$USER_GID" decidim 2> /dev/null - -chown -R -h "$USER_UID" "$BUNDLE_PATH" -chgrp -R -h "$USER_GID" "$BUNDLE_PATH" - -/usr/bin/sudo -EH -u decidim "$@" diff --git a/scripts/hello-world.sh b/scripts/hello-world.sh deleted file mode 100755 index bf467914..00000000 --- a/scripts/hello-world.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -set -e - -# runs db:drop, db:create and db:migrate. -# We can't use db:schema:load because we don't have the db/schema.rb -# file when we create the app for the first time and migrations haven't -# been run yet. -bundle exec rake db:migrate:reset -# Adds basic system, admin and user accounts, and lorem ipsum content. -bundle exec rake db:seed - -exec "$@"