From a0a495d804319c3f7ad179baba7b87b41f1dc1f3 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Tue, 4 Aug 2026 21:11:25 +0200 Subject: Rework the README/INSTALL documents and provide a bootstrap script (untested ;) --- lib/tasks/init.rake | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 lib/tasks/init.rake (limited to 'lib') diff --git a/lib/tasks/init.rake b/lib/tasks/init.rake new file mode 100644 index 00000000..7e3d8dcc --- /dev/null +++ b/lib/tasks/init.rake @@ -0,0 +1,75 @@ +namespace :cccms do + desc "Bootstrap a fresh installation: the node skeleton and one admin " \ + "account. Idempotent -- every step finds before it creates, so " \ + "re-running after a new step is added is safe. " \ + "Requires ADMIN_PASS. ADMIN_LOGIN and ADMIN_EMAIL are optional. " \ + "The admin is created without the role and promoted with " \ + "update_column, because admin_needs_second_factor refuses a NEW " \ + "admin without an enrolled factor -- it exempts retention, not " \ + "creation. The account therefore cannot do user management until " \ + "it enrols a second factor and signs in again; see INSTALL.md." + task :init => :environment do + password = ENV["ADMIN_PASS"].to_s + abort "usage: ADMIN_PASS=secret bundle exec rake cccms:init" if password.empty? + abort "ADMIN_PASS must be at least 6 characters" if password.length < 6 + + login = ENV.fetch("ADMIN_LOGIN", "admin") + email = ENV.fetch("ADMIN_EMAIL", "admin@example.org") + + # publish_draft! is called with no user, which guard_live_change! treats + # as a trusted system context -- the documented nil-user path, and the + # reason a rake task can publish into /updates and /disclosure at all. + ensure_node = lambda do |parent, slug, title, body| + existing = parent ? parent.children.find_by(:slug => slug) : Node.root + if existing + puts format(" %-14s exists (%d)", slug || "root", existing.id) + next existing + end + + node = parent ? parent.children.create!(:slug => slug) : Node.create! + Globalize.with_locale(I18n.default_locale) do + node.draft.update!(:title => title, :body => body.to_s) + end + node.publish_draft! + puts format(" %-14s created (%d)", slug || "root", node.id) + node + end + + puts "Node skeleton:" + root = ensure_node.(nil, nil, "CCC", "") + + # Referencing it is enough: Node.trash self-creates on first call. + puts format(" %-14s ready (%d)", "trash", Node.trash.id) + + ensure_node.(root, "home", "Startseite", "") + + ensure_node.(root, "updates", "Updates", + '[aggregate tags="update" limit="30" order_by="published_at" order_direction="DESC"]') + + ensure_node.(root, "disclosure", "Disclosure", "") + + club = ensure_node.(root, "club", "Chaos Computer Club", "") + ensure_node.(club, "erfas", "Erfa-Kreise", + '[aggregate children="direct" order_by="slug" partial="chapter"]') + ensure_node.(club, "chaostreffs", "Chaostreffs", + '[aggregate children="direct" order_by="slug" partial="chapter"]') + + puts + if User.any? + puts "Accounts exist already; skipping admin creation." + else + user = User.create!(:login => login, :email => email, + :password => password, + :password_confirmation => password) + user.update_column(:roles, %w[admin redaktion]) + puts "Created #{user.login} <#{user.email}> as admin + redaktion." + puts + puts "This account has no second factor, so it cannot yet create" + puts "users, reset factors or deactivate accounts. To finish:" + puts " 1. sign in as #{user.login}" + puts " 2. Mein Konto -> enable second factor, scan the QR, confirm" + puts " 3. sign out and sign in again, entering the code" + puts "Elevation is granted at that login and user management unlocks." + end + end +end -- cgit v1.3