im-open / create-release

An action that creates a GitHub release
MIT License
0 stars 1 forks source link

ARCH-1083 - Tweak the default commitish behavior depending on trigger type #3

Closed danielle-casella-adams closed 2 years ago

danielle-casella-adams commented 2 years ago

The github.sha value doesn't seem to be the sha we want to use for pull_request triggers. It does not match the sha inside of the github.context.pull_request.head.sha. I still have some confusion about what github.sha actually refers to because it does not match any sha in my log.

This PR will use the value from the head branch for pull requests. This will work much better than before where it wasn't tagging the right commit.

danielle-casella-adams commented 2 years ago

@hpractv I dismissed your review by pushing the change I started pushing yesterday but I didn't hit the one last dialog box to push it up.

mike-schenk commented 2 years ago

@danielle-casella-adams

From: https://docs.github.com/en/rest/reference/pulls#get-a-pull-request

When you get, create, or edit a pull request, GitHub creates a merge commit to test whether the pull request can be automatically merged into the base branch. This test commit is not added to the base branch or the head branch.

... that's why you don't see it in any of your logs.

The value of the merge_commit_sha attribute changes depending on the state of the pull request. _Before merging a pull request, the merge_commit_sha attribute holds the SHA of the test merge commit._ After merging a pull request, the merge_commit_sha attribute changes depending on how you merged the pull request:

  • If merged as a merge commit, merge_commit_sha represents the SHA of the merge commit.
  • If merged via a squash, merge_commit_sha represents the SHA of the squashed commit on the base branch.
  • If rebased, merge_commit_sha represents the commit that the base branch was updated to.

(emphasis added)

The context.sha property is set from the GITHUB_SHA environment variable whose value is apparently set by the pull request trigger from the pull request's merge_commit_sha attribute.

The pull_request trigger documentation that says GITHUB_SHA is the "Last merge commit on the GITHUB_REF branch" and that GITHUB_REF is the "PR merge branch" isn't exactly clear about this. It's subtle that the "PR merge branch" is neither the PR's base branch, nor its head branch. It's refs/pull/:prNumber/merge... that's the branch where that trial merge commit lives.

Note that the default behavior of actions/checkout will still checkout the trial merge commit, so unless the workflow specifies to checkout the PR's head branch, this change in this PR will end up tagging a different commit than the one that supplied the source code that was built. When a PR's branch is already up-to-date with its base branch, this should be harmless, but when it's not, it could be misleading.

So, I haven't tried this version 2.0.0 yet, but I wonder what it tags when a PR is merged. Will it tag the resulting commit on the base branch, (the merge_commit_sha described above), or will it tag the last commit on the head branch before the merge?


I've been hacking on a CI build workflow that behaves similarly to what is typically done in TeamCity today:

Comparing the standard TeamCity behavior and the GitHub actions default behavior, the pros and cons are almost mirrors of each other.


This is a snippet of my WIP workflow

