name: Release on: workflow_call: inputs: source: required: false default: '' type: string target: required: false default: '' type: string version: required: false default: '' type: string linux_armv7l: required: false default: false type: boolean prerelease: required: false default: true type: boolean workflow_dispatch: inputs: source: description: | SOURCE of this release's updates: channel, repo, tag, or channel/repo@tag (default: ) required: false default: '' type: string target: description: | TARGET to publish this release to: channel, tag, or channel@tag (default: if writable else [@source_tag]) required: false default: '' type: string version: description: | VERSION: yyyy.mm.dd[.rev] or rev (default: auto-generated) required: false default: '' type: string linux_armv7l: description: Include linux_armv7l default: true type: boolean prerelease: description: Pre-release default: false type: boolean permissions: contents: read jobs: prepare: permissions: contents: write runs-on: ubuntu-latest outputs: channel: ${{ steps.setup_variables.outputs.channel }} version: ${{ steps.setup_variables.outputs.version }} target_repo: ${{ steps.setup_variables.outputs.target_repo }} target_repo_token: ${{ steps.setup_variables.outputs.target_repo_token }} target_tag: ${{ steps.setup_variables.outputs.target_tag }} pypi_project: ${{ steps.setup_variables.outputs.pypi_project }} pypi_suffix: ${{ steps.setup_variables.outputs.pypi_suffix }} head_sha: ${{ steps.get_target.outputs.head_sha }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v6 with: python-version: "3.10" # Keep this in sync with test-workflows.yml - name: Process inputs id: process_inputs env: INPUTS: ${{ toJSON(inputs) }} run: | python -m devscripts.setup_variables process_inputs - name: Setup variables id: setup_variables env: INPUTS: ${{ toJSON(inputs) }} PROCESSED: ${{ toJSON(steps.process_inputs.outputs) }} REPOSITORY: ${{ github.repository }} PUSH_VERSION_COMMIT: ${{ vars.PUSH_VERSION_COMMIT }} PYPI_PROJECT: ${{ vars.PYPI_PROJECT }} 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)] }} TARGET_PYPI_PROJECT: ${{ vars[format('{0}_pypi_project', steps.process_inputs.outputs.target_repo)] }} TARGET_PYPI_SUFFIX: ${{ vars[format('{0}_pypi_suffix', steps.process_inputs.outputs.target_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)] }} HAS_SOURCE_ARCHIVE_REPO_TOKEN: ${{ !!secrets[format('{0}_archive_repo_token', steps.process_inputs.outputs.source_repo)] }} HAS_TARGET_ARCHIVE_REPO_TOKEN: ${{ !!secrets[format('{0}_archive_repo_token', steps.process_inputs.outputs.target_repo)] }} HAS_ARCHIVE_REPO_TOKEN: ${{ !!secrets.ARCHIVE_REPO_TOKEN }} run: | python -m devscripts.setup_variables - name: Update version & documentation env: CHANNEL: ${{ steps.setup_variables.outputs.channel }} # Use base repo since this could be committed; build jobs will call this again with true origin REPOSITORY: ${{ github.repository }} VERSION: ${{ steps.setup_variables.outputs.version }} run: | python devscripts/update-version.py -c "${CHANNEL}" -r "${REPOSITORY}" "${VERSION}" python devscripts/update_changelog.py -vv make doc - name: Push to release id: push_release env: VERSION: ${{ steps.setup_variables.outputs.version }} GITHUB_EVENT_SENDER_LOGIN: ${{ github.event.sender.login }} GITHUB_EVENT_REF: ${{ github.event.ref }} if: | !inputs.prerelease && steps.setup_variables.outputs.target_repo == github.repository run: | git config --global user.name "github-actions[bot]" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git add -u git commit -m "Release ${VERSION}" \ -m "Created by: ${GITHUB_EVENT_SENDER_LOGIN}" -m ":ci skip all" git push origin --force "${GITHUB_EVENT_REF}:release" - name: Get target commitish id: get_target run: | 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: needs: prepare uses: ./.github/workflows/build.yml with: version: ${{ needs.prepare.outputs.version }} channel: ${{ needs.prepare.outputs.channel }} origin: ${{ needs.prepare.outputs.target_repo }} linux_armv7l: ${{ inputs.linux_armv7l }} permissions: contents: read secrets: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} publish_pypi: needs: [prepare, build] if: ${{ needs.prepare.outputs.pypi_project }} runs-on: ubuntu-latest permissions: id-token: write # mandatory for trusted publishing steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v6 with: python-version: "3.10" - name: Install Requirements run: | sudo apt -y install pandoc man python devscripts/install_deps.py -o --include build - name: Prepare env: VERSION: ${{ needs.prepare.outputs.version }} SUFFIX: ${{ needs.prepare.outputs.pypi_suffix }} CHANNEL: ${{ needs.prepare.outputs.channel }} TARGET_REPO: ${{ needs.prepare.outputs.target_repo }} PYPI_PROJECT: ${{ needs.prepare.outputs.pypi_project }} run: | python devscripts/update-version.py -c "${CHANNEL}" -r "${TARGET_REPO}" -s "${SUFFIX}" "${VERSION}" python devscripts/update_changelog.py -vv python devscripts/make_lazy_extractors.py sed -i -E '0,/(name = ")[^"]+(")/s//\1'"${PYPI_PROJECT}"'\2/' pyproject.toml - name: Build run: | rm -rf dist/* make pypi-files printf '%s\n\n' \ 'Official repository: ' \ '**PS**: Some links in this document will not work since this is a copy of the README.md from Github' > ./README.md.new cat ./README.md >> ./README.md.new && mv -f ./README.md.new ./README.md python devscripts/set-variant.py pip -M "You installed yt-dlp with pip or using the wheel from PyPi; Use that to update" make clean-cache python -m build --no-isolation . - name: Upload artifacts if: github.event_name != 'workflow_dispatch' uses: actions/upload-artifact@v4 with: name: build-pypi path: | dist/* compression-level: 0 - name: Publish to PyPI if: github.event_name == 'workflow_dispatch' uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: true publish: needs: [prepare, build] permissions: contents: write runs-on: ubuntu-latest env: TARGET_REPO: ${{ needs.prepare.outputs.target_repo }} TARGET_TAG: ${{ needs.prepare.outputs.target_tag }} VERSION: ${{ needs.prepare.outputs.version }} HEAD_SHA: ${{ needs.prepare.outputs.head_sha }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/download-artifact@v4 with: path: artifact pattern: build-* merge-multiple: true - uses: actions/setup-python@v6 with: python-version: "3.10" - name: Generate release notes env: REPOSITORY: ${{ github.repository }} BASE_REPO: yt-dlp/yt-dlp NIGHTLY_REPO: yt-dlp/yt-dlp-nightly-builds MASTER_REPO: yt-dlp/yt-dlp-master-builds DOCS_PATH: ${{ env.TARGET_REPO == github.repository && format('/tree/{0}', env.TARGET_TAG) || '' }} run: | printf '%s' \ "[![Installation](https://img.shields.io/badge/-Which%20file%20to%20download%3F-white.svg?style=for-the-badge)]" \ "(https://github.com/${REPOSITORY}#installation \"Installation instructions\") " \ "[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)]" \ "(https://discord.gg/H5MNcFW63r \"Discord\") " \ "[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)]" \ "(https://github.com/${BASE_REPO}/blob/master/Collaborators.md#collaborators \"Donate\") " \ "[![Documentation](https://img.shields.io/badge/-Docs-brightgreen.svg?style=for-the-badge&logo=GitBook&labelColor=555555)]" \ "(https://github.com/${REPOSITORY}${DOCS_PATH}#readme \"Documentation\") " > ./RELEASE_NOTES if [[ "${TARGET_REPO}" == "${BASE_REPO}" ]]; then printf '%s' \ "[![Nightly](https://img.shields.io/badge/Nightly%20builds-purple.svg?style=for-the-badge)]" \ "(https://github.com/${NIGHTLY_REPO}/releases/latest \"Nightly builds\") " \ "[![Master](https://img.shields.io/badge/Master%20builds-lightblue.svg?style=for-the-badge)]" \ "(https://github.com/${MASTER_REPO}/releases/latest \"Master builds\")" >> ./RELEASE_NOTES fi printf '\n\n%s\n\n%s%s\n\n---\n' \ "#### A description of the various files is in the [README](https://github.com/${REPOSITORY}#release-files)" \ "The PyInstaller-bundled executables are subject to the licenses described in " \ "[THIRD_PARTY_LICENSES.txt](https://github.com/${BASE_REPO}/blob/${HEAD_SHA}/THIRD_PARTY_LICENSES.txt)" >> ./RELEASE_NOTES python ./devscripts/make_changelog.py -vv --collapsible >> ./RELEASE_NOTES printf '%s\n\n' '**This is a pre-release build**' >> ./PRERELEASE_NOTES cat ./RELEASE_NOTES >> ./PRERELEASE_NOTES printf '%s\n\n' "Generated from: https://github.com/${REPOSITORY}/commit/${HEAD_SHA}" >> ./ARCHIVE_NOTES cat ./RELEASE_NOTES >> ./ARCHIVE_NOTES - name: Publish to archive repo env: GH_TOKEN: ${{ secrets[needs.prepare.outputs.target_repo_token] }} GH_REPO: ${{ needs.prepare.outputs.target_repo }} TITLE_PREFIX: ${{ startswith(env.TARGET_REPO, 'yt-dlp/') && 'yt-dlp ' || '' }} TITLE: ${{ inputs.target != env.TARGET_REPO && inputs.target || needs.prepare.outputs.channel }} if: | inputs.prerelease && env.GH_TOKEN && env.GH_REPO && env.GH_REPO != github.repository run: | gh release create \ --notes-file ARCHIVE_NOTES \ --title "${TITLE_PREFIX}${TITLE} ${VERSION}" \ "${VERSION}" \ artifact/* - name: Prune old release env: GH_TOKEN: ${{ github.token }} if: | env.TARGET_REPO == github.repository && env.TARGET_TAG != env.VERSION run: | gh release delete --yes --cleanup-tag "${TARGET_TAG}" || true git tag --delete "${TARGET_TAG}" || true sleep 5 # Enough time to cover deletion race condition - name: Publish release env: GH_TOKEN: ${{ github.token }} NOTES_FILE: ${{ inputs.prerelease && 'PRERELEASE_NOTES' || 'RELEASE_NOTES' }} TITLE_PREFIX: ${{ github.repository == 'yt-dlp/yt-dlp' && 'yt-dlp ' || '' }} TITLE: ${{ env.TARGET_TAG != env.VERSION && format('{0} ', env.TARGET_TAG) || '' }} PRERELEASE: ${{ inputs.prerelease && '1' || '0' }} if: | env.TARGET_REPO == github.repository run: | gh_options=( --notes-file "${NOTES_FILE}" --target "${HEAD_SHA}" --title "${TITLE_PREFIX}${TITLE}${VERSION}" ) if ((PRERELEASE)); then gh_options+=(--prerelease) fi gh release create "${gh_options[@]}" "${TARGET_TAG}" artifact/*