# ConsulDemocracy Cloudron Package # # ConsulDemocracy is the citizen participation platform behind Madrid's # Consul (MIT): proposals, participatory budgeting, debates, polls, # legislation co-editing. Rails 8 + PostgreSQL, single web process plus # a delayed_job worker; memcached for the fragment cache. # # Upstream: https://github.com/consuldemocracy/consuldemocracy # - No published image; the in-repo Dockerfile targets local dev # (bind-mount uid remapping, Chromium for E2E tests). This package # builds from source on the same ruby base, trimmed: no Chromium, # no sudo, fixed non-root user, runtime configs via symlinks into # /app/data (generated by start.sh on every boot). # # Authentication: NATIVE OIDC (preferred). devise + omniauth with # omniauth_openid_connect (name: :oidc, discovery: true) configured # per-environment in config/secrets.yml (oidc_client_id / # oidc_client_secret / oidc_issuer - see devise.rb:289-296 and # app/lib/omniauth_tenant_setup.rb). start.sh maps the Cloudron # platform provider (CLOUDRON_OIDC_*) into the generated secrets.yml. # SAML is also available; local login remains for the seeded admin. # # Pattern: build-from-source (Rails). Gemfile.lock + package.json are # committed upstream, so bundle/npm install are deterministic. FROM ruby:3.4.10-trixie ENV RAILS_ROOT=/var/www/consul \ RAILS_ENV=production \ RAILS_LOG_TO_STDOUT=true # runtime deps: imagemagick (upload processing), libpq + postgres client # (pg gem + db tasks), memcached (production cache_store is mem_cache_store # with dalli), plus build headers for the native gems below RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends \ build-essential cmake pkg-config imagemagick libpq-dev \ postgresql-client memcached \ && rm -rf /var/lib/apt/lists/* RUN adduser --shell /bin/bash --disabled-password --gecos "" --uid 1000 consul WORKDIR $RAILS_ROOT # gem layer first (cache marker): lockfile-driven, system-wide install # (.ruby-version MUST keep its name: the Gemfile resolves ruby file: ".ruby-version") COPY repo/.ruby-version ./ COPY repo/Gemfile repo/Gemfile.lock ./ # the Gemfile evals ./Gemfile_custom (developer-local, not in git); empty is valid RUN touch Gemfile_custom && \ bundle config set --local without 'development test' && bundle install --jobs 4 # node for the asset pipeline (node-build pinned by .node-version) COPY repo/.node-version ./ ENV PATH=/usr/local/node/bin:$PATH RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ /tmp/node-build-master/bin/node-build "$(cat .node-version)" /usr/local/node && \ rm -rf /tmp/node-build-master COPY repo/package.json repo/package-lock.json ./ RUN npm install COPY repo/ ./ # Rails 8's regexp_timeout default kills assets:precompile on the # graphiql minified-JS regexes under CPU contention; disable it (the # platform proxy bounds request time anyway). Also let the runtime user # write tmp/log/public/assets and db/ (apartment schema dump warning). RUN echo 'Regexp.timeout = nil' > config/initializers/00_cloudron_regexp_timeout.rb && \ mkdir -p public/assets tmp/pids tmp/sockets log db && \ chown -R consul:consul public/assets tmp log db # persistence: runtime-generated configs + marker files live in localstorage # (repo ships only .example files, the real ones are generated at start). # VOLUME + ownership: a fresh named volume inherits this dir's owner, so # start.sh (running as consul, uid 1000) can write its generated configs RUN mkdir -p /app/data && chown consul:consul /app/data && \ ln -s /app/data/secrets.yml config/secrets.yml && \ ln -s /app/data/database.yml config/database.yml VOLUME /app/data COPY start.sh /app/start.sh EXPOSE 3000 USER consul ENTRYPOINT ["/bin/bash", "/app/start.sh"]