jobs:
  configure-workflow:
    runs-on: ubuntu-latest

    outputs:
      IS_PRERELEASE: ${{ env.IS_PRERELEASE }}
      REF_TO_BUILD: ${{ env.REF_TO_BUILD }}
      DO_BUILD: ${{ env.DO_BUILD }}
      DO_TAG_RELEASE: ${{ env.DO_TAG_RELEASE }}
      DO_NOTIFY_TEAMS: ${{ env.DO_NOTIFY_TEAMS }}
      CLEANUP_PRERELEASES: ${{ env.CLEANUP_PRERELEASES }}

    steps:
    - name: What is the state of this PR?
      uses: actions/github-script@v5
      with:

        # A draft PR should calculate a prerelease version number
        # A draft PR should build the "head" branch rather than the trial merged commit
        # A draft PR should run all tests with coverage and publish status checks
        # A draft PR should NOT tag and upload a GitHub release
        # A draft PR whose branch is behind the base should produce a build warning
        # A draft PR should NOT advertise its build outcome via Teams

        # A ready-to-review PR should calculate a prerelease version number
        # A ready-to-review PR should build the "head" branch rather than the trial merged commit
        # A ready-to-review PR should run all tests with coverage and publish status checks
        # A ready-to-review PR should tag and upload a GitHub release with the built version
        # A ready-to-review PR whose branch is behind the base should produce a failed status check
        # A ready-to-review PR should advertise its build outcome via Teams

        # A closed PR merged _to main_ should calculate a release version number
        # A closed PR merged _to main_ should build the merged commit
        # A closed PR merged _to main_ should run all tests with coverage and publish status checks
        # A closed PR merged _to main_ should tag and upload a GitHub release with the built version
        # A closed PR merged _to main_ should advertise its build outcome via Teams
        # A closed PR merged _to_main_ should delete prereleases that were previously created from the PR's branch

        # A closed PR merged to a branch other than main should NOT build or test anything, and not upload a release.
        # A closed PR merged to a branch other than main should delete prereleases that were previously created from the PR's branch

        # A closed PR that's not merged should NOT build or test anything, and not upload a release.
        # A closed PR that's not merged should delete prereleases that were previously created from the PR's branch

        script: |
          //NOTE: if this were written in an action, the github.x expressions would need be replaced with different properties on context.
          var sourceRef = '${{ github.head_ref }}';
          var targetRef = '${{ github.base_ref }}';
          var mergeRef = '${{ github.ref }}';

          var prIsDraft = '${{ github.event.pull_request.draft }}' === 'true';
          var prClosed = '${{ github.event.action }}' === 'closed';
          var prMergedToMain = '${{ github.event.pull_request.merged }}' === 'true' && targetRef === 'main';

          var isPreRelease = !prMergedToMain

          var refToBuildAndTag = sourceRef;
          if(prMergedToMain)
            refToBuildAndTag = mergeRef;

          var doBuild = true;
          if(prClosed && !prMergedToMain)
            doBuild = false;

          var doTagRelease = false;
          if(doBuild && !prIsDraft)
          {
            doTagRelease = true;
          }
          var doNotifyTeams = doTagRelease;
          core.exportVariable('IS_PRERELEASE', isPreRelease);
          core.exportVariable('REF_TO_BUILD', refToBuildAndTag);
          core.exportVariable('DO_BUILD', doBuild);
          core.exportVariable('DO_TAG_RELEASE', doTagRelease);
          core.exportVariable('DO_NOTIFY_TEAMS', doNotifyTeams);
          core.exportVariable('CLEANUP_PRERELEASES', prClosed);

          core.notice(`DO_BUILD=${doBuild}, DO_TAG_RELEASE=${doTagRelease}, REF_TO_BUILD=${refToBuildAndTag}, IS_PRERELEASE=${isPreRelease}, DO_NOTIFY_TEAMS=${doNotifyTeams}, CLEANUP_PRERELEASES=${prClosed}`);
          // QUESTION: is the following correct to determine if a PR branch is behind its target?
          if(!prClosed) {
            // Then we want to warn (or output a status check) if the PR branch is not up-to-date with the base branch
            var comparison = await github.rest.repos.compareCommitsWithBasehead({
              owner: context.repo.owner,
              repo: context.repo.repo,
              basehead: `${targetRef}...${sourceRef}`
            });
            console.log(comparison);
            if(prIsDraft) {
              core.warning('Branch is behind merge target.');
            }
            else {
              core.error('Branch is behind merge target.');
              //TODO: create the failed status check
            }
          }
  dotnet-build-and-test:

    runs-on: ubuntu-latest

    needs: [configure-workflow]
    if: ${{ needs.configure-workflow.outputs.DO_BUILD == 'true' }}

    env:
      DO_BUILD: ${{ needs.configure-workflow.outputs.DO_BUILD }}
      DO_TAG_RELEASE: ${{ needs.configure-workflow.outputs.DO_TAG_RELEASE }}
      REF_TO_BUILD: ${{ needs.configure-workflow.outputs.REF_TO_BUILD }}
      IS_PRERELEASE: ${{ needs.configure-workflow.outputs.IS_PRERELEASE }}

      PROJECT_ROOT: './src'
      DEPLOY_ZIP: 'published_app.zip'
      REPO_URL: 'https://github.com/${{ github.repository }}'

    steps:

      - name: Fetch all repo branches
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
          ref: ${{ env.REF_TO_BUILD }}

      # ... calculate version number, build, test, and optionally package  

      - name: Create Release with published_artifacts
        id: create_release
        if: ${{ env.DO_TAG_RELEASE == 'true' }}
        uses: im-open/create-release@v1.0.0
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }} # Special per-job token generated by GH for interacting with the repo
          commitish: ${{ env.REF_TO_BUILD }}
          delete-existing-release: true # Handy when you hit 're-run jobs' on a workflow run
          tag-name: ${{ env.NEXT_VERSION }}
          prerelease: ${{ env.IS_PRERELEASE }}
          asset-path: ${{ env.PROJECT_ROOT }}/${{ env.DEPLOY_ZIP }}
          asset-name: ${{ env.DEPLOY_ZIP }}
          asset-content-type: application/zip
          #body:          # TODO: add release notes
mike-schenk commented 2 years ago

So, I haven't tried this version 2.0.0 yet, but I wonder what it tags when a PR is merged. Will it tag the resulting commit on the base branch, (the merge_commit_sha described above), or will it tag the last commit on the head branch before the merge?

I just saw that you already answered this question in your email:

when a merge to main happens the tag ends up on the last commit from the branch, not the merge commit.

I'm sure that's not what we want. So I believe we will need to conditionally figure out a ref based on the state of the PR so we can supply it as a commitish ref to the im-open/create-release.