Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.

Commit ad8b277

Browse files
authored
Merge pull request #92 from vultisig/90-autogenerate-and-verify-schemasql
Check schema consistency test
2 parents 675c1c6 + cd78e02 commit ad8b277

3 files changed

Lines changed: 204 additions & 18 deletions

File tree

.github/workflows/migration-test.yml

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
jobs:
77
migration-test:
88
runs-on: ubuntu-latest
9-
9+
1010
services:
1111
postgres:
1212
image: postgres:15
@@ -21,7 +21,7 @@ jobs:
2121
--health-retries 5
2222
ports:
2323
- 5432:5432
24-
24+
2525
redis:
2626
image: redis:7
2727
options: >-
@@ -31,20 +31,20 @@ jobs:
3131
--health-retries 5
3232
ports:
3333
- 6379:6379
34-
34+
3535
steps:
3636
- name: Checkout code
3737
uses: actions/checkout@v4
38-
38+
3939
- name: Set up Go
4040
uses: actions/setup-go@v5
4141
with:
4242
go-version: '1.24.2'
43-
43+
4444
- name: Download go-wrappers
4545
run: |
4646
git clone https://github.com/vultisig/go-wrappers.git ../go-wrappers
47-
47+
4848
- name: Create test config
4949
run: |
5050
cat > dca.json <<EOF
@@ -82,43 +82,43 @@ jobs:
8282
}
8383
}
8484
EOF
85-
85+
8686
- name: Build dca binary
8787
run: |
8888
export LD_LIBRARY_PATH=../go-wrappers/includes/linux/:$LD_LIBRARY_PATH
8989
go build -o dca cmd/dca/server/*.go
90-
90+
9191
- name: Run dca and check migrations
9292
run: |
9393
export LD_LIBRARY_PATH=../go-wrappers/includes/linux/:$LD_LIBRARY_PATH
9494
export VS_CONFIG_NAME=dca
95-
95+
9696
# Start dca in background
9797
./dca &
9898
DCA_PID=$!
99-
99+
100100
# Give it time to run migrations
101101
sleep 10
102-
102+
103103
# Check if process is still running
104104
if ! kill -0 $DCA_PID 2>/dev/null; then
105105
echo "DCA process failed to start"
106106
exit 1
107107
fi
108-
108+
109109
# Check if migrations ran successfully by verifying tables exist
110110
PGPASSWORD=mypassword psql -h localhost -U myuser -d vultisig-plugin -c "\dt" | grep -E "(plugin_policies|time_triggers|transaction_history) | wc -l" > /dev/null
111111
if [ $? -ne 0 ]; then
112112
echo "Migrations did not run successfully - expected tables not found"
113113
kill $DCA_PID
114114
exit 1
115115
fi
116-
116+
117117
echo "All migrations ran successfully!"
118-
118+
119119
# Stop dca
120120
kill $DCA_PID
121-
121+
122122
- name: Check migration integrity
123123
run: |
124124
# Verify goose migrations table exists and has entries
@@ -127,5 +127,30 @@ jobs:
127127
echo "Goose migrations table not found or empty"
128128
exit 1
129129
fi
130-
131-
echo "Migration integrity check passed!"
130+
131+
echo "Migration integrity check passed!"
132+
133+
- name: Check schema updates
134+
run: |
135+
# Dump current schema
136+
PGPASSWORD=mypassword pg_dump -h localhost -U myuser -d vultisig-plugin \
137+
--schema-only \
138+
--no-comments \
139+
--no-owner \
140+
--quote-all-identifiers \
141+
-T public.goose_db_version \
142+
-T public.goose_db_version_id_seq | sed \
143+
-e '/^--.*/d' \
144+
-e '/^SET /d' \
145+
-e '/^SELECT pg_catalog./d' \
146+
-e 's/"public"\.//' | awk '/./ { e=0 } /^$$/ { e += 1 } e <= 1' \
147+
> ./current_schema.sql
148+
149+
# Compare with repository schema
150+
if ! diff -u ./storage/postgres/schema/schema.sql ./current_schema.sql; then
151+
echo "Schema has changed but schema.sql was not updated!"
152+
echo "Please run 'make dump-schema CONFIG=config-plugin.yaml' to update the schema file."
153+
exit 1
154+
fi
155+
156+
echo "Schema is up to date!"

