docs(brainstorming): complete tmux control mode depth and console-sid… #13
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: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| quality-gate: | |
| name: Quality Gate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: 'go.mod' | |
| - name: Install gofumpt | |
| run: go install mvdan.cc/gofumpt@v0.7.0 | |
| - name: Check formatting (gofumpt) | |
| run: | | |
| UNFORMATTED=$(gofumpt -l .) | |
| if [ -n "$UNFORMATTED" ]; then | |
| echo "::error::Files need formatting with gofumpt:" | |
| echo "$UNFORMATTED" | |
| exit 1 | |
| fi | |
| - name: Run go vet | |
| run: go vet ./... | |
| - name: Run golangci-lint | |
| uses: golangci/golangci-lint-action@v7 | |
| - name: Run tests with coverage and race detector | |
| run: go test ./... -v -count=1 -race -coverprofile=coverage.out -covermode=atomic | |
| - name: Display coverage summary | |
| run: go tool cover -func=coverage.out | |
| - name: Enforce coverage floor | |
| env: | |
| COVERAGE_THRESHOLD: '75' | |
| run: | | |
| COVERAGE=$(go tool cover -func=coverage.out | grep '^total:' | awk '{print $NF}' | tr -d '%') | |
| echo "Total coverage: ${COVERAGE}% (threshold: ${COVERAGE_THRESHOLD}%)" | |
| if [ "$(echo "$COVERAGE < $COVERAGE_THRESHOLD" | bc -l)" -eq 1 ]; then | |
| echo "::error::Coverage ${COVERAGE}% is below the ${COVERAGE_THRESHOLD}% threshold" | |
| exit 1 | |
| fi | |
| - name: Post coverage comment on PR | |
| if: github.event_name == 'pull_request' | |
| continue-on-error: true | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const { execSync } = require('child_process'); | |
| const summary = execSync('go tool cover -func=coverage.out').toString(); | |
| const totalLine = summary.split('\n').find(l => l.startsWith('total:')); | |
| const coverage = totalLine ? totalLine.match(/[\d.]+%/)?.[0] : 'unknown'; | |
| const body = `## Coverage Report\n\n**Total coverage: ${coverage}**\n\n<details>\n<summary>Package breakdown</summary>\n\n\`\`\`\n${summary}\`\`\`\n</details>`; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existing = comments.find(c => c.body.startsWith('## Coverage Report')); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| } | |
| - name: Validate build | |
| run: go build -o /dev/null ./cmd/switchboard | |
| test-docker-e2e: | |
| name: Docker E2E Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Cache Docker layers | |
| uses: actions/cache@v4 | |
| with: | |
| path: /tmp/.buildx-cache | |
| key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile.test', 'go.mod', 'go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-buildx- | |
| - name: Build Docker test image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile.test | |
| load: true | |
| tags: switchboard-test:latest | |
| cache-from: type=local,src=/tmp/.buildx-cache | |
| cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max | |
| - name: Create test-results directory | |
| run: mkdir -p test-results | |
| - name: Run Docker E2E tests | |
| run: docker compose -f docker-compose.test.yml run --rm -T test | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-e2e-results | |
| path: test-results/ | |
| retention-days: 14 | |
| # Rotate buildx cache to prevent unbounded growth | |
| - name: Rotate cache | |
| if: always() | |
| run: | | |
| rm -rf /tmp/.buildx-cache | |
| mv /tmp/.buildx-cache-new /tmp/.buildx-cache 2>/dev/null || true | |
| build-binaries: | |
| name: Build Binaries | |
| needs: quality-gate | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: 'go.mod' | |
| - name: Generate version | |
| id: version | |
| run: | | |
| VERSION="0.1.0-alpha.$(date -u +%Y%m%d).${GITHUB_SHA::7}" | |
| TAG="alpha-$(date -u +%Y%m%d)-${GITHUB_SHA::7}" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| - name: Build binaries | |
| run: | | |
| LDFLAGS="-X main.version=${{ steps.version.outputs.version }}" | |
| GOOS=darwin GOARCH=arm64 go build -ldflags "$LDFLAGS" -o switchboard-darwin-arm64 ./cmd/switchboard | |
| GOOS=darwin GOARCH=amd64 go build -ldflags "$LDFLAGS" -o switchboard-darwin-amd64 ./cmd/switchboard | |
| GOOS=linux GOARCH=amd64 go build -ldflags "$LDFLAGS" -o switchboard-linux-amd64 ./cmd/switchboard | |
| - name: Upload binaries | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: binaries | |
| path: switchboard-* | |
| retention-days: 14 | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| tag: ${{ steps.version.outputs.tag }} | |
| sign-and-notarize: | |
| name: Sign & Notarize | |
| needs: build-binaries | |
| if: github.event_name == 'push' && vars.SIGNING_ENABLED == 'true' | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Download binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| - name: Import certificates | |
| env: | |
| APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_INSTALLER_CERTIFICATE_P12: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_P12 }} | |
| APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }} | |
| run: | | |
| security create-keychain -p "" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "" build.keychain | |
| echo "$APPLE_CERTIFICATE_P12" | base64 --decode > cert.p12 | |
| security import cert.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign | |
| rm cert.p12 | |
| echo "$APPLE_INSTALLER_CERTIFICATE_P12" | base64 --decode > installer-cert.p12 | |
| security import installer-cert.p12 -k build.keychain -P "$APPLE_INSTALLER_CERTIFICATE_PASSWORD" -T /usr/bin/pkgbuild -T /usr/bin/productbuild -T /usr/bin/productsign | |
| rm installer-cert.p12 | |
| curl -sfo /tmp/DeveloperIDG2CA.cer https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer | |
| security add-certificates -k build.keychain /tmp/DeveloperIDG2CA.cer | |
| rm /tmp/DeveloperIDG2CA.cer | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain | |
| - name: Verify certificate import | |
| run: | | |
| echo "Listing signing identities:" | |
| security find-identity -v build.keychain | |
| echo "" | |
| echo "Checking for installer identity:" | |
| security find-identity -v build.keychain | grep -i "installer" || echo "WARNING: No installer identity found in keychain" | |
| - name: Sign darwin binaries | |
| env: | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| run: | | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp switchboard-darwin-arm64 | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp switchboard-darwin-amd64 | |
| - name: Verify signatures | |
| run: | | |
| codesign --verify --deep --strict switchboard-darwin-arm64 | |
| codesign --verify --deep --strict switchboard-darwin-amd64 | |
| - name: Build app bundles | |
| run: | | |
| VERSION="${{ needs.build-binaries.outputs.version }}" | |
| chmod +x scripts/create-app.sh | |
| ./scripts/create-app.sh switchboard-darwin-arm64 "$VERSION" . | |
| mv Switchboard.app Switchboard-arm64.app | |
| ./scripts/create-app.sh switchboard-darwin-amd64 "$VERSION" . | |
| mv Switchboard.app Switchboard-amd64.app | |
| - name: Sign app bundles | |
| env: | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| run: | | |
| codesign --force --deep --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp Switchboard-arm64.app | |
| codesign --force --deep --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp Switchboard-amd64.app | |
| - name: Build dmg images | |
| run: | | |
| VERSION="${{ needs.build-binaries.outputs.version }}" | |
| chmod +x scripts/create-dmg.sh | |
| ./scripts/create-dmg.sh Switchboard-arm64.app "$VERSION" switchboard-arm64.dmg | |
| ./scripts/create-dmg.sh Switchboard-amd64.app "$VERSION" switchboard-amd64.dmg | |
| - name: Build pkg installers | |
| env: | |
| APPLE_INSTALLER_IDENTITY: ${{ secrets.APPLE_INSTALLER_IDENTITY }} | |
| run: | | |
| if ! security find-identity -v build.keychain | grep -q "$APPLE_INSTALLER_IDENTITY"; then | |
| echo "::error::APPLE_INSTALLER_IDENTITY not found in keychain. Expected format: 'Developer ID Installer: Name (TeamID)'" | |
| echo "Available identities:" | |
| security find-identity -v build.keychain | |
| exit 1 | |
| fi | |
| VERSION="${{ needs.build-binaries.outputs.version }}" | |
| chmod +x scripts/create-pkg.sh | |
| ./scripts/create-pkg.sh switchboard-darwin-arm64 "$VERSION" "$APPLE_INSTALLER_IDENTITY" switchboard-arm64.pkg | |
| ./scripts/create-pkg.sh switchboard-darwin-amd64 "$VERSION" "$APPLE_INSTALLER_IDENTITY" switchboard-amd64.pkg | |
| - name: Notarize and staple all artifacts | |
| env: | |
| APPLE_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_NOTARIZATION_APPLE_ID }} | |
| APPLE_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} | |
| APPLE_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_NOTARIZATION_TEAM_ID }} | |
| run: | | |
| for ARTIFACT in switchboard-arm64.pkg switchboard-amd64.pkg switchboard-arm64.dmg switchboard-amd64.dmg; do | |
| echo "Notarizing $ARTIFACT..." | |
| xcrun notarytool submit "$ARTIFACT" \ | |
| --apple-id "$APPLE_NOTARIZATION_APPLE_ID" \ | |
| --password "$APPLE_NOTARIZATION_PASSWORD" \ | |
| --team-id "$APPLE_NOTARIZATION_TEAM_ID" \ | |
| --wait --timeout 14400 | |
| xcrun stapler staple "$ARTIFACT" | |
| done | |
| - name: Upload signed binaries and installers | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: signed-binaries | |
| path: | | |
| switchboard-darwin-arm64 | |
| switchboard-darwin-amd64 | |
| switchboard-arm64.pkg | |
| switchboard-amd64.pkg | |
| switchboard-arm64.dmg | |
| switchboard-amd64.dmg | |
| retention-days: 14 | |
| - name: Cleanup keychain | |
| if: always() | |
| run: security delete-keychain build.keychain || true | |
| release: | |
| name: Create Release | |
| needs: [build-binaries, sign-and-notarize] | |
| if: github.event_name == 'push' && !cancelled() && needs.build-binaries.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download signed binaries | |
| if: needs.sign-and-notarize.result == 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: signed-binaries | |
| - name: Download unsigned binaries (fallback) | |
| if: needs.sign-and-notarize.result != 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| - name: Download all binaries (for linux) | |
| if: needs.sign-and-notarize.result == 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| path: unsigned-binaries | |
| - name: Copy linux binary from unsigned artifacts | |
| if: needs.sign-and-notarize.result == 'success' | |
| run: cp unsigned-binaries/switchboard-linux-amd64 . && rm -rf unsigned-binaries | |
| - name: List release files | |
| run: ls -la switchboard-* *.pkg *.dmg 2>/dev/null || true | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.build-binaries.outputs.tag }} | |
| name: Alpha ${{ needs.build-binaries.outputs.tag }} | |
| body: | | |
| Automated alpha release from merge to main. | |
| **Version:** ${{ needs.build-binaries.outputs.version }} | |
| **Commit:** ${{ github.sha }} | |
| **Signed:** ${{ needs.sign-and-notarize.result == 'success' && 'Yes (Apple Developer ID)' || 'No (unsigned)' }} | |
| **Binaries:** | |
| - `switchboard-darwin-arm64` — macOS Apple Silicon | |
| - `switchboard-darwin-amd64` — macOS Intel | |
| - `switchboard-linux-amd64` — Linux x86_64 | |
| ${{ needs.sign-and-notarize.result == 'success' && '- `switchboard-arm64.pkg` — macOS Installer (Apple Silicon)\n- `switchboard-amd64.pkg` — macOS Installer (Intel)\n- `switchboard-arm64.dmg` — macOS Disk Image (Apple Silicon)\n- `switchboard-amd64.dmg` — macOS Disk Image (Intel)' || '' }} | |
| prerelease: true | |
| files: | | |
| switchboard-darwin-arm64 | |
| switchboard-darwin-amd64 | |
| switchboard-linux-amd64 | |
| switchboard-arm64.pkg | |
| switchboard-amd64.pkg | |
| switchboard-arm64.dmg | |
| switchboard-amd64.dmg | |
| update-homebrew: | |
| name: Update Homebrew Tap | |
| needs: [build-binaries, release] | |
| if: github.event_name == 'push' && needs.release.result == 'success' && vars.SIGNING_ENABLED == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Download signed binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: signed-binaries | |
| - name: Update Homebrew formula | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.build-binaries.outputs.version }}" | |
| TAG="${{ needs.build-binaries.outputs.tag }}" | |
| SHA256_ARM64=$(shasum -a 256 switchboard-darwin-arm64 | cut -d' ' -f1) | |
| SHA256_AMD64=$(shasum -a 256 switchboard-darwin-amd64 | cut -d' ' -f1) | |
| git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/arcavenae/homebrew-tap.git" homebrew-tap-repo | |
| cd homebrew-tap-repo | |
| mkdir -p Formula | |
| cp ../Formula/switchboard.rb Formula/switchboard.rb | |
| sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" Formula/switchboard.rb | |
| sed -i "s/TAG_PLACEHOLDER/$TAG/g" Formula/switchboard.rb | |
| sed -i "s/SHA256_ARM64_PLACEHOLDER/$SHA256_ARM64/g" Formula/switchboard.rb | |
| sed -i "s/SHA256_AMD64_PLACEHOLDER/$SHA256_AMD64/g" Formula/switchboard.rb | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/switchboard.rb | |
| git commit -m "Update switchboard to $VERSION" | |
| git push |