Reusable Workflows, Secrets and Environments

@finolacahill, can you post both your caller and called workflows? The screenshot isn’t rendering correctly for me, so if you could copy and paste that would help.

Also, thanks for the feedback about access to secrets when both are in the same repo.

Hi Jen,

Thank you so much for getting back to me, I really appreciate it. In the meantime I have realized my error and I’ll share here in case others fall into the same trap.

on:
    workflow_call:
        inputs:
            CI_UPSTREAM_CANDIDATE:
                required: true
                type: string
            CI_UPSTREAM_SHA:
                required: true
                type: string
        secrets:
            PAT:
                required: true

When defining the inputs/secrets in my called workflow I forgot to add “required: true” to the PAT secret. This resulted in the “secrets is not a valid event name” error when I ran my caller workflow.

This new feature is a great addition, and it’s fantastic to see Github Actions continue to evolve. But, if you have a feature request list anywhere, we at Intel would love the capacity to call or checkout reusable workflows from private repos using a personal access token!

All the best,
Finola

1 Like

Glad that’s working for you!

We do support reusable workflows in internal repositories, does that work for you? As opposed to private?

1 Like

Oh that’s good to know! But alas due to a variety of cross-enterprise privacy requirements we almost exclusively use private repositories as opposed to internal repositories, so we’ll be keeping the fingers crossed for future evolutions :slight_smile:

1 Like

Hey @jenschelkopf sorry it took me a while to test this, busy week :frowning:

I have created the following test:

name: Reusable workflow example

on:
  workflow_call:
    inputs:
      username:
        required: true
        type: string
      environment:
        required: false
        type: string

jobs:
  example_job:
    environment: ${{ inputs.environment }}
    name: Pass input and secrets to my-action
    runs-on: ubuntu-latest
    env:
      FOO: ${{ secrets.FOO }}
    steps:
      - run: |
          echo ${{ inputs.username }}
          echo "asd$FOO"

and the following caller

name: Test

on:
  workflow_dispatch:

jobs:
  reuse:
    uses: myOrg/myRepo/.github/workflows/reusable.yml@main
    with:
      username: mona
      environment: development

and while it does pick up the environment correctly and I can see a deployment, it does not seem to load/print (even masked) the secret.

I also tried directly with echo ${{ secret.FOO }} but it always seems to print blank as @devkeydet shared before. I was expecting to see the *** as a proof that it was actually seeing the secret.

Thanks!

Hi @pecigonzalo, you need to explicitly pass the environment secret. Here’s how it works:

Step (1), pass the environment secret to the called workflow:

  cd:
    needs: ci
    uses: wizard-whitebeard/automation/.github/workflows/release.yml@master
    secrets:
      PAT: ${{ secrets.PAT }}

Step (2), define the secret input in the called workflow:

on:
  workflow_call:
    secrets:
      PAT:
        required: true

Step (3), reference the environment where you want to use the secret (just like normal workflows):

  deploy_to_production:
    name: Deploy Production
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout a private repo
        uses: actions/checkout@v2
        with:
          repository: wizard-whitebeard/private-repo
          ref: main
          token: ${{ secrets.PAT }}
1 Like

Got it, so the caller can send ${{ secrets.PAT }} despite it not existing in its environment?

Ah, no. Are you trying to reference an environment in the called repository? That’s not expected to work. When you reference a reusable workflow, Actions executes it in the context of the caller repository.

You mentioned before that the called workflow picked up the environment and you can see the deployment. I would expect that is created an environment in the caller’s repository, and didn’t actually use the environment in the called’s repository. That’s generally how environments work (they get created if they don’t already exist).

Not exactly, all this is within the same repository.

In my example, secrets. PAT does exist in the referenced environment. So I’m not sure what you mean by this question. Can you rephrase?

I’m having the same issue here. I think what @pecigonzalo is referring to is that jobs.<job_id>.environment does not work when using jobs.<job_id>.uses/reusable workflows.

Example:

jobs:
  deploy:
    environment: staging
    uses: org/repo/.github/workflows/deploy.yml@main
    with:
        token: ${{ secrets.TOKEN }}  # This exists within the staging environment
The workflow is not valid. .github/workflows/deploy_caller.yml (Line: x, Col: 5): Unexpected value 'uses' .github/workflows/deploy_caller.yml (Line: y, Col: 5): Unexpected value 'with'

How can you pass a secret from a deployment environment to the reusable workflow?

