diff --git a/.github/README.md b/.github/README.md index 94dd3fe..14c7dcc 100644 --- a/.github/README.md +++ b/.github/README.md @@ -25,6 +25,7 @@ - [Migrating from v2 to v3](#migrating-from-v2-to-v3) - [Contributing](#contributing) - [Versioning](#versioning) +- [Releasing](#releasing) - [License](#license) ## Install @@ -271,6 +272,10 @@ Please read [CONTRIBUTING.md][CONTRIBUTING] for details on our code of conduct, We use [SemVer] for versioning. For the versions available, see the tags on this repository. +## Releasing + +Releases are cut by CI from the version in `src/IPData/IPData.csproj`. To ship a new version, bump `VersionPrefix` in a pull request and merge it: the CI run on `master` tags the merge commit `vX.Y.Z`, creates the GitHub release with generated notes and publishes the package to [NuGet](https://www.nuget.org/packages/IPData/). A merge whose version is already tagged publishes nothing. + ## License This project is licensed under the MIT License - see the [LICENSE.md][LICENSE] file for details diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df80885..3448f5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,3 +29,59 @@ jobs: - name: Pack run: dotnet pack src/IPData/IPData.csproj --configuration Release --no-build + + release: + needs: build-and-test + if: github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + runs-on: ubuntu-latest + permissions: + contents: write + concurrency: + group: release + cancel-in-progress: false + outputs: + tag: ${{ steps.release.outputs.tag }} + created: ${{ steps.release.outputs.created }} + + steps: + - uses: actions/checkout@v4 + + - name: Tag and create the release when the version is new + id: release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + VERSION=$(sed -nE 's/.*([^<]+)<.*/\1/p' src/IPData/IPData.csproj | head -1) + if [ -z "$VERSION" ]; then + echo "::error::VersionPrefix not found in src/IPData/IPData.csproj" + exit 1 + fi + TAG="v$VERSION" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + status=0 + git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null || status=$? + case "$status" in + 0) + echo "Tag $TAG already exists; nothing to release." + echo "created=false" >> "$GITHUB_OUTPUT" + ;; + 2) + gh release create "$TAG" --target "$GITHUB_SHA" --title "$TAG" --generate-notes + echo "Created tag and release $TAG." + echo "created=true" >> "$GITHUB_OUTPUT" + ;; + *) + echo "::error::Could not check whether tag $TAG exists (git ls-remote exit status $status)." + exit "$status" + ;; + esac + + publish: + needs: release + if: needs.release.outputs.created == 'true' + uses: ./.github/workflows/publish.yml + with: + tag: ${{ needs.release.outputs.tag }} + secrets: inherit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d05b076..65058c3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,6 +3,12 @@ name: Publish to NuGet on: release: types: [published] + workflow_call: + inputs: + tag: + description: Release tag to publish, for example v3.1.0 + required: true + type: string jobs: publish: @@ -21,7 +27,9 @@ jobs: - name: Get version from tag id: version - run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + env: + TAG: ${{ inputs.tag || github.ref_name }} + run: echo "VERSION=${TAG#v}" >> "$GITHUB_OUTPUT" - name: Build run: dotnet build --configuration Release --no-restore /p:Version=${{ steps.version.outputs.VERSION }}