{"id":41704746,"url":"https://github.com/nnellans/github-workflows-guide","last_synced_at":"2026-01-24T21:22:53.682Z","repository":{"id":124151774,"uuid":"594796263","full_name":"nnellans/github-workflows-guide","owner":"nnellans","description":"GitHub Workflows Guide","archived":false,"fork":false,"pushed_at":"2025-04-25T14:17:13.000Z","size":9032,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-25T15:27:18.964Z","etag":null,"topics":["ci-cd","cicd","continuous-delivery","continuous-deployment","continuous-integration","github","github-actions","github-workflow","workflows"],"latest_commit_sha":null,"homepage":"https://www.nathannellans.com","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nnellans.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-01-29T17:08:38.000Z","updated_at":"2025-04-25T14:16:01.000Z","dependencies_parsed_at":"2023-09-26T06:40:17.948Z","dependency_job_id":"6580c404-e3cd-4aac-a548-dd715fa862a5","html_url":"https://github.com/nnellans/github-workflows-guide","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/nnellans/github-workflows-guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnellans%2Fgithub-workflows-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnellans%2Fgithub-workflows-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnellans%2Fgithub-workflows-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnellans%2Fgithub-workflows-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nnellans","download_url":"https://codeload.github.com/nnellans/github-workflows-guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnellans%2Fgithub-workflows-guide/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28737128,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T21:19:41.845Z","status":"ssl_error","status_checked_at":"2026-01-24T21:13:38.675Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci-cd","cicd","continuous-delivery","continuous-deployment","continuous-integration","github","github-actions","github-workflow","workflows"],"created_at":"2026-01-24T21:22:53.127Z","updated_at":"2026-01-24T21:22:53.658Z","avatar_url":"https://github.com/nnellans.png","language":null,"readme":"# GitHub Workflow Guide\n\n- Version: 1.1.0\n- Author:\n  - Nathan Nellans\n  - Email: me@nathannellans.com\n  - Web:\n    - https://www.nathannellans.com\n    - https://github.com/nnellans/github-workflows-guide\n\n\u003e [!WARNING]\n\u003e This is an advanced guide and assumes you already know the basics of GitHub Workflows.  Think of this more like an advanced cheat sheet.  I went through the documentation and captured any notes that I felt were important, and organized them into the README file you see here.  If you are new to GitHub Workflows, then I would suggest going through the GitHub docs first.\n\n\u003e [!IMPORTANT]\n\u003e This is a live document.  Some of the sections are still a work in progress.  I will be continually updating it over time.\n\n---\n# Table of Contents\n- [Workflow Settings](#workflow-settings)\n- [Triggers](#triggers)\n- [Permissions for the GitHub Token](#permissions-for-the-github_token)\n- [Default Settings](#default-settings)\n- [Concurrency Settings](#concurrency-settings)\n- [Variables](#variables)\n- [Secrets](#secrets)\n- [Jobs and Steps](#jobs--defining-the-work)\n  - [Normal Jobs](#normal-jobs)\n  - [Calling a Reusable Workflow](#jobs-that-call-a-reusable-workflow-job-level-template)\n- [Reusable Actions vs. Reusable Workflows](#reusable-actions-vs-reusable-workflows)\n- [Workflow Commands](#workflow-commands)\n- [YAML Anchors \u0026 Aliases](#yaml-anchors--aliases)\n- [Links](#links)\n\n---\n\n# Workflow Settings\n\n```yaml\n# name of the workflow as shown in the GitHub UI\nname: 'string' # optional, default is the path \u0026 name of the yaml file\n\n# name to use for each run of the workflow\nrun-name: 'string' # optional, default is specific to how your workflow was triggered\n```\n- `run-name` can use expressions, and can reference the contexts of `github` and `inputs`\n\n# Triggers\n[Documentation - Triggering a workflow](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow)\n\n[Documentation - Events that trigger workflows](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows)\n\n```yaml\n# option 1: single event with no options\non: push\n\n# option 2: multiple events with no options\non:\n  - push\n  - fork\n\n# option 3: events with options\non:\n  push:\n    branches:\n      - blahblah\n  issues:\n    types:\n      - opened\n  schedule:\n    - cron: '30 5,17 * * *'\n\n# option 4: manual trigger where you can specify a max of 25 inputs\non:\n  workflow_dispatch:\n    inputs:\n      someInputName:\n        description:\n        required: true | false\n        default: 'defaultValue'\n        type: boolean | number | string | choice | environment\n        options: # only when type: choice\n          - option1\n          - option2\n      someOtherInput:\n        description:\n        required: true\n        type: string\n\n# option 5: if this workflow is used as a reusable workflow (job-level template)\non:\n  workflow_call:\n    inputs: # input parameters\n      inputName1:\n        description:\n        required: true | false\n        type: boolean | number | string # required\n        default: something # if omitted, boolean = false, number = 0, string = \"\"\n    secrets: # input secrets\n      secretName1:\n        description:\n        required: true | false\n    outputs: # output values\n      outputName1:\n        description:\n        value:\n```\n\n- If multiple events are specified, only 1 event needs to occur to trigger the workflow\n- If multiple events happen at the same time, then multiple runs of the workflow will trigger\n\n# Permissions for the GITHUB_TOKEN\n[Documentation - Modifying the permissions for the GITHUB_TOKEN](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token#modifying-the-permissions-for-the-github_token)\n\n[Documentation - Workflow Syntax - Permissions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#permissions)\n\n- Use this if you want to modify the default permissions granted to the `GITHUB_TOKEN`\n- Optional, the default can be set in the repo settings (by an admin) to either a `permissive` preset or a `restricted` preset\n- As a good security practice, you should grant the `GITHUB_TOKEN` the least required access\n- When the `permissions` key is used, all unspecified permissions are set to `none`, with the exception of the `metadata` scope, which always gets `read` access.\n- Supported scopes for `permissions`: workflow-level, job-level\n\n```yaml\n# option 1: full syntax\npermissions:\n  actions: read | write | none\n  artifact-metadata: read | write | none\n  attestations: read | write | none\n  checks: read | write | none\n  contents: read | write | none\n  deployments: read | write | none\n  discussions: read | write | none\n  id-token: read | write | none\n  issues: read | write | none\n  models: read | none\n  packages: read | write | none\n  pages: read | write | none\n  pull-requests: read | write | none\n  security-events: read | write | none\n  statuses: read | write | none\n\n# option 2: shortcut syntax to provide read or write access for all scopes\npermissions: read-all | write-all\n\n# option 3: shortcut syntax to disable permissions to all scopes\npermissions: {}\n```\n\nMore Info:\n- When you enable GitHub Actions, then a GitHub App will be installed on your repo\n  - The `GITHUB_TOKEN` secret is used to hold an installation access token for that app\n- Before each job begins, GitHub fetches an unique installation access token for the job\n  - The token expires when a job finishes or after a maximum of 24 hours.\n  - The token can authenticate on behalf of the GitHub App installed on your repo\n  - The token's permissions are limited to the repo that contains your workflow\n- [My blog post all about GitHub Apps and the `GITHUB_TOKEN`](https://www.nathannellans.com/post/github-apis-github-tokens-and-github-action-workflows)\n\n# Default Settings\n[Documentation - Setting a default shell and working directory](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/set-default-values-for-jobs)\n- Creates a map of default settings that will be inherited downstream\n- Supported scopes for `defaults`: workflow-level, job-level\n  - The most specific defaults wins\n\n```yaml\ndefaults:\n  run:\n    shell: bash\n    working-directory: scripts\n```\n\n# Concurrency Settings\n[Documentation - Control the concurrency of workflows and jobs](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency)\n- Ensures that only one Workflow (or only one Job) from the specified concurrency group can run at a time\n- Optional\n- Supported scopes for `concurrency`: workflow-level, job-level\n\n```yaml\n# option 1: specify a concurrency group with default settings\nconcurrency: groupName\n\n# option 2: specify a concurrency group with custom settings\nconcurrency:\n  group: groupName\n  cancel-in-progress: true # this will cancel any currently running workflows/jobs first\n```\n- `groupName` can be any string or expression (but limited to the `github` context only)\n- Default behavior: If a Workflow/Job in the concurrency group is currently running, then any new Workflows/Jobs will be placed into a pending state and will wait for the original Workflow/Job to finish. Only the most recent Workflow/Job is kept in the pending state, and all others will be cancelled.\n\n# Variables\n[Documentation - Store information in variables](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-variables)\n### Environment Variables\n- Cannot reference other variables in the same map\n- Supported scopes for `env`: workflow-level, job-level, step-level\n  - The most specific variable wins\n\n```yaml\n# defining environment variables (workflow-level)\nenv:\n  KEY1: value\n  KEY2: value\n\n# defining environment variables (job-level)\njobs:\n  someJobId:\n    env:\n      KEY1: value\n      KEY2: value\n\n# defining environment variables (step-level)\njobs:\n  someJobId:\n    steps:\n      - name: someStepName\n        env:\n          KEY1: value\n          KEY2: value\n\n# use an environment variable in the workflow yaml:\n${{ env.KEY }}\n\n# use an environment variable inside of a script by just accessing the shell variable as usual:\nlinux:  $KEY\nwindows powershell:  $env:KEY\nwindows cmd:  %KEY%\n\n# there are many default environment variables (see link above)\n# most also have a matching value in the github context so you can use them in the workflow yaml\n$GITHUB_REF and ${{ github.ref }}\n```\n\n### Configuration Variables\n- Defined in the GitHub UI\n- Can be shared by multiple Workflows\n- Supported scopes for `vars`: organization-level, repo-level, repo environment-level\n  - The most specific variable wins\n- Configuration variable naming restrictions:\n  - Can only contain alphanumeric characters or underscores\n  - Must not start with the `GITHUB_` prefix or a number\n  - Case insensitive\n  - Must be unique at the level they are created at\n- Configuration variable limits: 1,000 per Organization, 500 per Repo, 100 per Repo Environment\n\n```yaml\n# use a configuration variable in the workflow yaml:\n${{ vars.KEY }}\n\n# use a configuration variable inside of a script by just accessing the shell variable as usual:\nlinux:  $KEY\nwindows powershell:  $env:KEY\nwindows cmd:  %KEY%\n```\n\n---\n\n# Secrets\n[Documentation - Using secrets in GitHub Actions](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets)\n- Defined in the GitHub UI\n- Can be shared by multiple Workflows\n- Supported scopes for `secrets`: organization-level, repo-level, repo environment-level\n  - The most specific variable wins\n- Secrets naming restrictions:\n  - Can only contain alphanumeric characters or underscores\n  - Must not start with the `GITHUB_` prefix or a number\n  - Case insensitive\n  - Must be unique at the level they are created at\n- Secrets limits: 1,000 per Organization, 100 per Repo, 100 per Repo Environment\n- Avoid using structured data (like JSON) as the value of your Secret. This helps to ensure that GitHub can properly redact your Secret in logs.\n- Secrets cannot be directly referenced in `if:` conditionals\n  - Instead, consider setting secrets as Job-level environment variables, then referencing the environment variables to conditionally run Steps in the Job\n\n```yaml\n# Actions can't directly use secrets that are defined via the GitHub UI\n# However, you can use the secret as an input or environment variable\nsteps:\n  - name: Hello world action\n    env:   # Set the secret as an environment variable\n      SOME_VAR: ${{ secrets.Key }\n    uses: action/something@v1\n    with:  # Set the secret as a value to an input\n      someInput: ${{ secrets.Key }}\n```\n\n# Jobs / Defining the work\n\n## Normal Jobs:\n[Documentation - Using jobs in a workflow](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-jobs)\n\n```yaml\njobs:\n  symbolicJobName: # must be unique, start with a letter or underscore, and only contain letters, numbers, dashes, and underscores\n    name: 'string' # friendly name that is shown in the GitHub UI\n    runs-on: windows-latest | ubuntu-slim | ubuntu-latest | macos-latest | self-hosted # specifies the Agent to run on\n    needs: # Job dependencies\n    if: # Job conditions, ${{ ... }} can optionally be used to enclose your condition\n    continue-on-error: true # allows the Workflow to pass if this Job fails\n    timeout-minutes: 10 # max time a Job can run before being cancelled. optional, default is 360\n    permissions: # job-level GITHUB_TOKEN permissions\n    defaults: # job-level defaults\n    concurrency: # job-level concurrency group\n    env: # job-level variables\n      KEY: value\n    \n    environment: # see more below\n    container: # see more below\n    snapshot: # see more below\n    services: # see more below\n    strategy: # see more below\n    outputs: # see more below\n    \n    # list the Steps of this Job\n    steps:\n\n      # Use a GitHub Action\n      - id: 'symbolicStepName' # optional\n        name: 'string' # optional. friendly name that is shown in the GitHub UI\n        if: # Step conditions, ${{ ... }} can optionally be used to enclose your condition\n        continue-on-error: true # allows the Job to pass if this Step fails\n        timeout-minutes: 10 # max time to run the Step before killing the process\n        env: # Step-level variables\n          KEY: value\n        # option 1: use a public action\n        uses: actions/checkout@v3 # owner/repo@ref, or owner/repo/folder@ref, where ref can be a branch, tag, or SHA\n        # option 2: use an action file from a checked out repo\n        uses: ./.github/actions/someFolder # make sure to checkout the repo first, no ref is supported as it uses the ref that you checked out\n        # option 3: use an action from a public container image (only on Linux runners)\n        # there is currently no way to authenticate to the specified registry, so be careful of rate limits. also, that means private registries are not supported\n        uses: docker://alpine:3.8 # from Docker Hub\n        uses: docker://ghcr.io/owner/image # from GitHub Packages Container Registry\n        uses: docker://gcr.io/cloud-builders/gradle # from Google Container Registry\n        # parameters to pass to the action, must match what is defined in the action\n        with:\n          param1: value1\n          param2: value2\n          # when using an action from a public container image (option 3)\n          args: 'something' # this overwrites the CMD instruction in your Dockerfile\n          entrypoint: 'something' # this overwrite the ENTRYPOINT instruction in your Dockerfile\n\n      # Run a single-line Script\n      - name: something2\n        run: single-line command\n        shell: bash | pwsh | python | sh | cmd | powershell\n        working-directory: ./temp\n\n      # Run a multi-line Script\n      - name: something3\n        run: |\n          multi-line\n          command\n```\n\n### Job.Environment\n[Documentation - Managing environments for deployment](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments)\n- Specifies a GitHub environment to deploy to\n\n```yaml\njobs:\n  symbolicJobName:\n\n    # option 1 - specify just an environment name\n    environment: envName\n\n    # option 2 - specify environment name and url\n    environment:\n      name: envName\n      url: someUrl\n```\n- `envName` can be a string or any expression (except for the `secrets` context)\n\n### Job.Container\n[Documentation - Running jobs in a container](https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/run-jobs-in-a-container)\n- Defines a container that will run all Steps in this Job\n\n```yaml\njobs:\n  symbolicJobName:\n\n    # option 1 - shortcut syntax specifying just the image\n    container: node:14.16\n\n    # option 2 - full syntax\n    container:\n      image: node:14.16\n      credentials: # used to login to the container registry\n        username:\n        password:\n      env: # specify environment variables inside the container\n        KEY: value\n      ports: # array of ports to expose on the container\n        - 8080:80 # maps port 8080 on the docker host to port 80 on the container\n      volumes: # array of volumes for the container to use, you can specify named Docker volumes, anonymous Docker volumes, or bind mounts on the host\n        - source:destinationPath\n      options: --cpus 1 # specifies additional options for the docker create command, --network is not supported\n```\n- Optional, if omitted the Job will run directly on the Agent and not inside a Container\n- Only for Steps that don't already use their own Container\n- Only supported on Microsoft-hosted Ubuntu runners, or self-hosted Linux runners\n- `run` Steps inside of a Container will default to the `sh` shell, but you can override with `jobid.defaults.run` or `step.shell`\n\n### Job.Snapshot\n[Documentation - Using custom images](https://docs.github.com/en/actions/how-tos/manage-runners/larger-runners/use-custom-images)\n- For creating custom images that you can use with GitHub-hosted larger runners\n- Lets you preinstall tools, dependencies, and configurations into your runner image\n- Each job that includes the `snapshot` keyword creates a separate image\n- Each successful run of a job that includes the `snapshot` keyword creates a new version of that image\n\n```yaml\njobs:\n  symbolicJobName:\n\n    # option 1 - string syntax\n    # just specify an image name, this either creates a new image (1.0.0) or adds a new (minor) version to the existing image\n    # can not specify a version number with this syntax\n    snapshot: customImageName\n\n    # option 2 - mapping syntax\n    # lets you specify a version number, only major \u0026 minor versions are supported, patch version are not supported\n    snapshot:\n      image-name: customImageName\n      version: 2.*\n```\n\n### Job.Services\n[Documentation - Communicating with Docker service containers](https://docs.github.com/en/actions/tutorials/use-containerized-services/use-docker-service-containers)\n- Defines service container(s) that are used by your Job\n\n```yaml\njobs:\n  symbolicJobName:\n    services:\n      symbolicServiceName: # label used to access the service container\n        image: nginx\n        credentials: # used to login to the container registry\n          username:\n          password:\n        env: # specify environment variables inside the service container\n          KEY: value\n        ports: # an array of ports to expose on the service container\n          - 80\n        volumes: # array of volumes for the container to use, you can specify named Docker volumes, anonymous Docker volumes, or bind mounts on the host\n          - source:destinationPath\n        options: --cpus 1 # specifies additional options for the docker create command, --network is not supported\n```\n- Optional\n- Only supported on Microsoft-hosted Ubuntu runners, or self-hosted Linux runners\n- Not supported inside a composite action\n\n### Job.Strategy\n[Documentation - Running variations of jobs in a workflow](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/run-job-variations)\n- Use variables to make one Job run multiple different times\n\n```yaml\njobs:\n  symbolicJobName:\n    strategy:\n      fail-fast: boolean # optional, default is true\n      max-parallel: 5 # max number of matrix Jobs to run in parallel. optional, default is to run all Jobs in parallel (if enough runners are available)\n      matrix: # the variables that will define the different permutations\n        KEY1: [valueA, valueB]\n        KEY2: [valueX, valueY, valueZ]\n        include: # an extra list of objects to include\n        exclude: # an extra list of objects to exclude\n```\n- Optional\n- A different Job will run for each combination of KEYs, in this example that would be 6 different Jobs\n- There is a max of 256 Jobs\n- This will create a `matrix` context which lets you use `matrix.KEY1` and `matrix.KEY2` to reference the current iteration\n- `exclude` is processed first before `include`, this allows you to add back combinations that were previously excluded\n- When `fail-fast` is set to `true`, if any job in the matrix fails, then all in-progress and queued jobs in the matrix will be cancelled\n\n### Job.Outputs\n[Documentation - Passing information between jobs](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs)\n- Specify outputs of this Job\n\n```yaml\njobs:\n  symbolicJobName:\n    outputs: # map of outputs for this job\n      key: value\n      key: value\n```\n- These `outputs` are available to all downstream Jobs that **depend** on this Job\n- Max of 1 MB per Output, and 50 MB total per Workflow\n- Any expressions in an Output are evaluated at the end of a Job\n- Any secrets in an Output are redacted and not sent to GitHub Actions\n\n## Jobs that call a reusable workflow (job-level template):\n[Documentation - Reuse Workflows](https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows)\n\n[Documentation - Reusing workflow configurations](https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations)\n\n- Only the following parameters are supported in such a Job\n\n```yaml\njobs:\n  symbolicJobName: # must be unique, start with a letter or underscore, and only contain letters, numbers, dashes, and underscores\n    name: 'string' # friendly name that is shown in the GitHub UI\n    needs: # Job dependencies\n    if: # Job conditions, ${{ ... }} can optionally be used to enclose your condition\n    permissions: # job-level GITHUB_TOKEN permissions\n    concurrency: # job-level concurrency group\n    strategy: # define a matrix for parallel jobs\n    # option 1: a reusable workflow from another repo (public or private)\n    uses: org/repo/.github/workflows/file.yaml@ref # where ref can be a branch, tag, or SHA\n    # option 2: a reusable workflow file from the same repo\n    uses: ./.github/workflows/file.yaml # no ref is supported, it uses the same ref that triggered the parent workflow\n    # parameters to pass to the template, must match what is defined in the template\n    with:\n      param1: value1\n      param2: value2\n    secrets: # secrets to pass to the template, must match what is defined in the template\n      param1: ${{ secrets.someSecret }}\n      param2: ${{ secrets.someOtherSecret }}\n    secrets: inherit # pass all of the secrets from the parent workflow to the template. this includes org, repo, and environment secrets from the parent workflow\n```\n\n# Reusable Actions vs. Reusable Workflows\nThis list of features changes quite often. For example, Reusable Workflows being able to call other Reusable Workflows is fairly new.\n\n| | Reusable Actions | Reusable Workflows |\n| --- | --- | --- |\n| Scope | Step-level | Job-level |\n| Supports `env` variables\u003cbr /\u003edefined in parent Workflow | Yes | No |\n| Input types | none (string) | boolean, number, string |\n| Input Secrets | No[^1] | Yes |\n| Supports Service Containers | No | Yes |\n| Can specify Agent\u003cbr /\u003e(`runs-on`) | No | Yes |\n| Filename | Must be `action.yml`\u003cbr /\u003e(so, 1 per folder) | Can be anything `.yml`\u003cbr /\u003e(must be in `.github/workflows/` -\u003cbr /\u003eno subfolders) |\n| Nesting | 10 levels | 10 levels |\n| Logging | Summarized | Logging for each Job and Step |\n\n[^1]: You can not directly pass GitHub Secrets to an Action. However, you could use a Secret for the value of one of the Action's input parameters, or you could use a Secret as the value of an environment variable that the Action could then read.\n\n\u003e [!TIP]\n\u003e - Example [action-composite.yaml](./action-composite.yaml) file showing the complete syntax for a reusable Composite Action\n\u003e - Example [action-docker.yaml](./action-docker.yaml) file showing the complete syntax for a reusable Docker Action\n\u003e - Example [action-javascript.yaml](./action-javascript.yaml) file showing the complete syntax for a reusable JavaScript Action\n\n---\n\n# Workflow Commands\n[Documentation - Workflow commands for GitHub Actions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands)\n- These are special commands that can be used to communicate with the runner machine\n- They can do multiple different things, such as set environment variables, set output values, set debug messages, and more\n- Depending on the specific Workflow Command, it can be used in one of two ways:\n  - Using the `echo` command with a specific format\n  - Writing to a file\n\n```bash\n# Some examples (all using Bash), see the docs for a full reference\n\n# Print a debug message to the log\necho \"::debug::This is a debug message\"\n\n# Masking a string value so it's not shown in the logs\necho \"::add-mask::This value will be masked\"\n\n# Setting an environment variable\necho \"KEY=value\" \u003e\u003e \"$GITHUB_ENV\"\n\n# Setting an output parameter\necho \"KEY=value\" \u003e\u003e \"$GITHUB_OUTPUT\"\n```\n\n\u003e [!WARNING]\n\u003e For reusable workflows, any variables you set in the `env` context inside of the reusable workflow will NOT be available in the parent workflow. To get around this, the reusable workflow could create an `output` which can then be consumed by the parent workflow.\n\n\u003e [!WARNING]\n\u003e A masked value can NOT be passed from one Job to another Job in GitHub Actions\n\u003e - [GitHub Discussion](https://github.com/orgs/community/discussions/13082) on this topic\n\u003e - The [official docs](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#example-masking-and-passing-a-secret-between-jobs-or-workflows) want you to use a secret store, such as Azure KeyVault, to solve this problem. In effect, Job 1 uploads the value to the secret store, and then Job 2 downloads the value from the secret store.\n\n### Multi-Line Values\n\nIf you need to mask a sensitive, multi-line value, then you can do the following:\n\n```bash\nSENSITIVE=\"$(command that outputs a sensitive, multi-line value)\"\nwhile read -r line\ndo\n  echo \"::add-mask::${line}\"\ndone \u003c\u003c\u003c \"$SENSITIVE\"\n\n# In this example, the sensitive value will be assigned to the variable called SENSITIVE\n# The command used on line 1 will be logged in plain-text, so it must not include sensitive values (but, this is a plain-text YAML file, so you would never do that in the first place, right?)\n# The value assigned to the variable is then read, line-by-line, and a mask is applied to each line's value\n\n# An example of a safe command you could use:\nSENSITIVE=\"$(az keyvault secret show --name MySecretName --vault-name MyVaultName --query value --output tsv)\"\n```\n\nIf you need to set an environment variable or an output to use a multi-line value, then you can do the following:\n\n```bash\n# Make sure the delimiter you're using won't occur on a line of its own within the value\n{\n  echo 'KEY\u003c\u003cDELIMETER'\n  command(s) that produce multiple lines of output\n  echo DELIMETER\n} \u003e\u003e \"$GITHUB_ENV\"\n```\n\n---\n\n# YAML Anchors \u0026 Aliases\n[Documentation - YAML anchors and aliases](https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations)\n- GitHub Actions supports a limited set of YAML features like anchors and aliases\n- Use `\u0026symbolicName` to define the anchor (the section you want to capture)\n- Use `*symbolicName` to define one or more aliases, where each one will be a copy of the anchor\n- YAML Merge Keys, specified by `\u003c\u003c:` are not yet supported by GitHub. This means each anchor must be copied exactly as-is, with no way to add an override of a single value\n\n```yaml\njobs:\n\n  firstSymbolicJobName:\n    env: \u0026anchorName # this defines the section that will become the anchor\n      KEY1: value1\n      KEY2: value2\n      KEY3: value3\n\n  secondSymbolicJobName:\n    env: *anchorName # this defines an alias (the values in the anchor will be copied here)\n```\n\n---\n\n# Links\n- official github actions: https://github.com/orgs/actions/repositories\n- official azure actions: https://github.com/marketplace?query=Azure\u0026type=actions\u0026verification=verified_creator\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnnellans%2Fgithub-workflows-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnnellans%2Fgithub-workflows-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnnellans%2Fgithub-workflows-guide/lists"}