sethvargo / ratchet

A tool for securing CI/CD workflows with version pinning.
Apache License 2.0
769 stars 32 forks source link

Unintended changes #87

Closed prein closed 2 months ago

prein commented 2 months ago

New issue?

TL;DR

In some workflows ratchet introduces changes that are not related to its purpose and not welcome. For example consider the following 2 diffs: 1.

 jobs:
-
   semantic_release:
     runs-on: ubuntu-latest
-    if:
-      contains('
-      refs/heads/main
-      refs/heads/master
-      refs/heads/staging
-      refs/heads/release
-      ', github.ref)
+    if: contains(' refs/heads/main refs/heads/master refs/heads/staging refs/heads/release ', github.ref)
     outputs:

In the above an empty line below jobs: was removed. It was intended to be there. if: stanza was reformatted. While I can see where it's coming from and that we are not using vanilla yaml format, I'd prefer ratchet to do only the changes related to pinning versions.

  1. from the same file
       - name: Publish GitHub release
    -        uses: softprops/action-gh-release@v2
    -        if: inputs.publish_release &&
    -          steps.semantic_release.outputs.new_release_published == 'true'
    +        uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # ratchet:softprops/action-gh-release@v2
    +        if: inputs.publish_release && steps.semantic_release.outputs.new_release_published == 'true'
         with:
           tag_name: v${{ steps.semantic_release.outputs.new_release_version }}
           body: ${{ steps.semantic_release.outputs.new_release_notes }}
    +
           token: ${{ secrets.user_github_token || secrets.GITHUB_TOKEN }}
    +
    +
    +
    +
    +
    +
    +

    Empty lines added at the end of file in in between some of the lines under with:

Expected behavior

ratchet updating only the lines where it pins versions

Observed behavior

ratchet reformats yaml and adds empty lines

Version

ratchet 0.10.0 (homebrew, linux/amd64)

On what operating system are you using ratchet?

Linux

CI/CD workflow file

name: semantic_release

on:
  workflow_call:
    inputs:
      template:
        type: string
        required: true
      publish_release:
        type: boolean
        required: false
        default: true
    outputs:
      new_release_published:
        value: ${{ jobs.semantic_release.outputs.new_release_published }}
      new_release_version:
        value: ${{ jobs.semantic_release.outputs.new_release_version }}
      new_release_channel:
        value: ${{ jobs.semantic_release.outputs.new_release_channel }}
      new_release_notes:
        value: ${{ jobs.semantic_release.outputs.new_release_notes }}
    secrets:
      user_github_token:
        required: false

jobs:

  semantic_release:
    runs-on: ubuntu-latest
    if:
      contains('
      refs/heads/main
      refs/heads/master
      refs/heads/staging
      refs/heads/release
      ', github.ref)
    outputs:
      new_release_published: ${{ steps.semantic_release.outputs.new_release_published }}
      new_release_version: ${{ steps.semantic_release.outputs.new_release_version }}
      new_release_channel: ${{ steps.semantic_release.outputs.new_release_channel }}
      new_release_notes: ${{ steps.semantic_release.outputs.new_release_notes }}
    steps:

      - name: Checkout
        uses: actions/checkout@v4
        with:
          show-progress: 'false'

      - name: Configure registry
        run: |
          npm config set @redacted:registry https://npm.pkg.github.com
          npm config set //npm.pkg.github.com/:_authToken ${{ secrets.GITHUB_TOKEN }}

      - name: Semantic release
        id: semantic_release
        uses: cycjimmy/semantic-release-action@v4
        with:
          semantic_version: 18
          extends: |
            @redacted/semantic-release-config-${{ inputs.template }}
          extra_plugins: |
            conventional-changelog-conventionalcommits@4
            @redacted/semantic-release-config-${{ inputs.template }}@^1.x.x
        env:
          GH_TOKEN: ${{ secrets.user_github_token || secrets.GITHUB_TOKEN }}

      - name: Publish GitHub release
        uses: softprops/action-gh-release@v2
        if: inputs.publish_release &&
          steps.semantic_release.outputs.new_release_published == 'true'
        with:
          tag_name: v${{ steps.semantic_release.outputs.new_release_version }}
          body: ${{ steps.semantic_release.outputs.new_release_notes }}
          token: ${{ secrets.user_github_token || secrets.GITHUB_TOKEN }}

Relevant log output

No response

Additional information

No response

sethvargo commented 2 months ago

Hi @prein this is a documented known issue with the upstream YAML parsing library.

For

-    if:
-      contains('
-      refs/heads/main
-      refs/heads/master
-      refs/heads/staging
-      refs/heads/release
-      ', github.ref)
+    if: contains(' refs/heads/main refs/heads/master refs/heads/staging refs/heads/release ', github.ref)

that is functionally-equivalent YAML, and the YAML parsing library operates on nodes (not text). You can probably use a slightly different format to achieve the same result, which will not be reformatted:

jobs:
  semantic_release:
    runs-on: ubuntu-latest
    if: >
      contains('
      refs/heads/main
      refs/heads/master
      refs/heads/staging
      refs/heads/release
      ', github.ref)

The same is true for:

-        uses: softprops/action-gh-release@v2
-        if: inputs.publish_release &&
-          steps.semantic_release.outputs.new_release_published == 'true'
+        uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # ratchet:softprops/action-gh-release@v2
+        if: inputs.publish_release && steps.semantic_release.outputs.new_release_published == 'true'

you could switch to the following which would preserve scalar formatting:

      - name: Publish GitHub release
        uses: softprops/action-gh-release@v2
        if: >
          inputs.publish_release &&
          steps.semantic_release.outputs.new_release_published == 'true'

With those two changes, the file looks like this:

name: semantic_release

on:
  workflow_call:
    inputs:
      template:
        type: string
        required: true
      publish_release:
        type: boolean
        required: false
        default: true
    outputs:
      new_release_published:
        value: ${{ jobs.semantic_release.outputs.new_release_published }}
      new_release_version:
        value: ${{ jobs.semantic_release.outputs.new_release_version }}
      new_release_channel:
        value: ${{ jobs.semantic_release.outputs.new_release_channel }}
      new_release_notes:
        value: ${{ jobs.semantic_release.outputs.new_release_notes }}
    secrets:
      user_github_token:
        required: false

jobs:

  semantic_release:
    runs-on: ubuntu-latest
    if:
      contains('
      refs/heads/main
      refs/heads/master
      refs/heads/staging
      refs/heads/release
      ', github.ref)
    outputs:
      new_release_published: ${{ steps.semantic_release.outputs.new_release_published }}
      new_release_version: ${{ steps.semantic_release.outputs.new_release_version }}
      new_release_channel: ${{ steps.semantic_release.outputs.new_release_channel }}
      new_release_notes: ${{ steps.semantic_release.outputs.new_release_notes }}
    steps:

      - name: Checkout
        uses: actions/checkout@v4
        with:
          show-progress: 'false'

      - name: Configure registry
        run: |
          npm config set @redacted:registry https://npm.pkg.github.com
          npm config set //npm.pkg.github.com/:_authToken ${{ secrets.GITHUB_TOKEN }}

      - name: Semantic release
        id: semantic_release
        uses: cycjimmy/semantic-release-action@v4
        with:
          semantic_version: 18
          extends: |
            @redacted/semantic-release-config-${{ inputs.template }}
          extra_plugins: |
            conventional-changelog-conventionalcommits@4
            @redacted/semantic-release-config-${{ inputs.template }}@^1.x.x
        env:
          GH_TOKEN: ${{ secrets.user_github_token || secrets.GITHUB_TOKEN }}

      - name: Publish GitHub release
        uses: softprops/action-gh-release@v2
        if: inputs.publish_release &&
          steps.semantic_release.outputs.new_release_published == 'true'
        with:
          tag_name: v${{ steps.semantic_release.outputs.new_release_version }}
          body: ${{ steps.semantic_release.outputs.new_release_notes }}
          token: ${{ secrets.user_github_token || secrets.GITHUB_TOKEN }}

that results in no diff:

ratchet update action.yml && ratchet unpin action.yml