I like to write code, build, and release tooling, for my brain at 3 AM. No surprises, no clever bits to trip me up. Lately I’ve been playing with GitHub Actions to build and deploy some of my public projects. It took a little while to figure out something that I could copy and paste between them, and I’d like to share what I’ve managed to come up with.

Build Actions

Committed to your project as .github/workflows/build.yml. These actions trigger whenever to you push to your master/main branch, or to a PR off your master/main branch.

Java

---
name: "java-build"

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: "Set up JDK 11"
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'adopt'
      - name: Build
        run: make build

Go

name: "go-build"

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: "Set up Go"
        uses: actions/setup-go@v2
        with:
          go-version: 1.16
      - name: Build
        run: make build

Release Actions

Committed to your project as .github/workflows/release.yml. These actions trigger whenever you push a tag with v as its prefix. Please note that the automatic releases action will fail if your tag does not follow a semantic versioning format.

Java

---
name: "tagged-release"

on:
  push:
    tags:
      - "v*"

jobs:
  tagged-release:
    name: "Tagged Release"
    runs-on: "ubuntu-latest"

    steps:
      - uses: actions/checkout@v2
      - name: "Set up JDK 11"
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'adopt'
      - name: "Release Build"
        run: make release
      - name: "Automatic Releases"
        uses: marvinpinto/action-automatic-releases@v1.2.0
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          prerelease: false
          files: |
            LICENSE*
            build/libs/*.jar            

Go

---
name: "tagged-release"

on:
  push:
    tags:
      - "v*"

jobs:
  tagged-release:
    name: "Tagged Release"
    runs-on: "ubuntu-latest"
    steps:
      - uses: actions/checkout@v2
      - name: "Set up Go"
        uses: actions/setup-go@v2
        with:
          go-version: 1.16
      - name: "Release Build"
        run: make release
      - name: "Automatic Releases"
        uses: marvinpinto/action-automatic-releases@v1.2.0
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          prerelease: false
          files: |
            LICENSE*
            target/*            

Example Makefiles

I use Makefiles to coordinate language-specific and generic build and deploy tasks, so all these actions wind up triggering a make target rather than a language-specific build tool. It also means that I can come back to a project and know how to build it without needing to immediately remember language-specific build tool incantations.

Java

GIT_TAG := $(shell git describe --tags 2>/dev/null)

.PHONY: build
build: clean
	./gradlew --console plain -Pversion=dev

.PHONY:
release: clean
  ./gradlew --console plain -Pversion=${GIT_TAG}

.PHONY: clean
clean:
	rm -rf build

.PHONY: deps
deps:
	./gradlew dependencies

Go

GIT_TAG := $(shell git describe --tags 2>/dev/null)

.PHONY: build
build: clean test compile

.PHONY: clean
clean:
	rm -rf target

.PHONY: test
test:
	go test ./...

.PHONY: compile
compile:
	go build -o target/project ./project/...

.PHONY: release
release: clean
	GOOS=linux  GOARCH=amd64 go build -o target/project-${GIT_TAG}-linux  ./project/...
	GOOS=darwin GOARCH=amd64 go build -o target/project-${GIT_TAG}-darwin ./project/...

Hope you find this useful.