Skip to content

Add CoCo provisioning, attestation integration, and deployment guides #26234

Add CoCo provisioning, attestation integration, and deployment guides

Add CoCo provisioning, attestation integration, and deployment guides #26234

Workflow file for this run

# Copyright (c) 2022-2026, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: pre-merge
on:
# quick tests for pull requests and the releasing branches
push:
pull_request:
workflow_dispatch:
jobs:
skill-validation-selection:
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
run-tier1-security: ${{ steps.select.outputs.run-tier1-security }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: select
name: Select Tier 1 skill security validation
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
EVENT_NAME: ${{ github.event_name }}
run: |
# A manual dispatch deliberately runs the gate. For push and pull-request
# events, restrict the expensive scans to changes that can affect shipped
# skills or this gate's implementation and dependencies.
if [ "$EVENT_NAME" = "workflow_dispatch" ] || [ -z "$BASE_SHA" ] || \
[ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
echo "run-tier1-security=true" >> "$GITHUB_OUTPUT"
exit 0
fi
selection="$(git diff --name-only "$BASE_SHA" "$GITHUB_SHA" | bash ci/should_run_skill_tier1.sh)"
echo "run-tier1-security=${selection}" >> "$GITHUB_OUTPUT"
license-check:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Run license check
run: python3 ci/check_license_header.py
style-check:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.14
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Run style checks
run: ./runtest.sh --torch-backend=cpu -s
unit-tests:
needs: skill-validation-selection
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os: [ ubuntu-22.04, ubuntu-24.04 ]
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.14"
- name: Install dependencies
run: |
./runtest.sh --torch-backend=cpu -p
uv pip install --system --no-deps "flwr>=1.16,<1.26"
# Installed outside NVFlare's development environment because SkillEvaluator
# requires click>=8.3.3 while NVFlare pins click==8.1.7 below Python 3.14.
# Keep every Tier 1 static scanner explicit: a missing scanner must fail the
# test rather than silently reduce security coverage.
- name: Install Tier 1 skill security scanners
if: ${{ matrix.os == 'ubuntu-24.04' && matrix.python-version == '3.13' && needs.skill-validation-selection.outputs.run-tier1-security == 'true' }}
run: |
mkdir -p "$HOME/.local/bin"
uv tool install --python 3.13 \
"skillevaluator[security] @ git+https://github.com/NVIDIA/SkillEvaluator.git@4975c97d49e3623eeab739248e52d83c4aa8f582"
uv tool install --python 3.13 "semgrep>=1,<2"
uv tool install --python 3.13 \
"skillspector @ git+https://github.com/NVIDIA/SkillSpector.git@fd25398d7aa99353d86237b9c260759351f0e644"
GITLEAKS_VERSION=8.30.0
GITLEAKS_SHA256=79a3ab579b53f71efd634f3aaf7e04a0fa0cf206b7ed434638d1547a2470a66e
GITLEAKS_ARCHIVE="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
curl --fail --location --silent --show-error \
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/${GITLEAKS_ARCHIVE}" \
--output "${GITLEAKS_ARCHIVE}"
echo "${GITLEAKS_SHA256} ${GITLEAKS_ARCHIVE}" | sha256sum --check -
tar --extract --gzip --file "${GITLEAKS_ARCHIVE}" gitleaks
install -m 0755 gitleaks "$HOME/.local/bin/gitleaks"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Run unit test
env:
NVFLARE_RUN_SKILL_TIER1: ${{ needs.skill-validation-selection.outputs.run-tier1-security }}
NVFLARE_SKILL_TIER1_REQUIRED: ${{ matrix.os == 'ubuntu-24.04' && matrix.python-version == '3.13' && needs.skill-validation-selection.outputs.run-tier1-security == 'true' }}
run: ./runtest.sh --skip-install --numprocesses=auto -u
- name: Run Client API external-process lifecycle integration
if: ${{ matrix.os == 'ubuntu-24.04' && matrix.python-version == '3.12' }}
run: >-
python3 -m pytest -q
tests/integration_test/fast/client_api_external_process_lifecycle_test.py
hf-client-api-tests:
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- name: transformers-min
extra_deps: '"transformers>=4.40,<4.41" accelerate'
test_command: python3 -m pytest -q tests/unit_test/app_opt/hf/real_transformers_contract_test.py
- name: transformers-latest
extra_deps: 'transformers accelerate'
test_command: python3 -m pytest -q tests/unit_test/app_opt/hf/real_transformers_contract_test.py
- name: trl-018-example
extra_deps: '"trl>=0.18,<0.19" datasets peft'
test_command: python3 -m py_compile examples/hello-world/hello-huggingface/client.py examples/hello-world/hello-huggingface/model.py examples/hello-world/hello-huggingface/prepare_data.py examples/hello-world/hello-huggingface/job.py && python3 examples/hello-world/hello-huggingface/client.py --help && python3 examples/hello-world/hello-huggingface/prepare_data.py --help && python3 examples/hello-world/hello-huggingface/job.py --help && python3 -m pytest -q tests/unit_test/app_opt/hf/real_trl_contract_test.py
- name: trl-latest-example
extra_deps: 'trl datasets peft'
test_command: python3 -m py_compile examples/hello-world/hello-huggingface/client.py examples/hello-world/hello-huggingface/model.py examples/hello-world/hello-huggingface/prepare_data.py examples/hello-world/hello-huggingface/job.py && python3 examples/hello-world/hello-huggingface/client.py --help && python3 examples/hello-world/hello-huggingface/prepare_data.py --help && python3 examples/hello-world/hello-huggingface/job.py --help && python3 -m pytest -q tests/unit_test/app_opt/hf/real_trl_contract_test.py
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.14"
- name: Install dependencies
run: |
./runtest.sh --torch-backend=cpu -p
uv pip install --system ${{ matrix.extra_deps }}
- name: Run HF Client API checks (${{ matrix.name }})
run: ${{ matrix.test_command }}
coverage:
runs-on: ubuntu-24.04
needs: unit-tests
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.14
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.14"
- name: Install dependencies
run: |
./runtest.sh --torch-backend=cpu -p
uv pip install --system --no-deps "flwr>=1.16,<1.26"
- name: Run unit test with coverage
run: python3 -m pytest --numprocesses=auto -v --cov=nvflare --cov-report xml:cov.xml --dist loadgroup tests/unit_test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
with:
files: ./cov.xml
flags: unit-tests
fail_ci_if_error: false
use_oidc: true
- name: Add coverage summary
if: always()
run: |
python3 - <<'PY'
import json
import os
import xml.etree.ElementTree as ET
from pathlib import Path
def pct(value):
return f"{float(value) * 100:.2f}%"
def ratio(covered, valid):
return f"{int(covered):,}/{int(valid):,}"
def row(name, attrs, rate_key, covered_key, valid_key):
rate = pct(attrs.get(rate_key, 0))
counts = ratio(attrs.get(covered_key, 0), attrs.get(valid_key, 0))
return f"| {name} | {rate} ({counts}) |"
repo = os.environ.get("GITHUB_REPOSITORY", "")
sha = os.environ.get("GITHUB_SHA", "")
event_path = os.environ.get("GITHUB_EVENT_PATH")
if event_path and Path(event_path).exists():
with open(event_path, "r", encoding="utf-8") as f:
event = json.load(f)
sha = event.get("pull_request", {}).get("head", {}).get("sha", sha)
lines = ["### Coverage summary", ""]
cov_path = Path("cov.xml")
if cov_path.exists():
root = ET.parse(cov_path).getroot()
lines.extend([
"| Metric | Value |",
"| --- | ---: |",
row("Line coverage", root.attrib, "line-rate", "lines-covered", "lines-valid"),
])
if int(root.attrib.get("branches-valid", 0)):
lines.append(
row("Branch coverage", root.attrib, "branch-rate", "branches-covered", "branches-valid")
)
if repo and sha:
lines.extend(["", f"[View Codecov report](https://app.codecov.io/gh/{repo}/commit/{sha})"])
else:
lines.append("Coverage report `cov.xml` was not found.")
summary = "\n".join(lines) + "\n"
summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
if summary_path:
with open(summary_path, "a", encoding="utf-8") as f:
f.write(summary)
else:
print(summary)
PY
wheel-build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-24.04 ]
python-version: [ "3.11" ]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.14"
- name: Install dependencies
run: |
./runtest.sh --torch-backend=cpu -p
uv pip install --system --no-deps "flwr>=1.16,<1.26"
uv pip install --system build twine
- name: Run wheel build
run: python3 -m build --wheel