automate database init from CLI. add sqlite_history
may god bless simon willison
This commit is contained in:
parent
0110cc17e2
commit
c226669a09
5 changed files with 146 additions and 3 deletions
16
Makefile
Normal file
16
Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
.PHONY: tidy
|
||||||
|
tidy:
|
||||||
|
clojure-lsp clean-ns && clojure-lsp format && clojure-lsp diagnostics
|
||||||
|
|
||||||
|
.PHONY: storage/site.db
|
||||||
|
storage/site.db:
|
||||||
|
rm -f storage/*
|
||||||
|
sqlite3 storage/site.db 'PRAGMA journal_mode=WAL;'
|
||||||
|
clj -M dev/migration.clj
|
||||||
|
uv run dev/sqlite-history.py
|
||||||
|
|
||||||
|
.PHONY: db
|
||||||
|
db: storage/site.db
|
||||||
|
|
||||||
|
repl: storage/site.db
|
||||||
|
clj -M:dev dev
|
||||||
15
dev/migration.clj
Normal file
15
dev/migration.clj
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
(ns migration
|
||||||
|
#_{:clj-kondo/ignore [:unused-namespace]}
|
||||||
|
(:require [migratus.core :as migratus]
|
||||||
|
[taoensso.telemere.timbre :as log]))
|
||||||
|
|
||||||
|
(def config {:store :database
|
||||||
|
:migration-dir "migrations/"
|
||||||
|
:db {:dbtype "sqlite"
|
||||||
|
:dbname "storage/site.db"}})
|
||||||
|
|
||||||
|
;initialize the database using the 'init.sql' script
|
||||||
|
;; (migratus/init config)
|
||||||
|
|
||||||
|
;apply pending migrations
|
||||||
|
(migratus/migrate config)
|
||||||
70
dev/sqlite-history.py
Normal file
70
dev/sqlite-history.py
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.13"
|
||||||
|
# dependencies = [
|
||||||
|
# "sqlite-history",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
|
||||||
|
|
||||||
|
import sqlite_history
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
def configure_triggers(database_path, tables):
|
||||||
|
db = sqlite3.connect(database_path)
|
||||||
|
for table in tables:
|
||||||
|
if table.startswith("_") and table.endswith("_history"):
|
||||||
|
continue
|
||||||
|
# Does a history table exist already?
|
||||||
|
history_table_name = f"_{table}_history"
|
||||||
|
cursor = db.execute(
|
||||||
|
f"SELECT name FROM sqlite_master WHERE type='table' AND name='{history_table_name}';"
|
||||||
|
)
|
||||||
|
if cursor.fetchone():
|
||||||
|
print(f"History table {history_table_name} already exists - skipping.")
|
||||||
|
continue
|
||||||
|
sqlite_history.configure_history(db, table)
|
||||||
|
print(f"configured trigger for {table}")
|
||||||
|
|
||||||
|
|
||||||
|
def all_regular_tables(database_path):
|
||||||
|
"""'Regular' excludes FTS and related tables."""
|
||||||
|
conn = sqlite3.connect(database_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
hidden_tables = [
|
||||||
|
r[0]
|
||||||
|
for r in (
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT NAME FROM sqlite_master
|
||||||
|
WHERE type = 'table'
|
||||||
|
AND (
|
||||||
|
sql LIKE '%VIRTUAL TABLE%USING FTS%'
|
||||||
|
) OR name IN ('sqlite_stat1', 'sqlite_stat2', 'sqlite_stat3', 'sqlite_stat4')
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
).fetchall()
|
||||||
|
]
|
||||||
|
hidden_tables_copy = hidden_tables[:]
|
||||||
|
regular_tables = []
|
||||||
|
for row in cursor.execute(
|
||||||
|
"SELECT name FROM sqlite_master WHERE type='table';"
|
||||||
|
).fetchall():
|
||||||
|
table_name = row[0]
|
||||||
|
should_be_hidden = any(
|
||||||
|
table_name.startswith(hidden_table) for hidden_table in hidden_tables_copy
|
||||||
|
)
|
||||||
|
if not should_be_hidden:
|
||||||
|
regular_tables.append(table_name)
|
||||||
|
return regular_tables
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
database_path = "storage/site.db"
|
||||||
|
tables = all_regular_tables(database_path)
|
||||||
|
|
||||||
|
configure_triggers(database_path, tables)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
45
dev/tasks.clj
Normal file
45
dev/tasks.clj
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
(ns tasks
|
||||||
|
(:require [com.biffweb.task-runner :refer [run-task]]
|
||||||
|
[com.biffweb.tasks :as tasks]
|
||||||
|
[com.biffweb.tasks.lazy.babashka.fs :as fs]
|
||||||
|
[com.biffweb.tasks.lazy.babashka.process :refer [shell]]
|
||||||
|
[com.biffweb.tasks.lazy.clojure.java.io :as io]
|
||||||
|
[com.biffweb.tasks.lazy.com.biffweb.config :as config]))
|
||||||
|
|
||||||
|
(def config (delay (config/use-aero-config {:biff.config/skip-validation true})))
|
||||||
|
|
||||||
|
(defn dev
|
||||||
|
"Starts the app locally.
|
||||||
|
|
||||||
|
After running, wait for the `System started` message. Connect your editor to
|
||||||
|
nrepl port 7888 (by default). Whenever you save a file, Biff will:
|
||||||
|
|
||||||
|
- Evaluate any changed Clojure files
|
||||||
|
- Regenerate static HTML files
|
||||||
|
- Run tests"
|
||||||
|
[]
|
||||||
|
(if-not (fs/exists? "target/resources")
|
||||||
|
;; This is an awful hack. We have to run the app in a new process, otherwise
|
||||||
|
;; target/resources won't be included in the classpath. Downside of not
|
||||||
|
;; using bb tasks anymore -- no longer have a lightweight parent process
|
||||||
|
;; that can create the directory before starting the JVM.
|
||||||
|
(do
|
||||||
|
(io/make-parents "target/resources/_")
|
||||||
|
(shell "clj" "-M:dev" "dev"))
|
||||||
|
(let [{:keys [biff.tasks/main-ns biff.nrepl/port] :as _ctx} @config]
|
||||||
|
|
||||||
|
(when-not (fs/exists? "config.env")
|
||||||
|
(run-task "generate-config"))
|
||||||
|
(when (fs/exists? "package.json")
|
||||||
|
(shell "npm install"))
|
||||||
|
(spit ".nrepl-port" port)
|
||||||
|
((requiring-resolve (symbol (str main-ns) "-main"))))))
|
||||||
|
|
||||||
|
;; Tasks should be vars (#'hello instead of hello) so that `clj -M:dev help` can
|
||||||
|
;; print their docstrings.
|
||||||
|
(def custom-tasks
|
||||||
|
{"dev" #'dev})
|
||||||
|
|
||||||
|
(def tasks (merge tasks/tasks custom-tasks))
|
||||||
|
|
||||||
|
(comment tasks)
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
[clojure.test :as test]
|
[clojure.test :as test]
|
||||||
[com.biffweb :as biff]
|
[com.biffweb :as biff]
|
||||||
[com.biffweb.my-project.app :as app]
|
[com.biffweb.my-project.app :as app]
|
||||||
[com.biffweb.my-project.email :as email]
|
|
||||||
[com.biffweb.my-project.middleware :as mid]
|
[com.biffweb.my-project.middleware :as mid]
|
||||||
[com.biffweb.my-project.ui :as ui]
|
[com.biffweb.my-project.ui :as ui]
|
||||||
[migratus.core :as migratus]
|
[migratus.core :as migratus]
|
||||||
|
|
@ -40,7 +39,6 @@
|
||||||
(def initial-system
|
(def initial-system
|
||||||
{:biff/modules #'modules
|
{:biff/modules #'modules
|
||||||
:biff/merge-context-fn identity
|
:biff/merge-context-fn identity
|
||||||
:biff/send-email #'email/send-email
|
|
||||||
:biff/handler #'handler
|
:biff/handler #'handler
|
||||||
:biff.beholder/on-save #'on-save
|
:biff.beholder/on-save #'on-save
|
||||||
:biff.middleware/on-error #'ui/on-error
|
:biff.middleware/on-error #'ui/on-error
|
||||||
|
|
@ -51,7 +49,6 @@
|
||||||
(defn ctx->migratus-config [ctx]
|
(defn ctx->migratus-config [ctx]
|
||||||
{:store :database
|
{:store :database
|
||||||
:migration-dir "migrations/"
|
:migration-dir "migrations/"
|
||||||
:migration-table-name "migrations"
|
|
||||||
:db {:connection (jdbc/get-connection (:example/db-url ctx))
|
:db {:connection (jdbc/get-connection (:example/db-url ctx))
|
||||||
:managed-connection? true}})
|
:managed-connection? true}})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue