diff --git a/.github/workflows/backend/test-backend.yml b/.github/workflows/backend/test-backend.yml
deleted file mode 100644
index 26bdc7bc..00000000
--- a/.github/workflows/backend/test-backend.yml
+++ /dev/null
@@ -1,73 +0,0 @@
-name: Test Backend
-on:
- push:
- branches: ['main', 'staging', 'prod']
- paths:
- - 'backend/**'
- pull_request:
- branches: ['main', 'staging', 'prod']
- paths:
- - 'backend/**'
-jobs:
- test-backend:
- name: Test Backend
- runs-on: ubuntu-latest
- defaults:
- run:
- shell: bash
- working-directory: backend
- services:
- postgres:
- image: postgres:16
- env:
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: postgres
- POSTGRES_PORT: 5432
- POSTGRES_DB: postgres
- options: >-
- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
- ports:
- - 5432:5432
- steps:
- - uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v4
- with:
- go-version: '1.23'
- cache: true
- - name: Install dependencies
- run: go mod download
- - name: Install Goose
- run: go install github.com/pressly/goose/v3/cmd/goose@v3.22.1
- - name: Run migrations
- run: goose -dir .sqlc/migrations postgres "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" up
- - name: Run tests
- id: tests
- continue-on-error: true
- run: |
- # run tests and capture output
- go test -v -coverprofile=coverage.out ./... 2>&1 | tee test_output.txt
- # store the exit code explicitly
- echo "::set-output name=exit_code::${PIPESTATUS[0]}"
- - name: Generate coverage report
- run: go tool cover -html=coverage.out -o coverage.html
- - name: Upload coverage report
- if: ${{ !env.ACT && github.event_name == 'pull_request' }}
- uses: actions/upload-artifact@v4
- with:
- name: coverage-report
- path: coverage.html
- - name: Comment PR
- if: ${{ !env.ACT && github.event_name == 'pull_request' && always() }}
- uses: actions/github-script@v7
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: "const fs = require('fs');\n\n// Read test output\nconst testOutput = fs.readFileSync('test_output.txt', 'utf8');\n\n// Get coverage - look for the last coverage number in the output\nlet coverage = 'N/A';\nconst coverageMatches = testOutput.match(/coverage: (\\d+\\.\\d+)% of statements/g) || [];\nif (coverageMatches.length > 0) {\n const lastMatch = coverageMatches[coverageMatches.length - 1];\n coverage = lastMatch.match(/(\\d+\\.\\d+)%/)[1] + '%';\n}\n\n// Check if any tests failed\nconst hasFailed = testOutput.includes('FAIL') && !testOutput.includes('FAIL\\t[build failed]');\nconst testStatus = hasFailed ? 'failure' : 'success';\nconst color = testStatus === 'success' ? '✅' : '❌';\n\n// Parse test failures\nlet failureDetails = '';\nif (hasFailed) {\n const errorTraces = testOutput.match(/\\s+.*?_test\\.go:\\d+:[\\s\\S]*?Test:\\s+.*$/gm) || [];\n const failures = testOutput.match(/--- FAIL: .*?(?=(?:---|\\z))/gs) || [];\n \n failureDetails = `\n \n ❌ Test Failures
\n \n \\`\\`\\`\n ${failures.join('\\n')}\n \n Error Details:\n ${errorTraces.map(trace => trace.trim()).join('\\n')}\n \\`\\`\\`\n \n `;\n}\n\nconst output = `### Test Results ${color}\n\n**Status**: ${testStatus}\n**Coverage**: ${coverage}\n**OS**: \\`${{ runner.os }}\\`\n\n${failureDetails}\n\n\nTest Details
\n\n* Triggered by: @${{ github.actor }}\n* Commit: ${{ github.sha }}\n* Branch: ${{ github.ref }}\n* Workflow: ${{ github.workflow }}\n `;\n\n// Find existing comment\nconst { data: comments } = await github.rest.issues.listComments({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n});\n\nconst botComment = comments.find(comment => \n comment.user.type === 'Bot' && \n comment.body.includes('### Test Results')\n);\n\nif (botComment) {\n // Update existing comment\n await github.rest.issues.updateComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n comment_id: botComment.id,\n body: output\n });\n} else {\n // Create new comment\n await github.rest.issues.createComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n body: output\n });\n}\n"
- - uses: actions/cache@v4
- with:
- path: |
- ~/.cache/go-build
- ~/go/pkg/mod
- key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- restore-keys: |
- ${{ runner.os }}-go-
diff --git a/.github/workflows/backend/deploy-preview-backend.yml b/.github/workflows/deploy-preview-backend.yml
similarity index 95%
rename from .github/workflows/backend/deploy-preview-backend.yml
rename to .github/workflows/deploy-preview-backend.yml
index 62325e5f..63357692 100644
--- a/.github/workflows/backend/deploy-preview-backend.yml
+++ b/.github/workflows/deploy-preview-backend.yml
@@ -46,8 +46,9 @@ jobs:
rm -rf repo
git clone git@github.com:${{ github.repository }}.git repo
cd repo
+ cd backend
echo "Copy deploy preview script"
- cp .github/scripts/deploy-preview.sh .
+ cp ../.github/scripts/deploy-preview.sh .
echo "Running deploy-preview.sh"
chmod +x deploy-preview.sh
./deploy-preview.sh
diff --git a/.github/workflows/frontend/deploy-preview-frontend.yml b/.github/workflows/deploy-preview-frontend.yml
similarity index 91%
rename from .github/workflows/frontend/deploy-preview-frontend.yml
rename to .github/workflows/deploy-preview-frontend.yml
index 2d9cde84..7a2b52e4 100644
--- a/.github/workflows/frontend/deploy-preview-frontend.yml
+++ b/.github/workflows/deploy-preview-frontend.yml
@@ -37,15 +37,16 @@ jobs:
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: cd frontend && pnpm install --frozen-lockfile
- name: Build
- run: pnpm build
+ run: cd frontend && pnpm build
- name: Deploy to server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.PREVIEW_SERVER_IP }}
username: ${{ secrets.PREVIEW_USER }}
key: ${{ secrets.PREVIEW_SERVER_SSH_KEY }}
- source: "dist/"
+ source: "frontend/dist/"
target: "${{ secrets.TARGET_DIR }}"
strip_components: 1
+
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
new file mode 100644
index 00000000..041e93c6
--- /dev/null
+++ b/.github/workflows/test-backend.yml
@@ -0,0 +1,59 @@
+on:
+ push:
+ branches: ['main', 'staging', 'prod']
+ paths:
+ - 'backend/**'
+ pull_request:
+ branches: ['main', 'staging', 'prod']
+ paths:
+ - 'backend/**'
+jobs:
+ test-backend:
+ name: Test Backend
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:16
+ env:
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_PORT: 5432
+ POSTGRES_DB: postgres
+ 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
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.23'
+ cache: true
+ - name: Install dependencies
+ run: cd backend && go mod download
+ - name: Install Goose
+ run: cd backend && go install github.com/pressly/goose/v3/cmd/goose@v3.22.1
+ - name: Run migrations
+ run: cd backend && goose -dir .sqlc/migrations postgres "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" up
+ - name: Run tests
+ run: |
+ # run tests and capture output
+ cd backend
+ go test -v -coverprofile=coverage.out ./...
+ - name: Generate coverage report
+ if: ${{ !env.ACT && github.event_name == 'pull_request' && always() }}
+ run: cd backend && go tool cover -html=coverage.out -o coverage.html
+ - name: Upload coverage report
+ if: ${{ !env.ACT && github.event_name == 'pull_request' && always() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-report-backend
+ path: backend/coverage.html
+ # - name: Comment PR
+ # if: ${{ !env.ACT && github.event_name == 'pull_request' && always() }}
+ # uses: actions/github-script@v7
+ # with:
+ # github-token: ${{ secrets.GITHUB_TOKEN }}
+ # script: "const fs = require('fs');\n\n// Read test output\nconst testOutput = fs.readFileSync('backend/test_output.txt', 'utf8');\n\n// Get coverage - look for the last coverage number in the output\nlet coverage = 'N/A';\nconst coverageMatches = testOutput.match(/coverage: (\\d+\\.\\d+)% of statements/g) || [];\nif (coverageMatches.length > 0) {\n const lastMatch = coverageMatches[coverageMatches.length - 1];\n coverage = lastMatch.match(/(\\d+\\.\\d+)%/)[1] + '%';\n}\n\n// Check if any tests failed\nconst hasFailed = testOutput.includes('FAIL') && !testOutput.includes('FAIL\\t[build failed]');\nconst testStatus = hasFailed ? 'failure' : 'success';\nconst color = testStatus === 'success' ? '✅' : '❌';\n\n// Parse test failures\nlet failureDetails = '';\nif (hasFailed) {\n const errorTraces = testOutput.match(/\\s+.*?_test\\.go:\\d+:[\\s\\S]*?Test:\\s+.*$/gm) || [];\n const failures = testOutput.match(/--- FAIL: .*?(?=(?:---|\\z))/gs) || [];\n \n failureDetails = `\n \n ❌ Test Failures
\n \n \\`\\`\\`\n ${failures.join('\\n')}\n \n Error Details:\n ${errorTraces.map(trace => trace.trim()).join('\\n')}\n \\`\\`\\`\n \n `;\n}\n\nconst output = `### Test Results ${color}\n\n**Status**: ${testStatus}\n**Coverage**: ${coverage}\n**OS**: \\`${{ runner.os }}\\`\n\n${failureDetails}\n\n\nTest Details
\n\n* Triggered by: @${{ github.actor }}\n* Commit: ${{ github.sha }}\n* Branch: ${{ github.ref }}\n* Workflow: ${{ github.workflow }}\n `;\n\n// Find existing comment\nconst { data: comments } = await github.rest.issues.listComments({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n});\n\nconst botComment = comments.find(comment => \n comment.user.type === 'Bot' && \n comment.body.includes('### Test Results')\n);\n\nif (botComment) {\n // Update existing comment\n await github.rest.issues.updateComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n comment_id: botComment.id,\n body: output\n });\n} else {\n // Create new comment\n await github.rest.issues.createComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n body: output\n });\n}\n"
diff --git a/.github/workflows/frontend/test-frontend.yml b/.github/workflows/test-frontend.yml
similarity index 65%
rename from .github/workflows/frontend/test-frontend.yml
rename to .github/workflows/test-frontend.yml
index 05841915..95e46357 100644
--- a/.github/workflows/frontend/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -1,4 +1,3 @@
-name: Test Frontend
on:
push:
branches: ['main', 'staging', 'prod']
@@ -10,13 +9,11 @@ on:
- 'frontend/**'
jobs:
test-frontend:
+ name: Test Frontend
runs-on: ubuntu-latest
- defaults:
- run:
- shell: bash
- working-directory: frontend
steps:
- - uses: actions/checkout@v4
+ - name: Checkout code
+ uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
@@ -26,16 +23,14 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '20'
- cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: cd frontend && pnpm install --frozen-lockfile
- name: Run tests
- run: pnpm test:ci
+ run: cd frontend && pnpm test:ci
- name: Generate coverage report
- run: pnpm coverage
+ run: cd frontend && pnpm coverage
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
- name: coverage-report
- path: coverage/
- retention-days: 7
+ name: coverage-report-frontend
+ path: frontend/coverage/
diff --git a/backend/.gitignore b/backend/.gitignore
index 164b1434..af6d1cfa 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -4,4 +4,7 @@ tmp
*.pem
-static
\ No newline at end of file
+static
+
+# ignore test coverage
+coverage.out
diff --git a/backend/Makefile b/backend/Makefile
index 3a8f7b35..8333cdbd 100644
--- a/backend/Makefile
+++ b/backend/Makefile
@@ -73,7 +73,7 @@ start-testdb:
@goose -dir .sqlc/migrations postgres "$(TEST_DB_URL)" up
run-tests:
- @go test -v ./...
+ @go test -v -coverprofile=coverage.out ./...
stop-testdb:
@echo "Stopping test db..."
diff --git a/backend/README.md b/backend/README.md
index 86c0b78f..6482d96a 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -9,9 +9,9 @@ Make sure to download **VERSION 1.23** for best compatibility.
### Install Pre-requisite tools
-- Air (auto-reload backend): go install github.com/air-verse/air@latest
-- SQLc (generate type-safe code from SQL queries): go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
-- Goose (SQL migration management tool): go install github.com/pressly/goose/v3/cmd/goose@latest
+- Air (auto-reload backend): go install github.com/air-verse/air@1.61.1
+- SQLc (generate type-safe code from SQL queries): go install github.com/sqlc-dev/sqlc/cmd/sqlc@1.27.0
+- Goose (SQL migration management tool): go install github.com/pressly/goose/v3/cmd/goose@3.22.1
- Make
- Docker
@@ -22,8 +22,8 @@ Make sure to download **VERSION 1.23** for best compatibility.
1. Create a new PostgreSQL instance using docker with `make init-dev-db`
2. Start PostgreSQL for development `make start-dev-db`
- Check health of DB `make health-dev-db`
-4. Run migrations when ready `make up`
-5. Start development server `make dev`
+3. Run migrations when ready `make up`
+4. Start development server `make dev`
> Use `make query "SELECT ... FROM ..."` for quick query on the terminal.
> You should also checkout the other available commands in the Makefile.
diff --git a/frontend/index.html b/frontend/index.html
index e4b78eae..f07f8f4d 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -4,7 +4,7 @@
-
Vite + React + TS
+ SPUR