Skip to content

Commit 3358b27

Browse files
Mossakaclaude
andauthored
ci: add conventional commits and improve release process (#49)
* ci: add conventional commits enforcement - Add commitlint with husky git hooks for local validation - Add GitHub Action to lint commits on PRs - Add GitHub Action to enforce conventional PR titles - Configure allowed types: feat, fix, docs, style, refactor, etc. - Configure optional scopes: cli, docker, squid, proxy, ci, deps Signed-off-by: Jiaxiao (mossaka) Zhou <duibao55328@gmail.com> * chore: add eslint configuration and fix linting errors - Add .eslintrc.js with TypeScript support - Fix unnecessary escape character in regex - Fix no-prototype-builtins error with Object.prototype.hasOwnProperty.call - Add eslint-disable comments for intentionally unused code - Remove unused imports 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * ci: improve release notes generation with template system - Add release.yml config for GitHub auto-generated release notes - Add RELEASE_TEMPLATE.md with customizable release notes format - Add generate-release-notes.js script for safe template substitution - Auto-generate changelog from GitHub API with git log fallback - Include CLI help output in release notes - Support placeholder substitution for version, repository, etc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: add lint to pre-commit hook 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Signed-off-by: Jiaxiao (mossaka) Zhou <duibao55328@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 98e7aeb commit 3358b27

16 files changed

+1284
-47
lines changed

.eslintrc.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module.exports = {
2+
parser: '@typescript-eslint/parser',
3+
plugins: ['@typescript-eslint'],
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:@typescript-eslint/recommended',
7+
],
8+
env: {
9+
node: true,
10+
es2020: true,
11+
},
12+
parserOptions: {
13+
ecmaVersion: 2020,
14+
sourceType: 'module',
15+
},
16+
rules: {
17+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
18+
'@typescript-eslint/no-explicit-any': 'warn',
19+
},
20+
ignorePatterns: ['dist/', 'node_modules/'],
21+
};

.github/release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Configuration for GitHub's auto-generated release notes
2+
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
3+
4+
changelog:
5+
exclude:
6+
labels:
7+
- ignore-for-release
8+
- dependencies
9+
authors:
10+
- dependabot
11+
- dependabot[bot]
12+
categories:
13+
- title: "Breaking Changes"
14+
labels:
15+
- breaking-change
16+
- breaking
17+
- title: "New Features"
18+
labels:
19+
- feature
20+
- enhancement
21+
- title: "Bug Fixes"
22+
labels:
23+
- bug
24+
- bugfix
25+
- fix
26+
- title: "Documentation"
27+
labels:
28+
- documentation
29+
- docs
30+
- title: "Other Changes"
31+
labels:
32+
- "*"

.github/workflows/commitlint.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Lint Commits
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
jobs:
8+
commitlint:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
with:
13+
fetch-depth: 0
14+
15+
- uses: actions/setup-node@v4
16+
with:
17+
node-version: 20
18+
cache: 'npm'
19+
20+
- name: Install dependencies
21+
run: npm ci
22+
23+
- name: Lint commits
24+
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose

.github/workflows/pr-title.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: PR Title Check
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
types: [opened, edited, synchronize, reopened]
7+
8+
jobs:
9+
pr-title:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: amannn/action-semantic-pull-request@v5
13+
env:
14+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15+
with:
16+
# Configure which types are allowed
17+
types: |
18+
feat
19+
fix
20+
docs
21+
style
22+
refactor
23+
perf
24+
test
25+
build
26+
ci
27+
chore
28+
revert
29+
# Require lowercase subject
30+
subjectPattern: ^[a-z].+$
31+
subjectPatternError: |
32+
The subject "{subject}" must start with a lowercase letter.
33+
# Don't require a scope
34+
requireScope: false
35+
# Scopes that are allowed (if scope is provided)
36+
scopes: |
37+
cli
38+
docker
39+
squid
40+
proxy
41+
ci
42+
deps

