Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/codeowners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ jobs:
with:
fetch-depth: 0

- name: 'Update action.yml to build locally'
run: |
sed -i "s/image: .*/image: 'Dockerfile'/" action.yml
cat action.yml

- name: 'Codeowners Plus'
id: codeowners-plus
uses: ./
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: 'Set up Go'
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: '1.26'
go-version-file: go.mod

- name: 'Build'
run: go build -v ./...
Expand All @@ -37,7 +37,7 @@ jobs:
- name: 'Set up Go'
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: '1.26'
go-version-file: go.mod

- name: 'Golangci-lint'
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
20 changes: 19 additions & 1 deletion .github/workflows/goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: 1.26
go-version-file: go.mod

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7
Expand All @@ -34,3 +34,21 @@ jobs:
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Builds the action binaries with scripts/build-action-binary.sh (the
# same reproducible build prepare-release.sh used to embed checksums in
# action.yml), refuses to publish anything that does not match those
# checksums, and attaches the verified binaries to the release.
- name: Build & publish action binaries
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p dist
for arch in amd64 arm64; do
out="dist/codeowners-plus-action_linux_${arch}"
./scripts/build-action-binary.sh "${out}" "${arch}"
./scripts/verify-release-binary.sh "${out}" "${arch}"
done
gh release upload "${GITHUB_REF_NAME}" \
dist/codeowners-plus-action_linux_amd64 \
dist/codeowners-plus-action_linux_arm64
34 changes: 0 additions & 34 deletions .github/workflows/publish.yml

This file was deleted.

25 changes: 0 additions & 25 deletions Dockerfile

This file was deleted.

80 changes: 78 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,83 @@ inputs:
outputs:
data:
description: 'JSON string containing all the codeowners data (success, message, file-owners, file-optional, still-required)'
value: ${{ steps.run.outputs.data }}

runs:
using: 'docker'
image: 'docker://ghcr.io/multimediallc/codeowners-plus:latest'
using: 'composite'
steps:
- name: 'Resolve codeowners-plus binary'
id: resolve
shell: bash
env:
# Non-empty only in release commits: set by scripts/prepare-release.sh
# (which reproducibly builds the binaries to compute the checksums)
# and cleared by scripts/post-release.sh. A pin to the release tag or
# to the release commit's SHA downloads the prebuilt binary and
# verifies it against these checksums -- since they live in the
# pinned commit, the result is immutable. Any other ref builds from
# the pinned source, so a pin never resolves to anything newer than
# itself.
RELEASE_VERSION: ''
SHA256_LINUX_AMD64: ''
SHA256_LINUX_ARM64: ''
ACTION_REF: ${{ github.action_ref }}
ACTION_REPO: ${{ github.action_repository }}
ACTION_PATH: ${{ github.action_path }}
run: '"${ACTION_PATH}/scripts/action-resolve.sh"'

- name: 'Restore cached binary'
id: bincache
if: steps.resolve.outputs.cache-key != ''
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ steps.resolve.outputs.bin }}
key: ${{ steps.resolve.outputs.cache-key }}

# Downloads the release binary on a cache miss, or re-verifies the
# restored binary on a cache hit; both are checked against the checksum
# embedded in the pinned commit.
- name: 'Ensure release binary'
if: steps.resolve.outputs.mode == 'download'
shell: bash
env:
RELEASE_VERSION: ${{ steps.resolve.outputs.version }}
EXPECTED_SHA256: ${{ steps.resolve.outputs.sha256 }}
ACTION_REPO: ${{ github.action_repository }}
ACTION_PATH: ${{ github.action_path }}
ASSET: ${{ steps.resolve.outputs.asset }}
BIN: ${{ steps.resolve.outputs.bin }}
run: '"${ACTION_PATH}/scripts/action-ensure.sh"'

- name: 'Set up Go'
if: steps.resolve.outputs.mode == 'build' && steps.bincache.outputs.cache-hit != 'true'
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: ${{ github.action_path }}/go.mod
cache: false

Comment on lines +81 to +89

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need a separate cache action for build vs download, the cached path is the same.

- name: 'Build codeowners-plus from source'
if: steps.resolve.outputs.mode == 'build' && steps.bincache.outputs.cache-hit != 'true'
shell: bash
env:
ACTION_PATH: ${{ github.action_path }}
BIN: ${{ steps.resolve.outputs.bin }}
run: |
mkdir -p "$(dirname "$BIN")"
"${ACTION_PATH}/scripts/build-action-binary.sh" "$BIN"

