Problem
Currently, the CI/CD pipeline publishes Docker packages to GitHub Container Registry (GHCR) on every merge to master, resulting in:
- Dozens of unnecessary package versions (one per merge)
- Manual cleanup required to remove unwanted packages
- No clear distinction between development builds and official releases
- Lack of memorable release naming convention
Example current behavior:
Merge feat-1 → ghcr.io/.../webapi:latest, :sha-abc123
Merge feat-2 → ghcr.io/.../webapi:latest, :sha-def456
Merge feat-3 → ghcr.io/.../webapi:latest, :sha-789xyz
... (manual cleanup needed)
Proposed Solution
Implement a tag-based release workflow with stadium-themed naming convention (inspired by Ubuntu, Android, macOS naming patterns).
Key Changes:
-
Separate CI from CD:
- CI workflow (
dotnet-ci.yml) runs on every push/PR → build, test, coverage only
- CD workflow (
dotnet-cd.yml) runs ONLY when creating version tags → publish packages
-
Stadium Release Naming:
- Use famous football stadiums A-Z for release codenames
- Tag format:
v{SEMVER}-{STADIUM} (e.g., v1.0.0-azteca)
- Publish multiple Docker tags per release:
- Semantic version:
:1.0.0
- Stadium name:
:azteca
- Latest:
:latest
-
GitHub Releases:
- Auto-generate release notes with changelog
- Include stadium emoji 🏟️ and pull instructions
- Link to Docker images
Example workflow:
# Development (no packages published)
git checkout -b feat/add-jwt
git commit -m "feat: add JWT authentication"
# Merge PR → CI runs tests, NO package published
# Official release (packages published)
git tag -a v1.0.0-azteca -m "Release 1.0.0 - Azteca"
git push origin v1.0.0-azteca
# → Publishes: :1.0.0, :azteca, :latest
# → Creates GitHub Release with notes
Suggested Approach
1. Update CI Workflow (.github/workflows/dotnet-ci.yml)
Rename dotnet.yml to dotnet-ci.yml for clarity.
Remove the container job that publishes to GHCR on every merge:
# REMOVE THIS ENTIRE JOB:
container:
needs: coverage
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
# ... publishing steps ...
Keep only: build → test → coverage
2. Create CD Workflow (.github/workflows/dotnet-cd.yml)
Trigger: Only on version tags matching v*.*.*-* pattern
Steps:
- Extract version components from tag (semver, stadium name)
- Run build and tests in Release configuration
- Build Docker image with multiple tags
- Push to GHCR
- Generate changelog from commits
- Create GitHub Release with auto-generated notes
Key Features:
- Extract
v1.0.0-azteca → semver: 1.0.0, stadium: azteca
- Generate changelog: compare with previous tag
- Multi-platform support (currently linux/amd64)
- Cache layers for faster builds
3. Stadium List (A-Z) - World Cup Edition
Predefined list of 26 stadiums that hosted FIFA World Cup matches:
| Letter |
Stadium Name |
Location |
Tag Name |
| A |
Azteca |
Mexico (1970, 1986, 2026) |
azteca |
| B |
Bernabéu |
Spain (1982) |
bernabeu |
| C |
Centenario |
Uruguay (1930) |
centenario |
| D |
Düsseldorf (Merkur Spiel-Arena) |
Germany (2006) |
dusseldorf |
| E |
Ekaterinburg Arena |
Russia (2018) |
ekaterinburg |
| F |
Frankfurt Waldstadion |
Germany (1974, 2006) |
frankfurt |
| G |
Gelsenkirchen |
Germany (2006) |
gelsenkirchen |
| H |
Hard Rock Stadium |
USA (2026) |
hardrock |
| I |
Ibn Batouta Stadium |
Morocco (2030) |
ibnbatouta |
| J |
Johannesburg Soccer City |
South Africa (2010) |
johannesburg |
| K |
Kazan Arena |
Russia (2018) |
kazan |
| L |
Lusail |
Qatar (2022) |
lusail |
| M |
Maracanã |
Brazil (1950, 2014) |
maracana |
| N |
Nantes Beaujoire |
France (1998) |
nantes |
| O |
Olympiastadion Berlin |
Germany (1974, 2006) |
olympiastadion |
| P |
Parc des Princes |
France (1938, 1998) |
parcdesprince |
| Q |
Qatar 974 |
Qatar (2022) |
qatar974 |
| R |
Rose Bowl |
USA (1994) |
rosebowl |
| S |
San Siro |
Italy (1934, 1990) |
sansiro |
| T |
Toronto BMO Field |
Canada (2026) |
toronto |
| U |
Ullevi |
Sweden (1958) |
ullevi |
| V |
Volgograd Arena |
Russia (2018) |
volgograd |
| W |
Wembley |
England (1966) |
wembley |
| X |
Xiamen Egret Stadium |
(famous fallback) |
xiamen |
| Y |
Yokohama International Stadium |
Japan (2002) |
yokohama |
| Z |
Zentralstadion Leipzig |
Germany (1974, 2006) |
zentralstadion |
4. Documentation Updates
README.md - Add release instructions:
## 📦 Releases
This project uses stadium-themed release names:
### Create a Release
```bash
git tag -a v1.0.0-azteca -m "Release 1.0.0 - Azteca"
git push origin v1.0.0-azteca
Pull Docker Images
# By semantic version (recommended)
docker pull ghcr.io/nanotaboada/dotnet-samples-aspnetcore-webapi:1.0.0
# By stadium name
docker pull ghcr.io/nanotaboada/dotnet-samples-aspnetcore-webapi:azteca
# Latest
docker pull ghcr.io/nanotaboada/dotnet-samples-aspnetcore-webapi:latest
CHANGELOG.md - Template for releases:
# Changelog
All notable changes to this project will be documented in this file.
## [1.0.0 - Azteca] - 2026-01-21
### Added
- Initial stable release
- Player CRUD operations
- SQLite database with EF Core
- Docker support with Compose
- Comprehensive test coverage
### Changed
- N/A
### Fixed
- N/A
Acceptance Criteria
References
Naming Conventions
- Ubuntu: Warty Warthog, Jammy Jellyfish (adjective + animal)
- Android: Cupcake, Oreo, Tiramisu (desserts)
- macOS: Mavericks, Sonoma (California locations)
GitHub Actions
Examples
- Tag-based release workflow similar to:
Note: This is a breaking change to the CI/CD pipeline. After implementation:
- Delete old unnecessary packages from GHCR manually one last time
- Use tag-based releases going forward
- Master branch merges will only run tests, not publish packages
Problem
Currently, the CI/CD pipeline publishes Docker packages to GitHub Container Registry (GHCR) on every merge to master, resulting in:
Example current behavior:
Proposed Solution
Implement a tag-based release workflow with stadium-themed naming convention (inspired by Ubuntu, Android, macOS naming patterns).
Key Changes:
Separate CI from CD:
dotnet-ci.yml) runs on every push/PR → build, test, coverage onlydotnet-cd.yml) runs ONLY when creating version tags → publish packagesStadium Release Naming:
v{SEMVER}-{STADIUM}(e.g.,v1.0.0-azteca):1.0.0:azteca:latestGitHub Releases:
Example workflow:
Suggested Approach
1. Update CI Workflow (
.github/workflows/dotnet-ci.yml)Rename
dotnet.ymltodotnet-ci.ymlfor clarity.Remove the
containerjob that publishes to GHCR on every merge:Keep only: build → test → coverage
2. Create CD Workflow (
.github/workflows/dotnet-cd.yml)Trigger: Only on version tags matching
v*.*.*-*patternSteps:
Key Features:
v1.0.0-azteca→ semver:1.0.0, stadium:azteca3. Stadium List (A-Z) - World Cup Edition
Predefined list of 26 stadiums that hosted FIFA World Cup matches:
aztecabernabeucentenariodusseldorfekaterinburgfrankfurtgelsenkirchenhardrockibnbatoutajohannesburgkazanlusailmaracananantesolympiastadionparcdesprinceqatar974rosebowlsansirotorontoullevivolgogradwembleyxiamenyokohamazentralstadion4. Documentation Updates
README.md - Add release instructions:
Pull Docker Images
CHANGELOG.md - Template for releases:
Acceptance Criteria
dotnet.ymltodotnet-ci.ymldotnet-ci.yml) does not publish packages on merge to masterdotnet-cd.yml) created and functionalv1.0.0-aztecatriggers CD workflow:1.0.0,:azteca,:latestv1.0.0-azteca) successfully publishedReferences
Naming Conventions
GitHub Actions
Examples
Note: This is a breaking change to the CI/CD pipeline. After implementation: