diff --git a/.github/workflows/build-and-review-pr.yml b/.github/workflows/build-and-review-pr.yml index 6b09336..bf6aa38 100644 --- a/.github/workflows/build-and-review-pr.yml +++ b/.github/workflows/build-and-review-pr.yml @@ -21,216 +21,791 @@ on: # without disabling that requirement. If we have a status check that is always produced, # we can also use that to require all branches be up to date before they are merged. +# ------------------------------------------------------------------------------------- +# NOTE: This repo duplicates the reusable build-and-review workflow in im-open/.github +# that the rest of the actions use. If changes are needed in this workflow the +# same changes should be made in im-open/.github. This workflow is duplicated +# so it can use a local copy of itself in the workflow. This allows us to test +# the build pipeline with git-version-lite changes before we merge those changes. +# ------------------------------------------------------------------------------------- + jobs: - setup: + # setup-build-and-review: + # runs-on: ubuntu-latest + # outputs: + # HAS_SOURCE_CODE_CHANGES: ${{ steps.source-code.outputs.HAS_CHANGES }} + # NEXT_VERSION: ${{ steps.version.outputs.NEXT_VERSION }} + + # steps: + # - name: Checkout + # uses: actions/checkout@v3 + # with: + # fetch-depth: 0 + + # - name: Check for code changes to the action source code + # id: source-code + # uses: im-open/did-custom-action-code-change@v1 + # with: + # files-with-code: 'action.yml,package.json,package-lock.json' + # folders-with-code: 'src,dist' + # token: ${{ secrets.GITHUB_TOKEN }} + + # - name: Action Source Code Changed - ${{ steps.source-code.outputs.HAS_CHANGES }} (open for details) + # run: | + # if [ "${{ steps.source-code.outputs.HAS_CHANGES }}" == "true" ]; then + # echo "This PR changes the action's source code. Proceed with subsequent steps." + # else + # echo "This PR does not change the action's source code. Skipping subsequent steps." + # fi + + # - name: Get the next version for the repo + # if: steps.source-code.outputs.HAS_CHANGES == 'true' + # id: version + # uses: ./git-version-lite + + # - name: The next action version will be - ${{ steps.version.outputs.NEXT_VERSION || 'N/A'}} + # if: steps.source-code.outputs.HAS_CHANGES == 'true' + # run: echo "The next action version will be - ${{ steps.version.outputs.NEXT_VERSION }}" + + # build-and-review-pr: + # runs-on: ubuntu-latest + # needs: [setup-build-and-review] + + # env: + # NEXT_VERSION: ${{ needs.setup-build-and-review.outputs.NEXT_VERSION || 'N/A' }} + # HAS_CODE_CHANGES: ${{ needs.setup-build-and-review.outputs.HAS_SOURCE_CODE_CHANGES }} + # IS_FORK: ${{ github.event.pull_request.head.repo.fork }} + # PR_SOURCE: ${{ github.event.pull_request.head.repo.fork == true && 'fork' || 'branch' }} + # README: README.md + # HAS_BUILD_STEP: 'true' + # NEEDS_BUILD_COMMIT: false + # NEEDS_README_COMMIT: false + + # steps: + # - name: Action Source Code Changed (open for details) + # run: | + # if [ "${{env.HAS_CODE_CHANGES}}" == "true" ]; then + # echo "This PR changes the action's source code. Proceed with subsequent steps and jobs." + # else + # echo "This PR does not change the action's source code. Skipping subsequent steps and jobs." + # fi + + # # ---------------------------------------------------------------------------------------------------- + # # + # # The remaining steps in this build will use the env.HAS_CODE_CHANGES condition. Setting it on each + # # step rather than the job will ensure this job always runs and that we can use it for status checks. + # # + # # ---------------------------------------------------------------------------------------------------- + + # - name: PR Source - ${{ env.PR_SOURCE }} + # if: env.HAS_CODE_CHANGES == 'true' + # run: echo "PRs can come from a branch or a fork. This PR is from a ${{ env.PR_SOURCE }}." + + # - name: Checkout + # if: env.HAS_CODE_CHANGES == 'true' + # uses: actions/checkout@v3 + + # # ----------------------------------- + # # Check if action has been recompiled + # # ----------------------------------- + # - name: If action has build step - Setup Node 16.x + # uses: actions/setup-node@v3 + # if: env.HAS_CODE_CHANGES == 'true' && env.HAS_BUILD_STEP == 'true' + # with: + # node-version: 16.x + + # - name: If action has build step - Build the action + # if: env.HAS_CODE_CHANGES == 'true' && env.HAS_BUILD_STEP == 'true' + # run: 'npm run build' + + # - name: If action has build step - Check for unstaged build changes (open for details) + # if: env.HAS_CODE_CHANGES == 'true' && env.HAS_BUILD_STEP == 'true' + # run: | + # if [[ "$(git status --porcelain)" != "" ]]; then + # echo "There action needs to be re-built." + # echo "NEEDS_BUILD_COMMIT=true" >> "$GITHUB_ENV" + # else + # echo "The action has already been re-built" + # fi + + # # ------------------------------------- + # # Check if README needs version updates + # # ------------------------------------- + # - name: ${{ env.README }} - Update version to @${{ env.NEXT_VERSION }} + # if: env.HAS_CODE_CHANGES == 'true' + # id: update-readme + # uses: im-open/update-action-version-in-file@v1 + # with: + # file-to-update: ./${{ env.README }} # Default: 'README.md' + # action-name: ${{ github.repository }} + # updated-version: ${{ env.NEXT_VERSION }} + + # - name: ${{ env.README }} - Check for unstaged version changes (open for details) + # if: env.HAS_CODE_CHANGES == 'true' + # run: | + # if [ "${{ steps.update-readme.outputs.has-changes }}" == "true" ]; then + # echo "README.md needs version updates." + # echo "NEEDS_README_COMMIT=true" >> "$GITHUB_ENV" + # else + # echo "README.md does not need version updates." + # fi + + # # ------------------------------------------- + # # Fail the workflow if any updates are needed + # # ------------------------------------------- + # - name: Fail the workflow if there are any outstanding changes + # if: env.HAS_CODE_CHANGES == 'true' && (env.NEEDS_BUILD_COMMIT == 'true' || env.NEEDS_README_COMMIT == 'true') + # id: summary + # uses: actions/github-script@v6 + # with: + # script: | + + # // Setup vars for the script to use + # const hasBuildStep = ${{ env.HAS_BUILD_STEP }}; + # const needsBuildChanges = hasBuildStep && ${{ env.NEEDS_BUILD_COMMIT }}; + # const needsReadmeChanges = ${{ env.NEEDS_README_COMMIT }}; + + # const contribId = '#contributing'; + # const contributionLink = `https://github.com/${{ github.repository }}${contribId}`; + # const contributingTitle = contribId.replace('#', '').split('-').map(w => { return w.slice(0, 1).toUpperCase() + w.slice(1) }).join(' '); + + # const exampleId = '#usage-examples'; + # const readmeLink = `${{ github.event.pull_request.head.repo.html_url }}/blob/${{ github.event.pull_request.head.ref }}/${{ env.README }}`; + # const readmeExampleLink = `${readmeLink}${exampleId}`; + # const readmeExampleTitle = exampleId.replace('#', '').split('-').map(w => { return w.slice(0, 1).toUpperCase() + w.slice(1) }).join(' '); + + # // Construct the instructions for fixing the PR + # let instructions = `Before this PR can be merged, the following item(s) should be addressed to comply with the action's ${contributingTitle} Guidelines.` + # if (needsReadmeChanges) { + # instructions += ` + # - Please update the action's version in the ${readmeExampleTitle} section of ${{ env.README }}. Each instance of this action should be updated to: + # \`uses: ${{ github.repository }}@${{ env.NEXT_VERSION }}\``; + # } + # if (needsBuildChanges){ + # instructions += ` + # - Please ensure the action has been recompiled by running the following command from the root of the repository: + # \`npm run build\``; + # } + + # // Update the instructions with links + # let instructionsWithLinks = instructions + # .replace('of ${{ env.README }}.', `of [${{ env.README }}](${readmeLink}).`) + # .replace(`${contributingTitle} Guidelines`, `[${contributingTitle} Guidelines](${contributionLink})`) + # .replace(readmeExampleTitle, `[${readmeExampleTitle}](${readmeExampleLink})`); + + # // Comment on PR for branches. A fork's GH_TOKEN only has 'read' permission on all scopes so a comment cannot be made. + # if (!${{ env.IS_FORK }}) { + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: instructionsWithLinks + # }) + # } + + # // Add workflow summary & fail the build + # core.summary + # .addRaw(instructionsWithLinks) + # .write(); + # core.setFailed(instructions); + + test-general: runs-on: ubuntu-latest - outputs: - HAS_SOURCE_CODE_CHANGES: ${{ steps.source-code.outputs.HAS_CHANGES }} - NEXT_VERSION: ${{ steps.version.outputs.NEXT_VERSION }} + + env: + # These env variables are set by git-version-lite + PRIOR_VERSION: '' + NEXT_VERSION: '' + NEXT_MINOR_VERSION: '' + NEXT_MAJOR_VERSION: '' + PRIOR_VERSION_NO_PREFIX: '' + NEXT_VERSION_NO_PREFIX: '' + NEXT_MINOR_VERSION_NO_PREFIX: '' + NEXT_MAJOR_VERSION_NO_PREFIX: '' + + # Info for the repo we'll be testing git-version-lite against + TESTING_REPO: 'im-open/internal-repo-for-testing' + TEST_BRANCH: 'my-test-branch' + + # These NEXT tags are set based on the fetch-depth not being set, so + # git-version-lite starts from 0.0.0 when calculating the next version + PRIOR_TAG: '0.0.0' + + # This will be the next set of tags if default release type is 'minor' + NEXT_PATCH_TAG_FOR_MINOR_RELEASE_TYPE: '0.1.0' + NEXT_MINOR_TAG_FOR_MINOR_RELEASE_TYPE: '0.1' + NEXT_MAJOR_TAG_FOR_MINOR_RELEASE_TYPE: '0' + + # This will be the next set of tags if default release type is 'major' + NEXT_PATCH_TAG_FOR_MAJOR_RELEASE_TYPE: '1.0.0' + NEXT_MINOR_TAG_FOR_MAJOR_RELEASE_TYPE: '1.0' + NEXT_MAJOR_TAG_FOR_MAJOR_RELEASE_TYPE: '1' + steps: - - name: Checkout + #-------------------------------------- + # SETUP + #-------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: Setup - Checkout testing repo in the root directory + if: always() uses: actions/checkout@v3 with: - fetch-depth: 0 + ref: main + repository: ${{ env.TESTING_REPO }} + ssh-key: ${{ secrets.SSH_KEY_TESTING_REPO }} + # fetch-depth: 0 # Do not use this because we want to test what happens when fetch-depth is not set + + - name: Setup - Checkout this action (git-version-lite) into a 'gvl' subdirectory + if: always() + uses: actions/checkout@v3 + with: + path: ./gvl + + - name: Setup - List directories + if: always() + run: | + echo -e "\nRoot directory contents:" + ls -a + + echo -e "\ngit-version-lite contents:" + ls -a ./gvl - - name: Check for code changes to the action source code - id: source-code - uses: im-open/did-custom-action-code-change@v1 + #-------------------------------------- + # UNEXPECTED RELEASE TYPE + #-------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: When an unexpected default release type is provided + if: always() + uses: ./gvl + id: unexpected-release-type + continue-on-error: true # This is needed because we expect the step to fail. We need it to "pass" in order for the test job to succeed. with: - files-with-code: 'action.yml,package.json,package-lock.json' - folders-with-code: 'src,dist' - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Action Source Code Changed - ${{ steps.source-code.outputs.HAS_CHANGES }} (open for details) - run: | - if [ "${{ steps.source-code.outputs.HAS_CHANGES }}" == "true" ]; then - echo "This PR changes the action's source code. Proceed with subsequent steps." - else - echo "This PR does not change the action's source code. Skipping subsequent steps." - fi - - - name: Get the next version for the repo - if: steps.source-code.outputs.HAS_CHANGES == 'true' - id: version - uses: ./ - - - name: The next action version will be - ${{ steps.version.outputs.NEXT_VERSION || 'N/A'}} - if: steps.source-code.outputs.HAS_CHANGES == 'true' - run: echo "The next action version will be - ${{ steps.version.outputs.NEXT_VERSION }}" + default-release-type: 'unexpected' + # calculate-prerelease-version: false + # branch-name: '' + # tag-prefix: v + # fallback-to-no-prefix-search: true + + - name: Then the outcome should be failure + if: always() + run: ./gvl/test/assert-values-match.sh --name "step outcome" --expected "failure" --actual "${{ steps.unexpected-release-type.outcome }}" + + - name: Then the outputs should be empty + if: always() + run: | + ./test/assert-values-match.sh --name "PRIOR_VERSION output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.PRIOR_VERSION }}" + ./test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.PRIOR_VERSION_NO_PREFIX }}" + ./test/assert-values-match.sh --name "NEXT_VERSION output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.NEXT_VERSION }}" + ./test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.NEXT_VERSION_NO_PREFIX }}" + ./test/assert-values-match.sh --name "NEXT_MINOR_VERSION output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.NEXT_MINOR_VERSION }}" + ./test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./test/assert-values-match.sh --name "NEXT_MAJOR_VERSION output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.NEXT_MAJOR_VERSION }}" + ./test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX output" --expected "" --actual "${{ steps.unexpected-release-type.outputs.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: And the environment variables should be empty + if: always() + run: | + ./test/assert-values-match.sh --name "PRIOR_VERSION env" --expected "" --actual "${{ env.PRIOR_VERSION }}" + ./test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX env" --expected "" --actual "${{ env.PRIOR_VERSION_NO_PREFIX }}" + ./test/assert-values-match.sh --name "NEXT_VERSION env" --expected "" --actual "${{ env.NEXT_VERSION }}" + ./test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX env" --expected "" --actual "${{ env.NEXT_VERSION_NO_PREFIX }}" + ./test/assert-values-match.sh --name "NEXT_MINOR_VERSION env" --expected "" --actual "${{ env.NEXT_MINOR_VERSION }}" + ./test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX env" --expected "" --actual "${{ env.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./test/assert-values-match.sh --name "NEXT_MAJOR_VERSION env" --expected "" --actual "${{ env.NEXT_MAJOR_VERSION }}" + ./test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX env" --expected "" --actual "${{ env.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + #-------------------------------------- + # FALLBACK TO NO PREFIX + #-------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: When a new version is requested from git-version-lite with fallback-to-no-prefix set + if: always() + uses: ./gvl + id: fallback + continue-on-error: true # This is needed because we expect the step to fail. We need it to "pass" in order for the test job to succeed. + with: + tag-prefix: qbdb + fallback-to-no-prefix-search: true + # calculate-prerelease-version: false + # branch-name: '' + #default-release-type: 'major' + + + - name: Then the outcome should be success + if: always() + run: ./gvl/test/assert-values-match.sh --name "step outcome" --expected "success" --actual "${{ steps.fallback.outcome }}" + + - name: Then the outputs should be empty + if: always() + run: | + prior="${{ env.PRIOR_TAG }}" + patch="${{ env.NEXT_PATCH_TAG_FOR_MAJOR_RELEASE_TYPE}}" + minor="${{ env.NEXT_MINOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "$prior" --actual "${{ steps.fallback.outputs.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION" --expected "$patch" --actual "${{ steps.fallback.outputs.NEXT_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "$minor" --actual "${{ steps.fallback.outputs.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "$major" --actual "${{ steps.fallback.outputs.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ steps.fallback.outputs.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX" --expected "$patch" --actual "${{ steps.fallback.outputs.NEXT_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ steps.fallback.outputs.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ steps.fallback.outputs.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + + - name: And the environment variables should be empty + if: always() + run: | + prior="${{ env.PRIOR_TAG }}" + patch="${{ env.NEXT_PATCH_TAG_FOR_MAJOR_RELEASE_TYPE}}" + minor="${{ env.NEXT_MINOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "$prior" --actual "${{ env.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION" --expected "$patch" --actual "${{ env.NEXT_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ env.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX" --expected "$patch" --actual "${{ env.NEXT_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + test-no-base-commits: + runs-on: ubuntu-latest + + env: + # These env variables are set by git-version-lite + PRIOR_VERSION: '' + NEXT_VERSION: '' + NEXT_MINOR_VERSION: '' + NEXT_MAJOR_VERSION: '' + PRIOR_VERSION_NO_PREFIX: '' + NEXT_VERSION_NO_PREFIX: '' + NEXT_MINOR_VERSION_NO_PREFIX: '' + NEXT_MAJOR_VERSION_NO_PREFIX: '' + + # Info for the repo we'll be testing git-version-lite against + TESTING_REPO: 'im-open/internal-repo-for-testing' + TEST_BRANCH: 'my-test-branch' + + # These NEXT tags are set based on the fetch-depth not being set, so + # git-version-lite starts from 0.0.0 when calculating the next version + PRIOR_TAG: '0.0.0' + + # This will be the next set of tags if default release type is 'minor' + NEXT_PATCH_TAG_FOR_MINOR_RELEASE_TYPE: '0.1.0' + NEXT_MINOR_TAG_FOR_MINOR_RELEASE_TYPE: '0.1' + NEXT_MAJOR_TAG_FOR_MINOR_RELEASE_TYPE: '0' + + # This will be the next set of tags if default release type is 'major' + NEXT_PATCH_TAG_FOR_MAJOR_RELEASE_TYPE: '1.0.0' + NEXT_MINOR_TAG_FOR_MAJOR_RELEASE_TYPE: '1.0' + NEXT_MAJOR_TAG_FOR_MAJOR_RELEASE_TYPE: '1' + + + steps: + #-------------------------------------- + # SETUP + #-------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: Setup - Checkout testing repo in the root directory + if: always() + uses: actions/checkout@v3 + with: + ref: main + repository: ${{ env.TESTING_REPO }} + ssh-key: ${{ secrets.SSH_KEY_TESTING_REPO }} + # fetch-depth: 0 # Do not use this because we want to test what happens when fetch-depth is not set + + - name: Setup - Checkout this action (git-version-lite) into a 'gvl' subdirectory + if: always() + uses: actions/checkout@v3 + with: + path: ./gvl - build-and-review-pr: + - name: Setup - List directories + if: always() + run: | + echo -e "\nRoot directory contents:" + ls -a + + echo -e "\ngit-version-lite contents:" + ls -a ./gvl + + #------------------------------------------------- + # BASE COMMIT CANNOT BE DETERMINED FOR PRE-RELEASE + #------------------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: When a repo has been cloned without history or tags + run: "" + + - name: And a pre-release version is requested with a default-release-type of minor and a prefix of 'c' + if: always() + uses: ./gvl + id: prerelease-version + with: + calculate-prerelease-version: true + branch-name: 'refs/heads/my@test branch' # This should be cleaned up to 'my-test-branch' by git-version-lite + default-release-type: minor + tag-prefix: c + #fallback-to-no-prefix-search: true + + - name: Then the outcome should be success + if: always() + run: ./gvl/test/assert-values-match.sh --name "step outcome" --expected "success" --actual "${{ steps.prerelease-version.outcome }}" + + - name: Then the outputs should be based on the next minor version which includes the branch name in NEXT_VERSION + if: always() + run: | + + # In addition to the next semantic version, NEXT_VERSION will contain the branch name and a timestamp. Test + # that it contains the parts we know (version and branch name). We don't know the timestamp, so ignore that. + prefix="c" + patch="${{ env.NEXT_PATCH_TAG_FOR_MINOR_RELEASE_TYPE}}" + branch="${{ env.TEST_BRANCH }}" + substring="$patch-$branch." + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION" --substring "$prefix$substring" --string "${{ steps.prerelease-version.outputs.NEXT_VERSION }}" + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION_NO_PREFIX" --substring "$substring" --string "${{ steps.prerelease-version.outputs.NEXT_VERSION_NO_PREFIX }}" + + # These vars will NOT contain the branch name or timestamp so test these as exact matches. + prior="${{ env.PRIOR_TAG }}" + minor="${{ env.NEXT_MINOR_TAG_FOR_MINOR_RELEASE_TYPE}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_MINOR_RELEASE_TYPE}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "$prefix$prior" --actual "${{ steps.prerelease-version.outputs.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "$prefix$minor" --actual "${{ steps.prerelease-version.outputs.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "$prefix$major" --actual "${{ steps.prerelease-version.outputs.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ steps.prerelease-version.outputs.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ steps.prerelease-version.outputs.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ steps.prerelease-version.outputs.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: And the environment variables should be based on the next minor version which includes the branch name in NEXT_VERSION + if: always() + run: | + + # In addition to the next semantic version, NEXT_VERSION will contain the branch name and a timestamp. Test + # that it contains the parts we know (version and branch name). We don't know the timestamp, so ignore that. + prefix="c" + patch="${{ env.NEXT_PATCH_TAG_FOR_MINOR_RELEASE_TYPE}}" + branch="${{ env.TEST_BRANCH }}" + substring="$patch-$branch." + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION" --substring "$prefix$substring" --string "${{ env.NEXT_VERSION }}" + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION_NO_PREFIX" --substring "$substring" --string "${{ env.NEXT_VERSION_NO_PREFIX }}" + + # These vars will NOT contain the branch name or timestamp so test these as exact matches. + prior="${{ env.PRIOR_TAG }}" + minor="${{ env.NEXT_MINOR_TAG_FOR_MINOR_RELEASE_TYPE}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_MINOR_RELEASE_TYPE}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "$prefix$prior" --actual "${{ env.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "$prefix$minor" --actual "${{ env.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "$prefix$major" --actual "${{ env.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ env.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + + #--------------------------------------------- + # BASE COMMIT CANNOT BE DETERMINED FOR RELEASE + #--------------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: When a repo has been cloned without history or tags + run: "" + + - name: And a release version is requested with a default-release-type of major and prefix of 'none' + if: always() + uses: ./gvl + id: release-version + with: + calculate-prerelease-version: false + default-release-type: major + tag-prefix: none + #branch-name: '' + #fallback-to-no-prefix-search: true + + - name: Then the outcome should be success + if: always() + run: ./gvl/test/assert-values-match.sh --name "step outcome" --expected "success" --actual "${{ steps.release-version.outcome }}" + + - name: And the outputs should be based on the next major version + if: always() + run: | + prior="${{ env.PRIOR_TAG }}" + patch="${{ env.NEXT_PATCH_TAG_FOR_MAJOR_RELEASE_TYPE}}" + minor="${{ env.NEXT_MINOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "$prior" --actual "${{ steps.release-version.outputs.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION" --expected "$patch" --actual "${{ steps.release-version.outputs.NEXT_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "$minor" --actual "${{ steps.release-version.outputs.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "$major" --actual "${{ steps.release-version.outputs.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ steps.release-version.outputs.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX" --expected "$patch" --actual "${{ steps.release-version.outputs.NEXT_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ steps.release-version.outputs.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ steps.release-version.outputs.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: And the environment variables should be based on the next major version + if: always() + run: | + prior="${{ env.PRIOR_TAG }}" + patch="${{ env.NEXT_PATCH_TAG_FOR_MAJOR_RELEASE_TYPE}}" + minor="${{ env.NEXT_MINOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_MAJOR_RELEASE_TYPE}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "$prior" --actual "${{ env.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION" --expected "$patch" --actual "${{ env.NEXT_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ env.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX" --expected "$patch" --actual "${{ env.NEXT_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + + test-patch-increments: runs-on: ubuntu-latest - needs: [setup] env: - NEXT_VERSION: ${{ needs.setup.outputs.NEXT_VERSION || 'N/A' }} - HAS_CODE_CHANGES: ${{ needs.setup.outputs.HAS_SOURCE_CODE_CHANGES }} - IS_FORK: ${{ github.event.pull_request.head.repo.fork }} - PR_SOURCE: ${{ github.event.pull_request.head.repo.fork == true && 'fork' || 'branch' }} - README: README.md - HAS_BUILD_STEP: 'true' - NEEDS_BUILD_COMMIT: false - NEEDS_README_COMMIT: false + # These env variables are set by git-version-lite + PRIOR_VERSION: '' + NEXT_VERSION: '' + NEXT_MINOR_VERSION: '' + NEXT_MAJOR_VERSION: '' + PRIOR_VERSION_NO_PREFIX: '' + NEXT_VERSION_NO_PREFIX: '' + NEXT_MINOR_VERSION_NO_PREFIX: '' + NEXT_MAJOR_VERSION_NO_PREFIX: '' + + # Info for the repo we'll be testing git-version-lite against + TESTING_REPO: 'im-open/internal-repo-for-testing' + TEST_BRANCH: 'my-test-branch' + + # All the remaining tags are for TESTING_REPO. If anything changes tag-wise in that repo, these values need to be updated. + PRIOR_TAG: '1.1.0' + + # This will be the next set of tags if commits do not have major/minor keywords + NEXT_PATCH_TAG_FOR_PATCH_UPDATES: '1.1.1' + NEXT_MINOR_TAG_FOR_PATCH_UPDATES: '1.1' + NEXT_MAJOR_TAG_FOR_PATCH_UPDATES: '1' + + # This will be the next set of tags if commits contain minor increment keywords + NEXT_PATCH_TAG_FOR_MINOR_UPDATES: '1.2.0' + NEXT_MINOR_TAG_FOR_MINOR_UPDATES: '1.2' + NEXT_MAJOR_TAG_FOR_MINOR_UPDATES: '1' + + # This will be the next set of tags if commits contain major increment keywords + NEXT_PATCH_TAG_FOR_MAJOR_UPDATES: '2.0.0' + NEXT_MINOR_TAG_FOR_MAJOR_UPDATES: '2.0' + NEXT_MAJOR_TAG_FOR_MAJOR_UPDATES: '2' + steps: - - name: Action Source Code Changed (open for details) - run: | - if [ "${{env.HAS_CODE_CHANGES}}" == "true" ]; then - echo "This PR changes the action's source code. Proceed with subsequent steps and jobs." - else - echo "This PR does not change the action's source code. Skipping subsequent steps and jobs." - fi - - # ---------------------------------------------------------------------------------------------------- - # - # The remaining steps in this build will use the env.HAS_CODE_CHANGES condition. Setting it on each - # step rather than the job will ensure this job always runs and that we can use it for status checks. - # - # ---------------------------------------------------------------------------------------------------- - - - name: PR Source - ${{ env.PR_SOURCE }} - if: env.HAS_CODE_CHANGES == 'true' - run: echo "PRs can come from a branch or a fork. This PR is from a ${{ env.PR_SOURCE }}." - - - name: Checkout - if: env.HAS_CODE_CHANGES == 'true' + #-------------------------------------- + # SETUP + #-------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: Setup - Checkout testing repo in the root directory + if: always() uses: actions/checkout@v3 + with: + ref: main + repository: ${{ env.TESTING_REPO }} + fetch-depth: 0 + ssh-key: ${{ secrets.SSH_KEY_TESTING_REPO }} - # ----------------------------------- - # Check if action has been recompiled - # ----------------------------------- - - name: If action has build step - Setup Node 16.x - uses: actions/setup-node@v3 - if: env.HAS_CODE_CHANGES == 'true' && env.HAS_BUILD_STEP == 'true' + - name: Setup - Checkout this action (git-version-lite) into a 'gvl' subdirectory + if: always() + uses: actions/checkout@v3 with: - node-version: 16.x - - - name: If action has build step - Build the action - if: env.HAS_CODE_CHANGES == 'true' && env.HAS_BUILD_STEP == 'true' - run: 'npm run build' - - - name: If action has build step - Check for unstaged build changes (open for details) - if: env.HAS_CODE_CHANGES == 'true' && env.HAS_BUILD_STEP == 'true' - run: | - if [[ "$(git status --porcelain)" != "" ]]; then - echo "There action needs to be re-built." - echo "NEEDS_BUILD_COMMIT=true" >> "$GITHUB_ENV" - else - echo "The action has already been re-built" - fi - - # ------------------------------------- - # Check if README needs version updates - # ------------------------------------- - - name: ${{ env.README }} - Update version to @${{ env.NEXT_VERSION }} - if: env.HAS_CODE_CHANGES == 'true' - id: update-readme - uses: im-open/update-action-version-in-file@v1 + path: ./gvl + + - name: Setup - List directories + if: always() + run: | + echo -e "\nRoot directory contents:" + ls -a + + echo -e "\ngit-version-lite contents:" + ls -a ./gvl + + - name: Setup - Configure git for commiting + if: always() + run: | + git config user.name my-bot + git config user.email my-bot@im-open.com + + git fetch + git checkout -b ${{ env.TEST_BRANCH }} + git commit --allow-empty -m "My first message" + + + #--------------------------------------------- + # PRE-RELEASE WITH PATCH INCREMENT + #--------------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: When a repo has commits consistent with a patch increment + if: always() + run: ./gvl/test/setup/reset-repo-and-commit.sh --message "Making backwards compatible changes to the repo for a pre-release" + + - name: And a pre-release version is requested from git-version-lite + if: always() + uses: ./gvl + id: prerelease-version with: - file-to-update: ./${{ env.README }} # Default: 'README.md' - action-name: ${{ github.repository }} - updated-version: ${{ env.NEXT_VERSION }} - - - name: ${{ env.README }} - Check for unstaged version changes (open for details) - if: env.HAS_CODE_CHANGES == 'true' - run: | - if [ "${{ steps.update-readme.outputs.has-changes }}" == "true" ]; then - echo "README.md needs version updates." - echo "NEEDS_README_COMMIT=true" >> "$GITHUB_ENV" - else - echo "README.md does not need version updates." - fi - - # ------------------------------------------- - # Fail the workflow if any updates are needed - # ------------------------------------------- - - name: Fail the workflow if there are any outstanding changes - if: env.HAS_CODE_CHANGES == 'true' && (env.NEEDS_BUILD_COMMIT == 'true' || env.NEEDS_README_COMMIT == 'true') - id: summary - uses: actions/github-script@v6 + calculate-prerelease-version: true + branch-name: 'refs/heads/my@test branch' # This should be cleaned up to 'my-test-branch' by git-version-lite + #tag-prefix: v + #fallback-to-no-prefix-search: true + #default-release-type: major + + - name: Then the outcome should be success + if: always() + run: ./gvl/test/assert-values-match.sh --name "step outcome" --expected "success" --actual "${{ steps.prerelease-version.outcome }}" + + - name: Then the outputs should contain the next patch version which includes the branch name in NEXT_VERSION + if: always() + run: | + + # In addition to the next semantic version, NEXT_VERSION will contain the branch name and a timestamp. Test + # that it contains the parts we know (version and branch name). We don't know the timestamp, so ignore that. + patch="${{ env.NEXT_PATCH_TAG_FOR_PATCH_UPDATES}}" + $branch="${{ env.TEST_BRANCH }}" + substring="$patch-$branch." + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION" --substring "v$substring" --string "${{ steps.prerelease-version.outputs.NEXT_VERSION }}" + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION_NO_PREFIX" --substring "$substring" --string "${{ steps.prerelease-version.outputs.NEXT_VERSION_NO_PREFIX }}" + + # These vars will NOT contain the branch name or timestamp so test these as exact matches. + prior="${{ env.PRIOR_TAG }}" + minor="${{ env.NEXT_MINOR_TAG_FOR_PATCH_UPDATES}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_PATCH_UPDATES}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "v$prior" --actual "${{ steps.prerelease-version.outputs.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "v$minor" --actual "${{ steps.prerelease-version.outputs.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "v$major" --actual "${{ steps.prerelease-version.outputs.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ steps.prerelease-version.outputs.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ steps.prerelease-version.outputs.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ steps.prerelease-version.outputs.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: And the environment variables should contain the next patch version which includes the branch name in NEXT_VERSION + if: always() + run: | + + # In addition to the next semantic version, NEXT_VERSION will contain the branch name and a timestamp. Test + # that it contains the parts we know (version and branch name). We don't know the timestamp, so ignore that. + patch="${{ env.NEXT_PATCH_TAG_FOR_PATCH_UPDATES}}" + $branch="${{ env.TEST_BRANCH }}" + substring="$patch-$branch." + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION" --substring "v$substring" --string "${{ env.NEXT_VERSION }}" + ./gvl/test/assert-string-contains-substring.sh --name "NEXT_VERSION_NO_PREFIX" --substring "$substring" --string "${{ env.NEXT_VERSION_NO_PREFIX }}" + + # These vars will NOT contain the branch name or timestamp so test these as exact matches. + prior="${{ env.PRIOR_TAG }}" + minor="${{ env.NEXT_MINOR_TAG_FOR_PATCH_UPDATES}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_PATCH_UPDATES}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "v$prior" --actual "${{ env.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "v$minor" --actual "${{ env.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "v$major" --actual "${{ env.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ env.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + + #--------------------------------------------- + # RELEASE WITH PATCH INCREMENT + #--------------------------------------------- + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" + + - name: When a repo has commits consistent with a patch increment + if: always() + run: ./gvl/test/setup/reset-repo-and-commit.sh --message "Making backwards compatible changes to the repo for a release" + + + - name: And a release version is requested from git-version-lite + if: always() + uses: ./gvl + id: release-version with: - script: | - - // Setup vars for the script to use - const hasBuildStep = ${{ env.HAS_BUILD_STEP }}; - const needsBuildChanges = hasBuildStep && ${{ env.NEEDS_BUILD_COMMIT }}; - const needsReadmeChanges = ${{ env.NEEDS_README_COMMIT }}; - - const contribId = '#contributing'; - const contributionLink = `https://github.com/${{ github.repository }}${contribId}`; - const contributingTitle = contribId.replace('#', '').split('-').map(w => { return w.slice(0, 1).toUpperCase() + w.slice(1) }).join(' '); - - const exampleId = '#usage-examples'; - const readmeLink = `${{ github.event.pull_request.head.repo.html_url }}/blob/${{ github.event.pull_request.head.ref }}/${{ env.README }}`; - const readmeExampleLink = `${readmeLink}${exampleId}`; - const readmeExampleTitle = exampleId.replace('#', '').split('-').map(w => { return w.slice(0, 1).toUpperCase() + w.slice(1) }).join(' '); - - // Construct the instructions for fixing the PR - let instructions = `Before this PR can be merged, the following item(s) should be addressed to comply with the action's ${contributingTitle} Guidelines.` - if (needsReadmeChanges) { - instructions += ` - - Please update the action's version in the ${readmeExampleTitle} section of ${{ env.README }}. Each instance of this action should be updated to: - \`uses: ${{ github.repository }}@${{ env.NEXT_VERSION }}\``; - } - if (needsBuildChanges){ - instructions += ` - - Please ensure the action has been recompiled by running the following command from the root of the repository: - \`npm run build\``; - } - - // Update the instructions with links - let instructionsWithLinks = instructions - .replace('of ${{ env.README }}.', `of [${{ env.README }}](${readmeLink}).`) - .replace(`${contributingTitle} Guidelines`, `[${contributingTitle} Guidelines](${contributionLink})`) - .replace(readmeExampleTitle, `[${readmeExampleTitle}](${readmeExampleLink})`); - - // Comment on PR for branches. A fork's GH_TOKEN only has 'read' permission on all scopes so a comment cannot be made. - if (!${{ env.IS_FORK }}) { - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: instructionsWithLinks - }) - } - - // Add workflow summary & fail the build - core.summary - .addRaw(instructionsWithLinks) - .write(); - core.setFailed(instructions); - - # test: - # runs-on: ubuntu-latest + calculate-prerelease-version: false + #branch-name: '' + #tag-prefix: v + #fallback-to-no-prefix-search: true + #default-release-type: major - # env: - # PRODUCTION_READY_TAG: 'v1.1.3' - # PRE_RELEASE_TAG: 'v1.0.2' - # DRAFT_RELEASE_TAG: 'v1.0.1' - # NONEXISTENT_RELEASE_TAG: 'v304.973.444' + - name: Then the outcome should be success + if: always() + run: ./gvl/test/assert-values-match.sh --name "step outcome" --expected "success" --actual "${{ steps.release-version.outcome }}" + + - name: And the outputs should be the next patch version + if: always() + run: | + prior="${{ env.PRIOR_TAG }}" + patch="${{ env.NEXT_PATCH_TAG_FOR_PATCH_UPDATES}}" + minor="${{ env.NEXT_MINOR_TAG_FOR_PATCH_UPDATES}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_PATCH_UPDATES}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "v$prior" --actual "${{ steps.release-version.outputs.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION" --expected "v$patch" --actual "${{ steps.release-version.outputs.NEXT_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "v$minor" --actual "${{ steps.release-version.outputs.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "v$major" --actual "${{ steps.release-version.outputs.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ steps.release-version.outputs.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX" --expected "$patch" --actual "${{ steps.release-version.outputs.NEXT_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ steps.release-version.outputs.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ steps.release-version.outputs.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: And the environment variables should be the next patch version + if: always() + run: | + prior="${{ env.PRIOR_TAG }}" + patch="${{ env.NEXT_PATCH_TAG_FOR_PATCH_UPDATES}}" + minor="${{ env.NEXT_MINOR_TAG_FOR_PATCH_UPDATES}}" + major="${{ env.NEXT_MAJOR_TAG_FOR_PATCH_UPDATES}}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION" --expected "v$prior" --actual "${{ env.PRIOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION" --expected "v$patch" --actual "${{ env.NEXT_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION" --expected "v$minor" --actual "${{ env.NEXT_MINOR_VERSION }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION" --expected "v$major" --actual "${{ env.NEXT_MAJOR_VERSION }}" + + ./gvl/test/assert-values-match.sh --name "PRIOR_VERSION_NO_PREFIX" --expected "$prior" --actual "${{ env.PRIOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_VERSION_NO_PREFIX" --expected "$patch" --actual "${{ env.NEXT_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MINOR_VERSION_NO_PREFIX" --expected "$minor" --actual "${{ env.NEXT_MINOR_VERSION_NO_PREFIX }}" + ./gvl/test/assert-values-match.sh --name "NEXT_MAJOR_VERSION_NO_PREFIX" --expected "$major" --actual "${{ env.NEXT_MAJOR_VERSION_NO_PREFIX }}" + + - name: '-------------------------------------------------------------------------------------------------------' + run: echo "" - # steps: - # - uses: actions/checkout@v3 - - # #-------------------------------------- - # # RELEASE IS PRODUCTION READY RELEASE - # #-------------------------------------- - # - name: '-------------------------------------------------------------------------------------------------------' - # run: echo "" - # - name: When validating a prod ready release (${{ env.PRODUCTION_READY_TAG}}) - # uses: ./ - # if: always() - # id: production-release - # with: - # token: ${{ secrets.GITHUB_TOKEN }} - # release-tag: ${{ env.PRODUCTION_READY_TAG }} - # fail-for-prerelease: false - # - name: Then the outcome should be success - # if: always() - # run: ./test/assert-values-match.sh --name "step outcome" --expected "success" --actual "${{ steps.production-release.outcome }}" + # TODO: Maybe some matrix jobs to exercise the patterns for major/minor increments + #test-matrix-for-major-updates + #test-matrix-for-minor-updates - # - name: And the output should be true - # if: always() - # run: ./test/assert-values-match.sh --name "PRODUCTION_READY" --expected "true" --actual "${{ steps.production-release.outputs.PRODUCTION_READY }}" \ No newline at end of file diff --git a/.github/workflows/increment-version-on-merge.yml b/.github/workflows/increment-version-on-merge.yml index 7846bbf..971e57e 100644 --- a/.github/workflows/increment-version-on-merge.yml +++ b/.github/workflows/increment-version-on-merge.yml @@ -29,6 +29,15 @@ on: # if this action should be incremented and if new tags should be pushed to the repo based # on the same criteria used in the build-and-review-pr.yml workflow. + +# ------------------------------------------------------------------------------------ +# NOTE: This repo duplicates the reusable increment workflow in im-open/.github that +# the rest of the actions use. If changes are needed in this workflow they +# should also be made in im-open/.github. This workflow is duplicated because +# it uses the local copy of itself in the workflow which allows us to test the +# increment build with git-version-lite changes before we merge those changes. +# ------------------------------------------------------------------------------------ + jobs: increment-version: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 68f505a..42fae90 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ This template can be used to calculate a release or pre-release version. - [Source Code Changes](#source-code-changes) - [Recompiling Manually](#recompiling-manually) - [Updating the README.md](#updating-the-readmemd) + - [Tests](#tests) - [Code of Conduct](#code-of-conduct) - [License](#license) @@ -137,6 +138,7 @@ When creating PRs, please review the following guidelines: - [ ] At least one of the commit messages contains the appropriate `+semver:` keywords listed under [Incrementing the Version] for major and minor increments. - [ ] The action has been recompiled. See [Recompiling Manually] for details. - [ ] The README.md has been updated with the latest version of the action. See [Updating the README.md] for details. +- [ ] Any tests in the [build-and-review-pr] workflow are passing ### Incrementing the Version @@ -171,6 +173,10 @@ npm run build If changes are made to the action's [source code], the [usage examples] section of this file should be updated with the next version of the action. Each instance of this action should be updated. This helps users know what the latest tag is without having to navigate to the Tags page of the repository. See [Incrementing the Version] for details on how to determine what the next version will be or consult the first workflow run for the PR which will also calculate the next version. +### Tests + +The build and review PR workflow includes tests which are linked to a status check. That status check needs to succeed before a PR is merged to the default branch. When a PR comes from a branch, there should not be any issues running the tests. When a PR comes from a fork, tests may not have the required permissions or access to run since the `GITHUB_TOKEN` only has `read` access set for all scopes. Also, forks cannot access other secrets in the repository. In these scenarios, a fork may need to be merged into an intermediate branch by the repository owners to ensure the tests run successfully prior to merging to the default branch. + ## Code of Conduct This project has adopted the [im-open's Code of Conduct](https://github.com/im-open/.github/blob/main/CODE_OF_CONDUCT.md). @@ -189,4 +195,3 @@ Copyright © 2023, Extend Health, LLC. Code released under the [MIT license] [increment-version-on-merge]: ./.github/workflows/increment-version-on-merge.yml [esbuild]: https://esbuild.github.io/getting-started/#bundling-for-node [git-version-lite]: https://github.com/im-open/git-version-lite -[create a ref]: https://docs.github.com/en/rest/reference/git#create-a-reference diff --git a/action.yml b/action.yml index 067e0e3..c0da68e 100644 --- a/action.yml +++ b/action.yml @@ -5,7 +5,7 @@ description: An action to calculate the next tag for the repository based on com inputs: calculate-prerelease-version: description: 'Flag indicating whether to calculate a pre-release version rather than a release version. Accepts: true|false.' - required: true + required: false default: 'false' branch-name: @@ -14,17 +14,17 @@ inputs: tag-prefix: description: 'By default the action strips the prefixes off, but any value provided here will be prepended to the next calculated version.' - required: true + required: false default: 'v' fallback-to-no-prefix-search: description: 'Flag indicating whether it should fallback to a prefix-less search if no tags are found with the current prefix. Helpful when starting to use prefixes with tags. Accepted values: true|false.' - required: true + required: false default: 'true' default-release-type: description: 'The default release type that should be used when no tags are detected. Defaults to major. Accepted values: major|minor|patch' - required: true + required: false default: 'major' outputs: diff --git a/dist/index.js b/dist/index.js index 1ddb5c3..0dd054f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5176,9 +5176,13 @@ Setting Release Type to '${releaseType}' based on empty base commit.`); The base commit was found. The prior release version is: ${priorReleaseVersion}`); releaseType = determineReleaseTypeFromGitLog(baseCommit.abbreviatedCommitHash, 'HEAD'); } + const priorSemver = new SemVer(priorReleaseVersion); + const nextSemver = new SemVer(semver.inc(priorReleaseVersion, releaseType)); return { - priorVersion: new SemVer(priorReleaseVersion), - nextVersion: new SemVer(semver.inc(priorReleaseVersion, releaseType)) + priorVersion: priorSemver.toString(), + nextPatch: nextSemver.toString(), + nextMinor: `${nextSemver.major}.${nextSemver.minor}`, + nextMajor: `${nextSemver.major}` }; } function nextPrereleaseVersion2( @@ -5193,8 +5197,8 @@ The base commit was found. The prior release version is: ${priorReleaseVersion} } catch (error) { core2.info(`An error occurred retrieving the tags for the repository: ${error.message}`); } - let currentHeadCommit = git.commitMetadata('HEAD'); - let formattedDate = dateToPreReleaseComponent(currentHeadCommit.committerDate); + const currentHeadCommit = git.commitMetadata('HEAD'); + const formattedDate = dateToPreReleaseComponent(currentHeadCommit.committerDate); let priorReleaseVersion; let releaseType; if (baseCommit === null) { @@ -5210,12 +5214,16 @@ Setting Release Type to '${releaseType}' based on empty base commit.`); The base commit was found. The prior release version is: ${priorReleaseVersion}`); releaseType = determineReleaseTypeFromGitLog(baseCommit.abbreviatedCommitHash, 'HEAD'); } - let nextReleaseVersion3 = semver.inc(priorReleaseVersion, releaseType); - let prereleaseVersion = `${nextReleaseVersion3}-${label}.${formattedDate}`; + const nextReleaseVersion3 = semver.inc(priorReleaseVersion, releaseType); + const prereleaseVersion = `${nextReleaseVersion3}-${label}.${formattedDate}`; core2.info(`Cleaned Branch Name: '${label}'`); + const priorSemver = new SemVer(priorReleaseVersion); + const nextSemver = new SemVer(prereleaseVersion); return { - priorVersion: new SemVer(priorReleaseVersion), - nextVersion: new SemVer(prereleaseVersion) + priorVersion: priorSemver.toString(), + nextPatch: nextSemver.toString(), + nextMinor: `${nextSemver.major}.${nextSemver.minor}`, + nextMajor: `${nextSemver.major}` }; } module2.exports = { @@ -5239,6 +5247,16 @@ var tagPrefix = core.getInput('tag-prefix'); if (tagPrefix.toLowerCase() == 'none') { tagPrefix = ''; } +function setTheOutputs(name, value, tagPrefix2) { + const valueWithTag = `${tagPrefix2}${value}`; + core.setOutput(name, valueWithTag); + core.exportVariable(name, valueWithTag); + core.info(`${name}: ${valueWithTag}`); + const noPrefixName = `${name}_NO_PREFIX`; + core.setOutput(noPrefixName, value); + core.exportVariable(noPrefixName, value); + core.info(`${noPrefixName}: ${value}`); +} async function run() { try { const expectedReleaseTypes = ['major', 'minor', 'patch']; @@ -5261,23 +5279,13 @@ async function run() { core.info(`Calculating a release version...`); versionToBuild = nextReleaseVersion(defaultReleaseType, tagPrefix, fallbackToNoPrefixSearch); } - const { nextVersion, priorVersion } = versionToBuild; - const outputVersionEntries = Object.entries({ - NEXT_VERSION: nextVersion.toString(), - NEXT_MINOR_VERSION: `${nextVersion.major}.${nextVersion.minor}`, - NEXT_MAJOR_VERSION: nextVersion.major, - PRIOR_VERSION: priorVersion.toString() - }); - core.info(` -Finished examining the git history. The following outputs will be set:`); - [ - ...outputVersionEntries.map(([name, value]) => [name, `${tagPrefix}${value}`]), - ...outputVersionEntries.map(([name, value]) => [`${name}_NO_PREFIX`, value]) - ].forEach(entry => { - core.setOutput(...entry); - core.exportVariable(...entry); - console.info(...entry); - }); + console.log('version to build:'); + console.log(versionToBuild); + const { nextPatch, nextMinor, nextMajor, priorVersion } = versionToBuild; + setTheOutputs('PRIOR_VERSION', priorVersion, tagPrefix); + setTheOutputs('NEXT_VERSION', nextPatch, tagPrefix); + setTheOutputs('NEXT_MINOR_VERSION', nextMinor, tagPrefix); + setTheOutputs('NEXT_MAJOR_VERSION', nextMajor, tagPrefix); } catch (error) { const versionTxt = calculatePrereleaseVersion ? 'pre-release' : 'release'; core.setFailed( diff --git a/src/main.js b/src/main.js index ccc625f..e3c7d79 100644 --- a/src/main.js +++ b/src/main.js @@ -17,6 +17,20 @@ if (tagPrefix.toLowerCase() == 'none') { tagPrefix = ''; //action.yml sets it to v by default so the user wouldn't be able to set an empty string themselves. } +function setTheOutputs(name, value, tagPrefix) { + // Set the regular version (it has a tag prefix) + const valueWithTag = `${tagPrefix}${value}`; + core.setOutput(name, valueWithTag); + core.exportVariable(name, valueWithTag); + core.info(`${name}: ${valueWithTag}`); + + // Set the version without the tag prefix + const noPrefixName = `${name}_NO_PREFIX`; + core.setOutput(noPrefixName, value); + core.exportVariable(noPrefixName, value); + core.info(`${noPrefixName}: ${value}`); +} + async function run() { try { const expectedReleaseTypes = ['major', 'minor', 'patch']; @@ -44,24 +58,14 @@ async function run() { versionToBuild = nextReleaseVersion(defaultReleaseType, tagPrefix, fallbackToNoPrefixSearch); } - const { nextVersion, priorVersion } = versionToBuild; - - const outputVersionEntries = Object.entries({ - NEXT_VERSION: nextVersion.toString(), - NEXT_MINOR_VERSION: `${nextVersion.major}.${nextVersion.minor}`, - NEXT_MAJOR_VERSION: nextVersion.major, - PRIOR_VERSION: priorVersion.toString() - }); + console.log('version to build:'); + console.log(versionToBuild); - core.info(`\nFinished examining the git history. The following outputs will be set:`); - [ - ...outputVersionEntries.map(([name, value]) => [name, `${tagPrefix}${value}`]), - ...outputVersionEntries.map(([name, value]) => [`${name}_NO_PREFIX`, value]) - ].forEach(entry => { - core.setOutput(...entry); - core.exportVariable(...entry); - console.info(...entry); - }); + const { nextPatch, nextMinor, nextMajor, priorVersion } = versionToBuild; + setTheOutputs('PRIOR_VERSION', priorVersion, tagPrefix); + setTheOutputs('NEXT_VERSION', nextPatch, tagPrefix); + setTheOutputs('NEXT_MINOR_VERSION', nextMinor, tagPrefix); + setTheOutputs('NEXT_MAJOR_VERSION', nextMajor, tagPrefix); } catch (error) { const versionTxt = calculatePrereleaseVersion ? 'pre-release' : 'release'; core.setFailed( diff --git a/src/version.js b/src/version.js index e9d1e93..57db2ed 100644 --- a/src/version.js +++ b/src/version.js @@ -121,15 +121,17 @@ function dateToPreReleaseComponent(input) { /** * @typedef {{ - * priorVersion: SemVer, - * nextVersion: SemVer + * priorVersion: string, + * nextPatch: string, + * nextMinor: string, + * nextMajor: string * }} ReleaseBucket */ /** * @param defaultReleaseType {string} The default release type to use if no tags are detected * @param tagPrefix {string} The value to pre-pend to the calculated release - * @returns {ReleaseBucket} a SemVer next and prior versions based on the Git history since the last tagged release + * @returns {ReleaseBucket} next and prior versions based on the Git history since the last tagged release */ function nextReleaseVersion(defaultReleaseType, tagPrefix, fallbackToNoPrefixSearch) { let baseCommit; @@ -155,9 +157,13 @@ function nextReleaseVersion(defaultReleaseType, tagPrefix, fallbackToNoPrefixSea releaseType = determineReleaseTypeFromGitLog(baseCommit.abbreviatedCommitHash, 'HEAD'); } + const priorSemver = new SemVer(priorReleaseVersion); + const nextSemver = new SemVer(semver.inc(priorReleaseVersion, releaseType)); return { - priorVersion: new SemVer(priorReleaseVersion), - nextVersion: new SemVer(semver.inc(priorReleaseVersion, releaseType)) + priorVersion: priorSemver.toString(), + nextPatch: nextSemver.toString(), + nextMinor: `${nextSemver.major}.${nextSemver.minor}`, + nextMajor: `${nextSemver.major}` }; } @@ -175,9 +181,9 @@ function nextPrereleaseVersion(label, defaultReleaseType, tagPrefix, fallbackToN } catch (error) { core.info(`An error occurred retrieving the tags for the repository: ${error.message}`); } - let currentHeadCommit = git.commitMetadata('HEAD'); - let formattedDate = dateToPreReleaseComponent(currentHeadCommit.committerDate); + const currentHeadCommit = git.commitMetadata('HEAD'); + const formattedDate = dateToPreReleaseComponent(currentHeadCommit.committerDate); let priorReleaseVersion; let releaseType; @@ -193,13 +199,17 @@ function nextPrereleaseVersion(label, defaultReleaseType, tagPrefix, fallbackToN core.info(`\nThe base commit was found. The prior release version is: ${priorReleaseVersion}`); releaseType = determineReleaseTypeFromGitLog(baseCommit.abbreviatedCommitHash, 'HEAD'); } - let nextReleaseVersion = semver.inc(priorReleaseVersion, releaseType); - let prereleaseVersion = `${nextReleaseVersion}-${label}.${formattedDate}`; + const nextReleaseVersion = semver.inc(priorReleaseVersion, releaseType); + const prereleaseVersion = `${nextReleaseVersion}-${label}.${formattedDate}`; core.info(`Cleaned Branch Name: '${label}'`); + const priorSemver = new SemVer(priorReleaseVersion); + const nextSemver = new SemVer(prereleaseVersion); return { - priorVersion: new SemVer(priorReleaseVersion), - nextVersion: new SemVer(prereleaseVersion) + priorVersion: priorSemver.toString(), + nextPatch: nextSemver.toString(), + nextMinor: `${nextSemver.major}.${nextSemver.minor}`, + nextMajor: `${nextSemver.major}` }; } diff --git a/test/assert-string-contains-substring.sh b/test/assert-string-contains-substring.sh new file mode 100755 index 0000000..1e239c0 --- /dev/null +++ b/test/assert-string-contains-substring.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +name='' +string='' +substring='' + + +for arg in "$@"; do + case $arg in + --name) + name=$2 + shift # Remove argument --name from `$@` + shift # Remove argument value from `$@` + ;; + --string) + string=$2 + shift # Remove argument --expected from `$@` + shift # Remove argument value from `$@` + ;; + --substring) + substring=$2 + shift # Remove argument --actual from `$@` + shift # Remove argument value from `$@` + ;; + + esac +done + +echo " +Full value of $name: '$string' +Substring for $name: '$substring'" + +if [[ $string == *"$substring"* ]]; then + echo "The substring was found in $name" +else + echo "The substring was not found in $name" + exit 1 +fi \ No newline at end of file diff --git a/test/setup/reset-env-vars.sh b/test/setup/reset-env-vars.sh new file mode 100755 index 0000000..c9e8e89 --- /dev/null +++ b/test/setup/reset-env-vars.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "PRIOR_VERSION=" >> $GITHUB_ENV +echo "NEXT_VERSION=" >> $GITHUB_ENV +echo "NEXT_MINOR_VERSION=" >> $GITHUB_ENV +echo "NEXT_MAJOR_VERSION=" >> $GITHUB_ENV +echo "PRIOR_VERSION_NO_PREFIX=" >> $GITHUB_ENV +echo "NEXT_VERSION_NO_PREFIX=" >> $GITHUB_ENV +echo "NEXT_MINOR_VERSION_NO_PREFIX=" >> $GITHUB_ENV +echo "NEXT_MAJOR_VERSION_NO_PREFIX=" >> $GITHUB_ENV \ No newline at end of file diff --git a/test/setup/reset-repo-and-commit.sh b/test/setup/reset-repo-and-commit.sh new file mode 100755 index 0000000..586b172 --- /dev/null +++ b/test/setup/reset-repo-and-commit.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +commitMessage='' + +for arg in "$@"; do + case $arg in + --message) + commitMessage=$2 + shift # Remove argument --name from `$@` + shift # Remove argument value from `$@` + ;; + esac +done + +echo "Remove the last commit message" +git reset --soft HEAD~1 + +echo " +Make a message only commit:"; +git commit --allow-empty -m "$commitMessage" + +echo " +Print the last five commits:"; +git log -5 --oneline + +echo " +Reset the environment variables that git-version-lite will set"; +echo "PRIOR_VERSION=" >> $GITHUB_ENV +echo "NEXT_VERSION=" >> $GITHUB_ENV +echo "NEXT_MINOR_VERSION=" >> $GITHUB_ENV +echo "NEXT_MAJOR_VERSION=" >> $GITHUB_ENV +echo "PRIOR_VERSION_NO_PREFIX=" >> $GITHUB_ENV +echo "NEXT_VERSION_NO_PREFIX=" >> $GITHUB_ENV +echo "NEXT_MINOR_VERSION_NO_PREFIX=" >> $GITHUB_ENV +echo "NEXT_MAJOR_VERSION_NO_PREFIX=" >> $GITHUB_ENV \ No newline at end of file