diff --git a/.github/workflows/cli-release.yml b/.github/workflows/cli-release.yml index c2f8fb8..674fb4a 100644 --- a/.github/workflows/cli-release.yml +++ b/.github/workflows/cli-release.yml @@ -1,21 +1,47 @@ name: sw-serverless CLI release -# A tag cli-v (cli-v10.2.0) publishes the sw-serverless command as self-contained -# binaries on a GitHub release: nothing needs .NET installed to run them. +# Publishes the sw-serverless command as self-contained binaries on a GitHub release: nothing needs .NET +# installed to run them. Never run by hand or by a tag: the CI run that publishes this repository +# calls it with the version it computed (determine-semver: the next patch from the git tags, the +# run number for a prerelease), so the CLI carries the version of the release it ships with. +# a release, 10.0.61 cli-v10.0.61 the latest release +# a prerelease, 10.0.61-releases-r10-0-staging.121 cli-v10.0.61-stg.121 a pre-release, to try out +# Called by nuget-publish.yml, after the packages are published. on: - push: - tags: ["cli-v*"] - workflow_dispatch: + workflow_call: inputs: version: - description: "Version to release, e.g. 10.2.0 (tagged cli-v)" + description: "The version the calling run computed" + type: string required: true permissions: contents: write jobs: + channel: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.channel.outputs.version }} + prerelease: ${{ steps.channel.outputs.prerelease }} + steps: + - name: Channel + id: channel + env: + VERSION: ${{ inputs.version }} + run: | + v="$VERSION" + if [[ "$v" == *-* ]]; then + # A prerelease names its branch and run: 10.0.61-releases-r10-0-staging.121 is 10.0.61-stg.121. + v="${v%%-*}-stg.${v##*.}" + echo "prerelease=true" >> "$GITHUB_OUTPUT" + else + echo "prerelease=false" >> "$GITHUB_OUTPUT" + fi + echo "version=$v" >> "$GITHUB_OUTPUT" + build: + needs: channel runs-on: ubuntu-latest strategy: matrix: @@ -25,17 +51,11 @@ jobs: - uses: actions/setup-dotnet@v4 with: dotnet-version: "10.0.x" - - name: Version - id: version - run: | - v="${{ github.event.inputs.version }}" - [ -z "$v" ] && v="${GITHUB_REF_NAME#cli-v}" - echo "value=$v" >> "$GITHUB_OUTPUT" - name: Publish run: > dotnet publish SW.Serverless.Installer -c Release -r ${{ matrix.rid }} --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=none - -p:Version=${{ steps.version.outputs.value }} -o out + -p:Version=${{ needs.channel.outputs.version }} -o out - name: Package run: | mkdir -p dist @@ -51,10 +71,9 @@ jobs: path: dist/* release: - needs: build + needs: [channel, build] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: path: dist @@ -64,8 +83,14 @@ jobs: - name: Release env: GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} run: | - v="${{ github.event.inputs.version }}" - [ -z "$v" ] && v="${GITHUB_REF_NAME#cli-v}" - gh release create "cli-v$v" dist/* --title "sw-serverless $v" --target "${{ github.sha }}" \ - --notes "The sw-serverless command, self-contained for each platform. Install: curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/${{ github.event.repository.default_branch }}/scripts/install-cli.sh | sh — or download the archive for your platform and check it against SHA256SUMS." + v="${{ needs.channel.outputs.version }}" + install="curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/main/scripts/install-cli.sh" + if [ "${{ needs.channel.outputs.prerelease }}" = "true" ]; then + gh release create "cli-v$v" dist/* --title "sw-serverless CLI $v (pre-release)" --target "${{ github.sha }}" --prerelease \ + --notes "A pre-release of the sw-serverless command, built from ${{ github.ref_name }}, to try out. Install: $install | SW_SERVERLESS_VERSION=$v sh — or download the archive for your platform and check it against SHA256SUMS." + else + gh release create "cli-v$v" dist/* --title "sw-serverless CLI $v" --target "${{ github.sha }}" --latest \ + --notes "The sw-serverless command, self-contained for each platform. Install: $install | sh — or download the archive for your platform and check it against SHA256SUMS." + fi diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 8b3b5b1..17b1095 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -33,3 +33,23 @@ jobs: secrets: nuget-api-key: ${{ secrets.SWNUGETKEY }} dependabot-alerts-token: ${{ secrets.DEPENDABOT_ALERTS_TOKEN }} + + # The sw-serverless CLI and the Python and Node SDKs carry the version the packages were just + # published with, so nothing is tagged by hand. From main that is a release; a run by hand on + # another branch publishes a prerelease, which releases the CLI as a pre-release and skips the SDKs. + CLI: + needs: Publish + permissions: + contents: write # creates the GitHub release + uses: ./.github/workflows/cli-release.yml + with: + version: ${{ needs.Publish.outputs.version }} + + SDKs: + needs: Publish + permissions: + contents: read + uses: ./.github/workflows/sdk-release.yml + with: + version: ${{ needs.Publish.outputs.version }} + secrets: inherit diff --git a/.github/workflows/sdk-release.yml b/.github/workflows/sdk-release.yml index dd5cf11..3ce6553 100644 --- a/.github/workflows/sdk-release.yml +++ b/.github/workflows/sdk-release.yml @@ -1,15 +1,17 @@ name: SDK release -# A tag sdk-v (sdk-v10.2.0) publishes the Python SDK to PyPI as sw-serverless and the -# Node SDK to npm as @simplyworks/sw-serverless, at that version. Each is tested first, and a -# version already on its registry is left alone, so a failed run can simply be run again. +# Publishes the Python SDK to PyPI as sw-serverless and the Node SDK to npm as +# @simplyworks/sw-serverless. Never run by hand or by a tag: nuget-publish.yml calls it with the +# version it computed, so the SDKs carry the version of the packages they ship with. Only a release +# version is published; a prerelease (a branch build) isn't, since PyPI and npm consumers on a plain +# version range would pick it up. Each is tested first, and a version already on its registry is +# left alone, so a failed run can simply be run again. on: - push: - tags: ["sdk-v*"] - workflow_dispatch: + workflow_call: inputs: version: - description: "Version to publish, e.g. 10.2.0" + description: "The version the calling run computed" + type: string required: true permissions: @@ -17,6 +19,7 @@ permissions: jobs: python: + if: ${{ !contains(inputs.version, '-') }} runs-on: ubuntu-latest defaults: run: @@ -28,10 +31,12 @@ jobs: python-version: "3.12" - name: Version id: version + env: + VERSION: ${{ inputs.version }} run: | - v="${{ github.event.inputs.version }}" - [ -z "$v" ] && v="${GITHUB_REF_NAME#sdk-v}" + v="$VERSION" sed -i -E "s/^version = \".*\"/version = \"$v\"/" pyproject.toml + sed -i -E "s/^SDK_VERSION = \".*\"/SDK_VERSION = \"$v\"/" src/sw_serverless/_runner.py echo "value=$v" >> "$GITHUB_OUTPUT" - name: Test run: PYTHONPATH=src python -m unittest discover -s tests @@ -46,6 +51,7 @@ jobs: skip-existing: true node: + if: ${{ !contains(inputs.version, '-') }} runs-on: ubuntu-latest defaults: run: @@ -58,10 +64,12 @@ jobs: registry-url: "https://registry.npmjs.org" - name: Version id: version + env: + VERSION: ${{ inputs.version }} run: | - v="${{ github.event.inputs.version }}" - [ -z "$v" ] && v="${GITHUB_REF_NAME#sdk-v}" + v="$VERSION" npm version "$v" --no-git-tag-version --allow-same-version + sed -i -E "s/^const SDK_VERSION = \".*\";/const SDK_VERSION = \"$v\";/" src/index.js echo "value=$v" >> "$GITHUB_OUTPUT" - name: Test run: node --test diff --git a/README.md b/README.md index dfbb7d6..2ca249e 100644 --- a/README.md +++ b/README.md @@ -40,14 +40,14 @@ multi-node scheduler: adapters run on the machine that runs the host. | Tooling | NuGet `SimplyWorks.Serverless.Tooling` (`SW.Serverless.Tooling`) | What the CLI does, as a library: build, conformance tests, scaffolding, publishing. For an application that builds its own tools. | | CLI | `sw-serverless` (project `SW.Serverless.Installer`) | `init`, `build`, `test`, `run`, `manifest validate`, `publish`, `promote`, `versions`, `withdraw`. | -The Python and Node SDKs are not on PyPI or npm yet. You do not need them there: -`sw-serverless build` copies the SDK into every Python and Node package it builds. They will be -published to PyPI and npm later. +The Python and Node SDKs are published to PyPI (`sw-serverless`) and npm (`@simplyworks/sw-serverless`) +with each release, at the packages' version. You do not need them from there: `sw-serverless build` +copies the SDK into every Python and Node package it builds. ## Install the CLI Self-contained binaries are published on the -[GitHub releases](https://github.com/simplify9/SW-Serverless/releases) tagged `cli-v`, one +[GitHub releases](https://github.com/simplify9/SW-Serverless/releases) as `cli-v`, released with each version of the packages, one per platform: `sw-serverless-.tar.gz` for `linux-x64`, `linux-arm64`, `linux-musl-x64`, `linux-musl-arm64`, `osx-x64` and `osx-arm64`, and `sw-serverless-win-x64.zip`, with a `SHA256SUMS` file. On Linux or macOS, the install script picks your platform, checks the download and installs to `~/.local/bin`: diff --git a/docs/cli.md b/docs/cli.md index 51ded43..26b019d 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -15,7 +15,7 @@ manages published versions. ## Installing Self-contained binaries are published on the -[GitHub releases](https://github.com/simplify9/SW-Serverless/releases) tagged `cli-v`: +[GitHub releases](https://github.com/simplify9/SW-Serverless/releases) as `cli-v`, released with each version of the packages: | Platform | Asset | |---|---| diff --git a/scripts/install-cli.sh b/scripts/install-cli.sh index 82e2b7a..46722a2 100755 --- a/scripts/install-cli.sh +++ b/scripts/install-cli.sh @@ -20,8 +20,9 @@ case "$arch" in esac rid="$os-$arch" +# The newest release; a pre-release (-stg) only when named. if [ -n "${SW_SERVERLESS_VERSION:-}" ]; then tag="cli-v$SW_SERVERLESS_VERSION" -else tag=$(curl -fsSL "https://api.github.com/repos/$repo/releases" | grep -o '"tag_name": *"cli-v[^"]*"' | head -1 | sed 's/.*"\(cli-v[^"]*\)"/\1/'); fi +else tag=$(curl -fsSL "https://api.github.com/repos/$repo/releases" | grep -o '"tag_name": *"cli-v[^"]*"' | sed 's/.*"\(cli-v[^"]*\)"/\1/' | grep -E '^cli-v[0-9.]+$' | head -1); fi [ -n "$tag" ] || { echo "No sw-serverless release found" >&2; exit 1; } work=$(mktemp -d); trap 'rm -rf "$work"' EXIT