Skip to content

CI/CD with GitHub Actions

The repository’s workflows build and check every pull request, publish Docker images when a pull request is merged, and can deploy those images to your servers. A fork gets the same workflows; nothing deploys until you add the variables and secrets below.

WorkflowRunsNeeds
Smoke Test (smoke-test.yml)Pull requests into staging or production; manual. Jobs: Build Backend (build and tests), Build Web, Build Mobile (Flutter)Nothing
OpenAPI Spec Check (openapi-check.yml)Pull requests that touch backend/ or docs/public/openapi/; manualNothing
Publish Docker images (publish-images.yml)Push to staging or production; manualDOCKERHUB_NAMESPACE variable, DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets
Deploy to server (deploy.yml)Manual, or automatically after Publish Docker images (see below)Server secrets in the staging / production environment
Mobile CI (mobile-ci.yml)Manual, on staging or productionSigning and app secrets. See Mobile App
Deploy Documentation to Production (production-docs-deploy.yml)Manual, on productionThe same server secrets as Deploy to server

The last workflow copies the built docs site to /var/www/dharini-docs on a server with SSH and sudo. Skip it if you host docs/dist somewhere else.

staging and production are the two deployable branches. Create a GitHub environment with the same name for each (Settings, Environments). Deploy to server and Mobile CI pick the environment from the branch they run on, so each branch uses its own server and its own values. On the production environment, add required reviewers if you want a person to approve each deployment, and limit “Deployment branches” to production.

Add these under Settings, Secrets and variables, Actions.

Repository variables

NameValue
DOCKERHUB_NAMESPACEDocker Hub user or organization the images are pushed to. Publishing is skipped while this is unset
AUTO_DEPLOY_STAGING, AUTO_DEPLOY_PRODUCTIONOptional. true deploys that branch right after its images are published

Repository secrets

NameValue
DOCKERHUB_USERNAME, DOCKERHUB_TOKENDocker Hub login and an access token with Read & Write

Environment secrets (set separately in staging and production)

NameUsed byValue
HOST, USERNAME, SSH_PRIVATE_KEYDeploy to server, docs, Mobile CI web copyThe server and the user to connect as
JUMP_HOST, JUMP_USER, JUMP_KEYSameOnly if you reach the server through a jump host
API_BASE_URL, SENTRY_ENVMobile CIDiffer between staging and production
MAP_API_KEY, SENTRY_DSNMobile CIApp settings
ANDROID_KEYSTORE_BASE64, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD, ANDROID_STORE_PASSWORDMobile CISigning. Use the same keystore for both environments

A secret set on the repository is also visible to the environments, so values that are the same everywhere can go there.

Deploy to server logs in over SSH and works in the deploy folder of that user’s home directory:

  • deploy/infra/root.env holds the settings for that server. It is copied to .env on every deployment, so keep a separate file for staging and production. Production Setup, Step 5 lists the variables the backend needs to start, and Environment Variables describes all of them.
  • Each run downloads the current docker-compose.yml and deploy.sh into deploy/dharini and runs ./deploy.sh. Keep that folder name: Docker Compose uses it as the project name, and the database volume is named after it.
  • The user needs Docker with Compose (up --wait) and curl. The server does not need git, and nothing is built on it.

What differs between staging and production

Section titled “What differs between staging and production”

Give each server its own value for these, and never reuse secrets between them:

VariableStagingProduction
NEXT_PUBLIC_APP_ENVstaging (shows a test-data banner)production
NEXT_PUBLIC_API_URL, CORS_ORIGINS, FRONTEND_PLATFORMThe staging site’s addressesThe production site’s addresses
DB_PASSWORD, JWT_SECRET, WEBHOOK_SECRET, S3_*Its own valuesIts own values
PIPELINE_CALLBACK_URLStaging backend addressProduction backend address
NEXT_PUBLIC_APK_DOWNLOAD_URLThe staging-latest release linkThe releases/latest link (see Mobile App)
NEXT_PUBLIC_MOBILE_WEB_URLThe staging mobile web addressThe production mobile web address
NODE_ENV, ALLOW_FIXED_OTPproduction, or any other value with ALLOW_FIXED_OTP=true if testers should sign in with the fixed code 123456production, ALLOW_FIXED_OTP unset
IMAGE_REGISTRYYour Docker Hub namespace if it is not dsihartparkThe same

Then run Deploy to server on staging to try it, and on production when you are ready. To roll back, run it again with image_tag set to an earlier sha-<commit> from Docker Hub.

On any server with Docker, refresh the two files and run the script. This is all the workflow does:

Terminal window
curl -fsSLO https://raw.githubusercontent.com/dsih-artpark/dharini/production/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/dsih-artpark/dharini/production/deploy.sh && chmod +x deploy.sh
./deploy.sh production

A cron job, or any tool that can run this over SSH, gives you continuous deployment without GitHub Actions.