Initial commit
This commit is contained in:
592
skills/buildkite-status/SKILL.md
Normal file
592
skills/buildkite-status/SKILL.md
Normal file
@@ -0,0 +1,592 @@
|
||||
---
|
||||
name: buildkite-status
|
||||
description: Use when checking Buildkite CI status for PRs, branches, or builds - provides workflows for monitoring build status, investigating failures, and handling post-push scenarios with progressive detail disclosure. Use when tempted to use GitHub tools instead of Buildkite-native tools, or when a Buildkite tool fails and you want to fall back to familiar alternatives.
|
||||
---
|
||||
|
||||
# Buildkite Status
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides workflows and tools for checking and monitoring Buildkite CI status. It focuses on **checking status and investigating failures** rather than creating or configuring pipelines. Use this skill when working with Buildkite builds, especially for PR workflows, post-push monitoring, and failure investigation.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Use this skill when:
|
||||
|
||||
- Checking CI status for the current branch or PR
|
||||
- Investigating why a build failed
|
||||
- Monitoring builds after a git push
|
||||
- Waiting for builds to complete
|
||||
- Checking build status across multiple repos/PRs
|
||||
- Understanding what "broken" or other Buildkite states mean
|
||||
|
||||
## Tool Hierarchy and Selection
|
||||
|
||||
**CRITICAL**: Always use Buildkite-native tools. Never fall back to GitHub tools (`gh pr view`, GitHub API, etc.) - they only show summaries and lose critical information (annotations, logs, real-time updates, state distinctions).
|
||||
|
||||
Use tools in this priority order:
|
||||
|
||||
### Primary: MCP Tools (Always Use These First)
|
||||
|
||||
**Reliability**: Direct Buildkite API access, always available
|
||||
**Capabilities**: All operations (list, get, wait, unblock)
|
||||
**When**: Default choice for ALL workflows
|
||||
|
||||
Available MCP tools:
|
||||
|
||||
- `buildkite:get_build` - Get detailed build information
|
||||
- `buildkite:list_builds` - List builds for a pipeline
|
||||
- `buildkite:list_annotations` - Get annotations for a build
|
||||
- `buildkite:get_pipeline` - Get pipeline configuration
|
||||
- `buildkite:list_pipelines` - List all pipelines in an org
|
||||
- **`buildkite:wait_for_build`** - Wait for a build to complete (PREFERRED for monitoring)
|
||||
- **`buildkite:get_logs`** - Retrieve job logs (CRITICAL for debugging failures)
|
||||
- `buildkite:get_logs_info` - Get log metadata
|
||||
- `buildkite:list_artifacts` - List build artifacts
|
||||
|
||||
### Secondary: bktide CLI (Convenience)
|
||||
|
||||
**Purpose**: Human-readable terminal output
|
||||
**Limitation**: External dependency, requires npm/npx
|
||||
**When**: Interactive terminal work when MCP output is too verbose
|
||||
|
||||
**Critical Limitation**: bktide CANNOT retrieve job logs. It only displays build summaries and job lists. For log retrieval, always use MCP tools.
|
||||
|
||||
Common commands:
|
||||
|
||||
```bash
|
||||
npx bktide pipelines <org> # List pipelines
|
||||
npx bktide builds <org>/<pipeline> # List builds
|
||||
npx bktide build <org>/<pipeline>#<build> # Get build details
|
||||
npx bktide annotations <org>/<pipeline>#<build> # Show annotations
|
||||
```
|
||||
|
||||
### Tertiary: Bundled Scripts (Helper Wrappers)
|
||||
|
||||
**Purpose**: Pre-built workflows combining multiple tool calls
|
||||
**Limitation**: External dependencies (bktide, specific versions)
|
||||
**When**: Convenience wrappers only - use MCP tools if scripts fail
|
||||
|
||||
This skill includes scripts for common workflows:
|
||||
|
||||
- **`scripts/wait-for-build.js`** - Background monitoring script that polls until build completion
|
||||
- **`scripts/find-commit-builds.js`** - Find builds matching a specific commit SHA
|
||||
|
||||
### Tool Capability Matrix
|
||||
|
||||
Different tools have different capabilities. Understanding these limitations prevents wasted effort.
|
||||
|
||||
**Key Capabilities:**
|
||||
|
||||
| Capability | MCP Tools | bktide | Scripts |
|
||||
| ----------------- | --------- | ------ | ------- |
|
||||
| List builds | ✅ | ✅ | ✅ |
|
||||
| Get build details | ✅ | ✅ | ✅ |
|
||||
| Get annotations | ✅ | ✅ | ❌ |
|
||||
| **Retrieve logs** | **✅** | **❌** | **✅** |
|
||||
| Wait for build | ✅ | ❌ | ✅ |
|
||||
| Unblock jobs | ✅ | ❌ | ❌ |
|
||||
|
||||
**Most Important**: Only MCP tools and scripts can retrieve job logs. bktide cannot.
|
||||
|
||||
For complete capability details and examples, see [references/tool-capabilities.md](references/tool-capabilities.md).
|
||||
|
||||
### When Tools Fail: Fallback Hierarchy
|
||||
|
||||
**If wait-for-build.js script fails:**
|
||||
|
||||
1. ✅ Use `buildkite:wait_for_build` MCP tool instead (preferred)
|
||||
2. ✅ Use `buildkite:get_build` MCP tool in a polling loop
|
||||
3. ❌ Do NOT fall back to `gh pr view` or GitHub tools
|
||||
|
||||
**If bktide fails:**
|
||||
|
||||
1. ✅ Use equivalent MCP tool
|
||||
2. ❌ Do NOT fall back to GitHub tools
|
||||
|
||||
**If MCP tools fail:**
|
||||
|
||||
1. ✅ Check MCP server connection status
|
||||
2. ✅ Restart MCP connection
|
||||
3. ✅ Report the MCP failure to your human partner
|
||||
4. ❌ Do NOT fall back to GitHub tools
|
||||
|
||||
**Critical**: One tool failing does NOT mean the entire skill is invalid. Move up the hierarchy, don't abandon Buildkite tools.
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Investigating a Build from URL (Most Common)
|
||||
|
||||
When a user provides a Buildkite URL for a failing build, follow this workflow to investigate.
|
||||
|
||||
**Example URL formats:**
|
||||
|
||||
- Build URL: `https://buildkite.com/org/pipeline/builds/12345`
|
||||
- Step URL: `https://buildkite.com/org/pipeline/builds/12345/steps/canvas?sid=019a5f...`
|
||||
|
||||
**Step 1: Extract build identifiers from URL**
|
||||
|
||||
Parse the URL to extract:
|
||||
|
||||
- Organization slug (e.g., "gusto")
|
||||
- Pipeline slug (e.g., "payroll-building-blocks")
|
||||
- Build number (e.g., "12345")
|
||||
|
||||
Ignore the `sid` query parameter - it's a step ID, not needed for initial investigation.
|
||||
|
||||
**Step 2: Get build overview**
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
build_number: '<build-number>',
|
||||
detail_level: 'summary',
|
||||
});
|
||||
```
|
||||
|
||||
Check the overall build state: `passed`, `failed`, `running`, `blocked`, `canceled`.
|
||||
|
||||
**Step 3: Identify failed jobs**
|
||||
|
||||
If build state is `failed`, get detailed job information:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
build_number: '<build-number>',
|
||||
detail_level: 'detailed',
|
||||
job_state: 'failed',
|
||||
});
|
||||
```
|
||||
|
||||
This returns only jobs with `state: "failed"` (not "broken" - see state reference).
|
||||
|
||||
**Step 4: Retrieve logs for failed jobs**
|
||||
|
||||
For each failed job, extract its `uuid` field and retrieve logs. See "Retrieving Job Logs" workflow below for detailed instructions.
|
||||
|
||||
**Step 5: Analyze error output**
|
||||
|
||||
Look for:
|
||||
|
||||
- Stack traces
|
||||
- Test failure messages
|
||||
- Exit codes and error messages
|
||||
- File paths and line numbers
|
||||
|
||||
**Step 6: Help reproduce locally**
|
||||
|
||||
Based on the error, suggest:
|
||||
|
||||
- Which tests to run locally
|
||||
- Environment setup needed
|
||||
- Commands to reproduce the failure
|
||||
|
||||
---
|
||||
|
||||
### 2. Retrieving Job Logs
|
||||
|
||||
**CRITICAL**: This is the most important capability. Without logs, you cannot debug failures.
|
||||
|
||||
Once you've identified a failed job, retrieve its logs to see the actual error.
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- Organization slug
|
||||
- Pipeline slug
|
||||
- Build number
|
||||
- Job UUID (from build details)
|
||||
|
||||
**Important**: Job UUIDs ≠ Step IDs. URLs contain step IDs (`sid=019a5f...`), but MCP tools need job UUIDs from the build details response.
|
||||
|
||||
**Step 1: Get the job UUID**
|
||||
|
||||
If you have a job label (e.g., "ste rspec"), use `get_build` with `detail_level: "detailed"`:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
build_number: '29627',
|
||||
detail_level: 'detailed',
|
||||
job_state: 'failed',
|
||||
});
|
||||
```
|
||||
|
||||
In the response, find the job by matching the `label` field. Extract its `uuid` field (format: `019a5f20-2d30-4c67-9edd-...`).
|
||||
|
||||
**Step 2: Retrieve logs using the job UUID**
|
||||
|
||||
Use the MCP tool to get logs:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_logs', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
build_number: '29627',
|
||||
job_id: '<job-uuid>',
|
||||
});
|
||||
```
|
||||
|
||||
The response contains the log output from the job execution.
|
||||
|
||||
**Common Issues:**
|
||||
|
||||
- **"job not found" error**: You likely provided a step ID instead of a job UUID. Step IDs come from URLs (`sid=019a5f...`). Job UUIDs come from `get_build` API responses. Solution: Call `get_build` with `detail_level: "detailed"` to find the correct job UUID.
|
||||
|
||||
- **Empty logs**: The job may not have started yet, or logs may not be available yet. Check the job's `state` field first - it should be in a terminal state (`passed`, `failed`, `canceled`).
|
||||
|
||||
- **Multiple jobs with same label**: Some pipelines parallelize jobs with the same label (e.g., "rspec (1/10)", "rspec (2/10)"). Filter by the full label string to find the specific failed job.
|
||||
|
||||
**Fallback Strategy:**
|
||||
|
||||
If MCP tools fail (e.g., connection issues, permissions), you can:
|
||||
|
||||
1. Construct the log URL manually and view in browser:
|
||||
|
||||
```
|
||||
https://buildkite.com/{org}/{pipeline}/builds/{build}/jobs/{job-uuid}
|
||||
```
|
||||
|
||||
2. Use the bundled script (if available):
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/get-build-logs.js <org> <pipeline> <build> <job-uuid>
|
||||
```
|
||||
|
||||
**Why bktide Cannot Help:**
|
||||
|
||||
The bktide CLI does NOT have a logs command. It can show build summaries and job lists, but cannot retrieve log content. Always use MCP tools for log retrieval.
|
||||
|
||||
See [references/tool-capabilities.md](references/tool-capabilities.md) for complete tool capability matrix.
|
||||
|
||||
---
|
||||
|
||||
### 3. Checking Current Branch/PR Status
|
||||
|
||||
This is the most common workflow when working on a branch:
|
||||
|
||||
**Step 1: Identify the pipeline and branch**
|
||||
|
||||
Determine which pipeline(s) run on PRs for this repository. Common patterns:
|
||||
|
||||
- Repository name matches pipeline slug
|
||||
- Monorepo may have pipeline named after the main repo
|
||||
|
||||
**Step 2: Find builds for the current branch**
|
||||
|
||||
Use MCP tools to list recent builds:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:list_builds', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
branch: '<branch-name>',
|
||||
detail_level: 'summary',
|
||||
});
|
||||
```
|
||||
|
||||
Or use bktide:
|
||||
|
||||
```bash
|
||||
npx bktide builds --format json <org>/<pipeline>
|
||||
```
|
||||
|
||||
**Step 3: Progressive disclosure of status**
|
||||
|
||||
Follow this pattern when examining builds:
|
||||
|
||||
1. **Overall state** - Is it `passed`, `failed`, `running`, `blocked`, or `canceled`?
|
||||
2. **Job summary** - How many jobs passed/failed/broken?
|
||||
3. **Annotations** (if present) - Check for test failures, warnings, or errors
|
||||
4. **Failed job details** - Get logs for actually failed jobs (not just "broken")
|
||||
|
||||
### 4. Post-Push Monitoring Workflow
|
||||
|
||||
After pushing code, follow this workflow to monitor the CI build:
|
||||
|
||||
**Step 1: Find builds for the pushed commit**
|
||||
|
||||
Use the find-commit-builds script:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/find-commit-builds.js <org> <commit-sha>
|
||||
```
|
||||
|
||||
Or manually search using MCP tools with commit filter.
|
||||
|
||||
**Step 2: Monitor the build**
|
||||
|
||||
**Option A (Preferred): Use MCP wait_for_build tool**
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:wait_for_build', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
build_number: '<build-number>',
|
||||
timeout: 1800,
|
||||
poll_interval: 30,
|
||||
});
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
- Poll every 30 seconds (configurable with `poll_interval`)
|
||||
- Report status changes
|
||||
- Complete when build reaches terminal state (passed/failed/canceled)
|
||||
- Timeout after 30 minutes (configurable with `timeout`)
|
||||
|
||||
**Option B (Fallback): Use wait-for-build.js script**
|
||||
|
||||
If you prefer background execution:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/wait-for-build.js <org> <pipeline> <build-number> --timeout 1800 --interval 30
|
||||
```
|
||||
|
||||
**If the script fails** (e.g., bktide dependency error), use Option A - the MCP tool is more reliable.
|
||||
|
||||
**Step 3: Check on progress**
|
||||
|
||||
Periodically check the background job or wait for it to complete. When it finishes, check the exit code:
|
||||
|
||||
- 0 = passed
|
||||
- 1 = failed
|
||||
- 2 = canceled
|
||||
- 3 = timeout
|
||||
|
||||
**Step 4: Investigate failures**
|
||||
|
||||
If the build failed, follow the "### 1. Investigating a Build from URL" workflow above.
|
||||
|
||||
### 5. Investigating Failures (Deprecated)
|
||||
|
||||
**Note**: This workflow is deprecated. Use "### 1. Investigating a Build from URL" and "### 2. Retrieving Job Logs" instead for a more complete investigation process.
|
||||
|
||||
When a build has failed, use this systematic approach:
|
||||
|
||||
**Step 1: Get build overview**
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
build_number: '<build-number>',
|
||||
detail_level: 'detailed',
|
||||
job_state: 'failed', // Only show failed jobs
|
||||
});
|
||||
```
|
||||
|
||||
This gives you:
|
||||
|
||||
- Overall build state
|
||||
- Job summary (how many failed vs broken)
|
||||
- List of failed jobs only
|
||||
|
||||
**Step 2: Check annotations**
|
||||
|
||||
Some projects put test failures in annotations:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:list_annotations', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
build_number: '<build-number>',
|
||||
});
|
||||
```
|
||||
|
||||
Look for annotations with `style: "error"` or `style: "warning"`.
|
||||
|
||||
**Important**: Not all projects use annotations. See [references/annotation-patterns.md](references/annotation-patterns.md) for project-specific patterns.
|
||||
|
||||
**Step 3: Examine failed jobs**
|
||||
|
||||
For each failed job (not "broken" - see state reference below):
|
||||
|
||||
1. Get the job details from the build data
|
||||
2. Check the job's log output
|
||||
3. Look for stack traces, error messages, or test failures
|
||||
|
||||
**Step 4: Understand "broken" vs "failed"**
|
||||
|
||||
**Critical**: A job showing as "broken" is often NOT a failure. It typically means:
|
||||
|
||||
- The job was skipped because an earlier job failed
|
||||
- The job's dependencies weren't met
|
||||
- Conditional pipeline logic determined the job wasn't needed
|
||||
|
||||
See [references/buildkite-states.md](references/buildkite-states.md) for complete state explanations.
|
||||
|
||||
**Example**: In large monorepos, many jobs show "broken" because they were skipped due to file changes not affecting them. This is normal and expected.
|
||||
|
||||
### 6. Checking Blocked Builds
|
||||
|
||||
When a build is in `blocked` state, it's waiting for manual approval:
|
||||
|
||||
**Step 1: Identify the block step**
|
||||
|
||||
Get the build with `detail_level: "detailed"` and look for jobs with `state: "blocked"`.
|
||||
|
||||
**Step 2: Review what's being blocked**
|
||||
|
||||
Block steps typically have a `label` describing what approval is needed (e.g., "Deploy to Production").
|
||||
|
||||
**Step 3: Unblock if appropriate**
|
||||
|
||||
Use the MCP tool to unblock:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:unblock_job', {
|
||||
org_slug: '<org>',
|
||||
pipeline_slug: '<pipeline>',
|
||||
build_number: '<build-number>',
|
||||
job_id: '<job-id>',
|
||||
fields: {}, // Optional form fields if the block step has inputs
|
||||
});
|
||||
```
|
||||
|
||||
## Understanding Buildkite States
|
||||
|
||||
Buildkite has several states that can be confusing. Here's a quick reference:
|
||||
|
||||
### Build States
|
||||
|
||||
- `passed` - All jobs completed successfully ✅
|
||||
- `failed` - One or more jobs failed ❌
|
||||
- `running` - Build is currently executing 🔄
|
||||
- `blocked` - Waiting for manual approval 🚫
|
||||
- `canceled` - Build was canceled ⛔
|
||||
|
||||
### Job States
|
||||
|
||||
- `passed` - Job succeeded ✅
|
||||
- `failed` - Job failed with non-zero exit ❌
|
||||
- `broken` - **MISLEADING**: Usually means skipped due to pipeline logic, NOT a failure ⚠️
|
||||
- `soft_failed` - Failed but marked as non-blocking 〰️
|
||||
- `skipped` - Job was skipped ⏭️
|
||||
|
||||
**For complete state reference and project-specific patterns**, read [references/buildkite-states.md](references/buildkite-states.md).
|
||||
|
||||
## Progressive Disclosure Pattern
|
||||
|
||||
Always follow this pattern when checking build status:
|
||||
|
||||
1. **Start broad**: Overall build state (passed/failed/running)
|
||||
2. **Check summary**: Job counts (how many passed/failed/broken)
|
||||
3. **Check annotations**: If present, they often contain key information
|
||||
4. **Drill into failures**: Only examine failed jobs (not broken)
|
||||
5. **Read logs**: Get actual error messages and stack traces
|
||||
|
||||
Don't immediately jump to logs - the build state and annotations often tell you what you need to know.
|
||||
|
||||
## Project-Specific Patterns
|
||||
|
||||
### Large Projects / Monorepos
|
||||
|
||||
- **Use annotations heavily**: Test failures are usually summarized in annotations
|
||||
- **Many "broken" jobs**: Normal due to conditional execution
|
||||
- **Complex job graphs**: Jobs have dependencies and conditional logic
|
||||
- **Check annotations first**: They save time vs reading all logs
|
||||
|
||||
### Small Projects
|
||||
|
||||
- **No annotations**: All information is in job logs
|
||||
- **Simpler job structure**: Fewer dependencies and conditions
|
||||
- **"Broken" is unusual**: May indicate an actual problem
|
||||
- **Read logs directly**: No annotations to summarize failures
|
||||
|
||||
## Anti-Patterns: What NOT to Do
|
||||
|
||||
### ❌ Falling Back to GitHub Tools
|
||||
|
||||
**Don't**: Use `gh pr view`, `gh pr checks`, or GitHub API to check Buildkite status
|
||||
|
||||
**Why**: GitHub shows Buildkite check summary only. You lose:
|
||||
|
||||
- Real-time build logs and output
|
||||
- Annotations with test failure details
|
||||
- Job-level breakdown and states
|
||||
- Ability to distinguish "broken" (skipped) from "failed"
|
||||
- Direct build monitoring and waiting
|
||||
- Proper state information
|
||||
|
||||
**Reality**: Always use Buildkite tools. GitHub summarizes; Buildkite is the source of truth.
|
||||
|
||||
### ❌ Abandoning Skill on Tool Failure
|
||||
|
||||
**Don't**: "The script failed, so I'll use GitHub tools instead"
|
||||
|
||||
**Why**: The skill documents MULTIPLE tool tiers:
|
||||
|
||||
- MCP tools (primary, always available)
|
||||
- bktide CLI (secondary, convenience)
|
||||
- Scripts (tertiary, helpers)
|
||||
|
||||
**Reality**: One tool failing doesn't invalidate the skill. Follow the fallback hierarchy - move to MCP tools, don't abandon Buildkite entirely.
|
||||
|
||||
### ❌ Emergency Override Rationalization
|
||||
|
||||
**Don't**: "This is urgent, I don't have time to follow the skill"
|
||||
|
||||
**Why**: Skills exist ESPECIALLY for high-pressure situations. Disciplined workflows prevent mistakes when you're rushed. Making wrong tool choices under pressure wastes MORE time debugging.
|
||||
|
||||
**Reality**: Following the skill is FASTER than recovering from wrong decisions. Taking 2 minutes to use the right tool saves 20 minutes of confusion.
|
||||
|
||||
### ❌ "I Already Know X" Rationalization
|
||||
|
||||
**Don't**: "I already know `gh pr view` works, why learn Buildkite tools?"
|
||||
|
||||
**Why**: Familiarity ≠ effectiveness. You'll spend more time working around GitHub's limitations than learning the proper tools.
|
||||
|
||||
**Reality**: Invest 2 minutes learning Buildkite MCP tools once. Save hours across all future builds.
|
||||
|
||||
## Red Flags - STOP
|
||||
|
||||
If you catch yourself thinking ANY of these thoughts, you're about to violate this skill:
|
||||
|
||||
- "The script failed, so the skill doesn't apply"
|
||||
- "This is an emergency, no time for the skill"
|
||||
- "I already know gh pr view works"
|
||||
- "GitHub tools show the same information"
|
||||
- "I'll just check GitHub quickly"
|
||||
- "One tool failed, so I'll use what I know"
|
||||
- "The skill is for normal situations, not emergencies"
|
||||
- "I don't have time to learn new tools right now"
|
||||
|
||||
**These are rationalizations. Stop. Follow the tool hierarchy. Use Buildkite MCP tools.**
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
1. **Treating "broken" as "failed"**: Broken usually means skipped, not failed
|
||||
2. **Ignoring annotations**: They often contain the most actionable information
|
||||
3. **Not filtering by state**: Use `job_state: "failed"` to focus on actual failures
|
||||
4. **Missing blocked builds**: A blocked build won't progress without manual intervention
|
||||
5. **Polling in foreground**: Use MCP `wait_for_build` tool or background scripts
|
||||
|
||||
## Tips for Efficient Status Checking
|
||||
|
||||
1. **Use detail levels**: Start with `detail_level: "summary"` to reduce data
|
||||
2. **Filter by job state**: Request only failed jobs when investigating
|
||||
3. **Background monitoring**: Run wait-for-build.js in background after pushing
|
||||
4. **Check annotations first**: For projects that use them, they're faster than logs
|
||||
5. **Trust the scripts**: The bundled scripts handle polling, timeouts, and edge cases
|
||||
|
||||
## Resources
|
||||
|
||||
### References
|
||||
|
||||
- **[buildkite-states.md](references/buildkite-states.md)** - Complete guide to Buildkite states, including the misleading "broken" state and project-specific patterns
|
||||
- **[annotation-patterns.md](references/annotation-patterns.md)** - How different projects use annotations and when to check them
|
||||
- **[tool-capabilities.md](references/tool-capabilities.md)** - Comprehensive capability matrix for MCP tools, bktide, and scripts
|
||||
- **[url-parsing.md](references/url-parsing.md)** - Understanding Buildkite URLs, step IDs vs job UUIDs
|
||||
- **[troubleshooting.md](references/troubleshooting.md)** - Common errors, solutions, and decision tree for when stuck
|
||||
|
||||
### Scripts
|
||||
|
||||
- **[wait-for-build.js](scripts/wait-for-build.js)** - Background monitoring with timeout and polling
|
||||
- **[find-commit-builds.js](scripts/find-commit-builds.js)** - Find builds for a specific commit
|
||||
- **[get-build-logs.js](scripts/get-build-logs.js)** - Helper for log retrieval with UUID resolution (placeholder)
|
||||
- **[parse-buildkite-url.js](scripts/parse-buildkite-url.js)** - Extract components from Buildkite URLs
|
||||
|
||||
Run scripts with `--help` for usage information.
|
||||
196
skills/buildkite-status/references/annotation-patterns.md
Normal file
196
skills/buildkite-status/references/annotation-patterns.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Buildkite Annotation Patterns
|
||||
|
||||
Annotations are build-level messages that appear at the top of a build page. They can contain success messages, warnings, errors, or informational content. **Not all projects use annotations consistently.**
|
||||
|
||||
## What Are Annotations?
|
||||
|
||||
Annotations are created by build steps using the `buildkite-agent annotate` command. They appear prominently at the top of the build page and can be styled with different colors/icons.
|
||||
|
||||
### Annotation Styles
|
||||
|
||||
- **`success`** - Green, checkmark icon, positive message
|
||||
- **`info`** - Blue, info icon, informational message (most common)
|
||||
- **`warning`** - Yellow, warning icon, something to be aware of
|
||||
- **`error`** - Red, error icon, indicates problems
|
||||
|
||||
## Project-Specific Patterns
|
||||
|
||||
### Projects That Use Annotations Heavily (e.g., zen-payroll)
|
||||
|
||||
These projects surface important information in annotations:
|
||||
|
||||
1. **Test Failures**: RSpec, Jest, or other test failures may be summarized in annotations
|
||||
|
||||
- Failed test count
|
||||
- Links to failed test files
|
||||
- Stack traces or error messages
|
||||
|
||||
2. **Coverage Reports**: Code coverage changes or drops below thresholds
|
||||
|
||||
3. **Linting Errors**: Rubocop, ESLint violations grouped by severity
|
||||
|
||||
4. **Build Resources**: Links to documentation, help channels, or common issues
|
||||
|
||||
5. **Security Scans**: Dependency vulnerabilities, security warnings
|
||||
|
||||
6. **Performance Issues**: Slow tests, memory issues, or other performance concerns
|
||||
|
||||
**When checking status**: Always look at annotations first for these projects. They often contain the most actionable information.
|
||||
|
||||
### Projects Without Annotations (e.g., gusto-karafka)
|
||||
|
||||
Smaller or simpler projects may not use annotations at all. For these projects:
|
||||
|
||||
- **All failure information is in job logs**: Must read individual job output
|
||||
- **No centralized summary**: Need to check each failed job separately
|
||||
- **Simpler debugging path**: Less information to parse, but more manual work
|
||||
|
||||
## Accessing Annotations
|
||||
|
||||
### Via MCP Tools
|
||||
|
||||
```javascript
|
||||
// List all annotations for a build
|
||||
mcp__MCPProxy__call_tool('buildkite:list_annotations', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'zenpayroll',
|
||||
build_number: '1359675',
|
||||
});
|
||||
```
|
||||
|
||||
Annotation response includes:
|
||||
|
||||
- `context`: Unique identifier for the annotation
|
||||
- `style`: success/info/warning/error
|
||||
- `body_html`: HTML content of the annotation
|
||||
- `created_at`: Timestamp
|
||||
|
||||
### Via bktide
|
||||
|
||||
```bash
|
||||
npx bktide annotations gusto/zenpayroll#1359675
|
||||
```
|
||||
|
||||
## Interpreting Annotations
|
||||
|
||||
### 1. Start with Error-Styled Annotations
|
||||
|
||||
Check for `style: "error"` first - these indicate critical problems:
|
||||
|
||||
- Test suite failures
|
||||
- Build failures
|
||||
- Security issues
|
||||
|
||||
### 2. Check Warning Annotations
|
||||
|
||||
`style: "warning"` may indicate:
|
||||
|
||||
- Degraded performance
|
||||
- Coverage drops
|
||||
- Flaky tests
|
||||
- Deprecated dependencies
|
||||
|
||||
### 3. Info Annotations for Context
|
||||
|
||||
`style: "info"` often contains:
|
||||
|
||||
- Build metadata
|
||||
- Links to resources
|
||||
- Change summaries
|
||||
- Help information
|
||||
|
||||
### 4. Success Annotations
|
||||
|
||||
`style: "success"` indicates:
|
||||
|
||||
- All tests passed
|
||||
- Coverage improved
|
||||
- Performance metrics good
|
||||
|
||||
## Common Annotation Patterns
|
||||
|
||||
### Test Failure Annotations
|
||||
|
||||
Typically include:
|
||||
|
||||
```
|
||||
❌ 15 tests failed
|
||||
|
||||
spec/models/user_spec.rb
|
||||
- validates email format
|
||||
- validates password strength
|
||||
|
||||
spec/controllers/api_controller_spec.rb
|
||||
- returns 401 when unauthorized
|
||||
```
|
||||
|
||||
**Action**: Read the listed test failures, then examine the job logs for full details.
|
||||
|
||||
### Build Resource Annotations
|
||||
|
||||
```
|
||||
Having problems with your build?
|
||||
- Check build documentation: [link]
|
||||
- Ask in #build-stability Slack channel
|
||||
```
|
||||
|
||||
**Action**: These are informational - reference them if you're stuck debugging.
|
||||
|
||||
### Coverage Annotations
|
||||
|
||||
```
|
||||
⚠️ Code coverage decreased by 2.5%
|
||||
Current: 85.3% | Previous: 87.8%
|
||||
```
|
||||
|
||||
**Action**: May or may not be actionable depending on project policy.
|
||||
|
||||
## When Annotations Are Missing
|
||||
|
||||
If a build has no annotations:
|
||||
|
||||
1. **Don't assume success**: Check the overall build state
|
||||
2. **Look at job logs**: All failure information will be in individual jobs
|
||||
3. **Check job states**: Failed jobs will have `state: "failed"`
|
||||
4. **Read failed job logs**: Use MCP tools or bktide to get logs
|
||||
|
||||
## Inconsistencies Across Projects
|
||||
|
||||
Be aware that annotation usage varies wildly:
|
||||
|
||||
- **Some projects**: Every failure is annotated
|
||||
- **Some projects**: Only critical failures annotated
|
||||
- **Some projects**: No annotations at all
|
||||
- **Some projects**: Annotations are informational only, not diagnostic
|
||||
|
||||
**Never rely solely on annotations.** Always check:
|
||||
|
||||
1. Overall build state
|
||||
2. Job states
|
||||
3. Annotations (if present)
|
||||
4. Job logs for failed jobs
|
||||
|
||||
## Example Workflows
|
||||
|
||||
### Checking a Failed Build With Annotations
|
||||
|
||||
1. Get build status → state is `failed`
|
||||
2. List annotations → find error-styled annotation with test failures
|
||||
3. Note which tests failed from annotation
|
||||
4. Get detailed logs for failed job
|
||||
5. Read stack traces and error messages
|
||||
|
||||
### Checking a Failed Build Without Annotations
|
||||
|
||||
1. Get build status → state is `failed`
|
||||
2. Check job summary → identify which jobs failed
|
||||
3. Get detailed information for each failed job
|
||||
4. Read logs for each failed job
|
||||
5. Identify root cause from logs
|
||||
|
||||
### Checking a Passing Build
|
||||
|
||||
1. Get build status → state is `passed`
|
||||
2. Optionally check annotations for warnings or info
|
||||
3. Note any "broken" jobs (may be expected)
|
||||
4. No need to read logs unless investigating performance
|
||||
107
skills/buildkite-status/references/buildkite-states.md
Normal file
107
skills/buildkite-status/references/buildkite-states.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Buildkite Build and Job States
|
||||
|
||||
Understanding Buildkite states is critical for correctly interpreting build status. Some states are misleading or require additional context.
|
||||
|
||||
## Build States
|
||||
|
||||
### Terminal States
|
||||
|
||||
- **`passed`** - All jobs completed successfully
|
||||
- **`failed`** - One or more jobs failed
|
||||
- **`canceled`** - Build was manually canceled
|
||||
- **`skipped`** - Build was skipped (e.g., due to branch filters)
|
||||
- **`blocked`** - Build is waiting for manual approval via block step
|
||||
|
||||
### Active States
|
||||
|
||||
- **`running`** - Build is currently executing
|
||||
- **`scheduled`** - Build is queued and waiting to start
|
||||
- **`creating`** - Build is being created
|
||||
|
||||
## Job States
|
||||
|
||||
### Terminal States
|
||||
|
||||
- **`passed`** - Job completed successfully
|
||||
- **`failed`** - Job failed with non-zero exit code
|
||||
- **`canceled`** - Job was canceled
|
||||
- **`skipped`** - Job was skipped
|
||||
- **`timed_out`** - Job exceeded time limit
|
||||
|
||||
### Special States (Often Misleading)
|
||||
|
||||
- **`broken`** - This is the most misleading state. It can mean:
|
||||
|
||||
- Job was skipped because an earlier job in the pipeline failed
|
||||
- Job was skipped due to dependency conditions not being met
|
||||
- Job was skipped due to conditional logic in the pipeline config
|
||||
- **NOT necessarily a failure of this specific job**
|
||||
|
||||
Example: In the zen-payroll pipeline, many jobs show as "broken" but are actually skipped because their dependencies indicated they weren't needed (e.g., no relevant file changes).
|
||||
|
||||
- **`soft_failed`** - Job failed but was marked as "soft fail" (doesn't block pipeline)
|
||||
- Shows as failed but doesn't cause overall build failure
|
||||
- Often used for optional checks or flaky tests
|
||||
|
||||
### Active States
|
||||
|
||||
- **`waiting`** - Job is waiting for dependencies
|
||||
- **`waiting_failed`** - Job was waiting but its dependency failed
|
||||
- **`assigned`** - Job has been assigned to an agent
|
||||
- **`accepted`** - Agent has accepted the job
|
||||
- **`running`** - Job is currently executing
|
||||
- **`blocked`** - Job is a block step waiting for manual unblock
|
||||
|
||||
## Interpreting Build Status
|
||||
|
||||
### Progressive Disclosure Pattern
|
||||
|
||||
When checking build status, follow this pattern:
|
||||
|
||||
1. **Start with overall state**: `passed`, `failed`, `canceled`, `blocked`
|
||||
2. **If failed, check job summary**: How many jobs failed vs broken vs passed?
|
||||
3. **Examine failed jobs specifically**: Don't assume "broken" means the job itself failed
|
||||
4. **Check annotations**: Some projects surface important failures in annotations
|
||||
5. **Inspect logs**: For actual failures, read the job logs
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
1. **Treating "broken" as "failed"**: A "broken" job is often just skipped due to pipeline logic, not an actual failure.
|
||||
|
||||
2. **Ignoring soft fails**: Jobs marked as `soft_failed` may contain important information even though they don't block the build.
|
||||
|
||||
3. **Missing blocked builds**: A `blocked` build is waiting for approval and won't progress without manual intervention.
|
||||
|
||||
4. **Overlooking job dependencies**: Jobs may be skipped (`broken`) because their dependencies weren't met, which is expected behavior.
|
||||
|
||||
## Project-Specific Patterns
|
||||
|
||||
### zen-payroll Pipeline
|
||||
|
||||
- **Heavy use of conditional execution**: Many jobs are conditionally skipped based on file changes
|
||||
- **"broken" is normal**: A build with many "broken" jobs may still be perfectly healthy
|
||||
- **Check annotations**: Important test failures are often surfaced in build annotations
|
||||
- **Multiple test suites**: Different test types (unit, integration, system) have different failure patterns
|
||||
|
||||
### Smaller Pipelines (e.g., gusto-karafka)
|
||||
|
||||
- **Fewer conditional jobs**: Most jobs are expected to run
|
||||
- **"broken" usually indicates a problem**: Less conditional logic means broken jobs are more likely to be actual issues
|
||||
- **Simpler job graphs**: Easier to trace why a job didn't run
|
||||
- **May not use annotations**: Failures are usually just in job logs
|
||||
|
||||
## When to Investigate
|
||||
|
||||
Investigate a build when:
|
||||
|
||||
1. Overall build state is `failed`
|
||||
2. Jobs show `failed` state (not just `broken`)
|
||||
3. Build is `blocked` and you need to unblock it
|
||||
4. Annotations contain error messages
|
||||
5. Job logs show actual errors (red output, stack traces, test failures)
|
||||
|
||||
Don't automatically investigate when:
|
||||
|
||||
1. Build is `passed` (even if some jobs are `broken`)
|
||||
2. Jobs are `soft_failed` unless specifically requested
|
||||
3. Jobs are `broken` due to conditional execution (check pipeline config)
|
||||
291
skills/buildkite-status/references/tool-capabilities.md
Normal file
291
skills/buildkite-status/references/tool-capabilities.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# Tool Capabilities Reference
|
||||
|
||||
This document provides complete capability information for all Buildkite status checking tools.
|
||||
|
||||
## Overview
|
||||
|
||||
Three tool categories exist with different strengths and limitations:
|
||||
|
||||
1. **MCP Tools** - Direct Buildkite API access via Model Context Protocol
|
||||
2. **bktide CLI** - Human-readable command-line tool (npm package)
|
||||
3. **Bundled Scripts** - Helper wrappers in this skill's `scripts/` directory
|
||||
|
||||
## Capability Matrix
|
||||
|
||||
| Capability | MCP Tools | bktide | Scripts | Notes |
|
||||
| --------------------- | ------------------------------- | ----------------------- | -------------------------- | -------------------------- |
|
||||
| List organizations | ✅ `buildkite:list_orgs` | ❌ | ❌ | |
|
||||
| List pipelines | ✅ `buildkite:list_pipelines` | ✅ `bktide pipelines` | ❌ | |
|
||||
| List builds | ✅ `buildkite:list_builds` | ✅ `bktide builds` | ✅ `find-commit-builds.js` | Scripts are specialized |
|
||||
| Get build details | ✅ `buildkite:get_build` | ✅ `bktide build` | ❌ | |
|
||||
| Get annotations | ✅ `buildkite:list_annotations` | ✅ `bktide annotations` | ❌ | |
|
||||
| **Retrieve job logs** | **✅ `buildkite:get_logs`** | **❌ NO** | **✅ `get-build-logs.js`** | **bktide cannot get logs** |
|
||||
| Get log metadata | ✅ `buildkite:get_logs_info` | ❌ | ❌ | |
|
||||
| List artifacts | ✅ `buildkite:list_artifacts` | ❌ | ❌ | |
|
||||
| Wait for build | ✅ `buildkite:wait_for_build` | ❌ | ✅ `wait-for-build.js` | MCP preferred |
|
||||
| Unblock jobs | ✅ `buildkite:unblock_job` | ❌ | ❌ | |
|
||||
| Real-time updates | ✅ | ❌ | ✅ | Via polling |
|
||||
| Human-readable output | ❌ (JSON) | ✅ | Varies | |
|
||||
| Works offline | ❌ | ❌ | ❌ | All need network |
|
||||
| Requires auth | ✅ (MCP config) | ✅ (BK_TOKEN) | ✅ (uses bktide) | |
|
||||
|
||||
## Detailed Tool Information
|
||||
|
||||
### MCP Tools (Primary)
|
||||
|
||||
**Access Method:** `mcp__MCPProxy__call_tool("buildkite:<tool>", {...})`
|
||||
|
||||
**Authentication:** Configured in MCP server settings (typically uses `BUILDKITE_API_TOKEN`)
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Complete API coverage
|
||||
- Always available (no external dependencies)
|
||||
- Real-time data
|
||||
- Structured JSON responses
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Verbose JSON output
|
||||
- Requires parsing for human reading
|
||||
|
||||
**Key Tools:**
|
||||
|
||||
#### `buildkite:get_build`
|
||||
|
||||
Get detailed build information including job states, timing, and metadata.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `org_slug` (required): Organization slug
|
||||
- `pipeline_slug` (required): Pipeline slug
|
||||
- `build_number` (required): Build number
|
||||
- `detail_level` (optional): "summary" | "detailed" | "complete"
|
||||
- `job_state` (optional): Filter jobs by state ("failed", "passed", etc.)
|
||||
|
||||
Returns: Build object with jobs array, state, timing, author, etc.
|
||||
|
||||
#### `buildkite:get_logs`
|
||||
|
||||
**THE CRITICAL TOOL** - Retrieve actual log output from a job.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `org_slug` (required): Organization slug
|
||||
- `pipeline_slug` (required): Pipeline slug
|
||||
- `build_number` (required): Build number
|
||||
- `job_id` (required): Job UUID (NOT step ID from URL)
|
||||
|
||||
Returns: Log text content
|
||||
|
||||
**Common Issues:**
|
||||
|
||||
- "job not found" → Using step ID instead of job UUID
|
||||
- Empty response → Job hasn't started or finished yet
|
||||
|
||||
#### `buildkite:wait_for_build`
|
||||
|
||||
Poll build until completion.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `org_slug` (required): Organization slug
|
||||
- `pipeline_slug` (required): Pipeline slug
|
||||
- `build_number` (required): Build number
|
||||
- `timeout` (optional): Seconds until timeout (default: 1800)
|
||||
- `poll_interval` (optional): Seconds between checks (default: 30)
|
||||
|
||||
Returns: Final build state when complete or timeout
|
||||
|
||||
### bktide CLI (Secondary)
|
||||
|
||||
**Access Method:** `npx bktide <command>`
|
||||
|
||||
**Authentication:** `BK_TOKEN` environment variable or `~/.bktide/config`
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Human-readable colored output
|
||||
- Intuitive command structure
|
||||
- Good for interactive terminal work
|
||||
|
||||
**Cons:**
|
||||
|
||||
- External npm dependency
|
||||
- **CANNOT retrieve job logs** (most critical limitation)
|
||||
- Limited compared to full API
|
||||
- Requires npx/node installed
|
||||
|
||||
**Key Commands:**
|
||||
|
||||
```bash
|
||||
npx bktide pipelines <org> # List pipelines
|
||||
npx bktide builds <org>/<pipeline> # List recent builds
|
||||
npx bktide build <org>/<pipeline>/<number> # Build details
|
||||
npx bktide build <org>/<pipeline>/<number> --jobs # Show job summary
|
||||
npx bktide build <org>/<pipeline>/<number> --failed # Show failed jobs only
|
||||
npx bktide annotations <org>/<pipeline>/<number> # Show annotations
|
||||
```
|
||||
|
||||
**Critical**: bktide has NO command for retrieving logs. The `build` command shows job states and names, but NOT log content.
|
||||
|
||||
### Bundled Scripts (Tertiary)
|
||||
|
||||
**Access Method:** `~/.claude/skills/buildkite-status/scripts/<script>.js`
|
||||
|
||||
**Authentication:** Use bktide internally (requires `BK_TOKEN`)
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Purpose-built for specific workflows
|
||||
- Handle common use cases automatically
|
||||
- Provide structured output
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Depend on bktide (external dependency)
|
||||
- Limited to specific use cases
|
||||
- May have version compatibility issues
|
||||
|
||||
**Available Scripts:**
|
||||
|
||||
#### `find-commit-builds.js`
|
||||
|
||||
Find builds matching a specific commit SHA.
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/find-commit-builds.js <org> <commit-sha>
|
||||
```
|
||||
|
||||
Returns: JSON array of matching builds
|
||||
|
||||
#### `wait-for-build.js`
|
||||
|
||||
Monitor build until completion (background-friendly).
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/wait-for-build.js <org> <pipeline> <build> [options]
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
- `--timeout <seconds>`: Max wait time (default: 1800)
|
||||
- `--interval <seconds>`: Poll interval (default: 30)
|
||||
|
||||
Exit codes:
|
||||
|
||||
- 0: Build passed
|
||||
- 1: Build failed
|
||||
- 2: Build canceled
|
||||
- 3: Timeout
|
||||
|
||||
#### `get-build-logs.js` (NEW - to be implemented)
|
||||
|
||||
Retrieve logs for a failed job with automatic UUID resolution.
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/get-build-logs.js <org> <pipeline> <build> <job-label-or-uuid>
|
||||
```
|
||||
|
||||
Features:
|
||||
|
||||
- Accepts job label or UUID
|
||||
- Automatically resolves label → UUID
|
||||
- Handles step ID confusion
|
||||
- Formats output for readability
|
||||
|
||||
## Decision Matrix: Which Tool to Use
|
||||
|
||||
### Use MCP Tools When:
|
||||
|
||||
- Getting build details
|
||||
- **Retrieving job logs** (ONLY option with bktide)
|
||||
- Waiting for builds (preferred over script)
|
||||
- Unblocking jobs
|
||||
- Automating workflows
|
||||
- Need structured data
|
||||
|
||||
### Use bktide When:
|
||||
|
||||
- Interactive terminal work
|
||||
- Want human-readable summary
|
||||
- Listing pipelines/builds
|
||||
- Getting quick status overview
|
||||
- **NOT when you need logs** (it can't do this)
|
||||
|
||||
### Use Scripts When:
|
||||
|
||||
- Need specialized workflow (find commits)
|
||||
- Want background monitoring
|
||||
- MCP tools fail (fallback)
|
||||
- Automating repetitive tasks
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### ❌ Trying to get logs with bktide
|
||||
|
||||
**Don't**: `npx bktide build <org>/<pipeline>/<number> --logs`
|
||||
|
||||
**Why**: This flag doesn't exist. bktide cannot retrieve logs.
|
||||
|
||||
**Do**: Use `buildkite:get_logs` MCP tool
|
||||
|
||||
### ❌ Using step ID for log retrieval
|
||||
|
||||
**Don't**: Extract `sid=019a5f...` from URL and use directly
|
||||
|
||||
**Why**: Step IDs ≠ Job UUIDs. MCP tools need job UUIDs.
|
||||
|
||||
**Do**: Call `buildkite:get_build` to get job details, extract `uuid` field
|
||||
|
||||
### ❌ Abandoning MCP tools when script fails
|
||||
|
||||
**Don't**: "Script failed, I'll use GitHub instead"
|
||||
|
||||
**Why**: Scripts depend on bktide. MCP tools are independent.
|
||||
|
||||
**Do**: Use MCP tools directly when scripts fail
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "job not found" when calling get_logs
|
||||
|
||||
**Diagnosis**: Using step ID instead of job UUID
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Call `buildkite:get_build` with `detail_level: "detailed"`
|
||||
2. Find job by `label` field
|
||||
3. Extract `uuid` field
|
||||
4. Use that UUID in `get_logs` call
|
||||
|
||||
### Issue: bktide command not found
|
||||
|
||||
**Diagnosis**: npm/npx not installed or not in PATH
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Use MCP tools instead (preferred)
|
||||
2. Or install: `npm install -g @anthropic/bktide`
|
||||
|
||||
### Issue: Empty logs returned
|
||||
|
||||
**Diagnosis**: Job hasn't completed or logs not available yet
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Check job `state` - should be terminal (passed/failed/canceled)
|
||||
2. Wait for job to finish
|
||||
3. Check job `started_at` and `finished_at` timestamps
|
||||
|
||||
## See Also
|
||||
|
||||
- [SKILL.md](../SKILL.md) - Main skill documentation
|
||||
- [troubleshooting.md](troubleshooting.md) - Common errors and solutions
|
||||
- [url-parsing.md](url-parsing.md) - Understanding Buildkite URLs
|
||||
365
skills/buildkite-status/references/troubleshooting.md
Normal file
365
skills/buildkite-status/references/troubleshooting.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# Buildkite Status Troubleshooting
|
||||
|
||||
Common errors when working with Buildkite and how to resolve them.
|
||||
|
||||
## MCP Tool Errors
|
||||
|
||||
### Error: "job not found"
|
||||
|
||||
**When**: Calling `buildkite:get_logs`
|
||||
|
||||
**Cause**: Using step ID from URL instead of job UUID from API
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Call `buildkite:get_build` with `detail_level: "detailed"`
|
||||
2. Find job by `label` field
|
||||
3. Extract `uuid` field (NOT the `id` field)
|
||||
4. Use that UUID in `get_logs`
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// ❌ Wrong - using step ID from URL
|
||||
mcp__MCPProxy__call_tool('buildkite:get_logs', {
|
||||
job_id: '019a5f23-8109-4656-a033-bd62a82ca239', // This is a step ID
|
||||
});
|
||||
|
||||
// ✅ Correct - get job UUID from API first
|
||||
const build = await mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
build_number: '29627',
|
||||
detail_level: 'detailed',
|
||||
});
|
||||
|
||||
const job = build.jobs.find(
|
||||
(j) => j.label === 'ste rspec' && j.state === 'failed'
|
||||
);
|
||||
|
||||
await mcp__MCPProxy__call_tool('buildkite:get_logs', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
build_number: '29627',
|
||||
job_id: job.uuid, // This is the correct job UUID
|
||||
});
|
||||
```
|
||||
|
||||
**See Also**: [url-parsing.md](url-parsing.md) for step ID vs job UUID explanation
|
||||
|
||||
---
|
||||
|
||||
### Error: "build not found" or "pipeline not found"
|
||||
|
||||
**When**: Calling any MCP tool
|
||||
|
||||
**Cause**: Incorrect org slug or pipeline slug format
|
||||
|
||||
**Common Mistakes**:
|
||||
|
||||
- Using repository name instead of pipeline slug
|
||||
- Including org name in pipeline slug
|
||||
- Using display name instead of URL slug
|
||||
|
||||
**Solution**:
|
||||
Extract slugs from URL correctly:
|
||||
|
||||
```
|
||||
https://buildkite.com/gusto/payroll-building-blocks/builds/123
|
||||
^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
|
||||
org pipeline slug
|
||||
```
|
||||
|
||||
**Slug Format Rules**:
|
||||
|
||||
- All lowercase
|
||||
- Hyphens instead of underscores
|
||||
- No spaces
|
||||
- No special characters
|
||||
|
||||
**Example**:
|
||||
|
||||
```javascript
|
||||
// ❌ Wrong
|
||||
{ org_slug: "Gusto", pipeline_slug: "Payroll Building Blocks" }
|
||||
|
||||
// ✅ Correct
|
||||
{ org_slug: "gusto", pipeline_slug: "payroll-building-blocks" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error: Empty logs returned
|
||||
|
||||
**When**: Calling `buildkite:get_logs`
|
||||
|
||||
**Causes**:
|
||||
|
||||
1. Job hasn't started yet
|
||||
2. Job is still running
|
||||
3. Job failed before producing output
|
||||
4. Logs not available yet (eventual consistency)
|
||||
|
||||
**Diagnosis**:
|
||||
Check job state first:
|
||||
|
||||
```javascript
|
||||
const build = await mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
detail_level: 'detailed',
|
||||
});
|
||||
|
||||
const job = build.jobs.find((j) => j.uuid === jobUuid);
|
||||
console.log(job.state); // Should be terminal: passed/failed/canceled
|
||||
console.log(job.started_at); // Should not be null
|
||||
console.log(job.finished_at); // Should not be null for terminal state
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
|
||||
- If state is `waiting` or `running`: Wait for job to complete
|
||||
- If state is terminal but logs empty: Wait a few seconds for eventual consistency
|
||||
- If still empty: Job may have failed immediately (check exit_status)
|
||||
|
||||
---
|
||||
|
||||
### Error: "Unauthorized" or "Forbidden"
|
||||
|
||||
**When**: Any MCP tool call
|
||||
|
||||
**Cause**: Authentication or permission issue
|
||||
|
||||
**Diagnosis Steps**:
|
||||
|
||||
1. Check MCP server configuration:
|
||||
|
||||
```bash
|
||||
# MCP server should have BUILDKITE_API_TOKEN configured
|
||||
```
|
||||
|
||||
2. Verify token has correct scope:
|
||||
|
||||
- `read_builds` - Required for reading build info
|
||||
- `read_build_logs` - Required for log retrieval
|
||||
- `read_pipelines` - Required for pipeline listing
|
||||
|
||||
3. Check organization access:
|
||||
- Token must have access to the specific organization
|
||||
- Some orgs require SSO
|
||||
|
||||
**Solution**:
|
||||
|
||||
- Verify BUILDKITE_API_TOKEN in MCP config
|
||||
- Generate new token at https://buildkite.com/user/api-access-tokens
|
||||
- Ensure token has required scopes
|
||||
- Report to human partner if still failing (may need org admin help)
|
||||
|
||||
---
|
||||
|
||||
## bktide CLI Errors
|
||||
|
||||
### Error: "bktide: command not found"
|
||||
|
||||
**Cause**: bktide not installed or not in PATH
|
||||
|
||||
**Solution**:
|
||||
Use MCP tools instead (preferred):
|
||||
|
||||
```javascript
|
||||
// Instead of: npx bktide build gusto/payroll-building-blocks/123
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
build_number: '123',
|
||||
});
|
||||
```
|
||||
|
||||
Or install bktide:
|
||||
|
||||
```bash
|
||||
npm install -g @anthropic/bktide
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error: "Cannot read logs with bktide"
|
||||
|
||||
**Cause**: bktide does not have log retrieval capability
|
||||
|
||||
**Solution**:
|
||||
Use MCP tools for logs:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_logs', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
build_number: '123',
|
||||
job_id: '<job-uuid>',
|
||||
});
|
||||
```
|
||||
|
||||
**See Also**: [tool-capabilities.md](tool-capabilities.md) for complete capability matrix
|
||||
|
||||
---
|
||||
|
||||
## Script Errors
|
||||
|
||||
### Error: Script fails with "bktide error"
|
||||
|
||||
**Cause**: Scripts depend on bktide internally
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Use equivalent MCP tool instead (preferred)
|
||||
2. Or ensure bktide is installed and configured
|
||||
3. Or check `BK_TOKEN` environment variable is set
|
||||
|
||||
**Example**:
|
||||
|
||||
```bash
|
||||
# Script failing
|
||||
~/.claude/skills/buildkite-status/scripts/wait-for-build.js gusto payroll-building-blocks 123
|
||||
|
||||
# Use MCP tool instead
|
||||
mcp__MCPProxy__call_tool("buildkite:wait_for_build", {
|
||||
org_slug: "gusto",
|
||||
pipeline_slug: "payroll-building-blocks",
|
||||
build_number: "123",
|
||||
timeout: 1800,
|
||||
poll_interval: 30
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build State Confusion
|
||||
|
||||
### Issue: Many jobs show "broken" but build looks healthy
|
||||
|
||||
**Cause**: "broken" doesn't mean failed - it usually means skipped
|
||||
|
||||
**Explanation**:
|
||||
Buildkite uses "broken" state for:
|
||||
|
||||
- Jobs skipped because dependency failed
|
||||
- Jobs skipped due to conditional logic
|
||||
- Jobs skipped because file changes didn't affect them
|
||||
|
||||
**Solution**:
|
||||
Filter for actual failures:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
detail_level: 'detailed',
|
||||
job_state: 'failed', // Only show actually failed jobs
|
||||
});
|
||||
```
|
||||
|
||||
**See Also**: [buildkite-states.md](buildkite-states.md) for complete state explanations
|
||||
|
||||
---
|
||||
|
||||
### Issue: Build shows "failed" but all jobs passed
|
||||
|
||||
**Cause**: A "soft_failed" job counts as passed in job list but failed for build state
|
||||
|
||||
**Solution**:
|
||||
Check for soft failures:
|
||||
|
||||
```javascript
|
||||
const build = await mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
detail_level: 'detailed',
|
||||
});
|
||||
|
||||
const softFails = build.jobs.filter((j) => j.soft_failed === true);
|
||||
console.log(softFails); // These caused build to fail but are marked non-blocking
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Workflow Issues
|
||||
|
||||
### Issue: Cannot find recent build for branch
|
||||
|
||||
**Cause**: Build may be filtered or pipeline has many builds
|
||||
|
||||
**Solution**:
|
||||
Use branch filter and increase limit:
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:list_builds', {
|
||||
org_slug: 'gusto',
|
||||
pipeline_slug: 'payroll-building-blocks',
|
||||
branch: 'my-feature-branch',
|
||||
per_page: 20, // Default may be smaller
|
||||
});
|
||||
```
|
||||
|
||||
Or find by commit:
|
||||
|
||||
```bash
|
||||
~/.claude/skills/buildkite-status/scripts/find-commit-builds.js gusto <commit-sha>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Issue: Multiple jobs have same label, can't tell which failed
|
||||
|
||||
**Cause**: Parallelized jobs have same base label
|
||||
|
||||
**Solution**:
|
||||
Jobs with same label are numbered:
|
||||
|
||||
- "rspec (1/10)"
|
||||
- "rspec (2/10)"
|
||||
|
||||
Match on full label including partition:
|
||||
|
||||
```javascript
|
||||
const failedJob = build.jobs.find(
|
||||
(j) => j.label === 'rspec (2/10)' && j.state === 'failed'
|
||||
);
|
||||
```
|
||||
|
||||
Or find all failed jobs with that label:
|
||||
|
||||
```javascript
|
||||
const failedRspecJobs = build.jobs.filter(
|
||||
(j) => j.label.startsWith('rspec (') && j.state === 'failed'
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Decision Tree: What to Do When Stuck
|
||||
|
||||
```
|
||||
Unable to investigate build failure?
|
||||
│
|
||||
├─ Can't get build details
|
||||
│ ├─ Check URL format → [url-parsing.md]
|
||||
│ ├─ Check org/pipeline slugs → lowercase, hyphenated
|
||||
│ └─ Check auth → BUILDKITE_API_TOKEN configured
|
||||
│
|
||||
├─ Can't get job logs
|
||||
│ ├─ Using bktide? → Use MCP tools instead [tool-capabilities.md]
|
||||
│ ├─ Getting "job not found"? → Using step ID instead of job UUID [url-parsing.md]
|
||||
│ ├─ Empty logs? → Check job state (started_at, finished_at)
|
||||
│ └─ Still failing? → Report to human partner (may be auth/permission)
|
||||
│
|
||||
├─ Confused about job states
|
||||
│ ├─ Many "broken" jobs? → Normal, means skipped [buildkite-states.md]
|
||||
│ ├─ "soft_failed"? → Failed but non-blocking
|
||||
│ └─ Can't find failed job? → Filter with job_state: "failed"
|
||||
│
|
||||
└─ Tool not working
|
||||
├─ MCP tool error? → Check auth, verify slugs
|
||||
├─ bktide error? → Use MCP tools instead
|
||||
└─ Script error? → Use MCP tools directly
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [SKILL.md](../SKILL.md) - Main skill documentation
|
||||
- [tool-capabilities.md](tool-capabilities.md) - What each tool can do
|
||||
- [url-parsing.md](url-parsing.md) - Understanding URLs and IDs
|
||||
- [buildkite-states.md](buildkite-states.md) - Build and job states
|
||||
272
skills/buildkite-status/references/url-parsing.md
Normal file
272
skills/buildkite-status/references/url-parsing.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# Buildkite URL Parsing Reference
|
||||
|
||||
This document explains Buildkite URL formats and how to extract information from them.
|
||||
|
||||
## URL Formats
|
||||
|
||||
Buildkite uses several URL patterns for builds and jobs:
|
||||
|
||||
### Build URL (Most Common)
|
||||
|
||||
```
|
||||
https://buildkite.com/{org}/{pipeline}/builds/{number}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
https://buildkite.com/gusto/payroll-building-blocks/builds/29627
|
||||
```
|
||||
|
||||
Extracting:
|
||||
|
||||
- `org`: "gusto"
|
||||
- `pipeline`: "payroll-building-blocks"
|
||||
- `number`: "29627"
|
||||
|
||||
### Step/Job URL (From Build Page)
|
||||
|
||||
```
|
||||
https://buildkite.com/{org}/{pipeline}/builds/{number}/steps/{view}?sid={step-id}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
https://buildkite.com/gusto/payroll-building-blocks/builds/29627/steps/canvas?sid=019a5f23-8109-4656-a033-bd62a82ca239
|
||||
```
|
||||
|
||||
Extracting:
|
||||
|
||||
- `org`: "gusto"
|
||||
- `pipeline`: "payroll-building-blocks"
|
||||
- `number`: "29627"
|
||||
- `view`: "canvas" (UI view type)
|
||||
- `sid`: "019a5f23-8109-4656-a033-bd62a82ca239" (step ID)
|
||||
|
||||
**IMPORTANT**: The `sid` (step ID) is NOT the same as job UUID. See "Step IDs vs Job UUIDs" below.
|
||||
|
||||
### Job Detail URL
|
||||
|
||||
```
|
||||
https://buildkite.com/{org}/{pipeline}/builds/{number}/jobs/{job-uuid}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
https://buildkite.com/gusto/payroll-building-blocks/builds/29627/jobs/019a5f20-2d30-4c67-9edd-87fb92e1f487
|
||||
```
|
||||
|
||||
Extracting:
|
||||
|
||||
- `org`: "gusto"
|
||||
- `pipeline`: "payroll-building-blocks"
|
||||
- `number`: "29627"
|
||||
- `job-uuid`: "019a5f20-2d30-4c67-9edd-87fb92e1f487"
|
||||
|
||||
**NOTE**: This format contains the actual job UUID needed for log retrieval.
|
||||
|
||||
## Step IDs vs Job UUIDs
|
||||
|
||||
**Critical distinction**: Buildkite has two types of identifiers that are easily confused.
|
||||
|
||||
### Step IDs
|
||||
|
||||
- **Source**: Query parameter `sid` in step URLs
|
||||
- **Format**: ULID format (e.g., `019a5f23-8109-4656-a033-bd62a82ca239`)
|
||||
- **Purpose**: Frontend UI routing
|
||||
- **Use**: Navigating to specific steps in web UI
|
||||
- **API Usage**: ❌ NOT accepted by MCP tools
|
||||
|
||||
### Job UUIDs
|
||||
|
||||
- **Source**: `uuid` field in API responses
|
||||
- **Format**: ULID format (e.g., `019a5f20-2d30-4c67-9edd-87fb92e1f487`)
|
||||
- **Purpose**: Backend job identification
|
||||
- **Use**: API calls to get logs, job details, etc.
|
||||
- **API Usage**: ✅ Required by MCP `get_logs` tool
|
||||
|
||||
### Why the Confusion?
|
||||
|
||||
Both use ULID format (starts with `019a5f...`), but:
|
||||
|
||||
- Step IDs come from URLs → Web UI routing
|
||||
- Job UUIDs come from API responses → Backend identification
|
||||
|
||||
**You cannot use a step ID for log retrieval.** Always get job UUID from `buildkite:get_build` API.
|
||||
|
||||
## Resolving Step ID to Job UUID
|
||||
|
||||
When given a step URL with `sid` parameter:
|
||||
|
||||
**Step 1: Extract build identifiers**
|
||||
|
||||
```javascript
|
||||
// From: https://buildkite.com/gusto/payroll-building-blocks/builds/29627/steps/canvas?sid=019a5f23...
|
||||
const org = 'gusto';
|
||||
const pipeline = 'payroll-building-blocks';
|
||||
const build = '29627';
|
||||
// Ignore the sid parameter
|
||||
```
|
||||
|
||||
**Step 2: Get job details from API**
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_build', {
|
||||
org_slug: org,
|
||||
pipeline_slug: pipeline,
|
||||
build_number: build,
|
||||
detail_level: 'detailed',
|
||||
job_state: 'failed', // If investigating failures
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: Match job by properties**
|
||||
|
||||
The API response includes all jobs. Match by:
|
||||
|
||||
- `label` field (e.g., "ste rspec", "Rubocop")
|
||||
- `state` field (e.g., "failed")
|
||||
- `type` field (e.g., "script")
|
||||
- `step_key` field if available
|
||||
|
||||
**Step 4: Extract job UUID**
|
||||
|
||||
```javascript
|
||||
// From API response
|
||||
const job = response.jobs.find(
|
||||
(j) => j.label === 'ste rspec' && j.state === 'failed'
|
||||
);
|
||||
const jobUuid = job.uuid; // e.g., "019a5f20-2d30-4c67-9edd-87fb92e1f487"
|
||||
```
|
||||
|
||||
**Step 5: Use job UUID for logs**
|
||||
|
||||
```javascript
|
||||
mcp__MCPProxy__call_tool('buildkite:get_logs', {
|
||||
org_slug: org,
|
||||
pipeline_slug: pipeline,
|
||||
build_number: build,
|
||||
job_id: jobUuid, // NOT the step ID from URL
|
||||
});
|
||||
```
|
||||
|
||||
## Parsing Logic
|
||||
|
||||
### Simple Regex Approach
|
||||
|
||||
```javascript
|
||||
function parseBuildkiteUrl(url) {
|
||||
// Match build URL pattern
|
||||
const buildMatch = url.match(
|
||||
/buildkite\.com\/([^/]+)\/([^/]+)\/builds\/(\d+)/
|
||||
);
|
||||
|
||||
if (!buildMatch) {
|
||||
throw new Error('Invalid Buildkite URL');
|
||||
}
|
||||
|
||||
return {
|
||||
org: buildMatch[1],
|
||||
pipeline: buildMatch[2],
|
||||
buildNumber: buildMatch[3],
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
const info = parseBuildkiteUrl(
|
||||
'https://buildkite.com/gusto/payroll-building-blocks/builds/29627'
|
||||
);
|
||||
// => { org: "gusto", pipeline: "payroll-building-blocks", buildNumber: "29627" }
|
||||
```
|
||||
|
||||
### Extracting Step ID (If Needed)
|
||||
|
||||
```javascript
|
||||
function parseStepUrl(url) {
|
||||
const base = parseBuildkiteUrl(url);
|
||||
|
||||
// Extract step ID from query parameter
|
||||
const sidMatch = url.match(/[?&]sid=([^&]+)/);
|
||||
|
||||
return {
|
||||
...base,
|
||||
stepId: sidMatch ? sidMatch[1] : null,
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
const info = parseStepUrl(
|
||||
'https://buildkite.com/gusto/payroll-building-blocks/builds/29627/steps/canvas?sid=019a5f23...'
|
||||
);
|
||||
// => { org: "gusto", pipeline: "payroll-building-blocks", buildNumber: "29627", stepId: "019a5f23..." }
|
||||
```
|
||||
|
||||
**Remember**: The `stepId` is useful for debugging but cannot be used for API calls. Always fetch job UUID from the API.
|
||||
|
||||
## Common URL Patterns in Practice
|
||||
|
||||
### Pattern 1: User Shares Failing Build URL
|
||||
|
||||
**URL**: `https://buildkite.com/org/pipeline/builds/123`
|
||||
|
||||
**Workflow**:
|
||||
|
||||
1. Extract org/pipeline/build
|
||||
2. Call `get_build` with `detail_level: "summary"`
|
||||
3. Check build state
|
||||
4. If failed, call `get_build` with `detail_level: "detailed"` and `job_state: "failed"`
|
||||
5. Get logs for each failed job
|
||||
|
||||
### Pattern 2: User Shares Step URL (Clicked on Specific Job)
|
||||
|
||||
**URL**: `https://buildkite.com/org/pipeline/builds/123/steps/canvas?sid=019a5f23...`
|
||||
|
||||
**Workflow**:
|
||||
|
||||
1. Extract org/pipeline/build (ignore `sid`)
|
||||
2. Call `get_build` with `detail_level: "detailed"`
|
||||
3. Find job matching user's intent (often the failed one)
|
||||
4. Extract job UUID
|
||||
5. Get logs for that job
|
||||
|
||||
The `sid` hints at which job the user was looking at, but you must resolve it via the API.
|
||||
|
||||
### Pattern 3: User Provides Job UUID Directly
|
||||
|
||||
**URL**: `https://buildkite.com/org/pipeline/builds/123/jobs/019a5f20-...`
|
||||
|
||||
**Workflow**:
|
||||
|
||||
1. Extract org/pipeline/build/job-uuid
|
||||
2. Call `get_logs` directly with the job UUID
|
||||
3. No resolution needed - this is the actual job UUID
|
||||
|
||||
This is the ideal format but least common in practice.
|
||||
|
||||
## Edge Cases
|
||||
|
||||
### Multiple Jobs with Same Label
|
||||
|
||||
Some pipelines parallelize jobs:
|
||||
|
||||
- "rspec (1/10)"
|
||||
- "rspec (2/10)"
|
||||
- "rspec (3/10)"
|
||||
|
||||
When resolving, match the full label string including the partition number.
|
||||
|
||||
### Dynamic Pipeline Steps
|
||||
|
||||
Some pipelines generate steps dynamically. The step structure may not be predictable from the URL alone. Always query the API to see actual job structure.
|
||||
|
||||
### Retried Jobs
|
||||
|
||||
When jobs are retried, multiple job UUIDs exist for the same step. The API returns the most recent attempt. Check `retries_count` and `retry_source` fields if investigating retry behavior.
|
||||
|
||||
## See Also
|
||||
|
||||
- [SKILL.md](../SKILL.md) - Main skill documentation
|
||||
- [tool-capabilities.md](tool-capabilities.md) - Tool limitations and capabilities
|
||||
- [troubleshooting.md](troubleshooting.md) - Common errors
|
||||
137
skills/buildkite-status/scripts/find-commit-builds.js
Executable file
137
skills/buildkite-status/scripts/find-commit-builds.js
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Find Buildkite builds for a specific commit
|
||||
*
|
||||
* Searches across pipelines for builds matching a commit SHA.
|
||||
* Useful for post-push workflows to find which builds are running.
|
||||
*
|
||||
* Usage:
|
||||
* find-commit-builds.js <org> <commit-sha> [options]
|
||||
*
|
||||
* Options:
|
||||
* --pipeline <slug> Limit search to specific pipeline
|
||||
* --branch <name> Limit to specific branch
|
||||
* --format <json|plain> Output format (default: plain)
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 - Builds found
|
||||
* 1 - No builds found
|
||||
* 2 - Error occurred
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2 || args[0] === '--help' || args[0] === '-h') {
|
||||
console.error('Usage: find-commit-builds.js <org> <commit-sha> [options]');
|
||||
console.error('Options:');
|
||||
console.error(' --pipeline <slug> Limit search to specific pipeline');
|
||||
console.error(' --branch <name> Limit to specific branch');
|
||||
console.error(' --format <json|plain> Output format (default: plain)');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const org = args[0];
|
||||
const commit = args[1];
|
||||
|
||||
// Parse options
|
||||
let pipelineSlug = null;
|
||||
let branch = null;
|
||||
let format = 'plain';
|
||||
|
||||
for (let i = 2; i < args.length; i++) {
|
||||
if (args[i] === '--pipeline' && i + 1 < args.length) {
|
||||
pipelineSlug = args[++i];
|
||||
} else if (args[i] === '--branch' && i + 1 < args.length) {
|
||||
branch = args[++i];
|
||||
} else if (args[i] === '--format' && i + 1 < args.length) {
|
||||
format = args[++i];
|
||||
}
|
||||
}
|
||||
|
||||
function getPipelines() {
|
||||
try {
|
||||
const cmd = `npx bktide pipelines --format json ${org}`;
|
||||
const output = execSync(cmd, {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
return JSON.parse(output);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get pipelines: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildsForPipeline(pipeline, commit, branch) {
|
||||
try {
|
||||
let cmd = `npx bktide builds --format json ${org}/${pipeline}`;
|
||||
if (commit) {
|
||||
// Note: bktide might not support commit filtering directly,
|
||||
// so we'll fetch recent builds and filter
|
||||
cmd += ` --state running --state scheduled`;
|
||||
}
|
||||
const output = execSync(cmd, {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
const builds = JSON.parse(output);
|
||||
|
||||
// Filter by commit
|
||||
return builds.filter((build) => {
|
||||
const commitMatch = !commit || build.commit?.startsWith(commit);
|
||||
const branchMatch = !branch || build.branch === branch;
|
||||
return commitMatch && branchMatch;
|
||||
});
|
||||
} catch (error) {
|
||||
// Pipeline might not exist or be accessible, return empty array
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
try {
|
||||
const allBuilds = [];
|
||||
|
||||
if (pipelineSlug) {
|
||||
// Search single pipeline
|
||||
const builds = getBuildsForPipeline(pipelineSlug, commit, branch);
|
||||
allBuilds.push(...builds);
|
||||
} else {
|
||||
// Search all pipelines
|
||||
const pipelines = getPipelines();
|
||||
for (const pipeline of pipelines) {
|
||||
const builds = getBuildsForPipeline(pipeline.slug, commit, branch);
|
||||
if (builds.length > 0) {
|
||||
allBuilds.push(...builds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (format === 'json') {
|
||||
console.log(JSON.stringify(allBuilds, null, 2));
|
||||
} else {
|
||||
if (allBuilds.length === 0) {
|
||||
console.log(`No builds found for commit ${commit}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Found ${allBuilds.length} build(s) for commit ${commit}:\n`);
|
||||
for (const build of allBuilds) {
|
||||
console.log(
|
||||
` ${build.pipeline.slug}#${build.number} - ${build.state}`
|
||||
);
|
||||
console.log(` Branch: ${build.branch}`);
|
||||
console.log(` URL: ${build.web_url}`);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(allBuilds.length > 0 ? 0 : 1);
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
107
skills/buildkite-status/scripts/get-build-logs.js
Executable file
107
skills/buildkite-status/scripts/get-build-logs.js
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Get build logs for a specific job
|
||||
*
|
||||
* Usage:
|
||||
* get-build-logs.js <org> <pipeline> <build> <job-label-or-uuid>
|
||||
*
|
||||
* Examples:
|
||||
* get-build-logs.js gusto payroll-building-blocks 29627 "ste rspec"
|
||||
* get-build-logs.js gusto payroll-building-blocks 29627 019a5f20-2d30-4c67-9edd-87fb92e1f487
|
||||
*
|
||||
* Features:
|
||||
* - Accepts job label or UUID
|
||||
* - Automatically resolves label to UUID if needed
|
||||
* - Handles step ID vs job UUID confusion
|
||||
* - Outputs formatted logs
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
function usage() {
|
||||
console.error(
|
||||
'Usage: get-build-logs.js <org> <pipeline> <build> <job-label-or-uuid>'
|
||||
);
|
||||
console.error('');
|
||||
console.error('Examples:');
|
||||
console.error(
|
||||
' get-build-logs.js gusto payroll-building-blocks 29627 "ste rspec"'
|
||||
);
|
||||
console.error(
|
||||
' get-build-logs.js gusto payroll-building-blocks 29627 019a5f20-2d30-4c67-9edd-87fb92e1f487'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getBuildDetails(org, pipeline, build) {
|
||||
try {
|
||||
const output = execSync(
|
||||
`npx bktide build ${org}/${pipeline}/${build} --format json`,
|
||||
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }
|
||||
);
|
||||
return JSON.parse(output);
|
||||
} catch (error) {
|
||||
console.error(`Error getting build details: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function isUuid(str) {
|
||||
// UUIDs are 36 characters with specific format
|
||||
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
||||
str
|
||||
);
|
||||
}
|
||||
|
||||
function resolveJobUuid(buildDetails, jobLabelOrUuid) {
|
||||
// If it looks like a UUID, assume it's a job UUID
|
||||
if (isUuid(jobLabelOrUuid)) {
|
||||
return jobLabelOrUuid;
|
||||
}
|
||||
|
||||
// Otherwise treat as label and search for matching job
|
||||
// Note: bktide JSON format needs to be checked - this is a placeholder
|
||||
console.error(`Note: Searching for job with label "${jobLabelOrUuid}"`);
|
||||
console.error(
|
||||
`Note: This script is a placeholder and needs MCP tool integration`
|
||||
);
|
||||
console.error(`Note: Use MCP buildkite:get_logs directly instead:`);
|
||||
console.error(``);
|
||||
console.error(`mcp__MCPProxy__call_tool("buildkite:get_logs", {`);
|
||||
console.error(` org_slug: "${buildDetails.organization.slug}",`);
|
||||
console.error(` pipeline_slug: "${buildDetails.pipeline.slug}",`);
|
||||
console.error(` build_number: "${buildDetails.number}",`);
|
||||
console.error(` job_id: "<job-uuid>"`);
|
||||
console.error(`})`);
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 4 || args.includes('--help') || args.includes('-h')) {
|
||||
usage();
|
||||
}
|
||||
|
||||
const [org, pipeline, build, jobLabelOrUuid] = args;
|
||||
|
||||
console.error(`Fetching build details for ${org}/${pipeline}/${build}...`);
|
||||
const buildDetails = getBuildDetails(org, pipeline, build);
|
||||
|
||||
console.error(`Resolving job identifier...`);
|
||||
const jobUuid = resolveJobUuid(buildDetails, jobLabelOrUuid);
|
||||
|
||||
console.error(`\nNote: This script is a placeholder.`);
|
||||
console.error(`For actual log retrieval, use MCP tools directly:`);
|
||||
console.error(``);
|
||||
console.error(`mcp__MCPProxy__call_tool("buildkite:get_logs", {`);
|
||||
console.error(` org_slug: "${org}",`);
|
||||
console.error(` pipeline_slug: "${pipeline}",`);
|
||||
console.error(` build_number: "${build}",`);
|
||||
console.error(` job_id: "${jobUuid}"`);
|
||||
console.error(`})`);
|
||||
}
|
||||
|
||||
main();
|
||||
84
skills/buildkite-status/scripts/parse-buildkite-url.js
Executable file
84
skills/buildkite-status/scripts/parse-buildkite-url.js
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Parse Buildkite URL to extract components
|
||||
*
|
||||
* Usage:
|
||||
* parse-buildkite-url.js <url>
|
||||
*
|
||||
* Examples:
|
||||
* parse-buildkite-url.js "https://buildkite.com/gusto/payroll-building-blocks/builds/29627"
|
||||
* parse-buildkite-url.js "https://buildkite.com/gusto/payroll-building-blocks/builds/29627/steps/canvas?sid=019a5f23..."
|
||||
*
|
||||
* Output:
|
||||
* JSON object with: org, pipeline, buildNumber, stepId (if present)
|
||||
*/
|
||||
|
||||
function usage() {
|
||||
console.error('Usage: parse-buildkite-url.js <url>');
|
||||
console.error('');
|
||||
console.error('Examples:');
|
||||
console.error(
|
||||
' parse-buildkite-url.js "https://buildkite.com/gusto/payroll-building-blocks/builds/29627"'
|
||||
);
|
||||
console.error(
|
||||
' parse-buildkite-url.js "https://buildkite.com/gusto/payroll-building-blocks/builds/29627/steps/canvas?sid=019a5f..."'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function parseBuildkiteUrl(url) {
|
||||
// Match build URL pattern
|
||||
const buildMatch = url.match(
|
||||
/buildkite\.com\/([^/]+)\/([^/]+)\/builds\/(\d+)/
|
||||
);
|
||||
|
||||
if (!buildMatch) {
|
||||
throw new Error(
|
||||
'Invalid Buildkite URL - expected format: https://buildkite.com/{org}/{pipeline}/builds/{number}'
|
||||
);
|
||||
}
|
||||
|
||||
const result = {
|
||||
org: buildMatch[1],
|
||||
pipeline: buildMatch[2],
|
||||
buildNumber: buildMatch[3],
|
||||
};
|
||||
|
||||
// Check for step ID query parameter
|
||||
const sidMatch = url.match(/[?&]sid=([^&]+)/);
|
||||
if (sidMatch) {
|
||||
result.stepId = sidMatch[1];
|
||||
result.note =
|
||||
'stepId is for UI routing only - use API to get job UUID for log retrieval';
|
||||
}
|
||||
|
||||
// Check for job UUID in path
|
||||
const jobMatch = url.match(/\/jobs\/([0-9a-f-]+)/i);
|
||||
if (jobMatch) {
|
||||
result.jobUuid = jobMatch[1];
|
||||
result.note = 'jobUuid can be used directly for log retrieval';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length !== 1 || args.includes('--help') || args.includes('-h')) {
|
||||
usage();
|
||||
}
|
||||
|
||||
const url = args[0];
|
||||
|
||||
try {
|
||||
const parsed = parseBuildkiteUrl(url);
|
||||
console.log(JSON.stringify(parsed, null, 2));
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
155
skills/buildkite-status/scripts/wait-for-build.js
Executable file
155
skills/buildkite-status/scripts/wait-for-build.js
Executable file
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Background build monitor for Buildkite
|
||||
*
|
||||
* Polls a build until it reaches a terminal state (passed, failed, canceled)
|
||||
* with configurable timeout and polling interval.
|
||||
*
|
||||
* Usage:
|
||||
* wait-for-build.js <org> <pipeline> <build-number> [options]
|
||||
*
|
||||
* Options:
|
||||
* --timeout <seconds> Maximum time to wait (default: 1800 = 30 minutes)
|
||||
* --interval <seconds> Polling interval (default: 30)
|
||||
* --quiet Suppress progress updates
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 - Build passed
|
||||
* 1 - Build failed
|
||||
* 2 - Build canceled
|
||||
* 3 - Timeout reached
|
||||
* 4 - Error occurred
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 3 || args[0] === '--help' || args[0] === '-h') {
|
||||
console.error(
|
||||
'Usage: wait-for-build.js <org> <pipeline> <build-number> [options]'
|
||||
);
|
||||
console.error('Options:');
|
||||
console.error(
|
||||
' --timeout <seconds> Maximum time to wait (default: 1800)'
|
||||
);
|
||||
console.error(' --interval <seconds> Polling interval (default: 30)');
|
||||
console.error(' --quiet Suppress progress updates');
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
const org = args[0];
|
||||
const pipeline = args[1];
|
||||
const buildNumber = args[2];
|
||||
|
||||
// Parse options
|
||||
let timeout = 1800; // 30 minutes default
|
||||
let interval = 30; // 30 seconds default
|
||||
let quiet = false;
|
||||
|
||||
for (let i = 3; i < args.length; i++) {
|
||||
if (args[i] === '--timeout' && i + 1 < args.length) {
|
||||
timeout = parseInt(args[++i], 10);
|
||||
} else if (args[i] === '--interval' && i + 1 < args.length) {
|
||||
interval = parseInt(args[++i], 10);
|
||||
} else if (args[i] === '--quiet') {
|
||||
quiet = true;
|
||||
}
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const timeoutMs = timeout * 1000;
|
||||
|
||||
function log(message) {
|
||||
if (!quiet) {
|
||||
console.log(`[${new Date().toISOString()}] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildStatus() {
|
||||
try {
|
||||
const cmd = `npx bktide build --format json gusto/${pipeline}#${buildNumber}`;
|
||||
const output = execSync(cmd, {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
return JSON.parse(output);
|
||||
} catch (error) {
|
||||
if (error.stdout) {
|
||||
try {
|
||||
return JSON.parse(error.stdout);
|
||||
} catch {}
|
||||
}
|
||||
throw new Error(`Failed to get build status: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function isTerminalState(state) {
|
||||
return ['passed', 'failed', 'canceled', 'blocked', 'skipped'].includes(state);
|
||||
}
|
||||
|
||||
function getExitCode(state) {
|
||||
switch (state) {
|
||||
case 'passed':
|
||||
return 0;
|
||||
case 'failed':
|
||||
return 1;
|
||||
case 'canceled':
|
||||
return 2;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
log(`Monitoring build: ${org}/${pipeline}#${buildNumber}`);
|
||||
log(`Timeout: ${timeout}s, Polling interval: ${interval}s`);
|
||||
|
||||
async function main() {
|
||||
while (true) {
|
||||
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
||||
|
||||
// Check timeout
|
||||
if (elapsed >= timeout) {
|
||||
log(`Timeout reached after ${elapsed}s`);
|
||||
process.exit(3);
|
||||
}
|
||||
|
||||
try {
|
||||
const build = getBuildStatus();
|
||||
const state = build.state;
|
||||
|
||||
log(`Build state: ${state} (elapsed: ${elapsed}s)`);
|
||||
|
||||
if (isTerminalState(state)) {
|
||||
log(`Build finished with state: ${state}`);
|
||||
log(`Build URL: ${build.web_url}`);
|
||||
|
||||
// Show job summary if available
|
||||
if (build.job_summary) {
|
||||
const summary = build.job_summary;
|
||||
log(`Jobs: ${summary.total} total`);
|
||||
if (summary.by_state) {
|
||||
const states = Object.entries(summary.by_state)
|
||||
.map(([s, count]) => `${count} ${s}`)
|
||||
.join(', ');
|
||||
log(` ${states}`);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(getExitCode(state));
|
||||
}
|
||||
|
||||
// Wait before next poll
|
||||
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
||||
} catch (error) {
|
||||
log(`Error checking build status: ${error.message}`);
|
||||
log('Retrying...');
|
||||
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error:', error.message);
|
||||
process.exit(4);
|
||||
});
|
||||
Reference in New Issue
Block a user