.github/workflows/release.yml

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -106,57 +106,109 @@ jobs:
106106
cd release
107107
sha256sum * > checksums.txt
108108
109-
- name: Create Release Notes
110-
id: release_notes
109+
- name: Get previous release tag
110+
id: previous_tag
111111
run: |
112-
cat > release_notes.md << 'EOF'
113-
## Installation
112+
set -euo pipefail
113+
CURRENT_TAG="${{ steps.version_early.outputs.version }}"
114114
115-
### Binary Installation (Recommended)
115+
# Use git tags directly (more reliable than gh release list)
116+
# Get the most recent tag that is not the current tag
117+
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v "^${CURRENT_TAG}$" | head -n1 || echo "")
116118
117-
**Linux (x64):**
118-
```bash
119-
curl -L https://github.com/${{ github.repository }}/releases/download/${{ steps.version_early.outputs.version }}/awf-linux-x64 -o awf
120-
chmod +x awf
121-
sudo mv awf /usr/local/bin/
122-
```
119+
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
120+
echo "Previous tag: $PREVIOUS_TAG (current: $CURRENT_TAG)"
121+
122+
- name: Generate changelog from commits
123+
id: changelog
124+
run: |
125+
set -euo pipefail
126+
CURRENT_TAG="${{ steps.version_early.outputs.version }}"
127+
PREVIOUS_TAG="${{ steps.previous_tag.outputs.previous_tag }}"
128+
129+
echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG"
130+
131+
# Generate changelog using GitHub's API
132+
if [ -n "$PREVIOUS_TAG" ]; then
133+
CHANGELOG=$(gh api repos/${{ github.repository }}/releases/generate-notes \
134+
-f tag_name="$CURRENT_TAG" \
135+
-f previous_tag_name="$PREVIOUS_TAG" \
136+
--jq '.body' 2>/dev/null || echo "")
137+
else
138+
# First release - try API without previous tag
139+
CHANGELOG=$(gh api repos/${{ github.repository }}/releases/generate-notes \
140+
-f tag_name="$CURRENT_TAG" \
141+
--jq '.body' 2>/dev/null || echo "")
142+
fi
123143
124-
### NPM Installation (Alternative)
144+
# If API call failed, fall back to git log
145+
if [ -z "$CHANGELOG" ]; then
146+
echo "GitHub API failed, falling back to git log"
147+
if [ -n "$PREVIOUS_TAG" ]; then
148+
CHANGELOG=$(git log --oneline --pretty=format:"* %s (%h)" "$PREVIOUS_TAG..HEAD" 2>/dev/null || echo "* Initial release")
149+
else
150+
# First release - get all commits (no arbitrary limit)
151+
CHANGELOG=$(git log --oneline --pretty=format:"* %s (%h)" 2>/dev/null || echo "* Initial release")
152+
fi
153+
fi
125154
126-
```bash
127-
# Install from tarball
128-
npm install -g https://github.com/${{ github.repository }}/releases/download/${{ steps.version_early.outputs.version }}/awf.tgz
129-
```
155+
# Write changelog to file for multiline handling
156+
echo "$CHANGELOG" > changelog_body.md
130157
131-
### Requirements
158+
# Validate changelog was generated
159+
if [ ! -s changelog_body.md ]; then
160+
echo "Error: Changelog generation failed or produced empty output"
161+
exit 1
162+
fi
132163
133-
- Docker and Docker Compose must be installed
134-
- For iptables manipulation, run with sudo: `sudo awf ...`
135-
- Container images will be pulled automatically from GHCR on first run
164+
echo "Changelog generated successfully ($(wc -l < changelog_body.md) lines)"
165+
env:
166+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
136167

