diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9072c6a..949794c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,157 +9,16 @@ on: - "v*" pull_request: -permissions: - contents: read - concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - test: - name: Test - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - - - name: Format - run: cargo fmt --all -- --check - - - name: Clippy - run: cargo clippy --all-targets -- -D warnings - - - name: Test - run: cargo test --locked - - build: - name: Build musl (${{ matrix.name }}) - needs: test - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - - name: arm64 - runner: ubuntu-24.04-arm - rust_target: aarch64-unknown-linux-musl - dist_name: microdns-linux-arm64 - linker_env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER - cc_env: CC_aarch64_unknown_linux_musl - ar_env: AR_aarch64_unknown_linux_musl - - name: amd64 - runner: ubuntu-latest - rust_target: x86_64-unknown-linux-musl - dist_name: microdns-linux-amd64 - linker_env: CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER - cc_env: CC_x86_64_unknown_linux_musl - ar_env: AR_x86_64_unknown_linux_musl - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.rust_target }} - - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.name }}-musl - - - name: Install musl toolchain (apt) - run: | - set -euo pipefail - sudo apt-get update - sudo apt-get install -y musl-tools musl-dev - musl-gcc --version - - - name: Build static binary + package dist - run: | - set -euo pipefail - export ${{ matrix.linker_env }}=musl-gcc - export ${{ matrix.cc_env }}=musl-gcc - export ${{ matrix.ar_env }}=ar - export RUSTFLAGS='-C target-feature=+crt-static' - export MICRODNS_GIT_COMMIT="${GITHUB_SHA}" - export MICRODNS_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" - - cargo build --release --target ${{ matrix.rust_target }} - - mkdir -p dist - cp -f "target/${{ matrix.rust_target }}/release/microdns" "dist/${{ matrix.dist_name }}" - chmod 755 "dist/${{ matrix.dist_name }}" - - if [ "${{ matrix.name }}" = "arm64" ]; then - ( - cd dist - sha256sum microdns-linux-arm64 > SHA256SUMS - ) - fi - - file "dist/${{ matrix.dist_name }}" - if ldd "dist/${{ matrix.dist_name }}" 2>&1 | grep -qi 'not a dynamic\|statically linked'; then - echo "static binary OK: dist/${{ matrix.dist_name }}" - else - echo "warning: ldd output:" >&2 - ldd "dist/${{ matrix.dist_name }}" >&2 || true - fi - ls -lh "dist/${{ matrix.dist_name }}" - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: binaries-${{ matrix.name }} - path: dist/ - if-no-files-found: error - retention-days: 90 - - publish-oci: - name: Publish OCI artifact to GHCR (ORAS) - needs: build - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') - runs-on: ubuntu-latest + ci: permissions: contents: read - packages: write - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download arm64 binaries - uses: actions/download-artifact@v4 - with: - name: binaries-arm64 - path: dist/ - - - name: Set up ORAS - uses: oras-project/setup-oras@v1 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish microdns-linux-arm64 - run: ./scripts/publish-oci-microdns.sh dist/microdns-linux-arm64 - - - name: Job summary - run: | - { - echo "### OCI publish (ORAS)" - echo "" - echo "- Image: \`ghcr.io/dcc-bigfred/microdns-linux-arm64\`" - echo "- Tags: \`main\`, \`sha-${GITHUB_SHA::7}\`" - } >> "$GITHUB_STEP_SUMMARY" + uses: dcc-bigfred/common/.github/workflows/rust-musl-ci.yml@v2 + with: + binaries: '[{"name":"microdns","dist":"microdns-linux"}]' + build_env: | + MICRODNS_GIT_COMMIT=${{ github.sha }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fd5874c..1369fd9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,125 +5,13 @@ on: tags: - "v*" -permissions: - contents: write - packages: write - jobs: release: - name: Publish GitHub release + OCI tags - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Wait for CI build artifacts - id: ci - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - SHA="${GITHUB_SHA}" - DEADLINE=$(( $(date +%s) + 1800 )) - RUN_ID="" - - while [ "$(date +%s)" -lt "$DEADLINE" ]; do - RUN_JSON="$(gh run list --workflow=ci.yml --commit="$SHA" --limit=1 --json databaseId,status,conclusion)" - if [ "$RUN_JSON" = "[]" ]; then - echo "CI workflow run for ${SHA} not found yet; retrying..." - sleep 15 - continue - fi - - STATUS="$(echo "$RUN_JSON" | jq -r '.[0].status')" - CONCLUSION="$(echo "$RUN_JSON" | jq -r '.[0].conclusion')" - RUN_ID="$(echo "$RUN_JSON" | jq -r '.[0].databaseId')" - - if [ "$STATUS" = "completed" ]; then - if [ "$CONCLUSION" != "success" ]; then - echo "CI workflow run ${RUN_ID} finished with conclusion: ${CONCLUSION}" - exit 1 - fi - echo "CI workflow run ${RUN_ID} succeeded" - echo "run_id=${RUN_ID}" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "CI workflow run ${RUN_ID} status=${STATUS}; waiting..." - sleep 15 - done - - echo "Timed out after 30 minutes waiting for CI workflow on commit ${SHA}" - exit 1 - - - name: Download CI binaries (linux arm64) - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - mkdir -p release-assets - gh run download "${{ steps.ci.outputs.run_id }}" \ - --name binaries-arm64 \ - --dir release-assets - - - name: Download CI binaries (linux amd64) - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - mkdir -p release-assets - gh run download "${{ steps.ci.outputs.run_id }}" \ - --name binaries-amd64 \ - --dir release-assets/amd64 - find release-assets/amd64 -type f -exec cp -f {} release-assets/ \; - rm -rf release-assets/amd64 - - - name: Create release if missing - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - TAG="${GITHUB_REF_NAME}" - if ! gh release view "$TAG" >/dev/null 2>&1; then - gh release create "$TAG" \ - --title "$TAG" \ - --generate-notes - else - echo "Release ${TAG} already exists" - fi - - - name: Upload binaries to release - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - TAG="${GITHUB_REF_NAME}" - mapfile -d '' FILES < <(find release-assets -type f -print0) - if [ "${#FILES[@]}" -eq 0 ]; then - echo "No release assets found" - exit 1 - fi - gh release upload "$TAG" "${FILES[@]}" --clobber - - - name: Set up ORAS - uses: oras-project/setup-oras@v1 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Retag microdns ORAS artifact from main - run: ./scripts/retag-oci-microdns.sh "${GITHUB_REF_NAME}" - - - name: Job summary - run: | - { - echo "### Release ${GITHUB_REF_NAME}" - echo "" - echo "- GitHub Release assets uploaded (linux arm64 + amd64)" - echo "- ORAS: \`ghcr.io/dcc-bigfred/microdns-linux-arm64:${GITHUB_REF_NAME}\`" - echo "- ORAS: \`ghcr.io/dcc-bigfred/microdns-linux-arm64:latest-release\`" - } >> "$GITHUB_STEP_SUMMARY" + permissions: + contents: write + uses: dcc-bigfred/common/.github/workflows/rust-release.yml@v2 + with: + elf_binaries: microdns-linux-arm64 + elf_section: .microdns.version + ci_workflow: ci.yml + secrets: inherit diff --git a/.gitignore b/.gitignore index 4c4cfd8..cfe3178 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /target/ /dist/ +/.ci-github/ **/*.rs.bk *.swp *.swo diff --git a/Cargo.toml b/Cargo.toml index 9d93942..80d0aff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,11 @@ strip = true panic = "abort" overflow-checks = true +[profile.release-assertions] +inherits = "release" +debug-assertions = true +overflow-checks = true + [lints.rust] unused_must_use = "deny" diff --git a/Makefile b/Makefile index 4804ff2..24463cd 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ CARGO ?= cargo RUSTUP_TOOLCHAIN ?= stable export RUSTUP_TOOLCHAIN -.PHONY: all build release release-musl check test clean fmt clippy +.PHONY: all build release release-musl check test test-release-assertions \ + clean fmt clippy all: build @@ -28,6 +29,9 @@ check: test: $(CARGO) test +test-release-assertions: + $(CARGO) test --profile release-assertions + fmt: $(CARGO) fmt diff --git a/scripts/inject-elf-version.sh b/scripts/inject-elf-version.sh deleted file mode 100755 index 88698ed..0000000 --- a/scripts/inject-elf-version.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# Inject release version metadata into an ELF binary as section -# .microdns.version (JSON {"version":"v1.2.3","commit":"abc1234"}). -# Usage: inject-elf-version.sh -set -euo pipefail - -BINARY="${1:?usage: $0 }" -VERSION="${2:?}" -TAG_COMMIT="${3:?}" - -if [[ ! -f "${BINARY}" ]]; then - echo "error: binary not found: ${BINARY}" >&2 - exit 1 -fi - -if ! command -v objcopy >/dev/null 2>&1; then - echo "error: objcopy not found (install binutils)" >&2 - exit 1 -fi - -tmpdir="$(mktemp -d)" -cleanup() { rm -rf "${tmpdir}"; } -trap cleanup EXIT - -section_file="${tmpdir}/microdns.version.json" -printf '{"version":"%s","commit":"%s"}' "${VERSION}" "${TAG_COMMIT}" > "${section_file}" - -objcopy --remove-section .microdns.version "${BINARY}" 2>/dev/null || true -objcopy --add-section ".microdns.version=${section_file}" "${BINARY}" - -echo "Injected .microdns.version into ${BINARY}: version=${VERSION} commit=${TAG_COMMIT}" diff --git a/scripts/publish-oci-microdns.sh b/scripts/publish-oci-microdns.sh deleted file mode 100755 index b4dbefa..0000000 --- a/scripts/publish-oci-microdns.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -# Publish microdns linux/arm64 OCI artifact to GHCR (ORAS). -# Intended for CI on push to main/master only. -# Usage: publish-oci-microdns.sh -# -# Tags: main, sha-<7> -set -euo pipefail - -BIN="${1:?usage: $0 }" -IMAGE="${MICRODNS_OCI_IMAGE:-ghcr.io/dcc-bigfred/microdns-linux-arm64}" -BIN_MEDIA_TYPE="application/vnd.dcc-bigfred.microdns.linux.arm64.v1" - -if [[ ! -f "${BIN}" ]]; then - echo "error: binary not found: ${BIN}" >&2 - exit 1 -fi - -BRANCH="${GITHUB_REF_NAME:?GITHUB_REF_NAME required}" -if [[ "${BRANCH}" != "master" && "${BRANCH}" != "main" ]]; then - echo "error: OCI publish is only allowed from master/main (got ${BRANCH})" >&2 - exit 1 -fi - -SHA_TAG="sha-${GITHUB_SHA::7}" - -tmpdir="$(mktemp -d)" -cleanup() { rm -rf "${tmpdir}"; } -trap cleanup EXIT - -cp -f "${BIN}" "${tmpdir}/microdns-linux-arm64" -chmod 755 "${tmpdir}/microdns-linux-arm64" - -annotate=( - --annotation "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" - --annotation "org.opencontainers.image.revision=${GITHUB_SHA}" - --annotation "org.opencontainers.image.title=microdns" -) - -echo "Publishing ${IMAGE}:main and :${SHA_TAG}" -echo " microdns: $(wc -c < "${tmpdir}/microdns-linux-arm64") bytes" -( - cd "${tmpdir}" - oras push "${IMAGE}:main" "microdns-linux-arm64:${BIN_MEDIA_TYPE}" "${annotate[@]}" - oras push "${IMAGE}:${SHA_TAG}" "microdns-linux-arm64:${BIN_MEDIA_TYPE}" "${annotate[@]}" -) diff --git a/scripts/retag-oci-microdns.sh b/scripts/retag-oci-microdns.sh deleted file mode 100755 index 817b8c5..0000000 --- a/scripts/retag-oci-microdns.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -# Retag the microdns linux/arm64 OCI artifact from :main to a release tag and -# latest-release. Injects .microdns.version ELF section. -# Usage: retag-oci-microdns.sh e.g. v0.1.0 -set -euo pipefail - -RELEASE_TAG="${1:?usage: $0 }" -IMAGE="${MICRODNS_OCI_IMAGE:-ghcr.io/dcc-bigfred/microdns-linux-arm64}" -BIN_MEDIA_TYPE="application/vnd.dcc-bigfred.microdns.linux.arm64.v1" -TAG_COMMIT="${GITHUB_SHA:?GITHUB_SHA required (tag commit)}" - -tmpdir="$(mktemp -d)" -cleanup() { rm -rf "${tmpdir}"; } -trap cleanup EXIT - -echo "Pulling ${IMAGE}:main…" -oras pull "${IMAGE}:main" -o "${tmpdir}" - -find_layer() { - local want="$1" - if [[ -f "${tmpdir}/${want}" ]]; then - echo "${want}" - return 0 - fi - mapfile -t files < <(find "${tmpdir}" -type f \ - ! -name 'manifest.json' ! -name 'config.json' \ - -name "${want}" -printf '%f\n') - if [[ ${#files[@]} -eq 1 ]]; then - echo "${files[0]}" - return 0 - fi - return 1 -} - -BIN_NAME="$(find_layer microdns-linux-arm64)" || true -if [[ -z "${BIN_NAME}" ]]; then - echo "error: expected microdns-linux-arm64 in OCI artifact, found:" >&2 - find "${tmpdir}" -type f >&2 - exit 1 -fi - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -TAG_COMMIT_SHORT="${TAG_COMMIT:0:7}" -"${SCRIPT_DIR}/inject-elf-version.sh" "${tmpdir}/${BIN_NAME}" "${RELEASE_TAG}" "${TAG_COMMIT_SHORT}" - -annotate=( - --annotation "org.opencontainers.image.source=${GITHUB_SERVER_URL:-https://github.com}/${GITHUB_REPOSITORY:-dcc-bigfred/microdns}" - --annotation "org.opencontainers.image.revision=${TAG_COMMIT}" - --annotation "org.opencontainers.image.version=${RELEASE_TAG}" - --annotation "org.opencontainers.image.title=microdns" -) - -echo "Publishing ${IMAGE}:${RELEASE_TAG} and :latest-release" -echo " microdns: $(wc -c < "${tmpdir}/${BIN_NAME}") bytes" -( - cd "${tmpdir}" - oras push "${IMAGE}:${RELEASE_TAG}" "${BIN_NAME}:${BIN_MEDIA_TYPE}" "${annotate[@]}" - oras push "${IMAGE}:latest-release" "${BIN_NAME}:${BIN_MEDIA_TYPE}" "${annotate[@]}" -) diff --git a/src/version.rs b/src/version.rs index 7f018a2..53af99a 100644 --- a/src/version.rs +++ b/src/version.rs @@ -11,7 +11,7 @@ use std::fs; use std::path::Path; use std::sync::OnceLock; -/// ELF section name (must match `scripts/inject-elf-version.sh`). +/// ELF section name (must match org `.github` `inject-elf-version.sh` section arg). pub const SECTION_NAME: &str = ".microdns.version"; /// Public version payload returned by `microdns info` / `--version`.