Skip to content

Publish

Publish #6

Workflow file for this run

name: Publish
on:
push:
tags: [ 'v*' ]
workflow_dispatch:
inputs:
snapshot:
description: "Publish a snapshot instead of a release (leaves the release coordinate untouched)"
type: boolean
default: true
# Least privilege: publishing reads the repo and pushes to Maven Central, which uses
# its own credentials — the GitHub token needs nothing beyond read.
permissions:
contents: read
# Never let two releases race. Maven Central is immutable, so a duplicate in-flight
# deployment is not something that can be undone.
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: false
jobs:
publish:
# macOS is required: the iOS targets can only be built on an Apple host, and every module's
# six publications have to go up in one deployment.
runs-on: macos-15
timeout-minutes: 60
# Widened from the workflow-level `contents: read` solely to create the GitHub
# Release below. Everything else here writes to Maven Central using its own
# credentials, not the GitHub token.
permissions:
contents: write
steps:
# A release goes out from its tag, which the step below checks against VERSION_NAME. Run by hand from a
# branch with snapshot unticked, this workflow would otherwise release whatever VERSION_NAME says, with no
# tag and no GitHub Release.
- name: Refuse a release from anything but a tag
if: inputs.snapshot != true && !startsWith(github.ref, 'refs/tags/v')
run: |
echo "::error::A release runs from a v* tag, and $GITHUB_REF is not one. Tick snapshot, or push the tag."
exit 1
- uses: actions/checkout@v7
with:
# The API removal check reads the dumps at the last release tag.
fetch-depth: 0
- uses: actions/setup-java@v6
with:
distribution: 'zulu'
java-version: '21'
- uses: gradle/actions/setup-gradle@v6
# macos-15 defaults to Xcode 16.4, whose iOS SDK predates UIViewLayoutRegion — a
# symbol Compose Multiplatform's ui-uikit references. Kotlin/Native static caches
# link whole modules rather than tree-shaking, so any iOS *test executable* fails to
# link without a 26.x SDK (the static framework link survives because an archive
# never resolves external symbols). The image ships several Xcodes; take the newest.
- name: Select the newest installed Xcode
run: |
XCODE=$(ls -d /Applications/Xcode_*.app | sort -V | tail -1)
echo "Using $XCODE"
sudo xcode-select -s "$XCODE/Contents/Developer"
xcodebuild -version
# The version lives in gradle.properties, not in the tag. Publishing the wrong one
# is unrecoverable because Maven Central is immutable, so refuse to start on a
# mismatch rather than redeploying an already-released coordinate.
- name: Check the tag matches VERSION_NAME
if: startsWith(github.ref, 'refs/tags/v')
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PROJECT_VERSION="$(sed -n 's/^VERSION_NAME=//p' gradle.properties)"
echo "tag=$TAG_VERSION gradle.properties=$PROJECT_VERSION"
if [ "$TAG_VERSION" != "$PROJECT_VERSION" ]; then
echo "::error::Tag $GITHUB_REF_NAME does not match VERSION_NAME=$PROJECT_VERSION. Bump VERSION_NAME and retag."
exit 1
fi
- name: Run tests
run: ./gradlew :colorpicker-foundation:allTests :colorpicker-material3:allTests :color:allTests :color-compose:allTests
- name: Check the public API matches the committed dump
run: ./gradlew apiCheck
# apiCheck compares against the dump in the working tree, which apiDump rewrites along with
# the change. This compares against the last release, so a removal cannot ride along.
- name: Check nothing the last release published was removed
run: bash scripts/check-api-removals.sh
# Uploads one deployment holding every module's six publications to the Central Portal and
# releases it. The previous OSSRH Staging API upload only *staged* a deployment;
# nothing closed or released it, so nothing ever reached Maven Central.
# A snapshot is the safe rehearsal: it exercises the Portal token, the signing key
# and the whole upload path, but goes to the snapshot repository rather than
# consuming an immutable release coordinate. The version is overridden on the
# command line so gradle.properties is never edited just to test.
- name: Publish to Maven Central
run: |
VERSION="$(sed -n 's/^VERSION_NAME=//p' gradle.properties)"
if [ "${{ inputs.snapshot }}" = "true" ]; then
# VERSION_NAME may be a snapshot already; it must not publish as -SNAPSHOT-SNAPSHOT.
VERSION="${VERSION%-SNAPSHOT}"
echo "Publishing snapshot ${VERSION}-SNAPSHOT"
./gradlew publishToMavenCentral -PVERSION_NAME="${VERSION}-SNAPSHOT" --no-configuration-cache
else
echo "Publishing and releasing ${VERSION}"
./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
fi
env:
# Central Portal user tokens. An old OSSRH token returns 401 here.
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
# Armoured private key, its key id, and its passphrase.
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
# Only after Maven Central has accepted the release — a GitHub Release pointing at
# artifacts that never published would be worse than none. Skipped for snapshots,
# which are not releases and are overwritten freely.
#
# `gh` is preinstalled on the runner, so this adds no third-party action to the one
# job holding the signing key and the Portal token. `--verify-tag` refuses to invent
# a tag if the ref is somehow wrong.
- name: Create the GitHub Release
if: startsWith(github.ref, 'refs/tags/v') && inputs.snapshot != true
run: gh release create "$GITHUB_REF_NAME" --generate-notes --verify-tag
env:
GH_TOKEN: ${{ github.token }}