-
Where Workflows Live: Your workflows are
.ymlor.yamlfiles stored in the.github/workflows/directory of your repository.my-repo/ ├── .github/ │ └── workflows/ │ └── my-first-workflow.yml └── src/ └── README.md -
The Workflow Structure: Every workflow needs a name and a trigger.
name: My First CI Workflow # A human-readable name for your workflow on: # The event that triggers this workflow push: # This workflow runs on every 'push' to the repo branches: - main # Specifically, when changes are pushed to the 'main' branch jobs: # Workflows are made of one or more 'jobs' build: # This is the ID of our first job (you can name it anything) runs-on: ubuntu-latest # The type of runner to use (e.g., Ubuntu, Windows, macOS) steps: # Jobs are made of a sequence of 'steps' - name: Checkout code # A friendly name for this step uses: actions/checkout@v4 # Use a pre-built action to clone your repo - name: Say Hello # A simple custom step run: echo "Hello, GitHub Actions!" # The command to execute on the runner
-
name: (Optional) A friendly name for the workflow, shown in the GitHub UI. -
on: Triggers – Specifies when the workflow should run.push,pull_request: Most common code-related events.workflow_dispatch: Manual trigger (run from GitHub UI).schedule: Runs on a cron schedule.release: Runs when a release is published.workflow_call: Allows one workflow to be called from another (reusable workflows!).- Filtering: You can filter by
branches,tags,paths, etc.on: push: branches: - main - 'feature/**' # glob pattern for feature branches pull_request: types: [opened, synchronize, reopened] # Specific PR actions workflow_dispatch: # Enable manual run from UI schedule: - cron: '0 0 * * *' # Run daily at midnight UTC
-
jobs: The core execution units. Each job runs on a fresh instance of aruns-onrunner.- Dependency (
needs): Jobs can depend on each other, running sequentially.jobs: build: # ... test: runs-on: ubuntu-latest needs: build # This job will only run after 'build' job completes successfully steps: # ... deploy: runs-on: ubuntu-latest needs: [test, lint] # Can depend on multiple jobs if: github.ref == 'refs/heads/main' # Only deploy if on main branch steps: # ...
- Concurrency: Control how many workflow runs or jobs can execute simultaneously.
- Dependency (
-
runs-on: The Runner Environment – The type of machine the job executes on.ubuntu-latest,windows-latest,macos-latest(GitHub-hosted runners)self-hosted(Your own machines)
-
steps: A sequence of tasks within a job. Steps are executed in order.name: A readable description for the step.run: Executes a command-line script.- name: Run a Bash command run: | echo "Building the project..." npm install npm run build
uses: Executes a GitHub Action (a reusable piece of code).- Official Actions:
actions/checkout@v4,actions/setup-node@v4,actions/upload-artifact@v4. - Community Actions: Look for
owner/repo-name@tag(e.g.,docker/build-push-action@v5). - Inputs: Actions often accept inputs using
with:.- name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' # Specify Node.js version cache: 'npm' # Cache npm dependencies
- Official Actions:
-
environment: Define an environment for a job, useful for managing secrets and deployments.jobs: deploy: runs-on: ubuntu-latest environment: production # Links to a GitHub environment configured in repo settings steps: # ...
-
if: Conditional Execution – Control when a job or step runs. Uses Go template-like expressions.- name: Deploy to Production if: success() && github.ref == 'refs/heads/main' # Only run if previous steps succeeded AND on main branch run: deploy_to_prod.sh
Common
iffunctions:success(),always(),cancelled(),failure(). -
env: Environment Variables – Define variables for a job or step.jobs: build: runs-on: ubuntu-latest env: # Job-level environment variable NODE_ENV: production steps: - name: Configure database run: echo "DB_HOST=${{ secrets.DB_HOST }}" # Accessing a secret env: # Step-level environment variable (overrides job-level if same name) API_KEY: ${{ secrets.MY_API_KEY }}
-
secrets: Securely store sensitive information (API keys, passwords). Never hardcode secrets! Accessed via${{ secrets.MY_SECRET_NAME }}. Configure them in the repo settings.
-
CI/CD Fundamentals (Build, Test, Deploy):
name: Build, Test & Deploy on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: { node-version: '20' } - name: Install dependencies run: npm ci - name: Build project run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: my-app-build path: dist/ # Path to your built files test: runs-on: ubuntu-latest needs: build # Ensure build artifact is ready steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: { node-version: '20' } - name: Download build artifacts uses: actions/download-artifact@v4 with: name: my-app-build path: dist/ - name: Run tests run: npm test # Or whatever your test command is deploy: runs-on: ubuntu-latest needs: test if: github.ref == 'refs/heads/main' # Only deploy from main branch environment: production # Link to a protected environment steps: - name: Deploy to my cloud provider run: | # Example: Using AWS CLI (configure AWS secrets in environment settings) aws s3 sync dist/ s3://my-prod-bucket --delete aws cloudfront create-invalidation --distribution-id E123456789 --paths "/*"
-
Matrix Jobs (Run on Multiple Configurations): Test code across different OS, language versions, etc.
jobs: test: runs-on: ${{ matrix.os }} # Use a variable from the matrix strategy: matrix: os: [ubuntu-latest, windows-latest] node-version: [18, 20] # Test with Node 18 and 20 steps: - uses: actions/checkout@v4 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm ci - name: Run tests run: npm test
-
Reusable Workflows (
workflow_call): Make a workflow reusable by other workflows. Great for standardizing pipelines.reusable-workflow.yml(in.github/workflows/):name: Reusable Build Step on: workflow_call: inputs: node-version: required: true type: string outputs: build-id: description: "The ID of the build artifact" value: ${{ jobs.build.outputs.build-id }} jobs: build: runs-on: ubuntu-latest outputs: build-id: ${{ steps.upload.outputs.artifact-id }} # Output from the upload action steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: ${{ inputs.node-version }} } - run: npm install && npm run build - id: upload uses: actions/upload-artifact@v4 with: name: build-output path: ./dist # You might need a way to get artifact ID for output, # this is simplified; usually, you'd use a unique ID.
caller-workflow.yml(in.github/workflows/):name: My Application CI on: [push] jobs: app-build: uses: ./.github/workflows/reusable-workflow.yml # Call the reusable workflow with: node-version: '20' secrets: inherit # Pass all secrets from the caller to the reusable workflow deploy-app: needs: app-build runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4 with: name: build-output # Download the artifact from the reusable workflow - run: echo "Deploying build ID: ${{ needs.app-build.outputs.build-id }}"
-
Scheduling Workflows:
name: Daily Report Generation on: schedule: # Runs at 00:00 (midnight) UTC every day # Learn more about cron syntax: https://crontab.guru/ - cron: '0 0 * * *' jobs: generate-report: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run report script run: python generate_report.py - name: Upload report uses: actions/upload-artifact@v4 with: name: daily-report path: report.txt
- Use
actions/checkout@v4: Almost every workflow needs to clone your repo. - Cache Dependencies: Speed up builds using
actions/cache@v4(e.g., fornode_modules,pippackages). - Artifacts: Use
actions/upload-artifactandactions/download-artifactto pass files between jobs. - Secrets, Secrets, Secrets: NEVER hardcode sensitive data. Use GitHub Secrets.
- Read the Logs: The GitHub Actions UI provides excellent logs for debugging.
- Marketplace: Explore the GitHub Actions Marketplace for pre-built actions.
- Self-Hosted Runners: For specific hardware, networking, or large-scale private needs.
on: workflow_dispatch: Add this to workflows you want to run manually from the UI.concurrency: Use to manage parallel runs of workflows or jobs to prevent race conditions or resource exhaustion.