Improve Deployment Versioning and Ref Selection
- Dominant language
- Python
- Stars
- 9
- Forks
- 31
- Avg merge
- 3d 19h
- Merged PRs (30d)
- 16
Description
## Context
Our current deployment process is handled by two separate GitHub Actions workflows:
1. **Build and Push workflow** — builds the Docker images and pushes them to the container registry.
2. **Deploy workflow** — connects to the target server over SSH, pulls the Docker images, and updates the running services using Docker Compose.
The Build and Push workflow currently runs automatically when changes are pushed to `main`, while deployments use images tagged as `latest`.
Although this works for the standard deployment flow, it introduces some limitations around deploying hotfixes, identifying deployed versions, and rolling back to previous versions.
This issue proposes improving the existing process while keeping **build and deployment as separate workflows**.
## Current Problems
### Builds are coupled to `main`
Currently, Docker images are automatically built from `main`.
This works well for the regular development flow, but it becomes a limitation when we need to deploy something that has not yet been merged into `main`, such as a hotfix or a specific commit.
There is currently no straightforward way to explicitly select a branch, tag, or commit and produce a deployable image from it.
### Deployments rely on `latest`
The deployment workflow currently pulls images using the `latest` tag.
This makes it difficult to determine exactly which Git commit is running in an environment.
It also makes redeploying or rolling back to a previously known-good version unnecessarily difficult.
## Proposed Changes
### Keep Build and Deploy as separate workflows
The existing separation between building images and deploying them should be preserved.
The responsibilities should remain:
```text
Build Workflow
↓
Produces an immutable Docker image
↓
Container Registry
↓
Deploy Workflow
↓
Deploys an existing image to an environment
```
The deployment workflow should never rebuild the application. It should only deploy an image that already exists in the container registry.
This gives us a **build once, deploy many** model, where the same artifact can be deployed to staging, production, or redeployed later without rebuilding it.
### Tag Docker images using the commit SHA
Docker images should be published using the Git commit SHA as an immutable tag.
For example:
```text
registry.example.com/backend:a84bd12
registry.example.com/proxy:a84bd12
```
This creates a direct relationship between the source code and the deployed artifact:
```text
Git commit
↓
Docker image
↓
Deployment
```
The `latest` tag may optionally continue to exist for convenience, but deployment workflows should not depend on it.
### Keep automatic builds for `main`
The Build and Push workflow should continue running automatically whenever changes are pushed to `main`.
Each commit reaching `main` should therefore automatically produce a deployable Docker image identified by its commit SHA.
```text
push to main
↓
commit a84bd12
↓
automatic build
↓
image:a84bd12
↓
available for deployment
```
This preserves the convenience of the current workflow and ensures that commits reaching `main` automatically have a corresponding deployable artifact.
### Allow manual builds from an explicit Git ref
In addition to automatic builds from `main`, the Build and Push workflow should support manual execution using `workflow_dispatch`.
The workflow should accept a Git ref as input, allowing us to build:
* a branch;
* a tag;
* or a specific commit SHA.
For example:
```text
Build and Push
ref: hotfix/fix-something
↓
resolve commit SHA
↓
build images
↓
push image:a84bd12
```
This provides an escape hatch for cases where something needs to be deployed without first merging it into `main`.
Regardless of whether the workflow was triggered automatically or manually, the resulting Docker image should always be tagged using the resolved commit SHA.
### Deploy an explicit image version
The Deploy workflow should receive the image version to deploy as an explicit input.
Conceptually:
```text
environment: production
image_tag: a84bd12
```
Instead of pulling:
```text
backend:latest
```
the deployment should pull:
```text
backend:a84bd12
```
Docker Compose should then start the services using that exact image version.
The Deploy workflow should not need to know which branch produced the image. Its responsibility is simply to deploy a specific, already-built artifact to a specific environment.
## Desired Flow
### Regular deployment
```text
PR
↓
merge into main
↓
automatic Build and Push
↓
image:a84bd12
↓
Deploy
environment = staging
image_tag = a84bd12
↓
Deploy
environment = production
image_tag = a84bd12
```
The exact same Docker image can therefore be validated in staging and later promoted to production.
### Hotfix deployment
For changes that cannot wait to be merged into `main`:
```text
hotfix branch
↓
manual Build and Push
ref = hotfix/my-fix
↓
resolve SHA
↓
image:f921abc
↓
manual Deploy
environment = production
image_tag = f921abc
```
The hotfix does not need to be merged into `main` before a deployable artifact can be produced.
### Rollback
Because previous images remain identifiable by their commit SHA, rolling back becomes a matter of redeploying a previously known-good image:
```text
Current production:
image:a84bd12
↓ issue detected
Deploy workflow:
environment = production
image_tag = 9fc271a
↓
Production:
image:9fc271a
```
No rebuild or reverts on `main` should be necessary.
## Expected Outcome
After this change, we should be able to:
* Continue automatically building Docker images for every push to `main`.
* Manually build Docker images from a branch, tag, or specific commit when necessary.
* Publish immutable Docker images identified by their Git commit SHA.
* Deploy a specific image version instead of relying on `latest`.
* Deploy hotfixes without requiring them to be merged into `main` first.
* Identify exactly which Git commit is running in each environment.
* Deploy the same artifact across different environments without rebuilding it.
* Redeploy or roll back to a previously built version.
## Scope
This issue should initially focus on:
* Adding commit SHA tags to Docker images.
* Keeping automatic builds for pushes to `main`.
* Adding manual Build and Push execution with an explicit Git ref.
* Updating the Deploy workflow to receive an explicit image tag/version.
* Updating Docker Compose/deployment scripts to use the selected image tag.
* Preserving the separation between Build and Deploy workflows.
Contributor guide
Research direction
Start by inspecting the existing Build and Push and Deploy GitHub Actions workflows, then trace how Docker Compose and deployment scripts currently use the latest tag. Done means automatic main builds still work, manual ref builds publish commit-SHA images, and Deploy accepts an explicit image version without rebuilding.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, docker-compose, git, github-actions
- Domain
- ci-cd, devops, infrastructure, release
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100