Skip to content

Commit 79f5761

Browse files
authored
chore: add lefthook, PR workflows, and CI standardization (#1)
## Summary - Add lefthook.yml with gofmt, govet, and conventional commit hooks - Add PR review workflow (reviewforge) for automated code review - Add structlint validation workflow for PRs - Add PR title validation for conventional commits (squash merge support) - Standardize test.yml with setup-go@v5 and caching ## Test plan - [ ] Verify `go test ./...` passes - [ ] Check workflows render correctly in GitHub Actions tab
1 parent e67a635 commit 79f5761

8 files changed

Lines changed: 427 additions & 13 deletions

File tree

.github/workflows/pr.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: PR
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited, synchronize, reopened]
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
13+
jobs:
14+
title:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Validate PR title follows Conventional Commits
18+
env:
19+
TITLE: ${{ github.event.pull_request.title }}
20+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
run: |
22+
if echo "$TITLE" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\(.+\))?(!)?: .+"; then
23+
echo "PR title is valid: $TITLE"
24+
exit 0
25+
fi
26+
27+
BODY=$(cat <<'COMMENT'
28+
### ⚠️ Invalid PR Title
29+
30+
PR title must follow the **Conventional Commits** format since we use squash merge:
31+
32+
```
33+
<type>[optional scope][!]: <description>
34+
```
35+
36+
**Allowed types:** `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `build`, `ci`, `perf`, `revert`
37+
38+
**Examples:**
39+
- `feat: add new feature`
40+
- `fix(api): resolve null pointer`
41+
- `feat!: breaking change`
42+
- `chore(deps): update dependencies`
43+
COMMENT
44+
)
45+
46+
# Post comment on PR
47+
gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
48+
-X POST -f body="$BODY"
49+
50+
echo "::error::PR title must follow Conventional Commits format"
51+
exit 1
52+
53+
review:
54+
runs-on: ubuntu-latest
55+
steps:
56+
- uses: actions/checkout@v4
57+
- uses: AxeForging/reviewforge@main
58+
with:
59+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60+
AI_PROVIDER: gemini
61+
AI_MODEL: gemini-2.5-flash
62+
AI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
63+
SHOW_TOKEN_USAGE: true
64+
INCREMENTAL: false
65+
REVIEW_RULES: concise
66+
67+
validate:
68+
runs-on: ubuntu-latest
69+
steps:
70+
- uses: actions/checkout@v4
71+
- uses: AxeForging/structlint@main
72+
with:
73+
config: .structlint.yaml
74+
comment-on-pr: "true"
75+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/release.yml

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ on:
44
workflow_dispatch:
55
inputs:
66
tag:
7-
description: 'Release tag (e.g., v1.0.0)'
8-
required: true
7+
description: 'Release tag (leave empty for auto-bump from conventional commits)'
8+
required: false
99
type: string
1010

1111
permissions:
@@ -29,15 +29,40 @@ jobs:
2929
- name: Run tests
3030
run: go test ./... -v
3131

32+
- name: Build releaseforge
33+
run: make build-local
34+
35+
- name: Determine version
36+
id: version
37+
run: |
38+
if [ -n "${{ inputs.tag }}" ]; then
39+
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
40+
else
41+
NEXT=$(./releaseforge bump --quiet)
42+
echo "Auto-bumped version: ${NEXT}"
43+
echo "tag=${NEXT}" >> "$GITHUB_OUTPUT"
44+
fi
45+
46+
- name: Generate release notes
47+
env:
48+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
49+
run: |
50+
PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
51+
ARGS="generate --use-git-fallback --output /tmp/release-notes.md"
52+
if [ -n "$PREV_TAG" ]; then
53+
ARGS="$ARGS --git-tag $PREV_TAG --analyze-from-tag"
54+
fi
55+
./releaseforge $ARGS
56+
3257
- name: Create and push tag
3358
run: |
34-
git tag ${{ inputs.tag }}
35-
git push origin ${{ inputs.tag }}
59+
git tag ${{ steps.version.outputs.tag }}
60+
git push origin ${{ steps.version.outputs.tag }}
3661
3762
- name: Run GoReleaser
3863
uses: goreleaser/goreleaser-action@v6
3964
with:
4065
version: latest
41-
args: release --clean
66+
args: release --clean --release-notes /tmp/release-notes.md
4267
env:
4368
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/releaseforge.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: ReleaseForge
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
command:
7+
description: "Command: bump, generate"
8+
type: string
9+
default: "bump"
10+
tag:
11+
description: "Base semver tag"
12+
type: string
13+
default: ""
14+
branch:
15+
description: "Target branch"
16+
type: string
17+
default: "HEAD"
18+
provider:
19+
description: "LLM provider for generate"
20+
type: string
21+
default: "gemini"
22+
model:
23+
description: "LLM model for generate"
24+
type: string
25+
default: "gemini-2.0-flash"
26+
template-name:
27+
description: "Built-in template name"
28+
type: string
29+
default: ""
30+
max-commits:
31+
description: "Max commits to analyze"
32+
type: string
33+
default: "200"
34+
secrets:
35+
api_key:
36+
description: "LLM API key (for generate command)"
37+
required: false
38+
outputs:
39+
next-version:
40+
description: "Next semver version"
41+
value: ${{ jobs.releaseforge.outputs.next-version }}
42+
43+
permissions:
44+
contents: read
45+
46+
jobs:
47+
releaseforge:
48+
runs-on: ubuntu-latest
49+
outputs:
50+
next-version: ${{ steps.rf.outputs.next-version }}
51+
steps:
52+
- uses: actions/checkout@v4
53+
with:
54+
fetch-depth: 0
55+
56+
- id: rf
57+
uses: AxeForging/releaseforge@main
58+
with:
59+
command: ${{ inputs.command }}
60+
tag: ${{ inputs.tag }}
61+
branch: ${{ inputs.branch }}
62+
provider: ${{ inputs.provider }}
63+
model: ${{ inputs.model }}
64+
api-key: ${{ secrets.api_key }}
65+
template-name: ${{ inputs.template-name }}
66+
max-commits: ${{ inputs.max-commits }}

.github/workflows/test.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ name: Test
22

33
on:
44
push:
5-
branches: ["**"]
65
pull_request:
6+
branches:
7+
- main
78

89
jobs:
910
test:
1011
runs-on: ubuntu-latest
1112
steps:
12-
- uses: actions/checkout@v4
13+
- name: Checkout code
14+
uses: actions/checkout@v4
1315
with:
1416
fetch-depth: 0
1517

16-
- name: Set up Go
18+
- name: Setup Go
1719
uses: actions/setup-go@v5
1820
with:
1921
go-version: '1.24'

.goreleaser.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ snapshot:
4646
version_template: "{{ incpatch .Version }}-next"
4747

4848
changelog:
49-
sort: asc
50-
filters:
51-
exclude:
52-
- '^docs:'
53-
- '^test:'
49+
disable: true
5450

5551
release:
5652
github:

.structlint.yaml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# structlint configuration
2+
# Validates project directory structure and file naming
3+
4+
dir_structure:
5+
allowedPaths:
6+
- "."
7+
- "actions/**"
8+
- "services/**"
9+
- "helpers/**"
10+
- "domain/**"
11+
- "integration/**"
12+
- "doc/**"
13+
- "dist/**"
14+
- ".claude/**"
15+
- ".github/**"
16+
disallowedPaths:
17+
- "vendor/**"
18+
- "node_modules/**"
19+
- "tmp/**"
20+
- "temp/**"
21+
- ".git/**"
22+
- "*.log"
23+
requiredPaths:
24+
- "actions"
25+
- "services"
26+
- "domain"
27+
28+
file_naming_pattern:
29+
allowed:
30+
- "*.go"
31+
- "*.mod"
32+
- "*.sum"
33+
- "*.yaml"
34+
- "*.yml"
35+
- "*.json"
36+
- "*.toml"
37+
- "*.md"
38+
- "*.txt"
39+
- "*.png"
40+
- "*.jpg"
41+
- "*.svg"
42+
- "README*"
43+
- "LICENSE*"
44+
- "CHANGELOG*"
45+
- "Makefile"
46+
- "Dockerfile*"
47+
- "*.sh"
48+
- ".gitignore"
49+
- ".editorconfig"
50+
- ".golangci.yml"
51+
- ".goreleaser.yml"
52+
- ".github/**"
53+
- "go.work"
54+
- "go.work.sum"
55+
disallowed:
56+
- "*.env*"
57+
- ".env*"
58+
- "*.key"
59+
- "*.pem"
60+
- "*.log"
61+
- "*.tmp"
62+
- "*.temp"
63+
- "*~"
64+
- "*.swp"
65+
- "*.bak"
66+
- ".DS_Store"
67+
- "Thumbs.db"
68+
required:
69+
- "go.mod"
70+
- "README.md"
71+
- ".gitignore"
72+
- "*.go"
73+
74+
ignore:
75+
- ".git"
76+
- "vendor"
77+
- "node_modules"
78+
- "bin"
79+
- "dist"
80+
- ".idea"
81+
- ".vscode"
82+
- ".DS_Store"
83+
- "*.log"
84+
- "*.tmp"

0 commit comments

Comments
 (0)