Makefile

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,24 @@ plugin-server:
1717
@DYLD_LIBRARY_PATH=$(DYLD_LIBRARY) VS_CONFIG_NAME=config-plugin go run cmd/vultisigner/main.go
1818

1919
plugin-worker:
20-
@DYLD_LIBRARY_PATH=$(DYLD_LIBRARY) VS_CONFIG_NAME=config-plugin go run cmd/worker/main.go
20+
@DYLD_LIBRARY_PATH=$(DYLD_LIBRARY) VS_CONFIG_NAME=config-plugin go run cmd/worker/main.go
21+
22+
# Dump database schema
23+
# Usage: make dump-schema CONFIG=config-plugin.yaml
24+
dump-schema:
25+
@if [ -z "$(CONFIG)" ]; then \
26+
echo "Error: CONFIG parameter is required. Usage: make dump-schema CONFIG=config-plugin.yaml"; \
27+
exit 1; \
28+
fi
29+
@DSN=$$(yq eval '.database.dsn' $(CONFIG)); \
30+
pg_dump "$$DSN" --schema-only \
31+
--no-comments \
32+
--no-owner \
33+
--quote-all-identifiers \
34+
-T public.goose_db_version \
35+
-T public.goose_db_version_id_seq | sed \
36+
-e '/^--.*/d' \
37+
-e '/^SET /d' \
38+
-e '/^SELECT pg_catalog./d' \
39+
-e 's/"public"\.//' | awk '/./ { e=0 } /^$$/ { e += 1 } e <= 1' \
40+
> ./storage/postgres/schema/schema.sql

