diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e524994 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,66 @@ +name: ci + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + # gofmt exits 0 even when it reformats, so check for named files instead. + - name: gofmt + run: | + unformatted="$(gofmt -l .)" + if [ -n "$unformatted" ]; then + echo "not gofmt'd:" + echo "$unformatted" + exit 1 + fi + + - name: vet + run: go vet ./... + + - name: test + run: go test -race ./... + + # This repo's whole pitch is zero third-party dependencies. Fail loudly + # if a require block ever appears rather than letting it in unnoticed. + - name: no third-party dependencies + run: | + if go list -m all | tail -n +2 | grep .; then + echo "third-party dependencies found; this project vendors none" + exit 1 + fi + + # Dogfood: the linter must accept the commits it is being built from. + # + # Two things make this fiddlier than it looks on pull_request events: + # checkout is shallow, so the base commit must be fetched before a range + # can resolve against it; and HEAD is the synthetic "Merge into + # " commit GitHub builds for the PR, which is not a conventional + # subject. Range endpoints are always included, so --no-merges does not + # drop it — lint up to the branch's real tip instead. + - name: lint own commits + env: + EVENT: ${{ github.event_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + go build -o /tmp/commitlint . + if [ "$EVENT" = "pull_request" ]; then + git fetch --no-tags --depth=50 origin "$BASE_SHA" "$HEAD_SHA" + /tmp/commitlint lint --range "$BASE_SHA..$HEAD_SHA" + else + git log -1 --format=%B | /tmp/commitlint lint + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f20a9c5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,74 @@ +name: release + +# Cutting a release is: git tag vX.Y.Z && git push origin vX.Y.Z +on: + push: + tags: ["v*"] + +permissions: + contents: write # required to create the release and upload assets + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history so release notes can diff against the previous tag + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: test + run: go test -race ./... + + # A tag that ships a binary claiming a different version is worse than no + # release, so refuse to build one that disagrees with the tag. + - name: build release binaries + env: + VERSION: ${{ github.ref_name }} + run: | + mkdir -p dist + for target in \ + linux/amd64 linux/arm64 \ + darwin/amd64 darwin/arm64 \ + windows/amd64; do + os="${target%/*}"; arch="${target#*/}" + name="commitlint_${VERSION}_${os}_${arch}" + out="dist/${name}/commitlint" + [ "$os" = "windows" ] && out="${out}.exe" + mkdir -p "dist/${name}" + CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" \ + go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o "$out" . + cp LICENSE README.md "dist/${name}/" + if [ "$os" = "windows" ]; then + (cd dist && zip -qr "${name}.zip" "${name}") + else + (cd dist && tar -czf "${name}.tar.gz" "${name}") + fi + rm -rf "dist/${name}" + done + (cd dist && sha256sum ./*.tar.gz ./*.zip > checksums.txt) + ls -la dist + + - name: verify version stamp matches tag + env: + VERSION: ${{ github.ref_name }} + run: | + go build -ldflags "-X main.version=${VERSION}" -o /tmp/commitlint . + got="$(/tmp/commitlint version)" + if [ "$got" != "$VERSION" ]; then + echo "binary reports '$got' but tag is '$VERSION'" + exit 1 + fi + + - name: publish release + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ github.ref_name }} + run: | + gh release create "$VERSION" \ + --title "$VERSION" \ + --generate-notes \ + dist/*.tar.gz dist/*.zip dist/checksums.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a543404 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 DivergentCodes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 60a0bba..f8c939c 100644 --- a/README.md +++ b/README.md @@ -124,9 +124,32 @@ exec commitlint lint --file "$1" ## Releases -Versions are published as **GitHub tagged releases** with changelogs in the -release notes (no committed changelog file). Pin a released version in CI: +Versions are published as **GitHub tagged releases** with autogenerated +changelogs in the release notes (no committed changelog file). Pin a released +version in CI: ```sh -go install github.com/DivergentCodes/commitlint@v1.0.0 +go install github.com/DivergentCodes/commitlint@v1.0.1 ``` + +Each release also carries prebuilt binaries for linux, darwin, and windows +(amd64 and arm64, except windows) plus a `checksums.txt`, for environments +without a Go toolchain: + +```sh +curl -sSfL -O https://github.com/DivergentCodes/commitlint/releases/download/v1.0.1/commitlint_v1.0.1_linux_amd64.tar.gz +tar -xzf commitlint_v1.0.1_linux_amd64.tar.gz +./commitlint_v1.0.1_linux_amd64/commitlint version +``` + +Cutting a release is pushing a tag — `.github/workflows/release.yml` runs the +tests, cross-compiles, verifies the binary reports the tagged version, and +publishes: + +```sh +git tag v1.0.1 && git push origin v1.0.1 +``` + +## License + +[MIT](LICENSE)