Claude-code-plugins-plus-skills flyio-ci-integration

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/flyio-pack/skills/flyio-ci-integration" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-flyio-ci-integration && rm -rf "$T"
manifest: plugins/saas-packs/flyio-pack/skills/flyio-ci-integration/SKILL.md
source content

Fly.io CI Integration

Overview

Set up CI/CD for Fly.io edge deployments: run unit tests on every PR, deploy to staging on pull requests, and promote to production on merge to main. Fly.io uses Machines API for app management and deploy tokens for scoped CI authentication. CI pipelines build Docker images, deploy via

flyctl
, and run post-deploy health checks against the edge endpoints.

GitHub Actions Workflow

# .github/workflows/fly-ci.yml
name: Fly.io CI
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm test -- --reporter=verbose

  deploy:
    if: github.ref == 'refs/heads/main'
    needs: unit-tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: fly deploy --ha=false
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
      - name: Health check
        run: |
          sleep 10
          curl -sf https://my-app.fly.dev/health || exit 1

Mock-Based Unit Tests

// tests/fly-service.test.ts
import { describe, it, expect, vi } from 'vitest';
import { scaleApp } from '../src/fly-service';

vi.mock('../src/fly-client', () => ({
  FlyClient: vi.fn().mockImplementation(() => ({
    listMachines: vi.fn().mockResolvedValue([
      { id: 'mch_abc', state: 'started', region: 'iad', config: { size: 'shared-cpu-1x' } },
      { id: 'mch_def', state: 'started', region: 'lhr', config: { size: 'shared-cpu-1x' } },
    ]),
    scaleMachine: vi.fn().mockResolvedValue({ id: 'mch_abc', state: 'started' }),
    getApp: vi.fn().mockResolvedValue({ name: 'my-app', status: 'deployed', hostname: 'my-app.fly.dev' }),
  })),
}));

describe('Fly.io Service', () => {
  it('scales app machines across regions', async () => {
    const result = await scaleApp('my-app', { count: 3 });
    expect(result.machines).toBeDefined();
    expect(result.status).toBe('scaled');
  });
});

Integration Tests

// tests/integration/fly.integration.test.ts
import { describe, it, expect } from 'vitest';

const hasToken = !!process.env.FLY_API_TOKEN;

describe.skipIf(!hasToken)('Fly.io Live API', () => {
  it('lists apps via Machines API', async () => {
    const res = await fetch('https://api.machines.dev/v1/apps', {
      headers: { Authorization: `Bearer ${process.env.FLY_API_TOKEN}` },
    });
    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toHaveProperty('apps');
  });
});

Error Handling

CI IssueCauseFix
FLY_API_TOKEN
invalid
Token expired or revokedRegenerate with
fly tokens create deploy -a my-app
Deploy timeoutImage build too slowAdd Docker layer caching with
--build-cache
Health check failsApp not ready after deployIncrease sleep or use
fly status --wait
Machine stuck in
replacing
Rolling deploy conflictRun
fly machines list
and destroy orphaned machines
Region unavailableEdge region at capacitySet
primary_region
in
fly.toml
to a different region

Resources

Next Steps

For deployment strategies, see

flyio-deploy-integration
.