storage/postgres/schema/schema.sql

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
2+
CREATE TYPE "plugin_id" AS ENUM (
3+
'vultisig-dca-0000',
4+
'vultisig-payroll-0000',
5+
'vultisig-fees-feee'
6+
);
7+
8+
CREATE TYPE "transaction_status" AS ENUM (
9+
'PENDING',
10+
'SIGNING_FAILED',
11+
'SIGNED',
12+
'BROADCAST',
13+
'MINED',
14+
'REJECTED'
15+
);
16+
17+
CREATE TYPE "trigger_status" AS ENUM (
18+
'PENDING',
19+
'RUNNING'
20+
);
21+
22+
CREATE TYPE "tx_indexer_status" AS ENUM (
23+
'PROPOSED',
24+
'VERIFIED',
25+
'SIGNED'
26+
);
27+
28+
CREATE TYPE "tx_indexer_status_onchain" AS ENUM (
29+
'PENDING',
30+
'SUCCESS',
31+
'FAIL'
32+
);
33+
34+
CREATE TABLE "plugin_policies" (
35+
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
36+
"public_key" "text" NOT NULL,
37+
"plugin_id" "plugin_id" NOT NULL,
38+
"plugin_version" "text" NOT NULL,
39+
"policy_version" integer NOT NULL,
40+
"signature" "text" NOT NULL,
41+
"recipe" "text" NOT NULL,
42+
"active" boolean DEFAULT true NOT NULL,
43+
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
44+
"updated_at" timestamp with time zone DEFAULT "now"() NOT NULL
45+
);
46+
47+
CREATE TABLE "time_triggers" (
48+
"id" integer NOT NULL,
49+
"policy_id" "uuid" NOT NULL,
50+
"cron_expression" "text" NOT NULL,
51+
"start_time" timestamp without time zone NOT NULL,
52+
"end_time" timestamp without time zone,
53+
"frequency" integer NOT NULL,
54+
"interval" integer NOT NULL,
55+
"last_execution" timestamp without time zone,
56+
"status" "trigger_status" NOT NULL,
57+
"created_at" timestamp without time zone DEFAULT CURRENT_TIMESTAMP
58+
);
59+
60+
CREATE SEQUENCE "time_triggers_id_seq"
61+
AS integer
62+
START WITH 1
63+
INCREMENT BY 1
64+
NO MINVALUE
65+
NO MAXVALUE
66+
CACHE 1;
67+
68+
ALTER SEQUENCE "time_triggers_id_seq" OWNED BY "public"."time_triggers"."id";
69+
70+
CREATE TABLE "transaction_history" (
71+
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
72+
"policy_id" "uuid" NOT NULL,
73+
"tx_body" "text" NOT NULL,
74+
"tx_hash" "text" NOT NULL,
75+
"status" "transaction_status" NOT NULL,
76+
"created_at" timestamp with time zone DEFAULT "now"(),
77+
"updated_at" timestamp with time zone DEFAULT "now"(),
78+
"metadata" "jsonb",
79+
"error_message" "text"
80+
);
81+
82+
CREATE TABLE "tx_indexer" (
83+
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
84+
"plugin_id" character varying(255) NOT NULL,
85+
"tx_hash" character varying(255),
86+
"chain_id" integer NOT NULL,
87+
"policy_id" "uuid" NOT NULL,
88+
"from_public_key" character varying(255) NOT NULL,
89+
"proposed_tx_hex" "text" NOT NULL,
90+
"status" "tx_indexer_status" DEFAULT 'PROPOSED'::"public"."tx_indexer_status" NOT NULL,
91+
"status_onchain" "tx_indexer_status_onchain",
92+
"lost" boolean DEFAULT false NOT NULL,
93+
"broadcasted_at" timestamp without time zone,
94+
"created_at" timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
95+
"updated_at" timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
96+
);
97+
98+
ALTER TABLE ONLY "time_triggers" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."time_triggers_id_seq"'::"regclass");
99+
100+
ALTER TABLE ONLY "plugin_policies"
101+
ADD CONSTRAINT "plugin_policies_pkey" PRIMARY KEY ("id");
102+
103+
ALTER TABLE ONLY "time_triggers"
104+
ADD CONSTRAINT "time_triggers_pkey" PRIMARY KEY ("id");
105+
106+
ALTER TABLE ONLY "transaction_history"
107+
ADD CONSTRAINT "transaction_history_pkey" PRIMARY KEY ("id");
108+
109+
ALTER TABLE ONLY "tx_indexer"
110+
ADD CONSTRAINT "tx_indexer_pkey" PRIMARY KEY ("id");
111+
112+
ALTER TABLE ONLY "transaction_history"
113+
ADD CONSTRAINT "unique_tx_hash" UNIQUE ("tx_hash");
114+
115+
CREATE INDEX "idx_plugin_policies_active" ON "plugin_policies" USING "btree" ("active");
116+
117+
CREATE INDEX "idx_plugin_policies_plugin_id" ON "plugin_policies" USING "btree" ("plugin_id");
118+
119+
CREATE INDEX "idx_plugin_policies_public_key" ON "plugin_policies" USING "btree" ("public_key");
120+
121+
CREATE INDEX "idx_time_triggers_policy_id" ON "time_triggers" USING "btree" ("policy_id");
122+
123+
CREATE INDEX "idx_time_triggers_start_time" ON "time_triggers" USING "btree" ("start_time");
124+
125+
CREATE INDEX "idx_transaction_history_policy_id" ON "transaction_history" USING "btree" ("policy_id");
126+
127+
CREATE INDEX "idx_transaction_history_status" ON "transaction_history" USING "btree" ("status");
128+
129+
CREATE INDEX "idx_transaction_history_tx_hash" ON "transaction_history" USING "btree" ("tx_hash");
130+
131+
CREATE INDEX "idx_tx_indexer_status_onchain_lost" ON "tx_indexer" USING "btree" ("status_onchain", "lost");
132+
133+
ALTER TABLE ONLY "transaction_history"
134+
ADD CONSTRAINT "fk_policy" FOREIGN KEY ("policy_id") REFERENCES "plugin_policies"("id");
135+
136+
ALTER TABLE ONLY "time_triggers"
137+
ADD CONSTRAINT "time_triggers_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "plugin_policies"("id");
138+
139+
ALTER TABLE ONLY "transaction_history"
140+
ADD CONSTRAINT "transaction_history_policy_id_fkey" FOREIGN KEY ("policy_id") REFERENCES "plugin_policies"("id");
141+

0 commit comments

Comments
 (0)