Merge pull request #117 from basecamp/modernize-scripts

Modernize scripts
This commit is contained in:
Stanko Krtalić
2025-12-01 12:01:13 +01:00
committed by GitHub
5 changed files with 281 additions and 216 deletions

View File

@@ -59,6 +59,12 @@ announcing "Tagging as #{VERSION}" do
end
end
announcing "Creating GitHub release..." do
on(:local) do
execute :gh, :release, :create, "v#{VERSION}", "--notes-from-tag"
end
end
announcing "Building and pushing image..." do
versioning = "--build-arg APP_VERSION=#{VERSION} --build-arg GIT_REVISION=#{GIT_SHA}"

160
bin/setup
View File

@@ -1,77 +1,107 @@
#!/usr/bin/env ruby
require "fileutils"
require "socket"
require "timeout"
#!/usr/bin/env bash
set -eo pipefail
REDIS_PORT = 6379
REDIS_HOST = "localhost"
APP_ROOT = File.expand_path("..", __dir__)
# Prefer app executables
app_root="$(
cd "$(dirname "$0")/.."
pwd
)"
export PATH="$app_root/bin:$PATH"
def system!(*args)
system(*args, exception: true)
end
REDIS_PORT=6379
REDIS_HOST=localhost
def installed?(tool)
system("command -v #{tool} > /dev/null 2>&1")
end
if [ "$RAILS_ENV" = "production" ]; then
echo "RAILS_ENV is production; bailing out"
exit 1
fi
def redis_running?
Timeout::timeout(3) do
socket = TCPSocket.new(REDIS_HOST, REDIS_PORT)
socket.close
return true
end
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError, Timeout::Error
return false
end
if ENV["RAILS_ENV"] == "production"
puts "RAILS_ENV is production; bailing out"
exit
end
FileUtils.chdir APP_ROOT do
puts "== Installing dependencies =="
if installed?("brew")
system "brew install sqlite ffmpeg mise"
elsif installed?("pacman")
system "sudo pacman -S --noconfirm --needed sqlite ffmpeg mise"
elsif installed?("apt")
system "sudo apt-get install --no-install-recommends -y libsqlite3-0 ffmpeg"
end
if installed?("mise")
system "mise install"
# Install gum if needed
if ! command -v gum &>/dev/null; then
echo
echo "▸ Installing gum"
if command -v pacman &>/dev/null; then
sudo pacman -S --noconfirm gum
elif command -v brew &>/dev/null; then
brew install gum
else
puts "Couldn't install mise"
puts "Install mise using your package manager or via:"
puts "https://mise.jdx.dev/installing-mise.html"
echo "Please install gum: https://github.com/charmbracelet/gum"
exit 1
end
fi
echo
fi
system("bundle check") || system!("bundle install")
step() {
local step_name="$1"
shift
puts "\n== Preparing database =="
if ARGV.include?("--reset")
system "rm -rf ./storage/{db,files}"
system! "bin/rails db:reset"
end
system! "bin/rails db:prepare"
gum style --foreground 208 --bold "▸ $step_name"
gum style --foreground 240 "$*"
unless redis_running?
if installed?("docker")
system("docker run -d --name campfire-redis -p #{REDIS_PORT}:#{REDIS_PORT} redis:7")
"$@"
local exit_code=$?
echo
return $exit_code
}
redis_running() {
nc -z "$REDIS_HOST" "$REDIS_PORT" 2>/dev/null
}
echo
gum style --foreground 214 " ) "
gum style --foreground 208 " ) \\ campfire"
gum style --foreground 202 " ( ( ("
gum style --foreground 94 " .^^^."
echo
# Install dependencies
if command -v brew &>/dev/null; then
step "Installing packages" brew install sqlite ffmpeg mise
elif command -v pacman &>/dev/null; then
step "Installing packages" sudo pacman -S --noconfirm --needed sqlite ffmpeg mise
elif command -v apt &>/dev/null; then
step "Installing packages" sudo apt-get install --no-install-recommends -y libsqlite3-0 ffmpeg
fi
if ! command -v mise &>/dev/null; then
echo "Couldn't install mise"
echo "Install mise using your package manager or via:"
echo "https://mise.jdx.dev/installing-mise.html"
exit 1
fi
step "Installing Ruby" mise install --yes
eval "$(mise hook-env)"
bundle config set --local auto_install true
step "Installing RubyGems" bundle install
# Prepare database
if [[ $* == *--reset* ]]; then
rm -rf ./storage/{db,files}
step "Resetting the database" rails db:reset
fi
step "Preparing the database" rails db:prepare
# Start Redis if not running
if ! redis_running; then
if command -v docker &>/dev/null; then
if docker ps -aq -f name=campfire-redis | grep -q .; then
step "Starting Redis" docker start campfire-redis
else
puts "Couldn't start Redis"
puts "Install either docker or redis and then run this command again"
exit 1
end
end
step "Setting up Redis" docker run -d --name campfire-redis -p "$REDIS_PORT:$REDIS_PORT" redis:7
fi
else
echo "Couldn't start Redis"
echo "Install either docker or redis and then run this command again"
exit 1
fi
fi
puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"
step "Cleaning up logs and tempfiles" rails log:clear tmp:clear
puts "\n== Restarting services =="
system! "bin/rails restart"
end
step "Restarting services" rails restart
gum style --foreground 46 "✓ Done (${SECONDS} sec)"

