Files
airflow/.github/workflows/update-constraints-on-push-stable.yml
Jarek PotiukandRahul Vats 48435351c4 Pin the hardened base images to digests the CI has passed on (#76023)
* Refresh the hardened base image mirror every six hours

Docker rebuilds the hardened images in place and moves the dhi.io apt
repository on at the same time. With a weekly mirror, the base images
Airflow builds on lag the repository by up to a week, and any apt
install whose dependencies were rebuilt in between fails: build-essential
currently cannot be installed because the mirrored base has an older
libbz2 than the bzip2 the repository offers
(docker-hardened-images/catalog#689). Mirroring copies manifests between
registries without pulling layers, so running it several times a day is
cheap.

Generated-by: Claude Opus 5

* Pin the hardened base images to digests the CI has passed on

Building on the mirror's moving tags made CI non-reproducible: any
refresh of the mirror changed the image every build started from,
without a change in the repository and without the full CI having run
on it. A CI run could pass or fail depending only on when it ran, and
rebuilding the same commit later gave a different image.

The images now build on a digest, and only the upgrade check moves it,
in a PR that must pass the full CI before the new base is used. The
mirror refreshes every six hours and triggers that upgrade check when it
moves a tag past its pin, so the pins follow Docker's rebuilds - and the
dhi.io apt repository moving with them - within hours rather than drift
behind it. Every pinned and mirrored digest keeps a tag of its own, so a
pinned image never becomes untagged.

The hardened images keep stock Debian's apt sources beside dhi.io, and
apt took whichever offered the higher version, mixing Debian builds into
an image whose dependencies were built by Docker. dhi.io is now
preferred whenever it publishes the package.

Generated-by: Claude Opus 5

* Let dhi.io's package versions win over the image's own

Docker renumbered its builds ("1.0.8-6+dhi2" became
"1.0.8-6+deb13u0+dhi2", which sorts lower) and its apt repository moved
to the new numbers before its images did. Every package depending on a
renumbered build - bzip2, libffi-dev, libgpg-error-dev among them - is
then uninstallable, because apt will not replace the image's
higher-numbered copy with the repository's lower-numbered one, and the
CI image build fails (docker-hardened-images/catalog#689). Every package
in the image comes from dhi.io, so letting its repository decide their
versions is safe, and it keeps the build working through such a
renumbering.

Generated-by: Claude Opus 5

* Move the base images to Docker's current Python patch releases

Docker stops rebuilding a Python patch release once it publishes the
next, so the 3.11.17, 3.12.15 and 3.13.16 images still carry the old
package numbering that the dhi.io apt repository has moved past, and
installing packages on them fails (docker-hardened-images/catalog#689).
Docker fixed it in the images of the current patch releases.

Generated with the Python part of the upgrade check, which also pins
the new images to the digests the mirror serves for them.

Generated-by: Claude Opus 5

* Compare the registry host exactly in the mirror test

Generated-by: Claude Opus 5

* Lower the dhi.io apt pin below 1000 so dependencies can resolve

At priority 1001 the Package:* dhi.io preference forces dhi.io's build even as a downgrade, so apt keeps dhi.io's libpq5 17.11 and postgresql-client-18 (which needs libpq5 >= 18.6 from PGDG) cannot be installed. Below 1000 the preference still favours dhi.io for the packages it publishes but lets apt take a newer version from another repository to satisfy a dependency. The forced "downgrade" the >1000 priority handled no longer arises now the base image is pinned to a digest CI has passed on.

* Rank the PostgreSQL apt repository above the dhi.io preference

install_postgres adds the PGDG repository, but the hardened image's Package:* dhi.io preference outranks Debian's default, so apt kept dhi.io's libpq and postgresql-client-18 (which needs a newer libpq5 than dhi.io carries) failed with held broken packages. Pinning the PostgreSQL origin above the dhi.io preference lets apt take libpq from PGDG to satisfy the client.

* Use US spelling favors to pass the docs spellcheck

The docker-stack docs spellcheck rejects the British spelling favours in build.rst; use the US spelling there and in the matching install-script comments.

---------

Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
2026-10-30 13:52:53 +00:00

186 lines
7.5 KiB
YAML

# 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: Update constraints on push for stable branch (always)
on: # yamllint disable-line rule:truthy
push:
branches:
- v[0-9]+-[0-9]+-stable
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_USERNAME: ${{ github.actor }}
VERBOSE: "true"
jobs:
build-info:
name: "Build info"
runs-on: ["ubuntu-slim"]
outputs:
default-branch: ${{ steps.selective-checks.outputs.default-branch }}
default-constraints-branch: ${{ steps.selective-checks.outputs.default-constraints-branch }}
default-python-version: ${{ steps.selective-checks.outputs.default-python-version }}
python-versions: ${{ steps.selective-checks.outputs.python-versions }}
python-versions-list-as-string: ${{ steps.selective-checks.outputs.python-versions-list-as-string }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Fetch incoming commit ${{ github.sha }} with its parent
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 2
persist-credentials: false
- name: "Install Breeze"
uses: ./.github/actions/breeze
with:
free-space: "false"
id: breeze
- name: "Save github context to file"
# See ci-amd.yml for the full rationale: avoids ARG_MAX on big PRs by writing the
# github context to a file, and the single-quoted heredoc makes the zizmor
# template-injection finding a false positive (no bash expansion happens inside it).
shell: bash
run: | # zizmor: ignore[template-injection]
cat > "${RUNNER_TEMP}/github_context.json" << '__GITHUB_CONTEXT_END__'
${{ toJson(github) }}
__GITHUB_CONTEXT_END__
- name: Selective checks
id: selective-checks
env:
PR_LABELS: "[]"
COMMIT_REF: "${{ github.sha }}"
VERBOSE: "false"
GITHUB_CONTEXT_INPUT: "${{ runner.temp }}/github_context.json"
run: breeze ci selective-check 2>> ${GITHUB_OUTPUT}
build-ci-images:
name: "Build CI images"
needs: [build-info]
uses: ./.github/workflows/ci-image-build.yml
permissions:
contents: read
packages: write
with:
runners: '["ubuntu-22.04"]'
platform: "linux/amd64"
push-image: "false"
upload-image-artifact: "true"
upload-mount-cache-artifact: "false"
python-versions: ${{ needs.build-info.outputs.python-versions }}
branch: ${{ needs.build-info.outputs.default-branch }}
constraints-branch: ${{ needs.build-info.outputs.default-constraints-branch }}
use-uv: "true"
upgrade-to-newer-dependencies: "false"
docker-cache: "registry"
disable-airflow-repo-cache: "false"
generate-constraints:
name: "Generate constraints"
needs: [build-info, build-ci-images]
uses: ./.github/workflows/generate-constraints.yml
with:
runners: '["ubuntu-22.04"]'
platform: "linux/amd64"
python-versions-list-as-string: ${{ needs.build-info.outputs.python-versions-list-as-string }}
python-versions: ${{ needs.build-info.outputs.python-versions }}
generate-pypi-constraints: "true"
generate-no-providers-constraints: "true"
debug-resources: "false"
use-uv: "true"
secrets:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
update-constraints:
runs-on: ["ubuntu-22.04"]
timeout-minutes: 20
name: "Commit and push constraints"
needs: [build-info, generate-constraints]
# Shared with the other constraints publishers, so they never push to one branch at once.
concurrency:
group: constraints-publish-${{ needs.build-info.outputs.default-constraints-branch }}
cancel-in-progress: false
permissions:
contents: write
packages: read
env:
PYTHON_VERSIONS: ${{ needs.build-info.outputs.python-versions-list-as-string }}
steps:
- name: "Cleanup repo"
shell: bash
run: sudo rm -rf ${GITHUB_WORKSPACE}/*
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: "Set constraints branch name"
id: constraints-branch
run: ./scripts/ci/constraints/ci_branch_constraints.sh >> ${GITHUB_OUTPUT}
- name: Checkout ${{ steps.constraints-branch.outputs.branch }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: "constraints"
ref: ${{ steps.constraints-branch.outputs.branch }}
persist-credentials: true
fetch-depth: 0
- name: "Download constraints from the generate-constraints job"
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: constraints-*
path: ./files
- name: "Diff in constraints for Python: ${{ needs.build-info.outputs.python-versions-list-as-string }}"
run: ./scripts/ci/constraints/ci_diff_constraints.sh
- name: >-
Commit changed constraint files for Python:
${{ needs.build-info.outputs.python-versions-list-as-string }}
run: ./scripts/ci/constraints/ci_commit_constraints.sh
- name: "Push changes"
working-directory: "constraints"
run: git push
notify-on-failure:
name: "Notify on failure"
runs-on: ["ubuntu-slim"]
needs: [build-info, build-ci-images, generate-constraints, update-constraints]
if: failure()
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
steps:
- name: "Send Slack notification"
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
with:
method: chat.postMessage
token: ${{ env.SLACK_BOT_TOKEN }}
# yamllint disable rule:line-length
payload: |
channel: "internal-airflow-ci-cd"
text: "🚨 Update constraints workflow failed on branch *${{ github.ref_name }}*\n\n*Details:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View the failure log>"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "🚨 Update constraints workflow failed on *${{ github.ref_name }}*\n\n*Details:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View the failure log>"
# yamllint enable rule:line-length