From 2debb1765fcdde8be3d15f91fb1666df3757be4a Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Wed, 17 Jul 2024 13:40:33 +0700 Subject: [PATCH 01/18] Use Git tags for version numbers in CI --- .github/workflows/nuget-ci-cd.yml | 139 +++++++++++++++++++ src/Directory.Build.props | 9 ++ src/SIL.Harmony.Core/SIL.Harmony.Core.csproj | 1 + src/SIL.Harmony/SIL.Harmony.csproj | 1 + 4 files changed, 150 insertions(+) create mode 100644 .github/workflows/nuget-ci-cd.yml diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml new file mode 100644 index 0000000..b66584d --- /dev/null +++ b/.github/workflows/nuget-ci-cd.yml @@ -0,0 +1,139 @@ +name: Build NuGet packages +on: + push: + branches: + - main + pull_request: + branches: + - main + +on: + push: + # Note: main is NOT included here + branches: [ develop ] + tags: + - v* + pull_request: + branches: [ develop ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # We use `git describe` to find tags in commit history, so we need complete repo history + fetch-depth: 0 + + - name: Find most recent tag + shell: bash + run: | + DESCRIBE=$(git describe --long --match "v*") + TAG=$(echo "$DESCRIBE" | grep -E -o '^v[0-9]+\.[0-9]+\.[0-9]+') + echo "DESCRIBE=$DESCRIBE" >> "$GITHUB_ENV" + echo "TAG=$TAG" >> "$GITHUB_ENV" + + - name: Split version number from tag into major/minor/patch sections + shell: bash + run: | + MAJOR=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/') + MINOR=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/') + PATCH=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\3/') + echo "MAJOR=$MAJOR" >> "$GITHUB_ENV" + echo "MINOR=$MINOR" >> "$GITHUB_ENV" + echo "PATCH=$PATCH" >> "$GITHUB_ENV" + + - name: Get GitHub build number + shell: bash + run: echo "BUILD_NUMBER=${{ github.run_number }}" >> "$GITHUB_ENV" + + - name: Get PR number for prerelease suffix + if: github.event_name == 'pull_request' + shell: bash + run: echo "PR_NUMBER=${{ github.event.number }}" >> "$GITHUB_ENV" + + - name: Calculate prerelease suffix + shell: bash + run: | + SUFFIX="" + if [ -n "$PR_NUMBER" ]; then + SUFFIX="-PR${PR_NUMBER}.${BUILD_NUMBER}" + elif [ "$GITHUB_REF" = "refs/heads/develop" ]; then + SUFFIX="-beta.${BUILD_NUMBER}" + elif [ "$GITHUB_REF" = "refs/heads/main" ]; then + SUFFIX="-rc.${BUILD_NUMBER}" + fi + + echo "SUFFIX=$SUFFIX" >> "$GITHUB_ENV" + + - name: Calculate version number bump + # Same logic as GitVersion: + # * "+semver: breaking" or "+semver: major" in commit log will produce major version bump (and reset minor and patch to 0) + # * "+semver: feature" or "+semver: minor" in commit log will produce minor version bump (and reset patch to 0) + # Default is to bump the patch version + # Git log format "%B" is the raw body with no author's email or anything else + shell: bash + run: | + COMMIT_COUNT=$(echo "$DESCRIBE" | sed -E 's/^[^-]+-([^-]+)-.*$/\1/') + if [ -n "$COMMIT_COUNT" -a "$COMMIT_COUNT" -gt 0 ]; then + # Calculate bump based on commit messages + RAW_LOG=$(git log --format="%B" "$TAG"..HEAD) + if grep -E '\+semver: (breaking|major)' <<< "$RAW_LOG"; then + MAJOR=$(($MAJOR + 1)) + MINOR=0 + PATCH=0 + elif grep -E '\+semver: (feature|minor)' <<< "$RAW_LOG"; then + MINOR=$(($MINOR + 1)) + PATCH=0 + else + PATCH=$(($PATCH + 1)) + fi + fi + + echo "MAJOR=$MAJOR" >> "$GITHUB_ENV" + echo "MINOR=$MINOR" >> "$GITHUB_ENV" + echo "PATCH=$PATCH" >> "$GITHUB_ENV" + + - name: Set version number variables for MSBuild + shell: bash + run: | + echo "PACKAGE_VERSION=${MAJOR}.${MINOR}.${PATCH}${SUFFIX}" >> "$GITHUB_ENV" + if [ $MAJOR -eq 0 ]; then + echo "ASSEMBLY_VERSION=0.${MINOR}.0.0" >> "$GITHUB_ENV" + else + echo "ASSEMBLY_VERSION=${MAJOR}.0.0.0" >> "$GITHUB_ENV" + fi + echo "FILE_VERSION=${MAJOR}.${MINOR}.${PATCH}.${BUILD_NUMBER}" >> "$GITHUB_ENV" + + - name: Display version number for debugging + shell: bash + run: echo "Calculated version number is $PACKAGE_VERSION" + + - name: Install .NET + uses: actions/setup-dotnet@v4 + + - name: Build & test + run: dotnet test --configuration Release --logger GitHubActions + + - name: Pack + shell: bash + run: | + dotnet pack --include-symbols /p:PackageVersion="$PACKAGE_VERSION" /p:AssemblyVersion="$ASSEMBLY_VERSION" /p:FileVersion="$FILE_VERSION" + + - name: Upload packages to build artifacts + uses: actions/upload-artifact@v4 + with: + name: nuget-packages + path: src/artifacts/package/release/*nupkg + + - name: Publish package to NuGet.org + if: ${{ github.event_name == 'push' && github.ref.startswith('refs/tags') }} + shell: bash + run: | + echo Would run the following: + echo dotnet nuget push "src/artifacts/package/release/*nupkg" --skip-duplicate --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json + env: + NUGET_API_KEY: fake-key-for-testing-purposes + # NUGET_API_KEY: ${{ secrets.SILLSDEV_PUBLISH_NUGET_ORG }} diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 36c1bcc..4a44902 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -6,6 +6,15 @@ enable Nullable true + false + + + + Debug + $(ASSEMBLY_VERSION) + 0.1.0.0 + $(FILE_VERSION) + 0.1.0.0 diff --git a/src/SIL.Harmony.Core/SIL.Harmony.Core.csproj b/src/SIL.Harmony.Core/SIL.Harmony.Core.csproj index d0ac008..acdee42 100644 --- a/src/SIL.Harmony.Core/SIL.Harmony.Core.csproj +++ b/src/SIL.Harmony.Core/SIL.Harmony.Core.csproj @@ -2,6 +2,7 @@ SIL.Harmony.Core + true diff --git a/src/SIL.Harmony/SIL.Harmony.csproj b/src/SIL.Harmony/SIL.Harmony.csproj index 2844de2..6bb7e63 100644 --- a/src/SIL.Harmony/SIL.Harmony.csproj +++ b/src/SIL.Harmony/SIL.Harmony.csproj @@ -2,6 +2,7 @@ SIL.Harmony + true From 5c0ed814827219fb86bb37c5c9a72f3d4112b61c Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Wed, 17 Jul 2024 14:44:50 +0700 Subject: [PATCH 02/18] Fix workflow triggers --- .github/workflows/nuget-ci-cd.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index b66584d..0eecb46 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -1,11 +1,4 @@ name: Build NuGet packages -on: - push: - branches: - - main - pull_request: - branches: - - main on: push: From 8c81c575f15c10851e1a481fc1ebdd18c5a49165 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Wed, 17 Jul 2024 14:49:53 +0700 Subject: [PATCH 03/18] Also run workflow on pushes & PRs to main --- .github/workflows/nuget-ci-cd.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 0eecb46..64ea91e 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -2,12 +2,11 @@ name: Build NuGet packages on: push: - # Note: main is NOT included here - branches: [ develop ] + branches: [ develop, main ] tags: - v* pull_request: - branches: [ develop ] + branches: [ develop, main ] jobs: build: From afce442d86877c41727123bf7c18438a012a8996 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Wed, 17 Jul 2024 14:52:23 +0700 Subject: [PATCH 04/18] Fix conditional expression in workflow YAML Also omit the `${{ }}` since it's only *required* when the expression starts with `!` (which in YAML would mean "non-specific tag"). --- .github/workflows/nuget-ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 64ea91e..24c831d 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -121,7 +121,7 @@ jobs: path: src/artifacts/package/release/*nupkg - name: Publish package to NuGet.org - if: ${{ github.event_name == 'push' && github.ref.startswith('refs/tags') }} + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') shell: bash run: | echo Would run the following: From 443810ca5c223d33ed5860b0e114be34a7359290 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Wed, 17 Jul 2024 14:59:39 +0700 Subject: [PATCH 05/18] Add step to publish PR packages to GitHub registry --- .github/workflows/nuget-ci-cd.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 24c831d..1a0e941 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -120,6 +120,13 @@ jobs: name: nuget-packages path: src/artifacts/package/release/*nupkg + - name: Publish package to GitHub + if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) + shell: bash + run: | + echo Would run the following: + echo dotnet nuget push artifacts/*.nupkg -s https://nuget.pkg.github.com/sillsdev/index.json -k 'GITHUB_TOKEN goes here' --skip-duplicate + - name: Publish package to NuGet.org if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') shell: bash From 487725698802ffd5faf73bd41a765a4336ff6825 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 11:53:27 +0700 Subject: [PATCH 06/18] Actually publish to GitHub NuGet registry --- .github/workflows/nuget-ci-cd.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 1a0e941..08ed4c2 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -124,8 +124,9 @@ jobs: if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) shell: bash run: | - echo Would run the following: - echo dotnet nuget push artifacts/*.nupkg -s https://nuget.pkg.github.com/sillsdev/index.json -k 'GITHUB_TOKEN goes here' --skip-duplicate + dotnet nuget push artifacts/*.nupkg -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate + env: + NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} - name: Publish package to NuGet.org if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') From aa303bc4ea578139727d4e8e7b05dced8bf56766 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 11:55:17 +0700 Subject: [PATCH 07/18] Fix path, put quotes around glob I believe the `dotnet nuget push` command wants to interpret the `*.nuget` glob itself, rather than have the shell expand the glob. --- .github/workflows/nuget-ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 08ed4c2..8286099 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -124,7 +124,7 @@ jobs: if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) shell: bash run: | - dotnet nuget push artifacts/*.nupkg -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate + dotnet nuget push "src/artifacts/package/release/*.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate env: NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} From 945128c722eef40b99cb758fb1510a1ffaa98323 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 12:02:22 +0700 Subject: [PATCH 08/18] Put GITHUB_TOKEN directly on the command line --- .github/workflows/nuget-ci-cd.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 8286099..1ecf38a 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -124,9 +124,7 @@ jobs: if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) shell: bash run: | - dotnet nuget push "src/artifacts/package/release/*.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate - env: - NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} + dotnet nuget push "src/artifacts/package/release/*.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "${{ secrets.GITHUB_TOKEN }}" --skip-duplicate - name: Publish package to NuGet.org if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') From 3e1213777fbaf93f74fd1985c2233b4058bd89dd Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 13:16:49 +0700 Subject: [PATCH 09/18] Only push symbol packages for prereleases Symbol packages include the .dll alongside the .pdb, so if you don't need the .pdb then the only downside is a larger-than-needed download. But since prerelease packages are what you're most likely to be trying to debug, it's helpful to have the .pdb included. This also avoids the "Package 'Foo.0.2.1.symbols.nupkg' already exists" error when the "Foo.0.2.1.nupkg" package was uploaded earlier in the process. --- .github/workflows/nuget-ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 1ecf38a..25b93eb 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -124,7 +124,7 @@ jobs: if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) shell: bash run: | - dotnet nuget push "src/artifacts/package/release/*.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "${{ secrets.GITHUB_TOKEN }}" --skip-duplicate + dotnet nuget push "src/artifacts/package/release/*.symbols.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "${{ secrets.GITHUB_TOKEN }}" --skip-duplicate - name: Publish package to NuGet.org if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') From 45e9f9addd36ac048ea8c2581c702eeb517f915f Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 13:28:04 +0700 Subject: [PATCH 10/18] Try passing API key via environment Just to check that it still works --- .github/workflows/nuget-ci-cd.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 25b93eb..81988e7 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -124,7 +124,9 @@ jobs: if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) shell: bash run: | - dotnet nuget push "src/artifacts/package/release/*.symbols.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "${{ secrets.GITHUB_TOKEN }}" --skip-duplicate + dotnet nuget push "src/artifacts/package/release/*.symbols.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate + env: + NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} - name: Publish package to NuGet.org if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') From f00c39178c9dd014b12a590109639c9a17db15e1 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 13:33:55 +0700 Subject: [PATCH 11/18] Explicitly request package write permission in workflow Now that package write permission has been removed from the default permissions for the repo, this is now required for packages to upload. --- .github/workflows/nuget-ci-cd.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 81988e7..45cca88 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -10,6 +10,8 @@ on: jobs: build: + permissions: + packages: write runs-on: ubuntu-latest steps: From f378c99af5aa3654da322c6aeb36da9e188c2530 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 18 Jul 2024 13:37:05 +0700 Subject: [PATCH 12/18] Also publish the SIL.Harmony.Linq2db package --- src/SIL.Harmony.Linq2db/SIL.Harmony.Linq2db.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SIL.Harmony.Linq2db/SIL.Harmony.Linq2db.csproj b/src/SIL.Harmony.Linq2db/SIL.Harmony.Linq2db.csproj index 7338ee0..753f30e 100644 --- a/src/SIL.Harmony.Linq2db/SIL.Harmony.Linq2db.csproj +++ b/src/SIL.Harmony.Linq2db/SIL.Harmony.Linq2db.csproj @@ -2,6 +2,7 @@ SIL.Harmony.Linq2db + true From 8438cd6fe11462d383493e512c4fae66a4fd6995 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Thu, 15 Aug 2024 13:58:37 +0700 Subject: [PATCH 13/18] Don't push packages to GitHub package registry Too much hassle to consume since it doesn't allow anonymous access, so anyone wanting to consume these packages, even if the packages are public, would have to set up NuGet auth and a GitHub API key. --- .github/workflows/nuget-ci-cd.yml | 35 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 45cca88..2231fe3 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -122,20 +122,21 @@ jobs: name: nuget-packages path: src/artifacts/package/release/*nupkg - - name: Publish package to GitHub - if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) - shell: bash - run: | - dotnet nuget push "src/artifacts/package/release/*.symbols.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate - env: - NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish package to NuGet.org - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - shell: bash - run: | - echo Would run the following: - echo dotnet nuget push "src/artifacts/package/release/*nupkg" --skip-duplicate --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json - env: - NUGET_API_KEY: fake-key-for-testing-purposes - # NUGET_API_KEY: ${{ secrets.SILLSDEV_PUBLISH_NUGET_ORG }} + # Not using the GitHub package registry right now, since it doesn't allow anonymous access so it's a hassle to use + + # - name: Publish package to GitHub + # if: github.event_name == 'pull_request' || (github.event_name == 'push' && startsWith(github.ref, 'refs/heads')) + # shell: bash + # run: | + # dotnet nuget push "src/artifacts/package/release/*.symbols.nupkg" -s https://nuget.pkg.github.com/sillsdev/index.json -k "$NUGET_API_KEY" --skip-duplicate + # env: + # NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} + + # - name: Publish package to NuGet.org + # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + # shell: bash + # run: | + # echo Would run the following: + # echo dotnet nuget push "src/artifacts/package/release/*nupkg" --skip-duplicate --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json + # env: + # NUGET_API_KEY: ${{ secrets.SILLSDEV_PUBLISH_NUGET_ORG }} From 191f642655ba4b3abb58ef93c333d30d14c6e51d Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Fri, 27 Sep 2024 13:03:36 +0700 Subject: [PATCH 14/18] Move version number calculations to Bash script --- .github/workflows/nuget-ci-cd.yml | 80 ++----------------------------- src/calculate-version.sh | 79 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 75 deletions(-) create mode 100755 src/calculate-version.sh diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 2231fe3..7cbab96 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -21,85 +21,15 @@ jobs: # We use `git describe` to find tags in commit history, so we need complete repo history fetch-depth: 0 - - name: Find most recent tag - shell: bash - run: | - DESCRIBE=$(git describe --long --match "v*") - TAG=$(echo "$DESCRIBE" | grep -E -o '^v[0-9]+\.[0-9]+\.[0-9]+') - echo "DESCRIBE=$DESCRIBE" >> "$GITHUB_ENV" - echo "TAG=$TAG" >> "$GITHUB_ENV" - - - name: Split version number from tag into major/minor/patch sections - shell: bash - run: | - MAJOR=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/') - MINOR=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/') - PATCH=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\3/') - echo "MAJOR=$MAJOR" >> "$GITHUB_ENV" - echo "MINOR=$MINOR" >> "$GITHUB_ENV" - echo "PATCH=$PATCH" >> "$GITHUB_ENV" - - - name: Get GitHub build number - shell: bash - run: echo "BUILD_NUMBER=${{ github.run_number }}" >> "$GITHUB_ENV" - - - name: Get PR number for prerelease suffix + - name: Calculate version number for PR build if: github.event_name == 'pull_request' shell: bash - run: echo "PR_NUMBER=${{ github.event.number }}" >> "$GITHUB_ENV" + run: src/calculate-version.sh "${{ github.run_number }}" "${{ github.event.number }}" - - name: Calculate prerelease suffix + - name: Calculate version number for non-PR build + if: github.event_name != 'pull_request' shell: bash - run: | - SUFFIX="" - if [ -n "$PR_NUMBER" ]; then - SUFFIX="-PR${PR_NUMBER}.${BUILD_NUMBER}" - elif [ "$GITHUB_REF" = "refs/heads/develop" ]; then - SUFFIX="-beta.${BUILD_NUMBER}" - elif [ "$GITHUB_REF" = "refs/heads/main" ]; then - SUFFIX="-rc.${BUILD_NUMBER}" - fi - - echo "SUFFIX=$SUFFIX" >> "$GITHUB_ENV" - - - name: Calculate version number bump - # Same logic as GitVersion: - # * "+semver: breaking" or "+semver: major" in commit log will produce major version bump (and reset minor and patch to 0) - # * "+semver: feature" or "+semver: minor" in commit log will produce minor version bump (and reset patch to 0) - # Default is to bump the patch version - # Git log format "%B" is the raw body with no author's email or anything else - shell: bash - run: | - COMMIT_COUNT=$(echo "$DESCRIBE" | sed -E 's/^[^-]+-([^-]+)-.*$/\1/') - if [ -n "$COMMIT_COUNT" -a "$COMMIT_COUNT" -gt 0 ]; then - # Calculate bump based on commit messages - RAW_LOG=$(git log --format="%B" "$TAG"..HEAD) - if grep -E '\+semver: (breaking|major)' <<< "$RAW_LOG"; then - MAJOR=$(($MAJOR + 1)) - MINOR=0 - PATCH=0 - elif grep -E '\+semver: (feature|minor)' <<< "$RAW_LOG"; then - MINOR=$(($MINOR + 1)) - PATCH=0 - else - PATCH=$(($PATCH + 1)) - fi - fi - - echo "MAJOR=$MAJOR" >> "$GITHUB_ENV" - echo "MINOR=$MINOR" >> "$GITHUB_ENV" - echo "PATCH=$PATCH" >> "$GITHUB_ENV" - - - name: Set version number variables for MSBuild - shell: bash - run: | - echo "PACKAGE_VERSION=${MAJOR}.${MINOR}.${PATCH}${SUFFIX}" >> "$GITHUB_ENV" - if [ $MAJOR -eq 0 ]; then - echo "ASSEMBLY_VERSION=0.${MINOR}.0.0" >> "$GITHUB_ENV" - else - echo "ASSEMBLY_VERSION=${MAJOR}.0.0.0" >> "$GITHUB_ENV" - fi - echo "FILE_VERSION=${MAJOR}.${MINOR}.${PATCH}.${BUILD_NUMBER}" >> "$GITHUB_ENV" + run: src/calculate-version.sh "${{ github.run_number }}" - name: Display version number for debugging shell: bash diff --git a/src/calculate-version.sh b/src/calculate-version.sh new file mode 100755 index 0000000..3241148 --- /dev/null +++ b/src/calculate-version.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Pass build number and PR number as command-line params, e.g.: +# calculate-version.sh "${{ github.run_number }}" "${{ github.event.number }}" +# If present, command-line params override env vars + +BUILD_NUMBER=$1 +PR_NUMBER=$2 + +if [ -z "BUILD_NUMBER" ]; then + echo "Required: pass a build number as first parameter" + echo "Optional: pass a PR number as second parameter" + exit 2 +fi + +# If running in CI, current commit is in GITHUB_REF env var +# If not running in CI, use git rev-parse to calculate it +GITHUB_REF=${GITHUB_REF:-$(git rev-parse --symbolic-full-name HEAD)} + +# Find most recent tag +DESCRIBE=$(git describe --long --match "v*") +TAG=$(echo "$DESCRIBE" | grep -E -o '^v[0-9]+\.[0-9]+\.[0-9]+') + +# Split version number from tag into major/minor/patch sections +MAJOR=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/') +MINOR=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/') +PATCH=$(echo "$TAG" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\3/') + +# Calculate prerelease suffix, if any + SUFFIX="" +if [ -n "$PR_NUMBER" ]; then + SUFFIX="-PR${PR_NUMBER}.${BUILD_NUMBER}" +elif [ "$GITHUB_REF" = "refs/heads/develop" ]; then + SUFFIX="-beta.${BUILD_NUMBER}" +elif [ "$GITHUB_REF" = "refs/heads/main" ]; then + SUFFIX="-rc.${BUILD_NUMBER}" +fi + +# Calculate version number bump +# Same logic as GitVersion: +# * "+semver: breaking" or "+semver: major" in commit log will produce major version bump (and reset minor and patch to 0) +# * "+semver: feature" or "+semver: minor" in commit log will produce minor version bump (and reset patch to 0) +# Default is to bump the patch version +# Git log format "%B" is the raw body with no author's email or anything else +COMMIT_COUNT=$(echo "$DESCRIBE" | sed -E 's/^[^-]+-([^-]+)-.*$/\1/') +if [ -n "$COMMIT_COUNT" -a "$COMMIT_COUNT" -gt 0 ]; then + # Calculate bump based on commit messages + RAW_LOG=$(git log --format="%B" "$TAG"..HEAD) + if grep -E '\+semver: (breaking|major)' <<< "$RAW_LOG"; then + MAJOR=$(($MAJOR + 1)) + MINOR=0 + PATCH=0 + elif grep -E '\+semver: (feature|minor)' <<< "$RAW_LOG"; then + MINOR=$(($MINOR + 1)) + PATCH=0 + else + PATCH=$(($PATCH + 1)) + fi +fi + +# Set version number variables for MSBuild +export PACKAGE_VERSION=${MAJOR}.${MINOR}.${PATCH}${SUFFIX} +if [ $MAJOR -eq 0 ]; then + export ASSEMBLY_VERSION=0.${MINOR}.0.0 +else + export ASSEMBLY_VERSION=${MAJOR}.0.0.0 +fi +export FILE_VERSION=${MAJOR}.${MINOR}.${PATCH}.${BUILD_NUMBER} + +# Put output variables into GITHUB_ENV if it exists +if [ -n "$GITHUB_ENV" ]; then + echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> "$GITHUB_ENV" + echo "ASSEMBLY_VERSION=${ASSEMBLY_VERSION}" >> "$GITHUB_ENV" + echo "FILE_VERSION=${FILE_VERSION}" >> "$GITHUB_ENV" +else + echo "PACKAGE_VERSION=${PACKAGE_VERSION}" + echo "ASSEMBLY_VERSION=${ASSEMBLY_VERSION}" + echo "FILE_VERSION=${FILE_VERSION}" +fi From c92433285a9d3e4ebd3653be98022959286465f2 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Mon, 7 Oct 2024 13:30:33 +0700 Subject: [PATCH 15/18] force LF line endings in shell scripts --- .gitattributes | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 0c0b43f..75c9d16 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ *.verified.txt text eol=lf working-tree-encoding=UTF-8 *.verified.xml text eol=lf working-tree-encoding=UTF-8 -*.verified.json text eol=lf working-tree-encoding=UTF-8 \ No newline at end of file +*.verified.json text eol=lf working-tree-encoding=UTF-8 + +# force LF line endings on all shell scripts, even on Windows +*.sh text eol=lf \ No newline at end of file From d117649960710d1ebb84c49b26c1ee3420a4a0ed Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Tue, 15 Oct 2024 09:56:25 +0700 Subject: [PATCH 16/18] Always echo package version numbers to console Whether running in GHA or by hand, the calculated version numbers should always be echoed to the console. Then we can remove the GHA workflow step that was doing the same thing. --- .github/workflows/nuget-ci-cd.yml | 4 ---- src/calculate-version.sh | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nuget-ci-cd.yml b/.github/workflows/nuget-ci-cd.yml index 7cbab96..eee0605 100644 --- a/.github/workflows/nuget-ci-cd.yml +++ b/.github/workflows/nuget-ci-cd.yml @@ -31,10 +31,6 @@ jobs: shell: bash run: src/calculate-version.sh "${{ github.run_number }}" - - name: Display version number for debugging - shell: bash - run: echo "Calculated version number is $PACKAGE_VERSION" - - name: Install .NET uses: actions/setup-dotnet@v4 diff --git a/src/calculate-version.sh b/src/calculate-version.sh index 3241148..dedf917 100755 --- a/src/calculate-version.sh +++ b/src/calculate-version.sh @@ -72,8 +72,9 @@ if [ -n "$GITHUB_ENV" ]; then echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> "$GITHUB_ENV" echo "ASSEMBLY_VERSION=${ASSEMBLY_VERSION}" >> "$GITHUB_ENV" echo "FILE_VERSION=${FILE_VERSION}" >> "$GITHUB_ENV" -else - echo "PACKAGE_VERSION=${PACKAGE_VERSION}" - echo "ASSEMBLY_VERSION=${ASSEMBLY_VERSION}" - echo "FILE_VERSION=${FILE_VERSION}" fi + +# And show them on the console regardless +echo "PACKAGE_VERSION=${PACKAGE_VERSION}" +echo "ASSEMBLY_VERSION=${ASSEMBLY_VERSION}" +echo "FILE_VERSION=${FILE_VERSION}" From 1d00e11e8482e7921e998f1a2584b584142d2470 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Tue, 15 Oct 2024 10:05:18 +0700 Subject: [PATCH 17/18] Document how to write commit messages for semver bumps --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 2b0590d..f1c7a6e 100644 --- a/README.md +++ b/README.md @@ -175,3 +175,14 @@ ISyncable remoteModel; await dataModel.SyncWith(remoteModel); ``` It's that easy. All the heavy lifting is done by the interface which is fairly simple to implement. + +## Development + +### SemVer commit messages + +NuGet package versions are calculated from a combination of tags and commit messages. First, the most recent Git tag matching the pattern `v\d+.\d+.\d+` is located. If that is the commit being built, then that version number is used. If there have been any commits since then, the version number will be bumped by looking for one of the following patterns in the commit messages: + +* `+semver: major` or `+semver: breaking` - update major version number, reset others to 0 (so 2.3.1 would become 3.0.0) +* `+semver: minor` or `+semver: feature` - update minor version number, reset patch to 0 (so 2.3.1 would become 2.4.0) +* Anything else, including no `+semver` lines at all - update patch version number (so 2.3.1 would become 2.3.2) + * If you want to include `+semver` lines, then `+semver: patch` or `+semver: fix` are the standard ways to increment a patch version bump, but the patch version will be bumped regardless as long as there is at least one commit since the most recent tag. From 517169c83fbb953051cb385e3a7e2474fabbb572 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Tue, 15 Oct 2024 10:30:04 +0700 Subject: [PATCH 18/18] Fix no-parameters check --- src/calculate-version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calculate-version.sh b/src/calculate-version.sh index dedf917..f57334e 100755 --- a/src/calculate-version.sh +++ b/src/calculate-version.sh @@ -7,7 +7,7 @@ BUILD_NUMBER=$1 PR_NUMBER=$2 -if [ -z "BUILD_NUMBER" ]; then +if [ -z "$BUILD_NUMBER" ]; then echo "Required: pass a build number as first parameter" echo "Optional: pass a PR number as second parameter" exit 2