- name: 'Run codeowners-plus'
id: run
shell: bash
env:
# The hyphenated INPUT_GITHUB-TOKEN name is intentional: it mirrors
# what Docker actions export and is exactly what main.go reads via
# os.LookupEnv. Bash passes non-identifier env vars through to child
# processes untouched.
INPUT_GITHUB-TOKEN: ${{ inputs.github-token }}
INPUT_PR: ${{ inputs.pr }}
INPUT_REPOSITORY: ${{ inputs.repository }}
INPUT_VERBOSE: ${{ inputs.verbose }}
INPUT_QUIET: ${{ inputs.quiet }}
BIN: ${{ steps.resolve.outputs.bin }}
run: '"${BIN}"'
8 changes: 0 additions & 8 deletions entrypoint.sh

This file was deleted.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/multimediallc/codeowners-plus

go 1.25.0

toolchain go1.26.4

require (
github.com/bmatcuk/doublestar/v4 v4.10.0
github.com/boyter/gocodewalker v1.5.1
Expand Down
4 changes: 3 additions & 1 deletion goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ builds:
- amd64
- arm64
archives:
- name_template: >-
- id: "cli"
ids: ["cli"]
name_template: >-
{{ .ProjectName }}_{{ .Version }}_{{ title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
Expand Down
36 changes: 36 additions & 0 deletions scripts/action-ensure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#! /usr/bin/env bash
# Installs the release binary at BIN, verified against EXPECTED_SHA256 (the
# checksum from the pinned commit): downloads on a cache miss, re-verifies
# the (untrusted, repo-scoped) cached copy on a cache hit.

set -eu

: "${RELEASE_VERSION:?RELEASE_VERSION must be set}" "${ACTION_REPO:?ACTION_REPO must be set}" \
"${ASSET:?ASSET must be set}" "${BIN:?BIN must be set}" "${EXPECTED_SHA256:?EXPECTED_SHA256 must be set}"

# Downloads the release binary and installs it to BIN if it matches
# EXPECTED_SHA256.
fetch() {
local dest
dest="$(mktemp)"
Comment thread
BakerNet marked this conversation as resolved.
Outdated
echo "Downloading ${ASSET} from release ${RELEASE_VERSION}" >&2
curl -fsSL --retry 3 -o "${dest}" \
"https://github.com/${ACTION_REPO}/releases/download/${RELEASE_VERSION}/${ASSET}"
if ! echo "${EXPECTED_SHA256} ${dest}" | sha256sum -c --quiet -; then
Comment thread
BakerNet marked this conversation as resolved.
Outdated
echo "Error: downloaded ${ASSET} does not match the checksum in the pinned commit" >&2
exit 1
fi
mkdir -p "$(dirname "${BIN}")"
mv "${dest}" "${BIN}"
chmod +x "${BIN}"
}

if [ -x "${BIN}" ]; then
if echo "${EXPECTED_SHA256} ${BIN}" | sha256sum -c --quiet - >/dev/null 2>&1; then
Comment thread
BakerNet marked this conversation as resolved.
Outdated
echo "Cached binary checksum matched (${EXPECTED_SHA256})" >&2
exit 0
fi
echo "Warning: cached binary checksum mismatch; discarding cache and re-fetching." >&2
rm -f "${BIN}"
fi
fetch
65 changes: 65 additions & 0 deletions scripts/action-resolve.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#! /usr/bin/env bash
# Decides how the action binary is obtained, emitting mode=download|build
# (plus bin/version/sha256/asset/cache-key) to $GITHUB_OUTPUT. Pins to the
# release tag or a release commit's SHA download the prebuilt binary,
# verified against the checksums baked into the pinned commit (set in
# action.yml only in release commits); all other refs build from the
# checked-out source. A pin never resolves to anything newer than itself.

set -eu

RELEASE_VERSION="${RELEASE_VERSION:-}"

if [ "${RUNNER_OS:-Linux}" != "Linux" ]; then
echo "Error: codeowners-plus only supports Linux runners (got '${RUNNER_OS}')." >&2
exit 1
fi
arch=""
expected=""
case "${RUNNER_ARCH:-X64}" in
X64)
arch="amd64"
expected="${SHA256_LINUX_AMD64:-}"
;;
ARM64)
arch="arm64"
expected="${SHA256_LINUX_ARM64:-}"
;;
esac

{
echo "bin=${RUNNER_TEMP:-/tmp}/codeowners-plus-action/codeowners-plus"
echo "version=${RELEASE_VERSION}"
echo "sha256=${expected}"
} >>"$GITHUB_OUTPUT"

# A release-tag pin or a SHA pin of a release commit downloads the prebuilt
# binary; the embedded checksum makes the result immutable either way.
# Mutable refs (branches) always build from the checked-out source.
pinned_to_release=false
if [ -n "${RELEASE_VERSION}" ] && [ -n "${expected}" ]; then
if [ "${ACTION_REF:-}" = "${RELEASE_VERSION}" ] || [[ "${ACTION_REF:-}" =~ ^[0-9a-f]{40}$ ]]; then
pinned_to_release=true
fi
fi

if [ -n "${ACTION_REPO:-}" ] && [ "${pinned_to_release}" = "true" ] && [ -n "${arch}" ]; then
echo "Action pinned to release '${RELEASE_VERSION}' (ref '${ACTION_REF}'); using prebuilt binary." >&2
{
echo "mode=download"
echo "asset=codeowners-plus-action_linux_${arch}"
# Content-addressed: the key names exactly the verified binary bytes.
echo "cache-key=codeowners-plus-action-bin-${expected}"
} >>"$GITHUB_OUTPUT"
exit 0
fi

echo "Action ref '${ACTION_REF:-<unknown>}' is not a release; building from source." >&2
echo "mode=build" >>"$GITHUB_OUTPUT"
if [[ "${ACTION_REF:-}" =~ ^[0-9a-f]{40}$ ]]; then
# Immutable commit pin: safe to cache the built binary.
echo "cache-key=codeowners-plus-action-src-${ACTION_REF}-linux-${arch:-${RUNNER_ARCH:-unknown}}" >>"$GITHUB_OUTPUT"
else
# Branch or other mutable ref: never cache, always build fresh.
echo "cache-key=" >>"$GITHUB_OUTPUT"
fi
15 changes: 15 additions & 0 deletions scripts/build-action-binary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#! /usr/bin/env bash

# Build script for the github action binary.
# Single source of truth for build settings since we build in both
# prepare-release (to get the binary shasum) and
# in the actual release step (to get the artifact).

set -eu

out="$1"
arch="${2:-$(go env GOARCH)}"

cd "$(dirname "${BASH_SOURCE[0]}")/.."
Comment thread
BakerNet marked this conversation as resolved.
Outdated
GOOS=linux GOARCH="${arch}" CGO_ENABLED=0 \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o "${out}" .
16 changes: 11 additions & 5 deletions scripts/post-release.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#! /usr/bin/env bash

set -e
set -u
set -eu

ACTIONS_FILE="action.yml"
CLI_TOOL_FILE="tools/cli/main.go"
README_FILE="README.md"

Expand Down Expand Up @@ -39,23 +39,29 @@ else
git checkout -b "${BRANCH_NAME}"
fi

echo "Updating ${CLI_TOOL_FILE} and ${README_FILE}..."
echo "Updating ${ACTIONS_FILE}, ${CLI_TOOL_FILE}, and ${README_FILE}..."

# sed -i works differently on macOS and Linux.
# For GNU sed (Linux), -i without an argument is fine.
# For BSD sed (macOS), -i requires an argument (even if empty string for no backup).
if sed --version 2>/dev/null | grep -q GNU; then # GNU sed
sed -i "s|RELEASE_VERSION: '.*'|RELEASE_VERSION: ''|g" "${ACTIONS_FILE}"
sed -i "s|SHA256_LINUX_AMD64: '.*'|SHA256_LINUX_AMD64: ''|g" "${ACTIONS_FILE}"
sed -i "s|SHA256_LINUX_ARM64: '.*'|SHA256_LINUX_ARM64: ''|g" "${ACTIONS_FILE}"
sed -i "s|Version: .*|Version: \"${DEV_TAG}\",|g" "${CLI_TOOL_FILE}"
sed -i "s|codeowners-plus@.*|codeowners-plus@${VERSION_TAG}|g" "${README_FILE}"
else # BSD sed (macOS)
sed -i '' "s|RELEASE_VERSION: '.*'|RELEASE_VERSION: ''|g" "${ACTIONS_FILE}"
sed -i '' "s|SHA256_LINUX_AMD64: '.*'|SHA256_LINUX_AMD64: ''|g" "${ACTIONS_FILE}"
sed -i '' "s|SHA256_LINUX_ARM64: '.*'|SHA256_LINUX_ARM64: ''|g" "${ACTIONS_FILE}"
sed -i '' "s|Version: .*|Version: \"${DEV_TAG}\",|g" "${CLI_TOOL_FILE}"
sed -i '' "s|codeowners-plus@.*|codeowners-plus@${VERSION_TAG}|g" "${README_FILE}"
fi
gofmt -w tools/cli
echo "${CLI_TOOL_FILE} and ${README_FILE} updated."
echo "${ACTIONS_FILE}, ${CLI_TOOL_FILE}, and ${README_FILE} updated."

echo "Committing changes..."
git add "${CLI_TOOL_FILE}" "${README_FILE}"
git add "${ACTIONS_FILE}" "${CLI_TOOL_FILE}" "${README_FILE}"
git commit -m "${VERSION_TAG}"

echo "--- Post release process completed successfully! ---"
Expand Down
Loading
Loading