ci: add test workflow #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master, develop] | |
| tags: ["v*"] | |
| pull_request: | |
| branches: [master, develop] | |
| jobs: | |
| # Linting and code quality | |
| lint: | |
| name: Lint and Format Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Cache Go modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cache/go-build | |
| ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go- | |
| - name: Install dependencies | |
| run: make deps | |
| - name: Run linting | |
| run: make lint-dev | |
| - name: Check formatting and go mod tidy | |
| run: make verify | |
| # Security scanning | |
| security: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Install dependencies | |
| run: make deps | |
| - name: Run vulnerability check | |
| run: make vuln-check | |
| # Build and test on multiple platforms | |
| test: | |
| name: Test | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| go-version: ["1.23", "1.24", "1.25"] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ matrix.go-version }} | |
| - name: Cache Go modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cache/go-build | |
| ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go-${{ matrix.go-version }}- | |
| - name: Install dependencies | |
| run: make deps | |
| - name: Run tests with coverage | |
| run: make test-coverage | |
| - name: Run benchmarks | |
| run: make benchmark | |
| - name: Upload coverage to Codecov | |
| if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.25' | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| file: ./coverage.out | |
| flags: unittests | |
| name: codecov-umbrella | |
| fail_ci_if_error: false | |
| # Integration tests | |
| integration: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| needs: [test] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Build binary | |
| run: make build | |
| - name: Run integration tests | |
| run: make test-integration | |
| # Build cross-platform binaries | |
| build: | |
| name: Build Cross-Platform | |
| runs-on: ubuntu-latest | |
| needs: [lint, test, security] | |
| if: github.event_name == 'push' || github.event_name == 'pull_request' | |
| strategy: | |
| matrix: | |
| goos: [linux, windows, darwin] | |
| goarch: [amd64, arm64] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Build optimized cross-platform binary | |
| env: | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| run: | | |
| EXT="" | |
| if [ "$GOOS" = "windows" ]; then | |
| EXT=".exe" | |
| fi | |
| make release | |
| mv build/emojify "emojify-$GOOS-$GOARCH$EXT" | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: emojify-${{ matrix.goos }}-${{ matrix.goarch }} | |
| path: emojify-* | |
| retention-days: 30 | |
| # Docker build and push | |
| docker: | |
| name: Build Docker Images | |
| runs-on: ubuntu-latest | |
| needs: [test, lint] | |
| if: github.event_name == 'push' | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Validate release prerequisites | |
| validate-release: | |
| name: Validate Release Prerequisites | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Validate prerequisites | |
| env: | |
| # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} | |
| SCOOP_BUCKET_GITHUB_TOKEN: ${{ secrets.SCOOP_BUCKET_GITHUB_TOKEN }} | |
| WINGET_GITHUB_TOKEN: ${{ secrets.WINGET_GITHUB_TOKEN }} | |
| CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} | |
| AUR_KEY: ${{ secrets.AUR_KEY }} | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| GPG_KEY_FILE: ${{ secrets.GPG_KEY_FILE }} | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} | |
| run: ./scripts/validate-release-prerequisites.sh | |
| # GoReleaser configuration and build test | |
| goreleaser-test: | |
| name: GoReleaser Test | |
| runs-on: ubuntu-latest | |
| needs: [test, lint, security, integration, build, validate-release] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Cache Go modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cache/go-build | |
| ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go- | |
| - name: Install dependencies | |
| run: make deps | |
| - name: Install GoReleaser | |
| uses: goreleaser/goreleaser-action@v6 | |
| with: | |
| distribution: goreleaser | |
| version: "~> v2" | |
| install-only: true | |
| - name: Run comprehensive GoReleaser test | |
| env: | |
| # Provide dummy values for test environment | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| CHOCOLATEY_API_KEY: "dummy" | |
| WINGET_GITHUB_TOKEN: "dummy" | |
| AUR_KEY: "dummy" | |
| GPG_KEY_FILE: "dummy" | |
| run: | | |
| echo "🧪 Running GoReleaser configuration validation and build test..." | |
| # 1. Configuration check | |
| echo "📋 Validating GoReleaser configuration..." | |
| goreleaser check || { | |
| if [[ $? -eq 2 ]]; then | |
| echo "⚠️ Configuration valid but has deprecation warnings" | |
| else | |
| echo "❌ Configuration has errors" | |
| exit 1 | |
| fi | |
| } | |
| # 2. Build test (without publishing) | |
| echo "🔨 Testing build process..." | |
| goreleaser release --snapshot --clean --skip=publish --skip=sign --skip=snapcraft --skip=chocolatey --skip=docker | |
| # 3. Verify artifacts were created | |
| echo "📦 Verifying artifacts..." | |
| if [[ -d "dist" ]]; then | |
| BINARIES=$(find dist -name "emojify*" -type f | wc -l) | |
| ARCHIVES=$(find dist -name "*.tar.gz" -o -name "*.zip" | wc -l) | |
| echo "✅ Generated $BINARIES binaries and $ARCHIVES archives" | |
| # Show key artifacts | |
| echo "📁 Key artifacts created:" | |
| find dist -maxdepth 1 \( -name "*.tar.gz" -o -name "*.zip" -o -name "checksums.txt" \) | head -5 | sed 's/^/ - /' | |
| else | |
| echo "❌ No dist directory found" | |
| exit 1 | |
| fi | |
| echo "✅ GoReleaser test completed successfully - ready for release!" | |
| # Release with GoReleaser | |
| release: | |
| name: Release | |
| runs-on: ubuntu-latest | |
| needs: | |
| [ | |
| test, | |
| lint, | |
| security, | |
| integration, | |
| build, | |
| validate-release, | |
| goreleaser-test, | |
| ] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Install dependencies | |
| run: make deps | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # - name: Log in to Docker Hub | |
| # uses: docker/login-action@v3 | |
| # with: | |
| # username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| # password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Import GPG key | |
| uses: crazy-max/ghaction-import-gpg@v6 | |
| with: | |
| gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | |
| passphrase: ${{ secrets.GPG_PASSPHRASE }} | |
| - name: Run GoReleaser | |
| uses: goreleaser/goreleaser-action@v6 | |
| with: | |
| distribution: goreleaser | |
| version: "~> v2" | |
| args: release --clean | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} | |
| SCOOP_BUCKET_GITHUB_TOKEN: ${{ secrets.SCOOP_BUCKET_GITHUB_TOKEN }} | |
| WINGET_GITHUB_TOKEN: ${{ secrets.WINGET_GITHUB_TOKEN }} | |
| CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} | |
| AUR_KEY: ${{ secrets.AUR_KEY }} | |
| GPG_KEY_FILE: ${{ secrets.GPG_KEY_FILE }} | |
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | |
| # Update package repositories | |
| update-packages: | |
| name: Update Package Repositories | |
| runs-on: ubuntu-latest | |
| needs: [release] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Update Homebrew Formula | |
| run: | | |
| echo "Homebrew formula will be updated automatically by GoReleaser" | |
| - name: Trigger AUR Package Update | |
| run: | | |
| echo "AUR package will be updated automatically by GoReleaser" | |
| - name: Update Winget Manifest | |
| run: | | |
| echo "Winget manifest will be updated automatically by GoReleaser" | |
| # Notify on successful release | |
| notify: | |
| name: Notify Release | |
| runs-on: ubuntu-latest | |
| needs: [release, update-packages] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Get version | |
| id: version | |
| run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| - name: Create release announcement | |
| run: | | |
| echo "🚀 Emojify ${{ steps.version.outputs.version }} has been released!" | |
| echo "📦 Available on multiple package managers" | |
| echo "🍺 Homebrew: brew install damienbutt/tap/emojify" | |
| echo "🪟 Scoop: scoop install emojify" | |
| echo "🍫 Chocolatey: choco install emojify" | |
| echo "📦 WinGet: winget install damienbutt.emojify" | |
| echo "🐧 AUR: yay -S emojify-bin" | |
| echo "📸 Snap: sudo snap install emojify" |