Github actions: set environment variable for Windows build with PowerShell - powershell

I define GENERATOR_PLATFORM as an empty environment variable, and then I want
to set it to something for my Windows build. But, the variable never gets set:
env:
GENERATOR_PLATFORM:
steps:
- name: windows-dependencies
if: startsWith(matrix.os, 'windows')
run: |
$generator= "-DCMAKE_GENERATOR_PLATFORM=x64"
echo "Generator: ${generator}"
echo "GENERATOR_PLATFORM=$generator" >> $GITHUB_ENV
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build
run: cmake $GITHUB_WORKSPACE $GENERATOR_PLATFORM

If you are using a Windows/PowerShell environment, you have to use $env:GITHUB_ENV instead of $GITHUB_ENV:
echo "GENERATOR_PLATFORM=$generator" >> $env:GITHUB_ENV
This way, you can access your env var through $env:GENERATOR_PLATFORM, eg:
run: echo $env:GENERATOR_PLATFORM

To follow up on #soltex answer: The proposed solution only works if the encoding is set to utf-8. If your runner is using Windows PowerShell (i.e. not PowerShell v7+, which uses utf-8 by default), utf16-le is written to the environment file, which causes the variable to not being set.
The correct solution is this:
echo "GENERATOR_PLATFORM=$generator" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
From: https://github.com/actions/runner-images/issues/5251#issuecomment-1071030822
Further reading: Changing PowerShell's default output encoding to UTF-8

Related

How to run command with preceding variable [duplicate]

