Learn GitHub Actions #494
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This is a GitHub Actions workflow file | |
| # Workflows are defined in YAML files in your repository's .github/workflows/ directory | |
| # Comments in YAML look like this. | |
| # Name of the workflow (displayed in the Actions tab) | |
| name: Learn GitHub Actions | |
| # Optional: The name for workflow runs generated from the workflow. | |
| run-name: Learning GitHub Actions workflow triggered by ${{ github.actor }} | |
| # Events that trigger the workflow | |
| on: | |
| # Trigger on push | |
| push: | |
| # Path filters | |
| paths: | |
| - "src/**" # Any file in src/ and subdirectories | |
| - "docs/*.md" # Markdown files in docs/ only | |
| - "**.js" # JavaScript files anywhere | |
| - "!src/test/**" # Exclude test files | |
| - "config/[a-z]*.yml" # YAML files starting with lowercase letter | |
| - ".github/workflows/**" # Any workflow file changes | |
| # Branch filters | |
| branches: | |
| - main # main branch | |
| - "release/v[0-9].[0-9]" # release/v1.0, release/v2.1, etc. | |
| - "feature/*" # Any feature branch | |
| - "!experimental" # Exclude experimental branch | |
| # Tag filters | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" # Semantic version tags | |
| - "!*-alpha" # Exclude alpha tags | |
| # Trigger on pull requests targeting main | |
| pull_request: | |
| branches: [main] | |
| # Trigger on pull request reviews | |
| pull_request_review: | |
| types: [submitted] | |
| # Trigger manually from the GitHub UI | |
| workflow_dispatch: | |
| # Optional: Define inputs for manual trigger | |
| inputs: | |
| example-input: | |
| description: "An example input for manual trigger" | |
| required: false # Whether this input is required | |
| default: "default-value" | |
| type: string # type can be string, boolean, choice, environment, or number | |
| # Trigger on a schedule (cron syntax) | |
| schedule: | |
| - cron: "0 0 * * *" # Daily at midnight UTC | |
| # Environment variables available to all jobs | |
| env: | |
| NODE_VERSION: 24 | |
| PYTHON_VERSION: "3.9" | |
| # Use permissions to modify the default permissions granted to the GITHUB_TOKEN | |
| # Start read-only at the top level and grant writes per job that needs them. | |
| permissions: | |
| read-all # write-all|read-all or disable with {} | |
| # actions: read|write|none | |
| # attestations: read|write|none | |
| # checks: read|write|none | |
| # contents: read|write|none | |
| # deployments: read|write|none | |
| # id-token: write|none | |
| # issues: read|write|none | |
| # models: read|none | |
| # discussions: read|write|none | |
| # packages: read|write|none | |
| # pages: read|write|none | |
| # pull-requests: read|write|none | |
| # security-events: read|write|none | |
| # statuses: read|write|none | |
| # Provide defaults for steps | |
| defaults: | |
| run: | |
| shell: bash # Default shell for all run steps | |
| working-directory: ./ # Default working directory for all run steps | |
| # Concurrency allows you to control the concurrency of workflow runs. | |
| concurrency: | |
| # The concurrency key is used to group workflows or jobs together into a concurrency group. | |
| group: ci-${{ github.ref }} | |
| # Cancel any in-progress job or run | |
| cancel-in-progress: true | |
| # Jobs are the core building blocks of workflows | |
| # All jobs run in parallel by default, each job is a new clean runner environment | |
| jobs: | |
| # Job ID (can be referenced by other jobs) | |
| basic-job: | |
| # Human-readable name for the job | |
| name: Basic Job Example | |
| # The type of runner to use | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 # Maximum time this job can run before being cancelled | |
| permissions: # Permissions for the GITHUB_TOKEN in this job | |
| contents: read # Read access to repository contents | |
| issues: write # Write access to issues | |
| # Job level environment variables | |
| env: | |
| JOB_ENV: "development" | |
| # Conditionally run this job | |
| if: 1 == 1 | |
| # Outputs from this job can be used by other jobs | |
| outputs: | |
| example-output: "Hello, World!" | |
| # List of steps to execute | |
| steps: | |
| # GitHub Actions are called with the uses keyword | |
| - uses: sormuras/hello-world-java-action@v1 # {owner}/{repo}/{path}@{ref} | |
| name: My first step # Steps can have names for better readability | |
| timeout-minutes: 5 # The maximum time this step can run before being cancelled | |
| continue-on-error: true # Whether to continue running the workflow if this step fails | |
| with: | |
| # Inputs are parameters passed to the action | |
| who-to-greet: "Mona the Octocat" | |
| # Reference a specific commit | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| # Reference the major version of a release | |
| - uses: actions/checkout@v7 | |
| # Reference a specific version | |
| - uses: actions/checkout@v7.0.1 | |
| # Reference a branch | |
| - uses: actions/checkout@main | |
| # Run a simple shell command | |
| - run: echo "Hello, GitHub Actions!" | |
| # Run multiple commands with literal data types | |
| - name: Demonstrate data types and basic commands | |
| run: | | |
| echo "Current directory: $(pwd)" | |
| echo "Files: $(ls -la)" | |
| echo "Node version: $NODE_VERSION" # print job level environment variable | |
| echo "GitHub Token: ${{ secrets.GITHUB_TOKEN }}" # Secrets are automatically masked in logs | |
| echo "Value: $myIntegerNumber" # print step level environment variable | |
| echo "step-output=Hello, World!" >> $GITHUB_OUTPUT # Set step output | |
| echo "::group::Examples" | |
| echo "::add-mask::This is secret and masked" | |
| echo "::debug::This is a debug message" | |
| echo "::notice file=app.js,line=1,col=5,endColumn=7::Missing semicolon" | |
| stopMarker=$(uuidgen) | |
| echo "::stop-commands::$stopMarker" | |
| echo '::error:: This will NOT be rendered as a error, because stop-commands has been invoked.' | |
| echo "::$stopMarker::" | |
| echo '::warning:: This is a warning again, because stop-commands has been turned off.' | |
| echo "::endgroup::" | |
| echo "### Hello world! :rocket:" >> $GITHUB_STEP_SUMMARY # Create a job summary | |
| echo "Contains: ${{ contains('hello world', 'hello') }}" | |
| echo "Starts with: ${{ startsWith('hello world', 'hello') }}" | |
| echo "Ends with: ${{ endsWith('hello world', 'world') }}" | |
| echo "Format: ${{ format('Hello {0}!', 'world') }}" | |
| echo "Join: ${{ join(fromJSON('["apple", "banana", "cherry"]'), ', ') }}" | |
| echo "Object to JSON: ${{ toJSON(fromJSON('{"name": "octocat", "type": "mascot"}')) }}" | |
| echo "Hash Files: ${{ hashFiles('**/package-lock.json') }}" | |
| env: | |
| # Literal data types in expressions | |
| myNull: ${{ null }} | |
| myBoolean: ${{ false }} | |
| myIntegerNumber: ${{ 711 }} | |
| myFloatNumber: ${{ -9.2 }} | |
| myHexNumber: ${{ 0xff }} | |
| myExponentialNumber: ${{ -2.99e-2 }} | |
| myString: Mona the Octocat | |
| myStringInBraces: ${{ 'It''s open source!' }} | |
| # Secrets are automatically masked in logs | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| id: example-step | |
| working-directory: ./ | |
| # Contexts are a way to access information about workflow runs | |
| - name: Dump GitHub context | |
| run: | | |
| echo '${{ toJSON(github) }}' | |
| echo '${{ toJSON(env) }}' | |
| echo '${{ toJSON(vars) }}' | |
| echo '${{ toJSON(job) }}' | |
| echo '${{ toJSON(steps) }}' | |
| echo '${{ toJSON(runner) }}' | |
| echo '${{ toJSON(secrets) }}' | |
| echo '${{ toJSON(strategy) }}' | |
| echo '${{ toJSON(matrix) }}' | |
| echo '${{ toJSON(needs) }}' | |
| echo '${{ toJSON(inputs) }}' | |
| printenv | |
| # Use the outputs from a previous step | |
| - run: echo "${{ steps.example-step.outputs.step-output }}" | |
| # Step that continues on error | |
| - name: Step that might fail | |
| continue-on-error: true | |
| run: exit 1 | |
| # Upload artifacts | |
| - name: Create artifact | |
| run: | | |
| mkdir -p artifacts | |
| echo "Build artifact" > artifacts/output.txt | |
| echo "Build number: ${{ github.run_number }}" >> artifacts/output.txt | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: build-artifacts | |
| path: artifacts/ | |
| retention-days: 30 | |
| # Conditionally run a step | |
| # The github context contains information about the workflow run and the event that triggered the run. | |
| - if: contains(fromJSON('["push", "pull_request"]'), github.event_name) | |
| run: echo "Deploying to production server on branch $GITHUB_REF" | |
| - if: ${{ always() }} | |
| run: echo "This step always runs" | |
| - if: ${{ cancelled() }} | |
| run: echo "This step runs if the workflow is cancelled" | |
| - if: ${{ failure() }} | |
| run: echo "This step runs if any previous step failed" | |
| # Object Filter Example | |
| - if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'bug') }} | |
| run: echo "This PR has a bug label" | |
| # Composite actions let your group multiple steps into a single yml file | |
| - name: Inline Composite Action | |
| uses: ./.github/actions/hello-world | |
| with: | |
| name: "GitHub Actions" | |
| greeting: "Hello" | |
| # Job with conditions and matrix strategy | |
| build-matrix: | |
| name: Build Matrix | |
| runs-on: ${{ matrix.os }} | |
| # Run this job only if the basic-job succeeds | |
| needs: basic-job | |
| # Run this job only on push events (not PRs) | |
| if: github.event_name == 'push' | |
| # Matrix strategy: run job multiple times with different configurations | |
| strategy: | |
| # Don't cancel other matrix jobs if one fails | |
| fail-fast: false | |
| # The maximum number of jobs to run in parallel | |
| max-parallel: 10 | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| node-version: [22, 24, 26] | |
| # Exclude specific combinations | |
| exclude: | |
| - os: windows-latest | |
| node-version: 26 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: "npm" | |
| # Leverage caching to speed up builds | |
| - name: Cache node modules | |
| id: cache-npm | |
| uses: actions/cache@v6 | |
| env: | |
| cache-name: cache-node-modules | |
| with: | |
| # npm cache files are stored in `~/.npm` on Linux/macOS | |
| path: ~/.npm | |
| key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-build-${{ env.cache-name }}- | |
| ${{ runner.os }}-build- | |
| ${{ runner.os }} | |
| - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }} | |
| name: List the state of node modules | |
| continue-on-error: true | |
| run: npm list | |
| - run: npm i | |
| - run: npm run build | |
| - run: npm test | |
| container-job: | |
| runs-on: ubuntu-latest | |
| container: | |
| image: node:24 | |
| env: | |
| NODE_ENV: development | |
| ports: | |
| - 80 | |
| volumes: | |
| - my_docker_volume:/volume_mount | |
| options: --cpus 1 | |
| steps: | |
| - name: Check for dockerenv file | |
| run: (ls /.dockerenv && echo Found dockerenv) || (echo No dockerenv) | |
| # Job demonstrating services (databases, caches, etc.) | |
| services-job: | |
| name: Services Example | |
| runs-on: ubuntu-latest | |
| # Define service containers | |
| services: | |
| # PostgreSQL service | |
| postgres: | |
| image: postgres:13 | |
| env: | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: testdb | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| # Redis service | |
| redis: | |
| image: redis:6 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 6379:6379 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Test database connection | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y postgresql-client | |
| PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d testdb -c '\l' | |
| - name: Test Redis connection | |
| run: | | |
| sudo apt-get install -y redis-tools | |
| redis-cli -h localhost -p 6379 ping | |
| # Reusable workflows are reusable jobs | |
| reusable-job: | |
| uses: austenstone/actions-playground/.github/workflows/reusable-called.yml@main | |
| with: | |
| username: "GitHub Actions User" | |
| secrets: inherit # You must pass secrets explicitly to reusable workflows | |
| # Job for deployment (typically runs after tests pass) | |
| deploy: | |
| name: Deploy Application | |
| runs-on: ubuntu-latest | |
| # Only run on main branch and after other jobs succeed | |
| if: github.ref == 'refs/heads/main' | |
| needs: [basic-job, build-matrix] | |
| # Grant only what the commenting steps below actually need | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| # Use environments to track deployments and protect sensitive operations. | |
| # Deliberately a throwaway name: pointing a demo at a real environment | |
| # records fake deployments against it and trips its protection rules. | |
| environment: | |
| name: demo-production | |
| url: https://myapp.example.com | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Download artifacts from previous jobs | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: build-artifacts | |
| path: ./artifacts | |
| - name: Deploy to production | |
| run: | | |
| echo "Deploying to production..." | |
| echo "Artifact contents:" | |
| cat ./artifacts/output.txt | |
| # The GitHub CLI is pre-installed on GitHub-hosted runners | |
| - run: | | |
| gh pr comment ${{ github.event.pull_request.number }} \ | |
| --body "🚀 **Deployment Successful!**" | |
| if: github.event_name == 'pull_request' && success() | |
| # To use the GitHub CLI, you need to authenticate with a token | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Basic GitHub Script example - create an issue comment | |
| - name: Comment on issue | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: '👋 Comment from github-script!' | |
| }) |