GitHub Actions Script API Practical Implementation Guide¶
This article is a follow-up to the morning article
Morning article: GitHub Actions Self-Healing Workflows Implementation Guide
Target Audience
- Advanced users looking to leverage GitHub Actions Script API in production environments
Goals¶
- Master REST/GraphQL API selection strategies
- Implement authentication, permissions, and rate limiting solutions
- Establish error handling and logging best practices
Architecture Overview¶
GitHub Actions Script API operates in three execution contexts:
Workflow Context → Script Action → GitHub API
↓ ↓ ↓
- GITHUB_TOKEN - octokit/rest - Rate Limits
- Repository - context obj - Permissions
- Event payload - Error handling - Response format
Implementation Steps¶
Step 1: API Selection Strategy and Context Configuration¶
Understand REST vs GraphQL characteristics and use them appropriately.
- name: Smart API Usage Pattern
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_TOKEN }}
script: |
// REST: Simple CRUD operations
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'bug'
});
// GraphQL: Complex queries and related data fetching
const query = `
query($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
pullRequests(first: 10, states: OPEN) {
nodes {
number
title
author { login }
reviews(first: 5) {
nodes { state author { login } }
}
}
}
}
}
`;
const gqlResult = await github.graphql(query, {
owner: context.repo.owner,
repo: context.repo.repo
});
Step 2: Permission Escalation and Token Management¶
Implement differentiation between Personal Access Tokens (PAT) and GITHUB_TOKEN.
- name: Permission-aware API Operations
uses: actions/github-script@v7
env:
ADMIN_TOKEN: ${{ secrets.ADMIN_PAT }}
with:
script: |
// Permission check function
async function checkPermissions(requiredScope) {
try {
const { data: user } = await github.rest.users.getAuthenticated();
const { data: perms } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: user.login
});
const hasPermission = perms.permission === 'admin' ||
perms.permission === requiredScope;
if (!hasPermission) {
// Fallback: Use Admin PAT
const adminOctokit = github.getOctokit(process.env.ADMIN_TOKEN);
return adminOctokit;
}
return github;
} catch (error) {
core.setFailed(`Permission check failed: ${error.message}`);
}
}
// Example usage: Branch Protection settings
const apiClient = await checkPermissions('admin');
await apiClient.rest.repos.updateBranchProtection({
owner: context.repo.owner,
repo: context.repo.repo,
branch: 'main',
required_status_checks: {
strict: true,
contexts: ['build', 'test']
},
enforce_admins: false
});
Step 3: Rate Limiting and Batch Processing Optimization¶
Implement progressive backoff strategies for API limits.
- name: Rate-limit Resilient Processing
uses: actions/github-script@v7
with:
script: |
class RateLimitHandler {
constructor(octokit, options = {}) {
this.github = octokit;
this.maxRetries = options.maxRetries || 3;
this.baseDelay = options.baseDelay || 1000;
}
async executeWithRetry(apiCall, retryCount = 0) {
try {
const result = await apiCall();
// Log rate limit information
const remaining = result.headers['x-ratelimit-remaining'];
if (remaining && parseInt(remaining) < 100) {
const resetTime = new Date(result.headers['x-ratelimit-reset'] * 1000);
console.log(`⚠️ Rate limit low: ${remaining} remaining, resets at ${resetTime}`);
}
return result;
} catch (error) {
if (error.status === 403 && error.message.includes('rate limit')) {
if (retryCount < this.maxRetries) {
const delay = this.baseDelay * Math.pow(2, retryCount);
console.log(`Rate limited. Retrying in ${delay}ms... (${retryCount + 1}/${this.maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay));
return this.executeWithRetry(apiCall, retryCount + 1);
}
}
throw error;
}
}
}
// Usage example: Bulk issue processing
const handler = new RateLimitHandler(github);
const issues = await handler.executeWithRetry(async () => {
return github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100,
state: 'all'
});
});
Performance Comparison¶
| API Type | Processing Speed | Rate Limit | Complex Queries | Recommended Use |
|---|---|---|---|---|
| REST API | High | 5000req/hour | △ | CRUD operations |
| GraphQL API | Medium | 5000points/hour | ◎ | Related data fetching |
| GitHub App | Highest | 15000req/hour | ◎ | Large-scale processing |
Common Failure Patterns and Solutions¶
| Symptom | Cause | Solution |
|---|---|---|
| 403 Permission denied | Insufficient GITHUB_TOKEN permissions | Use PAT or App authentication |
| 404 Not Found | Private repository access | Add repo scope permissions |
| Rate limit exceeded | Excessive API calls | Implement batching and progressive backoff |
| Invalid GraphQL query | Query syntax errors | Use schema validation tools |
Automation Extension Ideas¶
- API Usage Monitoring: Set up rate limit alerts with CloudWatch/DataDog
- Caching Strategy: Store frequently accessed data with Redis/Memcached
- Log Aggregation: Analyze API execution history with ELK stack
- Test Automation: Mock API calls and regression testing
- Permission Auditing: Periodic token permission checks and automatic renewal