Skip to content

Latest commit

 

History

History
547 lines (420 loc) · 18.8 KB

File metadata and controls

547 lines (420 loc) · 18.8 KB
description Use javachanges in GitHub Actions for CI validation, release planning, variable sync, and Maven or Gradle publishing workflows.

javachanges GitHub Actions Usage Guide

1. Overview

This guide explains how to use javachanges in GitHub Actions for:

  1. regular CI validation
  2. release-plan generation
  3. GitHub Actions variable and secret management
  4. publish preflight and real publishing
  5. Maven and Gradle dependency caching

The examples in this guide use direct CLI commands such as:

mvn -q -DskipTests compile exec:java -Dexec.args="status --directory $PWD"

Note: this repository currently documents direct javachanges CLI usage. It does not ship a Makefile wrapper in the repository root.

2. What javachanges Can Do In GitHub Actions

Recommended command mapping:

Goal Command
Check pending release state status
Create or update a GitHub release PR github-release-plan --write-plan-files false --execute true
Tag the merged release commit github-tag-from-plan --fresh true --execute true
Generate release metadata or sync the GitHub Release github-release-from-plan --fresh true [--execute true]
Generate a starter GitHub Actions workflow init-github-actions --build-tool auto
Generate Maven settings from env vars write-settings --output .m2/settings.xml
Render required GitHub variables and secrets render-vars --env-file env/release.env.local --platform github
Check local/platform readiness doctor-local, doctor-platform
Sync GitHub Actions variables and secrets sync-vars --platform github
Audit remote GitHub Actions variables and secrets audit-vars --platform github
Validate a release publish locally or in CI preflight
Run the actual Maven deploy command publish --execute true
Run the actual Gradle publish command gradle-publish --execute true
Generate release notes directly release-notes --tag vX.Y.Z --output target/release-notes.md

3. Recommended Repository Layout

For a GitHub-based release workflow, keep these files under version control:

