Some checks failed
CI / Rustfmt (push) Successful in 21s
CI / Clippy (push) Successful in 2m3s
CI / Cargo Audit & Deny (push) Successful in 34s
CI / Web Blocking Checks (push) Successful in 1m27s
CI / Security Blocking Checks (push) Successful in 15s
CI / Web Advisory Checks (push) Successful in 32s
CI / Security Advisory Checks (push) Successful in 1m25s
Publish Images / Resolve Publish Metadata (push) Successful in 1s
CI / Tests (push) Successful in 8m56s
Publish Images / Publish web (arm64) (push) Failing after 3m49s
Publish Images / Publish web (amd64) (push) Failing after 1m28s
Publish Images / Build Rust Bundles (amd64) (push) Failing after 12m21s
Publish Images / Build Rust Bundles (arm64) (push) Failing after 12m28s
Publish Images / Publish agent (amd64) (push) Has been skipped
Publish Images / Publish api (amd64) (push) Has been skipped
Publish Images / Publish agent (arm64) (push) Has been skipped
Publish Images / Publish api (arm64) (push) Has been skipped
Publish Images / Publish executor (amd64) (push) Has been skipped
Publish Images / Publish executor (arm64) (push) Has been skipped
Publish Images / Publish notifier (amd64) (push) Has been skipped
Publish Images / Publish notifier (arm64) (push) Has been skipped
Publish Images / Publish manifest attune/api (push) Has been skipped
Publish Images / Publish manifest attune/executor (push) Has been skipped
Publish Images / Publish manifest attune/agent (push) Has been skipped
Publish Images / Publish manifest attune/notifier (push) Has been skipped
Publish Images / Publish manifest attune/web (push) Has been skipped
770 lines
26 KiB
YAML
770 lines
26 KiB
YAML
name: Publish Images
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
target_arch:
|
|
description: Architecture to publish
|
|
type: choice
|
|
options:
|
|
- all
|
|
- amd64
|
|
- arm64
|
|
default: all
|
|
target_image:
|
|
description: Image to publish
|
|
type: choice
|
|
options:
|
|
- all
|
|
- api
|
|
- executor
|
|
- notifier
|
|
- agent
|
|
- web
|
|
default: all
|
|
push:
|
|
branches:
|
|
- main
|
|
- master
|
|
tags:
|
|
- "v*"
|
|
|
|
env:
|
|
REGISTRY_HOST: ${{ vars.CLUSTER_GITEA_HOST }}
|
|
REGISTRY_NAMESPACE: ${{ vars.CONTAINER_REGISTRY_NAMESPACE }}
|
|
REGISTRY_PLAIN_HTTP: ${{ vars.CONTAINER_REGISTRY_INSECURE }}
|
|
REPOSITORY_NAME: attune
|
|
ARTIFACT_REPOSITORY: attune/build-artifacts
|
|
GNU_GLIBC_VERSION: "2.28"
|
|
CARGO_TERM_COLOR: always
|
|
CARGO_INCREMENTAL: 0
|
|
CARGO_NET_RETRY: 10
|
|
RUSTUP_MAX_RETRIES: 10
|
|
RUST_MIN_STACK: 67108864
|
|
SQLX_OFFLINE: true
|
|
RUNNER_TOOL_CACHE: /toolcache
|
|
|
|
jobs:
|
|
metadata:
|
|
name: Resolve Publish Metadata
|
|
runs-on: build-amd64
|
|
outputs:
|
|
registry: ${{ steps.meta.outputs.registry }}
|
|
namespace: ${{ steps.meta.outputs.namespace }}
|
|
registry_plain_http: ${{ steps.meta.outputs.registry_plain_http }}
|
|
image_tag: ${{ steps.meta.outputs.image_tag }}
|
|
image_tags: ${{ steps.meta.outputs.image_tags }}
|
|
artifact_ref_base: ${{ steps.meta.outputs.artifact_ref_base }}
|
|
steps:
|
|
- name: Resolve tags and registry paths
|
|
id: meta
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
registry="${REGISTRY_HOST}"
|
|
namespace="${REGISTRY_NAMESPACE}"
|
|
registry_plain_http_raw="${REGISTRY_PLAIN_HTTP:-}"
|
|
registry_host_only="${registry%%:*}"
|
|
registry_plain_http_default="false"
|
|
|
|
if [ -z "$registry" ]; then
|
|
echo "CLUSTER_GITEA_HOST app variable is required"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$namespace" ]; then
|
|
namespace="${{ github.repository_owner }}"
|
|
fi
|
|
|
|
if printf '%s' "$registry_host_only" | grep -Eq '(^|[.])svc[.]cluster[.]local$'; then
|
|
registry_plain_http_default="true"
|
|
fi
|
|
|
|
if [ -n "$registry_plain_http_raw" ]; then
|
|
case "$(printf '%s' "$registry_plain_http_raw" | tr '[:upper:]' '[:lower:]')" in
|
|
1|true|yes|on)
|
|
registry_plain_http="true"
|
|
;;
|
|
0|false|no|off)
|
|
registry_plain_http="false"
|
|
;;
|
|
*)
|
|
echo "CONTAINER_REGISTRY_INSECURE must be a boolean when set"
|
|
exit 1
|
|
;;
|
|
esac
|
|
else
|
|
registry_plain_http="$registry_plain_http_default"
|
|
fi
|
|
|
|
short_sha="$(printf '%s' "${{ github.sha }}" | cut -c1-12)"
|
|
ref_type="${{ github.ref_type }}"
|
|
ref_name="${{ github.ref_name }}"
|
|
|
|
if [ "$ref_type" = "tag" ] && printf '%s' "$ref_name" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+([-.].*)?$'; then
|
|
version="${ref_name#v}"
|
|
image_tags="${version},latest,sha-${short_sha}"
|
|
else
|
|
version="sha-${short_sha}"
|
|
image_tags="edge,sha-${short_sha}"
|
|
fi
|
|
|
|
artifact_ref_base="${registry}/${namespace}/${ARTIFACT_REPOSITORY}"
|
|
|
|
{
|
|
echo "registry=$registry"
|
|
echo "namespace=$namespace"
|
|
echo "registry_plain_http=$registry_plain_http"
|
|
echo "image_tag=$version"
|
|
echo "image_tags=$image_tags"
|
|
echo "artifact_ref_base=$artifact_ref_base"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
build-rust-bundles:
|
|
name: Build Rust Bundles (${{ matrix.arch }})
|
|
runs-on: ${{ matrix.runner_label }}
|
|
needs: metadata
|
|
if: |
|
|
github.event_name != 'workflow_dispatch' ||
|
|
inputs.target_arch == 'all' ||
|
|
inputs.target_arch == matrix.arch
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: amd64
|
|
runner_label: build-amd64
|
|
service_rust_target: x86_64-unknown-linux-gnu
|
|
service_target: x86_64-unknown-linux-gnu.2.28
|
|
musl_target: x86_64-unknown-linux-musl
|
|
- arch: arm64
|
|
runner_label: build-amd64
|
|
service_rust_target: aarch64-unknown-linux-gnu
|
|
service_target: aarch64-unknown-linux-gnu.2.28
|
|
musl_target: aarch64-unknown-linux-musl
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Cache Rust toolchain
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.rustup/toolchains
|
|
~/.rustup/update-hashes
|
|
key: rustup-publish-${{ runner.os }}-${{ matrix.arch }}-stable-v1
|
|
restore-keys: |
|
|
rustup-${{ runner.os }}-${{ matrix.arch }}-stable-v1
|
|
rustup-${{ runner.os }}-stable-v1
|
|
rustup-
|
|
|
|
- name: Setup Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: |
|
|
${{ matrix.service_rust_target }}
|
|
${{ matrix.musl_target }}
|
|
|
|
- name: Cache Cargo registry + index
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry/index
|
|
~/.cargo/registry/cache
|
|
~/.cargo/git/db
|
|
key: cargo-registry-publish-${{ matrix.arch }}-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
cargo-registry-publish-${{ matrix.arch }}-
|
|
cargo-registry-
|
|
|
|
- name: Cache Cargo build artifacts
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: target
|
|
key: cargo-publish-${{ matrix.arch }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/*.rs', '**/Cargo.toml') }}
|
|
restore-keys: |
|
|
cargo-publish-${{ matrix.arch }}-${{ hashFiles('**/Cargo.lock') }}-
|
|
cargo-publish-${{ matrix.arch }}-
|
|
|
|
- name: Install native build dependencies
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
apt-get update
|
|
apt-get install -y pkg-config libssl-dev file binutils python3 python3-pip
|
|
|
|
- name: Install Zig
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
pip3 install --break-system-packages --no-cache-dir ziglang
|
|
|
|
- name: Install cargo-zigbuild
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if ! command -v cargo-zigbuild >/dev/null 2>&1; then
|
|
cargo install --locked cargo-zigbuild
|
|
fi
|
|
|
|
- name: Build release binaries
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cargo zigbuild --release \
|
|
--target "${{ matrix.service_target }}" \
|
|
--bin attune-api \
|
|
--bin attune-executor \
|
|
--bin attune-notifier
|
|
|
|
- name: Verify minimum glibc requirement
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
output_dir="target/${{ matrix.service_rust_target }}/release"
|
|
|
|
get_min_glibc() {
|
|
local file_path="$1"
|
|
readelf -W --version-info --dyn-syms "$file_path" \
|
|
| grep 'Name: GLIBC_' \
|
|
| sed -E 's/.*GLIBC_([0-9.]+).*/\1/' \
|
|
| sort -t . -k1,1n -k2,2n \
|
|
| tail -n 1
|
|
}
|
|
|
|
version_gt() {
|
|
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n 1)" = "$1" ] && [ "$1" != "$2" ]
|
|
}
|
|
|
|
for binary in attune-api attune-executor attune-notifier; do
|
|
min_glibc="$(get_min_glibc "${output_dir}/${binary}")"
|
|
if [ -z "${min_glibc}" ]; then
|
|
echo "Failed to determine glibc requirement for ${binary}"
|
|
exit 1
|
|
fi
|
|
echo "${binary} requires glibc ${min_glibc}"
|
|
if version_gt "${min_glibc}" "${GNU_GLIBC_VERSION}"; then
|
|
echo "Expected ${binary} to require glibc <= ${GNU_GLIBC_VERSION}, got ${min_glibc}"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
- name: Build static agent binaries
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cargo zigbuild --release \
|
|
--target "${{ matrix.musl_target }}" \
|
|
--bin attune-agent \
|
|
--bin attune-sensor-agent
|
|
|
|
- name: Assemble binary bundle
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
bundle_root="dist/bundle/${{ matrix.arch }}"
|
|
service_output_dir="target/${{ matrix.service_rust_target }}/release"
|
|
mkdir -p "$bundle_root/bin" "$bundle_root/agent"
|
|
|
|
cp "${service_output_dir}/attune-api" "$bundle_root/bin/"
|
|
cp "${service_output_dir}/attune-executor" "$bundle_root/bin/"
|
|
cp "${service_output_dir}/attune-notifier" "$bundle_root/bin/"
|
|
cp target/${{ matrix.musl_target }}/release/attune-agent "$bundle_root/agent/"
|
|
cp target/${{ matrix.musl_target }}/release/attune-sensor-agent "$bundle_root/agent/"
|
|
|
|
cat > "$bundle_root/metadata.json" <<EOF
|
|
{
|
|
"git_sha": "${{ github.sha }}",
|
|
"ref": "${{ github.ref }}",
|
|
"arch": "${{ matrix.arch }}",
|
|
"image_tag": "${{ needs.metadata.outputs.image_tag }}"
|
|
}
|
|
EOF
|
|
|
|
tar -C dist/bundle/${{ matrix.arch }} -czf "dist/attune-binaries-${{ matrix.arch }}.tar.gz" .
|
|
|
|
- name: Setup ORAS
|
|
uses: oras-project/setup-oras@v1
|
|
|
|
- name: Log in to registry for artifacts
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
GITHUB_TOKEN_FALLBACK: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
registry_username="${REGISTRY_USERNAME:-${{ github.actor }}}"
|
|
registry_password="${REGISTRY_PASSWORD:-${GITHUB_TOKEN_FALLBACK:-}}"
|
|
login_args=()
|
|
|
|
if [ -z "$registry_password" ]; then
|
|
echo "Set CONTAINER_REGISTRY_PASSWORD or enable GITHUB_TOKEN package writes"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
login_args+=(--plain-http)
|
|
fi
|
|
|
|
oras login "${{ needs.metadata.outputs.registry }}" \
|
|
"${login_args[@]}" \
|
|
--username "$registry_username" \
|
|
--password "$registry_password"
|
|
|
|
- name: Push binary bundle artifact
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
push_args=()
|
|
artifact_file="attune-binaries-${{ matrix.arch }}.tar.gz"
|
|
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
push_args+=(--plain-http)
|
|
fi
|
|
|
|
cp "dist/${artifact_file}" "${artifact_file}"
|
|
|
|
oras push \
|
|
"${push_args[@]}" \
|
|
"${{ needs.metadata.outputs.artifact_ref_base }}:rust-binaries-${{ needs.metadata.outputs.image_tag }}-${{ matrix.arch }}" \
|
|
--artifact-type application/vnd.attune.rust-binaries.v1 \
|
|
"${artifact_file}:application/vnd.attune.rust-binaries.layer.v1.tar+gzip"
|
|
|
|
- name: Link binary bundle package to repository
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
api_base="${{ github.server_url }}/api/v1"
|
|
package_name="${ARTIFACT_REPOSITORY}"
|
|
|
|
status_code="$(curl -sS -o /tmp/package-link-response.txt -w '%{http_code}' -X POST \
|
|
-u "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD}" \
|
|
"${api_base}/packages/${{ needs.metadata.outputs.namespace }}/container/${package_name}/-/link/${REPOSITORY_NAME}")"
|
|
|
|
case "${status_code}" in
|
|
200|201|204|409)
|
|
;;
|
|
*)
|
|
cat /tmp/package-link-response.txt
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
publish-rust-images:
|
|
name: Publish ${{ matrix.image.name }} (${{ matrix.arch }})
|
|
runs-on: ${{ matrix.runner_label }}
|
|
needs:
|
|
- metadata
|
|
- build-rust-bundles
|
|
if: |
|
|
(github.event_name != 'workflow_dispatch' ||
|
|
inputs.target_arch == 'all' ||
|
|
inputs.target_arch == matrix.arch) &&
|
|
(github.event_name != 'workflow_dispatch' ||
|
|
inputs.target_image == 'all' ||
|
|
inputs.target_image == matrix.image.name)
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: amd64
|
|
runner_label: build-amd64
|
|
platform: linux/amd64
|
|
image:
|
|
name: api
|
|
repository: attune/api
|
|
source_path: bin/attune-api
|
|
dockerfile: docker/Dockerfile.runtime
|
|
- arch: amd64
|
|
runner_label: build-amd64
|
|
platform: linux/amd64
|
|
image:
|
|
name: executor
|
|
repository: attune/executor
|
|
source_path: bin/attune-executor
|
|
dockerfile: docker/Dockerfile.runtime
|
|
- arch: amd64
|
|
runner_label: build-amd64
|
|
platform: linux/amd64
|
|
image:
|
|
name: notifier
|
|
repository: attune/notifier
|
|
source_path: bin/attune-notifier
|
|
dockerfile: docker/Dockerfile.runtime
|
|
- arch: amd64
|
|
runner_label: build-amd64
|
|
platform: linux/amd64
|
|
image:
|
|
name: agent
|
|
repository: attune/agent
|
|
source_path: agent/attune-agent
|
|
dockerfile: docker/Dockerfile.agent-package
|
|
- arch: arm64
|
|
runner_label: build-arm64
|
|
platform: linux/arm64
|
|
image:
|
|
name: api
|
|
repository: attune/api
|
|
source_path: bin/attune-api
|
|
dockerfile: docker/Dockerfile.runtime
|
|
- arch: arm64
|
|
runner_label: build-arm64
|
|
platform: linux/arm64
|
|
image:
|
|
name: executor
|
|
repository: attune/executor
|
|
source_path: bin/attune-executor
|
|
dockerfile: docker/Dockerfile.runtime
|
|
- arch: arm64
|
|
runner_label: build-arm64
|
|
platform: linux/arm64
|
|
image:
|
|
name: notifier
|
|
repository: attune/notifier
|
|
source_path: bin/attune-notifier
|
|
dockerfile: docker/Dockerfile.runtime
|
|
- arch: arm64
|
|
runner_label: build-arm64
|
|
platform: linux/arm64
|
|
image:
|
|
name: agent
|
|
repository: attune/agent
|
|
source_path: agent/attune-agent
|
|
dockerfile: docker/Dockerfile.agent-package
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup ORAS
|
|
uses: oras-project/setup-oras@v1
|
|
|
|
- name: Setup Docker Buildx
|
|
if: needs.metadata.outputs.registry_plain_http != 'true'
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Setup Docker Buildx For Plain HTTP Registry
|
|
if: needs.metadata.outputs.registry_plain_http == 'true'
|
|
uses: docker/setup-buildx-action@v3
|
|
with:
|
|
buildkitd-config-inline: |
|
|
[registry."${{ needs.metadata.outputs.registry }}"]
|
|
http = true
|
|
insecure = true
|
|
|
|
- name: Log in to registry
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
GITHUB_TOKEN_FALLBACK: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
registry_username="${REGISTRY_USERNAME:-${{ github.actor }}}"
|
|
registry_password="${REGISTRY_PASSWORD:-${GITHUB_TOKEN_FALLBACK:-}}"
|
|
|
|
if [ -z "$registry_password" ]; then
|
|
echo "Set CONTAINER_REGISTRY_PASSWORD or enable GITHUB_TOKEN package writes"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$HOME/.docker"
|
|
auth="$(printf '%s:%s' "$registry_username" "$registry_password" | base64 | tr -d '\n')"
|
|
|
|
cat > "$HOME/.docker/config.json" <<EOF
|
|
{
|
|
"auths": {
|
|
"${{ needs.metadata.outputs.registry }}": {
|
|
"auth": "${auth}"
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
oras_login_args=()
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
oras_login_args+=(--plain-http)
|
|
fi
|
|
|
|
oras login "${{ needs.metadata.outputs.registry }}" \
|
|
"${oras_login_args[@]}" \
|
|
--username "$registry_username" \
|
|
--password "$registry_password"
|
|
|
|
- name: Pull binary bundle
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
pull_args=()
|
|
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
pull_args+=(--plain-http)
|
|
fi
|
|
|
|
mkdir -p dist/artifact
|
|
cd dist/artifact
|
|
|
|
oras pull \
|
|
"${pull_args[@]}" \
|
|
"${{ needs.metadata.outputs.artifact_ref_base }}:rust-binaries-${{ needs.metadata.outputs.image_tag }}-${{ matrix.arch }}"
|
|
|
|
tar -xzf "attune-binaries-${{ matrix.arch }}.tar.gz"
|
|
|
|
- name: Prepare packaging context
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
rm -rf dist/image
|
|
mkdir -p dist/image
|
|
|
|
case "${{ matrix.image.name }}" in
|
|
api|executor|notifier)
|
|
cp "dist/artifact/${{ matrix.image.source_path }}" dist/attune-service-binary
|
|
;;
|
|
agent)
|
|
cp dist/artifact/agent/attune-agent dist/attune-agent
|
|
cp dist/artifact/agent/attune-sensor-agent dist/attune-sensor-agent
|
|
;;
|
|
*)
|
|
echo "Unsupported image: ${{ matrix.image.name }}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
- name: Push architecture image
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
image_ref="${{ needs.metadata.outputs.registry }}/${{ needs.metadata.outputs.namespace }}/${{ matrix.image.repository }}:${{ needs.metadata.outputs.image_tag }}-${{ matrix.arch }}"
|
|
|
|
build_cmd=(
|
|
docker buildx build
|
|
.
|
|
--platform "${{ matrix.platform }}"
|
|
--file "${{ matrix.image.dockerfile }}"
|
|
)
|
|
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
build_cmd+=(--output "type=image,\"name=${image_ref}\",push=true,registry.insecure=true")
|
|
else
|
|
build_cmd+=(--tag "$image_ref" --push)
|
|
fi
|
|
|
|
"${build_cmd[@]}"
|
|
|
|
- name: Link container package to repository
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
api_base="${{ github.server_url }}/api/v1"
|
|
package_name="${{ matrix.image.repository }}"
|
|
|
|
status_code="$(curl -sS -o /tmp/package-link-response.txt -w '%{http_code}' -X POST \
|
|
-u "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD}" \
|
|
"${api_base}/packages/${{ needs.metadata.outputs.namespace }}/container/${package_name}/-/link/${REPOSITORY_NAME}")"
|
|
|
|
case "${status_code}" in
|
|
200|201|204|409)
|
|
;;
|
|
*)
|
|
cat /tmp/package-link-response.txt
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
publish-web-images:
|
|
name: Publish web (${{ matrix.arch }})
|
|
runs-on: ${{ matrix.runner_label }}
|
|
needs: metadata
|
|
if: |
|
|
(github.event_name != 'workflow_dispatch' ||
|
|
inputs.target_arch == 'all' ||
|
|
inputs.target_arch == matrix.arch) &&
|
|
(github.event_name != 'workflow_dispatch' ||
|
|
inputs.target_image == 'all' ||
|
|
inputs.target_image == 'web')
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: amd64
|
|
runner_label: build-amd64
|
|
platform: linux/amd64
|
|
- arch: arm64
|
|
runner_label: build-arm64
|
|
platform: linux/arm64
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Docker Buildx
|
|
if: needs.metadata.outputs.registry_plain_http != 'true'
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Setup Docker Buildx For Plain HTTP Registry
|
|
if: needs.metadata.outputs.registry_plain_http == 'true'
|
|
uses: docker/setup-buildx-action@v3
|
|
with:
|
|
buildkitd-config-inline: |
|
|
[registry."${{ needs.metadata.outputs.registry }}"]
|
|
http = true
|
|
insecure = true
|
|
|
|
- name: Configure OCI registry auth
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
GITHUB_TOKEN_FALLBACK: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
username="${REGISTRY_USERNAME:-${{ github.actor }}}"
|
|
password="${REGISTRY_PASSWORD:-${GITHUB_TOKEN_FALLBACK:-}}"
|
|
|
|
if [ -z "$password" ]; then
|
|
echo "Set CONTAINER_REGISTRY_PASSWORD or enable GITHUB_TOKEN package writes"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$HOME/.docker"
|
|
auth="$(printf '%s:%s' "$username" "$password" | base64 | tr -d '\n')"
|
|
|
|
cat > "$HOME/.docker/config.json" <<EOF
|
|
{
|
|
"auths": {
|
|
"${{ needs.metadata.outputs.registry }}": {
|
|
"auth": "${auth}"
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
- name: Push architecture image
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
image_ref="${{ needs.metadata.outputs.registry }}/${{ needs.metadata.outputs.namespace }}/attune/web:${{ needs.metadata.outputs.image_tag }}-${{ matrix.arch }}"
|
|
|
|
build_cmd=(
|
|
docker buildx build
|
|
.
|
|
--platform "${{ matrix.platform }}"
|
|
--file docker/Dockerfile.web
|
|
)
|
|
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
build_cmd+=(--output "type=image,\"name=${image_ref}\",push=true,registry.insecure=true")
|
|
else
|
|
build_cmd+=(--tag "$image_ref" --push)
|
|
fi
|
|
|
|
"${build_cmd[@]}"
|
|
|
|
- name: Link web container package to repository
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
api_base="${{ github.server_url }}/api/v1"
|
|
|
|
status_code="$(curl -sS -o /tmp/package-link-response.txt -w '%{http_code}' -X POST \
|
|
-u "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD}" \
|
|
"${api_base}/packages/${{ needs.metadata.outputs.namespace }}/container/attune/web/-/link/${REPOSITORY_NAME}")"
|
|
|
|
case "${status_code}" in
|
|
200|201|204|409)
|
|
;;
|
|
*)
|
|
cat /tmp/package-link-response.txt
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
publish-manifests:
|
|
name: Publish manifest ${{ matrix.repository }}
|
|
runs-on: build-amd64
|
|
needs:
|
|
- metadata
|
|
- publish-rust-images
|
|
- publish-web-images
|
|
if: |
|
|
github.event_name != 'workflow_dispatch' ||
|
|
(inputs.target_arch == 'all' && inputs.target_image == 'all')
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
repository:
|
|
- attune/api
|
|
- attune/executor
|
|
- attune/notifier
|
|
- attune/agent
|
|
- attune/web
|
|
steps:
|
|
- name: Configure OCI registry auth
|
|
shell: bash
|
|
env:
|
|
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
|
GITHUB_TOKEN_FALLBACK: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
username="${REGISTRY_USERNAME:-${{ github.actor }}}"
|
|
password="${REGISTRY_PASSWORD:-${GITHUB_TOKEN_FALLBACK:-}}"
|
|
|
|
if [ -z "$password" ]; then
|
|
echo "Set CONTAINER_REGISTRY_PASSWORD or enable GITHUB_TOKEN package writes"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$HOME/.docker"
|
|
auth="$(printf '%s:%s' "$username" "$password" | base64 | tr -d '\n')"
|
|
|
|
cat > "$HOME/.docker/config.json" <<EOF
|
|
{
|
|
"auths": {
|
|
"${{ needs.metadata.outputs.registry }}": {
|
|
"auth": "${auth}"
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
- name: Publish manifest tags
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
image_base="${{ needs.metadata.outputs.registry }}/${{ needs.metadata.outputs.namespace }}/${{ matrix.repository }}"
|
|
create_args=()
|
|
push_args=()
|
|
|
|
if [ "${{ needs.metadata.outputs.registry_plain_http }}" = "true" ]; then
|
|
create_args+=(--insecure)
|
|
push_args+=(--insecure)
|
|
fi
|
|
|
|
IFS=',' read -ra tags <<< "${{ needs.metadata.outputs.image_tags }}"
|
|
for tag in "${tags[@]}"; do
|
|
manifest_ref="${image_base}:${tag}"
|
|
amd64_ref="${image_base}:${{ needs.metadata.outputs.image_tag }}-amd64"
|
|
arm64_ref="${image_base}:${{ needs.metadata.outputs.image_tag }}-arm64"
|
|
|
|
docker manifest rm "$manifest_ref" >/dev/null 2>&1 || true
|
|
docker manifest create "${create_args[@]}" "$manifest_ref" "$amd64_ref" "$arm64_ref"
|
|
docker manifest annotate "$manifest_ref" "$amd64_ref" --os linux --arch amd64
|
|
docker manifest annotate "$manifest_ref" "$arm64_ref" --os linux --arch arm64
|
|
docker manifest push "${push_args[@]}" "$manifest_ref"
|
|
done
|