Skip to content

Consider converting from bash to JavaScript for cleaner API interactions #30

@DerekRoberts

Description

@DerekRoberts

Problem

The current bash implementation requires:

  • Manual API URL construction
  • curl + jq for API calls and JSON parsing
  • Complex string manipulation for extracting PR numbers (e.g., sed -n 's|^queue/[^/]*/pr-\([0-9]\+\).*|\1|p')
  • Multiple fallback strategies that are harder to maintain in bash
  • Limited testability (requires GitHub Actions environment)
  • Harder for JS/TS developers to contribute and maintain

Proposed Solution

Convert the action from a composite (bash) action to a Node.js action using:

  • @actions/core for GitHub Actions integration
  • @actions/github with Octokit for clean API calls
  • TypeScript for type safety and better maintainability
  • Native JavaScript for better error handling

Benefits

Organizational

  1. Team alignment: Organization is stronger with JS/TS than bash
  2. Easier contributions: More developers can review and contribute
  3. Better code reviews: TypeScript makes intent clearer
  4. Consistency: Aligns with other actions/tooling in the organization

Technical

  1. Cleaner API calls: Use Octokit SDK instead of curl + jq
    // Instead of: curl + jq parsing
    const { data: pulls } = await octokit.rest.repos.listPullRequestsAssociatedWithCommit({
      owner, repo, commit_sha: sha
    });
    pr = pulls.length > 0 ? pulls[0].number.toString() : null;
  2. Better error handling: Native try/catch and error objects
  3. More maintainable: Easier to read and modify logic
  4. No external dependencies: No need for jq in the runner
  5. Type safety: TypeScript catches errors at compile time
  6. Testability: Unit tests with mocked GitHub API
  7. Simpler string manipulation: Native regex instead of sed/grep chains

Readability Example

Bash (current):

pr=$(echo "${MERGE_GROUP_HEAD_REF}" | sed -n 's|^queue/[^/]*/pr-\([0-9]\+\).*|\1|p')

TypeScript (proposed):

const match = headRef.match(/^queue\/[^\/]+\/pr-(\d+)/);
const pr = match ? match[1] : null;

Migration Plan

Phase 1: Setup

  1. Create package.json with dependencies:
    • @actions/core
    • @actions/github
    • typescript
    • @types/node
    • @vercel/ncc (for bundling)
  2. Add TypeScript config (tsconfig.json)
  3. Add build scripts and GitHub Actions workflow for building

Phase 2: Implementation

  1. Create src/main.ts with TypeScript implementation
  2. Port all event handlers (pull_request, merge_group, push, release, workflow_dispatch)
  3. Implement helper functions:
    • getPrFromApi() using Octokit
    • getPrFromGit() using simple-git or exec
    • logDebug() using @actions/core
  4. Update action.yml to use node16 or node20 instead of composite

Phase 3: Testing

  1. Add unit tests with Jest/Vitest
  2. Mock GitHub API responses
  3. Test all event types
  4. Update existing workflow tests to verify compatibility

Phase 4: Rollout

  1. Create feature branch
  2. Test in staging/test workflows
  3. Merge and release as minor version (backward compatible)
  4. Monitor for issues

File Structure

action-get-pr/
├── src/
│   └── main.ts          # Main action logic
├── __tests__/
│   └── main.test.ts     # Unit tests
├── dist/
│   └── index.js         # Bundled output (git-ignored)
├── action.yml           # Updated to use node
├── package.json
├── tsconfig.json
└── .github/workflows/
    └── build.yml        # Build and test workflow

Considerations

  • Action size: Will be slightly larger, but Node.js is already in runners
  • Build step: Need to bundle with ncc and commit dist/index.js
  • Backward compatibility: Should maintain same inputs/outputs
  • Migration effort: ~108 lines of bash → ~200-300 lines of TypeScript (with tests)

Related

This would simplify the implementation added in #29 for workflow_dispatch support and improve maintainability for the team.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

Next

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions