SBF Innovation Lab Wiki
Deployment

Git workflow

Repositories, commits, branches, and pull requests

Make a new repository

Create the repository in the organisation before you write code, so the app lives there from day one.

GitHub website

  1. Open https://github.com/superbonfire-lab and click New repository.
  2. Set the owner to superbonfire-lab. Name the repository with the rules below.
  3. Set the visibility to Private.
  4. Optional: add a README and a .gitignore template for your language.
  5. Click Create repository, then clone it:
git clone https://github.com/superbonfire-lab/client-portal.git
cd client-portal

GitHub CLI

gh repo create superbonfire-lab/client-portal --private --clone
cd client-portal

Naming rules

  • Use kebab-case: client-portal, invoice-api, staff-dashboard.
  • Do not use spaces, capitals, underscores, or dots.
  • Use the same name for the repository, the Coolify resource, and the subdomain. Example: client-portal → client-portal.cp.superbonfire.tech.

Move a zip app into Git

If an app exists only as zip files, move it into Git once. After that, do not use zips to deploy.

unzip latest-version.zip -d my-app
cd my-app
git init
git branch -M main

# Ignore secrets and build output BEFORE the first commit
cat > .gitignore << 'GITIGNORE'
node_modules/
.env
.env.*
!.env.example
dist/
build/
.next/
__pycache__/
*.pyc
.venv/
.DS_Store
GITIGNORE

git add .
git commit -m "chore: initial import from zip v1.0"
gh repo create superbonfire-lab/my-app --private --source=. --push

PowerShell has no unzip or heredoc. Use these two commands instead, and run the other steps as shown:

Expand-Archive -Path latest-version.zip -DestinationPath my-app
@"
node_modules/
.env
.env.*
!.env.example
dist/
build/
.next/
__pycache__/
*.pyc
.venv/
.DS_Store
"@ | Set-Content -Encoding utf8 .gitignore

Check that no secret was committed:

git ls-files | grep -iE '\.env|secret|key|token|password' || echo "clean"

Commit messages

Use Conventional Commits: type: short description. Write in lowercase and in the imperative ("add", not "added"). Do not end with a full stop.

TypeUse forExample
featA new featurefeat: add invoice export to CSV
fixA bug fixfix: prevent login loop on expired token
docsDocumentation onlydocs: update deployment steps
refactorNo behaviour changerefactor: split billing service
testTeststest: cover empty cart case
choreMaintenance, dependencies, configchore: upgrade express to 4.19
ciPipeline or deploy configci: add lint step

Add a scope when it helps: feat(auth): add password reset. For a breaking change, add ! after the type (feat!: drop v1 API) and explain it in the commit body.

Branches and pull requests

Nobody commits straight to main. Every change goes through a branch and a pull request.

Branch names use the commit type and a kebab-case description: feat/add-invoice-export, fix/login-loop.

git checkout main
git pull origin main
git checkout -b feat/add-invoice-export
# make changes
git add -A
git commit -m "feat: add invoice export to CSV"
git push -u origin feat/add-invoice-export
gh pr create --fill --base main

A reviewer approves on GitHub. Merge with Squash and merge, then delete the branch.

  • Keep each pull request small: one feature or one fix.
  • Write the pull request title as a Conventional Commit. Squash merge uses it as the commit on main.
  • Get at least one review before you merge.
  • AI agents follow the same rules. The agent creates the branch, the commits, and the pull request. A person reviews and merges.

Day-to-day flow

1. git checkout -b feat/x          one branch per feature
2. commit                          feat:, fix:, docs:, ...
3. push and open a pull request    gh pr create --fill
4. review, then Squash and merge   someone else approves
5. Coolify redeploys               automatic with Auto Deploy
6. check the logs in Coolify       confirm the app is healthy

On this page