Patch Deprecating save-state and set-output commands in GitHub Workflows using sed command.
cloudsky13
Posted on May 6, 2023
Introduction
GitHub had announced that the set-output
and save-state
workflow commands will be deprecated from 31st May 2023.
More details on it here.
Let’s cover the changes required to patch this.
Old Syntax
- name: Save state
run: echo "::save-state name={name}::{value}"
- name: Set output
run: echo "::set-output name={name}::{value}"
New Syntax
- name: Save state
run: echo "{name}={value}" >> $GITHUB_STATE
- name: Set output
run: echo "{name}={value}" >> $GITHUB_OUTPUT
Automating syntax update using sed.
If you’re new to sed. You can read my introductory blog on sed to get started.
Since syntax for both set-output
and save-state
are identical we’ll be able to use the same logic for building the command.
First, we need to identify a pattern which we can pass in the sed command to mimic the old syntax.
sed -i 's|"::set-output name=\([^"]*\)::\([^"]*\)"|"\1=\2" >> $GITHUB_OUTPUT|g'
Firstly, we notice how
::set-output name=
will always be constant throughout all usages of the set-output, so we can we it as a search element.Now we observe the {name} and {value} are reusable entities and also these are separated by
::
This means we can leverage capture groups to temporarily store these entities.
Once done with the above steps we can simply append
>> $GITHUB_OUTPUT
as per the new syntax.
GitHub Workflow to commit changes in another repo
The below workflow takes the repository name
as input and replaces any occurrence of the old syntax of set-output
with the new syntax.
name: Update-target-repo-with-new-syntax
run-name: "Auto-patching"
on:
workflow_dispatch:
inputs:
Target-repo-name:
description: "Repository name to be updated"
required: true
env:
GITHUB_TOKEN: ${{ secrets.TARGET_REPO_TOKEN }}
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Repo
uses: actions/checkout@v3
with:
path: main
- name: Checkout Target Repo
uses: actions/checkout@v3
with:
repository: organisation-name/repo-name
token: $GITHUB_TOKEN
path: my-target-repo-path
ref: main
- name: Commit changes to Target Repo and create pull request
run: |
cd $GITHUB_WORKSPACE/my-target-repo-path/
git config --global user.email "<github_email>"
git config --global user.name "<github_username>"
git branch standard-branch-name
git checkout standard-branch-name
workflowList=$(ls ./.github/workflows/*)
for workflow in $workflowList
do
if grep -q -e ::save-state -e ::set-output $workflow; then
sed -i 's|"::set-output name=\([^"]*\)::\([^"]*\)"|"\1=\2" >> $GITHUB_OUTPUT|g' $workflow
sed -i 's|"::save-state name=\([^"]*\)::\([^"]*\)"|"\1=\2" >> $GITHUB_STATE|g' $workflow
fi
done
set -e
if ! { git commit -am "Replacing-deprecated-syntax-in-workflows"; }; then
echo "Nothing to commit"
exit 0
else
git push -u origin `standard-branch-name
gh pr create -B main -H standard-branch-name --title 'Updating-deprecated-synatx' --body 'Created by Github action' --repo organisation-name/repo-name
fi
Workflow steps in detail
- name: Checkout Source Repo
The workflow first checks out to the current repository using GitHub Action actions/checkout@v3
.
- name: Checkout Target Repo
It then checks out to the target repository using GitHub PAT Token or personal access token.
Note that path: my-target-repo-path
creates a directory in the PWD (Present Working Directory) and checks out to the target repo code in the ref
branch i.e. main.
Learn more on actions/checkout@v3
here.
- name: Commit changes to Target Repo and create pull request
In this step the workflow first changes the working directory using the cd
command. $GITHUB_WORKSPACE stores the default working directory of the GitHub runner.
Using git commands, it then creates a new branch with a standard name in the target repository.
After that it stores all the file names present in the workflow directory of the dot GitHub folder into a variable called workflowList
.
The for loop then iterates on each file name and use the grep command to check if the file returns any output for the search elements we discussed earlier.
Note we use -q
or --quiet
flag with the grep
command is used to suppress all normal outputs of the command.
Error Handling
If a match is found for the search element, the sed commands we orchestrated are executed on that workflow file to update it with the new syntax.
If no match is found the control simply proceeds to check the next file until all the files are checked.
Once all files are checked we need to commit and push our changes. But what if there weren't any changes, you get an error as below
To avoid this error, we need to make sure that git commit is executed only if there are any changes made in the working tree.
To check if working tree is clean or not I have added this if block, which checks the exit code from the git commit command.
If the exit code is non-zero it simply prints "Nothing to commit" and changes the exit code to 0. Thereby preventing the workflow from unwanted failures.
And in the else condition the changes are pushed to the new branch created by the workflow. Then a PR is created using the GitHub CLI command gh pr create
. Read about the command here.
Pull Request snapshot
Closing remarks
Thank You for reading my blog post.
Tried to cover an example of a real-world problem statement in this blog along with basic error handling.
If you liked my approach to this problem, do share it the with your peers.
Do share any ideas that pop up in your head which revolves around the use of bash and GitHub to solve a real-world problem.
Drop your comments, feedback and questions below. Have a good day. Cheers!
Posted on May 6, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.