Repository navigation
280 lines (277 loc) · 12.6 KB
/
Copy pathrelease-constraints.yml
File metadata and controls
280 lines (277 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# 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.
#
# Resolves the constraints that a release ships, rather than tagging whatever the
# `constraints-X-Y` branch happened to hold when the release ran.
#
# The stage is derived from the version, so the two cannot be mismatched by hand:
#
# * a pre-release - beta (`3.4.0b1`) or candidate (`3.3.1rc1`) - pins the providers at the versions
# PyPI holds (rc provider waves included) and lands on a branch of its own, so it never moves the
# branch every other build reads. Re-running it for the same pre-release replaces that branch and
# its tag, so the two always describe the run that produced them;
# * a final (`3.3.1`) pins the providers at their released versions, ignoring any candidate, and
# commits onto `constraints-X-Y` itself, which is what makes the released constraints the
# baseline everything downstream reads.
#
# A final therefore cannot be produced by retagging a pre-release: a released version must never
# pin an rc.
---
name: Release constraints
on: # yamllint disable-line rule:truthy
workflow_dispatch:
inputs:
version:
description: "Version the constraints belong to, e.g. 3.4.0b1, 3.3.1rc1 or 3.3.1"
required: true
type: string
ref:
description: "Ref the constraints are resolved from, e.g. v3-3-stable or the release tag"
required: true
type: string
permissions:
contents: read
concurrency:
group: release-constraints-${{ inputs.version }}
cancel-in-progress: false
jobs:
build-info:
timeout-minutes: 10
name: "Build info"
runs-on: ["ubuntu-22.04"]
if: contains(fromJSON('[
"ashb",
"eladkal",
"ephraimbuddy",
"jedcunningham",
"kaxil",
"pierrejeambrun",
"potiuk",
"utkarsharma2",
"vatsrahul1001",
"vincbeck",
]'), github.event.sender.login)
outputs:
python-versions: ${{ steps.selective-checks.outputs.python-versions }}
python-versions-list-as-string: ${{ steps.selective-checks.outputs.python-versions-list-as-string }}
default-branch: ${{ steps.selective-checks.outputs.default-branch }}
default-constraints-branch: ${{ steps.selective-checks.outputs.default-constraints-branch }}
constraints-branch: ${{ steps.stage.outputs.constraints-branch }}
target-branch: ${{ steps.stage.outputs.target-branch }}
allow-pre-releases: ${{ steps.stage.outputs.allow-pre-releases }}
steps:
- name: "Cleanup repo"
shell: bash
run: sudo rm -rf ${GITHUB_WORKSPACE}/*
- name: "Checkout ${{ inputs.ref }}"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.ref }}
fetch-depth: 2
persist-credentials: false
- name: "Install Breeze"
uses: ./.github/actions/breeze
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: "${{ inputs.ref }}"
VERBOSE: "false"
GITHUB_CONTEXT_INPUT: "${{ runner.temp }}/github_context.json"
run: breeze ci selective-check 2>> ${GITHUB_OUTPUT}
- name: "Derive the release stage from the version"
id: stage
shell: bash
env:
VERSION: ${{ inputs.version }}
run: |
if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+((b|rc)[0-9]+)?$ ]]; then
echo "'${VERSION}' is not a release version - expected X.Y.Z, X.Y.ZbN or X.Y.ZrcN." >&2
exit 1
fi
major="$(echo "${VERSION}" | cut -d. -f1)"
minor="$(echo "${VERSION}" | cut -d. -f2)"
constraints_branch="constraints-${major}-${minor}"
echo "constraints-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
# A pre-release - beta or rc - pins the providers at the versions PyPI holds (rc provider
# waves included) and lands on a branch and tag of its own, so it never moves the shared
# constraints-X-Y branch. A final pins released providers and commits onto the shared branch.
if [[ "${VERSION}" =~ (b|rc)[0-9]+$ ]]; then
echo "allow-pre-releases=true" >> "${GITHUB_OUTPUT}"
echo "target-branch=constraints-${VERSION}" >> "${GITHUB_OUTPUT}"
else
echo "allow-pre-releases=false" >> "${GITHUB_OUTPUT}"
echo "target-branch=${constraints_branch}" >> "${GITHUB_OUTPUT}"
fi
- name: "Parameters summary"
shell: bash
env:
VERSION: ${{ inputs.version }}
REF: ${{ inputs.ref }}
CONSTRAINTS_BRANCH: ${{ steps.stage.outputs.constraints-branch }}
TARGET_BRANCH: ${{ steps.stage.outputs.target-branch }}
ALLOW_PRE_RELEASES: ${{ steps.stage.outputs.allow-pre-releases }}
run: |
{
echo "## Release constraints"
echo ""
echo "| Parameter | Value |"
echo "|---|---|"
echo "| Version | \`${VERSION}\` |"
echo "| Resolved from ref | \`${REF}\` |"
echo "| Branched off | \`${CONSTRAINTS_BRANCH}\` |"
echo "| Committed to | \`${TARGET_BRANCH}\` |"
echo "| Pre-releases allowed | \`${ALLOW_PRE_RELEASES}\` |"
echo "| Tagged | \`constraints-${VERSION}\` |"
} | tee -a "${GITHUB_STEP_SUMMARY}"
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 }}
checkout-ref: ${{ inputs.ref }}
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"
# Only the PyPI constraints are what a release ships; the other modes serve CI, and
# regenerating them here would move them for reasons unrelated to the release.
generate-no-providers-constraints: "false"
# A release pins the providers as published on PyPI, so nothing is built from the sources
# here: a locally built provider wheel would answer the resolution and then be left out of
# the constraints, which is exactly the version the release needed pinned.
pypi-providers-only: "true"
allow-pre-releases: ${{ needs.build-info.outputs.allow-pre-releases }}
debug-resources: "false"
checkout-ref: ${{ inputs.ref }}
use-uv: "true"
secrets:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
publish-constraints:
runs-on: ["ubuntu-22.04"]
timeout-minutes: 20
name: "Publish and tag constraints"
needs: [build-info, generate-constraints]
permissions:
contents: write
packages: read
env:
VERSION: ${{ inputs.version }}
CONSTRAINTS_BRANCH: ${{ needs.build-info.outputs.constraints-branch }}
TARGET_BRANCH: ${{ needs.build-info.outputs.target-branch }}
ALLOW_PRE_RELEASES: ${{ needs.build-info.outputs.allow-pre-releases }}
steps:
- name: "Cleanup repo"
shell: bash
run: sudo rm -rf ${GITHUB_WORKSPACE}/*
- name: "Checkout ${{ inputs.ref }}"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.ref }}
persist-credentials: false
# Branched off the shared constraints branch in both cases; what differs is where the commit
# ends up, which the checkout below decides.
- name: "Checkout ${{ needs.build-info.outputs.constraints-branch }}"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: "constraints"
ref: ${{ needs.build-info.outputs.constraints-branch }}
persist-credentials: true
fetch-depth: 0
- name: "Download constraints from the generate-constraints job"
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: constraints-*
path: ./files
# A candidate's branch and tag belong to that candidate alone, so re-running for the same
# rc replaces them rather than adding to them: the branch would otherwise already hold the
# previous run's constraints (making the push a non-fast-forward) and the tag already exist.
# A final never gets this - it commits onto the shared constraints-X-Y branch, whose history
# every other build reads.
- name: "Delete the previous ${{ needs.build-info.outputs.target-branch }} branch and tag"
if: needs.build-info.outputs.allow-pre-releases == 'true'
working-directory: "constraints"
shell: bash
run: |
if git ls-remote --exit-code origin "refs/heads/${TARGET_BRANCH}" > /dev/null; then
echo "Deleting the existing '${TARGET_BRANCH}' branch."
git push origin --delete "refs/heads/${TARGET_BRANCH}"
fi
if git ls-remote --exit-code origin "refs/tags/constraints-${VERSION}" > /dev/null; then
echo "Deleting the existing 'constraints-${VERSION}' tag."
git push origin --delete "refs/tags/constraints-${VERSION}"
fi
git tag --delete "constraints-${VERSION}" > /dev/null 2>&1 || true
git branch --delete --force "${TARGET_BRANCH}" > /dev/null 2>&1 || true
- name: "Switch to ${{ needs.build-info.outputs.target-branch }}"
working-directory: "constraints"
shell: bash
run: git switch -c "${TARGET_BRANCH}" 2>/dev/null || git switch "${TARGET_BRANCH}"
- 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"
run: ./scripts/ci/constraints/ci_commit_constraints.sh
- name: "Push ${{ needs.build-info.outputs.target-branch }}"
working-directory: "constraints"
shell: bash
# Qualify as refs/heads/ -- for a release candidate the branch and the tag share a name
# (both constraints-<VERSION>), so a bare refspec is ambiguous ("matches more than one").
run: git push origin "refs/heads/${TARGET_BRANCH}"
- name: "Tag constraints-${{ inputs.version }}"
working-directory: "constraints"
shell: bash
run: |
git tag -a "constraints-${VERSION}" -m "Constraints for Apache Airflow ${VERSION}"
git push origin "refs/tags/constraints-${VERSION}"
- name: "Summary"
shell: bash
run: |
{
echo "Constraints for \`${VERSION}\` are on \`${TARGET_BRANCH}\`,"
echo "tagged \`constraints-${VERSION}\`."
} | tee -a "${GITHUB_STEP_SUMMARY}"