diff --git a/.github/actions/bash-script/action.yml b/.github/actions/bash-script/action.yml new file mode 100644 index 0000000..24684f1 --- /dev/null +++ b/.github/actions/bash-script/action.yml @@ -0,0 +1,38 @@ +name: 'File Processor' +description: 'Bash action with separate script file to process files' +author: 'Suspect Software' + +inputs: + path: + description: 'Path to search for files' + required: false + default: './' + pattern: + description: 'File pattern to match (e.g., *.md, *.js)' + required: false + default: '*.md' + operation: + description: 'Operation to perform (count/list/size)' + required: false + default: 'count' + +outputs: + count: + description: 'Number of files found (for count operation)' + files: + description: 'List of files found (for list operation)' + total-size: + description: 'Total size in bytes (for size operation)' + +runs: + using: 'composite' + steps: + - name: Execute file processor script + shell: bash + env: + INPUT_PATH: ${{ inputs.path }} + INPUT_PATTERN: ${{ inputs.pattern }} + INPUT_OPERATION: ${{ inputs.operation }} + run: | + chmod +x "${{ github.action_path }}/script.sh" + "${{ github.action_path }}/script.sh" diff --git a/.github/actions/bash-script/script.sh b/.github/actions/bash-script/script.sh new file mode 100755 index 0000000..b561bc8 --- /dev/null +++ b/.github/actions/bash-script/script.sh @@ -0,0 +1,82 @@ +#!/bin/bash +set -e + +# Script-based bash action +# This script demonstrates a more complex bash action with a separate script file + +echo "=== File Processor Action ===" + +# Read inputs from environment variables +INPUT_PATH="${INPUT_PATH:-./}" +INPUT_PATTERN="${INPUT_PATTERN:-*.md}" +INPUT_OPERATION="${INPUT_OPERATION:-count}" + +echo "Configuration:" +echo " Path: $INPUT_PATH" +echo " Pattern: $INPUT_PATTERN" +echo " Operation: $INPUT_OPERATION" +echo "" + +# Validate inputs +if [ ! -d "$INPUT_PATH" ] && [ ! -f "$INPUT_PATH" ]; then + echo "Error: Path does not exist: $INPUT_PATH" + exit 1 +fi + +# Perform operation +case "$INPUT_OPERATION" in + count) + echo "Counting files matching pattern..." + COUNT=$(find "$INPUT_PATH" -name "$INPUT_PATTERN" -type f 2>/dev/null | wc -l) + echo "Found $COUNT files" + echo "count=$COUNT" >> $GITHUB_OUTPUT + ;; + + list) + echo "Listing files matching pattern..." + FILES=$(find "$INPUT_PATH" -name "$INPUT_PATTERN" -type f 2>/dev/null) + if [ -n "$FILES" ]; then + echo "$FILES" + # Save to multiline output + { + echo 'files<> $GITHUB_OUTPUT + else + echo "No files found" + echo "files=" >> $GITHUB_OUTPUT + fi + ;; + + size) + echo "Calculating total size of files matching pattern..." + TOTAL_SIZE=0 + while IFS= read -r file; do + if [ -f "$file" ]; then + SIZE=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null) + TOTAL_SIZE=$((TOTAL_SIZE + SIZE)) + fi + done < <(find "$INPUT_PATH" -name "$INPUT_PATTERN" -type f 2>/dev/null) + + echo "Total size: $TOTAL_SIZE bytes" + echo "total-size=$TOTAL_SIZE" >> $GITHUB_OUTPUT + ;; + + *) + echo "Error: Unknown operation: $INPUT_OPERATION" + echo "Supported operations: count, list, size" + exit 1 + ;; +esac + +echo "" +echo "Operation completed successfully!" + +# Create step summary +{ + echo "### File Processor Results" + echo "- Operation: $INPUT_OPERATION" + echo "- Pattern: $INPUT_PATTERN" + echo "- Path: $INPUT_PATH" +} >> $GITHUB_STEP_SUMMARY diff --git a/.github/actions/bash-simple/action.yml b/.github/actions/bash-simple/action.yml new file mode 100644 index 0000000..ed49430 --- /dev/null +++ b/.github/actions/bash-simple/action.yml @@ -0,0 +1,52 @@ +name: 'Simple Bash Action' +description: 'A simple bash-based action that processes input and produces output' +author: 'Suspect Software' + +inputs: + name: + description: 'Name to greet' + required: true + greeting: + description: 'Greeting to use' + required: false + default: 'Hello' + format: + description: 'Output format (text/json)' + required: false + default: 'text' + +outputs: + message: + description: 'The greeting message' + time: + description: 'Time when the greeting was generated' + +runs: + using: 'composite' + steps: + - name: Generate greeting + shell: bash + run: | + # Get current time + CURRENT_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + # Generate message + MESSAGE="${{ inputs.greeting }}, ${{ inputs.name }}!" + + # Format output based on input + if [ "${{ inputs.format }}" == "json" ]; then + echo "Output in JSON format:" + echo "{\"message\": \"$MESSAGE\", \"time\": \"$CURRENT_TIME\"}" + else + echo "Output in text format:" + echo "$MESSAGE" + fi + + # Set outputs + echo "message=$MESSAGE" >> $GITHUB_OUTPUT + echo "time=$CURRENT_TIME" >> $GITHUB_OUTPUT + + # Add to step summary + echo "### Greeting Generated" >> $GITHUB_STEP_SUMMARY + echo "- Message: $MESSAGE" >> $GITHUB_STEP_SUMMARY + echo "- Time: $CURRENT_TIME" >> $GITHUB_STEP_SUMMARY diff --git a/.github/actions/composite-build-test/action.yml b/.github/actions/composite-build-test/action.yml new file mode 100644 index 0000000..a90de54 --- /dev/null +++ b/.github/actions/composite-build-test/action.yml @@ -0,0 +1,72 @@ +name: 'Build and Test' +description: 'Composite action to build and test a project' +author: 'Suspect Software' + +inputs: + build-command: + description: 'Command to build the project' + required: false + default: 'npm run build' + test-command: + description: 'Command to run tests' + required: false + default: 'npm test' + skip-tests: + description: 'Skip running tests' + required: false + default: 'false' + upload-coverage: + description: 'Upload test coverage report' + required: false + default: 'false' + +outputs: + build-status: + description: 'Status of the build step' + value: ${{ steps.build.outcome }} + test-status: + description: 'Status of the test step' + value: ${{ steps.test.outcome }} + +runs: + using: 'composite' + steps: + - name: Run linter + shell: bash + run: | + echo "Running linter..." + if [ -f "package.json" ] && grep -q '"lint"' package.json; then + npm run lint || echo "Linting completed with warnings" + else + echo "No linter configured, skipping..." + fi + + - name: Build project + id: build + shell: bash + run: | + echo "Building project with command: ${{ inputs.build-command }}" + ${{ inputs.build-command }} + + - name: Run tests + id: test + if: inputs.skip-tests != 'true' + shell: bash + run: | + echo "Running tests with command: ${{ inputs.test-command }}" + ${{ inputs.test-command }} + + - name: Upload coverage + if: inputs.upload-coverage == 'true' && steps.test.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage/ + retention-days: 30 + + - name: Build summary + shell: bash + run: | + echo "### Build and Test Summary" >> $GITHUB_STEP_SUMMARY + echo "- Build Status: ${{ steps.build.outcome }}" >> $GITHUB_STEP_SUMMARY + echo "- Test Status: ${{ steps.test.outcome }}" >> $GITHUB_STEP_SUMMARY diff --git a/.github/actions/composite-setup/action.yml b/.github/actions/composite-setup/action.yml new file mode 100644 index 0000000..7486a3c --- /dev/null +++ b/.github/actions/composite-setup/action.yml @@ -0,0 +1,54 @@ +name: 'Setup Environment' +description: 'Composite action to setup development environment with Node.js and dependencies' +author: 'Suspect Software' + +inputs: + node-version: + description: 'Node.js version to install' + required: false + default: '18' + cache-dependency-path: + description: 'Path to package-lock.json or yarn.lock for caching' + required: false + default: 'package-lock.json' + install-dependencies: + description: 'Whether to install dependencies' + required: false + default: 'true' + +outputs: + node-version: + description: 'The Node.js version that was installed' + value: ${{ steps.setup-node.outputs.node-version }} + cache-hit: + description: 'Whether dependencies were restored from cache' + value: ${{ steps.setup-node.outputs.cache-hit }} + +runs: + using: 'composite' + steps: + - name: Setup Node.js + id: setup-node + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: 'npm' + cache-dependency-path: ${{ inputs.cache-dependency-path }} + + - name: Install dependencies + if: inputs.install-dependencies == 'true' + shell: bash + run: | + if [ -f "package.json" ]; then + echo "Installing dependencies..." + npm ci + else + echo "No package.json found, skipping dependency installation" + fi + + - name: Print environment info + shell: bash + run: | + echo "Node.js version: $(node --version)" + echo "NPM version: $(npm --version)" + echo "Working directory: $(pwd)" diff --git a/.github/workflows/demo-bash-actions.yml b/.github/workflows/demo-bash-actions.yml new file mode 100644 index 0000000..6ec5b93 --- /dev/null +++ b/.github/workflows/demo-bash-actions.yml @@ -0,0 +1,92 @@ +name: Demo - Using Bash Actions + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' # Weekly on Sunday + +permissions: + contents: read + +jobs: + simple-bash-action: + runs-on: ubuntu-latest + name: Simple Bash Action Demo + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Use simple bash action with default greeting + - name: Greet with default + id: greet1 + uses: ./.github/actions/bash-simple + with: + name: 'World' + + - name: Display greeting output + run: | + echo "Message: ${{ steps.greet1.outputs.message }}" + echo "Time: ${{ steps.greet1.outputs.time }}" + + # Use simple bash action with custom greeting + - name: Greet with custom message + id: greet2 + uses: ./.github/actions/bash-simple + with: + name: 'GitHub Actions' + greeting: 'Welcome' + format: 'json' + + - name: Display custom greeting + run: | + echo "Message: ${{ steps.greet2.outputs.message }}" + echo "Time: ${{ steps.greet2.outputs.time }}" + + script-bash-action: + runs-on: ubuntu-latest + name: Script-based Bash Action Demo + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Count markdown files + - name: Count markdown files + id: count + uses: ./.github/actions/bash-script + with: + path: './' + pattern: '*.md' + operation: 'count' + + - name: Display count + run: | + echo "Found ${{ steps.count.outputs.count }} markdown files" + + # List YAML files + - name: List workflow files + id: list + uses: ./.github/actions/bash-script + with: + path: '.github/workflows' + pattern: '*.yml' + operation: 'list' + + - name: Display files + run: | + echo "Workflow files found:" + echo "${{ steps.list.outputs.files }}" + + # Calculate size of action files + - name: Calculate action files size + id: size + uses: ./.github/actions/bash-script + with: + path: '.github/actions' + pattern: '*.yml' + operation: 'size' + + - name: Display size + run: | + echo "Total size: ${{ steps.size.outputs.total-size }} bytes" diff --git a/.github/workflows/demo-composite-actions.yml b/.github/workflows/demo-composite-actions.yml new file mode 100644 index 0000000..72ee2b5 --- /dev/null +++ b/.github/workflows/demo-composite-actions.yml @@ -0,0 +1,65 @@ +name: Demo - Using Composite Actions + +on: + workflow_dispatch: + pull_request: + branches: [ main, develop ] + +permissions: + contents: read + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Use the composite setup action + - name: Setup environment + id: setup + uses: ./.github/actions/composite-setup + with: + node-version: '18' + cache-dependency-path: 'package-lock.json' + install-dependencies: 'true' + + - name: Display setup outputs + run: | + echo "Node version installed: ${{ steps.setup.outputs.node-version }}" + echo "Cache hit: ${{ steps.setup.outputs.cache-hit }}" + + # Use the composite build-test action + - name: Build and test + id: build-test + uses: ./.github/actions/composite-build-test + with: + build-command: 'echo "Building..." && mkdir -p dist' + test-command: 'echo "Testing..." && echo "All tests passed!"' + skip-tests: 'false' + upload-coverage: 'false' + + - name: Display build outputs + run: | + echo "Build status: ${{ steps.build-test.outputs.build-status }}" + echo "Test status: ${{ steps.build-test.outputs.test-status }}" + + # Example with different parameters + build-without-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup environment + uses: ./.github/actions/composite-setup + with: + node-version: '20' + + - name: Build only + uses: ./.github/actions/composite-build-test + with: + build-command: 'echo "Production build..." && mkdir -p dist/prod' + skip-tests: 'true' diff --git a/.github/workflows/demo-reusable-workflows.yml b/.github/workflows/demo-reusable-workflows.yml new file mode 100644 index 0000000..7e85786 --- /dev/null +++ b/.github/workflows/demo-reusable-workflows.yml @@ -0,0 +1,45 @@ +name: Demo - Using Reusable Workflows + +on: + workflow_dispatch: + push: + branches: [ main, develop ] + +permissions: + contents: read + +jobs: + # First job: Call the reusable CI workflow + ci: + uses: ./.github/workflows/reusable-ci.yml + with: + node-version: '20' + run-tests: true + + # Second job: Call the CI workflow with different parameters + ci-alternate: + uses: ./.github/workflows/reusable-ci.yml + with: + node-version: '18' + run-tests: false + + # Third job: Deploy to staging (depends on CI) + deploy-staging: + needs: ci + uses: ./.github/workflows/reusable-deploy.yml + with: + environment: staging + artifact-name: build-artifact + secrets: + DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} + + # Fourth job: Deploy to production (manual approval) + deploy-production: + needs: deploy-staging + uses: ./.github/workflows/reusable-deploy.yml + if: github.ref == 'refs/heads/main' + with: + environment: production + artifact-name: build-artifact + secrets: + DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml new file mode 100644 index 0000000..fde5ab8 --- /dev/null +++ b/.github/workflows/reusable-ci.yml @@ -0,0 +1,61 @@ +name: Reusable CI Workflow + +on: + workflow_call: + inputs: + node-version: + description: 'Node.js version to use' + required: false + type: string + default: '18' + run-tests: + description: 'Whether to run tests' + required: false + type: boolean + default: true + outputs: + test-result: + description: 'Result of the test execution' + value: ${{ jobs.ci.outputs.result }} + +permissions: + contents: read + +jobs: + ci: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.test.outputs.result }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: 'npm' + + - name: Install dependencies + run: | + echo "Installing dependencies..." + # npm ci or yarn install would go here + + - name: Run linter + run: | + echo "Running linter..." + # npm run lint would go here + + - name: Run tests + id: test + if: ${{ inputs.run-tests }} + run: | + echo "Running tests..." + echo "result=success" >> $GITHUB_OUTPUT + # npm test would go here + + - name: Build project + run: | + echo "Building project..." + # npm run build would go here diff --git a/.github/workflows/reusable-deploy.yml b/.github/workflows/reusable-deploy.yml new file mode 100644 index 0000000..7740608 --- /dev/null +++ b/.github/workflows/reusable-deploy.yml @@ -0,0 +1,60 @@ +name: Reusable Deployment Workflow + +on: + workflow_call: + inputs: + environment: + description: 'Target environment (staging/production)' + required: true + type: string + artifact-name: + description: 'Name of the artifact to deploy' + required: false + type: string + default: 'build-artifact' + secrets: + DEPLOY_TOKEN: + description: 'Token for deployment authentication' + required: true + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: ./dist + + - name: Validate deployment target + run: | + echo "Deploying to environment: ${{ inputs.environment }}" + if [[ "${{ inputs.environment }}" != "staging" && "${{ inputs.environment }}" != "production" ]]; then + echo "Error: Invalid environment specified" + exit 1 + fi + + - name: Deploy to ${{ inputs.environment }} + env: + DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} + run: | + echo "Deploying artifact: ${{ inputs.artifact-name }}" + echo "Environment: ${{ inputs.environment }}" + # Actual deployment commands would go here + # For example: scp, rsync, cloud provider CLI, etc. + echo "Deployment completed successfully!" + + - name: Post-deployment verification + run: | + echo "Verifying deployment..." + # Health check or smoke tests would go here + echo "Verification completed!" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3771e1a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,162 @@ +# Contributing to Workflow Examples + +Thank you for your interest in contributing to this repository! This guide will help you get started. + +## 🎯 Types of Contributions + +We welcome the following types of contributions: + +1. **New Examples**: Add new reusable workflows, composite actions, or bash actions +2. **Improvements**: Enhance existing examples with better practices or features +3. **Documentation**: Improve README, add comments, or create tutorials +4. **Bug Fixes**: Fix issues in existing examples + +## 📋 Guidelines + +### Adding New Examples + +When adding a new example, please: + +1. **Choose the Right Category**: + - Reusable workflows go in `.github/workflows/` + - Composite actions go in `.github/actions//` + - Bash actions can be simple (inline) or use separate script files + +2. **Follow Naming Conventions**: + - Use descriptive names: `reusable-.yml` + - Action folders: `-` (e.g., `composite-setup`) + - Demo workflows: `demo-.yml` + +3. **Include Documentation**: + - Add clear descriptions in action metadata + - Document all inputs and outputs + - Include usage examples in comments + - Update the main README.md + +4. **Add a Demo**: + - Create or update a demo workflow showing how to use your example + - Test that the demo works correctly + +### Code Style + +- **YAML Files**: + - Use 2 spaces for indentation + - Quote strings that contain special characters + - Add comments for complex logic + +- **Bash Scripts**: + - Use `set -e` for error handling + - Add comments explaining logic + - Validate inputs + - Use meaningful variable names + +- **Action Metadata**: + ```yaml + name: 'Action Name' + description: 'Clear description of what it does' + author: 'Your Name or Organization' + + inputs: + input-name: + description: 'What this input does' + required: false + default: 'sensible-default' + ``` + +### Testing + +Before submitting: + +1. Test your examples in a fork or test repository +2. Verify all inputs and outputs work as documented +3. Check that error cases are handled gracefully +4. Ensure demo workflows run successfully + +## 🚀 Submission Process + +1. **Fork the Repository** + ```bash + # Fork on GitHub, then clone your fork + git clone https://github.com/YOUR-USERNAME/workflow-examples.git + cd workflow-examples + ``` + +2. **Create a Branch** + ```bash + git checkout -b feature/my-new-example + ``` + +3. **Make Your Changes** + - Add your examples + - Update documentation + - Test thoroughly + +4. **Commit Your Changes** + ```bash + git add . + git commit -m "Add: description of your contribution" + ``` + +5. **Push and Create PR** + ```bash + git push origin feature/my-new-example + # Then create a Pull Request on GitHub + ``` + +### PR Description Template + +```markdown +## Description +Brief description of what this PR adds or changes. + +## Type of Change +- [ ] New example +- [ ] Enhancement to existing example +- [ ] Documentation improvement +- [ ] Bug fix + +## Testing +Describe how you tested your changes. + +## Checklist +- [ ] Code follows the style guidelines +- [ ] Documentation is updated +- [ ] Examples are tested and working +- [ ] Demo workflow is included/updated +``` + +## 💡 Example Ideas + +Looking for inspiration? Here are some examples we'd love to see: + +- **Reusable Workflows**: + - Security scanning workflow + - Release automation workflow + - Multi-platform build workflow + - Database migration workflow + +- **Composite Actions**: + - Docker build and push action + - Notification action (Slack, Teams, etc.) + - Code quality checks action + - Multi-language setup action + +- **Bash Actions**: + - Version bumping action + - Changelog generator + - Environment variable validator + - File content processor + +## ❓ Questions? + +If you have questions or need help: + +1. Check existing issues and discussions +2. Open a new issue with the `question` label +3. Reach out to maintainers + +## 📜 Code of Conduct + +Be respectful, inclusive, and constructive. We're all here to learn and improve. + +Thank you for contributing! 🎉 diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..d0d518c --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,395 @@ +# Detailed Examples and Use Cases + +This document provides additional context and detailed examples for using the workflows and actions in this repository. + +## Reusable Workflows + +### Example 1: Multi-Stage Pipeline + +Create a complete CI/CD pipeline using reusable workflows: + +```yaml +name: Complete Pipeline + +on: + push: + branches: [ main ] + +jobs: + # Stage 1: CI + continuous-integration: + uses: ./.github/workflows/reusable-ci.yml + with: + node-version: '20' + run-tests: true + + # Stage 2: Deploy to Staging + deploy-to-staging: + needs: continuous-integration + uses: ./.github/workflows/reusable-deploy.yml + with: + environment: staging + artifact-name: build-artifact + secrets: + DEPLOY_TOKEN: ${{ secrets.STAGING_TOKEN }} + + # Stage 3: Integration Tests + integration-tests: + needs: deploy-to-staging + runs-on: ubuntu-latest + steps: + - name: Run integration tests + run: | + echo "Running integration tests against staging..." + # Your integration tests here + + # Stage 4: Deploy to Production + deploy-to-production: + needs: integration-tests + uses: ./.github/workflows/reusable-deploy.yml + with: + environment: production + artifact-name: build-artifact + secrets: + DEPLOY_TOKEN: ${{ secrets.PRODUCTION_TOKEN }} +``` + +### Example 2: Matrix Strategy with Reusable Workflow + +Test across multiple Node.js versions: + +```yaml +name: Multi-Version Testing + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + node-version: [16, 18, 20] + uses: ./.github/workflows/reusable-ci.yml + with: + node-version: ${{ matrix.node-version }} + run-tests: true +``` + +## Composite Actions + +### Example 3: Chaining Composite Actions + +Use multiple composite actions in sequence: + +```yaml +name: Full Build Pipeline + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Step 1: Setup + - name: Setup environment + id: setup + uses: ./.github/actions/composite-setup + with: + node-version: '18' + install-dependencies: 'true' + + # Step 2: Build and Test + - name: Build and test + id: build + uses: ./.github/actions/composite-build-test + with: + build-command: 'npm run build' + test-command: 'npm run test:coverage' + upload-coverage: 'true' + + # Step 3: Use outputs from previous steps + - name: Summary + run: | + echo "Setup completed with Node ${{ steps.setup.outputs.node-version }}" + echo "Build status: ${{ steps.build.outputs.build-status }}" + echo "Test status: ${{ steps.build.outputs.test-status }}" +``` + +### Example 4: Conditional Composite Action Usage + +Use composite actions conditionally: + +```yaml +name: Conditional Build + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup for PR + uses: ./.github/actions/composite-setup + with: + node-version: '18' + + # Only run full tests on main branch PRs + - name: Full test suite + if: github.base_ref == 'main' + uses: ./.github/actions/composite-build-test + with: + build-command: 'npm run build' + test-command: 'npm run test:all' + upload-coverage: 'true' + + # Run quick tests on other PRs + - name: Quick tests + if: github.base_ref != 'main' + uses: ./.github/actions/composite-build-test + with: + build-command: 'npm run build' + test-command: 'npm run test:quick' + skip-tests: 'false' +``` + +## Bash Actions + +### Example 5: Processing Files with Bash Action + +Use the file processor action in various scenarios: + +```yaml +name: File Processing Examples + +on: [workflow_dispatch] + +jobs: + process-files: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Count different file types + - name: Count markdown files + id: count-md + uses: ./.github/actions/bash-script + with: + path: './' + pattern: '*.md' + operation: 'count' + + - name: Count JavaScript files + id: count-js + uses: ./.github/actions/bash-script + with: + path: './src' + pattern: '*.js' + operation: 'count' + + # List specific files + - name: List workflow files + id: list-workflows + uses: ./.github/actions/bash-script + with: + path: '.github/workflows' + pattern: '*.yml' + operation: 'list' + + # Calculate total size + - name: Calculate action size + id: action-size + uses: ./.github/actions/bash-script + with: + path: '.github/actions' + pattern: '*' + operation: 'size' + + # Create summary + - name: Create summary + run: | + echo "## File Statistics" >> $GITHUB_STEP_SUMMARY + echo "- Markdown files: ${{ steps.count-md.outputs.count }}" >> $GITHUB_STEP_SUMMARY + echo "- JavaScript files: ${{ steps.count-js.outputs.count }}" >> $GITHUB_STEP_SUMMARY + echo "- Total action size: ${{ steps.action-size.outputs.total-size }} bytes" >> $GITHUB_STEP_SUMMARY +``` + +### Example 6: Custom Notifications with Bash Action + +Use the simple bash action for notifications: + +```yaml +name: Deployment with Notifications + +on: + workflow_dispatch: + inputs: + environment: + description: 'Environment to deploy to' + required: true + type: choice + options: + - staging + - production + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Send start notification + - name: Deployment started + uses: ./.github/actions/bash-simple + with: + name: ${{ github.actor }} + greeting: 'Deployment to ${{ inputs.environment }} started by' + format: 'text' + + # Actual deployment steps + - name: Deploy + run: | + echo "Deploying to ${{ inputs.environment }}..." + # Your deployment commands here + + # Send completion notification + - name: Deployment completed + if: success() + uses: ./.github/actions/bash-simple + with: + name: ${{ inputs.environment }} + greeting: 'Successfully deployed to' + format: 'json' +``` + +## Advanced Patterns + +### Example 7: Combining All Three Types + +A real-world example using reusable workflows, composite actions, and bash actions: + +```yaml +name: Advanced Pipeline + +on: [push] + +jobs: + # Use composite actions for setup and build + build: + runs-on: ubuntu-latest + outputs: + artifact-name: ${{ steps.info.outputs.artifact }} + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/composite-setup + with: + node-version: '20' + + - uses: ./.github/actions/composite-build-test + with: + build-command: 'npm run build:production' + test-command: 'npm test' + + # Use bash action for file processing + - name: Check artifact size + id: size + uses: ./.github/actions/bash-script + with: + path: './dist' + pattern: '*' + operation: 'size' + + - name: Set artifact info + id: info + run: | + echo "artifact=build-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "Artifact size: ${{ steps.size.outputs.total-size }} bytes" + + - uses: actions/upload-artifact@v4 + with: + name: ${{ steps.info.outputs.artifact }} + path: ./dist + + # Use reusable workflow for deployment + deploy: + needs: build + uses: ./.github/workflows/reusable-deploy.yml + with: + environment: staging + artifact-name: ${{ needs.build.outputs.artifact-name }} + secrets: + DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} +``` + +## Tips and Tricks + +### Output Handling + +Accessing outputs from different action types: + +```yaml +# From reusable workflow +- uses: ./.github/workflows/reusable-ci.yml + # Outputs available at job level: ${{ needs.job-id.outputs.output-name }} + +# From composite action +- uses: ./.github/actions/composite-setup + id: setup + # Outputs available immediately: ${{ steps.setup.outputs.node-version }} + +# From bash action +- uses: ./.github/actions/bash-simple + id: greet + # Outputs available immediately: ${{ steps.greet.outputs.message }} +``` + +### Error Handling + +Best practices for error handling: + +```yaml +# In composite actions - use continue-on-error +- name: Optional step + uses: ./.github/actions/composite-build-test + continue-on-error: true + +# In bash actions - check exit codes +- name: Safe operation + uses: ./.github/actions/bash-script + with: + path: './optional-dir' + pattern: '*.txt' + operation: 'count' + # Action handles missing directories gracefully + +# In reusable workflows - use if conditions +deploy: + if: github.ref == 'refs/heads/main' + uses: ./.github/workflows/reusable-deploy.yml +``` + +## Debugging + +Enable debug logging: + +```bash +# In your repository settings, add these secrets: +ACTIONS_STEP_DEBUG=true +ACTIONS_RUNNER_DEBUG=true +``` + +Add debug output in your actions: + +```yaml +# In composite actions +- name: Debug info + shell: bash + run: | + echo "::debug::Current directory: $(pwd)" + echo "::debug::Files: $(ls -la)" +``` + +This should help you understand and use the examples more effectively! diff --git a/README.md b/README.md index 324341e..607f4f5 100644 --- a/README.md +++ b/README.md @@ -1 +1,294 @@ -# workflow-examples \ No newline at end of file +# GitHub Actions Workflow Examples + +A comprehensive collection of examples demonstrating GitHub Actions reusable workflows, composite actions, and bash-based actions. + +## 📚 Table of Contents + +- [Overview](#overview) +- [Repository Structure](#repository-structure) +- [Reusable Workflows](#reusable-workflows) +- [Composite Actions](#composite-actions) +- [Bash-Based Actions](#bash-based-actions) +- [Demo Workflows](#demo-workflows) +- [Usage Examples](#usage-examples) +- [Best Practices](#best-practices) +- [Contributing](#contributing) + +## 🎯 Overview + +This repository provides practical examples of three key GitHub Actions features: + +1. **Reusable Workflows**: Complete workflows that can be called from other workflows +2. **Composite Actions**: Custom actions composed of multiple steps +3. **Bash-Based Actions**: Simple actions using shell scripts + +Each example includes detailed inline documentation and demonstrates real-world use cases. + +## 📁 Repository Structure + +``` +.github/ +├── workflows/ +│ ├── reusable-ci.yml # Reusable CI workflow +│ ├── reusable-deploy.yml # Reusable deployment workflow +│ ├── demo-reusable-workflows.yml # Demo using reusable workflows +│ ├── demo-composite-actions.yml # Demo using composite actions +│ └── demo-bash-actions.yml # Demo using bash actions +└── actions/ + ├── composite-setup/ # Composite action for environment setup + │ └── action.yml + ├── composite-build-test/ # Composite action for build and test + │ └── action.yml + ├── bash-simple/ # Simple inline bash action + │ └── action.yml + └── bash-script/ # Bash action with separate script + ├── action.yml + └── script.sh +``` + +## 🔄 Reusable Workflows + +Reusable workflows allow you to call an entire workflow from another workflow, promoting DRY (Don't Repeat Yourself) principles. + +### Reusable CI Workflow + +**Location**: `.github/workflows/reusable-ci.yml` + +A flexible CI workflow that can be called with different parameters: + +**Features**: +- Configurable Node.js version +- Optional test execution +- Outputs test results +- Standard CI steps: checkout, setup, install, lint, test, build + +**Usage**: +```yaml +jobs: + ci: + uses: ./.github/workflows/reusable-ci.yml + with: + node-version: '20' + run-tests: true +``` + +### Reusable Deployment Workflow + +**Location**: `.github/workflows/reusable-deploy.yml` + +A deployment workflow supporting multiple environments: + +**Features**: +- Environment-specific deployments (staging/production) +- Artifact download and deployment +- Secret handling for deploy tokens +- Post-deployment verification + +**Usage**: +```yaml +jobs: + deploy: + uses: ./.github/workflows/reusable-deploy.yml + with: + environment: staging + artifact-name: build-artifact + secrets: + DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} +``` + +## 🧩 Composite Actions + +Composite actions group multiple steps into a reusable action that can be used across workflows. + +### Setup Environment Action + +**Location**: `.github/actions/composite-setup/action.yml` + +Sets up the development environment with Node.js and dependencies: + +**Features**: +- Node.js installation with configurable version +- Dependency caching +- Optional dependency installation +- Outputs: node version and cache hit status + +**Usage**: +```yaml +- name: Setup environment + uses: ./.github/actions/composite-setup + with: + node-version: '18' + cache-dependency-path: 'package-lock.json' + install-dependencies: 'true' +``` + +### Build and Test Action + +**Location**: `.github/actions/composite-build-test/action.yml` + +Runs linting, building, and testing with configurable commands: + +**Features**: +- Configurable build and test commands +- Optional test skipping +- Coverage report upload +- Build summary in GitHub UI +- Outputs: build and test status + +**Usage**: +```yaml +- name: Build and test + uses: ./.github/actions/composite-build-test + with: + build-command: 'npm run build' + test-command: 'npm test' + skip-tests: 'false' + upload-coverage: 'true' +``` + +## 💻 Bash-Based Actions + +Bash-based actions use shell scripts to perform custom operations. + +### Simple Bash Action + +**Location**: `.github/actions/bash-simple/action.yml` + +A simple greeting action demonstrating inline bash scripting: + +**Features**: +- Customizable greeting and name +- Multiple output formats (text/json) +- Timestamp output +- Step summary generation + +**Usage**: +```yaml +- name: Generate greeting + uses: ./.github/actions/bash-simple + with: + name: 'World' + greeting: 'Hello' + format: 'text' +``` + +### File Processor Action + +**Location**: `.github/actions/bash-script/` + +A more complex action using a separate bash script file: + +**Features**: +- File counting, listing, and size calculation +- Pattern matching support +- Multiple operations (count/list/size) +- Separate script file for better organization + +**Usage**: +```yaml +- name: Count files + uses: ./.github/actions/bash-script + with: + path: './' + pattern: '*.md' + operation: 'count' +``` + +## 🎬 Demo Workflows + +Three demonstration workflows show how to use all the examples: + +### Demo: Reusable Workflows +**File**: `.github/workflows/demo-reusable-workflows.yml` + +Demonstrates calling reusable workflows with different parameters and chaining them together. + +### Demo: Composite Actions +**File**: `.github/workflows/demo-composite-actions.yml` + +Shows how to use composite actions in real workflows, including accessing outputs. + +### Demo: Bash Actions +**File**: `.github/workflows/demo-bash-actions.yml` + +Illustrates both simple inline bash actions and script-based bash actions. + +## 📖 Usage Examples + +### Calling a Reusable Workflow + +```yaml +name: My Workflow + +on: [push] + +jobs: + build: + uses: owner/repo/.github/workflows/reusable-ci.yml@main + with: + node-version: '18' +``` + +### Using a Composite Action + +```yaml +steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/composite-setup + with: + node-version: '20' +``` + +### Using a Bash Action + +```yaml +steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/bash-simple + with: + name: 'GitHub' + greeting: 'Hi' +``` + +## ✨ Best Practices + +### Reusable Workflows +- Use `workflow_call` trigger +- Define clear inputs and outputs +- Document parameters with descriptions +- Version your workflows (use tags/branches) +- Keep workflows focused on a single purpose + +### Composite Actions +- Use meaningful names and descriptions +- Provide sensible defaults for inputs +- Document all inputs and outputs +- Use `shell: bash` for shell steps +- Group related steps together + +### Bash Actions +- Set `set -e` for error handling +- Validate inputs before processing +- Use environment variables for inputs +- Output to `$GITHUB_OUTPUT` for action outputs +- Use `$GITHUB_STEP_SUMMARY` for rich summaries +- Make scripts executable (`chmod +x`) + +## 🤝 Contributing + +Contributions are welcome! Please feel free to submit pull requests with: +- New example workflows or actions +- Improvements to existing examples +- Documentation enhancements +- Bug fixes + +## 📝 License + +See the [LICENSE](LICENSE) file for details. + +## 🔗 Resources + +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Reusable Workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows) +- [Creating Composite Actions](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action) +- [Creating a JavaScript Action](https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action) \ No newline at end of file