[build] Harden release workflow (#16444)

* Prepare to remove the `release` branch
* Remove use of PUSH_VERSION_COMMIT variable
* Use RELEASE_KEY for releases

Authored by: bashonly
This commit is contained in:
bashonly 2026-04-07 18:32:29 -05:00 committed by GitHub
parent 8001ff4349
commit a4acc42232
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 28 additions and 25 deletions

View File

@ -1,5 +1,4 @@
config-variables: config-variables:
- PUSH_VERSION_COMMIT
- UPDATE_TO_VERIFICATION - UPDATE_TO_VERIFICATION
- PYPI_PROJECT - PYPI_PROJECT
- PYPI_SUFFIX - PYPI_SUFFIX

View File

@ -27,6 +27,8 @@ on:
required: false required: false
GPG_SIGNING_KEY: GPG_SIGNING_KEY:
required: false required: false
RELEASE_KEY:
required: false
workflow_dispatch: workflow_dispatch:
inputs: inputs:
source: source:
@ -67,7 +69,7 @@ jobs:
prepare: prepare:
name: Prepare name: Prepare
permissions: permissions:
contents: write # Needed to git-push the release commit contents: read # Push via SSH; HTTPS write token is not needed
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
channel: ${{ steps.setup_variables.outputs.channel }} channel: ${{ steps.setup_variables.outputs.channel }}
@ -83,6 +85,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: true # Needed to git-push the release commit persist-credentials: true # Needed to git-push the release commit
ssh-key: ${{ secrets.RELEASE_KEY }}
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
@ -101,7 +104,6 @@ jobs:
INPUTS: ${{ toJSON(inputs) }} INPUTS: ${{ toJSON(inputs) }}
PROCESSED: ${{ toJSON(steps.process_inputs.outputs) }} PROCESSED: ${{ toJSON(steps.process_inputs.outputs) }}
REPOSITORY: ${{ github.repository }} REPOSITORY: ${{ github.repository }}
PUSH_VERSION_COMMIT: ${{ vars.PUSH_VERSION_COMMIT }}
PYPI_PROJECT: ${{ vars.PYPI_PROJECT }} PYPI_PROJECT: ${{ vars.PYPI_PROJECT }}
SOURCE_PYPI_PROJECT: ${{ vars[format('{0}_pypi_project', steps.process_inputs.outputs.source_repo)] }} SOURCE_PYPI_PROJECT: ${{ vars[format('{0}_pypi_project', steps.process_inputs.outputs.source_repo)] }}
SOURCE_PYPI_SUFFIX: ${{ vars[format('{0}_pypi_suffix', steps.process_inputs.outputs.source_repo)] }} SOURCE_PYPI_SUFFIX: ${{ vars[format('{0}_pypi_suffix', steps.process_inputs.outputs.source_repo)] }}
@ -110,6 +112,7 @@ jobs:
SOURCE_ARCHIVE_REPO: ${{ vars[format('{0}_archive_repo', steps.process_inputs.outputs.source_repo)] }} SOURCE_ARCHIVE_REPO: ${{ vars[format('{0}_archive_repo', steps.process_inputs.outputs.source_repo)] }}
TARGET_ARCHIVE_REPO: ${{ vars[format('{0}_archive_repo', steps.process_inputs.outputs.target_repo)] }} TARGET_ARCHIVE_REPO: ${{ vars[format('{0}_archive_repo', steps.process_inputs.outputs.target_repo)] }}
HAS_ARCHIVE_REPO_TOKEN: ${{ !!secrets.ARCHIVE_REPO_TOKEN }} HAS_ARCHIVE_REPO_TOKEN: ${{ !!secrets.ARCHIVE_REPO_TOKEN }}
HAS_RELEASE_KEY: ${{ !!secrets.RELEASE_KEY }}
run: | run: |
python -m devscripts.setup_variables python -m devscripts.setup_variables
@ -124,32 +127,26 @@ jobs:
python devscripts/update_changelog.py -vv python devscripts/update_changelog.py -vv
make doc make doc
- name: Push to release - name: Push release commit
id: push_release
env: env:
VERSION: ${{ steps.setup_variables.outputs.version }} VERSION: ${{ steps.setup_variables.outputs.version }}
GITHUB_EVENT_SENDER_LOGIN: ${{ github.event.sender.login }} GITHUB_EVENT_SENDER_LOGIN: ${{ github.event.sender.login }}
GITHUB_EVENT_REF: ${{ github.event.ref }} GITHUB_EVENT_REF: ${{ github.event.ref }}
if: steps.setup_variables.outputs.target_repo == github.repository && !inputs.prerelease RELEASE_KEY: ${{ secrets.RELEASE_KEY }}
if: steps.setup_variables.outputs.target_repo == github.repository && env.RELEASE_KEY && !inputs.prerelease
run: | run: |
git config --global user.name "github-actions[bot]" git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -u git add -u
git commit -m "Release ${VERSION}" \ git commit -m "Release ${VERSION}" \
-m "Created by: ${GITHUB_EVENT_SENDER_LOGIN}" -m ":ci skip all" -m "Created by: ${GITHUB_EVENT_SENDER_LOGIN}" -m ":ci skip all"
git push origin --force "${GITHUB_EVENT_REF}:release" git push origin "${GITHUB_EVENT_REF}"
- name: Get target commitish - name: Get target commitish
id: get_target id: get_target
run: | run: |
echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Update master
env:
GITHUB_EVENT_REF: ${{ github.event.ref }}
if: vars.PUSH_VERSION_COMMIT && !inputs.prerelease && steps.setup_variables.outputs.target_repo == github.repository
run: git push origin "${GITHUB_EVENT_REF}"
build: build:
name: Build name: Build
needs: [prepare] needs: [prepare]
@ -243,7 +240,8 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: true # Needed to git-push the release tag
ssh-key: ${{ secrets.RELEASE_KEY }}
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with: with:
path: artifact path: artifact
@ -311,6 +309,13 @@ jobs:
git tag --delete "${TARGET_TAG}" || true git tag --delete "${TARGET_TAG}" || true
sleep 5 # Enough time to cover deletion race condition sleep 5 # Enough time to cover deletion race condition
- name: Push tag
if: env.TARGET_REPO == github.repository
run: |
git tag "${TARGET_TAG}" "${HEAD_SHA}"
git push origin "${TARGET_TAG}"
sleep 5 # Enough time to cover git-push vs gh-release-create race condition
- name: Publish release - name: Publish release
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
@ -322,7 +327,7 @@ jobs:
run: | run: |
gh_options=( gh_options=(
--notes-file "${NOTES_FILE}" --notes-file "${NOTES_FILE}"
--target "${HEAD_SHA}" --verify-tag
--title "${TITLE_PREFIX}${TITLE}${VERSION}" --title "${TITLE_PREFIX}${TITLE}${VERSION}"
) )
if ((PRERELEASE)); then if ((PRERELEASE)); then

View File

@ -16,12 +16,11 @@ STABLE_REPOSITORY = 'yt-dlp/yt-dlp'
def setup_variables(environment): def setup_variables(environment):
""" """
`environment` must contain these keys: `environment` must contain these keys:
REPOSITORY, INPUTS, PROCESSED, REPOSITORY, INPUTS, PROCESSED, PYPI_PROJECT,
PUSH_VERSION_COMMIT, PYPI_PROJECT,
SOURCE_PYPI_PROJECT, SOURCE_PYPI_SUFFIX, SOURCE_PYPI_PROJECT, SOURCE_PYPI_SUFFIX,
TARGET_PYPI_PROJECT, TARGET_PYPI_SUFFIX, TARGET_PYPI_PROJECT, TARGET_PYPI_SUFFIX,
SOURCE_ARCHIVE_REPO, TARGET_ARCHIVE_REPO, SOURCE_ARCHIVE_REPO, TARGET_ARCHIVE_REPO,
HAS_ARCHIVE_REPO_TOKEN HAS_ARCHIVE_REPO_TOKEN, HAS_RELEASE_KEY
`INPUTS` must contain these keys: `INPUTS` must contain these keys:
prerelease prerelease
@ -57,7 +56,7 @@ def setup_variables(environment):
resolved_source = 'stable' resolved_source = 'stable'
revision = None revision = None
if INPUTS['prerelease'] or not environment['PUSH_VERSION_COMMIT']: if INPUTS['prerelease'] or not json.loads(environment['HAS_RELEASE_KEY']):
revision = dt.datetime.now(tz=dt.timezone.utc).strftime('%H%M%S') revision = dt.datetime.now(tz=dt.timezone.utc).strftime('%H%M%S')
version = calculate_version(INPUTS.get('version') or revision) version = calculate_version(INPUTS.get('version') or revision)

View File

@ -27,7 +27,6 @@ def _test(github_repository, note, repo_vars, repo_secrets, inputs, expected, ig
'INPUTS': json.dumps(inp), 'INPUTS': json.dumps(inp),
'PROCESSED': json.dumps(processed), 'PROCESSED': json.dumps(processed),
'REPOSITORY': github_repository, 'REPOSITORY': github_repository,
'PUSH_VERSION_COMMIT': variables.get('PUSH_VERSION_COMMIT') or '',
'PYPI_PROJECT': variables.get('PYPI_PROJECT') or '', 'PYPI_PROJECT': variables.get('PYPI_PROJECT') or '',
'SOURCE_PYPI_PROJECT': variables.get(f'{source_repo}_PYPI_PROJECT') or '', 'SOURCE_PYPI_PROJECT': variables.get(f'{source_repo}_PYPI_PROJECT') or '',
'SOURCE_PYPI_SUFFIX': variables.get(f'{source_repo}_PYPI_SUFFIX') or '', 'SOURCE_PYPI_SUFFIX': variables.get(f'{source_repo}_PYPI_SUFFIX') or '',
@ -36,6 +35,7 @@ def _test(github_repository, note, repo_vars, repo_secrets, inputs, expected, ig
'SOURCE_ARCHIVE_REPO': variables.get(f'{source_repo}_ARCHIVE_REPO') or '', 'SOURCE_ARCHIVE_REPO': variables.get(f'{source_repo}_ARCHIVE_REPO') or '',
'TARGET_ARCHIVE_REPO': variables.get(f'{target_repo}_ARCHIVE_REPO') or '', 'TARGET_ARCHIVE_REPO': variables.get(f'{target_repo}_ARCHIVE_REPO') or '',
'HAS_ARCHIVE_REPO_TOKEN': json.dumps(bool(secrets.get('ARCHIVE_REPO_TOKEN'))), 'HAS_ARCHIVE_REPO_TOKEN': json.dumps(bool(secrets.get('ARCHIVE_REPO_TOKEN'))),
'HAS_RELEASE_KEY': json.dumps(bool(secrets.get('RELEASE_KEY'))),
} }
result = setup_variables(env) result = setup_variables(env)
@ -67,11 +67,11 @@ def test_setup_variables():
'NIGHTLY_ARCHIVE_REPO': 'yt-dlp/yt-dlp-nightly-builds', 'NIGHTLY_ARCHIVE_REPO': 'yt-dlp/yt-dlp-nightly-builds',
'NIGHTLY_PYPI_PROJECT': 'yt-dlp', 'NIGHTLY_PYPI_PROJECT': 'yt-dlp',
'NIGHTLY_PYPI_SUFFIX': 'dev', 'NIGHTLY_PYPI_SUFFIX': 'dev',
'PUSH_VERSION_COMMIT': '1',
'PYPI_PROJECT': 'yt-dlp', 'PYPI_PROJECT': 'yt-dlp',
} }
BASE_REPO_SECRETS = { BASE_REPO_SECRETS = {
'ARCHIVE_REPO_TOKEN': '1', 'ARCHIVE_REPO_TOKEN': '1',
'RELEASE_KEY': '1',
} }
FORK_REPOSITORY = 'fork/yt-dlp' FORK_REPOSITORY = 'fork/yt-dlp'
FORK_ORG = FORK_REPOSITORY.partition('/')[0] FORK_ORG = FORK_REPOSITORY.partition('/')[0]
@ -227,8 +227,8 @@ def test_setup_variables():
}) })
_test( _test(
FORK_REPOSITORY, 'fork w/ PUSH_VERSION_COMMIT, stable', FORK_REPOSITORY, 'fork w/ RELEASE_KEY, stable',
{'PUSH_VERSION_COMMIT': '1'}, {}, {}, { {}, {'RELEASE_KEY': '1'}, {}, {
'channel': FORK_REPOSITORY, 'channel': FORK_REPOSITORY,
'version': DEFAULT_VERSION, 'version': DEFAULT_VERSION,
'target_repo': FORK_REPOSITORY, 'target_repo': FORK_REPOSITORY,
@ -237,8 +237,8 @@ def test_setup_variables():
'pypi_suffix': None, 'pypi_suffix': None,
}) })
_test( _test(
FORK_REPOSITORY, 'fork w/ PUSH_VERSION_COMMIT, prerelease', FORK_REPOSITORY, 'fork w/ RELEASE_KEY, prerelease',
{'PUSH_VERSION_COMMIT': '1'}, {}, {'prerelease': True}, { {}, {'RELEASE_KEY': '1'}, {'prerelease': True}, {
'channel': FORK_REPOSITORY, 'channel': FORK_REPOSITORY,
'version': DEFAULT_VERSION_WITH_REVISION, 'version': DEFAULT_VERSION_WITH_REVISION,
'target_repo': FORK_REPOSITORY, 'target_repo': FORK_REPOSITORY,