feat(ci): implemented automatic versioning plus releases

This commit is contained in:
stan
2025-10-08 21:17:44 +01:00
parent 583b58e5c2
commit 9d9fe84eef
10 changed files with 6444 additions and 34 deletions

View File

@@ -6,12 +6,15 @@ on:
types:
- completed
branches: ["main", "master"]
push:
tags:
- 'v*.*.*'
jobs:
build-and-push:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'push' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -34,6 +37,9 @@ jobs:
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image

44
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Release
on:
workflow_run:
workflows: ["Run Tests"]
types:
- completed
branches: ["main", "master"]
permissions:
contents: write
issues: write
pull-requests: write
jobs:
release:
name: Semantic Release
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Verify build
run: npm run build
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release

73
.releaserc.json Normal file
View File

@@ -0,0 +1,73 @@
{
"branches": ["master", "main"],
"repositoryUrl": "https://github.com/stan-smith/FossFLOW.git",
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
{ "type": "revert", "release": "patch" },
{ "type": "docs", "release": false },
{ "type": "style", "release": false },
{ "type": "chore", "release": false },
{ "type": "refactor", "release": "patch" },
{ "type": "test", "release": false },
{ "type": "build", "release": false },
{ "type": "ci", "release": false },
{ "breaking": true, "release": "major" }
]
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits",
"presetConfig": {
"types": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "perf", "section": "Performance" },
{ "type": "revert", "section": "Reverts" },
{ "type": "docs", "section": "Documentation", "hidden": false },
{ "type": "style", "section": "Styles", "hidden": true },
{ "type": "chore", "section": "Chores", "hidden": true },
{ "type": "refactor", "section": "Code Refactoring" },
{ "type": "test", "section": "Tests", "hidden": true },
{ "type": "build", "section": "Build System", "hidden": true },
{ "type": "ci", "section": "CI/CD", "hidden": true }
]
}
}
],
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md",
"changelogTitle": "# Changelog\n\nAll notable changes to FossFLOW will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)."
}
],
[
"@semantic-release/exec",
{
"prepareCmd": "npm run update-version ${nextRelease.version}"
}
],
"@semantic-release/github",
[
"@semantic-release/git",
{
"assets": [
"CHANGELOG.md",
"package.json",
"package-lock.json",
"packages/*/package.json"
],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}

View File

@@ -217,22 +217,76 @@ npm run dev
### 4. Commit Your Changes
We follow conventional commits:
**IMPORTANT**: We use [Conventional Commits](https://www.conventionalcommits.org/) with automated semantic versioning. Your commit messages directly control version bumps and changelog generation.
#### Commit Format
```
<type>(<scope>): <subject>
[optional body]
[optional footer]
```
#### Examples
```bash
git commit -m "feat: add undo/redo functionality"
git commit -m "fix: prevent menu from opening during drag"
git commit -m "docs: update installation instructions"
git commit -m "feat(connector)!: change default connector mode to click"
```
Commit types:
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code changes that neither fix bugs nor add features
- `test`: Adding or updating tests
- `chore`: Maintenance tasks
#### Commit Types
**Version-bumping commits:**
- `feat`: New feature (triggers MINOR version bump, e.g., 1.0.0 → 1.1.0)
- `fix`: Bug fix (triggers PATCH version bump, e.g., 1.0.0 → 1.0.1)
- `perf`: Performance improvement (triggers PATCH version bump)
- `refactor`: Code refactoring (triggers PATCH version bump)
**Non-version-bumping commits:**
- `docs`: Documentation only changes (no version bump)
- `style`: Code style changes - formatting, whitespace (no version bump)
- `test`: Adding or updating tests (no version bump)
- `chore`: Maintenance tasks, dependency updates (no version bump)
- `build`: Build system changes (no version bump)
- `ci`: CI/CD configuration changes (no version bump)
**Breaking changes:**
- Add `!` after type/scope OR add `BREAKING CHANGE:` in footer
- Triggers MAJOR version bump (e.g., 1.0.0 → 2.0.0)
- Example: `feat!: redesign node selection API`
#### Scopes (optional but recommended)
Common scopes in FossFLOW:
- `connector`: Connector-related changes
- `ui`: UI components and interactions
- `storage`: Storage and persistence
- `export`: Export/import functionality
- `docker`: Docker and deployment
- `i18n`: Internationalization
#### Breaking Change Examples
```bash
# Option 1: Using ! in type
git commit -m "feat(api)!: remove deprecated exportImage function"
# Option 2: Using footer
git commit -m "feat: update node API
BREAKING CHANGE: Node.position is now an object with x,y properties instead of array"
```
#### Release Notes
Your commits will automatically generate:
- Version number based on commit types
- Changelog with categorized changes
- GitHub release notes
## Coding Standards

194
docs/SEMANTIC_RELEASE.md Normal file
View File

@@ -0,0 +1,194 @@
# Semantic Release Setup
This document explains how FossFLOW uses automated semantic versioning and releases.
## Overview
FossFLOW uses [semantic-release](https://github.com/semantic-release/semantic-release) to automate:
- Version number calculation based on commit messages
- CHANGELOG.md generation
- GitHub release creation
- Git tag creation
- Docker image tagging with version numbers
## How It Works
### 1. Commit Messages Drive Versioning
When you commit code using conventional commits, the commit type determines the version bump:
| Commit Type | Version Bump | Example |
|-------------|--------------|---------|
| `feat:` | Minor (1.0.0 → 1.1.0) | New features |
| `fix:` | Patch (1.0.0 → 1.0.1) | Bug fixes |
| `perf:` | Patch (1.0.0 → 1.0.1) | Performance improvements |
| `refactor:` | Patch (1.0.0 → 1.0.1) | Code refactoring |
| `feat!:` or `BREAKING CHANGE:` | Major (1.0.0 → 2.0.0) | Breaking changes |
| `docs:`, `style:`, `test:`, `chore:` | No bump | Non-code changes |
### 2. Automated Workflow
When you push to `master` branch:
1. **Tests run** (via `.github/workflows/test.yml`)
2. **If tests pass**, semantic-release workflow triggers (`.github/workflows/release.yml`)
3. **Semantic-release analyzes** commits since last release
4. **If version bump needed**:
- Calculates new version number
- Updates `package.json` files in all workspace packages
- Generates CHANGELOG.md
- Creates git tag (e.g., `v1.2.0`)
- Commits changes with `[skip ci]`
- Pushes tag to GitHub
- Creates GitHub release with notes
5. **Docker workflow triggers** on new tag (`.github/workflows/docker.yml`)
6. **Docker images are tagged** with:
- `latest`
- `1.2.0` (full version)
- `1.2` (major.minor)
- `1` (major only)
### 3. Multiple Package Versioning
FossFLOW is a monorepo with multiple packages. All packages are versioned together:
- Root `package.json`
- `packages/fossflow-lib/package.json`
- `packages/fossflow-app/package.json`
- `packages/fossflow-backend/package.json`
The `scripts/update-version.js` script syncs version numbers across all packages.
## Configuration Files
### `.releaserc.json`
Main semantic-release configuration:
- Defines which branches trigger releases (`master`, `main`)
- Configures commit analysis rules
- Sets up changelog generation
- Defines which files to commit
### `.github/workflows/release.yml`
GitHub Actions workflow that:
- Runs after tests pass
- Executes semantic-release
- Uses `GITHUB_TOKEN` for GitHub API access
- Uses `NPM_TOKEN` for npm publishing (optional)
### `scripts/update-version.js`
Node.js script that updates version numbers in all package.json files simultaneously.
## Example Release Flow
### Scenario: Adding a New Feature
```bash
# Make your changes
git add .
git commit -m "feat(connector): add multi-point connector routing"
git push origin master
```
**Result:**
- Tests run and pass
- Semantic-release detects `feat:` commit
- Version bumps from 1.0.5 → 1.1.0
- CHANGELOG.md updated with new entry
- Git tag `v1.1.0` created
- GitHub release created
- Docker images tagged: `1.1.0`, `1.1`, `1`, `latest`
### Scenario: Fixing a Bug
```bash
git commit -m "fix(export): resolve image export quality issue"
git push origin master
```
**Result:**
- Version bumps from 1.1.0 → 1.1.1
- Patch release created
### Scenario: Breaking Change
```bash
git commit -m "feat(api)!: redesign node creation API
BREAKING CHANGE: createNode() now requires nodeType parameter"
git push origin master
```
**Result:**
- Version bumps from 1.1.1 → 2.0.0
- Major release created with breaking change highlighted
### Scenario: Documentation Update
```bash
git commit -m "docs: update installation instructions"
git push origin master
```
**Result:**
- No version bump
- No release created
- Changes still merged to master
## Manual Testing Locally
You can test semantic-release locally without publishing:
```bash
# Dry run (no changes made)
npx semantic-release --dry-run
# See what version would be released
npx semantic-release --dry-run --no-ci
```
## Troubleshooting
### No Release Created
Check if:
- Commits follow conventional commit format
- Commits include version-bumping types (`feat`, `fix`, etc.)
- Tests passed successfully
- You're on the `master` or `main` branch
### Version Not Updated
Ensure:
- `scripts/update-version.js` has execute permissions
- Script is referenced in `.releaserc.json` under `@semantic-release/exec`
### Docker Not Tagged
Verify:
- Git tag was created successfully
- Docker workflow has permission to run
## Additional Resources
- [Conventional Commits](https://www.conventionalcommits.org/)
- [Semantic Versioning](https://semver.org/)
- [Semantic Release Documentation](https://semantic-release.gitbook.io/semantic-release/)
- [Keep a Changelog](https://keepachangelog.com/)
## Maintaining This System
### Updating Semantic Release
```bash
npm update semantic-release @semantic-release/changelog @semantic-release/git @semantic-release/exec
```
### Adding New Commit Types
Edit `.releaserc.json` under `releaseRules` to add custom commit type behaviors.
### Changing Release Branch
Edit `.releaserc.json` and `.github/workflows/release.yml` to target different branches.

6021
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "fossflow-monorepo",
"version": "1.0.0",
"version": "1.0.5",
"private": true,
"description": "Monorepo for FossFLOW diagram editor and library",
"workspaces": [
@@ -19,13 +19,20 @@
"clean": "npm run clean --workspaces --if-present && rm -rf node_modules",
"publish:lib": "npm run build:lib && npm publish --workspace=packages/fossflow-lib",
"docker:build": "docker build -t fossflow:local .",
"docker:run": "docker compose -f compose.dev.yml up"
"docker:run": "docker compose -f compose.dev.yml up",
"update-version": "node scripts/update-version.js",
"semantic-release": "semantic-release"
},
"devDependencies": {
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1",
"@types/node": "^18.19.0",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"conventional-changelog-conventionalcommits": "^9.1.0",
"cross-env": "^10.0.0",
"semantic-release": "^24.2.9",
"typescript": "^5.3.3"
},
"engines": {

View File

@@ -1,6 +1,6 @@
{
"name": "fossflow-app",
"version": "1.0.0",
"version": "1.0.5",
"private": true,
"description": "Progressive Web App for creating isometric diagrams",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "fossflow-backend",
"version": "1.0.0",
"version": "1.0.5",
"description": "Optional backend server for FossFLOW persistent storage",
"main": "server.js",
"type": "module",
@@ -17,4 +17,4 @@
"devDependencies": {
"nodemon": "^3.0.1"
}
}
}

47
scripts/update-version.js Executable file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/env node
/**
* Updates version numbers across all packages in the monorepo
* Used by semantic-release to sync versions
*/
const fs = require('fs');
const path = require('path');
const version = process.argv[2];
if (!version) {
console.error('Error: Version number required');
process.exit(1);
}
console.log(`Updating all packages to version ${version}...`);
// List of package.json files to update
const packageFiles = [
'package.json',
'packages/fossflow-lib/package.json',
'packages/fossflow-app/package.json',
'packages/fossflow-backend/package.json'
];
packageFiles.forEach(file => {
const filePath = path.join(process.cwd(), file);
if (!fs.existsSync(filePath)) {
console.warn(`Warning: ${file} not found, skipping...`);
return;
}
try {
const packageJson = JSON.parse(fs.readFileSync(filePath, 'utf8'));
packageJson.version = version;
fs.writeFileSync(filePath, JSON.stringify(packageJson, null, 2) + '\n');
console.log(`Updated ${file} to ${version}`);
} catch (error) {
console.error(`Error updating ${file}:`, error.message);
process.exit(1);
}
});
console.log('Version update complete!');