diff --git a/.github/workflows/PRBuild.yaml b/.github/workflows/PRBuild.yaml index 26b47bc..04f35fd 100644 --- a/.github/workflows/PRBuild.yaml +++ b/.github/workflows/PRBuild.yaml @@ -1,9 +1,7 @@ -# Copyright © 2025 OpenCHAMI a Series of LF Projects, LLC -# +# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC # SPDX-License-Identifier: MIT name: Build each PR for testing and validation - on: pull_request: branches: @@ -17,91 +15,56 @@ on: type: string permissions: write-all # Necessary for the generate-build-provenance action with containers - jobs: - build: - - + config: runs-on: ubuntu-latest - + outputs: + rpm-unsigned: ${{ steps.names.outputs.rpm-unsigned }} + rpm-signed: ${{ steps.names.outputs.rpm-signed }} + keys-public: ${{ steps.names.outputs.keys-public }} steps: - - name: Set up latest stable Go - uses: actions/setup-go@v6.4.0 - with: - go-version: stable - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - image=moby/buildkit:master - network=host - - name: Docker Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Checkout - uses: actions/checkout@v6.0.2 - with: - fetch-tags: 1 - fetch-depth: 0 - # Set environment variables required by GoReleaser - - name: Set build environment variables + - id: names run: | - echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV - echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV - echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV - echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV - echo "CGO_ENABLED=0" >> $GITHUB_ENV - echo "IS_PR_BUILD=true" >> $GITHUB_ENV + { + echo "rpm-unsigned=rpms-unsigned" + echo "rpm-signed=rpms-signed" + echo "keys-public=public-keys" + } >> "$GITHUB_OUTPUT" - - name: Docker Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + build: + uses: OpenCHAMI/github-actions/.github/workflows/build-publish-container-goreleaser.yml@dev-rpm-quadlets + secrets: inherit + with: + cgo_enabled: 0 + registry_subject_name: ghcr.io/openchami/metadata-service + is_pr_build: true + pr_number: ${{ inputs.pr_number || github.event.pull_request.number || 0 }} - - name: Create Tag for PR - if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.pr_number != '') - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - PR_NUM="${{ github.event.number }}" - if [[ "${{ inputs.pr_number }}" != "" ]]; then - PR_NUM="${{ inputs.pr_number }}" - fi - git tag -f -a pr-${PR_NUM} -m "PR Release" + rpmbuild: + needs: [config, build] + uses: OpenCHAMI/github-actions/.github/workflows/build-rpm-quadlet.yml@dev-rpm-quadlets + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ needs.config.outputs.rpm-unsigned }} - - name: Build/Push container with goreleaser - uses: goreleaser/goreleaser-action@v6 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - version: '~> 2' - args: release --clean --skip=announce,validate,archive - id: goreleaser - - name: Process goreleaser output - id: process_goreleaser_output - run: | - echo "const fs = require('fs');" > process.js - echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js - echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js - echo "console.log(firstNonNullDigest);" >> process.js - echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js - node process.js - echo "digest=$(cat digest.txt)" >> $GITHUB_OUTPUT - - name: Attest Binaries - uses: actions/attest-build-provenance@v4.1.0 - with: - subject-path: dist/** - - name: generate build provenance - uses: actions/attest-build-provenance@v4.1.0 - with: - subject-name: ghcr.io/openchami/metadata-service - subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} - push-to-registry: true + rpmsign: + needs: [config, rpmbuild] + uses: OpenCHAMI/github-actions/.github/workflows/gpg-sign-artifacts.yml@dev-rpm-quadlets + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ needs.config.outputs.rpm-unsigned }} + artifact-name-signed-rpms: ${{ needs.config.outputs.rpm-signed }} + artifact-name-public-keys: ${{ needs.config.outputs.keys-public }} + + rpmvalidate: + needs: [config, rpmsign] + uses: OpenCHAMI/github-actions/.github/workflows/validate-rpm-quadlet.yml@dev-rpm-quadlets + secrets: inherit + with: + artifact-name-signed-rpms: ${{ needs.config.outputs.rpm-signed }} + expected-files: | + /usr/share/containers/systemd/metadata-data.volume + /usr/share/containers/systemd/metadata-service.container + /usr/share/licenses/metadata-service + /usr/share/licenses/metadata-service/MIT.txt diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml index 24862a8..0db10e2 100644 --- a/.github/workflows/Release.yaml +++ b/.github/workflows/Release.yaml @@ -1,9 +1,7 @@ -# Copyright © 2025 OpenCHAMI a Series of LF Projects, LLC -# +# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC # SPDX-License-Identifier: MIT -name: Release with goreleaser - +name: Release on: workflow_dispatch: push: @@ -11,73 +9,41 @@ on: - v* permissions: write-all # Necessary for the generate-build-provenance action with containers +env: + ARTIFACT_NAME_RPM_UNSIGNED: 'rpms-unsigned' + ARTIFACT_NAME_RPM_SIGNED: 'rpms-signed' + ARTIFACT_NAME_GPG_KEYS_PUBLIC: 'gpg-public-keys' jobs: build: - - - runs-on: ubuntu-latest - - steps: - - name: Set up latest stable Go - uses: actions/setup-go@v6.4.0 - with: - go-version: stable - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - image=moby/buildkit:master - network=host - - name: Docker Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Checkout - uses: actions/checkout@v6.0.2 - with: - fetch-tags: 1 - fetch-depth: 0 - # Set environment variables required by GoReleaser - - name: Set build environment variables - run: | - echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV - echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV - echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV - echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV - echo "CGO_ENABLED=0" >> $GITHUB_ENV - echo "IS_PR_BUILD=false" >> $GITHUB_ENV - - - name: Release with goreleaser - uses: goreleaser/goreleaser-action@v6 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - version: latest - args: release --clean - id: goreleaser - - name: Process goreleaser output - id: process_goreleaser_output - run: | - echo "const fs = require('fs');" > process.js - echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js - echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js - echo "console.log(firstNonNullDigest);" >> process.js - echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js - node process.js - echo "digest=$(cat digest.txt)" >> $GITHUB_OUTPUT - - name: Attest Binaries - uses: actions/attest-build-provenance@v1 - with: - subject-path: dist/** - - name: generate build provenance - uses: actions/attest-build-provenance@v1 - with: - subject-name: ghcr.io/openchami/metadata-service - subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} - push-to-registry: true + uses: OpenCHAMI/github-actions/.github/workflows/build-publish-container-goreleaser.yml@dev-rpm-quadlets + secrets: inherit + with: + cgo_enabled: 0 + is_pr_build: false + registry_subject_name: ghcr.io/openchami/metadata-service + + rpmbuild: + needs: build + uses: OpenCHAMI/github-actions/.github/workflows/build-rpm-quadlet.yml@dev-rpm-quadlets + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ env.ARTIFACT_NAME_RPM_UNSIGNED }} + + rpmsign: + needs: rpmbuild + uses: OpenCHAMI/github-actions/.github/workflows/gpg-sign-artifacts.yml@dev-rpm-quadlets + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ env.ARTIFACT_NAME_RPM_UNSIGNED }} + artifact-name-signed-rpms: ${{ env.ARTIFACT_NAME_RPM_SIGNED }} + artifact-name-public-keys: ${{ env.ARTIFACT_NAME_GPG_KEYS_PUBLIC }} + + release: + needs: rpmsign + uses: OpenCHAMI/github-actions/.github/workflows/release-signed-artifacts.yml@dev-rpm-quadlets + secrets: inherit + with: + artifact-name-signed-rpms: ${{ env.ARTIFACT_NAME_RPM_SIGNED }} + artifact-name-public-keys: ${{ env.ARTIFACT_NAME_GPG_KEYS_PUBLIC }} diff --git a/Makefile b/Makefile index 4258ceb..2154957 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: MIT -.PHONY: help build test lint clean install run container-build container-run generate generate-check dev +.PHONY: help build test lint clean install run container-build container-run generate generate-check dev rpm-build rpm-clean # Variables BINARY_NAME=ochami-metadata @@ -15,6 +15,12 @@ DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") CONTAINER_PROG ?= $(shell command -v docker 2>/dev/null) CONTAINER_TAG ?= latest CONTAINER_GO_VERSION ?= $(shell awk '/^go / {print $$2; exit}' go.mod) +# RPM version/release: strip the leading 'v' and rewrite git-describe's +# '-N-gHASH[-dirty]' suffix into RPM-legal dot separators (hyphens aren't +# allowed in an RPM Version field). An exact tag like v0.1.2 becomes 0.1.2. +RPM_VERSION ?= $(shell echo "$(VERSION)" | sed -e 's/^v//' -e 's/-/./g') +RPM_RELEASE ?= 1 +RPM_TOPDIR ?= $(CURDIR)/dist/rpmbuild LDFLAGS=-ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)" FABRICA_CMD ?= go run github.com/openchami/fabrica/cmd/fabrica@latest FABRICA_SOURCE_ARG ?= @@ -102,6 +108,23 @@ container-run: container-build ## Build and run Docker container release-snapshot: ## Create a snapshot release with GoReleaser goreleaser release --snapshot --clean +rpm-build: ## Build the metadata-service RPM (VERSION/RPM_RELEASE override the derived defaults) + @command -v rpmbuild >/dev/null 2>&1 || { echo "rpmbuild is required but not installed."; exit 1; } + rm -rf $(RPM_TOPDIR) + mkdir -p $(RPM_TOPDIR)/SOURCES/metadata-service-$(RPM_VERSION)/LICENSES + cp packaging/systemd/* $(RPM_TOPDIR)/SOURCES/metadata-service-$(RPM_VERSION)/ + cp LICENSES/MIT.txt $(RPM_TOPDIR)/SOURCES/metadata-service-$(RPM_VERSION)/LICENSES/ + tar -C $(RPM_TOPDIR)/SOURCES -czf $(RPM_TOPDIR)/SOURCES/metadata-service-$(RPM_VERSION).tar.gz \ + metadata-service-$(RPM_VERSION) + rpmbuild --define "_topdir $(RPM_TOPDIR)" \ + --define "version $(RPM_VERSION)" \ + --define "rel $(RPM_RELEASE)" \ + -bb packaging/metadata-service.spec + @echo "Built: $(RPM_TOPDIR)/RPMS/noarch/$$(ls $(RPM_TOPDIR)/RPMS/noarch)" + +rpm-clean: ## Remove local RPM build artifacts + rm -rf $(RPM_TOPDIR) + fmt: ## Format code $(GO) fmt ./... goimports -w . diff --git a/packaging/metadata-service.spec b/packaging/metadata-service.spec new file mode 100644 index 0000000..e513ee2 --- /dev/null +++ b/packaging/metadata-service.spec @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT +# +# See `make rpm-build` and docs/RPM_PACKAGING.md for the tag-to-version +# mapping and how the packaged quadlet's image tag is pinned to it. + +Name: metadata-service +Version: %{version} +Release: %{rel}%{?dist} +Summary: OpenCHAMI metadata-service Quadlet units + +License: MIT +URL: https://github.com/OpenCHAMI/metadata-service +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch + + +# TODO: FIXME: metadata-service depends on smd and tokensmith, which do not +# exist as standalone RPMs yet. +# Requires: smd +# Requires: tokensmith +Requires: podman + +%description +Podman Quadlet unit files (container + volume) for running metadata-service +as part of an OpenCHAMI deployment. + +%prep +%setup -q + +%install +mkdir -p %{buildroot}/usr/share/containers/systemd + +sed "s|@IMAGE_TAG@|v%{version}|" metadata-service.container \ + > %{buildroot}/usr/share/containers/systemd/metadata-service.container +chmod 644 %{buildroot}/usr/share/containers/systemd/metadata-service.container + +install -m 644 metadata-data.volume \ + %{buildroot}/usr/share/containers/systemd/metadata-data.volume + + +%files +%license LICENSES/MIT.txt +/usr/share/containers/systemd/metadata-service.container +/usr/share/containers/systemd/metadata-data.volume + +%post +# reload systemd so the new Quadlet-generated unit is seen +systemctl daemon-reload + +%postun +# reload systemd so the removed unit is dropped +systemctl daemon-reload diff --git a/packaging/systemd/metadata-data.volume b/packaging/systemd/metadata-data.volume new file mode 100644 index 0000000..4060aeb --- /dev/null +++ b/packaging/systemd/metadata-data.volume @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT + +[Unit] +Description=metadata-service Data Volume + +[Volume] +VolumeName=metadata-service-data diff --git a/packaging/systemd/metadata-service.container b/packaging/systemd/metadata-service.container new file mode 100644 index 0000000..c7d8e33 --- /dev/null +++ b/packaging/systemd/metadata-service.container @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT + +[Unit] +Description=The metadata-service container +Requires=smd.service tokensmith.service +After=smd.service tokensmith.service +PartOf=openchami.target + +[Container] +ContainerName=metadata-service +HostName=metadata-service +Image=ghcr.io/openchami/metadata-service:@IMAGE_TAG@ + +# VOLUME +Volume=metadata-data.volume:/data + +# NETWORK +Network=openchami-internal.network + +# ENVIRONMENT +EnvironmentFile=/etc/openchami/configs/openchami.env +Secret=metadata-service-bootstrap-token,type=env,target=TOKENSMITH_BOOTSTRAP_TOKEN + +# EXTRAS +# for wireguard tunnel creation +AddCapability=cap_net_admin +AddDevice=/dev/net/tun + +# Proxy settings +PodmanArgs=--http-proxy=false + +# TODO: CHANGE ME TO USE THE CONFIG FILE +Exec=serve --tokensmith-url=http://tokensmith:8080 + +[Service] +ExecStartPre=/usr/sbin/tokensmith_bootstrap_token metadata-service +Restart=always