137-
## Verification
168+
- name: Generate CLI help output
169+
id: cli_help
170+
run: |
171+
set -euo pipefail
138172
139-
Verify checksums after download:
140-
```bash
141-
sha256sum -c checksums.txt
142-
```
173+
# Generate CLI help from the built binary
174+
node dist/cli.js --help > cli_help.txt
143175
144-
## Usage
176+
# Validate CLI help was generated
177+
if [ ! -s cli_help.txt ]; then
178+
echo "Error: CLI help generation failed or produced empty output"
179+
exit 1
180+
fi
145181
146-
```bash
147-
sudo awf --allow-domains github.com,api.github.com 'curl https://api.github.com'
148-
```
182+
echo "CLI help generated ($(wc -l < cli_help.txt) lines):"
183+
cat cli_help.txt
149184
150-
See [README.md](https://github.com/${{ github.repository }}/blob/${{ steps.version_early.outputs.version }}/README.md) for full documentation.
185+
- name: Create Release Notes
186+
id: release_notes
187+
env:
188+
VERSION: ${{ steps.version_early.outputs.version }}
189+
VERSION_NUMBER: ${{ steps.version_early.outputs.version_number }}
190+
REPOSITORY: ${{ github.repository }}
191+
run: |
192+
set -euo pipefail
193+
194+
# Use Node.js script for safe template substitution
195+
# (avoids shell injection issues with special characters)
196+
node scripts/generate-release-notes.js \
197+
changelog_body.md \
198+
cli_help.txt \
199+
release_notes.md
200+
201+
# Validate output was generated
202+
if [ ! -s release_notes.md ]; then
203+
echo "Error: Release notes generation failed"
204+
exit 1
205+
fi
151206
152-
## Container Images
207+
# Cleanup temp files
208+
rm -f changelog_body.md cli_help.txt
153209
154-
Published to GitHub Container Registry:
155-
- `ghcr.io/${{ github.repository }}/squid:${{ steps.version_early.outputs.version_number }}`
156-
- `ghcr.io/${{ github.repository }}/copilot:${{ steps.version_early.outputs.version_number }}`
157-
- `ghcr.io/${{ github.repository }}/squid:latest`
158-
- `ghcr.io/${{ github.repository }}/copilot:latest`
159-
EOF
210+
echo "Release notes preview (first 20 lines):"
211+
head -20 release_notes.md
160212
161213
- name: Create GitHub Release
162214
uses: softprops/action-gh-release@v1

.husky/commit-msg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx --no -- commitlint --edit $1

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npm run lint && npm run build

commitlint.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module.exports = {
2+
extends: ['@commitlint/config-conventional'],
3+
rules: {
4+
// Enforce lowercase for type
5+
'type-case': [2, 'always', 'lower-case'],
6+
// Enforce lowercase for subject
7+
'subject-case': [2, 'always', 'lower-case'],
8+
// No period at end of subject
9+
'subject-full-stop': [2, 'never', '.'],
10+
// Max 72 chars for subject (git best practice)
11+
'header-max-length': [2, 'always', 72],
12+
},
13+
};

docs/RELEASE_TEMPLATE.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Release Notes Template
2+
3+
This file is used to generate release notes automatically during the release workflow.
4+
Edit this file to change the format of release notes for all future releases.
5+
6+
## Available Placeholders
7+
8+
| Placeholder | Description | Example |
9+
|-------------|-------------|---------|
10+
| `{{CHANGELOG}}` | Auto-generated changelog from GitHub API or git log | PR list or commit list |
11+
| `{{CLI_HELP}}` | Output of `awf --help` command | CLI usage and options |
12+
| `{{REPOSITORY}}` | GitHub repository path | `githubnext/gh-aw-firewall` |
13+
| `{{VERSION}}` | Full version tag with 'v' prefix | `v0.3.0` |
14+
| `{{VERSION_NUMBER}}` | Version number without 'v' prefix | `0.3.0` |
15+
16+
## Template Content
17+
18+
Everything below the `---` separator becomes the release notes.
19+
20+
---
21+
22+
## What's Changed
23+
24+
{{CHANGELOG}}
25+
26+
## CLI Options
27+
28+
```
29+
{{CLI_HELP}}
30+
```
31+
32+
## Installation
33+
34+
### Binary Installation (Recommended)
35+
36+
**Linux (x64):**
37+
```bash
38+
curl -L https://github.com/{{REPOSITORY}}/releases/download/{{VERSION}}/awf-linux-x64 -o awf
39+
chmod +x awf
40+
sudo mv awf /usr/local/bin/
41+
```
42+
43+
### NPM Installation (Alternative)
44+
45+
```bash
46+
# Install from tarball
47+
npm install -g https://github.com/{{REPOSITORY}}/releases/download/{{VERSION}}/awf.tgz
48+
```
49+
50+
### Requirements
51+
52+
- Docker and Docker Compose must be installed
53+
- For iptables manipulation, run with sudo: `sudo awf ...`
54+
- Container images will be pulled automatically from GHCR on first run
55+
56+
## Verification
57+
58+
Verify checksums after download:
59+
```bash
60+
sha256sum -c checksums.txt
61+
```
62+
63+
## Quick Start
64+
65+
```bash
66+
# Basic usage with domain whitelist
67+
sudo awf --allow-domains github.com,api.github.com -- curl https://api.github.com
68+
69+
# Pass environment variables
70+
sudo awf --allow-domains api.github.com -e GITHUB_TOKEN=xxx -- gh api /user
71+
72+
# Mount additional volumes
73+
sudo awf --allow-domains github.com -v /my/data:/data:ro -- cat /data/file.txt
74+
75+
# Set working directory in container
76+
sudo awf --allow-domains github.com --container-workdir /workspace -- pwd
77+
```
78+
79+
See [README.md](https://github.com/{{REPOSITORY}}/blob/{{VERSION}}/README.md) for full documentation.
80+
81+
## Container Images
82+
83+
Published to GitHub Container Registry:
84+
- `ghcr.io/{{REPOSITORY}}/squid:{{VERSION_NUMBER}}`
85+
- `ghcr.io/{{REPOSITORY}}/copilot:{{VERSION_NUMBER}}`
86+
- `ghcr.io/{{REPOSITORY}}/squid:latest`
87+
- `ghcr.io/{{REPOSITORY}}/copilot:latest`

0 commit comments

Comments
 (0)