Files
concrete/.github/workflows/continuous-integration.yaml

898 lines
39 KiB
YAML

name: concrete-framework CI Pipeline
on:
pull_request:
push:
branches:
- main
tags:
- "v*"
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
inputs:
rebuild-env-docker:
description: "String to indicate whether the docker should be rebuilt, true or false"
required: true
default: "false"
# Allows external webhook trigger
repository_dispatch:
types:
- rebuild-env-docker
schedule:
# * is a special character in YAML so you have to quote this string
# At 22:00 on Sunday
# Timezone is UTC, so Paris time is +2 during the summer and +1 during winter
- cron: '0 22 * * 0'
env:
FORCE_REBUILD_DOCKER: ${{ (github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.rebuild-env-docker)) || (github.event_name == 'repository_dispatch' && github.event.action == 'rebuild-env-docker') }}
ENV_DOCKERFILE: docker/Dockerfile.concrete-framework-env
PREFLIGHT_IMAGE_BASE: ghcr.io/zama-ai/concrete-framework-env:preflight
LATEST_IMAGE: ghcr.io/zama-ai/concrete-framework-env:latest
BASE_IMAGE: ghcr.io/zama-ai/concrete-framework-env
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
IS_PR: ${{ github.event_name == 'pull_request' }}
jobs:
build-preflight-docker:
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
name: Build & Push the concrete-framework-env preflight Docker Image
runs-on: ubuntu-20.04
outputs:
image: ${{ steps.set_image.outputs.image || env.LATEST_IMAGE }}
compiler-tag: ${{ steps.set_image.outputs.compiler-tag }}
needs-push: ${{ env.BUILD_DOCKER }}
force-rebuild-docker: ${{ env.FORCE_REBUILD_DOCKER }}
report: ${{ steps.report.outputs.report || 'Did not run.' }}
steps:
- uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
with:
fetch-depth: 0
- name: Get changed files
if: ${{ (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) || github.event_name == 'pull_request' }}
id: files
run: |
CURRENT_SHA="${{ github.sha }}"
if [[ "${IS_PR}" == "true" ]]; then
BEFORE_SHA="${{ github.event.pull_request.base.sha }}"
else
BEFORE_SHA="$(./script/actions_utils/get_latest_run.sh \
--token ${{ secrets.BOT_TOKEN }} \
--org-repo ${{ github.repository }} \
--event-types 'push workflow_dispatch repository_dispatch')"
fi
ALL_CHANGED_FILES=$(git diff --name-only "${CURRENT_SHA}" "${BEFORE_SHA}")
echo "Before sha1 ${BEFORE_SHA}"
echo "Current sha1 ${CURRENT_SHA}"
echo "Changed files:"
echo "${ALL_CHANGED_FILES}"
echo "::set-output name=all::${ALL_CHANGED_FILES//$'\n'/ }"
- name: Should rebuild docker check
run: |
set +e
ALL_CHANGED_FILES="${{ steps.files.outputs.all }}"
ALL_CHANGED_FILES="${ALL_CHANGED_FILES// /$'\n'}"
echo "${ALL_CHANGED_FILES}" | grep "${ENV_DOCKERFILE}$"
DOCKERFILE_CHANGED=$?
if [[ "${DOCKERFILE_CHANGED}" == "0" || "${FORCE_REBUILD_DOCKER}" == "true" ]]; then
echo "Should rebuild docker image!"
echo "BUILD_DOCKER=true" >> "$GITHUB_ENV"
else
echo "Docker image up to date."
echo "BUILD_DOCKER=false" >> "$GITHUB_ENV"
fi
# https://github.com/zama-ai/concrete-framework-internal/issues/809
# Remove gh_dl_release call once package is on PyPi
- name: Set prefligh Docker image download compiler
id: set_image
if: ${{ fromJSON(env.BUILD_DOCKER) }}
env:
WHEEL_SPEC: cp38-cp38-manylinux_2_24_x86_64
run: |
PREFLIGHT_IMAGE_TAG=$(echo ${{ github.ref }} | sed -e 's/\//-/g')
PREFLIGHT_IMAGE="${PREFLIGHT_IMAGE_BASE}-${PREFLIGHT_IMAGE_TAG}"
LABEL_SHA1=$(git rev-parse HEAD)
echo "::set-output name=image::${PREFLIGHT_IMAGE}"
echo "PREFLIGHT_IMAGE=${PREFLIGHT_IMAGE}" >> "$GITHUB_ENV"
echo "LABEL_SHA1=${LABEL_SHA1}" >> "$GITHUB_ENV"
mkdir -p pkg
./script/actions_utils/gh_dl_release.sh \
--token ${{ secrets.BOT_TOKEN }} \
--org-repo ${{ secrets.COMPILER_REPO }} \
--file "${WHEEL_SPEC}" \
--dest-dir "pkg" \
--github-env "${GITHUB_ENV}" \
--compiler-tag-output-file compiler-output-tag.txt
COMPILER_TAG=$(cat compiler-output-tag.txt)
echo "::set-output name=compiler-tag::${COMPILER_TAG}"
- name: Login to GitHub Container Registry
if: ${{ fromJSON(env.BUILD_DOCKER) }}
uses: docker/login-action@42d299face0c5c43a0487c477f595ac9cf22f1a7
with:
registry: ghcr.io
username: ${{ secrets.BOT_USERNAME }}
password: ${{ secrets.BOT_TOKEN }}
- name: Build concrete-framework-env Image
if: ${{ success() && !cancelled() && fromJSON(env.BUILD_DOCKER) }}
uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
with:
context: .
# builder: ${{ steps.buildx.outputs.name }}
build-args: |
WHEEL=${{ env.WHEEL }}
file: docker/Dockerfile.concrete-framework-env
no-cache: true
push: true
tags: "${{ env.PREFLIGHT_IMAGE }}"
labels: |
concrete_framework_sha=${{ env.LABEL_SHA1 }}
- name: Set notification report
id: report
if: ${{ always() }}
run: |
REPORT="Docker image preflight build ${{ env.PREFLIGHT_IMAGE }} finished with \
status ${{ job.status }}. Rebuilt image: ${{ env.BUILD_DOCKER || 'false' }}."
echo "${REPORT}"
echo "::set-output name=report::${REPORT}"
echo "REPORT=${REPORT}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() && !success() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "${{ env.REPORT }} (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
start-runner:
needs: [build-preflight-docker]
name: Start EC2 runner
runs-on: ubuntu-20.04
outputs:
label: ${{ steps.start-ec2-runner.outputs.label }}
ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Start EC2 runner
id: start-ec2-runner
uses: machulav/ec2-github-runner@502fc5cc476bcf6771c5ab7863d706715d124202
with:
mode: start
github-token: ${{ secrets.EC2_RUNNER_BOT_TOKEN }}
ec2-image-id: ${{ secrets.AWS_EC2_AMI }}
ec2-instance-type: ${{ secrets.AWS_EC2_INSTANCE_TYPE }}
subnet-id: ${{ secrets.AWS_EC2_SUBNET_ID }}
security-group-id: ${{ secrets.AWS_EC2_SECURITY_GROUP_ID }}
build:
needs: [build-preflight-docker, start-runner]
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
runs-on: ${{ needs.start-runner.outputs.label }}
container:
image: ${{ needs.build-preflight-docker.outputs.image }}
credentials:
username: ${{ secrets.BOT_USERNAME }}
password: ${{ secrets.BOT_TOKEN }}
defaults:
run:
shell: '/usr/bin/bash -e {0}'
strategy:
fail-fast: false
matrix:
# YAML footgun : https://twitter.com/webology/status/1445394072492023811?s=20
# Versions need to be quoted or risk being interpreted as floating point numbers
python-version: ["3.8"]
include:
- os: ubuntu-20.04
outputs:
report: ${{ steps.report.outputs.report || 'Did not run.' }}
steps:
- name: Checkout Code
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@f38219332975fe8f9c04cca981d674bf22aea1d3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
id: install-deps
run: |
python -m pip install --upgrade pip
python -m pip install poetry
make setup_env
- name: Check commits first line format
id: ccfl
if: ${{ fromJSON(env.IS_PR) && steps.install-deps.outcome == 'success' && !cancelled() }}
uses: gsactions/commit-message-checker@f27f413dcf8ebcb469d2ce4ae4e45e131d105de6
with:
pattern: '^((feat|fix|chore|refactor|style|test|docs)(\((bounds|helpers|data_types|debugging|extensions|fhe_circuit|mlir|graph|optimization|representation|tracing|values|benchmarks|ci|scripts|compilation|execution|deps)\))?\:) .+$'
flags: 'gs'
error: "Your first line has to contain a commit type and scope like \"feat(my_feature): msg\".\
Pattern: '^((feat|fix|chore|refactor|style|test|docs)(\\((bounds|helpers|data_types|debugging|extensions|fhe_circuit|mlir|graph|optimization|representation|tracing|values|benchmarks|ci|scripts|compilation|execution|deps)\\))?\\:)'"
excludeDescription: 'true' # optional: this excludes the description body of a pull request
excludeTitle: 'true' # optional: this excludes the title of a pull request
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true
- name: Check commits line length
id: ccll
if: ${{ fromJSON(env.IS_PR) && steps.install-deps.outcome == 'success' && !cancelled() }}
uses: gsactions/commit-message-checker@f27f413dcf8ebcb469d2ce4ae4e45e131d105de6
with:
pattern: '(^.{0,74}$\r?\n?){0,20}'
flags: 'gm'
error: 'The maximum line length of 74 characters is exceeded.'
excludeDescription: 'true' # optional: this excludes the description body of a pull request
excludeTitle: 'true' # optional: this excludes the title of a pull request
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true
- name: Commit conformance
id: commit-conformance
if: ${{ steps.install-deps.outcome == 'success' && !cancelled() }}
env:
CCFL_OK: ${{ (fromJSON(env.IS_PR) && steps.ccfl.outcome == 'success') || steps.ccfl.outcome == 'skipped' }}
CCLL_OK: ${{ (fromJSON(env.IS_PR) && steps.ccll.outcome == 'success') || steps.ccll.outcome == 'skipped' }}
run: |
if [[ "${CCFL_OK}" != "true" || "${CCLL_OK}" != "true" ]]; then
echo "Issues with commits. First line ok: ${CCFL_OK}. Line length ok: ${CCLL_OK}."
exit 1
fi
- name: Source code Conformance
id: cs
if: ${{ steps.install-deps.outcome == 'success' && !cancelled() }}
# pcc launches an internal target with proper flags
run: |
make pcc
- name: Build docs
id: cbd
if: ${{ steps.install-deps.outcome == 'success' && !cancelled() }}
run: |
make docs
- name: Generate release changelog
id: changelog
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && steps.install-deps.outcome == 'success' && !cancelled() }}
run: |
GIT_TAG=$(echo "${{ github.ref }}" | sed 's/refs\/tags\///g')
CHANGELOG_FILE="CHANGELOG_${GIT_TAG}.md"
echo "::set-output name=changelog-file::${CHANGELOG_FILE}"
poetry run python ./script/make_utils/changelog_helper.py \
--to-ref "${GIT_TAG}" \
--to-ref-must-have-tag \
--ancestor-must-have-tag > "${CHANGELOG_FILE}"
- name: Conformance status
id: conformance
if: ${{ always() && !cancelled() }}
env:
CONFORMANCE_STATUS: ${{ steps.commit-conformance.outcome == 'success' && steps.cs.outcome == 'success' && steps.cbd.outcome == 'success' }}
run: |
if [[ "${CONFORMANCE_STATUS}" != "true" ]]; then
echo "Conformance failed, check logs"
exit 1
fi
# Taring the docs allows for much faster upload speed (from ~3min worst case to ~2s best case)
- name: Tar docs artifacts
if: ${{ steps.conformance.outcome == 'success' && !cancelled() }}
run: |
cd docs/_build/html
tar -cvf docs.tar *
- name: Archive docs artifacts
if: ${{ steps.conformance.outcome == 'success' && !cancelled() }}
uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2
with:
name: html-docs
path: docs/_build/html/docs.tar
- name: Upload changelog artifacts
if: ${{ steps.changelog.outcome == 'success' && !cancelled() }}
uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2
with:
name: changelog
path: ${{ steps.changelog.outputs.changelog-file }}
- name: PyTest Source Code
id: pytest
if: ${{ steps.conformance.outcome == 'success' && !cancelled() }}
run: |
make pytest
- name: PyTest CodeBlocks
if: ${{ steps.conformance.outcome == 'success' && !cancelled() }}
run: |
make pytest_codeblocks
- name: PyTest Notebooks
if: ${{ github.event_name == 'schedule' && steps.conformance.outcome == 'success' && !cancelled() }}
run: |
make pytest_nb
- name: PyTest Progress Tracker
if: ${{ steps.conformance.outcome == 'success' && !cancelled() }}
run: |
make pytest_progress_tracker
- name: Test coverage
id: coverage
if: ${{ always() && steps.pytest.outcome != 'skipped' && !cancelled() }}
run: |
./script/actions_utils/coverage.sh global-coverage-infos.json
- name: Comment with coverage
uses: marocchino/sticky-pull-request-comment@39c5b5dc7717447d0cba270cd115037d32d28443
if: ${{ steps.coverage.outcome != 'skipped' && !cancelled() }}
continue-on-error: true
with:
path: diff-coverage.txt
recreate: true
- name: Set notification report
id: report
if: ${{ always() }}
run: |
REPORT="Build finished with status ${{ job.status }}."
echo "${REPORT}"
echo "::set-output name=report::${REPORT}"
echo "REPORT=${REPORT}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() && !success() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "${{ env.REPORT }} (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
stop-runner:
name: Stop EC2 runner
needs: [build, start-runner]
runs-on: ubuntu-20.04
if: ${{ always() && (needs.start-runner.result != 'skipped') }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Stop EC2 runner
uses: machulav/ec2-github-runner@502fc5cc476bcf6771c5ab7863d706715d124202
with:
github-token: ${{ secrets.EC2_RUNNER_BOT_TOKEN }}
label: ${{ needs.start-runner.outputs.label }}
ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }}
mode: stop
weekly-pip-audit:
if: ${{ github.event_name == 'schedule' }}
runs-on: ubuntu-20.04
steps:
- name: Checkout Code
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Set up Python 3.8
uses: actions/setup-python@f38219332975fe8f9c04cca981d674bf22aea1d3
with:
python-version: '3.8'
- name: Set up env
run: |
python -m pip install --upgrade pip
python -m pip install poetry
sudo apt update && sudo apt install graphviz* -y
make setup_env
- name: Run pip-audit
shell: bash
run: |
VULN_OUT="$(mktemp --suffix=.json)"
REPORT_OUT="$(mktemp --suffix=.txt)"
echo "REPORT_OUT=${REPORT_OUT}" >> "$GITHUB_ENV"
poetry run pip-audit -f json > "${VULN_OUT}"
cat "${VULN_OUT}"
poetry run python ./script/actions_utils/parse_pip_audit_vulns.py \
--vulns-json "${VULN_OUT}" \
--vulns-report "${REPORT_OUT}"
# We load the report in a new step if we exited with an error code above to let the workflow fail
- name: Load report in env
if: ${{ always() }}
run: |
cat "${REPORT_OUT}"
REPORT="$(cat "${REPORT_OUT}")"
echo "REPORT=${REPORT}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() && !success() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "${{ env.REPORT || 'Error during pip-audit' }} (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
publish-docs:
needs: [build]
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
outputs:
report: ${{ steps.report.outputs.report || 'Did not run.' }}
runs-on: ubuntu-20.04
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
steps:
- name: Prepare docs push
id: docs-push-infos
run: |
if [[ ${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_BUCKET_NAME }} != "" ]] && \
[[ ${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_DISTRIBUTION_ID }} != "" ]]; then
REF_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///g')
echo "::set-output name=has-preprod::true"
echo "::set-output name=aws-bucket::${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_BUCKET_NAME }}"
echo "::set-output name=aws-distribution::${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_DISTRIBUTION_ID }}"
echo "::set-output name=dest-dir::concrete-framework/${REF_NAME}"
else
echo "::set-output name=has-preprod::false"
fi
- name: Download Documentation
if: ${{ fromJSON(steps.docs-push-infos.outputs.has-preprod) }}
id: download
uses: actions/download-artifact@f023be2c48cc18debc3bacd34cb396e0295e2869
with:
name: html-docs
- name: Untar docs artifacts
id: untar
if: ${{ fromJSON(steps.docs-push-infos.outputs.has-preprod) }}
run: |
tar -xvf docs.tar
rm docs.tar
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Publish Documentation to S3
id: publish
if: ${{ steps.untar.outcome == 'success' && !cancelled() }}
env:
AWS_S3_BUCKET: ${{ steps.docs-push-infos.outputs.aws-bucket }}
SOURCE_DIR: '.'
DEST_DIR: ${{ steps.docs-push-infos.outputs.dest-dir }}
run: |
aws s3 sync "${SOURCE_DIR}" s3://"${AWS_S3_BUCKET}/${DEST_DIR}" --delete --acl public-read
- name: Invalidate CloudFront Cache
if: ${{ steps.publish.outcome == 'success' }}
env:
SOURCE_PATH: "/${{ steps.docs-push-infos.outputs.dest-dir }}/*"
DISTRIBUTION_ID: ${{ steps.docs-push-infos.outputs.aws-distribution }}
run: |
aws cloudfront create-invalidation \
--distribution-id "${DISTRIBUTION_ID}" \
--paths "${SOURCE_PATH}"
- name: Set notification report
id: report
if: ${{ always() }}
run: |
REPORT="Publishing documentation finished with status ${{ job.status }}. \
Pushed to preprod: ${{ steps.docs-push-infos.outputs.has-preprod }}"
echo "${REPORT}"
echo "::set-output name=report::${REPORT}"
echo "REPORT=${REPORT}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() && !success() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "${{ env.REPORT }} (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
push-docker-image:
needs: [build-preflight-docker, build]
if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main' && fromJSON(needs.build-preflight-docker.outputs.needs-push)) || fromJSON(needs.build-preflight-docker.outputs.force-rebuild-docker) }}
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
outputs:
report: ${{ steps.report.outputs.report || 'Did not run.' }}
name: Push env docker image
runs-on: ubuntu-20.04
env:
PREFLIGHT_IMAGE: ${{ needs.build-preflight-docker.outputs.image }}
COMPILER_TAG: ${{ needs.build-preflight-docker.outputs.compiler-tag }}
steps:
- uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Login to GitHub Container Registry
uses: docker/login-action@42d299face0c5c43a0487c477f595ac9cf22f1a7
with:
registry: ghcr.io
username: ${{ secrets.BOT_USERNAME }}
password: ${{ secrets.BOT_TOKEN }}
- name: Pull preflight image
run: |
docker pull "${PREFLIGHT_IMAGE}"
# https://github.com/zama-ai/concrete-framework-internal/issues/809
# update once release workflow is ok on the compiler side
- name: Retag to latest and concrete_compiler_version-concrete_framework_sha1 and push
run: |
SHA1=$(git rev-parse HEAD)
TAGGED_IMAGE="${BASE_IMAGE}:${COMPILER_TAG}-${SHA1}"
docker tag "${PREFLIGHT_IMAGE}" "${LATEST_IMAGE}"
docker tag "${PREFLIGHT_IMAGE}" "${TAGGED_IMAGE}"
docker push "${LATEST_IMAGE}"
docker push "${TAGGED_IMAGE}"
- name: Set notification report
id: report
if: ${{ always() }}
run: |
REPORT="Pushing docker image ${{ env.BASE_IMAGE }} finished with status \
${{ job.status }}."
echo "${REPORT}"
echo "::set-output name=report::${REPORT}"
echo "REPORT=${REPORT}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() && !success() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "${{ env.REPORT }} (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
package-release:
needs: [build]
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
outputs:
report: ${{ steps.report.outputs.report || 'Did not run.' }}
name: Package and artifacts release
runs-on: ubuntu-20.04
env:
RELEASE_IMAGE_BASE: ghcr.io/zama-ai/concrete-framework
steps:
- uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
# See #570 To be updated to only install required dependencies group with poetry 1.2 and
# remove graphviz installs which are only required for the actual package and not dev tools
- name: Install dependencies
run: |
sudo apt-get install --no-install-recommends -y graphviz*
python -m pip install --upgrade pip
python -m pip install poetry
make setup_env
- name: Set tag in env
# 'poetry version' cannot be piped properly so do it in 2 steps
# the project version does not have the leading v to be semver compatible
run: |
PROJECT_VERSION=$(poetry version)
PROJECT_VERSION=$(echo "$PROJECT_VERSION" | cut -d ' ' -f 2)
GIT_TAG=$(echo "${{ github.ref }}" | sed 's/refs\/tags\///g')
if [[ "v${PROJECT_VERSION}" != "${GIT_TAG}" ]]; then
echo "Mismatch between tag and version: ${GIT_TAG}, v${PROJECT_VERSION}"
exit 1
fi
echo "PROJECT_VERSION=${PROJECT_VERSION}" >> "$GITHUB_ENV"
echo "GIT_TAG=${GIT_TAG}" >> "$GITHUB_ENV"
RELEASE_IMG_GIT_TAG="${RELEASE_IMAGE_BASE}:${GIT_TAG}"
echo "RELEASE_IMG_GIT_TAG=${RELEASE_IMG_GIT_TAG}" >> "$GITHUB_ENV"
RELEASE_IMG_TAGS_TO_PUSH="${RELEASE_IMG_GIT_TAG}"
EXISTING_TAGS=$(curl \
-X GET \
-H "Authorization: Bearer $(echo ${{ secrets.BOT_TOKEN }} | base64)" \
https://ghcr.io/v2/zama-ai/concrete-framework/tags/list | jq -rc '.tags | join(" ")')
# We want the space separated list of versions to be expanded
# shellcheck disable=SC2086
IS_LATEST_INFO=$(poetry run python script/make_utils/version_utils.py \
islatest \
--new-version "${GIT_TAG}" \
--existing-versions $EXISTING_TAGS)
IS_LATEST=$(echo "${IS_LATEST_INFO}" | jq -rc '.is_latest')
echo "IS_LATEST=${IS_LATEST}" >> "$GITHUB_ENV"
IS_PRERELEASE=$(echo "${IS_LATEST_INFO}" | jq -rc '.is_prerelease')
echo "IS_PRERELEASE=${IS_PRERELEASE}" >> "$GITHUB_ENV"
if [[ "${IS_LATEST}" == "true" ]]; then
RELEASE_IMG_LATEST_TAG="${RELEASE_IMAGE_BASE}:latest"
RELEASE_IMG_TAGS_TO_PUSH="${RELEASE_IMG_TAGS_TO_PUSH},${RELEASE_IMG_LATEST_TAG}"
fi
echo "RELEASE_IMG_TAGS_TO_PUSH=${RELEASE_IMG_TAGS_TO_PUSH}" >> "$GITHUB_ENV"
# Disabled buildx for now as we are seeing a lot of fails on layer pushes
# - name: Set up Docker Buildx
# id: buildx
# uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25
- name: Login to GitHub Container Registry
uses: docker/login-action@42d299face0c5c43a0487c477f595ac9cf22f1a7
with:
registry: ghcr.io
username: ${{ secrets.BOT_USERNAME }}
password: ${{ secrets.BOT_TOKEN }}
- name: Build concrete-framework Image
if: ${{ success() && !cancelled() }}
uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
with:
context: .
# builder: ${{ steps.buildx.outputs.name }}
file: docker/Dockerfile.release
load: true
push: false
tags: "${{ env.RELEASE_IMG_TAGS_TO_PUSH }}"
no-cache: true
- name: Release image sanity check
if: ${{ success() && !cancelled() }}
run: |
echo "Running sanity check for ${RELEASE_IMG_GIT_TAG}"
docker run --rm -v "$(pwd)"/docker/release_resources:/data \
"${RELEASE_IMG_GIT_TAG}" /bin/bash -c "python ./sanity_check.py"
- name: Create directory for artifacts
if: ${{ success() && !cancelled() }}
run: |
ARTIFACTS_RAW_DIR=/tmp/release_artifacts/raw
mkdir -p "${ARTIFACTS_RAW_DIR}"
echo "ARTIFACTS_RAW_DIR=${ARTIFACTS_RAW_DIR}" >> "$GITHUB_ENV"
ARTIFACTS_PACKAGED_DIR=/tmp/release_artifacts/packaged
mkdir -p "${ARTIFACTS_PACKAGED_DIR}"
echo "ARTIFACTS_PACKAGED_DIR=${ARTIFACTS_PACKAGED_DIR}" >> "$GITHUB_ENV"
- name: Download Documentation
if: ${{ success() && !cancelled() }}
id: download-docs
uses: actions/download-artifact@f023be2c48cc18debc3bacd34cb396e0295e2869
with:
name: html-docs
path: ${{ env.ARTIFACTS_RAW_DIR }}/html_docs/
- name: Untar docs artifacts
if: ${{ success() && !cancelled() }}
run: |
cd ${{ env.ARTIFACTS_RAW_DIR }}/html_docs/
tar -xvf docs.tar
rm docs.tar
- name: Download changelog
if: ${{ success() && !cancelled() }}
id: download-changelog
uses: actions/download-artifact@f023be2c48cc18debc3bacd34cb396e0295e2869
with:
name: changelog
path: ${{ env.ARTIFACTS_RAW_DIR }}/changelog/
- name: Prepare docs push
id: docs-push-infos
run: |
if [[ ${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_BUCKET_NAME }} != "" ]] && \
[[ ${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_DISTRIBUTION_ID }} != "" ]] && \
[[ "${IS_PRERELEASE}" == "true" ]]; then
echo "::set-output name=aws-bucket::${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_BUCKET_NAME }}"
echo "::set-output name=aws-distribution::${{ secrets.AWS_REPO_PREPROD_DOCUMENTATION_DISTRIBUTION_ID }}"
else
echo "::set-output name=aws-bucket::${{ secrets.AWS_REPO_DOCUMENTATION_BUCKET_NAME }}"
echo "::set-output name=aws-distribution::${{ secrets.AWS_REPO_DOCUMENTATION_DISTRIBUTION_ID }}"
fi
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Update versions.json for docs
if: ${{ success() && !cancelled() }}
env:
RAW_DOCS_DIR: ${{ steps.download-docs.outputs.download-path }}
run: |
DOWNLOADED_VERSIONS_JSON_FILE=$(mktemp --suffix=.json)
OUTPUT_VERSIONS_JSON_FILE=$(mktemp --suffix=.json)
OPTS=""
if [[ $IS_LATEST = "true" ]]; then
OPTS="${OPTS} --latest "
fi
if [[ $IS_PRERELEASE = "true" ]]; then
OPTS="${OPTS} --prerelease "
fi
aws s3api get-object \
--bucket ${{ steps.docs-push-infos.outputs.aws-bucket }} \
--key concrete-framework/versions.json "${DOWNLOADED_VERSIONS_JSON_FILE}"
# shellcheck disable=SC2086
poetry run python ./script/actions_utils/generate_versions_json.py \
--add-version "${PROJECT_VERSION}" \
--versions-json-file "${DOWNLOADED_VERSIONS_JSON_FILE}" \
--output-json "${OUTPUT_VERSIONS_JSON_FILE}" \
$OPTS
echo "OUTPUT_VERSIONS_JSON_FILE=${OUTPUT_VERSIONS_JSON_FILE}" >> "$GITHUB_ENV"
# Copy to docs to keep a version in docs artifacts
cp "${OUTPUT_VERSIONS_JSON_FILE}" "${RAW_DOCS_DIR}"/versions.json
- name: Create ready to upload/packaged artifacts and release body
if: ${{ success() && !cancelled() }}
env:
RAW_DOCS_DIR: ${{ steps.download-docs.outputs.download-path }}
RAW_CHANGELOG_DIR: ${{ steps.download-changelog.outputs.download-path }}
run: |
pushd "${RAW_DOCS_DIR}"
zip -r "${ARTIFACTS_PACKAGED_DIR}/html-docs.zip" ./*
tar -cvzf "${ARTIFACTS_PACKAGED_DIR}/html-docs.tar.gz" ./*
# Remove the versions.json to avoid pushing it to S3 but have it in release artifacts
rm versions.json
popd
cp "${RAW_CHANGELOG_DIR}"/* "${ARTIFACTS_PACKAGED_DIR}"
ls -a "${ARTIFACTS_PACKAGED_DIR}"
RELEASE_BODY_FILE=RELEASE_BODY.md
echo "RELEASE_BODY_FILE=${RELEASE_BODY_FILE}" >> "$GITHUB_ENV"
cp ./script/actions_utils/RELEASE_TEMPLATE.md "${RELEASE_BODY_FILE}"
{
echo "Docker Image: ${RELEASE_IMG_GIT_TAG}";
echo "Documentation: https://${{ steps.docs-push-infos.outputs.aws-bucket }}/concrete-framework/${PROJECT_VERSION}";
echo "";
} >> "${RELEASE_BODY_FILE}"
cat "${RAW_CHANGELOG_DIR}"/* >> "${RELEASE_BODY_FILE}"
- name: Push release docker image
if: ${{ success() && !cancelled() }}
run: |
docker image push --all-tags "${RELEASE_IMAGE_BASE}"
- name: Push release documentation
if: ${{ success() && !cancelled() }}
env:
AWS_S3_BUCKET: ${{ steps.docs-push-infos.outputs.aws-bucket }}
SOURCE_DIR: ${{ steps.download-docs.outputs.download-path }}
DEST_DIR: 'concrete-framework/${{ env.PROJECT_VERSION }}'
run: |
aws s3 sync "${SOURCE_DIR}" s3://"${AWS_S3_BUCKET}/${DEST_DIR}" --delete --acl public-read
- name: Push release documentation as stable
if: ${{ success() && !cancelled() && !fromJSON(env.IS_PRERELEASE) && fromJSON(env.IS_LATEST) }}
env:
AWS_S3_BUCKET: ${{ steps.docs-push-infos.outputs.aws-bucket }}
SOURCE_DIR: ${{ steps.download-docs.outputs.download-path }}
DEST_DIR: 'concrete-framework/stable'
run: |
aws s3 sync "${SOURCE_DIR}" s3://"${AWS_S3_BUCKET}/${DEST_DIR}" --delete --acl public-read
- name: Invalidate CloudFront Cache for stable
if: ${{ success() && !fromJSON(env.IS_PRERELEASE) && fromJSON(env.IS_LATEST) }}
env:
SOURCE_PATH: "/concrete-framework/stable/*"
DISTRIBUTION_ID: ${{ steps.docs-push-infos.outputs.aws-distribution }}
run: |
aws cloudfront create-invalidation \
--distribution-id "${DISTRIBUTION_ID}" \
--paths "${SOURCE_PATH}"
- name: Create GitHub release
if: ${{ success() && !cancelled() }}
id: create-release
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5
with:
body_path: ${{ env.RELEASE_BODY_FILE }}
prerelease: ${{ fromJSON(env.IS_PRERELEASE) }}
files: |
${{ env.ARTIFACTS_PACKAGED_DIR }}/*
tag_name: ${{ env.GIT_TAG }}
fail_on_unmatched_files: true
token: ${{ secrets.BOT_TOKEN }}
# TODO: https://github.com/zama-ai/concrete-framework-internal/issues/809
# Remove versions.html
- name: Push updated versions.html
if: ${{ success() }}
run: |
aws s3 cp "${OUTPUT_VERSIONS_HTML_FILE}" \
s3://${{ steps.docs-push-infos.outputs.aws-bucket }}/concrete-framework/versions.html \
--acl public-read
aws cloudfront create-invalidation \
--distribution-id ${{ steps.docs-push-infos.outputs.aws-distribution }} \
--paths /concrete-framework/versions.html
- name: Set notification report
id: report
if: ${{ always() }}
run: |
REPORT="Creating release for ${GIT_TAG} finished with status ${{ job.status }}. \
GitHub release link: ${{ steps.create-release.outputs.url }}."
echo "${REPORT}"
echo "::set-output name=report::${REPORT}"
echo "REPORT=${REPORT}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() && !success() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "${{ env.REPORT }} (${{ env.ACTION_RUN_URL }})"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
send-report:
if: ${{ always() }}
needs:
[
build-preflight-docker,
start-runner,
build,
stop-runner,
publish-docs,
push-docker-image,
package-release,
]
name: Send Slack notification
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Prepare whole job status
if: ${{ always() }}
continue-on-error: true
env:
NEEDS_JSON: ${{ toJSON(needs) }}
run: |
echo "${NEEDS_JSON}" > /tmp/needs_context.json
JOB_STATUS=$(python3 ./script/actions_utils/actions_combine_status.py \
--needs_context_json /tmp/needs_context.json)
echo "JOB_STATUS=${JOB_STATUS}" >> "$GITHUB_ENV"
- name: Slack Notification
if: ${{ always() }}
continue-on-error: true
uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7
env:
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
SLACK_COLOR: ${{ env.JOB_STATUS || 'failure' }}
SLACK_MESSAGE: "Full run finished with status ${{ env.JOB_STATUS || 'failure' }} \
(${{ env.ACTION_RUN_URL }})\n\
- build-preflight-docker: ${{ needs.build-preflight-docker.outputs.report || 'Did not run.' }}\n\n\
- start-runner: ${{ needs.start-runner.result }}\n\n\
- build: ${{ needs.build.outputs.report || 'Did not run.' }}\n\n\
- stop-runner: ${{ needs.stop-runner.result }}\n\n\
- publish-docs: ${{ needs.publish-docs.outputs.report || 'Did not run.' }}\n\n\
- push-docker-image: ${{ needs.push-docker-image.outputs.report || 'Did not run.' }}\n\n\
- package-release: ${{ needs.package-release.outputs.report || 'Did not run.' }}"
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}