GitHub Actions Tutorial — Project: Custom Action
Project: Custom Action — PR Comment Bot
Build a composite action that posts a formatted comment on pull requests with build status, test results, and a link to the deployment preview.
Requirements
| Feature | Implementation |
|---|---|
| Composite action | action.yml with using: composite |
| Inputs | build-status, test-count, preview-url |
| Uses GitHub API | gh CLI to post comments |
| Publishable | Has branding and README |
| Tested | CI workflow tests the action |
Project Structure
pr-comment-action/
├── action.yml
├── comment.sh
├── .github/
│ └── workflows/
│ └── test.yml
├── README.md
└── LICENSE
action.yml
name: 'PR Comment Bot'
description: 'Posts a formatted build status comment on pull requests'
author: 'my-org'
branding:
icon: 'message-square'
color: 'blue'
inputs:
build-status:
description: 'Build status (success/failure)'
required: true
test-count:
description: 'Number of tests passed'
required: false
default: '0'
preview-url:
description: 'Preview deployment URL'
required: false
default: ''
github-token:
description: 'GitHub token for posting comments'
required: true
runs:
using: 'composite'
steps:
- name: Post comment
env:
BUILD_STATUS: ${{ inputs.build-status }}
TEST_COUNT: ${{ inputs.test-count }}
PREVIEW_URL: ${{ inputs.preview-url }}
GH_TOKEN: ${{ inputs.github-token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: bash ${{ github.action_path }}/comment.sh
shell: bash
comment.sh
#!/bin/bash
set -e
if [ "$BUILD_STATUS" = "success" ]; then
ICON="✅"
else
ICON="❌"
fi
BODY="## ${ICON} Build Report
| Metric | Value |
|--------|-------|
| **Status** | ${BUILD_STATUS} |
| **Tests** | ${TEST_COUNT} passed |"
if [ -n "$PREVIEW_URL" ]; then
BODY="${BODY}
| **Preview** | [Open Preview](${PREVIEW_URL}) |"
fi
BODY="${BODY}
---
*Posted by PR Comment Bot*"
gh pr comment "$PR_NUMBER" --repo "$REPO" --body "$BODY"
Test Workflow
.github/workflows/test.yml:
name: Test Action
on:
pull_request:
permissions:
pull-requests: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Test the action
uses: ./
with:
build-status: 'success'
test-count: '42'
preview-url: 'https://preview.example.com'
github-token: ${{ secrets.GITHUB_TOKEN }}
Publishing
- Push to a public repository
- Create a release:
git tag v1.0.0 && git push origin v1.0.0 - Create the
v1tag:git tag -fa v1 v1.0.0 && git push origin v1 --force - In GitHub Releases, check “Publish to Marketplace”
Usage by others:
- uses: my-org/pr-comment-action@v1
with:
build-status: 'success'
test-count: '42'
github-token: ${{ secrets.GITHUB_TOKEN }}
Design Decisions
| Decision | Reasoning |
|---|---|
| Composite (not JavaScript) | Simpler — no build step, no node_modules |
gh CLI for comments |
Pre-installed on GitHub runners; simpler than REST API |
| External script | Bash in action.yml is hard to maintain; separate file is testable |
| Token as input | Explicit — caller decides which token to use |
← Previous: Project — Reusable Library
Next: Practice Exercises →