214 lines
9.6 KiB
YAML
214 lines
9.6 KiB
YAML
name: immutable-release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*.*.*"
|
|
|
|
concurrency:
|
|
group: release-${{ gitea.ref_name }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 45
|
|
environment: production
|
|
steps:
|
|
- name: Check out immutable tag
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Install pinned .NET SDKs
|
|
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
|
|
with:
|
|
dotnet-version: |
|
|
8.0.128
|
|
10.0.301
|
|
|
|
- name: Install pinned Buildx and BuildKit
|
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
|
with:
|
|
version: v0.35.0
|
|
install: true
|
|
driver-opts: image=moby/buildkit:v0.25.2@sha256:0f63d66f8d2de0bd16438284831a3e9ee6ca7cd57b6eb3ed6e38a7a456590fa7
|
|
|
|
- name: Validate tag and produce reproducible artifacts
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
version="${GITHUB_REF_NAME#v}"
|
|
./scripts/check-release-tag.sh "$GITHUB_REF_NAME"
|
|
previous_tag="$(git tag --merged HEAD^ --list 'v*.*.*' --sort=-version:refname | sed -n '1p')"
|
|
if [[ -n "$previous_tag" ]]; then
|
|
./scripts/check-compatibility.sh "$previous_tag"
|
|
elif [[ -n "$(git tag --list 'v*.*.*' | sed -n '1p')" ]]; then
|
|
echo "No prior release tag is an ancestor of $GITHUB_REF_NAME." >&2
|
|
exit 1
|
|
else
|
|
./scripts/check-compatibility.sh __initial_release_without_base__
|
|
fi
|
|
release_builder="rendezvous-release-builder:${GITHUB_SHA}"
|
|
docker buildx build \
|
|
--platform linux/amd64 \
|
|
--file eng/release-builder.Dockerfile \
|
|
--target release-builder \
|
|
--load \
|
|
--tag "$release_builder" .
|
|
mkdir -p "${RUNNER_TEMP}/release-home" "${RUNNER_TEMP}/nuget"
|
|
docker run --rm \
|
|
--user "$(id -u):$(id -g)" \
|
|
--env HOME="${RUNNER_TEMP}/release-home" \
|
|
--env NUGET_PACKAGES="${RUNNER_TEMP}/nuget" \
|
|
--volume "$GITHUB_WORKSPACE:/source" \
|
|
--volume "${RUNNER_TEMP}:${RUNNER_TEMP}" \
|
|
--workdir /source \
|
|
"$release_builder" \
|
|
./scripts/build-release.sh "$version" "${RUNNER_TEMP}/release/$version"
|
|
./scripts/verify-real-consumers.sh "$version" "${RUNNER_TEMP}/release/$version"
|
|
echo "RENDEZVOUS_VERSION=$version" >>"$GITHUB_ENV"
|
|
echo "RENDEZVOUS_RELEASE_DIR=${RUNNER_TEMP}/release/$version" >>"$GITHUB_ENV"
|
|
echo "RENDEZVOUS_RELEASE_BUILDER=$release_builder" >>"$GITHUB_ENV"
|
|
|
|
- name: Build exact container candidate
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
|
|
common=(
|
|
--no-cache
|
|
--pull=false
|
|
--provenance=false
|
|
--platform linux/amd64
|
|
--build-arg SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH"
|
|
--build-arg SOURCE_REVISION_ID="$GITHUB_SHA"
|
|
)
|
|
release_tag="git.finalfactory.de/heikyu/rendezvous:${RENDEZVOUS_VERSION}"
|
|
image_one="${RUNNER_TEMP}/rendezvous-image-1.tar"
|
|
image_two="${RUNNER_TEMP}/rendezvous-image-2.tar"
|
|
docker buildx build "${common[@]}" --tag "$release_tag" \
|
|
--output "type=docker,dest=$image_one,rewrite-timestamp=true" .
|
|
docker buildx build "${common[@]}" --tag "$release_tag" \
|
|
--output "type=docker,dest=$image_two,rewrite-timestamp=true" .
|
|
cmp --silent "$image_one" "$image_two"
|
|
docker load --input "$image_one"
|
|
candidate_id="$(docker image inspect --format '{{.Id}}' "$release_tag")"
|
|
buildkit_version="$(docker buildx inspect --bootstrap | sed -n 's/.*BuildKit version: *//p' | sed -n '1p')"
|
|
docker run --rm \
|
|
--user "$(id -u):$(id -g)" \
|
|
--volume "$GITHUB_WORKSPACE:/source" \
|
|
--volume "${RUNNER_TEMP}:${RUNNER_TEMP}" \
|
|
--workdir /source \
|
|
"$RENDEZVOUS_RELEASE_BUILDER" \
|
|
python3 eng/release_artifacts.py record-container-build \
|
|
--provenance "$RENDEZVOUS_RELEASE_DIR/release-provenance.json" \
|
|
--buildx-version "$(docker buildx version)" \
|
|
--buildkit-version "$buildkit_version" \
|
|
--image-id "$candidate_id"
|
|
|
|
- name: Stage HTTP registration, browse, and authenticated UDP traversal
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
secret="deploy/compose/secrets/signing-key"
|
|
cleanup() {
|
|
RENDEZVOUS_UID=1654 RENDEZVOUS_GID=1654 RENDEZVOUS_IMAGE="git.finalfactory.de/heikyu/rendezvous:${RENDEZVOUS_VERSION}" \
|
|
docker compose -f deploy/compose/compose.yaml down --volumes >/dev/null 2>&1 || true
|
|
rm -f "$secret"
|
|
}
|
|
trap cleanup EXIT
|
|
install -d -m 0700 deploy/compose/secrets
|
|
openssl rand -out "$secret" 32
|
|
chmod 0444 "$secret"
|
|
export RENDEZVOUS_UID=1654
|
|
export RENDEZVOUS_GID=1654
|
|
port_suffix="$(( ${GITHUB_RUN_ID:-$$} % 10000 ))"
|
|
export RENDEZVOUS_HTTP_HOST_PORT="$(( 20000 + port_suffix ))"
|
|
export RENDEZVOUS_UDP_HOST_PORT="$(( 40000 + port_suffix ))"
|
|
runner_network="$(docker inspect "$HOSTNAME" | jq -er \
|
|
'.[0].NetworkSettings.Networks | keys[0]')"
|
|
runner_workspace_source="$(docker inspect "$HOSTNAME" | jq -er \
|
|
--arg destination "$GITHUB_WORKSPACE" \
|
|
'.[0].Mounts[] | select(.Destination == $destination) | .Source')"
|
|
export RENDEZVOUS_CONFIG_SOURCE="$runner_workspace_source/deploy/compose/appsettings.Production.json"
|
|
export RENDEZVOUS_SECRET_SOURCE="$runner_workspace_source/deploy/compose/secrets/signing-key"
|
|
export RENDEZVOUS_IMAGE="git.finalfactory.de/heikyu/rendezvous:${RENDEZVOUS_VERSION}"
|
|
docker compose -f deploy/compose/compose.yaml up --detach --no-build
|
|
container_id="$(docker compose -f deploy/compose/compose.yaml ps -q rendezvous)"
|
|
test -n "$container_id"
|
|
docker network connect "$runner_network" "$container_id"
|
|
service_ip="$(docker inspect "$container_id" | jq -er \
|
|
--arg network "$runner_network" \
|
|
'.[0].NetworkSettings.Networks[$network].IPAddress')"
|
|
export RENDEZVOUS_SMOKE_HTTP_URL="http://${service_ip}:8080/"
|
|
export RENDEZVOUS_SMOKE_UDP_ENDPOINT="${service_ip}:9050"
|
|
for attempt in {1..100}; do
|
|
curl --fail --silent "${RENDEZVOUS_SMOKE_HTTP_URL%/}/health/ready" >/dev/null 2>&1 && break
|
|
if (( attempt == 100 )); then
|
|
docker compose -f deploy/compose/compose.yaml logs rendezvous
|
|
exit 1
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
./scripts/smoke-deployment.sh
|
|
|
|
- name: Scan candidate for high and critical vulnerabilities
|
|
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0, post-incident safe SHA
|
|
with:
|
|
image-ref: git.finalfactory.de/heikyu/rendezvous:${{ env.RENDEZVOUS_VERSION }}
|
|
version: v0.69.3
|
|
format: table
|
|
exit-code: "1"
|
|
ignore-unfixed: false
|
|
severity: HIGH,CRITICAL
|
|
|
|
- name: Generate container SPDX inventory
|
|
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0, post-incident safe SHA
|
|
with:
|
|
image-ref: git.finalfactory.de/heikyu/rendezvous:${{ env.RENDEZVOUS_VERSION }}
|
|
version: v0.69.3
|
|
format: spdx-json
|
|
output: ${{ env.RENDEZVOUS_RELEASE_DIR }}/FinalFactory.Rendezvous.Container.${{ env.RENDEZVOUS_VERSION }}.spdx.json
|
|
|
|
- name: Finalize checksums over the publish-ready candidate
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
source_date_epoch="$(git show -s --format=%ct HEAD)"
|
|
docker run --rm \
|
|
--user "$(id -u):$(id -g)" \
|
|
--volume "$GITHUB_WORKSPACE:/source" \
|
|
--volume "${RUNNER_TEMP}:${RUNNER_TEMP}" \
|
|
--workdir /source \
|
|
"$RENDEZVOUS_RELEASE_BUILDER" \
|
|
bash -c 'python3 eng/release_artifacts.py normalize-container-sbom \
|
|
--file "$1/FinalFactory.Rendezvous.Container.$2.spdx.json" \
|
|
--version "$2" \
|
|
--commit "$3" \
|
|
--source-date-epoch "$4" \
|
|
&& ./scripts/finalize-release-candidate.sh "$2" "$1"' \
|
|
_ "$RENDEZVOUS_RELEASE_DIR" "$RENDEZVOUS_VERSION" "$GITHUB_SHA" "$source_date_epoch"
|
|
|
|
- name: Preserve verified candidate artifacts
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: rendezvous-${{ env.RENDEZVOUS_VERSION }}
|
|
path: ${{ env.RENDEZVOUS_RELEASE_DIR }}
|
|
if-no-files-found: error
|
|
retention-days: 30
|
|
|
|
- name: Install pinned signing client
|
|
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
|
with:
|
|
cosign-release: v3.0.6
|
|
|
|
- name: Publish once, sign, attest, and create release
|
|
shell: bash
|
|
env:
|
|
RENDEZVOUS_RELEASE_USERNAME: ${{ secrets.RELEASE_USERNAME }}
|
|
RENDEZVOUS_RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
|
|
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
|
run: ./scripts/publish-release.sh "$RENDEZVOUS_VERSION" "$RENDEZVOUS_RELEASE_DIR"
|