Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .commitlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [
2,
"always",
[
"feat",
"fix",
"docs",
"style",
"refactor",
"perf",
"test",
"build",
"ci",
"chore",
"revert"
]
],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"scope-case": [2, "always", "lower-case"],
"subject-empty": [2, "never"],
"subject-full-stop": [2, "never", "."],
"subject-case": [
2,
"never",
["sentence-case", "start-case", "pascal-case", "upper-case"]
],
"header-max-length": [2, "always", 100],
"body-leading-blank": [1, "always"],
"body-max-line-length": [2, "always", 100],
"footer-leading-blank": [1, "always"],
"footer-max-line-length": [2, "always", 100]
}
}

65 changes: 65 additions & 0 deletions .github/actions/hello-action/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# =============================================================================
# GitHub Action: Hello World with Timestamp
# =============================================================================
# This is a composite GitHub Action that demonstrates best practices for
# creating reusable actions. It greets a user and returns a timestamp.
#
# Features:
# - Input validation
# - Output generation
# - Error handling
# - Modern Node.js runtime (node20)
# - Comprehensive documentation
# =============================================================================

name: 'Hello World Action'
description: 'A professional greeting action that demonstrates GitHub Actions best practices'
author: 'GitHub Actions Team'

# Branding for GitHub Marketplace (optional but recommended)
branding:
icon: 'message-circle'
color: 'blue'

# =============================================================================
# INPUTS
# =============================================================================
# Define all inputs with clear descriptions, types, and defaults
inputs:
who-to-greet:
description: 'Name of the person to greet'
required: false
default: 'World'

greeting-style:
description: 'Style of greeting (formal, casual, enthusiastic)'
required: false
default: 'casual'

include-timestamp:
description: 'Whether to include a timestamp in the greeting'
required: false
default: 'true'

# =============================================================================
# OUTPUTS
# =============================================================================
# Define all outputs that consumers of this action can use
outputs:
message:
description: 'The complete greeting message'

timestamp:
description: 'ISO 8601 timestamp of when the greeting was generated'

greeted-person:
description: 'The name of the person who was greeted'

# =============================================================================
# RUNTIME CONFIGURATION
# =============================================================================
# Use the latest stable Node.js runtime for better performance and security
runs:
using: 'node20'
main: 'index.js'

173 changes: 173 additions & 0 deletions .github/actions/hello-action/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/**
* =============================================================================
* GitHub Action: Hello World Implementation
* =============================================================================
*
* This action demonstrates best practices for GitHub Actions development:
* - Proper error handling with try-catch
* - Input validation and sanitization
* - Structured logging
* - Output setting
* - GitHub context usage
* - Type safety considerations
*
* Dependencies:
* - @actions/core: Core utilities for actions (inputs, outputs, logging)
* - @actions/github: GitHub API client and webhook context
*
* @see https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action
* =============================================================================
*/

const core = require('@actions/core');
const github = require('@actions/github');

/**
* Generates a greeting based on the specified style
* @param {string} name - Name of the person to greet
* @param {string} style - Style of greeting (formal, casual, enthusiastic)
* @returns {string} The formatted greeting
*/
function generateGreeting(name, style) {
const greetings = {
formal: `Good day, ${name}. It is a pleasure to greet you.`,
casual: `Hello, ${name}!`,
enthusiastic: `🎉 Hey there, ${name}! Great to see you! 🎉`
};

// Default to casual if style not recognized
return greetings[style] || greetings.casual;
}

/**
* Validates input parameters
* @param {string} name - Name to validate
* @param {string} style - Style to validate
* @throws {Error} If validation fails
*/
function validateInputs(name, style) {
if (!name || name.trim().length === 0) {
throw new Error('Name cannot be empty');
}

const validStyles = ['formal', 'casual', 'enthusiastic'];
if (!validStyles.includes(style)) {
core.warning(
`Invalid greeting style '${style}'. Valid options: ${validStyles.join(', ')}. Defaulting to 'casual'.`
);
}
}

/**
* Main action entry point
* Orchestrates the greeting logic and handles all I/O
*/
async function run() {
try {
// =========================================================================
// 1. INPUT RETRIEVAL
// =========================================================================
// Get inputs defined in action.yml
const nameToGreet = core.getInput('who-to-greet');
const greetingStyle = core.getInput('greeting-style').toLowerCase();
const includeTimestamp = core.getInput('include-timestamp') === 'true';

// Log inputs for debugging (be careful not to log secrets!)
core.debug(`Greeting ${nameToGreet} with ${greetingStyle} style`);

// =========================================================================
// 2. INPUT VALIDATION
// =========================================================================
validateInputs(nameToGreet, greetingStyle);

// =========================================================================
// 3. BUSINESS LOGIC
// =========================================================================
const greeting = generateGreeting(nameToGreet, greetingStyle);
const timestamp = new Date().toISOString();
const localTime = new Date().toLocaleString('en-US', {
timeZone: 'UTC',
dateStyle: 'full',
timeStyle: 'long'
});

// =========================================================================
// 4. OUTPUT GENERATION
// =========================================================================
let message = greeting;
if (includeTimestamp) {
message += ` (Greeted at: ${localTime})`;
}

// Log to console (visible in action logs)
core.info('='.repeat(80));
core.info(message);
core.info('='.repeat(80));

// Set outputs that can be used by subsequent steps
core.setOutput('message', message);
core.setOutput('timestamp', timestamp);
core.setOutput('greeted-person', nameToGreet);

// =========================================================================
// 5. GITHUB CONTEXT (Demonstrating advanced features)
// =========================================================================
// Log GitHub context information
core.startGroup('GitHub Context Information');
core.info(`Repository: ${github.context.repo.owner}/${github.context.repo.repo}`);
core.info(`Event: ${github.context.eventName}`);
core.info(`Actor: ${github.context.actor}`);
core.info(`Ref: ${github.context.ref}`);
core.info(`SHA: ${github.context.sha}`);
core.endGroup();

// Optionally log the full payload (useful for debugging webhooks)
if (core.isDebug()) {
core.startGroup('Event Payload');
core.debug(JSON.stringify(github.context.payload, null, 2));
core.endGroup();
}

// =========================================================================
// 6. SUCCESS SUMMARY
// =========================================================================
// Create a job summary (visible in the GitHub UI)
await core.summary
.addHeading('Greeting Summary 👋')
.addTable([
[{data: 'Field', header: true}, {data: 'Value', header: true}],
['Person Greeted', nameToGreet],
['Greeting Style', greetingStyle],
['Timestamp', localTime],
['Message', greeting]
])
.write();

} catch (error) {
// =========================================================================
// 7. ERROR HANDLING
// =========================================================================
// Proper error handling ensures the action fails gracefully
// and provides useful debugging information

core.error(`Action failed: ${error.message}`);

// Include stack trace in debug mode
if (error.stack) {
core.debug(error.stack);
}

// Set the action as failed
// This will mark the workflow step as failed and stop the workflow
// (unless continue-on-error: true is set)
core.setFailed(error.message);
}
}

// =============================================================================
// ENTRY POINT
// =============================================================================
// Execute the action
// Note: We use an async IIFE to allow await in the main function
run();

21 changes: 21 additions & 0 deletions .github/actions/hello-action/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "hello-action",
"version": "1.0.0",
"description": "A professional GitHub Action demonstrating best practices",
"main": "index.js",
"scripts": {
"test": "echo \"Add tests here\" && exit 0"
},
"keywords": [
"github-actions",
"greeting",
"hello-world"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0"
}
}

Loading
Loading