diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml new file mode 100644 index 0000000..51296ed --- /dev/null +++ b/.github/workflows/release-prepare.yml @@ -0,0 +1,152 @@ +name: Prepare release + +on: + workflow_dispatch: + inputs: + version: + description: "Target version (semver, with or without v prefix). Pre-releases like 0.2.0-rc.1 are allowed." + required: true + type: string + +permissions: + contents: write + pull-requests: write + +concurrency: + group: release-prepare + cancel-in-progress: false + +env: + BUN_VERSION: 1.3.3 + +jobs: + prepare: + runs-on: ubuntu-latest + steps: + - name: Checkout main + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: ${{ env.BUN_VERSION }} + + - name: Validate and normalize version + id: ver + env: + INPUT_VERSION: ${{ inputs.version }} + run: | + VERSION="${INPUT_VERSION#v}" + SEMVER_REGEX='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$' + if [[ ! "$VERSION" =~ $SEMVER_REGEX ]]; then + echo "Error: '$INPUT_VERSION' is not a valid semver version" >&2 + exit 1 + fi + TAG="v${VERSION}" + BRANCH="release/${TAG}" + { + echo "version=${VERSION}" + echo "tag=${TAG}" + echo "branch=${BRANCH}" + } >> "$GITHUB_OUTPUT" + echo "Normalized: tag=${TAG}, branch=${BRANCH}" + + - name: Pre-flight checks + env: + GH_TOKEN: ${{ github.token }} + BRANCH: ${{ steps.ver.outputs.branch }} + TAG: ${{ steps.ver.outputs.tag }} + run: | + if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then + echo "Error: branch '$BRANCH' already exists on origin" >&2 + exit 1 + fi + if git ls-remote --exit-code --tags origin "$TAG" >/dev/null 2>&1; then + echo "Error: tag '$TAG' already exists on origin" >&2 + exit 1 + fi + set +e + gh release view "$TAG" >/dev/null 2>&1 + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + echo "Error: release '$TAG' already exists" >&2 + exit 1 + elif [ "$rc" -ne 1 ]; then + echo "Error: failed to check release '$TAG' (gh exit code: $rc)" >&2 + exit 1 + fi + + - name: Configure git identity + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Create release branch + env: + BRANCH: ${{ steps.ver.outputs.branch }} + run: git switch -c "$BRANCH" + + - name: Bump versions + env: + VERSION: ${{ steps.ver.outputs.version }} + run: | + bun pm pkg set version="$VERSION" + (cd bin/cli && bun pm pkg set version="$VERSION") + (cd packages/jammin-sdk && bun pm pkg set version="$VERSION") + + - name: Commit and push + env: + TAG: ${{ steps.ver.outputs.tag }} + BRANCH: ${{ steps.ver.outputs.branch }} + run: | + git add package.json bin/cli/package.json packages/jammin-sdk/package.json + if git diff --cached --quiet; then + echo "Error: no version changes to commit; package versions may already be ${TAG}" >&2 + exit 1 + fi + git commit -m "chore(release): bump version to ${TAG}" + git push origin "$BRANCH" + + - name: Create draft release + id: release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.ver.outputs.tag }} + run: | + RELEASE_URL=$(gh release create "$TAG" \ + --draft \ + --title "$TAG" \ + --notes "") + echo "url=${RELEASE_URL}" >> "$GITHUB_OUTPUT" + + - name: Open PR + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.ver.outputs.tag }} + BRANCH: ${{ steps.ver.outputs.branch }} + RELEASE_URL: ${{ steps.release.outputs.url }} + run: | + cat > /tmp/pr_body.md <