5 Likes

I found a bug filed that sounds similar. I tested and it worked for me. Adding env: ${{ secrets }} in the called workflow allows my called workflow use environment secrets.

Edit: On second try, I got it working without env: ${{ secrets }}. I am using an environment called deploy where my secrets live. My calling workflow does not have environment: deploy set. However, in my calling workflow, I pass secrets to the reusable workflow (which is configured with `environment: deploy) and the workflow succeeds. It works, it’s just not the configuration I was expecting to work.

Interesting @cloin , could you post some code example ?

I’m having the same issue as @BryceBeagle mentionned, as jobs.<job_id>.environment is not usable when calling a reusable workflow.

@totogtr sure. This is an example of what I have working right now

environment: deploy has Quay username/passwords secrets.

calling workflow:

jobs:
  call-deploy-workflow:
    uses:  me/my-repo/.github/workflows/deploy.yml@main
    secrets:
      QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
      QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}

reusable workflow:

# Workflow name
name: Image build

# Controls when the workflow will run
on:
  # This is a reusable workflow
  workflow_call:
    secrets:
      QUAY_PASSWORD:
        description: 'needed for registry login'
        required: false
      QUAY_USERNAME:
        description: 'needed for registry login'
        required: false

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:

  # This workflow contains a single job called "build"
  build:
    runs-on: ubuntu-latest
    environment: deploy
    steps:
      - name: Log in to quay.io
        id: registry-quay
        uses: redhat-actions/podman-login@v1
        with:
          registry: quay.io
          username: ${{ secrets.QUAY_USERNAME }}
          password: ${{ secrets.QUAY_PASSWORD }}

I was expecting to be able to do this:

Thanks, I’ll try that for environment secrets !

I’ve tried something similar for repo secrets, but instead of passing the secrets values to an action, I’d like to export them to env variables (that terraform can pick up in my case) such as

env:
  ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}

I’m not sure if it should work but in my case it doesn’t, the env var are staying empty.

Whereas if I do

      - name: get secrets
        run: |
          echo "ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }}" >> $GITHUB_ENV

the env var are populated correctly.

A bit less clean way to do it in my opinion…

I am also getting this behavior (I tried multiple different combinations). The key factor here is that there are reusable values that I want to use in my env: in the “root” level. These values would have to be declared N times. Let me give you a more tangible example.

env:
  dockerRegistry: 'my_docker_registry'

jobs:
  deploy_dev:
    uses: .../.github/workflows/template.yaml@branch
    with:
      env: 'dev'
      dockerRegistry: '${{ env.dockerRegistry }}' # My docker registry is not a secret, and there is a max number of secrets that can be used per repo
  deploy_prod:
    uses: .../.github/workflows/template.yaml@branch
    with:
      env: 'prod'
      dockerRegistry: '${{ env.dockerRegistry }}'

If am not able to define custom env vars for a template, then these values have to be hardcoded in each reusable workflow call

1 Like

Hi Jen!
I caught another little quirk in re-usable actions. I’m writing a template to use reusable workflows at scale, and within that template I’m using the GITHUB_REPOSITORY env variable (Environment variables - GitHub Docs) This var is in the format “owner/repository”. The intention is to use the var as follows:

jobs:
    call-workflow:
        uses: ${GITHUB_REPOSITORY}/.github/workflows/pull_request.yml@master

Unfortunately using this variable is causing a “invalid value workflow reference: references to workflows must be prefixed with format ‘owner/repository/’” syntax error. It would be great not to have to hardcode the repo URL when calling a workflow from within the same repository.
All the best,
Finola

also looking for a way to specify the environment that a reusable workflow will have access to the secrets for without hardcoding the environment within the workflow itself.

Something like this (from a previous comment) would be perfect

jobs:
  deploy:
    environment: staging
    uses: org/repo/.github/workflows/deploy.yml@main
    with:
        token: ${{ secrets.TOKEN }}

@matty-rose

I solved this doing it like this in the caller workflow

with:
  ENVIRONMENT: staging

Then in my reusable workflow i have:

environment: ${{ inputs.ENVIRONMENT }}

The example above is using Bash syntax for variable expansion, but uses: isn’t interpreted by Bash. Try this instead:

jobs:
    call-workflow:
        uses: ${{ github.repository }}/.github/workflows/pull_request.yml@master

That uses the GitHub Actions expression syntax, as documented here:
https://docs.github.com/en/actions/learn-github-actions/expressions