DEV Community

cloudsky13
cloudsky13

Posted on

Patch Deprecating save-state and set-output commands in GitHub Workflows using sed command.

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}"
Enter fullscreen mode Exit fullscreen mode

New Syntax

- name: Save state
  run: echo "{name}={value}" >> $GITHUB_STATE

- name: Set output
  run: echo "{name}={value}" >> $GITHUB_OUTPUT
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode
  1. 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.

  2. Now we observe the {name} and {value} are reusable entities and also these are separated by ::

  3. This means we can leverage capture groups to temporarily store these entities.

  4. 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

Enter fullscreen mode Exit fullscreen mode

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

Exit code 1, error message

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.

Solution to above error

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

Pull request


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!

Top comments (1)

Collapse
 
rajhawaldar profile image
Raj Hawaldar

Great automation @cloudsky13. Following you for more such content!