Skip to content
Closed
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
41 changes: 27 additions & 14 deletions docs/08-ci-cd-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ The jobs differ per repo. What each one actually runs:
| Lint, Test | Every push and every PR |
| `publish` | **Only** when the ref is a tag matching `v*` — `if: startsWith(github.ref, 'refs/tags/v')` |

> **A merge to `main` does not produce an image.** It runs lint and tests only.
> The image on `ghcr.io` changes when, and only when, someone pushes a `v*` tag.
> This is the single most common source of confusion: `main` can be many commits
> ahead of the newest published image, and that is by design.
> **No merge produces an image — not into `develop`, not into `main`.** A merge
> runs lint and tests only. The image on `ghcr.io` changes when, and only when,
> someone pushes a `v*` tag. This is the single most common source of confusion:
> both branches can sit many commits ahead of the newest published image, and
> that is by design.

### Known gaps

Expand Down Expand Up @@ -156,27 +157,39 @@ docker build -t ghcr.io/forail-platform/forail-frontend:2026.05.0 .

## Release Process

1. Ensure GitHub Actions is green on `main` for every repo being released
Work is integrated on `develop`; `main` holds released code only, and the tag
that publishes an image is always cut **on `main`**. See the git-flow section in
[10 — Contributing Guide](10-contributing-guide.md).

1. Ensure GitHub Actions is green on `develop` for every repo being released
2. Bump `VERSION` in the repos that ship a version, and the Helm chart's
`version` / `appVersion` so it pins the images you are about to publish
3. Write the release notes (`forail-deploy/docs/RELEASE_NOTES_v<version>.md`) and
the docs-site release page
4. Tag each changed repo and push the tag — this is what builds and publishes:
`git tag -a v2026.05.0 -m "Forail 2026.05.0" && git push github v2026.05.0`
5. Install the published chart and images into a clean cluster and run the full
4. Validate a release candidate first — see below
5. Merge `develop` into `main` (`git merge --no-ff develop`) and push it
6. Tag each changed repo **on `main`** and push the tag — this is what builds
and publishes: `git tag -a v2026.05.0 -m "Forail 2026.05.0" && git push github v2026.05.0`
7. Install the published chart and images into a clean cluster and run the full
regression before announcing anything
6. Create the GitHub Release
8. Push `main` and the tags to `origin` as well, so the GitLab mirror keeps up
9. Create the GitHub Release

### Validate before you release

Cut an rc tag first (`v2026.05.0-rc1`). It publishes a real image through the
same job, so the release candidate can be installed into a clean cluster and put
through the full Cypress suite. Only then cut the real tag. Rc images stay on
`ghcr.io` — harmless, but do not point a chart at one.
Cut an rc tag first (`v2026.05.0-rc1`) from the commit you intend to release. It
publishes a real image through the same `publish` job, so the candidate can be
installed into a clean cluster and put through the full Cypress suite. Only then
merge to `main` and cut the real tag.

An rc tag is the one case where tagging off `develop` is fine — it is a
throwaway build, not a release. Rc images stay on `ghcr.io`; harmless, but never
point a chart at one.

### Watch out

- **Never release without passing tests.**
- **Tag format must have `v` prefix:** `v2026.05.0`, not `2026.05.0`.
- **Merging to `main` publishes nothing** — only a `v*` tag does.
- **Merging publishes nothing** — into `develop` or `main`, only a `v*` tag does.
- **Tag on `main`**, not on `develop` — the sole exception is a throwaway rc tag.
- **Image visibility** — when a new package is first pushed to `ghcr.io`, GitHub creates it as **private** by default. You must manually flip it to public via the Packages settings (`https://github.com/orgs/forail-platform/packages`).
78 changes: 67 additions & 11 deletions docs/10-contributing-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,33 @@ test/inventory-api-tests # Tests
chore/update-dependencies # Maintenance
```

### The two long-lived branches

Every Forail repo has exactly two permanent branches:

| Branch | What it is |
| --------- | -------------------------------------------------------------------------- |
| `develop` | **The integration branch.** All day-to-day work lands here. |
| `main` | **Released code only.** It changes when a release is cut, and at no other time. |

Everything else is temporary and gets deleted once it is merged.

```
feature/x ──┐
fix/y ──────┼──► develop ──(release)──► main ──► tag v2026.08.0 ──► CI publishes images
chore/z ────┘ ▲
└── hotfix/critical-thing (from main, back into develop too)
```

### Standard flow

**`main` is the integration branch.** There is no `devel` branch in any Forail
repo — branch from `main` and target `main` in the PR.
**Branch from `develop`, and target `develop` in the PR.** Do not open PRs
against `main` — the only thing that merges into `main` is a release.

```bash
# 1. Create branch from main
git checkout main
git pull github main
# 1. Create branch from develop
git checkout develop
git pull github develop
git checkout -b feature/my-feature

# 2. Make changes, test, commit
Expand All @@ -65,17 +83,55 @@ vagrant ssh -c "cd /awx_devel && forail-test"
git add forail/main/models/my_model.py
git commit -m "feat(models): add Policy model for governance"

# 3. Push and create PR
# 3. Push and create PR against develop
git push github feature/my-feature
gh pr create --base main
gh pr create --base develop
```

### Updating the branch

```bash
git checkout main && git pull github main
git checkout develop && git pull github develop
git checkout feature/my-feature
git rebase main
git rebase develop
```

### Cutting a release

```bash
# develop is green and validated (see the rc procedure in 08-ci-cd-pipeline.md)
git checkout main && git pull github main
git merge --no-ff develop
git push github main

# The tag is what publishes the images -- always tag on main, never on develop
git tag -a v2026.08.0 -m "Forail 2026.08.0"
git push github v2026.08.0
git push origin main v2026.08.0 # keep the GitLab mirror level
```

### Hotfixes

A fix that cannot wait for the next release branches from `main`, merges back
into `main`, and **must also be merged into `develop`** — otherwise the next
release quietly reverts it.

```bash
git checkout -b hotfix/token-leak main
# ... fix, test, PR into main, tag ...
git checkout develop && git merge --no-ff hotfix/token-leak
```

### Deleting merged branches

Delete a feature branch as soon as its PR is merged, locally and on both
remotes. A repo should normally show only `main`, `develop`, and whatever is
actively in flight.

```bash
git branch -d feature/my-feature
git push github --delete feature/my-feature
git push origin --delete feature/my-feature
```

### Remotes
Expand Down Expand Up @@ -153,15 +209,15 @@ cd forail/ui_next && npx tsc --noEmit
- [ ] All tests pass
- [ ] No lint errors
- [ ] Commit messages follow conventions
- [ ] Branch is rebased on latest `main`
- [ ] Branch is rebased on latest `develop`
- [ ] Changes are minimal and focused

### PR guidelines

- **Keep PRs small** — ideally under 500 lines. Large PRs are harder to review.
- **One concern per PR** — don't mix a bug fix with refactoring.
- **Include tests** — new features need tests, bug fixes need a regression test.
- **Target `main`** — all PRs merge into `main`.
- **Target `develop`** — all PRs merge into `develop`. `main` receives releases only.

### Review Checklist

Expand Down
Loading