Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 9 additions & 48 deletions .github/workflows/master-guardrails.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: Master branch guardrails
name: Protected branch guardrails

# Runs on every PR that targets master. Blocks merges that would re-introduce
# Bedrock/JWT defaults, leak credentials, or remove the provider abstraction.
# Runs on every PR that targets master/main/release. Blocks merges that would
# leak credentials or remove the provider abstraction.
#
# What it does NOT do: enforce required reviews / required checks. Those toggles
# live in GitHub Settings > Branches > Branch protection rules. See BRANCHING.md.

on:
pull_request:
branches: [master, main]
branches: [master, main, release]
Comment on lines 9 to +11

jobs:
forbidden-content-scan:
Expand Down Expand Up @@ -37,38 +37,16 @@ jobs:
exit 1
fi

- name: 2. Default providers must remain "emergent" in backend/.env.example
run: |
if [ -f backend/.env.example ]; then
if ! grep -qE '^LLM_PROVIDER=emergent\b' backend/.env.example; then
echo "::error::backend/.env.example must default LLM_PROVIDER=emergent on master."
exit 1
fi
if ! grep -qE '^AUTH_PROVIDER=emergent\b' backend/.env.example; then
echo "::error::backend/.env.example must default AUTH_PROVIDER=emergent on master."
exit 1
fi
fi

- name: 3. Provider abstraction files must exist
- name: 2. Provider abstraction files must exist
run: |
for f in backend/llm_provider.py backend/auth_provider.py; do
if [ ! -f "$f" ]; then
echo "::error::$f is missing. The provider abstraction must remain on master."
echo "::error::$f is missing. The provider abstraction must remain."
exit 1
fi
done

- name: 4. emergentintegrations must remain a dependency
run: |
if [ -f backend/requirements.txt ]; then
if ! grep -qi '^emergentintegrations' backend/requirements.txt; then
echo "::error::backend/requirements.txt no longer pins emergentintegrations. master must keep it."
exit 1
fi
fi

- name: 5. No hardcoded AWS / JWT secrets
- name: 3. No hardcoded AWS / JWT secrets
run: |
# Scan only the diff (not the whole repo) so existing acceptable strings don't trip it
if grep -nE 'AKIA[0-9A-Z]{16}' /tmp/patch.diff; then
Expand All @@ -87,27 +65,10 @@ jobs:
exit 1
fi

- name: 6. backend/.env (the live one, not example) must default to emergent if committed
# If .env somehow ends up in the diff (e.g., gitignore was relaxed), enforce defaults.
- name: 4. backend/.env must never be committed
# If .env somehow ends up in the diff (e.g., gitignore was relaxed), block it.
run: |
if [ -f backend/.env ] && grep -qE '^backend/\.env$' /tmp/changed.txt; then
echo "::error::backend/.env should never be committed. .gitignore is the line of defense."
exit 1
fi

branch-name-check:
name: Branch-name & PR-title check for local_setup cherry-picks
runs-on: ubuntu-latest
steps:
- name: Verify PRs from local_setup are explicitly labelled
env:
HEAD: ${{ github.head_ref }}
TITLE: ${{ github.event.pull_request.title }}
run: |
if [ "$HEAD" = "local_setup" ] || [ "$HEAD" = "local-setup" ]; then
if ! echo "$TITLE" | grep -qE '\[from local_setup\]|\[ALLOW-LOCAL-SETUP\]'; then
echo "::error::This PR is from $HEAD. To prevent accidental merges of self-hosted defaults, prefix the PR title with [from local_setup] (and re-read the Provider hygiene checklist)."
exit 1
fi
echo "PR is explicitly labelled; proceeding. Reviewer must still complete the Provider hygiene checklist."
fi
150 changes: 67 additions & 83 deletions BRANCHING.md
Original file line number Diff line number Diff line change
@@ -1,125 +1,109 @@
# Branching & merge policy
# Branching & Merge Policy

This repo runs in **two deployment modes** from a single codebase. The provider
abstraction in `backend/llm_provider.py` and `backend/auth_provider.py` lets
the SAME source code switch between modes via env vars only.
This repo keeps provider logic behind `backend/llm_provider.py` and
`backend/auth_provider.py` so auth and LLM behavior can be configured without
rewriting product code.

| Branch | LLM_PROVIDER | AUTH_PROVIDER | Where it runs |
| ------------- | ------------ | ------------- | ---------------------------- |
| `master` | `emergent` | `emergent` | emergent.sh preview + deploy |
| `local_setup` | `bedrock` | `jwt` | your local / self-hosted |
| Branch | LLM_PROVIDER | AUTH_PROVIDER | Where it runs |
| --------- | ------------ | ------------- | -------------------------- |
| `master` | `bedrock` | `jwt` | primary development branch |
| `release` | n/a | n/a | PR-only release branch |

> **Source of truth: `master`.** Features land here first.
> **`release` is PR-only.** Nobody should push directly or force-update it, but
> release PRs can be reviewed and merged.

---

## Day-to-day workflow
## Day-to-day Workflow

### 1. New feature on Emergent.sh
```
work in emergent.sh chat → "Save to GitHub" → master
```
Emergent pushes to `master`. Done.

### 2. Sync local with the latest master
```
git checkout local_setup
git fetch origin
git merge origin/master
# resolve conflicts ONLY in expected files (server.py rare; .env never)
git push
```
### 1. New work

