Reusable GitHub Actions workflows for the BehindTheMusicTree organization.
See CHANGELOG.md for a detailed history of changes.
Contributions are welcome! See CONTRIBUTING.md for guidelines.
- Available Workflows
- Usage
- Required Configuration
- Setup Instructions
- Workflow Behavior
- Webhook Endpoint
- Troubleshooting
- Contributing
- Changelog
- License
Runs actionlint on the caller repo. Uses .github/actionlint.yaml from the caller if present, so repo-specific ignore rules (e.g. for reusable workflow calls) apply.
Workflow file: .github/workflows/actionlint.yml
No inputs. Call from your repo on push or pull_request:
jobs:
actionlint:
uses: BehindTheMusicTree/github-workflows/.github/workflows/actionlint.yml@mainTriggers a server redeployment webhook. Validates configuration, ensures env is prod or staging, and POSTs to the webhook (optional JSON image overrides in body). Response must start with Redeploying BTMT ecosystem.
Workflow file: .github/workflows/call-redeployment-webhook.yml
| Input | Required | Description |
|---|---|---|
env |
Yes | prod or staging (lowercase) |
images |
No | Optional JSON object of image overrides (e.g. {"gateway_image": "user/repo:tag"}). Default {}. |
App-agnostic reusable: upload an env fragment and merge it into the server scripts/.env for one environment. Path and fragment content come from the caller’s vars and secrets (no app-specific inputs). Any app can use it by setting the required vars/secrets in its repo and calling with secrets: inherit and the right environment.
Workflow file: .github/workflows/sync-env-to-server.yml
| Input | Required | Description |
|---|---|---|
sync_env |
Yes | prod or staging (lowercase) |
app_name |
Yes | App name for fragment path (e.g. htmt-api) |
fragment_artifact |
Yes | Artifact name and path to fragment file (e.g. sync-env-fragment-staging/fragment.env) |
Caller must: (1) Build the fragment in a job (app-specific keys), write it to a file (e.g. fragment.env), and upload it with actions/upload-artifact using the same artifact name. (2) Have a job that calls this workflow with needs: build-fragment, secrets: inherit, and inputs sync_env, app_name, fragment_artifact (e.g. sync-env-fragment-staging/fragment.env). Required vars (repo or environment): VPS_IP, REDEPLOYMENT_ROOT (e.g. /var/webhook/redeployment), SYNC_ENV_REMOTE_FILENAME_PREFIX_BASE. Required secrets: SERVER_DEPLOY_USERNAME, SERVER_DEPLOY_SSH_PRIVATE_KEY. Required vars (repo or environment): VPS_IP, REDEPLOYMENT_ROOT (e.g. /var/webhook/redeployment), SYNC_ENV_REMOTE_FILENAME_PREFIX_BASE, and either HTMT_API_APP_NAME or APP_NAME (app name for fragment path). Required secrets: SERVER_DEPLOY_USERNAME, SERVER_DEPLOY_SSH_PRIVATE_KEY, plus any vars/secrets for the keys included in the fragment (see workflow: FRAGMENT_KEYS and the “Build env fragment” step). To support a new app or new keys, add the key to FRAGMENT_KEYS and to the Build env fragment step env in this repo.
Uploads compose env files to pool/compose/<app_name>/ on the server. Caller must upload an artifact (e.g. app-env-files) containing env files. Use non-dotfile names in the artifact (e.g. env_api, env_gtmt_front) so upload-artifact includes them; this workflow renames them to dotfiles (e.g. .env_api) before uploading.
Workflow file: .github/workflows/deploy-app-env-file.yml
| Input | Required | Description |
|---|---|---|
env |
Yes | prod or staging (lowercase) |
app_name |
Yes | Subdir under pool/compose (e.g. htmt-api, gtmt-front) |
artifact_name |
No | Artifact name; default app-env-files |
Uploads a single nginx env fragment to pool/nginx/<app_name>.env. Caller must upload an artifact (e.g. nginx-env-fragment) containing exactly one file.
Workflow file: .github/workflows/deploy-nginx-env-fragment.yml
| Input | Required | Description |
|---|---|---|
env |
Yes | prod or staging (lowercase) |
app_name |
Yes | Fragment is uploaded as pool/nginx/<app_name>.env |
artifact_name |
No | Artifact name; default nginx-env-fragment |
Uploads partial docker-compose files to the server compose dir. Before upload, adds suffix -<env> to every container_name in the compose files (e.g. gtmt-front → gtmt-front-staging). Caller must upload an artifact (e.g. compose-parts) containing the partial YAML file(s).
Workflow file: .github/workflows/deploy-docker-compose-part.yml
| Input | Required | Description |
|---|---|---|
env |
Yes | prod or staging (lowercase) |
app_version |
No | For logging only |
artifact_name |
No | Artifact name; default compose-parts |
jobs:
call-redeployment-webhook:
name: Call Redeployment Webhook
uses: BehindTheMusicTree/github-workflows/.github/workflows/call-redeployment-webhook.yml@main
with:
env: "staging" # or "prod"
images: "{}" # optional: {"gateway_image": "user/repo:tag"}
secrets: inheritWith dependencies (e.g. after build):
jobs:
build-and-push:
name: Build and Push
runs-on: ubuntu-latest
steps:
- name: Build
run: echo "Building..."
call-redeployment-webhook:
name: Call Redeployment Webhook
needs: [build-and-push]
uses: BehindTheMusicTree/github-workflows/.github/workflows/call-redeployment-webhook.yml@main
with:
env: "staging"
secrets: inheritReference caller workflows that prepare artifacts and call the deploy reusables are in examples/:
examples/deploy-htmt-api-env-and-compose.yml— API app: prepare nginx fragment, app env files (API/DB/AFP), and compose parts; call deploy-nginx-env-fragment, deploy-app-env-file, deploy-docker-compose-part. Copy to your htmt-api repo and adapt.examples/deploy-gtmt-front-env-and-compose.yml— Front app: prepare env file and compose parts; call deploy-app-env-file and deploy-docker-compose-part. Copy to your gtmt-front repo and adapt.
Caller pattern: one prepare job uploads artifacts (use non-dotfile names for app env files); separate jobs with needs: [prepare] call each reusable with secrets: inherit. Deploy workflows run in the caller’s context and need the vars/secrets listed below.
This repo only contains workflow definitions. Each repository that calls these workflows must configure the required secrets and variables in GitHub (repository or organization) under the environment used (staging or prod).
| Type | Name | Description |
|---|---|---|
| Variable | VPS_IP |
VPS IP or hostname for webhook URL (use when main domain points elsewhere, e.g. Vercel) |
| Variable | REDEPLOYMENT_HOOK_ID_BASE |
Base hook id; URL path is /hooks/<REDEPLOYMENT_HOOK_ID_BASE>-<env> (e.g. myhook-staging) |
| Secret | REDEPLOYMENT_WEBHOOK_PORT |
Port the webhook service listens on |
| Secret | REDEPLOYMENT_WEBHOOK_SECRET_STAGING |
Webhook secret for env staging (X-Secret header) |
| Secret | REDEPLOYMENT_WEBHOOK_SECRET_PROD |
Webhook secret for env prod |
Required by sync-env-to-server (caller’s environment):
| Type | Name | Description |
|---|---|---|
| Variable | VPS_IP |
VPS IP or hostname for SSH |
| Variable | REDEPLOYMENT_ROOT |
Redeployment tree root on server (e.g. /var/webhook/redeployment); scripts dir = {REDEPLOYMENT_ROOT}-{env}/scripts/ |
| Variable | SYNC_ENV_REMOTE_FILENAME_PREFIX_BASE |
Fragment filename prefix (e.g. sync-env-) |
| Variable | HTMT_API_APP_NAME or APP_NAME |
App name for fragment path (e.g. htmt-api) |
| Secret | SERVER_DEPLOY_USERNAME |
SSH user |
| Secret | SERVER_DEPLOY_SSH_PRIVATE_KEY |
SSH private key |
Plus any vars/secrets for the fragment keys (see workflow; add new keys in the reusable when needed).
Required by deploy-app-env-file, deploy-nginx-env-fragment, and deploy-docker-compose-part (caller’s environment must have these):
| Type | Name | Description |
|---|---|---|
| Variable | REDEPLOYMENT_ROOT |
Redeployment tree root (e.g. /var/webhook/redeployment); pool/compose paths use {REDEPLOYMENT_ROOT}-{env}/ |
| Variable | DOCKER_COMPOSE_DIR_NAME |
Compose dir name under redeploy dir |
| Variable | VPS_IP |
VPS IP or hostname for SSH |
| Secret | SERVER_DEPLOY_USERNAME |
SSH user for deploy |
| Secret | SERVER_DEPLOY_SSH_PRIVATE_KEY |
SSH private key for deploy |
- Configure secrets and variables in the repo (or org) that calls the workflows: Settings → Environments → create or select
staging/prodand add the required entries from Required Configuration. - Add workflow calls to your workflow file (see Usage). For deploy flows, copy and adapt a caller from
examples/. - Verify access: Public repos can use these reusables as-is; private repos require the org to allow reusable workflows from private repos.
- Validate env: Ensures
envisprodorstaging. - Check required config: Validates webhook-related secrets and variables for that env.
- Call webhook: POSTs to the webhook URL (optional JSON body for image overrides).
- Validate response: Fails if response does not start with
Redeploying BTMT ecosystem.
- Status code:
200 OK - Response body: Must start with
Redeploying BTMT ecosystem(e.g.Redeploying BTMT ecosystem (staging)is accepted).
The webhook workflow fails with clear errors if:
envis notprodorstaging- Required secrets/variables are missing
- Webhook is unreachable (e.g. connection refused)
- Response does not match expected prefix or hook not found (404)
The workflow builds the URL using VPS_IP (the webhook runs on the VPS; use VPS IP when the main domain points elsewhere, e.g. Vercel):
http://<VPS_IP>:<REDEPLOYMENT_WEBHOOK_PORT>/hooks/<REDEPLOYMENT_HOOK_ID_BASE>-<env>
Example: VPS_IP=203.0.113.10, REDEPLOYMENT_WEBHOOK_PORT=9000, REDEPLOYMENT_HOOK_ID_BASE=btmt-redeploy, env=staging
→ http://203.0.113.10:9000/hooks/btmt-redeploy-staging
Manual check (use the secret for the env you target):
curl -v -X POST -H "Content-Type: application/json" -H "X-Secret: YOUR_SECRET" -d '{}' --max-time 15 \
http://<VPS_IP>:9000/hooks/btmt-redeploy-staging- Ensure the repository is public (or your organization allows private repo access)
- Verify the workflow file exists on the
mainbranch - Check that the repository path is correct
- Set all required secrets and variables for the environment you use (
stagingorprod) - Use
REDEPLOYMENT_WEBHOOK_SECRET_STAGINGandREDEPLOYMENT_WEBHOOK_SECRET_PROD(not a singleREDEPLOYMENT_WEBHOOK_SECRET) - Ensure
REDEPLOYMENT_HOOK_ID_BASEis set; the hook path is<REDEPLOYMENT_HOOK_ID_BASE>-<env>
- Ensure the webhook service is running on the server:
systemctl status webhook - Check port and firewall;
REDEPLOYMENT_WEBHOOK_PORTmust match the server - In
hooks.json, the hook id must be<REDEPLOYMENT_HOOK_ID_BASE>-<env>(e.g.btmt-redeploy-staging) - Verify the X-Secret header matches the secret for that env (
REDEPLOYMENT_WEBHOOK_SECRET_STAGINGor_PROD)
- Artifacts are created in the caller’s run; reusables in another repo may not see them. Use a single combined artifact (e.g. one
app-env-fileswith all env files) and consider inlining the deploy job in the caller if the reusable still can’t download it. - Use non-dotfile names in app-env artifacts (e.g.
env_apinot.env_api) soupload-artifactincludes the files; the deploy-app-env-file workflow renames them to dotfiles on the server.
This repository contains reusable workflows for the BehindTheMusicTree organization.
For detailed contribution guidelines, including development workflow, branching strategy, and pull request process, see CONTRIBUTING.md.
Quick start:
- Create a feature branch
- Make your changes
- Test the workflow in a calling repository
- Submit a pull request
See CHANGELOG.md for a detailed history of changes, including version history, new features, bug fixes, and improvements.
[Specify your license here]