View File

@@ -16,8 +16,5 @@ module Campfire
# Fallback to English if translation key is missing
config.i18n.fallbacks = true
# Use SQL schema format to include search-related objects
config.active_record.schema_format = :sql
end
end

180
db/schema.rb generated Normal file
View File

@@ -0,0 +1,180 @@
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2025_11_26_115722) do
create_table "accounts", force: :cascade do |t|
t.datetime "created_at", null: false
t.text "custom_styles"
t.string "join_code", null: false
t.string "name", null: false
t.datetime "updated_at", null: false
end
create_table "action_text_rich_texts", force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.string "name", null: false
t.bigint "record_id", null: false
t.string "record_type", null: false
t.datetime "updated_at", null: false
t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
end
create_table "active_storage_attachments", force: :cascade do |t|
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.string "name", null: false
t.bigint "record_id", null: false
t.string "record_type", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.bigint "byte_size", null: false
t.string "checksum"
t.string "content_type"
t.datetime "created_at", null: false
t.string "filename", null: false
t.string "key", null: false
t.text "metadata"
t.string "service_name", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", force: :cascade do |t|
t.bigint "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "bans", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "ip_address", null: false
t.datetime "updated_at", null: false
t.integer "user_id", null: false
t.index ["ip_address"], name: "index_bans_on_ip_address"
t.index ["user_id"], name: "index_bans_on_user_id"
end
create_table "boosts", force: :cascade do |t|
t.integer "booster_id", null: false
t.string "content", limit: 16, null: false
t.datetime "created_at", null: false
t.integer "message_id", null: false
t.datetime "updated_at", null: false
t.index ["booster_id"], name: "index_boosts_on_booster_id"
t.index ["message_id"], name: "index_boosts_on_message_id"
end
create_table "memberships", force: :cascade do |t|
t.datetime "connected_at"
t.integer "connections", default: 0, null: false
t.datetime "created_at", null: false
t.string "involvement", default: "mentions"
t.integer "room_id", null: false
t.datetime "unread_at"
t.datetime "updated_at", null: false
t.integer "user_id", null: false
t.index ["room_id", "created_at"], name: "index_memberships_on_room_id_and_created_at"
t.index ["room_id", "user_id"], name: "index_memberships_on_room_id_and_user_id", unique: true
t.index ["room_id"], name: "index_memberships_on_room_id"
t.index ["user_id"], name: "index_memberships_on_user_id"
end
create_table "messages", force: :cascade do |t|
t.string "client_message_id", null: false
t.datetime "created_at", null: false
t.integer "creator_id", null: false
t.integer "room_id", null: false
t.datetime "updated_at", null: false
t.index ["creator_id"], name: "index_messages_on_creator_id"
t.index ["room_id"], name: "index_messages_on_room_id"
end
create_table "push_subscriptions", force: :cascade do |t|
t.string "auth_key"
t.datetime "created_at", null: false
t.string "endpoint"
t.string "p256dh_key"
t.datetime "updated_at", null: false
t.string "user_agent"
t.integer "user_id", null: false
t.index ["endpoint", "p256dh_key", "auth_key"], name: "idx_on_endpoint_p256dh_key_auth_key_7553014576"
t.index ["user_id"], name: "index_push_subscriptions_on_user_id"
end
create_table "rooms", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "creator_id", null: false
t.string "name"
t.string "type", null: false
t.datetime "updated_at", null: false
end
create_table "searches", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "query", null: false
t.datetime "updated_at", null: false
t.integer "user_id", null: false
t.index ["user_id"], name: "index_searches_on_user_id"
end
create_table "sessions", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "ip_address"
t.datetime "last_active_at", null: false
t.string "token", null: false
t.datetime "updated_at", null: false
t.string "user_agent"
t.integer "user_id", null: false
t.index ["token"], name: "index_sessions_on_token", unique: true
t.index ["user_id"], name: "index_sessions_on_user_id"
end
create_table "users", force: :cascade do |t|
t.text "bio"
t.string "bot_token"
t.datetime "created_at", null: false
t.string "email_address"
t.string "name", null: false
t.string "password_digest"
t.integer "role", default: 0, null: false
t.integer "status", default: 0, null: false
t.datetime "updated_at", null: false
t.index ["bot_token"], name: "index_users_on_bot_token", unique: true
t.index ["email_address"], name: "index_users_on_email_address", unique: true
end
create_table "webhooks", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "url"
t.integer "user_id", null: false
t.index ["user_id"], name: "index_webhooks_on_user_id"
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "bans", "users"
add_foreign_key "boosts", "messages"
add_foreign_key "messages", "rooms"
add_foreign_key "messages", "users", column: "creator_id"
add_foreign_key "push_subscriptions", "users"
add_foreign_key "searches", "users"
add_foreign_key "sessions", "users"
add_foreign_key "webhooks", "users"
# Virtual tables defined in this database.
# Note that virtual tables may not work with other database engines. Be careful if changing database.
create_virtual_table "message_search_index", "fts5", ["body", "tokenize=porter"]
end

