@logickernel/agileflow 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -211
- package/docs/README.md +39 -24
- package/docs/best-practices.md +201 -234
- package/docs/branching-strategy.md +153 -254
- package/docs/cli-reference.md +84 -64
- package/docs/configuration.md +154 -167
- package/docs/conventional-commits.md +207 -160
- package/docs/getting-started.md +162 -117
- package/docs/installation.md +244 -106
- package/docs/migration-guide.md +212 -299
- package/docs/release-management.md +195 -384
- package/docs/troubleshooting.md +276 -250
- package/docs/version-centric-cicd.md +239 -116
- package/package.json +3 -2
- package/src/utils.js +23 -6
|
@@ -1,166 +1,289 @@
|
|
|
1
|
-
# Version-Centric CI/CD
|
|
1
|
+
# Version-Centric CI/CD
|
|
2
2
|
|
|
3
|
-
AgileFlow
|
|
3
|
+
AgileFlow enables a **version-centric approach** to CI/CD where versioning is decoupled from build and deployment. This architecture simplifies pipelines and provides flexibility.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## The Decoupled Architecture
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
9
|
+
│ Merge to main │ │ Tag: v1.2.3 │ │ Build/Deploy │
|
|
10
|
+
│ │ ──────▶ │ │ ──────▶ │ │
|
|
11
|
+
│ AgileFlow │ │ (event) │ │ Your pipelines │
|
|
12
|
+
│ creates tag │ │ │ │ │
|
|
13
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### How It Works
|
|
17
|
+
|
|
18
|
+
1. **On merge to main**: AgileFlow analyzes commits and creates a version tag
|
|
19
|
+
2. **Tag creation event**: Triggers your build and deploy pipelines
|
|
20
|
+
3. **Build/Deploy**: Uses the tag as the version identifier
|
|
21
|
+
|
|
22
|
+
### Benefits
|
|
23
|
+
|
|
24
|
+
- **Separation of concerns** — Versioning is independent from build/deploy
|
|
25
|
+
- **Flexibility** — Any process can hook into tag creation
|
|
26
|
+
- **Simplicity** — Each pipeline has one responsibility
|
|
27
|
+
- **Reusability** — Same build pipeline for all versions
|
|
28
|
+
- **Auditability** — Clear version trail for every deployment
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Traditional vs. Version-Centric
|
|
33
|
+
|
|
34
|
+
### Traditional (Coupled)
|
|
9
35
|
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
36
|
+
```yaml
|
|
37
|
+
# Everything in one pipeline
|
|
38
|
+
on: push to main
|
|
39
|
+
→ calculate version
|
|
40
|
+
→ build
|
|
41
|
+
→ deploy staging
|
|
42
|
+
→ deploy production
|
|
16
43
|
```
|
|
17
44
|
|
|
18
|
-
**Problems
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
**Problems:**
|
|
46
|
+
- Complex, monolithic pipelines
|
|
47
|
+
- Version logic mixed with build logic
|
|
48
|
+
- Hard to rerun individual steps
|
|
49
|
+
|
|
50
|
+
### Version-Centric (Decoupled)
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
# Pipeline 1: Versioning
|
|
54
|
+
on: push to main
|
|
55
|
+
→ AgileFlow creates tag
|
|
56
|
+
|
|
57
|
+
# Pipeline 2: Release
|
|
58
|
+
on: tag created
|
|
59
|
+
→ build with tag version
|
|
60
|
+
→ deploy staging
|
|
61
|
+
→ deploy production
|
|
35
62
|
```
|
|
36
63
|
|
|
37
|
-
**Benefits
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
- **Consistent Testing**: All tests run against the same version that will be deployed
|
|
42
|
-
- **Clear Audit Trail**: Every deployment is tied to a specific, documented version
|
|
64
|
+
**Benefits:**
|
|
65
|
+
- Simple, focused pipelines
|
|
66
|
+
- Versioning completely separate
|
|
67
|
+
- Easy to rerun builds for any version
|
|
43
68
|
|
|
44
|
-
|
|
69
|
+
---
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
## Implementation
|
|
47
72
|
|
|
48
|
-
###
|
|
49
|
-
- **Purpose**: Generate semantic version and comprehensive release notes
|
|
50
|
-
- **Output**: `VERSION` variable available to all subsequent stages
|
|
51
|
-
- **Automation**: Uses AgileFlow tool to analyze commit history and determine next version
|
|
52
|
-
- **Artifacts**: Version tag pushed to repository, release notes generated
|
|
73
|
+
### GitHub Actions
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
**Versioning workflow** (`.github/workflows/version.yml`):
|
|
76
|
+
```yaml
|
|
77
|
+
name: Version
|
|
78
|
+
on:
|
|
79
|
+
push:
|
|
80
|
+
branches: [main]
|
|
59
81
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
-
|
|
82
|
+
jobs:
|
|
83
|
+
version:
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
with:
|
|
88
|
+
fetch-depth: 0
|
|
65
89
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
- **Benefits**: Identical behavior across all environments
|
|
70
|
-
- **Rollback**: Simple version-based rollback (e.g., "rollback to v1.2.2")
|
|
90
|
+
- uses: actions/setup-node@v4
|
|
91
|
+
with:
|
|
92
|
+
node-version: '20'
|
|
71
93
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
- name: Create version tag
|
|
95
|
+
env:
|
|
96
|
+
AGILEFLOW_TOKEN: ${{ secrets.AGILEFLOW_TOKEN }}
|
|
97
|
+
run: npx @logickernel/agileflow github
|
|
98
|
+
```
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
**Release workflow** (`.github/workflows/release.yml`):
|
|
101
|
+
```yaml
|
|
102
|
+
name: Release
|
|
103
|
+
on:
|
|
104
|
+
push:
|
|
105
|
+
tags:
|
|
106
|
+
- 'v*'
|
|
82
107
|
|
|
83
|
-
|
|
108
|
+
jobs:
|
|
109
|
+
build:
|
|
110
|
+
runs-on: ubuntu-latest
|
|
111
|
+
steps:
|
|
112
|
+
- uses: actions/checkout@v4
|
|
84
113
|
|
|
85
|
-
|
|
114
|
+
- name: Get version
|
|
115
|
+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
|
116
|
+
|
|
117
|
+
- name: Build
|
|
118
|
+
run: docker build -t myapp:$VERSION .
|
|
119
|
+
|
|
120
|
+
deploy-staging:
|
|
121
|
+
needs: build
|
|
122
|
+
runs-on: ubuntu-latest
|
|
123
|
+
environment: staging
|
|
124
|
+
steps:
|
|
125
|
+
- run: kubectl set image deployment/myapp myapp=myapp:$VERSION
|
|
126
|
+
|
|
127
|
+
deploy-production:
|
|
128
|
+
needs: build
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
environment: production
|
|
131
|
+
steps:
|
|
132
|
+
- run: kubectl set image deployment/myapp myapp=myapp:$VERSION
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### GitLab CI
|
|
86
136
|
|
|
87
137
|
```yaml
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
138
|
+
stages:
|
|
139
|
+
- version
|
|
140
|
+
- build
|
|
141
|
+
- deploy
|
|
91
142
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
stage:
|
|
143
|
+
# Versioning - runs on merge to main
|
|
144
|
+
agileflow:
|
|
145
|
+
stage: version
|
|
146
|
+
image: node:20-alpine
|
|
95
147
|
script:
|
|
96
|
-
-
|
|
97
|
-
|
|
148
|
+
- npx @logickernel/agileflow gitlab
|
|
149
|
+
rules:
|
|
150
|
+
- if: '$CI_COMMIT_BRANCH == "main"'
|
|
98
151
|
|
|
99
|
-
# Build
|
|
152
|
+
# Build - runs on tag creation
|
|
100
153
|
build:
|
|
101
154
|
stage: build
|
|
102
155
|
script:
|
|
103
|
-
- docker build -t myapp:$
|
|
104
|
-
- docker push myapp:$
|
|
105
|
-
|
|
106
|
-
-
|
|
107
|
-
|
|
108
|
-
# Deploy stage deploys the same version everywhere
|
|
109
|
-
deploy-testing:
|
|
110
|
-
stage: deploy
|
|
111
|
-
script:
|
|
112
|
-
- kubectl set image deployment/myapp myapp=myapp:${VERSION}
|
|
113
|
-
environment:
|
|
114
|
-
name: testing
|
|
115
|
-
needs:
|
|
116
|
-
- build
|
|
156
|
+
- docker build -t myapp:$CI_COMMIT_TAG .
|
|
157
|
+
- docker push myapp:$CI_COMMIT_TAG
|
|
158
|
+
rules:
|
|
159
|
+
- if: '$CI_COMMIT_TAG =~ /^v/'
|
|
117
160
|
|
|
161
|
+
# Deploy - runs on tag creation
|
|
118
162
|
deploy-staging:
|
|
119
163
|
stage: deploy
|
|
120
164
|
script:
|
|
121
|
-
- kubectl set image deployment/myapp myapp=myapp:$
|
|
165
|
+
- kubectl set image deployment/myapp myapp=myapp:$CI_COMMIT_TAG
|
|
122
166
|
environment:
|
|
123
167
|
name: staging
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- build
|
|
168
|
+
rules:
|
|
169
|
+
- if: '$CI_COMMIT_TAG =~ /^v/'
|
|
127
170
|
|
|
128
171
|
deploy-production:
|
|
129
172
|
stage: deploy
|
|
130
173
|
script:
|
|
131
|
-
- kubectl set image deployment/myapp myapp=myapp:$
|
|
174
|
+
- kubectl set image deployment/myapp myapp=myapp:$CI_COMMIT_TAG
|
|
132
175
|
environment:
|
|
133
176
|
name: production
|
|
134
177
|
when: manual
|
|
135
|
-
|
|
136
|
-
-
|
|
178
|
+
rules:
|
|
179
|
+
- if: '$CI_COMMIT_TAG =~ /^v/'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
184
|
+
## Version-Centric Deployments
|
|
185
|
+
|
|
186
|
+
### All Environments Use the Same Version
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
Tag v1.2.3
|
|
190
|
+
│
|
|
191
|
+
├──▶ Build: myapp:v1.2.3
|
|
192
|
+
│
|
|
193
|
+
├──▶ Staging: myapp:v1.2.3
|
|
194
|
+
│
|
|
195
|
+
└──▶ Production: myapp:v1.2.3
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
No environment drift — every environment runs identical code.
|
|
199
|
+
|
|
200
|
+
### Simple Rollbacks
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Rollback = deploy previous tag
|
|
204
|
+
kubectl set image deployment/myapp myapp=myapp:v1.2.2
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Clear Audit Trail
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# What version is running?
|
|
211
|
+
kubectl get deployment myapp -o jsonpath='{.spec.template.spec.containers[0].image}'
|
|
212
|
+
# myapp:v1.2.3
|
|
213
|
+
|
|
214
|
+
# What's in that version?
|
|
215
|
+
git show v1.2.3
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Advanced Patterns
|
|
221
|
+
|
|
222
|
+
### Conditional Deployments
|
|
223
|
+
|
|
224
|
+
Deploy only specific version types:
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
# Only deploy minor/major versions to production
|
|
228
|
+
deploy-production:
|
|
229
|
+
rules:
|
|
230
|
+
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.0$/'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Multiple Services
|
|
234
|
+
|
|
235
|
+
Same version for all services in a monorepo:
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
build-backend:
|
|
239
|
+
script:
|
|
240
|
+
- docker build -t backend:$CI_COMMIT_TAG ./backend
|
|
241
|
+
|
|
242
|
+
build-frontend:
|
|
243
|
+
script:
|
|
244
|
+
- docker build -t frontend:$CI_COMMIT_TAG ./frontend
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Notifications
|
|
248
|
+
|
|
249
|
+
Announce new versions:
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
notify:
|
|
141
253
|
script:
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
|
|
254
|
+
- |
|
|
255
|
+
curl -X POST "$SLACK_WEBHOOK" \
|
|
256
|
+
-d "{\"text\": \"Released $CI_COMMIT_TAG\"}"
|
|
257
|
+
rules:
|
|
258
|
+
- if: '$CI_COMMIT_TAG =~ /^v/'
|
|
145
259
|
```
|
|
146
260
|
|
|
261
|
+
---
|
|
262
|
+
|
|
147
263
|
## Key Advantages
|
|
148
264
|
|
|
149
|
-
1. **Eliminates
|
|
150
|
-
2. **Simplifies
|
|
151
|
-
3. **
|
|
152
|
-
4. **
|
|
153
|
-
5. **
|
|
154
|
-
|
|
265
|
+
1. **Eliminates environment drift** — All environments run identical versions
|
|
266
|
+
2. **Simplifies operations** — Work with versions, not branch states
|
|
267
|
+
3. **Enables easy rollbacks** — Just redeploy a previous tag
|
|
268
|
+
4. **Provides clear audit trail** — Every deployment tied to a version
|
|
269
|
+
5. **Decouples concerns** — Versioning separate from build/deploy
|
|
270
|
+
|
|
271
|
+
---
|
|
155
272
|
|
|
156
273
|
## Migration Path
|
|
157
274
|
|
|
158
|
-
If
|
|
275
|
+
If using a traditional coupled approach:
|
|
276
|
+
|
|
277
|
+
1. **Add AgileFlow** — Create versioning workflow
|
|
278
|
+
2. **Add tag-triggered workflow** — For build/deploy
|
|
279
|
+
3. **Test both workflows** — Verify tags trigger releases
|
|
280
|
+
4. **Remove old logic** — Clean up version calculation from build pipeline
|
|
281
|
+
|
|
282
|
+
---
|
|
159
283
|
|
|
160
|
-
|
|
161
|
-
2. **Gradually Simplify**: Remove environment-specific branches over time
|
|
162
|
-
3. **Update Deployments**: Modify deployment scripts to use `${VERSION}` variable
|
|
163
|
-
4. **Standardize Testing**: Run all tests against the versioned artifacts
|
|
164
|
-
5. **Document Changes**: Update runbooks to reference versions instead of branches
|
|
284
|
+
## Related Documentation
|
|
165
285
|
|
|
166
|
-
|
|
286
|
+
- [Getting Started](./getting-started.md) — Quick start
|
|
287
|
+
- [Installation Guide](./installation.md) — Setup instructions
|
|
288
|
+
- [Branching Strategy](./branching-strategy.md) — Git workflow
|
|
289
|
+
- [Best Practices](./best-practices.md) — Recommended patterns
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logickernel/agileflow",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Automatic semantic versioning and changelog generation based on conventional commits",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"README.md"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
18
|
+
"prepack": "chmod +x bin/agileflow"
|
|
18
19
|
},
|
|
19
20
|
"keywords": [
|
|
20
21
|
"semantic-versioning",
|
package/src/utils.js
CHANGED
|
@@ -52,14 +52,23 @@ function ensureGitRepo() {
|
|
|
52
52
|
/**
|
|
53
53
|
* Gets the current branch name.
|
|
54
54
|
* @returns {string} Current branch name
|
|
55
|
-
* @throws {Error} If in detached HEAD state
|
|
55
|
+
* @throws {Error} If in detached HEAD state and no CI environment variable is available
|
|
56
56
|
*/
|
|
57
57
|
function getCurrentBranch() {
|
|
58
58
|
const branch = runWithOutput('git branch --show-current').trim();
|
|
59
|
-
if (
|
|
60
|
-
|
|
59
|
+
if (branch) {
|
|
60
|
+
return branch;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
|
|
63
|
+
// Handle detached HEAD state (common in CI environments)
|
|
64
|
+
// GitLab CI provides CI_COMMIT_BRANCH (for branches) or CI_COMMIT_REF_NAME (for branches/tags)
|
|
65
|
+
// GitHub Actions provides GITHUB_REF_NAME (for branches/tags)
|
|
66
|
+
const ciBranch = process.env.CI_COMMIT_BRANCH || process.env.CI_COMMIT_REF_NAME || process.env.GITHUB_REF_NAME;
|
|
67
|
+
if (ciBranch) {
|
|
68
|
+
return ciBranch;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
throw new Error('Repository is in a detached HEAD state. Please check out a branch and try again.');
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
// Conventional commit type configuration
|
|
@@ -375,17 +384,25 @@ function calculateNextVersionAndChangelog(expandedInfo) {
|
|
|
375
384
|
* @returns {Array<{hash: string, datetime: string, author: string, message: string, tags: Array<string>}>}
|
|
376
385
|
*/
|
|
377
386
|
function getAllBranchCommits(branch) {
|
|
387
|
+
// Try to resolve the branch (may be a local branch or remote branch like origin/main)
|
|
388
|
+
let branchRef = branch;
|
|
378
389
|
try {
|
|
379
390
|
runWithOutput(`git rev-parse --verify ${branch}`);
|
|
380
391
|
} catch {
|
|
381
|
-
|
|
392
|
+
// Try with origin/ prefix (common in CI environments where local branch doesn't exist)
|
|
393
|
+
try {
|
|
394
|
+
runWithOutput(`git rev-parse --verify origin/${branch}`);
|
|
395
|
+
branchRef = `origin/${branch}`;
|
|
396
|
+
} catch {
|
|
397
|
+
return [];
|
|
398
|
+
}
|
|
382
399
|
}
|
|
383
400
|
|
|
384
401
|
const RS = '\x1E';
|
|
385
402
|
const COMMIT_SEP = `${RS}${RS}`;
|
|
386
403
|
|
|
387
404
|
try {
|
|
388
|
-
const logCmd = `git log --format=%H${RS}%ai${RS}%an${RS}%B${COMMIT_SEP} ${
|
|
405
|
+
const logCmd = `git log --format=%H${RS}%ai${RS}%an${RS}%B${COMMIT_SEP} ${branchRef}`;
|
|
389
406
|
const output = runWithOutput(logCmd).trim();
|
|
390
407
|
if (!output) return [];
|
|
391
408
|
|