diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 50b7930..c8ac895 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,71 +1,67 @@ -name: Build and Release +name: Release on: push: tags: - - 'v*.*.*' # Matches tags like v1.0.0, v2.1.3, etc. + - 'v*.*.*' # Matches tags like v1.0.0, v2.1.3, v0.2.0-rc.1 + +permissions: + contents: write # create the release and upload its assets jobs: - build: + release: runs-on: ubuntu-latest steps: - name: Check out the repository - uses: actions/checkout@v3 + uses: actions/checkout@v7 + with: + fetch-depth: 0 # the tag check needs main, and git describe needs the tags + + - name: Check that a release tag is on main + run: | + # A pre-release tag (for example v0.2.0-dev.1a2b3c4 from make + # dev-release) can come from any branch: /releases/latest never + # returns a pre-release, so installed CLIs do not update to it. + if [[ "$GITHUB_REF_NAME" == *-* ]]; then + echo "Pre-release tag $GITHUB_REF_NAME: any branch is allowed." + exit 0 + fi + git fetch --no-tags origin main + if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then + echo "::error::Tag $GITHUB_REF_NAME is not on main. Release tags must be on the release branch." + exit 1 + fi - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v7 with: go-version: '1.27' check-latest: true - - name: Run build script - run: bash ./build.sh - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-artifacts - path: build/*.tar.gz - - release: - needs: build - runs-on: ubuntu-latest - - steps: - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: build-artifacts - path: build + - name: Run the checks + run: make check - - name: Create GitHub Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false + # Installed CLIs download fly-linux-.tar.gz and look for the binary + # fly-linux- in it. Do not change these asset names. + - name: Build the release archives + run: make release VERSION="$GITHUB_REF_NAME" - - name: Upload Release Assets for amd64 - uses: actions/upload-release-asset@v1 + - name: Create the GitHub release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: build/fly-linux-amd64.tar.gz - asset_name: fly-linux-amd64.tar.gz - asset_content_type: application/gzip - - - name: Upload Release Assets for arm64 - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: build/fly-linux-arm64.tar.gz - asset_name: fly-linux-arm64.tar.gz - asset_content_type: application/gzip \ No newline at end of file + GH_TOKEN: ${{ github.token }} + run: | + # A pre-release (for example v0.2.0-rc.1) is not returned by + # /releases/latest, so installed CLIs do not update to it. + prerelease=() + if [[ "$GITHUB_REF_NAME" == *-* ]]; then + prerelease=(--prerelease) + fi + gh release create "$GITHUB_REF_NAME" \ + build/fly-linux-amd64.tar.gz \ + build/fly-linux-arm64.tar.gz \ + build/checksums.txt \ + --title "$GITHUB_REF_NAME" \ + --generate-notes \ + --verify-tag \ + "${prerelease[@]}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..98a8f4c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + branches: [develop, main] + # All pull requests: stacked pull requests target each other, not develop. + pull_request: + +permissions: + contents: read + +# A new push cancels the older run for the same branch or pull request. +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Check out the repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 # git describe needs the tags for the version + + - name: Set up Go + uses: actions/setup-go@v7 + with: + go-version: '1.27' + check-latest: true + + - name: Run the checks + run: make check + + - name: Build the release archives + run: make release diff --git a/Makefile b/Makefile index 0d7f006..ab9c73e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Developer commands for fly. Run "make help" for the list. # Keep this file compatible with GNU Make 3.81, the version on macOS. -.PHONY: build test vet lint vuln fmt fmt-check check release clean help +.PHONY: build test vet lint vuln fmt fmt-check check release dev-version dev-release clean help BINARY := fly PKG := github.com/flywp/server-cli @@ -16,6 +16,13 @@ LDFLAGS := -X $(PKG)/internal/version.Version=$(VERSION) \ -X $(PKG)/internal/version.CommitHash=$(COMMIT) \ -X $(PKG)/internal/version.BuildDate=$(BUILD_DATE) +# Dev pre-releases are named -dev., for +# example v0.2.0-dev.1a2b3c4. DEV_BASE defaults to the minor version after the +# latest release tag; override it, for example make dev-release DEV_BASE=v0.1.2. +DEV_BASE ?= $(shell latest=$$(git describe --tags --abbrev=0 --exclude '*-*' 2>/dev/null || echo v0.0.0); \ + echo "$$latest" | awk -F. '{ sub(/^v/, "", $$1); printf "v%d.%d.0", $$1, $$2 + 1 }') +DEV_VERSION = $(DEV_BASE)-dev.$(shell git rev-parse --short=7 HEAD) + # Release platforms. Installed CLIs download fly--.tar.gz and look # for the binary fly-- in it, so do not change these names. RELEASE_PLATFORMS := linux/amd64 linux/arm64 @@ -59,8 +66,27 @@ release: ## Build the static release archives and checksums.txt in build/ done cd build && (command -v sha256sum >/dev/null 2>&1 && sha256sum *.tar.gz || shasum -a 256 *.tar.gz) > checksums.txt +dev-version: ## Print the dev pre-release version of HEAD + @echo $(DEV_VERSION) + +# The Release workflow publishes the tag as a pre-release. A tag on a commit +# whose release workflow does not know pre-releases would become the latest +# release and reach every installed CLI, so dev-release refuses such commits. +dev-release: ## Tag HEAD as a dev pre-release and push the tag (CI publishes it) + @set -e; \ + if [ -n "$$(git status --porcelain --untracked-files=no)" ]; then \ + echo "Commit or stash your changes first: the pre-release is built from the commit."; exit 1; fi; \ + if [ -z "$$(git branch -r --contains HEAD)" ]; then \ + echo "Push this commit to a branch first."; exit 1; fi; \ + if ! git show HEAD:.github/workflows/build.yml | grep -q -- '--prerelease'; then \ + echo "The release workflow at this commit does not publish pre-releases. Do not tag it."; exit 1; fi; \ + git tag -a "$(DEV_VERSION)" -m "Dev pre-release $(DEV_VERSION)"; \ + git push origin "$(DEV_VERSION)"; \ + echo "Pushed $(DEV_VERSION). The Release workflow publishes it as a pre-release:"; \ + echo " https://github.com/flywp/server-cli/releases/tag/$(DEV_VERSION)" + clean: ## Remove bin/ and build/ rm -rf bin/ build/ help: ## Show the targets - @grep -E '^[a-z-]+:.*## ' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " %-10s %s\n", $$1, $$2}' + @grep -E '^[a-z-]+:.*## ' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " %-12s %s\n", $$1, $$2}' diff --git a/README.md b/README.md index cafa8a7..f1bce35 100644 --- a/README.md +++ b/README.md @@ -119,13 +119,50 @@ make build # builds bin/fly with the version from git make test # go test ./... -race make lint # golangci-lint (pinned version, built with the module's Go) make vuln # govulncheck -make check # fmt-check, vet, lint, test and vuln: run this before each merge +make check # fmt-check, vet, lint, test and vuln (CI runs the same) make release # static linux/amd64 and linux/arm64 archives + checksums.txt in build/ make help # lists all targets ``` `make release VERSION=v0.2.0` stamps a specific version. The release archives must keep the names `fly-linux-.tar.gz` with the binary `fly-linux-` inside: installed CLIs look for these names when they run `fly update`. +CI runs `make check` and `make release` on every pull request and on every push to `develop` and `main`. + +### Releasing + +`main` is the release branch. To publish a release, tag a commit on `main` and push the tag: + +```bash +git tag -a v0.2.0 -m "v0.2.0" +git push origin v0.2.0 +``` + +The Release workflow checks that the tag is on `main`, runs `make check`, builds the archives with `make release`, and creates the GitHub release with both archives and `checksums.txt`. A tag with a pre-release suffix, such as `v0.2.0-rc.1`, becomes a pre-release, so installed CLIs do not update to it. + +### Dev pre-releases + +To test a branch on real servers before it merges, publish a dev pre-release of its current commit: + +```bash +make dev-version # prints the tag, for example v0.2.0-dev.1a2b3c4 +make dev-release # tags the commit and pushes the tag; CI publishes the pre-release +``` + +The version is the next minor version after the latest release, plus the short commit hash (`DEV_BASE=v0.1.2` overrides the first part). Pre-release tags can come from any branch. `make dev-release` refuses uncommitted changes, commits that are not pushed, and commits whose release workflow would publish the tag as a full release. + +`fly update` and `install.sh` only install the latest full release, so install a dev pre-release on a test server by hand: + +```bash +tag=v0.2.0-dev.1a2b3c4 arch=amd64 # arch: amd64 or arm64 (uname -m: x86_64 or aarch64) +base=https://github.com/flywp/server-cli/releases/download/$tag +curl -fsSLO "$base/fly-linux-$arch.tar.gz" && curl -fsSLO "$base/checksums.txt" +sha256sum -c --ignore-missing checksums.txt +tar -xzf "fly-linux-$arch.tar.gz" && sudo install -m 0755 "fly-linux-$arch" /usr/local/bin/fly +fly version +``` + +To build the same version locally without publishing it, run `make release VERSION=$(make -s dev-version)`. + ## License This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. diff --git a/build.sh b/build.sh deleted file mode 100755 index a7e5f0c..0000000 --- a/build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -# The release workflow runs this script. The build steps are in the Makefile: -# see "make release". - -set -e - -cd "$(dirname "$0")" -exec make release