-
Notifications
You must be signed in to change notification settings - Fork 0
ci: add CI/CD automation workflows #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
669ace2
ab7608b
7d8da16
710fac7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main, develop] | ||
| pull_request: | ||
| branches: [main, develop] | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Bun | ||
| uses: oven-sh/setup-bun@v1 | ||
| with: | ||
| bun-version: latest | ||
|
|
||
| - name: Install dependencies | ||
| run: bun install | ||
|
|
||
| - name: Type check | ||
| run: bun run typecheck | ||
|
|
||
| - name: Run tests with coverage | ||
| run: bun test --coverage --coverage-reporter=lcov | ||
|
|
||
| - name: Upload coverage to Codecov | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| files: ./coverage/lcov.info | ||
| flags: unittests | ||
| name: codecov-umbrella | ||
| fail_ci_if_error: true | ||
| verbose: true | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,272 @@ | ||||||||||||||||||||||||||||||||||
| name: Create Release PR | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||
| version_type: | ||||||||||||||||||||||||||||||||||
| description: 'Version bump type' | ||||||||||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||||||||||
| type: choice | ||||||||||||||||||||||||||||||||||
| options: | ||||||||||||||||||||||||||||||||||
| - patch | ||||||||||||||||||||||||||||||||||
| - minor | ||||||||||||||||||||||||||||||||||
| - major | ||||||||||||||||||||||||||||||||||
| - auto | ||||||||||||||||||||||||||||||||||
| default: 'auto' | ||||||||||||||||||||||||||||||||||
| custom_version: | ||||||||||||||||||||||||||||||||||
| description: 'Custom version (overrides version_type if set, e.g., 1.2.3)' | ||||||||||||||||||||||||||||||||||
| required: false | ||||||||||||||||||||||||||||||||||
| type: string | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||
| contents: write | ||||||||||||||||||||||||||||||||||
| pull-requests: write | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||
| create-release-pr: | ||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||
| - name: Checkout main branch | ||||||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| ref: main | ||||||||||||||||||||||||||||||||||
| fetch-depth: 0 | ||||||||||||||||||||||||||||||||||
| token: ${{ secrets.GITHUB_TOKEN }} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Setup Bun | ||||||||||||||||||||||||||||||||||
| uses: oven-sh/setup-bun@v1 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| bun-version: latest | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Get current version | ||||||||||||||||||||||||||||||||||
| id: current | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| CURRENT_VERSION=$(jq -r '.version' package.json) | ||||||||||||||||||||||||||||||||||
| echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||
| echo "Current version: $CURRENT_VERSION" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Determine version bump from commits | ||||||||||||||||||||||||||||||||||
| id: analyze | ||||||||||||||||||||||||||||||||||
| if: inputs.version_type == 'auto' && inputs.custom_version == '' | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| # Get commits since last tag | ||||||||||||||||||||||||||||||||||
| LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if [ -z "$LAST_TAG" ]; then | ||||||||||||||||||||||||||||||||||
| COMMITS=$(git log --oneline) | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| COMMITS=$(git log --oneline ${LAST_TAG}..HEAD) | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "Commits since last tag:" | ||||||||||||||||||||||||||||||||||
| echo "$COMMITS" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Analyze commits for version bump (conventional commits) | ||||||||||||||||||||||||||||||||||
| BUMP="patch" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if echo "$COMMITS" | grep -qiE "^[a-f0-9]+ (feat|feature)(\(.+\))?!:|BREAKING CHANGE"; then | ||||||||||||||||||||||||||||||||||
| BUMP="major" | ||||||||||||||||||||||||||||||||||
| elif echo "$COMMITS" | grep -qiE "^[a-f0-9]+ (feat|feature)(\(.+\))?:"; then | ||||||||||||||||||||||||||||||||||
| BUMP="minor" | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "bump=$BUMP" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||
| echo "Determined bump type: $BUMP" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Calculate new version | ||||||||||||||||||||||||||||||||||
| id: version | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| CURRENT="${{ steps.current.outputs.version }}" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Use custom version if provided | ||||||||||||||||||||||||||||||||||
| if [ -n "${{ inputs.custom_version }}" ]; then | ||||||||||||||||||||||||||||||||||
| CUSTOM="${{ inputs.custom_version }}" | ||||||||||||||||||||||||||||||||||
| # Strip optional 'v' prefix and validate strict semver (X.Y.Z) | ||||||||||||||||||||||||||||||||||
| CUSTOM="${CUSTOM#v}" | ||||||||||||||||||||||||||||||||||
| if [[ ! "$CUSTOM" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | ||||||||||||||||||||||||||||||||||
| echo "::error::Invalid custom version '${{ inputs.custom_version }}'. Must be semver (e.g., 1.2.3 or v1.2.3)." | ||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
| NEW_VERSION="$CUSTOM" | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| # Determine bump type | ||||||||||||||||||||||||||||||||||
| if [ "${{ inputs.version_type }}" == "auto" ]; then | ||||||||||||||||||||||||||||||||||
| BUMP="${{ steps.analyze.outputs.bump }}" | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| BUMP="${{ inputs.version_type }}" | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Split version into parts | ||||||||||||||||||||||||||||||||||
| IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Validate no prerelease suffix (e.g., 1.0.0-alpha.1) | ||||||||||||||||||||||||||||||||||
| if [[ ! "$MAJOR" =~ ^[0-9]+$ ]] || [[ ! "$MINOR" =~ ^[0-9]+$ ]] || [[ ! "$PATCH" =~ ^[0-9]+$ ]]; then | ||||||||||||||||||||||||||||||||||
| echo "::error::Prerelease versions not supported (got: $CURRENT). Use explicit version input instead." | ||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case "$BUMP" in | ||||||||||||||||||||||||||||||||||
| major) | ||||||||||||||||||||||||||||||||||
| NEW_VERSION="$((MAJOR + 1)).0.0" | ||||||||||||||||||||||||||||||||||
| ;; | ||||||||||||||||||||||||||||||||||
| minor) | ||||||||||||||||||||||||||||||||||
| NEW_VERSION="${MAJOR}.$((MINOR + 1)).0" | ||||||||||||||||||||||||||||||||||
| ;; | ||||||||||||||||||||||||||||||||||
| patch) | ||||||||||||||||||||||||||||||||||
| NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" | ||||||||||||||||||||||||||||||||||
| ;; | ||||||||||||||||||||||||||||||||||
| esac | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||||
| echo "New version: $NEW_VERSION" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Create release branch | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| BRANCH_NAME="release-v${{ steps.version.outputs.new_version }}" | ||||||||||||||||||||||||||||||||||
| git checkout -b "$BRANCH_NAME" | ||||||||||||||||||||||||||||||||||
| echo "Created branch: $BRANCH_NAME" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Update package.json version | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| NEW_VERSION="${{ steps.version.outputs.new_version }}" | ||||||||||||||||||||||||||||||||||
| jq --arg v "$NEW_VERSION" '.version = $v' package.json > package.json.tmp | ||||||||||||||||||||||||||||||||||
| mv package.json.tmp package.json | ||||||||||||||||||||||||||||||||||
| echo "Updated package.json to version $NEW_VERSION" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Generate changelog entry | ||||||||||||||||||||||||||||||||||
| id: changelog | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| NEW_VERSION="${{ steps.version.outputs.new_version }}" | ||||||||||||||||||||||||||||||||||
| TODAY=$(date +%Y-%m-%d) | ||||||||||||||||||||||||||||||||||
| LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Get commits grouped by type | ||||||||||||||||||||||||||||||||||
| if [ -z "$LAST_TAG" ]; then | ||||||||||||||||||||||||||||||||||
| COMMITS=$(git log --pretty=format:"%s" HEAD) | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| COMMITS=$(git log --pretty=format:"%s" ${LAST_TAG}..HEAD) | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Build changelog entry | ||||||||||||||||||||||||||||||||||
| ENTRY="## [$NEW_VERSION] - $TODAY"$'\n\n' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Features (normalize case before sed to handle Feat/FEAT variants) | ||||||||||||||||||||||||||||||||||
| FEATURES=$(echo "$COMMITS" | grep -iE "^feat(\(.+\))?:" | tr '[:upper:]' '[:lower:]' | sed 's/^feat(\([^)]*\)): /- **\1**: /' | sed 's/^feat: /- /' || true) | ||||||||||||||||||||||||||||||||||
| if [ -n "$FEATURES" ]; then | ||||||||||||||||||||||||||||||||||
| ENTRY+="### Added"$'\n'"$FEATURES"$'\n\n' | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Fixes (normalize case before sed to handle Fix/FIX variants) | ||||||||||||||||||||||||||||||||||
| FIXES=$(echo "$COMMITS" | grep -iE "^fix(\(.+\))?:" | tr '[:upper:]' '[:lower:]' | sed 's/^fix(\([^)]*\)): /- **\1**: /' | sed 's/^fix: /- /' || true) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+155
to
+162
|
||||||||||||||||||||||||||||||||||
| # Features (normalize case before sed to handle Feat/FEAT variants) | |
| FEATURES=$(echo "$COMMITS" | grep -iE "^feat(\(.+\))?:" | tr '[:upper:]' '[:lower:]' | sed 's/^feat(\([^)]*\)): /- **\1**: /' | sed 's/^feat: /- /' || true) | |
| if [ -n "$FEATURES" ]; then | |
| ENTRY+="### Added"$'\n'"$FEATURES"$'\n\n' | |
| fi | |
| # Fixes (normalize case before sed to handle Fix/FIX variants) | |
| FIXES=$(echo "$COMMITS" | grep -iE "^fix(\(.+\))?:" | tr '[:upper:]' '[:lower:]' | sed 's/^fix(\([^)]*\)): /- **\1**: /' | sed 's/^fix: /- /' || true) | |
| # Features (handle Feat/FEAT variants with case-insensitive matching) | |
| FEATURES=$(echo "$COMMITS" | grep -iE "^feat(\(.+\))?:" | sed -E 's/^feat\(([^)]*)\): /- **\1**: /I; s/^feat: /- /I' || true) | |
| if [ -n "$FEATURES" ]; then | |
| ENTRY+="### Added"$'\n'"$FEATURES"$'\n\n' | |
| fi | |
| # Fixes (handle Fix/FIX variants with case-insensitive matching) | |
| FIXES=$(echo "$COMMITS" | grep -iE "^fix(\(.+\))?:" | sed -E 's/^fix\(([^)]*)\): /- **\1**: /I; s/^fix: /- /I' || true) |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sed regex patterns use \( and \) for capture groups, but after tr '[:upper:]' '[:lower:]' converts the commit prefix to lowercase, the pattern ^feat(\([^)]*\)) expects a literal opening parenthesis that's already lowercase 'feat('. However, the closing parenthesis in the sed pattern \([^)]*\) will correctly capture the scope. The issue is that after tr, the commit will be like 'feat(scope): message' but the sed pattern assumes 'feat(' is already matched by grep. This should work, but the double sed chaining could be simplified to a single sed with case-insensitive flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting
fail_ci_if_error: truewill cause the CI workflow to fail if Codecov upload fails. This can be problematic if Codecov has service issues or if the token is temporarily unavailable. Consider setting this tofalseto make CI more resilient to external service failures, or at least document this behavior in the setup documentation.