Attio ci integration
425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill attio-ci-integrationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
'Configure CI/CD pipelines for Attio integrations with GitHub Actions,
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.4 KB, as published. Nobody here has run it
Attio CI Integration
Overview
Set up CI/CD pipelines that validate Attio integrations without burning API quota on every push. Uses MSW mocks for unit tests and gated live API tests for pre-release validation.
Prerequisites
- GitHub repository with Actions enabled
- Attio test workspace token (separate from production)
- Node.js project with vitest
Instructions
Step 1: GitHub Actions Workflow
# .github/workflows/attio-integration.yml
name: Attio Integration
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
unit-tests:
name: Unit Tests (mocked API)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
integration-tests:
name: Integration Tests (live API)
runs-on: ubuntu-latest
# Only run on main branch pushes and manual triggers
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: unit-tests
env:
ATTIO_API_KEY: ${{ secrets.ATTIO_API_KEY_TEST }}
ATTIO_LIVE: "1"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
- run: npm ci
- name: Verify Attio connectivity
run: |
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${ATTIO_API_KEY}" \
https://api.attio.com/v2/objects)
if [ "$STATUS" != "200" ]; then
echo "Attio API unreachable (HTTP $STATUS). Skipping live tests."
exit 0
fi
- run: npm run test:integration
timeout-minutes: 5
Step 2: Configure GitHub Secrets
# Use a dedicated test workspace token with minimal scopes
gh secret set ATTIO_API_KEY_TEST --body "sk_test_workspace_token"
# Optional: webhook secret for webhook handler tests
gh secret set ATTIO_WEBHOOK_SECRET_TEST --body "whsec_test_secret"
Step 3: Unit Tests with MSW Mocks
// tests/unit/attio-service.test.ts
import { describe, it, expect, beforeAll, afterAll, afterEach } from "vitest";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
const BASE = "https://api.attio.com/v2";
const server = setupServer(
http.get(`${BASE}/objects`, () =>
HttpResponse.json({
data: [
{ api_slug: "people", singular_noun: "Person" },
{ api_slug: "companies", singular_noun: "Company" },
],
})
),
http.post(`${BASE}/objects/people/records/query`, async ({ request }) => {
const body = await request.json() as Record<string, unknown>;
const limit = (body as any).limit || 10;
return HttpResponse.json({
data: Array.from({ length: Math.min(limit as number, 3) }, (_, i) => ({
id: { object_id: "obj_people", record_id: `rec_${i}` },
values: {
name: [{ full_name: `Person ${i}` }],
email_addresses: [{ email_address: `person${i}@test.com` }],
},
})),
});
}),
// Simulate rate limiting
http.post(`${BASE}/objects/companies/records`, () =>
HttpResponse.json(
{ status_code: 429, type: "rate_limit_error", code: "rate_limit_exceeded", message: "Rate limited" },
{
status: 429,
headers: { "Retry-After": new Date(Date.now() + 1000).toUTCString() },
}
)
)
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe("Attio Service", () => {
it("lists workspace objects", async () => {
const res = await fetch(`${BASE}/objects`, {
headers: { Authorization: "Bearer sk_test" },
});
const data = await res.json();
expect(data.data).toHaveLength(2);
expect(data.data[0].api_slug).toBe("people");
});
it("handles rate limit responses", async () => {
const res = await fetch(`${BASE}/objects/companies/records`, {
method: "POST",
headers: { Authorization: "Bearer sk_test", "Content-Type": "application/json" },
body: JSON.stringify({ data: { values: {} } }),
});
expect(res.status).toBe(429);
expect(res.headers.get("Retry-After")).toBeTruthy();
});
});
Step 4: Integration Tests (Live API)
// tests/integration/attio-live.test.ts
import { describe, it, expect } from "vitest";
import { AttioClient } from "../../src/attio/client";
const LIVE = process.env.ATTIO_LIVE === "1" && !!process.env.ATTIO_API_KEY;
const client = LIVE ? new AttioClient(process.env.ATTIO_API_KEY!) : null;
describe.skipIf(!LIVE)("Attio Live API", () => {
it("lists objects", async () => {
const res = await client!.get<{ data: Array<{ api_slug: string }> }>("/objects");
expect(res.data.map((o) => o.api_slug)).toContain("people");
});
it("queries people with filter", async () => {
const res = await client!.post<{ data: any[] }>(
"/objects/people/records/query",
{ limit: 1 }
);
expect(Array.isArray(res.data)).toBe(true);
});
it("lists attributes on people object", async () => {
const res = await client!.get<{ data: Array<{ api_slug: string; type: string }> }>(
"/objects/people/attributes"
);
const slugs = res.data.map((a) => a.api_slug);
expect(slugs).toContain("name");
expect(slugs).toContain("email_addresses");
});
});
Step 5: Release Workflow with Attio Smoke Test
# .github/workflows/release.yml
name: Release
on:
push:
tags: ["v*"]
jobs:
release:
runs-on: ubuntu-latest
env:
ATTIO_API_KEY: ${{ secrets.ATTIO_API_KEY_PROD }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- run: npm test
- name: Attio smoke test
run: |
curl -sf https://api.attio.com/v2/objects \
-H "Authorization: Bearer ${ATTIO_API_KEY}" \
| jq '.data | length' | xargs -I{} echo "Attio: {} objects accessible"
- run: npm run build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Error Handling
| CI Issue | Cause | Solution |
|---|---|---|
| Integration tests flaky | Attio rate limits in CI | Run live tests only on main, not PRs |
| Secret not found | Missing GitHub secret | gh secret set ATTIO_API_KEY_TEST |
| Live tests timeout | Slow API or network | Add timeout-minutes: 5 and connectivity check |
| MSW not intercepting | Version mismatch | Match MSW v2 imports (msw/node) |
Resources
Next Steps
For deployment patterns, see attio-deploy-integration.