Passing object as a variable to Github action - github

How can I pass an additional object type variable?
When I try to do this in the yaml file a get an error:
Invalid type found: one of string , number , boolean were expected but an array was found
on: pull_request_review
name: Label approved pull requests
jobs:
labelWhenApproved:
name: Label when approved
runs-on: ubuntu-latest
steps:
- name: Label when approved
uses: ***
env:
APPROVALS:
- value: "1"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ADD_LABEL: "approved"
REMOVE_LABEL: "awaiting%20review"

The env: section allows you to pass Environment Variables to the actions. The environment variables are key - value. You cannot pass objects.
From GitHub docs:
A map of environment variables
In some cases you can "stringify" a whole object and pass it as an environment variable using toJSON(), but the action itself should "know" how to handle it (e.g to parse the object from the string)
Example how to pass all secrets to an actions:
env:
SECRETS: '${{ toJSON(secrets) }}'
Note: There can be env in different levels in the workflows - you can have them on "global", on a job, or on step (in your case it is a step)

This is a bit of an old question and you've probably found some sort of workaround, but here is an option for you. This is what we do when we need to pass an object into a string input:
env:
APPROVALS: |
- value: "1"
The | converts the next block into a multi-line string. Everything until the next outdentation is a string.
What this means is that your action *** must then use a yaml parser to convert from the string to an object.

Related

Parameterize `runs-on` value for GitHub action

I have this GitHub workflow that I need to parameterize on which runners runs. So in the YAML file I tried:
# ...
jobs:
process:
name: Process
runs-on: ${{ secrets.GH_RUNNER_TAG }}
# ...
However, I get this error:
The workflow is not valid. .github/workflows/action.yml (Line: 12, Col: 14): Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.GH_RUNNER_TAG
Is the secrets injection not available for this element? Is there some other alternative? The value does not need to be a secret but I need to have it in one place and not edit hundreds of YAML files everytime the runner tag would change...
EDIT1:
I've tried, as GuiFalourd suggested, to create an environment variable at the workflow level which would hold the secret:
env:
RUNNER_LABEL: ${{ secrets.GH_RUNNER_TAG }}
jobs:
analyze:
name: Analyze
runs-on: $RUNNER_LABEL
And it doesn't work. The action gets stuck. I tried using:
$RUNNER_LABEL -> gets stuck
"$RUNNER_LABEL" -> gets stuck, too
${{ env.RUNNER_LABEL }} -> action does not start, outputs error:
The workflow is not valid. .github/workflows/action.yml (Line: 14, Col: 14): Unrecognized named-value: 'env'. Located at position 1 within expression: env.RUNNER_LABEL
Furthermore, I've checked that the env var is properly assigned, by placing a valid, hard-coded value for runs-on and setting first step as:
steps:
- name: Test
run: echo "$RUNNER_LABEL"
which produces "***" - proof that a secret has been output and redacted automatically by GitHub.
This is achievable using Reusable Workflow by configuring the "called" workflow to accept inputs from the caller.
The main pipeline which we can name it as "process" will use a shared codebase/pipeline lets call it "common" which can accept inputs, one of these inputs can be the runs-on value.
For example
# common.yml
name: parameterized job
on:
workflow_call:
inputs:
runner:
required: true
type: string
jobs:
common:
name: Common
runs-on: ${{ inputs.runner }}
steps:
- run: echo "Hello World"
# process.yml
name: process
on:
push:
jobs:
process:
uses: username/repo/.github/workflows/common.yml#branch
with:
runner: machine_with_specific_label # using ${{ env.MY_RUNNER_LABEL }} is possible as well

How do I pass the job name into a github action's input?

