TripQlub Platform — TeamCity Build & Deployment Strategy¶
Overview¶
This document describes the build and deployment pipeline for the TripQlub platform, which consists of a single monorepo containing:
- Backend API Server — Java/Jetty with multiple modules (Gradle build)
- Frontend B2C — Public customer-facing web application(s)
- Frontend Agency/Admin — Internal agency and admin web application
- Frontend B2B — Business-to-business web application
- Database Migrations — Versioned SQL scripts managed by Flyway
All applications share a single Git repository. The build strategy uses path-based VCS triggers in TeamCity to ensure that only the affected components are built and deployed when changes are committed.
Repository Structure¶
tripqlub/
├── backend/
│ ├── api-core/
│ ├── module-bookings/
│ ├── module-itineraries/
│ ├── module-auth/
│ ├── ...
│ └── build.gradle
├── frontend-b2c/
│ ├── src/
│ ├── package.json
│ └── ...
├── frontend-agency/
│ ├── src/
│ ├── package.json
│ └── ...
├── frontend-b2b/
│ ├── src/
│ ├── package.json
│ └── ...
├── db/
│ └── migration/
│ ├── V001__initial_schema.sql
│ ├── V002__add_booking_status.sql
│ └── ...
├── shared/
│ ├── configs/
│ └── ...
├── build.gradle (root Gradle build)
└── settings.gradle
Note: Adjust directory names to match the actual repository layout. The trigger rules below should be updated accordingly.
Build Configurations¶
1. Backend — Build & Deploy¶
Purpose: Compile, test, package, and deploy the Java/Jetty backend API server.
VCS Trigger Rules:
Build Steps:
| Step | Runner | Command / Configuration |
|---|---|---|
| 1. Build & Test | Gradle | clean build |
| 2. Package | Gradle | shadowJar or distTar (depending on packaging strategy) |
| 3. Upload Artifact | Command Line | Upload JAR/archive to S3 artifact bucket |
| 4. Deploy | Command Line | Trigger deployment to target environment (see Deployment section) |
Gradle Build Step Details:
# Step 1 & 2 can be combined
./gradlew clean build shadowJar \
-PdbUrl=jdbc:postgresql://localhost:5432/tripqlub_test \
-PdbUser=%env.DB_USER% \
-PdbPassword=%env.DB_PASSWORD%
Artifact Paths:
Parameters:
| Parameter | Description | Example |
|---|---|---|
env.DEPLOY_TARGET |
Target environment | dev / stage / prod |
env.DB_HOST |
Database host for the target | prod-db.internal |
env.BACKEND_HOST |
Backend server SSH target | api.tripqlub.com |
2. Frontend B2C — Build & Deploy¶
Purpose: Build and deploy the public customer-facing web application.
VCS Trigger Rules:
Build Steps:
| Step | Runner | Command / Configuration |
|---|---|---|
| 1. Install Dependencies | Command Line | cd frontend-b2c && npm ci |
| 2. Build | Command Line | cd frontend-b2c && npm run build |
| 3. Upload to S3 / Deploy | Command Line | Sync build output to web server or S3 bucket |
Build Commands:
# Step 1: Install
cd frontend-b2c
npm ci
# Step 2: Build with environment-specific config
npm run build -- --env=%env.DEPLOY_TARGET%
# Step 3: Deploy (example: sync to server via rsync over SSH)
rsync -avz --delete dist/ deploy@%env.B2C_HOST%:/var/www/tripqlub-b2c/
Artifact Paths:
3. Frontend Agency/Admin — Build & Deploy¶
Purpose: Build and deploy the internal agency and admin web application.
VCS Trigger Rules:
Build Steps:
| Step | Runner | Command / Configuration |
|---|---|---|
| 1. Install Dependencies | Command Line | cd frontend-agency && npm ci |
| 2. Build | Command Line | cd frontend-agency && npm run build |
| 3. Deploy | Command Line | Sync build output to admin server |
Build Commands:
cd frontend-agency
npm ci
npm run build -- --env=%env.DEPLOY_TARGET%
rsync -avz --delete dist/ deploy@%env.AGENCY_HOST%:/var/www/tripqlub-agency/
4. Frontend B2B — Build & Deploy¶
Purpose: Build and deploy the B2B web application.
VCS Trigger Rules:
Build Steps:
| Step | Runner | Command / Configuration |
|---|---|---|
| 1. Install Dependencies | Command Line | cd frontend-b2b && npm ci |
| 2. Build | Command Line | cd frontend-b2b && npm run build |
| 3. Deploy | Command Line | Sync build output to B2B server |
Build Commands:
cd frontend-b2b
npm ci
npm run build -- --env=%env.DEPLOY_TARGET%
rsync -avz --delete dist/ deploy@%env.B2B_HOST%:/var/www/tripqlub-b2b/
5. Database Migrations¶
Purpose: Execute pending Flyway migrations against the target database.
VCS Trigger Rules:
Build Steps:
| Step | Runner | Command / Configuration |
|---|---|---|
| 1. Run Flyway Migrate | Gradle | flywayMigrate with target DB parameters |
| 2. Validate | Gradle | flywayValidate (optional post-check) |
Build Commands:
./gradlew flywayMigrate \
-Pflyway.url=jdbc:postgresql://%env.DB_HOST%:5432/%env.DB_NAME% \
-Pflyway.user=%env.DB_USER% \
-Pflyway.password=%env.DB_PASSWORD% \
-Pflyway.locations=filesystem:db/migration
Important Considerations:
- DB migrations should run before backend deployment when both are triggered by the same commit
- Use a snapshot dependency from the Backend build to the DB Migrations build to enforce ordering
- For production, consider adding a manual approval step before migration execution
Deployment Strategy¶
Environment Pipeline¶
Commit
│
▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ DEV │────▶│ STAGE │────▶│ PROD │
└─────────┘ └─────────┘ └─────────┘
(automatic) (automatic or (manual
manual trigger) trigger)
Each build configuration should be parameterized by environment. There are two approaches:
Option A: Separate Build Configs Per Environment¶
Create copies of each build configuration for each environment (e.g., "Backend — Deploy DEV", "Backend — Deploy STAGE", "Backend — Deploy PROD"). The DEV config has VCS triggers; STAGE and PROD are triggered manually or via snapshot dependencies on the DEV build.
Pros: Clear separation, easy to see what's deployed where, simple permissions (restrict PROD configs).
Cons: More configurations to maintain.
Option B: Single Build Config with Environment Parameter¶
Each build configuration accepts a DEPLOY_TARGET parameter. The VCS trigger deploys to DEV automatically. STAGE and PROD deployments are triggered manually with the parameter overridden.
Pros: Less duplication, fewer configurations.
Cons: Harder to audit what's deployed where.
Recommendation: Option A is better for your setup — it's more explicit and allows you to set different permissions for production deployments.
Dependency & Ordering¶
Snapshot Dependencies¶
For cases where a single commit triggers multiple builds (e.g., changes to shared/ trigger all builds), configure snapshot dependencies to control execution order:
This ensures: 1. Database migrations run first 2. Backend builds and deploys after migrations succeed 3. Frontend builds run in parallel after backend succeeds (if there are frontend changes)
Note: Snapshot dependencies enforce ordering only when builds are triggered together. If only a frontend changes (and backend/DB are not triggered), the frontend builds independently without waiting.
TeamCity VCS Trigger Configuration (UI Steps)¶
For each build configuration:
- Navigate to Build Configuration Settings → Triggers
- Click Add new trigger → VCS Trigger
- Expand Show advanced options
- In the VCS trigger rules field, enter the path rules listed above for the specific configuration
- Set Quiet period to 30–60 seconds (batches rapid commits into a single build)
- Under Branch filter, set to your main/deploy branch:
Checkout Rules (Optional Optimization)¶
If you want to reduce checkout time, you can add VCS Checkout Rules to only fetch relevant directories. However, since your builds may need cross-directory access (e.g., shared configs), it's generally safer to check out the full repository and rely only on trigger rules for filtering.
Full Deployment Sequence Example¶
Scenario: A commit modifies backend/ and db/migration/.
- TeamCity detects the commit and evaluates trigger rules
- DB Migrations build triggers (matches
db/migration/**) - Backend build triggers (matches
backend/**) - Snapshot dependency ensures DB Migrations completes first
- DB Migrations runs
flywayMigrate— applies pending scripts - Backend builds, tests, packages, and deploys the new JAR
- Frontend builds are not triggered (no matching path changes)
Scenario: A commit modifies only frontend-b2c/.
- TeamCity detects the commit
- Only Frontend B2C build triggers
- B2C app is built and deployed
- Backend, other frontends, and DB migrations are untouched
Scenario: A commit modifies shared/.
- All build configurations trigger (all include
shared/**in their rules) - Dependency chain ensures correct ordering
- Everything is rebuilt and redeployed
Gradle Configuration for Flyway¶
Add to your root build.gradle:
plugins {
id 'org.flywaydb.flyway' version '10.21.0'
}
flyway {
url = project.findProperty('flyway.url') ?: 'jdbc:postgresql://localhost:5432/tripqlub'
user = project.findProperty('flyway.user') ?: 'postgres'
password = project.findProperty('flyway.password') ?: ''
locations = [
project.findProperty('flyway.locations') ?: 'filesystem:db/migration'
]
baselineOnMigrate = true
baselineVersion = '0'
}
Baselining an Existing Database¶
For your existing databases that already have a schema:
# Run once per existing database to mark current state as the baseline
./gradlew flywayBaseline \
-Pflyway.url=jdbc:postgresql://prod-host:5432/tripqlub \
-Pflyway.user=admin \
-Pflyway.password=*** \
-Pflyway.baselineVersion=1
This creates the flyway_schema_history table and marks version 1 as already applied. All future migrations (V2, V3, ...) will be applied normally.
Migration Script Naming Convention¶
db/migration/
├── V001__initial_baseline.sql
├── V002__add_booking_status_column.sql
├── V003__create_itinerary_templates_table.sql
├── V004__add_agency_commission_fields.sql
└── ...
Rules:
- Prefix: V followed by a zero-padded version number
- Double underscore __ separates version from description
- Description uses underscores for spaces
- Once applied to any environment, never modify an existing migration — always create a new one
- For reversible changes, consider also creating U (undo) scripts: U003__drop_itinerary_templates_table.sql
Security Notes¶
- Store database credentials as TeamCity password parameters (type: Password), never in plain text
- Use environment-specific parameter sets to prevent accidental production deployments
- For production deployments, enable manual approval in the build chain
- The deployment pipeline should use the air-gapped S3/SQS mechanism already in place for production environments