Shared GitHub Actions reusable workflows and composite actions for Gradle/Android projects.
Personal use only. This repo is built for my own projects and is not intended for general use. There are no releases, no stability guarantees, and no support for external consumers.
Each project references components from this repo directly at @main — no releases required.
Consumer wrappers map only the secrets each reusable workflow needs; they do not use
secrets: inherit.
Call from a consuming repo's workflow with uses: LikeTheSalad/github-tools/.github/workflows/<name>@main.
Wrapper files can be regenerated with python3 scripts/setup-consumer.py <path-to-consumer-repo>.
The script derives required inputs and explicit secret mappings directly from the reusable workflow
schema in this repo, so input/secret changes in existing workflows do not require script updates.
Optional secrets may be emitted as commented-out suggestions in generated wrappers.
Runs an automatic wrapper sync check, the project's verification suite, optionally on Windows, and
optionally boots an Android emulator for instrumentation tests. Consumer wrappers should expose a
final local job named checks that depends on the reusable workflow call; register that final
checks job as the single required branch-protection check.
| Input | Type | Default | Description |
|---|---|---|---|
app-id |
string | required | GitHub App ID used by the wrapper sync check |
java-version |
string | 21 |
JDK version |
checks-command |
string | ./gradlew check |
Main verification command |
run-on-windows |
boolean | false |
Also run on windows-latest |
run-instrumentation-tests |
boolean | false |
Boot an Android emulator and run tests |
instrumentation-test-command |
string | ./gradlew connectedDebugAndroidTest |
Instrumentation test command |
instrumentation-api-level |
number | 35 |
Android API level for the emulator |
# .github/workflows/pr-check.yml
name: PR Check
on:
pull_request:
workflow_dispatch:
jobs:
pr_check:
permissions:
contents: write
pull-requests: write
uses: LikeTheSalad/github-tools/.github/workflows/pr-check.yml@main
with:
app-id: ${{ vars.APP_ID }}
checks-command: ./checks.sh
run-instrumentation-tests: true
instrumentation-test-command: ./gradlew -p demo-app connectedDebugAndroidTest
secrets:
GH_BOT_PRIVATE_KEY: ${{ secrets.GH_BOT_PRIVATE_KEY }}
checks:
name: checks
if: ${{ always() }}
needs: pr_check
runs-on: ubuntu-latest
steps:
- name: Finalize check status
env:
RESULT: ${{ needs.pr_check.result }}
run: test "$RESULT" = "success"Validates that a consumer repo's generated github-tools wrapper files are in sync with this repo. If drift is found, it runs the setup script, pushes the changes to a bot-managed branch, opens a PR, and fails the workflow so the caller stays red until the update PR is merged.
This workflow is intended to be called both from pr-check.yml and from a consumer-side manual
wrapper that exposes workflow_dispatch.
| Input | Type | Default | Description |
|---|---|---|---|
app-id |
string | required | GitHub App ID used to push the auto-update branch and open the PR |
# .github/workflows/sync-wrappers.yml
name: Sync github-tools wrappers
on:
workflow_dispatch:
jobs:
sync:
permissions:
contents: write
pull-requests: write
uses: LikeTheSalad/github-tools/.github/workflows/sync-wrappers.yml@main
with:
app-id: ${{ vars.APP_ID }}
secrets:
GH_BOT_PRIVATE_KEY: ${{ secrets.GH_BOT_PRIVATE_KEY }}Calculates the next version from merged PR labels (breaking → major, enhancement → minor,
neither → patch), creates release/{version} and pre-release/{version} branches, updates
gradle.properties, CHANGELOG.md, and optionally README.md, then opens a PR for human review.
Runs automatically on the 1st of every month (skips if any PR is open) and can be triggered manually with an optional version override.
Prerequisite: CHANGELOG.md must contain a <!-- CHANGELOG_INSERT --> marker above the first
## Version heading.
| Input | Type | Default | Description |
|---|---|---|---|
app-id |
string | required | GitHub App ID — pass vars.APP_ID |
java-version |
string | 21 |
JDK version |
version-override |
string | '' |
Force bump type: patch, minor, major, or empty for auto |
readme-version-sed |
string | '' |
sed expression to update version in README.md; use {version} as placeholder; empty = skip |
changelog-file |
string | CHANGELOG.md |
Path to the changelog |
# .github/workflows/release-prepare.yml
name: Release — Prepare
on:
schedule:
- cron: '0 0 1 * *'
workflow_dispatch:
inputs:
version_override:
description: 'Force version bump type (overrides label detection)'
type: choice
options: ['', 'patch', 'minor', 'major']
default: ''
jobs:
release:
permissions:
contents: write
pull-requests: write
uses: LikeTheSalad/github-tools/.github/workflows/release-prepare.yml@main
with:
app-id: ${{ vars.APP_ID }}
version-override: ${{ inputs.version_override || '' }}
readme-version-sed: 's/version "[0-9]\+\.[0-9]\+\.[0-9]\+"/version "{version}"/g'
secrets:
GH_BOT_PRIVATE_KEY: ${{ secrets.GH_BOT_PRIVATE_KEY }}Triggered when a pre-release/** PR is merged into release/**. Publishes artifacts to the
configured destinations, creates a git tag and GitHub Release (body populated from CHANGELOG.md),
then opens release/{version} → main with auto-merge enabled.
Prerequisite: the repo must have Allow auto-merge enabled (Settings → General → Pull
Requests) so the final PR to main can merge automatically once CI passes.
| Input | Type | Default | Description |
|---|---|---|---|
app-id |
string | required | GitHub App ID — pass vars.APP_ID |
java-version |
string | 21 |
JDK version |
publish-to-maven-central |
boolean | false |
Publish to Maven Central |
publish-to-gradle-portal |
boolean | false |
Publish to Gradle Plugin Portal |
# .github/workflows/release-publish.yml
name: Release — Publish
on:
pull_request:
types: [closed]
branches:
- 'release/**'
jobs:
release:
permissions:
contents: write
pull-requests: write
uses: LikeTheSalad/github-tools/.github/workflows/release-publish.yml@main
with:
app-id: ${{ vars.APP_ID }}
publish-to-maven-central: true
publish-to-gradle-portal: true
secrets:
GH_BOT_PRIVATE_KEY: ${{ secrets.GH_BOT_PRIVATE_KEY }}
MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}Call from any workflow step with uses: LikeTheSalad/github-tools/.github/actions/<name>@main.
| Action | Inputs | Output | Description |
|---|---|---|---|
setup-java |
java-version (default: 21) |
— | Installs Temurin JDK |
setup-bot-git-user |
app-id, private-key |
token |
Creates App token, configures git identity |
generate-changelog |
version, token, changelog-file (default: CHANGELOG.md) |
— | Inserts a new version section into the changelog from merged PRs |
publish-maven-central |
maven-central-username, maven-central-password, gpg-private-key, gpg-password |
— | Runs ./gradlew publishAndReleaseToMavenCentral -Prelease=true |
publish-gradle-portal |
gradle-publish-key, gradle-publish-secret, gpg-private-key, gpg-password |
— | Runs ./gradlew publishPlugins -Prelease=true |
Steps required when setting up these workflows in a new GitHub remote repository.
The release workflows use a GitHub App token to create branches, push commits, and open PRs. Using
an App token instead of GITHUB_TOKEN allows bot-created PRs to trigger pr-check.yml.
- Go to GitHub → Settings → Developer settings → GitHub Apps → New GitHub App.
- Fill in the required fields (name, homepage URL). Disable the webhook.
- Set the following repository permissions:
- Contents: Read and write
- Pull requests: Read and write
- Metadata: Read-only (mandatory)
- Click Create GitHub App.
- Note the App ID — you will need it in step 3.
- Scroll to Private keys and click Generate a private key. Save the
.pemfile. - Click Install App and install it on the repository.
Maven Central and the Gradle Plugin Portal require signed artifacts.
gpg --gen-key
gpg --keyserver keyserver.ubuntu.com --send-keys <KEY_ID>
gpg --armor --export-secret-keys <KEY_ID> # → GPG_PRIVATE_KEY secretGo to Settings → Secrets and variables → Actions.
Secrets
| Name | Value |
|---|---|
GH_BOT_PRIVATE_KEY |
Full contents of the .pem file from step 1 |
GPG_PRIVATE_KEY |
Armored private key from step 2 |
GPG_PASSWORD |
Passphrase chosen when generating the GPG key |
MAVEN_CENTRAL_USERNAME |
Maven Central user token (if publishing there) |
MAVEN_CENTRAL_PASSWORD |
Maven Central token password (if publishing there) |
GRADLE_PUBLISH_KEY |
Gradle Plugin Portal API key (if publishing there) |
GRADLE_PUBLISH_SECRET |
Gradle Plugin Portal API secret (if publishing there) |
Variables
| Name | Value |
|---|---|
APP_ID |
The App ID from step 1 |
Allow auto-merge — release-publish.yml opens the final release/** → main PR and enables
auto-merge on it. Go to Settings → General → Pull Requests and check Allow auto-merge.
Allow squash merging — Renovate uses squash merge for dependency updates. Check Allow squash merging in the same section.
Branch protection on main — Go to Settings → Branches, add a rule for main:
- Require status checks to pass before merging
- Add
checksas a required check
- Add
- Require branches to be up to date before merging
Branch protection on release/** — Add a rule for release/**:
- Require status checks to pass before merging
- Add
checksas a required check
- Add
- Require branches to be up to date before merging
Add the <!-- CHANGELOG_INSERT --> marker above the first ## Version heading:
Change Log
==========
<!-- CHANGELOG_INSERT -->
## Version 1.0.0 (2025-01-01)
...To enable automatic dependency updates with auto-merge:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"automerge": true,
"automergeStrategy": "squash",
"schedule": ["* * 2-31 * *"]
}The schedule keeps Renovate from opening PRs on the 1st of the month, leaving the release
window clear for release-prepare.yml.