Path Purpose
.changesets/*.md Pending release intent
CHANGELOG.md Generated changelog
env/release.env.example Variable template for repository publishing
.github/workflows/*.yml CI and release workflows

4. Local Preparation Before Touching GitHub Actions

4.1 Build the CLI

mvn -q test

4.2 Initialize a local env file

mvn -q -DskipTests compile exec:java -Dexec.args="init-env --target env/release.env.local"

Template fields in env/release.env.example:

Variable Meaning
MAVEN_RELEASE_REPOSITORY_URL Release repository URL
MAVEN_SNAPSHOT_REPOSITORY_URL Snapshot repository URL
MAVEN_RELEASE_REPOSITORY_ID Release server id used in Maven settings
MAVEN_SNAPSHOT_REPOSITORY_ID Snapshot server id used in Maven settings
MAVEN_REPOSITORY_USERNAME Shared username fallback
MAVEN_REPOSITORY_PASSWORD Shared password fallback
MAVEN_CENTRAL_USERNAME Optional Sonatype Central Portal token username fallback
MAVEN_CENTRAL_PASSWORD Optional Sonatype Central Portal token password fallback
MAVEN_RELEASE_REPOSITORY_USERNAME Optional explicit release username
MAVEN_RELEASE_REPOSITORY_PASSWORD Optional explicit release password
MAVEN_SNAPSHOT_REPOSITORY_USERNAME Optional explicit snapshot username
MAVEN_SNAPSHOT_REPOSITORY_PASSWORD Optional explicit snapshot password
JAVACHANGES_SNAPSHOT_BUILD_STAMP Optional explicit snapshot publish stamp for CI

4.3 Check your local environment

mvn -q -DskipTests compile exec:java -Dexec.args="doctor-local --env-file env/release.env.local --github-repo owner/repo"

4.4 Preview GitHub variables and secrets

mvn -q -DskipTests compile exec:java -Dexec.args="render-vars --env-file env/release.env.local --platform github"

4.5 Sync GitHub variables and secrets with gh

Dry-run:

mvn -q -DskipTests compile exec:java -Dexec.args="sync-vars --env-file env/release.env.local --platform github --repo owner/repo"

Apply:

mvn -q -DskipTests compile exec:java -Dexec.args="sync-vars --env-file env/release.env.local --platform github --repo owner/repo --execute true"

Audit:

mvn -q -DskipTests compile exec:java -Dexec.args="audit-vars --env-file env/release.env.local --platform github --github-repo owner/repo"

sync-vars writes:

Remote type Names
GitHub Actions variables MAVEN_RELEASE_REPOSITORY_URL, MAVEN_SNAPSHOT_REPOSITORY_URL, MAVEN_RELEASE_REPOSITORY_ID, MAVEN_SNAPSHOT_REPOSITORY_ID
GitHub Actions secrets MAVEN_REPOSITORY_USERNAME, MAVEN_REPOSITORY_PASSWORD, MAVEN_CENTRAL_USERNAME, MAVEN_CENTRAL_PASSWORD, MAVEN_RELEASE_REPOSITORY_USERNAME, MAVEN_RELEASE_REPOSITORY_PASSWORD, MAVEN_SNAPSHOT_REPOSITORY_USERNAME, MAVEN_SNAPSHOT_REPOSITORY_PASSWORD

4.6 Generate a starter workflow

Use init-github-actions when you want a minimal release workflow committed into the target repository:

mvn -q -DskipTests compile exec:java -Dexec.args="init-github-actions --directory /path/to/repo --build-tool auto --force true"

For Gradle repositories, --build-tool auto detects settings.gradle, settings.gradle.kts, build.gradle, or build.gradle.kts and writes a workflow that downloads the released CLI jar before running github-release-plan, github-tag-from-plan, gradle-publish, and github-release-from-plan.

5. GitHub Actions Workflow Patterns

5.1 CI-only validation

Use this when you want pull requests to validate build health and pending release state.

name: CI

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - uses: actions/setup-java@v5
        with:
          distribution: corretto
          java-version: '8'
          cache: maven
          cache-dependency-path: pom.xml

      - name: Verify build
        run: mvn -B verify

      - name: Inspect release state
        run: mvn -B -DskipTests compile exec:java -Dexec.args="status --directory $GITHUB_WORKSPACE"

5.2 Release-plan pull request automation

Use this pattern when main should accumulate .changesets/*.md files and produce a reviewed release PR.

name: Release Plan

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: write
  pull-requests: write

jobs:
  release-pr:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - uses: actions/setup-java@v5
        with:
          distribution: corretto
          java-version: '8'
          cache: maven
          cache-dependency-path: pom.xml

      - name: Build CLI
        run: mvn -B -DskipTests compile

      - name: Create or update release PR
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          mvn -B -DskipTests exec:java
          -Dexec.args="github-release-plan --directory $GITHUB_WORKSPACE --write-plan-files false --execute true"

5.3 Release tag automation

Use this when a merged release PR should create and push the final vX.Y.Z git tag automatically.

name: Tag Release

on:
  pull_request:
    types: [closed]

permissions:
  contents: write

jobs:
  tag-release:
    if: >
      github.event.pull_request.merged == true &&
      github.event.pull_request.base.ref == 'main' &&
      github.event.pull_request.head.ref == 'changeset-release/main'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          ref: ${{ github.event.pull_request.merge_commit_sha }}
          fetch-depth: 0

      - uses: actions/setup-java@v5
        with:
          distribution: corretto
          java-version: '8'
          cache: maven
          cache-dependency-path: pom.xml

      - name: Build CLI
        run: mvn -B -DskipTests compile

      - name: Tag merged release commit
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          mvn -B -DskipTests exec:java
          -Dexec.args="github-tag-from-plan --directory $GITHUB_WORKSPACE --fresh true --execute true"

5.4 Snapshot publish with javachanges publish

Use this when pushes to a dedicated snapshot branch should publish unique snapshots into your own snapshot repository.

name: Publish Snapshot

on:
  push:
    branches:
      - snapshot
  workflow_dispatch:
    inputs:
      snapshot_build_stamp:
        description: Optional explicit snapshot build stamp
        required: false
        type: string

permissions:
  contents: read

jobs:
  publish-snapshot:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - uses: actions/setup-java@v5
        with:
          distribution: corretto
          java-version: '8'
          cache: maven
          cache-dependency-path: pom.xml

      - name: Build CLI
        run: mvn -B -DskipTests compile

      - name: Resolve snapshot build stamp
        id: snapshot_build_stamp
        env:
          INPUT_SNAPSHOT_BUILD_STAMP: ${{ inputs.snapshot_build_stamp }}
        run: |
          if [ -n "$INPUT_SNAPSHOT_BUILD_STAMP" ]; then
            value="$INPUT_SNAPSHOT_BUILD_STAMP"
          else
            value="${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}.$(git rev-parse --short HEAD)"
          fi
          echo "value=$value" >> "$GITHUB_OUTPUT"

      - name: Preflight snapshot publish
        env:
          JAVACHANGES_SNAPSHOT_BUILD_STAMP: ${{ steps.snapshot_build_stamp.outputs.value }}
          MAVEN_SNAPSHOT_REPOSITORY_URL: ${{ vars.MAVEN_SNAPSHOT_REPOSITORY_URL }}
          MAVEN_SNAPSHOT_REPOSITORY_ID: ${{ vars.MAVEN_SNAPSHOT_REPOSITORY_ID }}
          MAVEN_REPOSITORY_USERNAME: ${{ secrets.MAVEN_REPOSITORY_USERNAME }}
          MAVEN_REPOSITORY_PASSWORD: ${{ secrets.MAVEN_REPOSITORY_PASSWORD }}
          MAVEN_SNAPSHOT_REPOSITORY_USERNAME: ${{ secrets.MAVEN_SNAPSHOT_REPOSITORY_USERNAME }}
          MAVEN_SNAPSHOT_REPOSITORY_PASSWORD: ${{ secrets.MAVEN_SNAPSHOT_REPOSITORY_PASSWORD }}
        run: |
          mvn -B -DskipTests compile exec:java \
            -Dexec.args="preflight --directory $GITHUB_WORKSPACE --snapshot"

      - name: Publish snapshot
        env:
          JAVACHANGES_SNAPSHOT_BUILD_STAMP: ${{ steps.snapshot_build_stamp.outputs.value }}
          MAVEN_SNAPSHOT_REPOSITORY_URL: ${{ vars.MAVEN_SNAPSHOT_REPOSITORY_URL }}
          MAVEN_SNAPSHOT_REPOSITORY_ID: ${{ vars.MAVEN_SNAPSHOT_REPOSITORY_ID }}
          MAVEN_REPOSITORY_USERNAME: ${{ secrets.MAVEN_REPOSITORY_USERNAME }}
          MAVEN_REPOSITORY_PASSWORD: ${{ secrets.MAVEN_REPOSITORY_PASSWORD }}
          MAVEN_SNAPSHOT_REPOSITORY_USERNAME: ${{ secrets.MAVEN_SNAPSHOT_REPOSITORY_USERNAME }}
          MAVEN_SNAPSHOT_REPOSITORY_PASSWORD: ${{ secrets.MAVEN_SNAPSHOT_REPOSITORY_PASSWORD }}
        run: |
          mvn -B -DskipTests compile exec:java \
            -Dexec.args="publish --directory $GITHUB_WORKSPACE --snapshot --execute true"

This publishes a unique revision such as 1.2.3-123456789.1.abc1234-SNAPSHOT, instead of repeatedly deploying the raw 1.2.3-SNAPSHOT. A common repository policy is to reserve main for release planning and merge development that should produce published snapshots into a separate snapshot branch.

5.5 Generic release publish with javachanges publish

Use this when the target repository publishes tagged releases into your own release repository after github-tag-from-plan has pushed the final tag, and you want javachanges to:

  1. validate the tag and version state
  2. generate .m2/settings.xml
  3. render the exact Maven deploy command
  4. execute the real deploy from the tag pipeline
name: Publish Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: read

jobs:
  publish-release:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - uses: actions/setup-java@v5
        with:
          distribution: corretto
          java-version: '8'
          cache: maven
          cache-dependency-path: pom.xml

      - name: Build CLI
        run: mvn -B -DskipTests compile

      - name: Publish
        env:
          MAVEN_RELEASE_REPOSITORY_URL: ${{ vars.MAVEN_RELEASE_REPOSITORY_URL }}
          MAVEN_REPOSITORY_USERNAME: ${{ secrets.MAVEN_REPOSITORY_USERNAME }}
          MAVEN_REPOSITORY_PASSWORD: ${{ secrets.MAVEN_REPOSITORY_PASSWORD }}
          MAVEN_RELEASE_REPOSITORY_USERNAME: ${{ secrets.MAVEN_RELEASE_REPOSITORY_USERNAME }}
          MAVEN_RELEASE_REPOSITORY_PASSWORD: ${{ secrets.MAVEN_RELEASE_REPOSITORY_PASSWORD }}
        run: |
          mvn -B -DskipTests compile exec:java \
            -Dexec.args="publish --directory $GITHUB_WORKSPACE --tag ${GITHUB_REF_NAME} --execute true"

Note: these generic publish flows use the repository URLs and credentials from env/release.env.example.
If you are publishing to Maven Central with a central-publish Maven profile, see Publish To Maven Central and GitHub Actions Release Flow.

5.6 Gradle release-plan workflow

For Gradle repositories, use the released CLI jar and Gradle caching. The release-plan command is the same; only the invocation and build steps change.

name: Gradle Release Plan

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: write
  pull-requests: write

jobs:
  release-pr:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - uses: actions/setup-java@v5
        with:
          distribution: corretto
          java-version: '17'
          cache: gradle

      - name: Verify Gradle build
        run: ./gradlew build

      - name: Download javachanges
        run: >
          mvn -q dependency:copy
          -Dartifact=io.github.sonofmagic:javachanges:__JAVACHANGES_LATEST_RELEASE_VERSION__
          -DoutputDirectory=.javachanges

      - name: Create or update release PR
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: >
          java -jar .javachanges/javachanges-__JAVACHANGES_LATEST_RELEASE_VERSION__.jar
          github-release-plan
          --directory "$GITHUB_WORKSPACE"
          --github-repo "$GITHUB_REPOSITORY"
          --write-plan-files false
          --execute true

For Gradle artifact publishing after a release tag, use gradle-publish so javachanges can read the release tag, resolve the Gradle command, and hand off the version:

java -jar .javachanges/javachanges-__JAVACHANGES_LATEST_RELEASE_VERSION__.jar \
  gradle-publish \
  --directory "$GITHUB_WORKSPACE" \
  --tag "$GITHUB_REF_NAME" \
  --execute true

6. Maven And Gradle Cache Behavior In GitHub Actions

The recommended configuration is:

- uses: actions/setup-java@v5
  with:
    distribution: corretto
    java-version: '8'
    cache: maven
    cache-dependency-path: pom.xml

What this helps with:

Cached well Not solved by Maven cache
Maven dependencies in ~/.m2/repository git checkout and git fetch
Maven plugins and plugin dependencies JDK download and setup
Repeat builds with the same pom.xml hash GPG key import
Cross-workflow reuse of the same dependency graph Sonatype or custom repository publish latency

Important behavior:

Situation Result
First run of a new cache key Downloads still happen
pom.xml changes A new cache key may need fresh downloads
GitHub-hosted runner starts clean Cache still restores from GitHub storage, not from previous local disk state
You cache target/ instead of Maven repo Usually a bad trade-off for Java library CI

7. Recommended GitHub Actions Checks

Use this order:

  1. mvn -B verify
  2. javachanges status
  3. javachanges plan --apply true only in the release-plan workflow
  4. javachanges preflight --snapshot before any snapshot deploy
  5. javachanges publish --snapshot --execute true only in a snapshot workflow
  6. javachanges preflight --tag ... before any release deploy
  7. javachanges github-tag-from-plan --execute true when the reviewed release PR is merged
  8. javachanges publish --execute true only in a tag-driven release workflow

8. Common Mistakes

Problem Cause Fix
Release PR keeps changing unexpectedly release workflow edits files outside .changesets, pom.xml, or CHANGELOG.md limit the release-plan commit scope
Gradle release PR keeps changing unexpectedly release workflow edits files outside .changesets, gradle.properties, or CHANGELOG.md limit the release-plan commit scope
Publish job says repository credentials are missing required vars or secrets were never synced run render-vars, sync-vars, then audit-vars
Snapshot workflow keeps producing the same visible version the build stamp was fixed or never updated set JAVACHANGES_SNAPSHOT_BUILD_STAMP or derive one from run id and commit sha
Maven downloads still appear after enabling cache new cache key or first run let one successful run warm the cache
publish fails on a dirty worktree preflight and publish reject local changes by default commit changes or pass --allow-dirty true only when intended
GitHub workflow examples mention wrappers you do not have copied examples assume local wrapper scripts or Make targets use direct CLI commands from this guide

9. Recommended Documentation Split

Use these docs together:

Need Document
Full GitHub Actions release PR flow used by this repository GitHub Actions Release Flow
Maven Central publishing requirements Publish To Maven Central
Cross-platform release commands Development Guide

10. Summary

The practical GitHub Actions path is:

  1. validate with status in CI
  2. generate a reviewed release PR with github-release-plan
  3. push the final release tag with github-tag-from-plan
  4. manage GitHub variables and secrets with render-vars, sync-vars, and audit-vars
  5. publish snapshots from a dedicated snapshot branch with preflight --snapshot and publish --snapshot
  6. publish tagged releases with publish --tag ...
  7. use a Maven Central-specific workflow only when the repository really publishes releases to Central

11. References