This question already has answers here:
PowerShell: Setting an environment variable for a single command only
(10 answers)
Closed 4 years ago.
I know that you can pass environment variables to docker-compose.
docker-compose.yml
. . .
mysql:
image: mariadb:10.2
ports:
- "${DB_PORT}:3306"
. . .
$ DB_PORT=3396 docker-compose up
However this only works using bash. I am using PowerShell and am trying to find an equivalent that is only a one line command.
PS> $env:DB_PORT:3306 docker-compose up does not work. Neither does
multiline
$env:DB_PORT=3396 `
>> docker-compose -up
The error I get is
Unexpected token 'docker-compose' in expression or statement.
If I do it one at a time it does work...
PS> $env:DB_PORT=3396
PS> docker-compose -up
Is there not way to do this in PowerShell when the equivalent in bash is ridiculously simple?
POSIX-like shells such as bash offer a way to set environment variables in a command-scoped way, simply by prepending <varName>=<value> pairs directly to a command, as the following example demonstrates:
$ foo=bar bash -c 'echo "[$foo]"'; echo "[$foo]"
[bar]
[]
foo=bar defines environment variable foo for the bash -c '...' child process only; the next command - echo ... - does not see this variable.
PowerShell has NO equivalent construct.
The best you can do is to define the environment variable of interest first, in a separate statement, using ;, PowerShell's statement separator. Any external utility you invoke thereafter - which invariably runs in a child process - will see it, but note that the environment variable will remain in effect in the current PowerShell session, unless you manually remove it:
# Set the env. variable, call the command that should see it,
# remove it afterwards.
PS> $env:foo = 'bar'; bash -c 'echo "[$foo]"'; $env:foo = $null
[bar]
Note how $env:foo = $null i.e., setting the environment variable to $null is the same as removing it; alternatively, you could all Remove-Item env:foo
If you also want to restore a pre-existing value afterwards:
$env:foo = 'original'
# Temporarily change $env:foo to a different value, invoke the
# program that should see it, then restore the previous value.
& { $org, $env:foo = $env:foo, 'bar'; bash -c 'echo "[$foo]"'; $env:foo = $org }
$env:foo
The above yields:
[bar]
original
showing that while the bash process saw the temporary value, bar, the original value of $env:foo was restored afterwards.
Also note another important difference:
In POSIX-like shells, environment variables are implicitly surfaced as shell variables - they share the one and only namespace the shell has for variables.
By contrast, PowerShell surfaces environment variables only via the $env:<varName> namespace (e.g., $env:foo), which is distinct from the (prefix-less) namespace for PowerShell's own variables (e.g., $foo).

How to update the PATH in a github action workflow file for a windows-latest hosted runner

I'm currently trying to add GitHub actions workflow to a repo...
To do a C++/CMake/swig/python development (i.e. native python library dev), I need to download and install swigwin and have it available in the PATH...
Unfortunately it seems the $env:Path... command is not take into account during the next subsequent steps
Example
name: Python Windows CI
on: [push, pull_request]
jobs:
# Building using the GitHub runner environment directly.
build:
runs-on: windows-latest
steps:
- uses: actions/checkout#v2
- name: Check cmake
run: cmake --version
- name: Install swig
run: |
(New-Object System.Net.WebClient).DownloadFile("http://prdownloads.sourceforge.net/swig/swigwin-4.0.1.zip","swigwin-4.0.1.zip");
Expand-Archive .\swigwin-4.0.1.zip .;
$env:Path += ";.\swigwin-4.0.1";
swig -version;
- name: Check swig
run: swig -version # swig cmdlet not found...
Observed
> Set up job
> Run actions/checkout#v23s
> Check cmake
v Install swig
...
SWIG Version 4.0.1
...
v Check swig
swig -version
shell: C:\Program Files\PowerShell\6\pwsh.EXE -command ". '{0}'"
swig : The term 'swig' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At D:\a\_temp\0a8dc0e1-ec51-429b-abd0-cb3597e983ac.ps1:2 char:1
+ swig -version
+ ~~~~
+ CategoryInfo : ObjectNotFound: (swig:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException
##[error]Process completed with exit code 1.
The add-path and set-env commands have been deprecated the 1st October 2020 for security reasons: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/
The recommended way to add to %PATH% is using environment files as follows:
Assuming you use Powershell, the default shell:
echo "C:\directory\to\add\to\path" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
or alternatively for bash:
echo "C:\directory\to\add\to\path" >> $GITHUB_PATH
There is also a shorter way to achieve this in Powershell, which is the default shell that windows hosted runner uses:
Add-Content $env:GITHUB_PATH "C:\directory\to\add\to\path"
See Add-Content
The other answers are a bit out of date (due to GitHub Actions deprecating add-path as explained in #Kel Solaar's answer), here's a full example based on #Mizux answer:
- name: Install swig
if: "startsWith(runner.os, 'windows')"
run: |
(New-Object System.Net.WebClient).DownloadFile("http://prdownloads.sourceforge.net/swig/swigwin-4.0.1.zip","swigwin-4.0.1.zip");
Expand-Archive .\swigwin-4.0.1.zip .;
echo "$((Get-Item .).FullName)/swigwin-4.0.1" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Another difference to #Mizus answer is that the absolute path to the swig directory is used, this is to ensure it still works even though the working directory changes.
EDIT: GitHub have deprecated this, please see other answer...
ref: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/
You must use the action syntax echo "::add-path::...", in your case:
...
- name: Install swig
run: |
(New-Object System.Net.WebClient).DownloadFile("http://prdownloads.sourceforge.net/swig/swigwin-4.0.1.zip","swigwin-4.0.1.zip");
Expand-Archive .\swigwin-4.0.1.zip .;
echo "::add-path::./swigwin-4.0.1"
- name: Check swig
run: swig -version
src: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#adding-a-system-path

set PATH in azure pipelines in Windows

I am using Azure Pipelines to build a Rakudo binary for Raku (previously aka Perl 6) in Windows.
This is my azure-pipelines.yml file:
jobs:
- job: Windows
pool:
vmImage: 'vs2017-win2016'
steps:
- bash: |
mkdir -p $(Build.SourcesDirectory)/rakudo-win
curl -L https://github.com/rakudo/rakudo/releases/download/2019.07.1/rakudo-2019.07.1.tar.gz | tar xz
mv rakudo-2019.07.1 rakudo
cd rakudo
C:/Strawberry/perl/bin/perl Configure.pl --gen-moar --gen-nqp --backends=moar --prefix=$(Build.SourcesDirectory)/rakudo-win
make
make install
- bash: |
echo "##vso[task.prependpath]$(Build.SourcesDirectory)/rakudo-win/bin"
- bash: |
perl6 -v
The pipeline script builds perl6 binary fine inside $(Build.SourcesDirectory)/rakudo-win/bin folder. There is indeed perl6.exe inside $(Build.SourcesDirectory)/rakudo-win/bin. To make it available, I set the path by prepending it in the bash script. But when I try to run command perl6 -v, the build fails at this step.
I searched for similar issues in SO here, here, here.
Still I could not solve my issue. Any help how to make perl6 binary available at PATH?
EDITED
Next thing I did was create another .yml script as follows:
jobs:
- job: Windows
pool:
vmImage: 'vs2017-win2016'
steps:
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
- pwsh: |
mkdir -p C:\rakudo-win
Invoke-WebRequest -Uri "https://github.com/rakudo/rakudo/releases/download/2019.07.1/rakudo-2019.07.1.tar.gz" -OutFile "rakudo.tar.gz"
tar -xvf .\rakudo.tar.gz
cd rakudo-2019.07.1
C:\Strawberry\perl\bin\perl Configure.pl --gen-moar --gen-nqp --backends=moar --prefix=C:\rakudo-win
make
make install
- pwsh: |
$oldpath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path
$newpath = "C:\rakudo-win\bin;$oldpath"
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newpath
- script: |
SET PATH=C:\rakudo-win\bin;%PATH%
- script: |
perl6 -v
and tried changing PATH twice once in powershell and another in cmdline. But still it throws following error:
'perl6' is not recognized as an internal or external command,
operable program or batch file.
Any help?
The best method I have found for setting the PATH for subsequent tasks in Azure Pipelines is by using the logging command syntax mentioned in the first of the three SO links you looked at. Since you are using PowerShell in your updated yaml pipeline, the command would be:
Write-Host "##vso[task.prependpath]$(Build.SourcesDirectory)/rakudo-win/bin"
Note that this only applies to subsequent tasks, if you try outputting the PATH variable in the current task it will not have updated.
In fact, you are very close to the correct solution. Your second powershell task has set the PATH successfully. You can add another separate task to print out the system PATH value to verify this.
- pwsh: |
$NewPathInRegistry = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path
Write-Host $NewPathInRegistry
To set the PATH programmatically, you can not use set command, this command can indeed set the environment variable PATH, but the disadvantage of set is the new PATH value is only available in the current command line terminal, it does not actually been added into the System Variable. That's why you were getting an unrecognized error in the next script step.
To permanently add the directory to environment variable PATH so that it can work for next others steps, you need use setx or add them into Registry by using reg add. But the usage of setx has limitation that the PATH value max to 1024 characters. So, here the best solution is updating PATH by modifying the Registry value.
BUT, updating Registry still has another issue, you must kill current process and run one new process to run perl6 so that it can read the new available Registry setting.
If run stop-process in Azure devops pipeline, it will make the task failed with exit code -1. This is the expected exit code, so you can set the continueOnError: true to step so that the next steps can continue.
why not just do this:
- script: |
PATH=$BUILD_SOURCESDIRECTORY/rakudo-win/bin:$PATH perl6 -v

Concourse CI shell commands being quoted

We are trying to copy multiple jar files in a Concourse CI package.yml file as part of a run step:
run:
path:
args:
- -exc
- |
...
cp project/target/*.jar build-output/.
But Concourse is adding single quotes to the source file, so that it's looking for a file named 'project/target/*.jar', and of course it's not finding it.
+ cp 'project/target/*.jar' build-output/.
cp: can't stat 'project/target/*.jar'; no such file or directory
I even tried putting double quotes around the jar file name, hoping that it might keep Concourse from changing it, but it made no difference.
We want to use file globbing so that we can use this generically, so that we don't need to know the file names ahead of time. Is there any way we can get this to work?
Bizarre. Try specifying the shell:
run:
path: sh <== missing `sh` or the shell you have available in the image
args:
- -exc
- |
...
cp project/target/*.jar build-output/ <== no `.`
Mhhh, actually maybe the quotes are misleading, there is nothing below project/target :-) Try with
run:
path: sh <== missing `sh` or the shell you have available in the image
args:
- -exc
- |
...
# is anything here ?
ls -1 project/target
cp project/target/*.jar build-output/ <== no `.`
set -x mode quotes all arguments; it doesn't mean that it actually quoted the argument. I think you had a red herring, but I'm glad you got it working.

Environment variables in Compose using Powershell with only 1 line [duplicate]

This question already has answers here:
PowerShell: Setting an environment variable for a single command only
(10 answers)
Closed 4 years ago.
I know that you can pass environment variables to docker-compose.
docker-compose.yml
. . .
mysql:
image: mariadb:10.2
ports:
- "${DB_PORT}:3306"
. . .
$ DB_PORT=3396 docker-compose up
However this only works using bash. I am using PowerShell and am trying to find an equivalent that is only a one line command.
PS> $env:DB_PORT:3306 docker-compose up does not work. Neither does
multiline
$env:DB_PORT=3396 `
>> docker-compose -up
The error I get is
Unexpected token 'docker-compose' in expression or statement.
If I do it one at a time it does work...
PS> $env:DB_PORT=3396
PS> docker-compose -up
Is there not way to do this in PowerShell when the equivalent in bash is ridiculously simple?
POSIX-like shells such as bash offer a way to set environment variables in a command-scoped way, simply by prepending <varName>=<value> pairs directly to a command, as the following example demonstrates:
$ foo=bar bash -c 'echo "[$foo]"'; echo "[$foo]"
[bar]
[]
foo=bar defines environment variable foo for the bash -c '...' child process only; the next command - echo ... - does not see this variable.
PowerShell has NO equivalent construct.
The best you can do is to define the environment variable of interest first, in a separate statement, using ;, PowerShell's statement separator. Any external utility you invoke thereafter - which invariably runs in a child process - will see it, but note that the environment variable will remain in effect in the current PowerShell session, unless you manually remove it:
# Set the env. variable, call the command that should see it,
# remove it afterwards.
PS> $env:foo = 'bar'; bash -c 'echo "[$foo]"'; $env:foo = $null
[bar]
Note how $env:foo = $null i.e., setting the environment variable to $null is the same as removing it; alternatively, you could all Remove-Item env:foo
If you also want to restore a pre-existing value afterwards:
$env:foo = 'original'
# Temporarily change $env:foo to a different value, invoke the
# program that should see it, then restore the previous value.
& { $org, $env:foo = $env:foo, 'bar'; bash -c 'echo "[$foo]"'; $env:foo = $org }
$env:foo
The above yields:
[bar]
original
showing that while the bash process saw the temporary value, bar, the original value of $env:foo was restored afterwards.
Also note another important difference:
In POSIX-like shells, environment variables are implicitly surfaced as shell variables - they share the one and only namespace the shell has for variables.
By contrast, PowerShell surfaces environment variables only via the $env:<varName> namespace (e.g., $env:foo), which is distinct from the (prefix-less) namespace for PowerShell's own variables (e.g., $foo).