blob: c3dcd2db1408f2cc6d77a37934f86b6653d7852c [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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: "Build Images"
on: # yamllint disable-line rule:truthy
pull_request_target:
permissions:
# all other permissions are set to none
contents: read
pull-requests: read
env:
MOUNT_SELECTED_LOCAL_SOURCES: "false"
ANSWER: "yes"
CHECK_IMAGE_FOR_REBUILD: "true"
SKIP_CHECK_REMOTE_IMAGE: "true"
DB_RESET: "true"
VERBOSE: "true"
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_USERNAME: ${{ github.actor }}
# You can override CONSTRAINTS_GITHUB_REPOSITORY by setting secret in your repo but by default the
# Airflow one is going to be used
CONSTRAINTS_GITHUB_REPOSITORY: >-
${{ secrets.CONSTRAINTS_GITHUB_REPOSITORY != '' &&
secrets.CONSTRAINTS_GITHUB_REPOSITORY || 'apache/airflow' }}
# This token is WRITE one - pull_request_target type of events always have the WRITE token
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
IMAGE_TAG_FOR_THE_BUILD: "${{ github.event.pull_request.head.sha || github.sha }}"
USE_SUDO: "true"
concurrency:
group: build-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-info:
timeout-minutes: 10
name: "Build Info"
runs-on: ${{ github.repository == 'apache/airflow' && 'self-hosted' || 'ubuntu-20.04' }}
env:
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
outputs:
runs-on: ${{ github.repository == 'apache/airflow' && '["self-hosted"]' || '["ubuntu-20.04"]' }}
python-versions: "${{ steps.selective-checks.python-versions }}"
upgrade-to-newer-dependencies: ${{ steps.selective-checks.outputs.upgrade-to-newer-dependencies }}
all-python-versions-list-as-string: >-
${{ steps.selective-checks.outputs.all-python-versions-list-as-string }}
default-python-version: ${{ steps.selective-checks.outputs.default-python-version }}
run-tests: ${{ steps.selective-checks.outputs.run-tests }}
run-kubernetes-tests: ${{ steps.selective-checks.outputs.run-kubernetes-tests }}
image-build: ${{ steps.selective-checks.outputs.image-build }}
cache-directive: ${{ steps.selective-checks.outputs.cache-directive }}
default-branch: ${{ steps.selective-checks.outputs.default-branch }}
default-constraints-branch: ${{ steps.selective-checks.outputs.default-constraints-branch }}
debian-version: ${{ steps.selective-checks.outputs.debian-version }}
target-commit-sha: "${{steps.discover-pr-merge-commit.outputs.target-commit-sha ||
github.event.pull_request.head.sha ||
github.sha
}}"
steps:
- name: Discover PR merge commit
id: discover-pr-merge-commit
run: |
TARGET_COMMIT_SHA="$(gh api '${{ github.event.pull_request.url }}' --jq .merge_commit_sha)"
echo "TARGET_COMMIT_SHA=$TARGET_COMMIT_SHA" >> $GITHUB_ENV
echo "::set-output name=target-commit-sha::${TARGET_COMMIT_SHA}"
if: github.event_name == 'pull_request_target'
# The labels in the event aren't updated when re-triggering the job, So lets hit the API to get
# up-to-date values
- name: Get latest PR labels
id: get-latest-pr-labels
run: |
echo -n "::set-output name=pull-request-labels::"
gh api graphql --paginate -F node_id=${{github.event.pull_request.node_id}} -f query='
query($node_id: ID!, $endCursor: String) {
node(id:$node_id) {
... on PullRequest {
labels(first: 100, after: $endCursor) {
nodes { name }
pageInfo { hasNextPage endCursor }
}
}
}
}' --jq '.data.node.labels.nodes[]' | jq --slurp -c '[.[].name]'
if: github.event_name == 'pull_request_target'
# Retrieve it to be able to determine which files has changed in the incoming commit of the PR
# we checkout the target commit and it's parent to be able to compare them
- name: Cleanup repo
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- uses: actions/checkout@v2
with:
ref: ${{ env.TARGET_COMMIT_SHA }}
persist-credentials: false
fetch-depth: 2
- name: "Setup python"
uses: actions/setup-python@v4
with:
# We do not have output from selective checks yet, so we need to hardcode python
python-version: 3.7
cache: 'pip'
cache-dependency-path: ./dev/breeze/setup*
- name: "Retrieve defaults from branch_defaults.py"
# We cannot "execute" the branch_defaults.py python code here because that would be
# a security problem (we cannot run any code that comes from the sources coming from the PR.
# Therefore, we extract the branches via embedded Python code
# we need to do it before next step replaces checked-out breeze and scripts code coming from
# the PR, because the PR defaults have to be retrieved here.
id: defaults
run: |
python - <<EOF >>$GITHUB_ENV
from pathlib import Path
import re
import sys
DEFAULTS_CONTENT = Path('dev/breeze/src/airflow_breeze/branch_defaults.py').read_text()
BRANCH_PATTERN = r'^AIRFLOW_BRANCH = "(.*)"$'
CONSTRAINTS_BRANCH_PATTERN = r'^DEFAULT_AIRFLOW_CONSTRAINTS_BRANCH = "(.*)"$'
DEBIAN_VERSION_PATTERN = r'^DEBIAN_VERSION = "(.*)"$'
branch = re.search(BRANCH_PATTERN, DEFAULTS_CONTENT, re.MULTILINE).group(1)
constraints_branch = re.search(CONSTRAINTS_BRANCH_PATTERN, DEFAULTS_CONTENT, re.MULTILINE).group(1)
debian_version = re.search(DEBIAN_VERSION_PATTERN, DEFAULTS_CONTENT, re.MULTILINE).group(1)
output = f"""
DEFAULT_BRANCH={branch}
DEFAULT_CONSTRAINTS_BRANCH={constraints_branch}
DEBIAN_VERSION={debian_version}
""".strip()
print(output)
# Stdout is redirected to GITHUB_ENV but we also print it to stderr to see it in ci log
print(output, file=sys.stderr)
EOF
- name: Checkout main branch to use breeze from there.
uses: actions/checkout@v3
with:
ref: "main"
persist-credentials: false
submodules: recursive
- run: ./scripts/ci/install_breeze.sh
- name: Selective checks
id: selective-checks
env:
PR_LABELS: "${{ steps.get-latest-pr-labels.outputs.pull-request-labels }}"
COMMIT_REF: "${{ env.TARGET_COMMIT_SHA }}"
run: breeze ci selective-check
- name: env
run: printenv
env:
PR_LABELS: ${{ steps.get-latest-pr-labels.outputs.pull-request-labels }}
GITHUB_CONTEXT: ${{ toJson(github) }}
build-ci-images:
permissions:
packages: write
timeout-minutes: 80
name: "Build CI images ${{ needs.build-info.outputs.all-python-versions-list-as-string }}"
runs-on: ${{ fromJson(needs.build-info.outputs.runs-on) }}
needs: [build-info]
if: |
needs.build-info.outputs.image-build == 'true' &&
github.event.pull_request.head.repo.full_name != 'apache/airflow'
env:
DEFAULT_BRANCH: ${{ needs.build-info.outputs.default-branch }}
DEFAULT_CONSTRAINTS_BRANCH: ${{ needs.build-info.outputs.default-constraints-branch }}
DEBIAN_VERSION: ${{ needs.build-info.outputs.debian-version }}
RUNS_ON: ${{ fromJson(needs.build-info.outputs.runs-on)[0] }}
BACKEND: sqlite
outputs: ${{toJSON(needs.build-info.outputs) }}
steps:
- name: Cleanup repo
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- uses: actions/checkout@v3
with:
ref: ${{ needs.build-info.outputs.target-commit-sha }}
persist-credentials: false
submodules: recursive
- name: "Setup python"
uses: actions/setup-python@v4
with:
python-version: ${{ needs.build-info.outputs.default-python-version }}
- name: >
Checkout "main branch to 'main-airflow' folder
to use ci/scripts from there.
uses: actions/checkout@v3
with:
path: "main-airflow"
ref: "main"
persist-credentials: false
submodules: recursive
- name: >
Override "scripts/ci" with the "main" branch
so that the PR does not override it
# We should not override those scripts which become part of the image as they will not be
# changed in the image built - we should only override those that are executed to build
# the image.
run: |
rm -rfv "scripts/ci"
rm -rfv "dev"
mv -v "main-airflow/scripts/ci" "scripts"
mv -v "main-airflow/dev" "."
- uses: actions/setup-python@v4
with:
python-version: ${{ needs.build-info.outputs.default-python-version }}
cache: 'pip'
cache-dependency-path: ./dev/breeze/setup*
- run: ./scripts/ci/install_breeze.sh
- name: "Free space"
run: breeze ci free-space
- name: Cache pre-commit envs
uses: actions/cache@v3
with:
path: ~/.cache/pre-commit
key: "pre-commit-${{steps.host-python-version.outputs.host-python-version}}-\
${{ hashFiles('.pre-commit-config.yaml') }}"
restore-keys: pre-commit-${{steps.host-python-version.outputs.host-python-version}}
- name: "Regenerate dependencies in case they was modified manually so that we can build an image"
run: >
breeze static-checks --type update-providers-dependencies --all-files
--show-diff-on-failure --color always || true
if: needs.build-info.outputs.default-branch == 'main'
- name: Compile www assets
run: breeze compile-www-assets
- name: >-
Build & Push AMD64 CI images ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
${{ needs.build-info.outputs.all-python-versions-list-as-string }}
run: breeze ci-image build --push --tag-as-latest --run-in-parallel
env:
UPGRADE_TO_NEWER_DEPENDENCIES: ${{ needs.build-info.outputs.upgrade-to-newer-dependencies }}
DOCKER_CACHE: ${{ needs.build-info.outputs.cache-directive }}
IMAGE_TAG: ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
PYTHON_VERSIONS: ${{ needs.build-info.outputs.all-python-versions-list-as-string }}
- name: Push empty CI image ${{ env.PYTHON_MAJOR_MINOR_VERSION }}:${{ env.IMAGE_TAG_FOR_THE_BUILD }}
if: failure() || cancelled()
run: breeze ci-image build --push --empty-image --run-in-parallel
env:
IMAGE_TAG: ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
- name: "Candidates for pip resolver backtrack triggers"
if: failure() || cancelled()
run: >
breeze ci find-newer-dependencies --max-age 1
--python "${{ needs.build-info.outputs.default-python-version }}"
- name: "Fix ownership"
run: breeze ci fix-ownership
if: always()
build-prod-images:
permissions:
packages: write
timeout-minutes: 80
name: "Build PROD images ${{ needs.build-info.outputs.all-python-versions-list-as-string }}"
runs-on: ${{ fromJson(needs.build-info.outputs.runs-on) }}
needs: [build-info, build-ci-images]
if: |
needs.build-info.outputs.image-build == 'true' &&
github.event.pull_request.head.repo.full_name != 'apache/airflow'
env:
DEFAULT_BRANCH: ${{ needs.build-info.outputs.default-branch }}
DEFAULT_CONSTRAINTS_BRANCH: ${{ needs.build-info.outputs.default-constraints-branch }}
DEBIAN_VERSION: ${{ needs.build-info.outputs.debian-version }}
RUNS_ON: ${{ fromJson(needs.build-info.outputs.runs-on)[0] }}
BACKEND: sqlite
steps:
- name: Cleanup repo
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- uses: actions/checkout@v3
with:
ref: ${{ needs.build-info.outputs.target-commit-sha }}
persist-credentials: false
submodules: recursive
- name: "Setup python"
uses: actions/setup-python@v4
with:
python-version: ${{ needs.build-info.outputs.default-python-version }}
- name: >
Checkout "main" branch to 'main-airflow' folder
to use ci/scripts from there.
uses: actions/checkout@v3
with:
path: "main-airflow"
ref: "main"
persist-credentials: false
submodules: recursive
- name: >
Override "scripts/ci" with the "main" branch
so that the PR does not override it
# We should not override those scripts which become part of the image as they will not be
# changed in the image built - we should only override those that are executed to build
# the image.
run: |
rm -rfv "scripts/ci"
rm -rfv "dev"
mv -v "main-airflow/scripts/ci" "scripts"
mv -v "main-airflow/dev" "."
- uses: actions/setup-python@v4
with:
python-version: ${{ needs.build-info.outputs.default-python-version }}
cache: 'pip'
cache-dependency-path: ./dev/breeze/setup*
- run: ./scripts/ci/install_breeze.sh
- name: "Free space"
run: breeze ci free-space
- name: Cache pre-commit envs
uses: actions/cache@v3
with:
path: ~/.cache/pre-commit
key: "pre-commit-${{steps.host-python-version.outputs.host-python-version}}-\
${{ hashFiles('.pre-commit-config.yaml') }}"
restore-keys: pre-commit-${{steps.host-python-version.outputs.host-python-version}}
- name: "Regenerate dependencies in case they was modified manually so that we can build an image"
run: >
breeze static-checks --type update-providers-dependencies --all-files
--show-diff-on-failure --color always || true
- name: >
Pull CI image for PROD build:
${{ needs.build-info.outputs.default-python-version }}:${{ env.IMAGE_TAG_FOR_THE_BUILD }}
run: breeze ci-image pull --tag-as-latest
env:
# Always use default Python version of CI image for preparing packages
PYTHON_MAJOR_MINOR_VERSION: ${{ needs.build-info.outputs.default-python-version }}
IMAGE_TAG: ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
- name: "Cleanup dist and context file"
run: rm -fv ./dist/* ./docker-context-files/*
- name: "Prepare providers packages"
run: >
breeze release-management prepare-provider-packages
--package-list-file ./scripts/ci/installed_providers.txt
--package-format wheel
--version-suffix-for-pypi dev0
if: needs.build-info.outputs.default-branch == 'main'
- name: "Prepare airflow package"
run: >
breeze release-management prepare-airflow-package
--package-format wheel --version-suffix-for-pypi dev0
- name: "Move dist packages to docker-context files"
run: mv -v ./dist/*.whl ./docker-context-files
- name: >-
Build & Push PROD images ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
${{ needs.build-info.outputs.all-python-versions-list-as-string }}
run: >
breeze prod-image build
--run-in-parallel
--tag-as-latest
--push
--install-packages-from-context
env:
UPGRADE_TO_NEWER_DEPENDENCIES: ${{ needs.build-info.outputs.upgrade-to-newer-dependencies }}
DOCKER_CACHE: ${{ needs.build-info.outputs.cache-directive }}
IMAGE_TAG: ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
PYTHON_VERSIONS: ${{ needs.build-info.outputs.all-python-versions-list-as-string }}
- name: Push empty PROD images ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
if: failure() || cancelled()
run: breeze prod-image build --cleanup-context --push --empty-image --run-in-parallel
env:
IMAGE_TAG: ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
- name: "Fix ownership"
run: breeze ci fix-ownership
if: always()
build-ci-images-arm:
timeout-minutes: 50
name: "Build ARM CI images ${{ needs.build-info.outputs.all-python-versions-list-as-string }}"
runs-on: ${{ fromJson(needs.build-info.outputs.runs-on) }}
needs: [build-info, build-prod-images]
if: |
needs.build-info.outputs.image-build == 'true' &&
needs.build-info.outputs.upgrade-to-newer-dependencies != 'false' &&
github.event.pull_request.head.repo.full_name != 'apache/airflow'
env:
DEFAULT_BRANCH: ${{ needs.build-info.outputs.default-branch }}
DEFAULT_CONSTRAINTS_BRANCH: ${{ needs.build-info.outputs.default-constraints-branch }}
DEBIAN_VERSION: ${{ needs.build-info.outputs.debian-version }}
RUNS_ON: ${{ fromJson(needs.build-info.outputs.runs-on)[0] }}
BACKEND: sqlite
outputs: ${{toJSON(needs.build-info.outputs) }}
steps:
- name: Cleanup repo
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- uses: actions/checkout@v2
with:
ref: ${{ needs.build-info.outputs.target-commit-sha }}
persist-credentials: false
submodules: recursive
- name: >
Checkout "main" branch to 'main-airflow' folder
to use ci/scripts from there.
uses: actions/checkout@v2
with:
path: "main-airflow"
ref: "main"
persist-credentials: false
submodules: recursive
- name: >
Override "scripts/ci" with the "main" branch
so that the PR does not override it
# We should not override those scripts which become part of the image as they will not be
# changed in the image built - we should only override those that are executed to build
# the image.
run: |
rm -rfv "scripts/ci"
rm -rfv "dev"
mv -v "main-airflow/scripts/ci" "scripts"
mv -v "main-airflow/dev" "."
- name: "Setup python"
uses: actions/setup-python@v2
with:
python-version: ${{ needs.build-info.outputs.default-python-version }}
- run: ./scripts/ci/install_breeze.sh
- name: "Free space"
run: breeze ci free-space
- name: "Start ARM instance"
run: ./scripts/ci/images/ci_start_arm_instance_and_connect_to_docker.sh
- name: >
Build ARM CI images ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
${{ needs.build-info.outputs.all-python-versions-list-as-string }}
run: >
breeze ci-image build --run-in-parallel --builder airflow_cache --platform "linux/arm64"
env:
UPGRADE_TO_NEWER_DEPENDENCIES: ${{ needs.build-info.outputs.upgrade-to-newer-dependencies }}
DOCKER_CACHE: ${{ needs.build-info.outputs.cache-directive }}
IMAGE_TAG: ${{ env.IMAGE_TAG_FOR_THE_BUILD }}
PYTHON_VERSIONS: ${{ needs.build-info.outputs.all-python-versions-list-as-string }}
- name: "Stop ARM instance"
run: ./scripts/ci/images/ci_stop_arm_instance.sh
if: always()
- name: "Fix ownership"
run: breeze ci fix-ownership
if: always()