Skip to content

Commit b7c3eca

Browse files
authored
feat: sdl example (#41)
1 parent 20602a2 commit b7c3eca

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Declarative rollout using bytebase-action image
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- "schema/*.sql"
9+
10+
# cancel previous workflow run if a new workflow run is triggered
11+
# to prevent multiple rollout
12+
concurrency:
13+
group: ${{ github.workflow }}
14+
cancel-in-progress: true
15+
16+
env:
17+
BYTEBASE_URL: https://demo.bytebase.com
18+
BYTEBASE_SERVICE_ACCOUNT: [email protected]
19+
BYTEBASE_SERVICE_ACCOUNT_SECRET: ${{ secrets.BYTEBASE_SERVICE_ACCOUNT_SECRET }}
20+
BYTEBASE_PROJECT: "projects/hr"
21+
22+
jobs:
23+
build:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
- name: Build app and upload
29+
run: |
30+
echo "Building..."
31+
echo "Build done!"
32+
echo "Uploading..."
33+
echo "Upload done!"
34+
create-rollout:
35+
needs: build
36+
runs-on: ubuntu-latest # use self-hosted machines if your Bytebase runs in internal networks.
37+
container:
38+
image: bytebase/bytebase-action:latest
39+
outputs:
40+
bytebase-plan: ${{ steps.set-output.outputs.plan }}
41+
steps:
42+
- name: Checkout
43+
uses: actions/checkout@v4
44+
- name: Roll out database change
45+
env:
46+
BYTEBASE_TARGETS: "instances/test-sample-instance/databases/hr_test,instances/prod-sample-instance/databases/hr_prod"
47+
FILE_PATTERN: "schema/*.sql"
48+
BYTEBASE_OUTPUT: ${{ runner.temp }}/bytebase-metadata.json
49+
run: |
50+
bytebase-action rollout --url=${{ env.BYTEBASE_URL }} --service-account=${{ env.BYTEBASE_SERVICE_ACCOUNT }} --service-account-secret=${{ env.BYTEBASE_SERVICE_ACCOUNT_SECRET }} --project=${{ env.BYTEBASE_PROJECT }} --file-pattern=${{ env.FILE_PATTERN }} --targets=${{ env.BYTEBASE_TARGETS }} --declarative --output=${{ env.BYTEBASE_OUTPUT }}
51+
- name: Set output
52+
id: set-output
53+
run: |
54+
PLAN=$(jq -r .plan ${{ runner.temp }}/bytebase-metadata.json)
55+
echo "plan=$PLAN" >> $GITHUB_OUTPUT
56+
deploy-to-test:
57+
needs: create-rollout
58+
runs-on: ubuntu-latest # use self-hosted machines if your Bytebase runs in internal networks.
59+
environment: test
60+
container:
61+
image: bytebase/bytebase-action:latest
62+
steps:
63+
- name: Checkout
64+
uses: actions/checkout@v4
65+
- name: Roll out database change
66+
env:
67+
BYTEBASE_TARGET_STAGE: environments/test
68+
run: |
69+
bytebase-action rollout --url=${{ env.BYTEBASE_URL }} --service-account=${{ env.BYTEBASE_SERVICE_ACCOUNT }} --service-account-secret=${{ env.BYTEBASE_SERVICE_ACCOUNT_SECRET }} --project=${{ env.BYTEBASE_PROJECT }} --target-stage=${{ env.BYTEBASE_TARGET_STAGE }} --plan=${{ needs.create-rollout.outputs.bytebase-plan }}
70+
- name: Deploy app
71+
run: |
72+
echo "Deploying app to test environment..."
73+
echo "Deploy app to test environment done!"
74+
deploy-to-prod:
75+
needs:
76+
- deploy-to-test
77+
- create-rollout
78+
runs-on: ubuntu-latest
79+
environment: prod
80+
container:
81+
image: bytebase/bytebase-action:latest
82+
steps:
83+
- name: Checkout
84+
uses: actions/checkout@v4
85+
- name: rollout
86+
env:
87+
BYTEBASE_TARGET_STAGE: environments/prod
88+
run: |
89+
bytebase-action rollout --url=${{ env.BYTEBASE_URL }} --service-account=${{ env.BYTEBASE_SERVICE_ACCOUNT }} --service-account-secret=${{ env.BYTEBASE_SERVICE_ACCOUNT_SECRET }} --project=${{ env.BYTEBASE_PROJECT }} --target-stage=${{ env.BYTEBASE_TARGET_STAGE }} --plan=${{ needs.create-rollout.outputs.bytebase-plan }}
90+
- name: Deploy app
91+
run: |
92+
echo "Deploying app to prod environment..."
93+
echo "Deploy app to prod environment done!"

schema/schema.sql

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
CREATE TABLE public.employee (
2+
emp_no SERIAL NOT NULL,
3+
birth_date DATE NOT NULL,
4+
first_name TEXT NOT NULL,
5+
last_name TEXT NOT NULL,
6+
gender TEXT NOT NULL CHECK (gender IN('M', 'F')) NOT NULL,
7+
hire_date DATE NOT NULL,
8+
PRIMARY KEY (emp_no)
9+
);
10+
11+
CREATE INDEX idx_employee_hire_date ON public.employee (hire_date);
12+
13+
CREATE TABLE public.department (
14+
dept_no TEXT NOT NULL,
15+
dept_name TEXT NOT NULL,
16+
PRIMARY KEY (dept_no),
17+
UNIQUE (dept_name)
18+
);
19+
20+
CREATE TABLE public.dept_manager (
21+
emp_no INT NOT NULL,
22+
dept_no TEXT NOT NULL,
23+
from_date DATE NOT NULL,
24+
to_date DATE NOT NULL,
25+
FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE,
26+
FOREIGN KEY (dept_no) REFERENCES department (dept_no) ON DELETE CASCADE,
27+
PRIMARY KEY (emp_no, dept_no)
28+
);
29+
30+
CREATE TABLE public.dept_emp (
31+
emp_no INT NOT NULL,
32+
dept_no TEXT NOT NULL,
33+
from_date DATE NOT NULL,
34+
to_date DATE NOT NULL,
35+
FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE,
36+
FOREIGN KEY (dept_no) REFERENCES department (dept_no) ON DELETE CASCADE,
37+
PRIMARY KEY (emp_no, dept_no)
38+
);
39+
40+
CREATE TABLE public.title (
41+
emp_no INT NOT NULL,
42+
title TEXT NOT NULL,
43+
from_date DATE NOT NULL,
44+
to_date DATE,
45+
FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE,
46+
PRIMARY KEY (emp_no, title, from_date)
47+
);
48+
49+
CREATE TABLE public.salary (
50+
emp_no INT NOT NULL,
51+
amount INT NOT NULL,
52+
from_date DATE NOT NULL,
53+
to_date DATE NOT NULL,
54+
FOREIGN KEY (emp_no) REFERENCES employee (emp_no) ON DELETE CASCADE,
55+
PRIMARY KEY (emp_no, from_date)
56+
);
57+
58+
CREATE INDEX idx_salary_amount ON public.salary (amount);
59+
60+
CREATE TABLE public.audit (
61+
id SERIAL PRIMARY KEY,
62+
operation TEXT NOT NULL,
63+
query TEXT,
64+
user_name TEXT NOT NULL,
65+
changed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
66+
);
67+
68+
CREATE INDEX idx_audit_operation ON public.audit (operation);
69+
CREATE INDEX idx_audit_username ON public.audit (user_name);
70+
CREATE INDEX idx_audit_changed_at ON public.audit (changed_at);
71+
72+
CREATE OR REPLACE FUNCTION public.log_dml_operations() RETURNS TRIGGER AS $$
73+
BEGIN
74+
IF (TG_OP = 'INSERT') THEN
75+
INSERT INTO public.audit (operation, query, user_name)
76+
VALUES ('INSERT', current_query(), current_user);
77+
RETURN NEW;
78+
ELSIF (TG_OP = 'UPDATE') THEN
79+
INSERT INTO public.audit (operation, query, user_name)
80+
VALUES ('UPDATE', current_query(), current_user);
81+
RETURN NEW;
82+
ELSIF (TG_OP = 'DELETE') THEN
83+
INSERT INTO public.audit (operation, query, user_name)
84+
VALUES ('DELETE', current_query(), current_user);
85+
RETURN OLD;
86+
END IF;
87+
RETURN NULL;
88+
END;
89+
$$ LANGUAGE plpgsql;
90+
91+
-- only log update and delete, otherwise, it will cause too much change.
92+
CREATE TRIGGER salary_log_trigger
93+
AFTER UPDATE OR DELETE ON public.salary
94+
FOR EACH ROW
95+
EXECUTE FUNCTION public.log_dml_operations();
96+
97+
CREATE OR REPLACE VIEW public.dept_emp_latest_date AS
98+
SELECT
99+
emp_no,
100+
MAX(
101+
from_date) AS from_date,
102+
MAX(
103+
to_date) AS to_date
104+
FROM
105+
public.dept_emp
106+
GROUP BY
107+
emp_no;
108+
109+
-- shows only the current department for each employee
110+
CREATE OR REPLACE VIEW public.current_dept_emp AS
111+
SELECT
112+
l.emp_no,
113+
dept_no,
114+
l.from_date,
115+
l.to_date
116+
FROM
117+
public.dept_emp d
118+
INNER JOIN public.dept_emp_latest_date l ON d.emp_no = l.emp_no
119+
AND d.from_date = l.from_date
120+
AND l.to_date = d.to_date;

schema/users.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE public.users (
2+
id SERIAL PRIMARY KEY,
3+
name VARCHAR(255) NOT NULL,
4+
email VARCHAR(255) NOT NULL UNIQUE
5+
);

0 commit comments

Comments
 (0)