blob: 9701f9e7a0d32bd634391540a5cb187157273d85 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# A tag is a release candidate and publishes nothing. The GitHub release
# is created after the Apache vote has passed, so it is the event that
# publishes the image under its version. released, not published: a draft
# or a prerelease must not publish either.
release:
types: [released]
# By hand: publish a release that predates this workflow, or retry a
# publish that failed.
workflow_dispatch:
inputs:
tag:
description: 'Release tag to publish, for example v0.1.0'
required: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
# Every action used here is in the apache/*, actions/* or github/* namespace.
# The ASF GitHub Actions policy allows only those without a reviewed SHA pin,
# so docker login, buildx and the push are plain shell steps rather than
# third-party actions.
jobs:
license:
name: License
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.27'
cache: true
# The skywalking-eyes actions bring their own Go, pinned below what
# go.mod requires, and the dependency check runs go with it. Both
# checks run through the Makefile instead, with the toolchain the
# module declares, which is what make check does locally.
- name: Check license headers and dependency licenses
run: make license-check dep-check
build:
name: Build and Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.27'
cache: true
- name: Verify go.mod is tidy
run: |
go mod tidy
git diff --exit-code go.mod go.sum
- name: Vet
run: go vet ./...
- name: Build
run: go build -o bin/ ./cmd/asz
# The suite is split so a failure names which layer broke. The race
# detector needs a C toolchain, which the Windows runner does not put on
# its path, so Windows runs without it.
- name: Unit tests
run: go test ${{ runner.os != 'Windows' && '-race' || '' }} -count=1 ./pkg/... ./internal/...
- name: Scenarios, chain and boundary tests
run: go test ${{ runner.os != 'Windows' && '-race' || '' }} -count=1 ./tests/...
# The same scenarios once more, through the command a person runs, so
# the command is exercised and a failure prints as a person sees it.
- name: Scenarios through the command
shell: bash
run: |
for f in tests/scenarios/*.yaml; do
case "$f" in *.expect.yaml) continue;; esac
echo "== $f"
./bin/asz${{ runner.os == 'Windows' && '.exe' || '' }} scenario check "$f" || exit 1
done
# A real OpenTelemetry Collector, not the in-process receiver the scenario
# checks use: the protobuf the sender writes must be accepted by an OTLP
# receiver that is not ours, and what its file exporter writes must rebuild
# into a root that verifies. Ubuntu only, because it needs docker.
collector:
name: Collector
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.27'
cache: true
- name: Build
run: make build
- name: Push into a Collector and verify its output
run: tools/e2e-collector.sh
# Builds every platform on every run so a broken cross-compile is caught
# at once, and keeps the packages as an artifact. When a release is
# released on GitHub, after the vote where there is one, the packages and
# their checksums are attached to it: that is where people download them
# until the project distributes through dist.apache.org.
binaries:
name: Binaries
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
- uses: actions/setup-go@v5
with:
go-version: '1.27'
cache: true
- name: Version
id: v
env:
EVENT: ${{ github.event_name }}
INPUT_TAG: ${{ inputs.tag }}
run: |
ref="$GITHUB_REF"
if [ "$EVENT" = "workflow_dispatch" ]; then ref="refs/tags/$INPUT_TAG"; fi
case "$ref" in
refs/tags/v*) v="${ref#refs/tags/v}" ;;
*) v="$(git rev-parse HEAD)" ;;
esac
echo "version=$v" >> "$GITHUB_OUTPUT"
# Every platform is cross-compiled from one runner: Go needs no
# emulation and no C toolchain for this, so one job proves all five.
- name: Build every platform
run: make binaries checksums VERSION=${{ steps.v.outputs.version }}
- name: Smoke test
run: dist/build/linux-amd64/asz version
- uses: actions/upload-artifact@v4
with:
name: binaries-${{ steps.v.outputs.version }}
path: |
dist/*.tgz
dist/*.zip
dist/*.sha512
if-no-files-found: error
- name: Attach to the GitHub release
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.event.release.tag_name }}
run: gh release upload "$TAG" dist/*.tgz dist/*.zip dist/*.sha512 --clobber
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.27'
cache: true
- name: Lint
run: make lint
# The container image. A pull request builds it; a push to main or a
# version tag builds it for both platforms and publishes it to GHCR under
# this repository's name.
docker:
name: Docker
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
IMAGE: ghcr.io/${{ github.repository }}
steps:
- uses: actions/checkout@v4
with:
# A promotion by hand builds the tag it names. Every other event
# builds the ref that triggered it. Tags are needed to decide
# whether a promoted version is the newest release.
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
fetch-depth: 0
- name: Set up buildx
run: docker buildx create --use --name asz
- name: Image tags
id: meta
env:
EVENT: ${{ github.event_name }}
INPUT_TAG: ${{ inputs.tag }}
run: |
set -eu
image=$(echo "$IMAGE" | tr '[:upper:]' '[:lower:]')
sha=$(git rev-parse HEAD)
# Every build is tagged with its complete commit id, never moved.
tags="$image:$sha"
version="$sha"
ref="$GITHUB_REF"
if [ "$EVENT" = "workflow_dispatch" ]; then ref="refs/tags/$INPUT_TAG"; fi
case "$EVENT:$ref" in
push:refs/heads/main)
# The development head. latest is reserved for releases.
tags="$tags,$image:main" ;;
release:refs/tags/v*|workflow_dispatch:refs/tags/v*)
version="${ref#refs/tags/v}"
tags="$tags,$image:$version"
case "$version" in
*-*)
# A pre-release such as 0.2.0-rc1 moves no floating tag.
;;
*)
tags="$tags,$image:${version%.*}"
# latest must name the newest release, so promoting a patch
# of an older line must not drag it backwards.
highest=$(git tag --list 'v*' --sort=-version:refname | grep -v -- '-' | head -1)
if [ "v$version" = "$highest" ]; then tags="$tags,$image:latest"; fi ;;
esac ;;
esac
echo "tags=$tags" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "sha=$sha" >> "$GITHUB_OUTPUT"
echo "::notice::tags: $tags"
- name: Build
if: github.event_name == 'pull_request'
run: docker buildx build --platform linux/amd64 --load --build-arg VERSION=pr -t asz:pr .
- name: Smoke test
if: github.event_name == 'pull_request'
run: docker run --rm asz:pr version && docker run --rm asz:pr glossary | head -c 400
- name: Log in to GHCR
if: github.event_name != 'pull_request'
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Build and push
if: github.event_name != 'pull_request'
run: |
set -eu
args=""
for t in $(echo "${{ steps.meta.outputs.tags }}" | tr ',' ' '); do args="$args -t $t"; done
docker buildx build --platform linux/amd64,linux/arm64 $args \
--build-arg VERSION="${{ steps.meta.outputs.version }}" \
--label org.opencontainers.image.revision="${{ steps.meta.outputs.sha }}" \
--label org.opencontainers.image.version="${{ steps.meta.outputs.version }}" \
--push .
# Single fan-in job so branch protection needs only one required check.
result:
name: Required
if: always()
needs: [license, build, lint, docker, binaries, collector]
runs-on: ubuntu-latest
steps:
- name: Fail if any job failed
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
- name: Success
run: echo "all required checks passed"