Skip to content

Commit 1daf39d

Browse files
add export
1 parent 2dfdf37 commit 1daf39d

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed

.github/workflows/bb-export.yml

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
name: Bytebase Export SQL
2+
on:
3+
pull_request:
4+
types: [closed]
5+
branches:
6+
- main
7+
paths:
8+
- 'export/**'
9+
workflow_dispatch:
10+
11+
jobs:
12+
bytebase-export:
13+
if: github.event.pull_request.merged == true
14+
runs-on: ubuntu-latest
15+
permissions:
16+
pull-requests: write
17+
issues: write
18+
contents: read
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
with:
23+
ref: ${{ github.event.pull_request.head.sha }}
24+
fetch-depth: 0
25+
26+
- name: Login Bytebase
27+
id: bytebase-login
28+
uses: bytebase/[email protected]
29+
with:
30+
bytebase-url: ${{ secrets.BYTEBASE_URL }}
31+
service-key: ${{ secrets.BYTEBASE_SERVICE_KEY }}
32+
service-secret: ${{ secrets.BYTEBASE_SERVICE_SECRET }}
33+
34+
- name: Get changed files
35+
id: changed-files
36+
uses: tj-actions/changed-files@v42
37+
with:
38+
files: |
39+
export/**/*.sql
40+
since_last_remote_commit: true
41+
42+
- name: Process SQL files
43+
id: process-sql
44+
if: steps.changed-files.outputs.any_changed == 'true'
45+
run: |
46+
# Function to make API calls with error handling
47+
call_api() {
48+
local url="$1"
49+
local method="$2"
50+
local data="$3"
51+
local description="$4"
52+
53+
echo "Making $description API call..."
54+
response=$(curl -s -w "\n%{http_code}" \
55+
--request "$method" "$url" \
56+
--header "Authorization: Bearer ${{ steps.bytebase-login.outputs.token }}" \
57+
--header "Content-Type: application/json" \
58+
--data "$data")
59+
60+
status_code=$(echo "$response" | tail -n1)
61+
body=$(echo "$response" | sed '$d')
62+
63+
echo "Status code: $status_code"
64+
if [[ $status_code -lt 200 || $status_code -ge 300 ]]; then
65+
echo "Failed $description. Status: $status_code"
66+
echo "Response: $body"
67+
return 1
68+
fi
69+
70+
echo "$body"
71+
return 0
72+
}
73+
74+
# Process each SQL file
75+
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
76+
echo "Processing $file"
77+
78+
# Find and parse manifest.toml
79+
DIR_PATH=$(dirname "$file")
80+
MANIFEST_PATH=""
81+
while [[ "$DIR_PATH" == export* ]]; do
82+
if [[ -f "$DIR_PATH/manifest.toml" ]]; then
83+
MANIFEST_PATH="$DIR_PATH/manifest.toml"
84+
break
85+
fi
86+
DIR_PATH=$(dirname "$DIR_PATH")
87+
done
88+
89+
if [[ -z "$MANIFEST_PATH" ]]; then
90+
echo "Error: No manifest.toml found for $file"
91+
exit 1
92+
fi
93+
94+
# Parse manifest.toml
95+
PROJECT=$(python3 -c "import toml; print(toml.load('$MANIFEST_PATH')['project'])")
96+
INSTANCE=$(python3 -c "import toml; print(toml.load('$MANIFEST_PATH')['instance'])")
97+
DATABASE=$(python3 -c "import toml; print(toml.load('$MANIFEST_PATH')['database'])")
98+
FORMAT=$(python3 -c "import toml; config=toml.load('$MANIFEST_PATH'); print(config.get('format', 'JSON'))")
99+
100+
# Read SQL content and encode to base64
101+
SQL_CONTENT=$(base64 < "$file")
102+
103+
# Generate UUID for step ID
104+
STEP_ID=$(python3 -c "import uuid; print(str(uuid.uuid4()))")
105+
106+
BASE_URL="${{ steps.bytebase-login.outputs.api_url }}"
107+
108+
# 1. Create Sheet
109+
sheet_data=$(call_api \
110+
"$BASE_URL/v1/projects/$PROJECT/sheets" \
111+
"POST" \
112+
"{\"title\":\"\",\"content\":\"$SQL_CONTENT\",\"type\":\"TYPE_SQL\",\"source\":\"SOURCE_BYTEBASE_ARTIFACT\",\"visibility\":\"VISIBILITY_PUBLIC\"}" \
113+
"Create Sheet") || exit 1
114+
115+
SHEET_NAME=$(echo "$sheet_data" | python3 -c "import sys, json; print(json.load(sys.stdin)['name'])")
116+
117+
# 2. Create Plan
118+
plan_data=$(call_api \
119+
"$BASE_URL/v1/projects/$PROJECT/plans" \
120+
"POST" \
121+
"{\"steps\":[{\"specs\":[{\"id\":\"$STEP_ID\",\"export_data_config\":{\"target\":\"/instances/$INSTANCE/databases/$DATABASE\",\"format\":\"$FORMAT\",\"sheet\":\"$SHEET_NAME\"}}]}],\"title\":\"Export data from $DATABASE\",\"description\":\"EXPORT\"}" \
122+
"Create Plan") || exit 1
123+
124+
PLAN_NAME=$(echo "$plan_data" | python3 -c "import sys, json; print(json.load(sys.stdin)['name'])")
125+
126+
# 3. Create Issue
127+
issue_data=$(call_api \
128+
"$BASE_URL/v1/projects/$PROJECT/issues" \
129+
"POST" \
130+
"{\"approvers\":[],\"approvalTemplates\":[],\"subscribers\":[],\"title\":\"Issue: Export data from instances/$INSTANCE/databases/$DATABASE\",\"description\":\"SQL request from GitHub\",\"type\":\"DATABASE_DATA_EXPORT\",\"assignee\":\"\",\"plan\":\"$PLAN_NAME\"}" \
131+
"Create Issue") || exit 1
132+
133+
# 4. Create Rollout
134+
rollout_data=$(call_api \
135+
"$BASE_URL/v1/projects/$PROJECT/rollouts" \
136+
"POST" \
137+
"{\"plan\":\"$PLAN_NAME\"}" \
138+
"Create Rollout") || exit 1
139+
140+
# Extract issue link for PR comment
141+
ISSUE_NUMBER=$(echo "$issue_data" | python3 -c "import sys, json; print(json.load(sys.stdin)['name'].split('/')[-1])")
142+
ISSUE_LINK="${{ secrets.BYTEBASE_URL }}/projects/$PROJECT/issues/$ISSUE_NUMBER"
143+
echo "ISSUE_LINK=$ISSUE_LINK" >> $GITHUB_ENV
144+
145+
echo "Successfully processed $file"
146+
done
147+
148+
- name: Comment on PR
149+
uses: actions/github-script@v7
150+
if: always()
151+
env:
152+
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
153+
with:
154+
script: |
155+
const changedFiles = process.env.CHANGED_FILES || '';
156+
let commentBody = `### SQL Export Summary\n\n`;
157+
158+
commentBody += `✅ **PR Status:** Merged\n\n`;
159+
160+
commentBody += `📝 **Processed SQL Files:**\n\n`;
161+
if (changedFiles.trim()) {
162+
commentBody += changedFiles.split(' ').map(f => `- ${f}`).join('\n');
163+
} else {
164+
commentBody += `None`;
165+
}
166+
167+
commentBody += '\n\n**Status:** ${process.env.STATUS || 'Completed'}`;
168+
169+
await github.rest.issues.createComment({
170+
...context.repo,
171+
issue_number: context.issue.number,
172+
body: commentBody
173+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT * FROM employee;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
project = "sample-project"
2+
instance = "prod-sample-instance"
3+
database = "hr_prod"
4+
format = "CSV" # Optional, defaults to "JSON" if not specified

0 commit comments

Comments
 (0)