forked from confidential-containers/cloud-api-adaptor
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: port podvm build/deploy workflows from fortress #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alhassankhedr-cohere
merged 15 commits into
cohere
from
alhassankhedr/cc-192-cloud-api-adaptor-port-podvm-builddeploy-workflows-from
Apr 22, 2026
Merged
Changes from 4 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
fa349fa
feat: port podvm build/deploy workflows from fortress
alhassankhedr-cohere 844b0ec
fix: address zizmor workflow security findings
alhassankhedr-cohere 65a2d56
chore: use cvm-measure feature branch until PR #12 merges
alhassankhedr-cohere 464c0f2
fix: group GITHUB_OUTPUT redirects to satisfy shellcheck SC2129
alhassankhedr-cohere a31d854
test: temporarily add feature branch to push trigger
alhassankhedr-cohere 81ff7f7
fix: pass GITHUB_TOKEN to cvm-measure clone
alhassankhedr-cohere 8949b4b
fix: use CVM_MEASURE_TOKEN to clone private cvm-measure repo
alhassankhedr-cohere 8fed5be
feat: always deploy to GCP, overwrite on branch pushes
alhassankhedr-cohere 26587fc
feat: build both release and debug images in parallel
alhassankhedr-cohere 711031d
fix: add --break-system-packages to pip install for Ubuntu 24.04
alhassankhedr-cohere d5f53dc
fix: quote variable in git clone URL to satisfy shellcheck SC2086
alhassankhedr-cohere d4cf7d6
chore: add TODO reminders for pre-merge cleanup
alhassankhedr-cohere bde0a9f
Merge branch 'cohere' into alhassankhedr/cc-192-cloud-api-adaptor-por…
alhassankhedr-cohere 098eb08
refactor: combine deploy-gcp-release/debug into a single matrix job
alhassankhedr-cohere 5f6b5c5
fix: respect replace_existing_image input on workflow_dispatch
alhassankhedr-cohere File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,334 @@ | ||
| name: Build PodVM Image (Cohere) | ||
|
|
||
| on: | ||
| push: | ||
| tags: ["podvm-v*"] | ||
| branches: ["cohere"] | ||
| workflow_dispatch: | ||
| inputs: | ||
| image_profile: | ||
| description: "Image profile" | ||
| required: false | ||
| type: choice | ||
| options: [release, debug] | ||
| default: release | ||
| distro: | ||
| description: "PodVM distro" | ||
| required: false | ||
| type: choice | ||
| options: [ubuntu, fedora] | ||
| default: ubuntu | ||
| ssh_public_key: | ||
| description: "SSH public key for debug images (optional)" | ||
| required: false | ||
| type: string | ||
| guest_components_repo: | ||
| description: "guest-components repo (default: cohere-ai/guest-components)" | ||
| required: false | ||
| type: string | ||
| default: "https://github.com/cohere-ai/guest-components.git" | ||
| guest_components_ref: | ||
| description: "guest-components ref (default: cohere)" | ||
| required: false | ||
| type: string | ||
| default: "cohere" | ||
| custom_gc_binaries: | ||
| description: "guest-components binaries to build from source" | ||
| required: false | ||
| type: string | ||
| default: "attestation-agent,api-server-rest" | ||
| aa_features: | ||
| description: "attestation-agent cargo features" | ||
| required: false | ||
| type: string | ||
| default: "bin,ttrpc,kbs,coco_as,rust-crypto,tdx-attester,nvidia-attester" | ||
| deploy_gcp: | ||
| description: "Deploy to GCP after build" | ||
| required: false | ||
| type: boolean | ||
| default: true | ||
| replace_existing_image: | ||
| description: "Replace existing GCP image if it exists" | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
|
|
||
| permissions: | ||
| id-token: write # OIDC token for build provenance attestation | ||
| attestations: write # actions/attest-build-provenance | ||
|
alhassankhedr-cohere marked this conversation as resolved.
Dismissed
|
||
| contents: read # actions/checkout | ||
| packages: write # push OCI artifact to GHCR | ||
|
alhassankhedr-cohere marked this conversation as resolved.
Dismissed
|
||
|
|
||
| env: | ||
| TEE_PLATFORM: tdx | ||
| OCI_IMAGE: ghcr.io/${{ github.repository }}/podvm | ||
| VERSIONS_YAML: src/cloud-api-adaptor/versions.yaml | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build PodVM OS Image | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| image_name: ${{ steps.meta.outputs.image_name }} | ||
| image_tag: ${{ steps.meta.outputs.image_tag }} | ||
| oci_ref: ${{ steps.push.outputs.oci_ref }} | ||
| oci_digest: ${{ steps.push.outputs.digest }} | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Free up runner disk space | ||
| run: | | ||
| echo "=== Before cleanup ===" | ||
| df -h / | ||
| sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc \ | ||
| /usr/local/share/boost /opt/hostedtoolcache/CodeQL | ||
| sudo apt-get purge -y google-cloud-cli azure-cli microsoft-edge-stable \ | ||
| dotnet-* aspnetcore-* 2>/dev/null || true | ||
| sudo apt-get autoremove -y | ||
| echo "=== After cleanup ===" | ||
| df -h / | ||
|
|
||
| - name: Install system dependencies | ||
| run: | | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y --no-install-recommends \ | ||
| alien binutils bubblewrap dnf git make mtools qemu-utils uidmap | ||
| sudo apt-get clean | ||
| sudo rm -rf /var/lib/apt/lists/* | ||
|
|
||
| command -v yq || sudo snap install yq | ||
| yq --version | ||
|
|
||
| - name: Tune kernel for mkosi | ||
| run: | | ||
| sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true | ||
|
|
||
| - name: Install ORAS | ||
| run: | | ||
| ORAS_VERSION=$(yq '.tools.oras' "$VERSIONS_YAML") | ||
| curl -fsSLO "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz" | ||
| tar -xzf "oras_${ORAS_VERSION}_linux_amd64.tar.gz" oras | ||
| rm -f "oras_${ORAS_VERSION}_linux_amd64.tar.gz" | ||
| sudo mv oras /usr/local/bin/ | ||
| oras version | ||
|
|
||
| - name: Install cvm-measure | ||
| run: | | ||
| git clone --depth 1 --branch alhassankhedr/cc-167-tdx-measurement-toolkit \ | ||
|
alhassankhedr-cohere marked this conversation as resolved.
|
||
| https://github.com/cohere-ai/cvm-measure.git /tmp/cvm-measure | ||
|
alhassankhedr-cohere marked this conversation as resolved.
Outdated
|
||
| pip install /tmp/cvm-measure | ||
|
cursor[bot] marked this conversation as resolved.
Outdated
|
||
| rm -rf /tmp/cvm-measure | ||
| cvm-measure --version | ||
|
|
||
| - name: Compute image metadata | ||
| id: meta | ||
| env: | ||
| PROFILE: ${{ inputs.image_profile || 'release' }} | ||
| DISTRO: ${{ inputs.distro || 'ubuntu' }} | ||
| run: | | ||
| if [[ "$GITHUB_REF" == refs/tags/podvm-v* ]]; then | ||
| TAG="${GITHUB_REF#refs/tags/podvm-}" | ||
| else | ||
| TAG="dev-$(date +%Y%m%d)-${GITHUB_SHA::8}" | ||
| fi | ||
| TAG="${TAG//./-}" | ||
| IMAGE_NAME="podvm-${DISTRO}-${TEE_PLATFORM}-${PROFILE}-${TAG}" | ||
| OCI_TAG="${TAG}-${DISTRO}-${PROFILE}" | ||
| { | ||
| echo "image_name=$IMAGE_NAME" | ||
| echo "image_tag=$OCI_TAG" | ||
| echo "profile=$PROFILE" | ||
| echo "distro=$DISTRO" | ||
| echo "caa_commit=$GITHUB_SHA" | ||
| echo "caa_commit_short=${GITHUB_SHA::7}" | ||
| echo "caa_ref=${GITHUB_REF_NAME}" | ||
| } >> "$GITHUB_OUTPUT" | ||
| echo "Building: $IMAGE_NAME (tag: $TAG)" | ||
|
|
||
| - name: Inject SSH key for debug image | ||
| if: inputs.image_profile == 'debug' && inputs.ssh_public_key != '' | ||
| working-directory: src/cloud-api-adaptor/podvm-mkosi | ||
| env: | ||
| SSH_PUBLIC_KEY: ${{ inputs.ssh_public_key }} | ||
| run: | | ||
| mkdir -p resources | ||
| printf '%s\n' "$SSH_PUBLIC_KEY" > resources/authorized_keys | ||
| chmod 0400 resources/authorized_keys | ||
| echo "SSH key injected for debug image" | ||
|
|
||
| - name: Build binaries | ||
| working-directory: src/cloud-api-adaptor/podvm-mkosi | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| PODVM_DISTRO: ${{ steps.meta.outputs.distro }} | ||
| AA_FEATURES: ${{ inputs.aa_features || 'bin,ttrpc,kbs,coco_as,rust-crypto,tdx-attester,nvidia-attester' }} | ||
| GC_REPO: ${{ inputs.guest_components_repo || 'https://github.com/cohere-ai/guest-components.git' }} | ||
| GC_REF: ${{ inputs.guest_components_ref || 'cohere' }} | ||
| GC_CUSTOM_BINARIES: ${{ inputs.custom_gc_binaries || 'attestation-agent,api-server-rest' }} | ||
| run: | | ||
| MAKE_ARGS=( | ||
| "VERIFY_PROVENANCE=yes" | ||
| "TEE_PLATFORM=$TEE_PLATFORM" | ||
| "PODVM_DISTRO=$PODVM_DISTRO" | ||
| ) | ||
| if [ -n "$GC_CUSTOM_BINARIES" ]; then | ||
| MAKE_ARGS+=("CUSTOM_GC_BINARIES=$GC_CUSTOM_BINARIES") | ||
| MAKE_ARGS+=("GUEST_COMPONENTS_REPO=$GC_REPO") | ||
| MAKE_ARGS+=("GUEST_COMPONENTS_REF=$GC_REF") | ||
| [ -n "$AA_FEATURES" ] && MAKE_ARGS+=("AA_FEATURES=$AA_FEATURES") | ||
| fi | ||
| make "${MAKE_ARGS[@]}" binaries | ||
| docker system prune -af --volumes 2>/dev/null || true | ||
| echo "Disk after binaries build:" | ||
| df -h / | ||
|
|
||
| - name: Build OS image | ||
| working-directory: src/cloud-api-adaptor/podvm-mkosi | ||
| env: | ||
| PROFILE: ${{ steps.meta.outputs.profile }} | ||
| PODVM_DISTRO: ${{ steps.meta.outputs.distro }} | ||
| run: | | ||
| TARGET="image" | ||
| if [ "$PROFILE" = "debug" ]; then | ||
| TARGET="image-debug" | ||
| fi | ||
| TEE_PLATFORM="$TEE_PLATFORM" \ | ||
| PODVM_DISTRO="$PODVM_DISTRO" \ | ||
| make "$TARGET" | ||
|
|
||
| ls -lh build/ | ||
|
|
||
| - name: Free disk space | ||
| run: | | ||
| echo "=== Before cleanup ===" | ||
| df -h / | ||
| mv src/cloud-api-adaptor/podvm-mkosi/build/system.raw /tmp/disk.raw | ||
| docker system prune -af --volumes 2>/dev/null || true | ||
| echo "=== After cleanup ===" | ||
| df -h / | ||
|
|
||
| - name: Extract UKI and predict RTMR2 | ||
| id: measure | ||
| env: | ||
| IMAGE_NAME: ${{ steps.meta.outputs.image_name }} | ||
| CAA_COMMIT: ${{ steps.meta.outputs.caa_commit }} | ||
| CAA_REF: ${{ steps.meta.outputs.caa_ref }} | ||
| DISTRO: ${{ steps.meta.outputs.distro }} | ||
| PROFILE: ${{ steps.meta.outputs.profile }} | ||
| run: | | ||
| cvm-measure extract-uki --disk /tmp/disk.raw --output /tmp/BOOTX64.EFI | ||
|
|
||
| RTMR2=$(python3 -c " | ||
| import hashlib | ||
| from cvm_measure.tdx.pe import pe_extract_section | ||
| from cvm_measure.tdx.rtmr import extend_rtmr, SHA384_SIZE | ||
| uki = open('/tmp/BOOTX64.EFI', 'rb').read() | ||
| sections = ['linux', 'osrel', 'cmdline', 'initrd', 'ucode', 'uname', 'sbat', 'pcrpkey'] | ||
| rtmr2 = bytes(SHA384_SIZE) | ||
| for name in sections: | ||
| data = pe_extract_section(uki, f'.{name}', use_virtual_size=True) | ||
| if data is None: | ||
| continue | ||
| name_hash = hashlib.sha384(f'.{name}\0'.encode('ascii')).digest() | ||
| rtmr2 = extend_rtmr(rtmr2, name_hash) | ||
| content_hash = hashlib.sha384(data).digest() | ||
| rtmr2 = extend_rtmr(rtmr2, content_hash) | ||
| print(rtmr2.hex()) | ||
| ") | ||
|
|
||
| KERNEL=$(python3 -c " | ||
| from cvm_measure.tdx.pe import pe_extract_section | ||
| data = pe_extract_section(open('/tmp/BOOTX64.EFI','rb').read(), '.uname', use_virtual_size=True) | ||
| print(data.decode(errors='replace').strip('\x00') if data else 'unknown') | ||
| ") | ||
|
|
||
| jq -n \ | ||
| --arg image_name "$IMAGE_NAME" \ | ||
| --arg rtmr2_sha384 "$RTMR2" \ | ||
| --arg kernel "$KERNEL" \ | ||
| --arg caa_commit "$CAA_COMMIT" \ | ||
| --arg caa_ref "$CAA_REF" \ | ||
| --arg caa_version "$CAA_REF" \ | ||
| --arg distro "$DISTRO" \ | ||
| --arg profile "$PROFILE" \ | ||
| --arg tee_platform "$TEE_PLATFORM" \ | ||
| --arg build_date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | ||
| '$ARGS.named' > /tmp/measurements.json | ||
|
|
||
| cat /tmp/measurements.json | ||
|
|
||
| - name: Prepare artifacts | ||
| run: | | ||
| tar -czf /tmp/disk.tar.gz -C /tmp disk.raw | ||
| rm /tmp/disk.raw | ||
| echo "Compressed size: $(du -sh /tmp/disk.tar.gz | cut -f1)" | ||
|
|
||
| - name: Login to GHCR | ||
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Push artifact to GHCR | ||
| id: push | ||
| env: | ||
| OCI_TAG: ${{ steps.meta.outputs.image_tag }} | ||
| IMAGE_TITLE: ${{ steps.meta.outputs.image_name }} | ||
| DISTRO: ${{ steps.meta.outputs.distro }} | ||
| PROFILE: ${{ steps.meta.outputs.profile }} | ||
| CAA_COMMIT: ${{ steps.meta.outputs.caa_commit }} | ||
| run: | | ||
| OCI_REF="${OCI_IMAGE}:${OCI_TAG}" | ||
| RTMR2=$(jq -r '.rtmr2_sha384' /tmp/measurements.json) | ||
|
|
||
| cd /tmp | ||
| oras push "$OCI_REF" \ | ||
| disk.tar.gz:application/vnd.cohere.podvm.disk.tar+gzip \ | ||
| measurements.json:application/vnd.cohere.podvm.measurements+json \ | ||
| --annotation "org.opencontainers.image.title=${IMAGE_TITLE}" \ | ||
| --annotation "org.opencontainers.image.description=PodVM OS image (${DISTRO}/${TEE_PLATFORM}/${PROFILE})" \ | ||
| --annotation "org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}" \ | ||
| --annotation "org.opencontainers.image.revision=${GITHUB_SHA}" \ | ||
| --annotation "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | ||
| --annotation "com.cohere.caa.commit=${CAA_COMMIT}" \ | ||
| --annotation "com.cohere.caa.version=${GITHUB_REF_NAME}" \ | ||
| --annotation "com.cohere.rtmr2=${RTMR2}" \ | ||
| --format json > oras-output.json | ||
|
|
||
| cat oras-output.json | ||
| DIGEST=$(jq -r '.digest' oras-output.json) | ||
|
|
||
| { | ||
| echo "digest=$DIGEST" | ||
| echo "oci_ref=${OCI_REF}@${DIGEST}" | ||
| echo "oci_tag=$OCI_TAG" | ||
| } >> "$GITHUB_OUTPUT" | ||
| echo "Pushed: $OCI_REF @ $DIGEST" | ||
|
|
||
| - name: Attest build provenance | ||
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4 | ||
| with: | ||
| subject-name: ${{ env.OCI_IMAGE }} | ||
| subject-digest: ${{ steps.push.outputs.digest }} | ||
| push-to-registry: true | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Deploy to GCP (reusable workflow) — runs on tag pushes or when requested | ||
| # --------------------------------------------------------------------------- | ||
| deploy-gcp: | ||
| name: Create image | ||
| needs: build | ||
| if: | | ||
| always() && needs.build.result == 'success' && | ||
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/podvm-v') || | ||
| github.event_name == 'workflow_dispatch' && inputs.deploy_gcp) | ||
| uses: ./.github/workflows/deploy-gcp-cohere.yaml | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| with: | ||
| image_tag: ${{ needs.build.outputs.image_tag }} | ||
| replace_existing_image: ${{ inputs.replace_existing_image || false }} | ||
| secrets: | ||
| GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} | ||
| GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.