Generate unique string in Github Actions - unique

I have the following env definition for a job in Github Actions:
jobs:
build-test-deploy:
runs-on: ubuntu-latest
env:
FOO : foobar-${{ github.sha }}
BAR : BAZ
However the github.sha remains identical when the same workflow is re-run without any commit in between. I need the FOO to be unique for every run (even if it's technically a re-run), independently of commits.
Two options I'm thinking of:
either generate a random string
or get the current unix timestamp
I don't know how to get either of these values in the context of the env: directive. There are some actions available to use within steps but how is it possible to get this kind of unique values right from the env directive instead?

There is a github.run_attempt variable now (not sure since when) that increments on re-runs. So by combing that with e.g. github.run_id you can generate per-run fully unique strings.
jobs:
build-test-deploy:
runs-on: ubuntu-latest
env:
FOO : foobar-${{ github.run_id }}-${{ github.run_attempt }}
BAR : BAZ
Make sure to put a delimiter inbetween (- in the example above) as you might get duplicates otherwise. Without it, e.g. 123 could result from both run 12 attempt 3 or run 1 attempt 23.
Quick link to the relevant docs for further reading.

jobs:
build-test-deploy:
runs-on: ubuntu-latest
env:
BAR : BAZ
steps:
- name: generate FOO unique variable based on timestamp
run: echo FOO=foobar--$(date +%s) >> $GITHUB_ENV
- name: other step that needs FOO
run: echo ${{ env.FOO }}

Related

Passing vars using reusable workflow with no success

We try to pass some env variables using a workaround to the reusable workflow as follows, but no variables are passed.
Workflow YAML is:
name: "call my_reusable_workflow"
on:
workflow_dispatch:
env:
env_branch: ${{ github.head_ref }}
env_workspace: ${{ github.workspace }}
jobs:
call_reusable_workflow_job:
uses: my_github/my-reusable-workflow-repo/.github/workflows/used_wf_test.yml#master
with:
env_vars: |
hello-to=Meir
branch_name=${{ env.env_branch }}
secrets:
my_token: ${{secrets.ENVPAT}}
and the reusable workflow YAML is:
name: my_reusable_workflow
on:
workflow_call:
inputs:
env_vars:
required: true
type: string
description: list of vars and values
secrets:
giraffe_token:
required: true
jobs:
reusable_workflow_job:
runs-on: ubuntu-latest
steps:
- name: set environment variables
if: ${{ inputs.env_vars }}
run: |
for env in "${{ inputs.env_vars }}"
do
printf "%s\n" $env >> $GITHUB_ENV
done
When the action is running it gets the value of hello-to=Meir but doesn't get the value branch_name=${{ env.env_branch }}.
I tried to pass the value also as branch_name=${{ github.head_ref }} but with no success.
According to the Limitations of Reusing workflows:
Any environment variables set in an env context defined at the workflow level in the caller workflow are not propagated to the called workflow. For more information, see "Variables" and "Contexts."
So, the env context is not supported in reusable workflow callers at the moment.
However, you can pass the Default Environment Variables to reusable workflow callers.
For example, in your particular scenario, you want to use these contexts:
github.head_ref
github.workspace
The equivalent default environment variables are:
GITHUB_HEAD_REF
GITHUB_WORKSPACE
And, your reusable workflow (e.g. reusable_workflow_set_env_vars.yml) will be called by its caller (e.g. reusable_workflow_set_env_vars_caller.yml) like this:
name: reusable_workflow_set_env_vars_caller
on:
workflow_dispatch:
jobs:
set-env-vars:
uses: ./.github/workflows/reusable_workflow_set_env_vars.yml
with:
env_vars: |
TEST_VAR='test var'
GITHUB_HEAD_REF=$GITHUB_HEAD_REF
GITHUB_WORKSPACE=$GITHUB_WORKSPACE
GITHUB_REF=$GITHUB_REF
Apart from that, regarding your implementation of the reusable workflow (e.g. reusable_workflow_set_env_vars.yml):
As the env_vars is of string type, you need to somehow solidify it against YAML multiline whitespace variants e.g. >.
You can visualize and observe whitespace on this online utility (https://yaml-multiline.info/).
With the current implementation, there may be word splitting for variable values containing spaces in them. So, you might need to iterate per line i.e. up to the newline character. This thread (https://superuser.com/questions/284187/bash-iterating-over-lines-in-a-variable) might be helpful for this.

Passing env variable inputs to a reusable workflow

I'm trying to call a reusable workflow from another one, passing it some input variables. In the caller workflow I have some environment variables that I want to pass as input to the reusable one, like so:
env:
SOME_VAR: bla_bla_bla
ANOTHER_VAR: stuff_stuff
jobs:
print:
runs-on: ubuntu-latest
steps:
- name: Print inputs passed to the reusable workflow
run: |
echo "some var: $SOME_VAR"
echo "another var: $ANOTHER_VAR"
call_reusable:
uses: ...
with:
input_var: $SOME_VAR
another_input_var: $ANOTHER_VAR
the reusable workflow:
on:
workflow_dispatch:
workflow_call:
inputs:
input_var:
required: true
type: string
another_input_var:
required: true
type: string
jobs:
the_job:
runs-on: ubuntu-latest
steps:
- name: Print inputs
run: |
echo "input_var: ${{ inputs.input_var }}"
echo "another_input_var: ${{ inputs.another_input_var }}"
The Print inputs passed to the reusable workflow step works fine - all variables are correctly printed. However, the Print inputs step in the reusable workflow (the callee) does not work as expected - all the variables are empty.
I couldn't find anything in the docs suggesting that there is something wrong with my approach so, the way I see it, this should be working. Still, looking at the logs there is something wrong, as in the reusable workflow (callee) I can see:
Run echo "input_var: $SOME_VAR"
echo "another_input_var: $ANOTHER_VAR"
shell: /usr/bin/bash -e {0}
input_var:
another_input_var:
I tried wrapping the values in the with: block in $(echo) but that didn't work.
Any ideas?
After some researches, I found this thread explaining that:
You can’t pass ENV variables to the reusable workflow, so they are almost useless in this pattern.
Moreover, on the official documentation, it is stated that:
Any environment variables set in an env context defined at the workflow level in the caller workflow are not propagated to the called workflow.
Therefore, in your case, you wont be able to achieve what you want using directly the env variable, but there are workarounds.
Note: It would be great if GitHub comes up with a better way to assign values inline or pass them into reusable workflows, as handling each parameter many times just to pass it into a reusable workflow is cumbersome.
A workaround could be to use outputs from a first job, and use those outputs as the reusable workflow inputs.
Here is an example of how it could be done:
env:
SOME_VAR: bla_bla_bla
ANOTHER_VAR: stuff_stuff
jobs:
print:
runs-on: ubuntu-latest
outputs:
some_var: ${{ steps.step1.outputs.some_var }}
another_var: ${{ steps.step1.outputs.another_var }}
steps:
- name: Print inputs passed to the reusable workflow
id: step1
run: |
echo "some var: $SOME_VAR"
echo "::set-output name=some_var::$SOME_VAR"
echo "another var: $ANOTHER_VAR"
echo "::set-output name=another_var::$ANOTHER_VAR"
call_reusable:
needs:
- print
uses: ...
with:
input_var: ${{ needs.print.outputs.some_var }}
another_input_var: ${{ needs.print.outputs.another_var }}
That way, without updating the reusable workflow implementation, the inputs would be filled with the expected values.
Here are the workflow I used to test: main + reusable
And you can check the workflow run with the expected outcome here.
The use of ::set-output is now deprecated.
See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/.
Now recommended:
- name: Save state
run: echo "{name}={value}" >> $GITHUB_STATE
- name: Set output
run: echo "{name}={value}" >> $GITHUB_OUTPUT

Github actions - on pull request merged with speciphic labels

I want to trigger an action when the following constraints fulfilled.
pr merged to main branch AND pr contains label == 'site'.
This is what I came up with, the problem that this one is running when the pr is created.
Nothing happens when I merge it to main.
What am I doing wrong?
name: Build and push site
on:
pull_request:
branches: [main]
types: [labeled, closed]
jobs:
build-push-site:
if: github.event.pull_request.merged == true && contains( github.event.pull_request.labels.*.name, 'site')
uses: avifatal/nx-tokenct/.github/workflows/build-push.yml#main
with:
...
(inspired by this Run Github Actions when pull requests have a specific label)
Thanks
You should only work on "closed" branches, not "labeled" ones. You check labels later anyway.
This will execute when PR is closed, and check that
PR is merged
PR has label site
name: Build and push site
on:
pull_request:
types:
- closed
jobs:
build-push-site:
if: ${{ (github.event.pull_request.merged == true) && (contains(github.event.pull_request.labels.*.name, 'site')) }}
...

Exclude a combination involving a branch from GitHub Actions?

The bounty expires in 3 days. Answers to this question are eligible for a +50 reputation bounty.
IronSean wants to draw more attention to this question.
I'd like to exclude certain parts of my build matrix on certain branches. Something conceptually like the following, which doesn't work because branches is not a matrix variable.
name: Tests
on:
pull_request:
push:
branches: [hackage, develop]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
resolver: [lts-3.22 , lts-17, lts-18, lts, nightly]
exclude:
- branches: [hackage]
resolver: [nightly, lts]
Is there a way to do this idiomatically. Here, for example to exclude nightly and lts resolvers on the hackage branch?
You can use if with github.ref_name to identify the branch and the contains() function to check for matrix.resolver for the parts that you want to exclude.
Example:
if: github.ref_name == 'hackage' && contains(fromJSON('["nightly", "lts"]'), matrix.resolver)
if could be for jobs i.e. jobs.<job_id>.if or steps i.e. jobs.<job_id>.steps[*].if.

What is the difference between 'name' and 'id' in Github Actions

In the github action for Google app engine deploy there is a reference to the id in a github action:
- id: Deploy
uses: google-github-actions/deploy-appengine#main
with:
credentials: ${{ secrets.GCP_SA_KEY }}
But the Github Actions examples don't refer to id, rather it refers to the name as the id:
Each job must have an id to associate with the job. The key job_id is a string and its value is a map of the job's configuration data. You must replace <job_id> with a string that is unique to the jobs object. The <job_id> must start with a letter or _ and contain only alphanumeric characters, -, or _.
jobs:
my_first_job:
name: My first job
my_second_job:
name: My second job
What's the difference?
I believe you are confusing between a step definition, and a job definition.
This is a step:
steps:
- id: deploy
uses: google-github-actions/deploy-appengine#main
with:
credentials: ${{ secrets.gcp_credentials }}
as can be seen in the Usage section of the deploy-appengine repository.
The GitHub Actions workflow syntax documentation is the definitive guide - if you see something written elsewhere that is not mentioned in this guide, it is either a mistake or a misunderstanding.
As for the difference between ID and Name (both in jobs and steps):
ID is used as a reference, from other jobs or steps (for example, in jobs.<job_id>.needs).
Name is used for display purposes on GitHub.
Finally, for completeness, here are the ID/name related entries in the GitHub workflow syntax:
name: Test # <- Workflow name
jobs:
test: # <- Job ID
name: Run test suite # <- Optional Job Name
steps:
- id: checkout # <- Optional step ID
name: Checkout code # <- Optional step name