jobs:
my-name:
name: "My Name"
...
steps:
- name: Slack Notification
uses: my-action
with:
slack-msg: ${{ jobs.${{ env.GITHUB_JOB }}.name }}
I want that slack-msg to evaluate to "My Name". I'm using my-action in multiple jobs, and I always want to pass in the job name, but I don't know how to do that. When I tried the above, the job literally didn't run and I don't know how to troubleshoot why: the github workflow log for my-name literally doesn't exist.
How do I pass job-name into an input parameter?
As nested expression are not supported you can use a trick like below to obtaint the matrix job name.
jobs:
test:
env:
# to expose matrix job name to steps, which is not possible with expansions
JOB_NAME: ${{ matrix.name || format('{0} ({1})', matrix.tox-target, matrix.os) }}
name: ${{ matrix.name || format('{0} ({1})', matrix.tox-target, matrix.os) }}
Note that you cannot really access the matrix name, but you can ensure you save the same name into an environment variable and use that.

How to set Azure DevOps yaml variables conditionally based on parameter value

I am trying to set variables based on a parameter value in a yaml pipeline. It seems that I've read many other posts which show examples like the one below that the authors have said worked, but I cannot get past issues when trying to do something like below.
I've tried many variations on this example as well, too many to list here. Sometimes it will show 'values' as a duplicate key. In other cases I've been able to try and start a run and get the prompt with environment selection, but then opening the stage dialog throws a parse error.
Is there some sort of difference between variable declaration at the top of the file vs in a stage or job? That seems to be the difference that I notice when reading through other examples.
Ultimately what I'm trying to do is set the ServiceConnection variable value based on the value of the environment parameter.
parameters:
- name: environment
displayName: Environment
type: string
values:
- DEV
- TEST
pr: none
trigger: none
pool: PrivateAgentPool
variables:
- name: 'isMain'
value: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]
- name: 'buildConfiguration'
value: 'Release'
- name: 'environment'
value: ${{ parameters.environment }}
- name: 'ServiceConnection'
${{ if eq(variables['environment'], 'DEV') }}:
value: 'svcConnectionDev'
${{ if eq(variables['environment'], 'TEST') }}:
value: 'svcConnectionTest'
Looks like your solution is almost correct. Consider the below example.
parameters:
- name: region
type: string
default: westeurope
values:
- westeurope
- northeurope
variables:
${{ if eq(parameters['region'], 'westeurope') }}:
ServiceConnection: "svcConnectionDev"
${{ else }}:
enter code here
if you want to used this ServiceConnectionvar across you can do it just by calling $ServiceConnection
you could use bash with conditions:
steps:
- bash: |
echo "##vso[task.setvariable variable=ServiceConnection]svcConnectionDev"
condition: eq('${{parameters.environment}}', 'DEV')

Azure Yaml Pipelines - Dynamic object parameter to template

I would like to trigger a job template with an object as parameter.
Unfortunately, even based on the examples I couldn't find a way to do that.
I would appreciate if someone could guide me how to achieve this.
What I want to achieve, is to replace the ["DEPLOY", "CONFIG"] part with a dynamic variable:
- template: job-template.yaml
parameters:
jobs: ["DEPLOY", "CONFIG"]
This is not possible. YAML is very limited here and you may read more about this here
Yaml variables have always been string: string mappings.
So for instance you can define paramaters as complex type
Template file
parameters:
- name: 'instances'
type: object
default: {}
- name: 'server'
type: string
default: ''
steps:
- ${{ each instance in parameters.instances }}:
- script: echo ${{ parameters.server }}:${{ instance }}
Main file
steps:
- template: template.yaml
parameters:
instances:
- test1
- test2
server: someServer
But you are not able to do it dynamically/programmatically as every output you will create will end up as simple string.
What you can do is to pass as string and then using powershell split that string. But it all depends what you want to run further because you won't be able to simply iterate over yaml structure in that way. All what you can do is to run in in powershell loop and do something, but it can be not enough for you.
It's possible with some logic. see below
- template: job-template.yaml
parameters:
param: ["DEPLOY", "CONFIG"]
and in job-template.yaml file you can define. So every job name will be different
parameters:
param: []
jobs:
- ${{each jobName in parameters.param}}:
- job: ${{jobName}}
steps:
- task: Downl......

