Skip to content

Commit 9552c2c

Browse files
minsoo-webclaude
andauthored
chore: add auto-tag workflow for automated release tagging (#15)
Add GitHub Actions workflow that automatically creates version tags when Cargo.toml version changes on main branch merge, eliminating the manual tagging step that was frequently forgotten. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 96478fd commit 9552c2c

File tree

4 files changed

+285
-0
lines changed

4 files changed

+285
-0
lines changed

.github/workflows/auto-tag.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Auto Tag
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- Cargo.toml
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
auto-tag:
15+
name: Create Version Tag
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 2
21+
22+
- name: Check version change
23+
id: version
24+
run: |
25+
OLD_VERSION=$(git show HEAD~1:Cargo.toml 2>/dev/null | grep -m 1 '^version' | sed 's/.*"\(.*\)".*/\1/' || echo "")
26+
NEW_VERSION=$(grep -m 1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
27+
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
28+
echo "changed=true" >> $GITHUB_OUTPUT
29+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
30+
else
31+
echo "changed=false" >> $GITHUB_OUTPUT
32+
fi
33+
34+
- name: Create and push tag
35+
if: steps.version.outputs.changed == 'true'
36+
run: |
37+
TAG="v${{ steps.version.outputs.version }}"
38+
git tag "$TAG"
39+
git push origin "$TAG" 2>/dev/null || echo "Tag $TAG already exists on remote; skipping."
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Brainstorm: PR 머지 시 자동 태그 생성
2+
3+
**Date:** 2026-03-14
4+
**Status:** Draft
5+
6+
## What We're Building
7+
8+
PR이 main 브랜치에 머지될 때, `Cargo.toml``version` 필드가 변경되었고 해당 버전의 git tag가 아직 존재하지 않으면 자동으로 `v{version}` 태그를 생성하는 GitHub Actions 워크플로우.
9+
10+
이를 통해 현재 수동으로 수행하는 "머지 후 태그 생성 → push → release 트리거" 단계를 제거한다.
11+
12+
## Why This Approach
13+
14+
**문제:** 현재 PR 머지 후 수동으로 git tag를 생성해야 release.yml이 트리거된다. 이 단계를 잊으면 릴리스가 누락된다 (실제로 Cargo.toml은 v0.9.1이지만 최신 태그는 v0.7.0).
15+
16+
**선택한 방식:** 별도의 GitHub Actions 워크플로우 파일 추가
17+
- 기존 `ci.yml``release.yml`(cargo-dist 자동생성)을 건드리지 않음
18+
- 관심사 분리: CI 검증 / 태그 생성 / 릴리스 빌드가 각각 독립
19+
- 단순한 로직: Cargo.toml diff 확인 → 태그 존재 여부 확인 → 태그 생성
20+
21+
**고려했지만 선택하지 않은 방식:**
22+
- `ci.yml`에 job 추가: cargo-dist가 release.yml을 관리하듯, 워크플로우 파일 간 역할이 혼합됨
23+
- release-please 등 외부 도구: 과도한 복잡성. Cargo.toml 버전을 직접 관리하는 현재 컨벤션과 충돌 가능
24+
25+
## Key Decisions
26+
27+
1. **범위:** 앞으로의 머지만 대상. 기존 누락 태그(v0.8.0~v0.9.1)는 별도 수동 처리
28+
2. **트리거 조건:** main 머지 시 Cargo.toml의 `version` 필드가 변경되었고, 해당 `v{version}` 태그가 없을 때
29+
3. **구현:** 새로운 GitHub Actions 워크플로우 파일 (e.g., `.github/workflows/auto-tag.yml`)
30+
4. **태그 형식:** 기존 컨벤션 유지 — `v{major}.{minor}.{patch}`
31+
5. **태그 생성 후:** 태그가 push되면 기존 `release.yml`이 자동 트리거되어 빌드/릴리스 진행
32+
33+
## Workflow Design (High-Level)
34+
35+
```
36+
main에 push 발생
37+
→ Cargo.toml의 version 필드 변경 감지 (git diff)
38+
→ 해당 버전의 태그 존재 여부 확인
39+
→ 태그가 없으면 v{version} 태그 생성 및 push
40+
→ release.yml 자동 트리거
41+
```
42+
43+
## Open Questions
44+
45+
_(없음 — 모든 핵심 결정이 확정됨)_
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: "chore: PR 머지 시 자동 태그 생성 워크플로우 추가"
3+
type: chore
4+
status: active
5+
date: 2026-03-15
6+
origin: docs/brainstorms/2026-03-14-auto-tag-on-merge-brainstorm.md
7+
---
8+
9+
# chore: PR 머지 시 자동 태그 생성 워크플로우 추가
10+
11+
main 브랜치에 PR이 머지될 때, `Cargo.toml``version` 필드가 변경되었고 해당 버전의 git tag가 존재하지 않으면 `v{version}` 태그를 자동 생성하는 GitHub Actions 워크플로우를 추가한다. (see brainstorm: docs/brainstorms/2026-03-14-auto-tag-on-merge-brainstorm.md)
12+
13+
## Acceptance Criteria
14+
15+
- [ ] `.github/workflows/auto-tag.yml` 파일 생성
16+
- [ ] main 브랜치 push 시에만 트리거
17+
- [ ] `Cargo.toml`의 version 필드 변경을 감지 (`git diff HEAD~1`)
18+
- [ ] 해당 버전의 `v{version}` 태그가 이미 존재하면 스킵
19+
- [ ] 태그가 없으면 `v{version}` 태그를 생성하고 push
20+
- [ ] 태그 push 후 기존 `release.yml`이 자동 트리거됨을 확인
21+
- [ ] 기존 `ci.yml`, `release.yml` 수정 없음
22+
23+
## Context
24+
25+
**현재 프로세스 (수동):**
26+
1. PR에서 `Cargo.toml` version bump
27+
2. PR 머지
28+
3. 수동으로 `git tag v{version}` && `git push --tags` ← 잊기 쉬움
29+
4. `release.yml` 트리거 → 빌드/릴리스
30+
31+
**자동화 후:**
32+
1. PR에서 `Cargo.toml` version bump
33+
2. PR 머지
34+
3. `auto-tag.yml`이 자동으로 태그 생성 및 push
35+
4. `release.yml` 자동 트리거 → 빌드/릴리스
36+
37+
**핵심 참고 사항:**
38+
- `release.yml``v[0-9]+.[0-9]+.[0-9]+*` 태그 패턴에 반응 (`.github/workflows/release.yml:45`)
39+
- `contents: write` 권한이 필요 (태그 push용)
40+
- `GITHUB_TOKEN` 사용 (별도 secret 불필요)
41+
42+
## MVP
43+
44+
### .github/workflows/auto-tag.yml
45+
46+
```yaml
47+
name: Auto Tag
48+
49+
on:
50+
push:
51+
branches: [main]
52+
paths: [Cargo.toml]
53+
54+
permissions:
55+
contents: write
56+
57+
jobs:
58+
auto-tag:
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v4
62+
with:
63+
fetch-depth: 2
64+
65+
- name: Check version change
66+
id: version
67+
run: |
68+
OLD_VERSION=$(git show HEAD~1:Cargo.toml | grep '^version' | sed 's/.*"\(.*\)".*/\1/')
69+
NEW_VERSION=$(grep '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
70+
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
71+
echo "changed=true" >> $GITHUB_OUTPUT
72+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
73+
else
74+
echo "changed=false" >> $GITHUB_OUTPUT
75+
fi
76+
77+
- name: Check tag exists
78+
if: steps.version.outputs.changed == 'true'
79+
id: tag
80+
run: |
81+
TAG="v${{ steps.version.outputs.version }}"
82+
if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "$TAG"; then
83+
echo "exists=true" >> $GITHUB_OUTPUT
84+
else
85+
echo "exists=false" >> $GITHUB_OUTPUT
86+
fi
87+
88+
- name: Create and push tag
89+
if: steps.version.outputs.changed == 'true' && steps.tag.outputs.exists == 'false'
90+
run: |
91+
TAG="v${{ steps.version.outputs.version }}"
92+
git tag "$TAG"
93+
git push origin "$TAG"
94+
```
95+
96+
## Sources
97+
98+
- **Origin brainstorm:** [docs/brainstorms/2026-03-14-auto-tag-on-merge-brainstorm.md](docs/brainstorms/2026-03-14-auto-tag-on-merge-brainstorm.md) — Key decisions: 별도 워크플로우 파일로 구현, Cargo.toml 변경 감지 방식, 앞으로의 머지만 대상
99+
- **release.yml 트리거 패턴:** `.github/workflows/release.yml:45` — `v[0-9]+.[0-9]+.[0-9]+*`
100+
- **release.yml 권한:** `.github/workflows/release.yml:17-18` — `contents: write`
101+
- **버전 컨벤션:** `CLAUDE.md:13-17`
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
title: "Auto-tag CI workflow for chromaport Rust releases"
3+
date: 2026-03-15
4+
tags:
5+
- github-actions
6+
- ci-cd
7+
- release-automation
8+
- git-workflow
9+
- cargo-dist
10+
component: ci-workflow
11+
problem_type: workflow-issue
12+
severity: medium
13+
time_to_resolve: short
14+
---
15+
16+
# Auto-tag CI workflow for chromaport Rust releases
17+
18+
## Problem
19+
20+
PR 머지 후 수동으로 git tag를 생성해야 `release.yml`(cargo-dist)이 트리거되었다. 이 수동 단계를 잊으면 릴리스가 누락됨. 실제로 `Cargo.toml``v0.9.1`이었지만 최신 태그는 `v0.7.0`으로 여러 버전이 누락되어 있었다.
21+
22+
## Root Cause
23+
24+
PR 머지와 태그 생성 사이에 자동화가 없었다. `release.yml`은 태그 push 이벤트(`v[0-9]+.[0-9]+.[0-9]+*`)에만 반응하므로, 개발자가 수동으로 태그를 생성하지 않으면 릴리스 파이프라인이 실행되지 않았다.
25+
26+
## Investigation
27+
28+
세 가지 접근 방식을 평가:
29+
30+
1. **별도 워크플로우 파일** (선택): `.github/workflows/auto-tag.yml`로 관심사 분리. 기존 `ci.yml`/`release.yml` 수정 없음.
31+
2. **ci.yml에 job 추가**: cargo-dist가 `release.yml`을 관리하므로 역할 혼합 우려.
32+
3. **release-please**: 외부 도구 의존성 추가, `Cargo.toml` 직접 관리 컨벤션과 충돌 가능.
33+
34+
## Solution
35+
36+
`.github/workflows/auto-tag.yml` 생성:
37+
38+
```yaml
39+
name: Auto Tag
40+
41+
on:
42+
push:
43+
branches:
44+
- main
45+
paths:
46+
- Cargo.toml
47+
48+
permissions:
49+
contents: write
50+
51+
jobs:
52+
auto-tag:
53+
name: Create Version Tag
54+
runs-on: ubuntu-latest
55+
steps:
56+
- uses: actions/checkout@v4
57+
with:
58+
fetch-depth: 2
59+
60+
- name: Check version change
61+
id: version
62+
run: |
63+
OLD_VERSION=$(git show HEAD~1:Cargo.toml 2>/dev/null | grep -m 1 '^version' | sed 's/.*"\(.*\)".*/\1/' || echo "")
64+
NEW_VERSION=$(grep -m 1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
65+
if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
66+
echo "changed=true" >> $GITHUB_OUTPUT
67+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
68+
else
69+
echo "changed=false" >> $GITHUB_OUTPUT
70+
fi
71+
72+
- name: Create and push tag
73+
if: steps.version.outputs.changed == 'true'
74+
run: |
75+
TAG="v${{ steps.version.outputs.version }}"
76+
git tag "$TAG"
77+
git push origin "$TAG" 2>/dev/null || echo "Tag $TAG already exists on remote; skipping."
78+
```
79+
80+
## Key Implementation Details
81+
82+
- **`grep -m 1 '^version'`**: 첫 번째 version 라인만 매칭하여 dependency 버전과 혼동 방지
83+
- **`HEAD~1` fallback**: `2>/dev/null || echo ""`로 첫 커밋 등 예외 상황 대응
84+
- **TOCTOU 제거**: 별도 태그 존재 체크 없이 `git push` 실패를 graceful하게 처리
85+
- **`fetch-depth: 2`**: merge commit에서도 `HEAD~1`은 first parent(이전 main)를 가리키므로 충분
86+
- **`permissions: contents: write`**: 태그 push에 필요한 최소 권한
87+
88+
## Prevention
89+
90+
- **자동화**: 머지 후 수초 내에 태그가 자동 생성되어 수동 단계 완전 제거
91+
- **모니터링**: 머지 후 Actions 탭에서 "Auto Tag" 워크플로우 성공 확인
92+
- **실패 모드**: 권한 문제, 워크플로우 비활성화, version 미변경 시 스킵
93+
- **기존 누락 태그**: 수동으로 별도 생성 필요 (`git tag v0.8.0 <commit-hash>`)
94+
95+
## Related
96+
97+
- [Brainstorm: auto-tag on merge](../../brainstorms/2026-03-14-auto-tag-on-merge-brainstorm.md)
98+
- [Plan: auto-tag on merge](../../plans/2026-03-15-chore-auto-tag-on-merge-plan.md)
99+
- [Plan: release workflow trigger cleanup](../../plans/2026-03-10-chore-release-workflow-trigger-cleanup-plan.md)
100+
- Commit `fcf41e5`: release.yml에서 `pull_request` 트리거 제거 및 `v` prefix 강제

0 commit comments

Comments
 (0)