GitHub Actions Certification
Custom Actions
Troubleshooting JavaScript Actions using Workflow Commands
Learn how to debug custom JavaScript GitHub Actions step by step. We’ll use workflow commands and the @actions/core
toolkit to inspect annotations, logs, summaries, and properly handle failures. By the end, you’ll be able to:
- Read inputs and set outputs
- Log at different levels (info, notice, warning, error)
- Mask secrets and export environment variables
- Generate job summaries
- Fail actions with descriptive exit codes
Table of Contents
- Importing and Cloning the Repository
- Reviewing action.yaml
- Examining package.json
- Business Logic in index.js
- Building the Action
- Testing with a Workflow
- Enabling Actions and Running the Workflow
7.1 Viewing Annotations
7.2 Inspecting Logs - Error Scenario: Invalid Phone Number
- Setting Failure Exit Codes
- Conclusion
- Links & References
1. Importing and Cloning the Repository
First, import the sample project into your GitHub account. Choose TroubleshootingJS-Actions, set visibility to Public, and click Import.
Then clone it locally:
git clone https://github.com/your-org/TroubleshootingJS-Actions.git
cd TroubleshootingJS-Actions
2. Reviewing action.yaml
Open action.yaml, the manifest that defines inputs, outputs, and runtime:
name: 'Demo - Troubleshooting Custom JavaScript Actions'
description: 'Use @actions/core methods for logging, secrets, and summaries'
inputs:
name:
description: 'User name'
required: true
default: 'Siddharth'
phone:
description: 'User phone number'
required: true
default: '0123456789'
country:
description: 'User country'
required: true
default: 'india'
outputs:
customized_greeting:
description: 'Greeting passed to next steps'
runs:
using: 'node20'
main: 'dist/index.js'
Section | Purpose |
---|---|
inputs | Define user-provided values (name, phone, country) |
outputs | Pass data to downstream steps |
runs | Specify Node.js version and bundled entrypoint |
3. Examining package.json
The package.json file lists dependencies for your action bundle:
{
"name": "troubleshooting-custom-action",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@vercel/ncc": "^0.38.0"
}
}
- @actions/core: Toolkit for inputs, outputs, logging, secret masking
- @vercel/ncc: Bundles your JavaScript into a single file
Refer to the actions toolkit documentation for examples such as:
const core = require('@actions/core');
core.getInput('name', { required: true });
core.setOutput('customized_greeting', 'Hello World');
core.exportVariable('ENV_VAR', 'value');
4. Business Logic in index.js
The main script reads inputs, logs messages, masks secrets, exports variables, and builds a summary:
const core = require('@actions/core');
try {
// 1. Read inputs
const nameInput = core.getInput('name', { required: true });
const phoneInput = core.getInput('phone', { required: true });
const country = core.getInput('country', { required: true });
// 2. Debug logging
if (core.isDebug()) {
core.info('Running in debug mode');
core.debug('Debug-level message');
}
core.info('---------- START ----------');
// 3. Prepare greeting
const greeting = `Hello ${nameInput}, your phone number is ${phoneInput}`;
// 4. Log at different levels
core.info(`Info: ${greeting}`);
core.notice(`Notice: ${greeting}`);
core.warning(`Warning: ${greeting}`);
core.error(`Error: ${greeting}`);
core.info('---------- END ------------');
// 5. Set output
core.setOutput('customized_greeting', greeting);
// 6. Validate phone and export variable
if (phoneInput.length !== 10) {
core.error(`Invalid phone number: ${phoneInput}`);
// core.setFailed('Invalid phone number provided!');
} else {
const prefix = country.toLowerCase() === 'india' ? '+91'
: country.toLowerCase() === 'canada' ? '+1' : '';
core.exportVariable('JS_ACTION_PHONE_VAR', `${prefix}${phoneInput}`);
}
// 7. Mask secret and write summary
core.setSecret(phoneInput);
core.summary
.addHeading('Action Summary')
.addCodeBlock("const core = require('@actions/core');", 'js')
.addTable([
[{ data: 'Name', header: true }, { data: 'Country', header: true }, { data: 'Phone', header: true }],
[nameInput, country, phoneInput]
])
.addQuote('Phone number has been masked')
.addLink('View Repository', 'https://github.com/sidd-harth/troubleshooting-actions')
.write();
} catch (error) {
core.setFailed(error.message);
}
Note
The core.setSecret()
method masks sensitive values in logs. Use it whenever you log or store secrets.
5. Building the Action
Install dependencies and bundle into dist/index.js
:
npm install
npx ncc build index.js --license licenses.txt
This creates dist/index.js
with all required modules included.
6. Testing with a Workflow
Create .github/workflows/test.yml
to trigger the action manually:
on:
workflow_dispatch:
inputs:
name:
description: 'User name'
required: true
default: 'Sid'
phone:
description: 'User phone'
required: true
default: '9876543210'
country:
description: 'User country'
required: true
default: 'india'
type: choice
options: [india, canada, others]
jobs:
custom-action:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Troubleshooting Action
id: action_step
uses: ./
with:
name: ${{ inputs.name }}
phone: ${{ inputs.phone }}
country:${{ inputs.country }}
- name: Display Outputs
run: |
echo "Greeting: ${{ steps.action_step.outputs.customized_greeting }}"
echo "Phone Var: $JS_ACTION_PHONE_VAR"
7. Enabling Actions and Running the Workflow
Imported repositories have Actions disabled by default. Enable them before running:
- Go to Settings → Actions → General
- Under Actions permissions, select Allow all actions and reusable workflows
Warning
core.error()
logs an error but does not fail the step. To stop the job on error, use core.setFailed()
.
Once enabled, trigger the workflow via Actions → Run workflow with default inputs. The job should succeed.
7.1 Viewing Annotations
GitHub UI displays annotations from different logging levels:
7.2 Inspecting Logs
Detailed logs show messages and confirm that secrets are masked:
8. Error Scenario: Invalid Phone Number
If the phone number is shorter than 10 digits, you’ll see the logged error but the job remains green (until you opt to fail it):
9. Setting Failure Exit Codes
To mark the step as failed and stop downstream jobs, uncomment core.setFailed()
:
if (phoneInput.length !== 10) {
core.error(`Invalid phone number: ${phoneInput}`);
core.setFailed('Invalid phone number provided!');
} else {
// Export logic...
}
Rebuild and commit, then rerun. A short number now causes the action to fail and skip subsequent steps.
After failure is enabled, the logs will clearly indicate a non-zero exit:
10. Conclusion
You’ve covered essential patterns for debugging JavaScript Actions:
- Use
@actions/core
for inputs, outputs, logs, secrets, and summaries - Bundle with ncc for single-file distribution
- Write workflows to validate functionality
- View annotations and logs directly in the GitHub UI
- Fail actions properly with
core.setFailed()
Apply these techniques to create maintainable, self-documented, and debuggable GitHub Actions.
11. Links & References
- GitHub Actions Toolkit – core
- GitHub Actions Exit Codes
- ncc Bundler by Vercel
- GitHub Workflow Syntax
- Node.js v20
Watch Video
Watch video content