How can I invoke a YAML pipeline that has both variables and runtime parameters?

I have a scenario where I need to have both:
runtime parameters, so that the pipeline can be triggered manually from the UI, where users triggering it can choose from a predefined set of options (defined in YAML)
variables, so that the pipeline can be invoked via REST APIs
Regarding runtime parameters, I was able to create the following sample pipeline:
parameters:
- name: image
displayName: Pool Image
type: string
default: ubuntu-latest
values:
- windows-latest
- ubuntu-latest
trigger: none
stages:
- stage: A
jobs:
- job: A
steps:
- pwsh: |
echo "This should be triggering against image: $MY_IMAGE_NAME"
env:
MY_IMAGE_NAME: ${{ parameters.image }}
When I run it, I can see the dropdown list where I can choose the image name and it is reflected in the output message of the PowerShell script.
Regarding variables, I have defined one called "image" here (notice the value is empty):
The idea now is to invoke the pipeline from REST APIs and have the image name replaced by the value coming from the variable:
{
"definition": {
"id": 1
},
"sourceBranch": "master",
"parameters": "{\"image\": \"windows-latest\" }"
}
In order to make the step print the value I'm passing here, I need to correct the environment variable in some way. I thought it would be sufficient to write something like:
env:
MY_IMAGE_NAME: ${{ coalesce(variables.image, parameters.image) }}
That's because I want to give the priority to the variables, then to parameters, so that in case none is specified, I always have a default value the pipeline can use.
However, this approach doesn't work, probably because we're dealing with different expansion times for variables, but I don't really know what I should be writing instead (if there is a viable option, of course).
What I also tried is:
env:
MY_IMAGE_NAME: ${{ coalesce($(image), parameters.image) }}
MY_IMAGE_NAME: ${{ coalesce('$(image)', parameters.image) }}
MY_IMAGE_NAME: $[ coalesce(variables.image, parameters.image) ]
MY_IMAGE_NAME: $[ coalesce($(image), parameters.image) ]
None of those are working, so I suspect this may not be feasible at all.
There is a workaround that I'm currently thinking of, which is to create two different pipelines so that those can be invoked independently, but while this is quite easy for me to accomplish, given I'm using a lot of templates, I don't find it the right way to proceed, so I'm open to any suggestion.
I tested and found you might need to define a variable and assign the parameter's value to it (eg. Mimage: ${{parameters.image}}). And define another variable(eg. Vimage) and assign $[coalesce(variables.image, variables.Vimage)] to it. Then refer to $(Vimage) in the env field of powershell task. Please check out below yaml.
parameters:
- name: image
displayName: Pool Image
type: string
default: ubuntu-latest
values:
- windows-latest
- ubuntu-latest
trigger: none
stages:
- stage: A
jobs:
- job: A
variables:
Mimage: ${{parameters.image}}
Vimage: $[coalesce(variables.image, variables.Mimage)]
steps:
- pwsh: |
echo "This should be triggering against image: $env:MY_IMAGE_NAME"
env:
MY_IMAGE_NAME: $(Vimage)
Env field of powershell task is usually for mapping secret variables. You can directly refer to $(Vimage) in the powershell script: echo "This should be triggering against image: $(Vimage).
Note: To queue a build via REST API with provided parameters, you need to check Let users override this value when running this pipeline to make the varilabe to be settable at queue time.
Update:
You can try passing the variables to the parameters of the template to make the parameters for template dynamic. Please check below simple yaml.
jobs:
- template: template.yaml
parameters:
MTimage: ${{parameters.image}}
VTimage: $(Vimage)
template.yaml:
parameters:
MTimage:
VTimage:
jobs:
- job: buildjob
steps:
- powershell: |
echo "${{parameters.VTimage}}"
echo "${{parameters.MTimage}}"