Skip to content

docs(brainstorming): complete tmux control mode depth and console-sid… #13

docs(brainstorming): complete tmux control mode depth and console-sid…

docs(brainstorming): complete tmux control mode depth and console-sid… #13

Workflow file for this run

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