View File

@@ -1,148 +0,0 @@
CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
CREATE TABLE IF NOT EXISTS "push_subscriptions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "endpoint" varchar, "p256dh_key" varchar, "auth_key" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_agent" varchar, CONSTRAINT "fk_rails_43d43720fc"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
);
CREATE INDEX "index_push_subscriptions_on_user_id" ON "push_subscriptions" ("user_id");
CREATE INDEX "idx_on_endpoint_p256dh_key_auth_key_7553014576" ON "push_subscriptions" ("endpoint", "p256dh_key", "auth_key");
CREATE TABLE IF NOT EXISTS "active_storage_attachments" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "record_type" varchar NOT NULL, "record_id" bigint NOT NULL, "blob_id" bigint NOT NULL, "created_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c3b3935057"
FOREIGN KEY ("blob_id")
REFERENCES "active_storage_blobs" ("id")
);
CREATE INDEX "index_active_storage_attachments_on_blob_id" ON "active_storage_attachments" ("blob_id");
CREATE UNIQUE INDEX "index_active_storage_attachments_uniqueness" ON "active_storage_attachments" ("record_type", "record_id", "name", "blob_id");
CREATE TABLE IF NOT EXISTS "active_storage_variant_records" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "blob_id" bigint NOT NULL, "variation_digest" varchar NOT NULL, CONSTRAINT "fk_rails_993965df05"
FOREIGN KEY ("blob_id")
REFERENCES "active_storage_blobs" ("id")
);
CREATE UNIQUE INDEX "index_active_storage_variant_records_uniqueness" ON "active_storage_variant_records" ("blob_id", "variation_digest");
CREATE VIRTUAL TABLE message_search_index using fts5(body, tokenize=porter)
/* message_search_index(body) */;
CREATE TABLE IF NOT EXISTS 'message_search_index_data'(id INTEGER PRIMARY KEY, block BLOB);
CREATE TABLE IF NOT EXISTS 'message_search_index_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
CREATE TABLE IF NOT EXISTS 'message_search_index_content'(id INTEGER PRIMARY KEY, c0);
CREATE TABLE IF NOT EXISTS 'message_search_index_docsize'(id INTEGER PRIMARY KEY, sz BLOB);
CREATE TABLE IF NOT EXISTS 'message_search_index_config'(k PRIMARY KEY, v) WITHOUT ROWID;
CREATE TABLE IF NOT EXISTS "accounts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "join_code" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "custom_styles" text);
CREATE TABLE IF NOT EXISTS "action_text_rich_texts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "body" text, "record_type" varchar NOT NULL, "record_id" bigint NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
CREATE UNIQUE INDEX "index_action_text_rich_texts_uniqueness" ON "action_text_rich_texts" ("record_type", "record_id", "name");
CREATE TABLE IF NOT EXISTS "messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "room_id" integer NOT NULL, "creator_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "client_message_id" varchar NOT NULL, CONSTRAINT "fk_rails_761a2f12b3"
FOREIGN KEY ("creator_id")
REFERENCES "users" ("id")
, CONSTRAINT "fk_rails_a8db0fb63a"
FOREIGN KEY ("room_id")
REFERENCES "rooms" ("id")
);
CREATE INDEX "index_messages_on_room_id" ON "messages" ("room_id");
CREATE INDEX "index_messages_on_creator_id" ON "messages" ("creator_id");
CREATE TABLE IF NOT EXISTS "boosts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "message_id" integer NOT NULL, "booster_id" integer NOT NULL, "content" varchar(16) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_3539c52d73"
FOREIGN KEY ("message_id")
REFERENCES "messages" ("id")
);
CREATE INDEX "index_boosts_on_message_id" ON "boosts" ("message_id");
CREATE INDEX "index_boosts_on_booster_id" ON "boosts" ("booster_id");
CREATE TABLE IF NOT EXISTS "searches" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "query" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_e192b86393"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
);
CREATE INDEX "index_searches_on_user_id" ON "searches" ("user_id");
CREATE TABLE IF NOT EXISTS "rooms" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar DEFAULT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "type" varchar NOT NULL, "creator_id" bigint NOT NULL);
CREATE TABLE IF NOT EXISTS "memberships" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "room_id" integer NOT NULL, "user_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "unread_at" datetime(6) DEFAULT NULL, "involvement" varchar DEFAULT 'mentions', "connections" integer DEFAULT 0 NOT NULL, "connected_at" datetime(6) DEFAULT NULL);
CREATE INDEX "index_memberships_on_room_id" ON "memberships" ("room_id");
CREATE INDEX "index_memberships_on_user_id" ON "memberships" ("user_id");
CREATE INDEX "index_memberships_on_room_id_and_created_at" ON "memberships" ("room_id", "created_at");
CREATE UNIQUE INDEX "index_memberships_on_room_id_and_user_id" ON "memberships" ("room_id", "user_id");
CREATE TABLE IF NOT EXISTS "sessions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "token" varchar NOT NULL, "ip_address" varchar, "user_agent" varchar, "last_active_at" datetime(6) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_758836b4f0"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
);
CREATE INDEX "index_sessions_on_user_id" ON "sessions" ("user_id");
CREATE UNIQUE INDEX "index_sessions_on_token" ON "sessions" ("token");
CREATE TABLE IF NOT EXISTS "webhooks" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "url" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_51bf96d3bc"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
);
CREATE INDEX "index_webhooks_on_user_id" ON "webhooks" ("user_id");
CREATE TABLE IF NOT EXISTS "active_storage_blobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" varchar NOT NULL, "filename" varchar NOT NULL, "content_type" varchar, "metadata" text, "service_name" varchar NOT NULL, "byte_size" bigint NOT NULL, "checksum" varchar, "created_at" datetime(6) NOT NULL);
CREATE UNIQUE INDEX "index_active_storage_blobs_on_key" ON "active_storage_blobs" ("key") /*application='Campfire'*/;
CREATE TABLE IF NOT EXISTS "bans" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "ip_address" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_070022cd76"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
);
CREATE INDEX "index_bans_on_user_id" ON "bans" ("user_id") /*application='Campfire'*/;
CREATE INDEX "index_bans_on_ip_address" ON "bans" ("ip_address") /*application='Campfire'*/;
CREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "role" integer DEFAULT 0 NOT NULL, "email_address" varchar, "password_digest" varchar, "bio" text, "bot_token" varchar, "status" integer DEFAULT 0 NOT NULL);
CREATE UNIQUE INDEX "index_users_on_email_address" ON "users" ("email_address") /*application='Campfire'*/;
CREATE UNIQUE INDEX "index_users_on_bot_token" ON "users" ("bot_token") /*application='Campfire'*/;
INSERT INTO "schema_migrations" (version) VALUES
('20251126115722'),
('20251126092013'),
('20250825100959'),
('20250825100958'),
('20250825100957'),
('20240209110503'),
('20240131105830'),
('20240130213001'),
('20240130003150'),
('20240115124901'),
('20240110071740'),
('20231220143106'),
('20231215043540'),
('20231214195401'),
('20231213193944'),
('20231211162954'),
('20231205194643'),
('20231204234657'),
('20231204195302'),
('20231203230519'),
('20231203230140'),
('20231203181128'),
('20231203020307'),
('20231129205116'),
('20231128225451'),
('20231127103708'),
('20231116114233'),
('20231115175300'),
('20231113103004'),
('20231031091442'),
('20231020093252'),
('20231011155822'),
('20231011155708'),
('20231002121723'),
('20230930070541'),
('20230929190610'),
('20230928012127'),
('20230928004135'),
('20230928002516'),
('20230927202350'),
('20230901135127'),
('20230817084502'),
('20230816150813'),
('20230815121033'),
('20230802130519'),
('20230726135144'),
('20230711120929'),
('20230704101753'),
('20230629205414'),
('20230622090619'),
('20230620092849'),
('20230613125232'),
('20230608122228'),
('20230607131322'),
('20230530141826'),
('20230530133902'),
('20230526132438'),
('20230525142056'),
('20230516102736'),
('20230509112001'),
('20230509111756'),
('20230509110753'),
('20230509110019'),
('20230504103837'),
('20230425145239'),
('20230425130330'),
('20230425104254'),
('20230425104237'),
('20230425104133');