From 03f9d1ec2643a47c108a1591861929ffb4ad19ea Mon Sep 17 00:00:00 2001 From: Tomas Rutkauskas Date: Fri, 16 Oct 2026 16:00:12 +0300 Subject: [PATCH] Trunk-based pipeline: release on a GitHub Release; fix GHSA-7pxr-59rr-hqj2 - build.yml: every pull request and push to main restores, builds the library and all examples, and packs a 0.0.0-ci.* artifact. Nothing is published; the job name `build` is the required check. - publish.yml replaces release.yml: runs on a published GitHub Release, only for tomasr78, checks the SemVer tag, the pre-release tick, that a stable version is on main and has never been on nuget.org, packs with -p:Version from the tag and pushes without --skip-duplicate. Ready for nuget.org Trusted Publishing via the NUGET_USER variable. - Actions pinned to commit SHAs, SDK pinned to the 10.0.4xx band. - Version is a 0.0.0-local placeholder in the csproj; high and critical NuGet advisories (direct or transitive) now fail the build. - Microsoft.OpenApi.Readers 1.6.23 -> 1.6.31 (patched in 1.6.30). - Package metadata: repository URL, description, release notes, snupkg. - Remove the unused Dockerfile publish path (.NET Core 3.1 SDK image). Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 78 +++++++++++++ .github/workflows/publish.yml | 209 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 24 ---- CHANGELOG.md | 14 +++ ConvertApi/ConvertApi.csproj | 27 +++-- ConvertApi/docs/README.md | 4 +- Dockerfile | 7 -- README.md | 4 +- RELEASING.md | 33 ++++++ global.json | 7 ++ 10 files changed, 361 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 Dockerfile create mode 100644 RELEASING.md create mode 100644 global.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..0abb1c5 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: Build +run-name: >- + ${{ github.event_name == 'pull_request' && format('Checking that #{0} builds', github.event.pull_request.number) + || format('Building {0}', github.ref_name) }} + +# Trunk-based: main is the only long-lived branch. Every pull request and every push to main builds the +# whole solution (the library and all Examples) and packs the library, but nothing is published here: +# this is a public NuGet package, and NuGet versions are permanent, so a release is a deliberate act - +# publishing a GitHub Release v{semver}, which runs publish.yml. There is no test suite, so a clean build +# and pack is the check that gates the merge (the required status check is this job's name, `build`). +on: + pull_request: + branches: [main] + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: build + runs-on: ubuntu-latest + timeout-minutes: 15 + concurrency: + # A newer push to the same pull request cancels the older build; builds of main never cancel each other. + group: build-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.run_id }} + cancel-in-progress: true + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + global-json-file: global.json + + # The csproj turns high and critical NuGet advisories (NU1903/NU1904), direct or transitive, into + # errors, so a vulnerable dependency fails this step rather than reaching a release. + - name: Restore + run: dotnet restore ConvertApi.sln + + - name: Build the library and the examples + run: dotnet build ConvertApi.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true + + # A pre-release version that sorts below any real release and names the run, so a package + # downloaded from this run can be tried out but never mistaken for a published one. + - name: Pack + id: pack + env: + RUN: ${{ github.run_number }} + SHORT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -euo pipefail + VERSION="0.0.0-ci.$RUN.g${SHORT_SHA:0:7}" + dotnet pack ConvertApi/ConvertApi.csproj -c Release --no-build -o out \ + -p:Version="$VERSION" -p:ContinuousIntegrationBuild=true + test -f "out/ConvertApi.$VERSION.nupkg" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ConvertApi-${{ steps.pack.outputs.version }} + path: out/ + retention-days: 14 + if-no-files-found: error + + - name: Summary + env: + VERSION: ${{ steps.pack.outputs.version }} + run: | + { + echo "| | |" + echo "|---|---|" + echo "| Package | \`ConvertApi $VERSION\` (artifact only, not published) |" + echo "| Dependencies | $(unzip -p "out/ConvertApi.$VERSION.nupkg" ConvertApi.nuspec | grep -o 'dependency id="[^"]*" version="[^"]*"' | sort -u | sed 's/dependency id="\([^"]*\)" version="\([^"]*\)"/\1 \2/' | paste -sd ',' - | sed 's/,/, /g') |" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..44c40b4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,209 @@ +name: Publish +run-name: Publishing ConvertApi ${{ github.event.release.tag_name }} to nuget.org + +# A release is a GitHub Release, never a merge. Merging to main only builds (build.yml); publishing the +# Release v{semver} runs this file, which packs that tag's commit with the tag's version and pushes it to +# nuget.org. NuGet versions are public and permanent - a pushed version can be unlisted but never +# replaced - so nothing here retries, overwrites or skips a duplicate. +# +# gh release create v3.1.19 --target main --generate-notes -> ConvertApi 3.1.19 +# gh release create v3.2.0-preview.1 --target main --prerelease ... -> ConvertApi 3.2.0-preview.1 +# +# A stable version must be a commit on main. A pre-release may be any commit, so a branch can be tried +# out as a preview package before it is merged. +on: + release: + types: [published] + +permissions: + contents: read + +env: + # Only this account may publish a Release that reaches nuget.org. GitHub cannot express this as a + # permission on the Team plan, so it is checked here; the real controls are the v* tag ruleset (only + # repository admins create v* tags) and the nuget environment, which accepts only v* tags. + PUBLISHER: tomasr78 + PACKAGE_ID: ConvertApi + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + timeout-minutes: 30 + environment: + name: nuget + url: https://www.nuget.org/packages/ConvertApi/${{ steps.tag.outputs.version }} + concurrency: + group: publish-nuget + cancel-in-progress: false + permissions: + contents: read + # Only for nuget.org Trusted Publishing (NuGet/login below), which trades this run's OIDC token for + # a short-lived API key. Unused while the repository still publishes with the NUGET_API_KEY secret. + id-token: write + steps: + - name: Check who published the release + run: | + # A re-run keeps GITHUB_ACTOR as the original user; GITHUB_TRIGGERING_ACTOR is whoever pressed + # "Re-run", so both must be the publisher or anyone with write access could replay a release. + if [ "$GITHUB_ACTOR" != "$PUBLISHER" ] || [ "${GITHUB_TRIGGERING_ACTOR:-$GITHUB_ACTOR}" != "$PUBLISHER" ]; then + echo "::error::Only $PUBLISHER may publish to nuget.org - this release was published by $GITHUB_ACTOR and run by ${GITHUB_TRIGGERING_ACTOR:-$GITHUB_ACTOR}." + exit 1 + fi + echo "Published by $GITHUB_ACTOR" + + # The release event runs this file from the tag, so the workflow and the code come from the same commit. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Check the tag and the version + id: tag + env: + TAG: ${{ github.event.release.tag_name }} + PRERELEASE: ${{ github.event.release.prerelease }} + run: | + set -euo pipefail + # SemVer 2.0 without build metadata (nuget.org ignores "+..." and it would only confuse). + SEMVER='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$' + if ! [[ "$TAG" =~ $SEMVER ]]; then + echo "::error::Tag '$TAG' is not v{major}.{minor}.{patch} or v{major}.{minor}.{patch}-{pre-release}, e.g. v3.1.19 or v3.2.0-preview.1" + exit 1 + fi + if [ "$GITHUB_REF" != "refs/tags/$TAG" ]; then + echo "::error::The run is on $GITHUB_REF, not on the release tag refs/tags/$TAG" + exit 1 + fi + VERSION="${TAG#v}" + SHA=$(git rev-parse "refs/tags/$TAG^{commit}") + if [ "$(git rev-parse HEAD)" != "$SHA" ]; then + echo "::error::Checked out $(git rev-parse HEAD), but $TAG points at $SHA" + exit 1 + fi + + # The GitHub "pre-release" tick and the version must agree, so a preview cannot become the + # package's latest stable version by a forgotten tick, and a stable version cannot hide as a pre-release. + if [[ "$VERSION" == *-* ]]; then + if [ "$PRERELEASE" != true ]; then + echo "::error::$TAG is a pre-release version, so the GitHub Release must be marked as a pre-release (gh release create --prerelease)" + exit 1 + fi + else + if [ "$PRERELEASE" = true ]; then + echo "::error::$TAG is a stable version, but the GitHub Release is marked as a pre-release. Use a pre-release version such as ${TAG}-preview.1, or untick pre-release." + exit 1 + fi + if ! git merge-base --is-ancestor "$SHA" origin/main; then + echo "::error::$TAG ($SHA) is not on main. Stable versions are released from main only; release a branch as a pre-release instead." + exit 1 + fi + fi + + # nuget.org lists every version ever pushed, unlisted ones included, in lower case. + INDEX=$(curl -fsS --retry 3 "https://api.nuget.org/v3-flatcontainer/${PACKAGE_ID,,}/index.json") + if jq -e --arg v "${VERSION,,}" '.versions | index($v)' <<<"$INDEX" >/dev/null; then + echo "::error::$PACKAGE_ID $VERSION already exists on nuget.org and can never be pushed again. Release the next version instead." + exit 1 + fi + if [[ "$VERSION" != *-* ]]; then + LATEST=$(jq -r '[.versions[] | select(contains("-") | not)] | last // empty' <<<"$INDEX") + if [ -n "$LATEST" ] && [ "$(printf '%s\n%s\n' "$LATEST" "$VERSION" | sort -V | tail -n1)" != "$VERSION" ]; then + echo "::warning::$VERSION is lower than the latest stable $LATEST, so it will not become the version that new installs get." + fi + fi + + { + echo "version=$VERSION" + echo "sha=$SHA" + } >> "$GITHUB_OUTPUT" + echo "Publishing $PACKAGE_ID $VERSION from $SHA" + + - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + global-json-file: global.json + + # The csproj fails the restore on any high or critical advisory in the dependency graph, so a + # vulnerable dependency cannot be published. + - name: Restore + run: dotnet restore ConvertApi/ConvertApi.csproj + + - name: Build and pack + env: + VERSION: ${{ steps.tag.outputs.version }} + run: | + set -euo pipefail + dotnet build ConvertApi/ConvertApi.csproj -c Release --no-restore \ + -p:Version="$VERSION" -p:ContinuousIntegrationBuild=true + dotnet pack ConvertApi/ConvertApi.csproj -c Release --no-build -o out \ + -p:Version="$VERSION" -p:ContinuousIntegrationBuild=true + ls -l out + + - name: Check the package + env: + VERSION: ${{ steps.tag.outputs.version }} + SHA: ${{ steps.tag.outputs.sha }} + run: | + set -euo pipefail + PKG="out/$PACKAGE_ID.$VERSION.nupkg" + test -f "$PKG" && test -f "out/$PACKAGE_ID.$VERSION.snupkg" + NUSPEC=$(unzip -p "$PKG" "$PACKAGE_ID.nuspec") + grep -q "$VERSION" <<<"$NUSPEC" || { echo "::error::$PKG does not carry version $VERSION"; exit 1; } + grep -q "commit=\"$SHA\"" <<<"$NUSPEC" || { echo "::error::$PKG does not record commit $SHA"; exit 1; } + echo "$NUSPEC" | grep -E '<(id|version|repository|dependency) ' + + # Trusted Publishing: once a nuget.org policy for this repository, publish.yml and the nuget + # environment exists, setting the repository variable NUGET_USER (the nuget.org profile name that + # owns the policy) switches publishing to a short-lived key, and NUGET_API_KEY can be deleted. + - name: Log in to nuget.org (Trusted Publishing) + id: login + if: vars.NUGET_USER != '' + uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0 + with: + user: ${{ vars.NUGET_USER }} + + # No --skip-duplicate: a version that already exists must fail loudly, never pass as published. + # The .snupkg next to the .nupkg is pushed to the symbol server by the same command. + - name: Push to nuget.org + env: + VERSION: ${{ steps.tag.outputs.version }} + NUGET_API_KEY: ${{ steps.login.outputs.NUGET_API_KEY || secrets.NUGET_API_KEY }} + run: | + set -euo pipefail + if [ -z "$NUGET_API_KEY" ]; then + echo "::error::No nuget.org key: set up Trusted Publishing (NUGET_USER) or the NUGET_API_KEY secret" + exit 1 + fi + dotnet nuget push "out/$PACKAGE_ID.$VERSION.nupkg" \ + --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json + + # nuget.org validates and indexes a push before anyone can restore it, usually within minutes. A slow + # index is not a failed release, so this only warns. + - name: Wait for nuget.org to serve the package + env: + VERSION: ${{ steps.tag.outputs.version }} + run: | + set -uo pipefail + for _ in $(seq 1 40); do + if curl -fsS "https://api.nuget.org/v3-flatcontainer/${PACKAGE_ID,,}/index.json" \ + | jq -e --arg v "${VERSION,,}" '.versions | index($v)' >/dev/null; then + echo "$PACKAGE_ID $VERSION is available on nuget.org" + exit 0 + fi + sleep 30 + done + echo "::warning::$PACKAGE_ID $VERSION was pushed but nuget.org has not served it after 20 minutes. Check https://www.nuget.org/packages/$PACKAGE_ID/$VERSION" + + - name: Summary + env: + VERSION: ${{ steps.tag.outputs.version }} + SHA: ${{ steps.tag.outputs.sha }} + AUTH: ${{ steps.login.outcome == 'success' && 'Trusted Publishing' || 'NUGET_API_KEY secret' }} + run: | + { + echo "| | |" + echo "|---|---|" + echo "| Package | [\`$PACKAGE_ID $VERSION\`](https://www.nuget.org/packages/$PACKAGE_ID/$VERSION) |" + echo "| Commit | \`$SHA\` |" + echo "| Published with | $AUTH |" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index d5dc367..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Release to NuGet - -on: - push: - tags: - - 'v*' - -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Checkout - uses: actions/checkout@master - with: - ref: ${{ github.ref }} - - name: Setup .NET SDK - uses: actions/setup-dotnet@v1 - - name: Build - run: dotnet build ConvertApi -c Release - - name: Pack nugets - run: dotnet pack ConvertApi -c Release --no-build --property:PackageOutputPath=. - - name: Push to NuGet - run: dotnet nuget push "./ConvertApi/*.nupkg" --api-key ${{secrets.NUGET_API_KEY}} --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a43473..4291536 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on Keep a Changelog and this project adheres to Semantic Versioning. +## [3.1.19] — 2026-10-16 + +### Security +- Bump `Microsoft.OpenApi.Readers` 1.6.23 → 1.6.31, fixing the high-severity YAML alias-expansion + denial of service [GHSA-7pxr-59rr-hqj2](https://github.com/advisories/GHSA-7pxr-59rr-hqj2) + (patched in 1.6.30). No API change: the converter catalog returns the same converters and tags. + +### Changed +- Releases are now cut from a GitHub Release `v{version}`; the package version comes from the tag. + The build now fails on any high or critical advisory in the dependency graph. +- Package metadata: repository URL points to `convertapi-library-dotnet`, the description no longer + names specific .NET versions, release notes link to GitHub Releases, and a symbol package (`.snupkg`) + is published alongside. + ## [3.1.15-dev] — 2026-05-14 ### Added diff --git a/ConvertApi/ConvertApi.csproj b/ConvertApi/ConvertApi.csproj index bcc2116..c39aa35 100644 --- a/ConvertApi/ConvertApi.csproj +++ b/ConvertApi/ConvertApi.csproj @@ -1,15 +1,16 @@ - true - 3.1.18 + + 0.0.0-local ConvertApi ConvertApi https://www.convertapi.com - https://github.com/ConvertAPI/convertapi-dotnet - GIT + https://github.com/ConvertAPI/convertapi-library-dotnet + git file-converter document-converter office-to-pdf office2pdf docx-to-pdf docx2pdf xlsx-to-pdf xlsx2pdf pptx-to-pdf ppt2pdf pdf-merge pdf-split pdf-compress pdf-watermark prdf-extract pdf2extract pdf-protect pdfa ocr redact html-to-pdf web-to-pdf image-to-pdf jpg-to-pdf png-to-pdf email-to-pdf eml-to-pdf msg-to-pdf convertapi pdf docx xlsx pptx office converter csharp dotnet sdk - 2012-2025 ConvertApi + 2012-2026 ConvertApi https://www.convertapi.com/terms https://www.convertapi.com/favicon/android-chrome-192x192.png README.md @@ -17,17 +18,21 @@ ConvertApi ConvertApi ConvertApiDotNet - 3.1.18.0 - 3.1.18.0 - Development snapshot (-dev prerelease). Changelog will evolve during the development cycle. + https://github.com/ConvertAPI/convertapi-library-dotnet/releases netstandard2.0;netcoreapp3.1 - 3.1.18 - ConvertAPI for .NET — one call to turn Office files into PDF or back again (DOCX ⇄ PDF, XLSX ⇄ PDF, PPTX ⇄ PDF). Convert HTML, images, ebooks, email, zip; merge, split, watermark, compress, OCR, redact, protect or repair PDF. Async, cross-platform, .NET Standard 2.0+ / .NET Core 5-9 + ConvertAPI for .NET — one call to turn Office files into PDF or back again (DOCX ⇄ PDF, XLSX ⇄ PDF, PPTX ⇄ PDF). Convert HTML, images, ebooks, email, zip; merge, split, watermark, compress, OCR, redact, protect or repair PDF. Async and cross-platform: targets .NET Standard 2.0, so it runs on .NET Framework 4.6.2+ and every modern .NET. + true + snupkg + + true + all + $(WarningsAsErrors);NU1903;NU1904 - + diff --git a/ConvertApi/docs/README.md b/ConvertApi/docs/README.md index 0fa1ab0..d3286d2 100644 --- a/ConvertApi/docs/README.md +++ b/ConvertApi/docs/README.md @@ -462,7 +462,7 @@ int conversionsConsumed = user.ConversionsConsumed; ### More examples -You can find more advanced examples in the [examples](https://github.com/ConvertAPI/convertapi-dotnet/tree/master/Examples) folder. +You can find more advanced examples in the [examples](https://github.com/ConvertAPI/convertapi-library-dotnet/tree/main/Examples) folder. #### Converting your first file, complete example: @@ -492,4 +492,4 @@ catch (ConvertApiException e) Please leave all comments, bugs, requests, and issues on the Issues page. We'll respond to your request ASAP! ### License -The ConvertAPI .NET Library is licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php "Read more about the MIT license form") license. Refere to the [LICENSE](https://github.com/ConvertAPI/convertapi-dotnet/blob/master/LICENSE) file for more information. +The ConvertAPI .NET Library is licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php "Read more about the MIT license form") license. Refere to the [LICENSE](https://github.com/ConvertAPI/convertapi-library-dotnet/blob/main/LICENSE) file for more information. diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index dabdc70..0000000 --- a/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic -ARG NUGET_KEY -WORKDIR /src -COPY . . -RUN dotnet build ConvertApi -RUN dotnet pack --output . ConvertApi -RUN dotnet nuget push ConvertApi.*.nupkg --api-key="$NUGET_KEY" --source="https://api.nuget.org/v3/index.json" --skip-duplicate \ No newline at end of file diff --git a/README.md b/README.md index 2854cb1..06f3d77 100644 --- a/README.md +++ b/README.md @@ -455,7 +455,7 @@ int conversionsConsumed = user.ConversionsConsumed; ### More examples -You can find more advanced examples in the [examples](https://github.com/ConvertAPI/convertapi-dotnet/tree/master/Examples) folder. +You can find more advanced examples in the [examples](https://github.com/ConvertAPI/convertapi-library-dotnet/tree/main/Examples) folder. #### Converting your first file, complete example: @@ -485,4 +485,4 @@ catch (ConvertApiException e) Please leave all comments, bugs, requests, and issues on the Issues page. We'll respond to your request ASAP! ### License -The ConvertAPI .NET Library is licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php "Read more about the MIT license form") license. Refere to the [LICENSE](https://github.com/ConvertAPI/convertapi-dotnet/blob/master/LICENSE) file for more information. +The ConvertAPI .NET Library is licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php "Read more about the MIT license form") license. Refere to the [LICENSE](https://github.com/ConvertAPI/convertapi-library-dotnet/blob/main/LICENSE) file for more information. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..e73c592 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,33 @@ +# Releasing + +`main` is the only long-lived branch. Changes reach it through pull requests, merge commits only, and +every pull request and push to `main` must pass the `build` check (`.github/workflows/build.yml`: restore, +build the library and every example, pack). Merging publishes nothing. + +A release is a **GitHub Release** whose tag is the version, published by `tomasr78`: + +```bash +gh release create v3.1.19 --repo ConvertAPI/convertapi-library-dotnet --target main --title v3.1.19 --generate-notes +``` + +That runs `.github/workflows/publish.yml` in the `nuget` environment, which: + +1. refuses a Release published or re-run by anyone else; +2. checks the tag is SemVer (`v3.1.19`, `v3.2.0-preview.1`), that the GitHub pre-release tick matches + the version, that a stable version is a commit on `main`, and that nuget.org has never had that version; +3. builds and packs that commit with `-p:Version={tag without v}`, failing on any high or critical + advisory in the dependency graph; +4. pushes `ConvertApi.{version}.nupkg` and its `.snupkg` to nuget.org and waits until nuget.org serves it. + +**Pre-releases** work the same way and may come from any branch: + +```bash +gh release create v3.2.0-preview.1 --repo ConvertAPI/convertapi-library-dotnet --target my-branch --prerelease --title v3.2.0-preview.1 --notes "Preview of ..." +``` + +nuget.org versions are permanent. A bad version can only be unlisted (and, if needed, deprecated) on +nuget.org, then fixed by releasing the next version. Never delete and re-create a release tag. + +The version in `ConvertApi.csproj` is a `0.0.0-local` placeholder; pull request builds pack +`0.0.0-ci.{run}.g{sha}` as a downloadable artifact so a change can be tried before it is released. +Update `CHANGELOG.md` in the pull request that changes behaviour. diff --git a/global.json b/global.json new file mode 100644 index 0000000..3dbe096 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "10.0.400", + "rollForward": "latestPatch", + "allowPrerelease": false + } +}