### 3. Local-only changes (deployment configs, infra)
```
git checkout local_setup
git checkout master
git pull
git checkout -b feat/my-change
# make changes
git commit -m "infra: bump fly.toml memory"
git push
git push -u origin feat/my-change
# open PR feat/my-change -> master
```
**Never** PR these back to master.

### 4. Polish/refactor done locally that SHOULD reach master
### 2. Release work

```
git checkout master
git pull
git checkout -b feat/my-polish
git cherry-pick <commit-sha-from-local_setup>
# verify nothing provider-specific snuck in (see checklist below)
git push -u origin feat/my-polish
# open PR feat/my-polish → master
git checkout -b release/v0.1.0 master
git push -u origin release/v0.1.0
# open PR release/v0.1.0 -> release
```
**Do not** PR `local_setup → master` directly. Always cherry-pick into a
feature branch first.

---

## What MUST stay on `master`

These are enforced automatically by `.github/workflows/master-guardrails.yml`:

1. `backend/.env.example` defaults to `LLM_PROVIDER=emergent` and `AUTH_PROVIDER=emergent`
2. `backend/llm_provider.py` and `backend/auth_provider.py` exist
3. `emergentintegrations` stays in `backend/requirements.txt`
4. No `.env` file is committed
5. No real AWS keys / JWT secrets in the diff
1. `backend/llm_provider.py` and `backend/auth_provider.py` exist
2. No `.env` file is committed
3. No real AWS keys / JWT secrets in the diff

The PR template's checklist asks reviewers to verify the same.

---

## What MUST stay on `local_setup`
## Required GitHub Branch Protection

(no automation — these are human discipline)
Go to **Settings -> Branches -> Add branch protection rule**:

1. `backend/.env`: `LLM_PROVIDER=bedrock`, `AUTH_PROVIDER=jwt`, plus AWS + JWT secrets
2. Any deployment configs your stack needs (Dockerfile tweaks, `fly.toml`,
`nginx.conf`, k8s manifests, etc.) — keep them in a `deploy/` folder so they
are easy to keep separate during cherry-picks
3. Optional `requirements.local.txt` if you ever need self-hosted-only Python
deps that should NOT ship to master
- **Branch name pattern**: `master`
- Require a pull request before merging
- Require approvals if you want review gates
- Require review from Code Owners if `.github/CODEOWNERS` is configured
- Require status checks to pass before merging
- Add: `Scan for forbidden content`
- Require branches to be up to date before merging
- Do not allow bypassing the above settings
- Restrict pushes that create matching branches if you want to block direct pushes

> The workflow must run at least once on a PR before its jobs appear in the
> "Status checks" picker. Open a small PR to surface them.

---

## Required GitHub branch protection (one-time setup in GitHub UI)
## Required GitHub Release Branch Protection

Go to **Settings → Branches → Add branch protection rule**:
To allow PRs into `release` while blocking direct pushes, use a GitHub ruleset
or branch protection rule that requires pull requests.

- **Branch name pattern**: `master`
- ✅ Require a pull request before merging
- ✅ Require approvals: at least 1
- ✅ **Require review from Code Owners** ← enables `.github/CODEOWNERS`
- ✅ Require status checks to pass before merging
- Add: `Scan for forbidden content`
- Add: `Branch-name & PR-title check for local_setup cherry-picks`
- ✅ Require branches to be up to date before merging
- ✅ Do not allow bypassing the above settings
- ✅ Restrict pushes that create matching branches (optional — locks down direct pushes)
Go to **Settings -> Rules -> Rulesets -> New ruleset -> New branch ruleset**:

> The `master-guardrails` workflow MUST run at least once on a PR before its
> jobs appear in the "Status checks" picker. Open a no-op PR (e.g., editing
> this file) to surface them.
- **Ruleset name**: `Protect release branch`
- **Enforcement status**: `Active`
- **Target branches**: include by pattern, `release`
- Enable:
- **Require a pull request before merging**
- **Require status checks to pass**
- **Restrict deletions**
- **Block force pushes**
- **Require linear history** if available
- Do not enable **Restrict updates** if you want normal PR merges to work.
- If you are solo, keep required approvals at `0` or add yourself as an allowed
bypass actor only if GitHub requires an escape hatch.

> **Before any of this kicks in, edit `.github/CODEOWNERS`** and replace
> `@YOUR-GITHUB-USERNAME` with your real GitHub handle (or a team handle like
> `@your-org/maintainers`). Without that, the `Require review from Code Owners`
> rule has no one to assign.
Also keep `.github/workflows/master-guardrails.yml` required for PRs so release
PRs get the same secret/provider checks as `master`.

---

## Troubleshooting

**Q: `git merge origin/master` produced a conflict in `backend/.env`.**
A: That should never happen — `.env` is gitignored. Double-check your local
checkout doesn't have `backend/.env` tracked (`git rm --cached backend/.env`).

**Q: I accidentally pushed Bedrock defaults to master.**
A: The `master-guardrails` action will block the PR. If somehow it merged,
revert with `git revert <merge-sha>` and force-restore the `emergent`
defaults in `backend/.env.example`.

**Q: I want to test JWT mode against the live preview without breaking
master.**
A: Don't. Test it on `local_setup` or in a temporary branch. The Emergent
preview env always boots from master's `.env`, which must stay on emergent
defaults.
A: That should never happen because `.env` is gitignored. Double-check your local
checkout does not have `backend/.env` tracked:

```
git rm --cached backend/.env
```

**Q: The guardrail workflow blocked a PR.**
A: Fix the flagged file in your feature branch, push again, and let the PR checks
rerun.
Loading