From 59b4af1de744c5e07218ff7bdbd3f8ce5909818f Mon Sep 17 00:00:00 2001 From: Tianzhou Date: Tue, 5 Aug 2025 00:01:16 +0800 Subject: [PATCH 1/5] feat: add city column --- singlefile/schema.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/singlefile/schema.sql b/singlefile/schema.sql index 68cbea6..123a562 100644 --- a/singlefile/schema.sql +++ b/singlefile/schema.sql @@ -76,7 +76,8 @@ $$; CREATE TABLE IF NOT EXISTS users ( id integer PRIMARY KEY, email text NOT NULL CHECK (email LIKE '%@%'), - name text NOT NULL + name text NOT NULL, + city text NOT NULL ); COMMENT ON TABLE users IS 'User accounts'; From fc0397c4de7cd7cb81d7de04187b0294f99b8b2c Mon Sep 17 00:00:00 2001 From: Tianzhou Date: Tue, 5 Aug 2025 00:01:36 +0800 Subject: [PATCH 2/5] chore: add debug --- .github/workflows/pgschema-plan-single.yml | 38 ++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pgschema-plan-single.yml b/.github/workflows/pgschema-plan-single.yml index e282c9f..f361ed9 100644 --- a/.github/workflows/pgschema-plan-single.yml +++ b/.github/workflows/pgschema-plan-single.yml @@ -30,14 +30,48 @@ jobs: - name: Run pgschema plan id: plan run: | - # Run pgschema plan and capture output - PLAN_OUTPUT=$(pgschema plan \ + # Debug: Print environment info + echo "::group::Environment Info" + echo "Current directory: $(pwd)" + echo "Schema file path: ${{ github.workspace }}/singlefile/schema.sql" + echo "Checking if schema file exists:" + ls -la "${{ github.workspace }}/singlefile/schema.sql" || echo "Schema file not found!" + echo "Go version: $(go version)" + echo "pgschema location: $(which pgschema)" + echo "::endgroup::" + + # Debug: Test pgschema is installed + echo "::group::Testing pgschema installation" + pgschema --version || echo "Failed to run pgschema --version" + echo "::endgroup::" + + # Run pgschema plan with timeout and capture output + echo "::group::Running pgschema plan" + set +e # Don't exit on error + PLAN_OUTPUT=$(timeout 60s pgschema plan \ + --debug \ --host "${{ secrets.DB_HOST }}" \ --port "${{ secrets.DB_PORT }}" \ --db "${{ secrets.DB_NAME }}" \ --user "${{ secrets.DB_USER }}" \ --file "${{ github.workspace }}/singlefile/schema.sql" \ --format human 2>&1) + EXIT_CODE=$? + set -e # Re-enable exit on error + echo "::endgroup::" + + # Check exit code + if [ $EXIT_CODE -eq 124 ]; then + echo "::error::pgschema plan timed out after 60 seconds" + PLAN_OUTPUT="Error: pgschema plan timed out after 60 seconds. This might indicate a connection issue to the database." + elif [ $EXIT_CODE -ne 0 ]; then + echo "::error::pgschema plan failed with exit code $EXIT_CODE" + fi + + # Debug: Show raw output + echo "::group::Raw pgschema output" + echo "$PLAN_OUTPUT" + echo "::endgroup::" # Escape special characters for GitHub Actions PLAN_OUTPUT="${PLAN_OUTPUT//'%'/'%25'}" From 639a617054b64924dfd0505aeb4efdb673560670 Mon Sep 17 00:00:00 2001 From: Tianzhou Date: Tue, 5 Aug 2025 00:52:28 +0800 Subject: [PATCH 3/5] chore: use test container --- .github/workflows/pgschema-plan-single.yml | 97 ++++++++++++---------- README.md | 14 +++- 2 files changed, 64 insertions(+), 47 deletions(-) diff --git a/.github/workflows/pgschema-plan-single.yml b/.github/workflows/pgschema-plan-single.yml index f361ed9..5e7f5bc 100644 --- a/.github/workflows/pgschema-plan-single.yml +++ b/.github/workflows/pgschema-plan-single.yml @@ -15,6 +15,24 @@ jobs: pgschema-plan-single: runs-on: ubuntu-latest + env: + PGPASSWORD: postgres + + services: + postgres: + image: postgres:17 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + POSTGRES_DB: testdb + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: - name: Checkout code uses: actions/checkout@v4 @@ -27,63 +45,50 @@ jobs: - name: Install pgschema run: go install github.com/pgschema/pgschema@latest - - name: Run pgschema plan - id: plan + - name: Setup baseline schema from main branch run: | - # Debug: Print environment info - echo "::group::Environment Info" - echo "Current directory: $(pwd)" - echo "Schema file path: ${{ github.workspace }}/singlefile/schema.sql" - echo "Checking if schema file exists:" - ls -la "${{ github.workspace }}/singlefile/schema.sql" || echo "Schema file not found!" - echo "Go version: $(go version)" - echo "pgschema location: $(which pgschema)" - echo "::endgroup::" + # Checkout main branch to get baseline schema + git fetch origin main + git checkout origin/main -- singlefile/schema.sql || { + echo "::error::Could not checkout schema.sql from main branch" + exit 1 + } - # Debug: Test pgschema is installed - echo "::group::Testing pgschema installation" - pgschema --version || echo "Failed to run pgschema --version" - echo "::endgroup::" + # Copy main branch schema to temporary location + cp singlefile/schema.sql /tmp/main_schema.sql + + # Restore current branch schema + git checkout HEAD -- singlefile/schema.sql - # Run pgschema plan with timeout and capture output - echo "::group::Running pgschema plan" - set +e # Don't exit on error - PLAN_OUTPUT=$(timeout 60s pgschema plan \ + # Apply main branch schema to establish baseline database state + echo "::group::Applying baseline schema" + pgschema apply \ + --auto-approve \ + --host localhost \ + --port 5432 \ + --db testdb \ + --user postgres \ + --file /tmp/main_schema.sql \ + || echo "::warning::Failed to apply baseline schema" + echo "::endgroup::" + + - name: Run pgschema plan + id: plan + run: | + # Run pgschema plan + PLAN_OUTPUT=$(pgschema plan \ --debug \ - --host "${{ secrets.DB_HOST }}" \ - --port "${{ secrets.DB_PORT }}" \ - --db "${{ secrets.DB_NAME }}" \ - --user "${{ secrets.DB_USER }}" \ + --host localhost \ + --port 5432 \ + --db testdb \ + --user postgres \ --file "${{ github.workspace }}/singlefile/schema.sql" \ --format human 2>&1) - EXIT_CODE=$? - set -e # Re-enable exit on error - echo "::endgroup::" - - # Check exit code - if [ $EXIT_CODE -eq 124 ]; then - echo "::error::pgschema plan timed out after 60 seconds" - PLAN_OUTPUT="Error: pgschema plan timed out after 60 seconds. This might indicate a connection issue to the database." - elif [ $EXIT_CODE -ne 0 ]; then - echo "::error::pgschema plan failed with exit code $EXIT_CODE" - fi - - # Debug: Show raw output - echo "::group::Raw pgschema output" - echo "$PLAN_OUTPUT" - echo "::endgroup::" - - # Escape special characters for GitHub Actions - PLAN_OUTPUT="${PLAN_OUTPUT//'%'/'%25'}" - PLAN_OUTPUT="${PLAN_OUTPUT//$'\n'/'%0A'}" - PLAN_OUTPUT="${PLAN_OUTPUT//$'\r'/'%0D'}" # Set output echo "plan<> $GITHUB_OUTPUT echo "$PLAN_OUTPUT" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - env: - PGPASSWORD: ${{ secrets.DB_PASSWORD }} - name: Comment PR uses: actions/github-script@v7 diff --git a/README.md b/README.md index ba37a11..5267863 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,12 @@ Both workflows automatically: ## Setup -### 1. Required GitHub Secrets +### 1. GitHub Secrets +#### Single File Workflow +No secrets required! The single-file workflow uses a PostgreSQL 17 test container for demonstration purposes. + +#### Multi File Workflow (if using external database) Configure the following secrets in your repository settings: - `DB_HOST` - PostgreSQL database host (default: localhost) @@ -30,6 +34,8 @@ Configure the following secrets in your repository settings: #### Single File Approach - Place your complete schema in `singlefile/schema.sql` - All tables, indexes, functions, and triggers in one file +- Uses PostgreSQL 17 test container (no external database needed) +- Compares current branch schema against main branch schema - Workflow: `.github/workflows/pgschema-plan-single.yml` #### Multi File Approach @@ -72,6 +78,12 @@ This structure demonstrates how to organize complex schemas across multiple file ## Security Notes +### Single File Workflow +- Uses isolated PostgreSQL 17 test container +- No external database credentials required +- Safe for demonstration and testing purposes + +### Multi File Workflow - Database credentials are stored as encrypted GitHub secrets - The workflow only has read access to your database (it runs `plan`, not `apply`) - Consider using a read-only database user for additional security From 91d3d182267b1f766c076afc5b51b14f8f32ab71 Mon Sep 17 00:00:00 2001 From: Tianzhou Date: Tue, 5 Aug 2025 01:14:07 +0800 Subject: [PATCH 4/5] chore: multi file change --- .github/workflows/pgschema-plan-multi.yml | 66 +++++++++++++++++----- .github/workflows/pgschema-plan-single.yml | 4 +- README.md | 23 ++------ multifile/tables/orders.sql | 2 - multifile/tables/users.sql | 5 +- 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/.github/workflows/pgschema-plan-multi.yml b/.github/workflows/pgschema-plan-multi.yml index 924b270..ff7f030 100644 --- a/.github/workflows/pgschema-plan-multi.yml +++ b/.github/workflows/pgschema-plan-multi.yml @@ -15,6 +15,24 @@ jobs: pgschema-plan-multi: runs-on: ubuntu-latest + env: + PGPASSWORD: postgres + + services: + postgres: + image: postgres:17 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + POSTGRES_DB: testdb + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: - name: Checkout code uses: actions/checkout@v4 @@ -27,29 +45,49 @@ jobs: - name: Install pgschema run: go install github.com/pgschema/pgschema@latest + - name: Setup baseline schema from main branch + run: | + # Checkout main branch to get baseline schema + git fetch origin main + git checkout origin/main -- multifile/ || { + echo "::error::Could not checkout multifile/ from main branch" + exit 1 + } + + # Copy main branch schema to temporary location + cp -r multifile /tmp/main_multifile + + # Restore current branch schema + git checkout HEAD -- multifile/ + + # Apply main branch schema to establish baseline database state + echo "::group::Applying baseline schema" + pgschema apply \ + --auto-approve \ + --host localhost \ + --port 5432 \ + --db testdb \ + --user postgres \ + --file /tmp/main_multifile/main.sql \ + || echo "::warning::Failed to apply baseline schema" + echo "::endgroup::" + - name: Run pgschema plan id: plan run: | - # Run pgschema plan with directory of SQL files + # Run pgschema plan PLAN_OUTPUT=$(pgschema plan \ - --host "${{ secrets.DB_HOST }}" \ - --port "${{ secrets.DB_PORT }}" \ - --db "${{ secrets.DB_NAME }}" \ - --user "${{ secrets.DB_USER }}" \ + --host localhost \ + --port 5432 \ + --db testdb \ + --user postgres \ --file "${{ github.workspace }}/multifile/main.sql" \ --format human 2>&1) - # Escape special characters for GitHub Actions - PLAN_OUTPUT="${PLAN_OUTPUT//'%'/'%25'}" - PLAN_OUTPUT="${PLAN_OUTPUT//$'\n'/'%0A'}" - PLAN_OUTPUT="${PLAN_OUTPUT//$'\r'/'%0D'}" - # Set output echo "plan<> $GITHUB_OUTPUT echo "$PLAN_OUTPUT" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - env: - PGPASSWORD: ${{ secrets.DB_PASSWORD }} - name: Comment PR uses: actions/github-script@v7 @@ -61,7 +99,7 @@ jobs: // Decode the escaped output const decodedOutput = decodeURIComponent(planOutput); - const body = `## pgschema Plan Output (Multi File) + const body = `## pgschema Plan Output
Click to expand plan details @@ -83,7 +121,7 @@ jobs: const botComment = comments.find(comment => comment.user.type === 'Bot' && - comment.body.includes('pgschema Plan Output (Multi File)') + comment.body.includes('pgschema Plan Output') ); if (botComment) { diff --git a/.github/workflows/pgschema-plan-single.yml b/.github/workflows/pgschema-plan-single.yml index 5e7f5bc..4b8f353 100644 --- a/.github/workflows/pgschema-plan-single.yml +++ b/.github/workflows/pgschema-plan-single.yml @@ -100,7 +100,7 @@ jobs: // Decode the escaped output const decodedOutput = decodeURIComponent(planOutput); - const body = `## pgschema Plan Output (Single File) + const body = `## pgschema Plan Output
Click to expand plan details @@ -122,7 +122,7 @@ jobs: const botComment = comments.find(comment => comment.user.type === 'Bot' && - comment.body.includes('pgschema Plan Output (Single File)') + comment.body.includes('pgschema Plan Output') ); if (botComment) { diff --git a/README.md b/README.md index 5267863..5b48af3 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,7 @@ Both workflows automatically: ### 1. GitHub Secrets -#### Single File Workflow -No secrets required! The single-file workflow uses a PostgreSQL 17 test container for demonstration purposes. - -#### Multi File Workflow (if using external database) -Configure the following secrets in your repository settings: - -- `DB_HOST` - PostgreSQL database host (default: localhost) -- `DB_PORT` - PostgreSQL database port (default: 5432) -- `DB_NAME` - Database name -- `DB_USER` - Database username -- `DB_PASSWORD` - Database password +No secrets required! Both workflows use a PostgreSQL 17 test container for demonstration purposes, making them fully self-contained. ### 2. Schema Organization @@ -43,6 +33,8 @@ Configure the following secrets in your repository settings: - Uses `main.sql` as the entry point with psql `\i` directives - Organize files by type in subdirectories (tables/, functions/, views/, etc.) - Each file contains a specific database object +- Uses PostgreSQL 17 test container (no external database needed) +- Compares current branch schema against main branch schema - Workflow: `.github/workflows/pgschema-plan-multi.yml` ## Usage @@ -78,15 +70,10 @@ This structure demonstrates how to organize complex schemas across multiple file ## Security Notes -### Single File Workflow -- Uses isolated PostgreSQL 17 test container +- Both workflows use isolated PostgreSQL 17 test containers - No external database credentials required - Safe for demonstration and testing purposes - -### Multi File Workflow -- Database credentials are stored as encrypted GitHub secrets -- The workflow only has read access to your database (it runs `plan`, not `apply`) -- Consider using a read-only database user for additional security +- Containers are ephemeral and destroyed after each workflow run ## Next Steps diff --git a/multifile/tables/orders.sql b/multifile/tables/orders.sql index f0a3b0a..e3a57da 100644 --- a/multifile/tables/orders.sql +++ b/multifile/tables/orders.sql @@ -9,8 +9,6 @@ CREATE TABLE IF NOT EXISTS orders ( amount numeric(10,2) DEFAULT 0.00 ); -COMMENT ON TABLE orders IS 'Customer orders'; - COMMENT ON COLUMN orders.user_id IS 'Reference to user'; -- diff --git a/multifile/tables/users.sql b/multifile/tables/users.sql index 94aa3a8..faff482 100644 --- a/multifile/tables/users.sql +++ b/multifile/tables/users.sql @@ -4,8 +4,9 @@ CREATE TABLE IF NOT EXISTS users ( id integer PRIMARY KEY, - email text NOT NULL CHECK (email LIKE '%@%'), - name text NOT NULL + email text NOT NULL, + name text NOT NULL, + city text NOT NULL ); COMMENT ON TABLE users IS 'User accounts'; From 0863a154e5582e0200fd8889d82f6c7cf539cfda Mon Sep 17 00:00:00 2001 From: Tianzhou Date: Tue, 5 Aug 2025 01:15:31 +0800 Subject: [PATCH 5/5] chore: revert single file --- multifile/tables/orders.sql | 2 ++ multifile/tables/users.sql | 5 ++--- singlefile/schema.sql | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/multifile/tables/orders.sql b/multifile/tables/orders.sql index e3a57da..f0a3b0a 100644 --- a/multifile/tables/orders.sql +++ b/multifile/tables/orders.sql @@ -9,6 +9,8 @@ CREATE TABLE IF NOT EXISTS orders ( amount numeric(10,2) DEFAULT 0.00 ); +COMMENT ON TABLE orders IS 'Customer orders'; + COMMENT ON COLUMN orders.user_id IS 'Reference to user'; -- diff --git a/multifile/tables/users.sql b/multifile/tables/users.sql index faff482..94aa3a8 100644 --- a/multifile/tables/users.sql +++ b/multifile/tables/users.sql @@ -4,9 +4,8 @@ CREATE TABLE IF NOT EXISTS users ( id integer PRIMARY KEY, - email text NOT NULL, - name text NOT NULL, - city text NOT NULL + email text NOT NULL CHECK (email LIKE '%@%'), + name text NOT NULL ); COMMENT ON TABLE users IS 'User accounts'; diff --git a/singlefile/schema.sql b/singlefile/schema.sql index 123a562..68cbea6 100644 --- a/singlefile/schema.sql +++ b/singlefile/schema.sql @@ -76,8 +76,7 @@ $$; CREATE TABLE IF NOT EXISTS users ( id integer PRIMARY KEY, email text NOT NULL CHECK (email LIKE '%@%'), - name text NOT NULL, - city text NOT NULL + name text NOT NULL ); COMMENT ON TABLE users IS 'User accounts';