From 1d03633c5a1621b9f3a756f0a4f9dc61fab3aeaa Mon Sep 17 00:00:00 2001 From: bashonly Date: Sun, 12 Nov 2023 17:49:15 -0600 Subject: [PATCH] [build] Overhaul and unify release workflow Authored by: bashonly, Grub4K Co-authored-by: Simon Sawicki --- .github/workflows/build.yml | 4 +- .github/workflows/publish.yml | 97 ------- .github/workflows/release-master.yml | 28 ++ .github/workflows/release-nightly.yml | 55 ++-- .github/workflows/release.yml | 352 +++++++++++++++++++++----- devscripts/update-formulae.py | 39 --- devscripts/update-version.py | 15 +- devscripts/utils.py | 7 +- setup.py | 4 +- yt_dlp/update.py | 1 + yt_dlp/version.py | 4 + 11 files changed, 365 insertions(+), 241 deletions(-) delete mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release-master.yml delete mode 100644 devscripts/update-formulae.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac0cfdf7c..c9260eeca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,9 +96,7 @@ jobs: auto-activate-base: false - name: Install Requirements run: | - sudo apt-get -y install zip pandoc man sed - python -m pip install -U pip setuptools wheel - python -m pip install -U Pyinstaller -r requirements.txt + sudo apt -y install zip pandoc man sed reqs=$(mktemp) cat > $reqs << EOF python=3.10.* diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 9ebf54e7f..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Publish -on: - workflow_call: - inputs: - channel: - default: stable - required: true - type: string - version: - required: true - type: string - target_commitish: - required: true - type: string - prerelease: - default: false - required: true - type: boolean - secrets: - ARCHIVE_REPO_TOKEN: - required: false - -permissions: - contents: write - -jobs: - publish: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - uses: actions/download-artifact@v3 - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - - name: Generate release notes - run: | - printf '%s' \ - '[![Installation](https://img.shields.io/badge/-Which%20file%20should%20I%20download%3F-white.svg?style=for-the-badge)]' \ - '(https://github.com/yt-dlp/yt-dlp#installation "Installation instructions") ' \ - '[![Documentation](https://img.shields.io/badge/-Docs-brightgreen.svg?style=for-the-badge&logo=GitBook&labelColor=555555)]' \ - '(https://github.com/yt-dlp/yt-dlp/tree/2023.03.04#readme "Documentation") ' \ - '[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)]' \ - '(https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators "Donate") ' \ - '[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)]' \ - '(https://discord.gg/H5MNcFW63r "Discord") ' \ - ${{ inputs.channel != 'nightly' && '"[![Nightly](https://img.shields.io/badge/Get%20nightly%20builds-purple.svg?style=for-the-badge)]" \ - "(https://github.com/yt-dlp/yt-dlp-nightly-builds/releases/latest \"Nightly builds\")"' || '' }} \ - > ./RELEASE_NOTES - printf '\n\n' >> ./RELEASE_NOTES - cat >> ./RELEASE_NOTES << EOF - #### A description of the various files are in the [README](https://github.com/yt-dlp/yt-dlp#release-files) - --- - $(python ./devscripts/make_changelog.py -vv --collapsible) - EOF - printf '%s\n\n' '**This is an automated nightly pre-release build**' >> ./NIGHTLY_NOTES - cat ./RELEASE_NOTES >> ./NIGHTLY_NOTES - printf '%s\n\n' 'Generated from: https://github.com/${{ github.repository }}/commit/${{ inputs.target_commitish }}' >> ./ARCHIVE_NOTES - cat ./RELEASE_NOTES >> ./ARCHIVE_NOTES - - - name: Archive nightly release - env: - GH_TOKEN: ${{ secrets.ARCHIVE_REPO_TOKEN }} - GH_REPO: ${{ vars.ARCHIVE_REPO }} - if: | - inputs.channel == 'nightly' && env.GH_TOKEN != '' && env.GH_REPO != '' - run: | - gh release create \ - --notes-file ARCHIVE_NOTES \ - --title "yt-dlp nightly ${{ inputs.version }}" \ - ${{ inputs.version }} \ - artifact/* - - - name: Prune old nightly release - if: inputs.channel == 'nightly' && !vars.ARCHIVE_REPO - env: - GH_TOKEN: ${{ github.token }} - run: | - gh release delete --yes --cleanup-tag "nightly" || true - git tag --delete "nightly" || true - sleep 5 # Enough time to cover deletion race condition - - - name: Publish release${{ inputs.channel == 'nightly' && ' (nightly)' || '' }} - env: - GH_TOKEN: ${{ github.token }} - if: (inputs.channel == 'nightly' && !vars.ARCHIVE_REPO) || inputs.channel != 'nightly' - run: | - gh release create \ - --notes-file ${{ inputs.channel == 'nightly' && 'NIGHTLY_NOTES' || 'RELEASE_NOTES' }} \ - --target ${{ inputs.target_commitish }} \ - --title "yt-dlp ${{ inputs.channel == 'nightly' && 'nightly ' || '' }}${{ inputs.version }}" \ - ${{ inputs.prerelease && '--prerelease' || '' }} \ - ${{ inputs.channel == 'nightly' && '"nightly"' || inputs.version }} \ - artifact/* diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml new file mode 100644 index 000000000..0208b3bef --- /dev/null +++ b/.github/workflows/release-master.yml @@ -0,0 +1,28 @@ +name: Release (master) +on: + push: + branches: + - master + paths: + - "yt_dlp/**.py" + - "!yt_dlp/version.py" + - "setup.py" + - "pyinst.py" +concurrency: + group: release-master + cancel-in-progress: true +permissions: + contents: read + +jobs: + release: + if: vars.BUILD_MASTER != '' + uses: ./.github/workflows/release.yml + with: + prerelease: true + source: master + permissions: + contents: write + packages: write + id-token: write # mandatory for trusted publishing + secrets: inherit diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 543e2e6f7..947eada27 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -1,52 +1,35 @@ name: Release (nightly) on: - push: - branches: - - master - paths: - - "yt_dlp/**.py" - - "!yt_dlp/version.py" -concurrency: - group: release-nightly - cancel-in-progress: true + schedule: + - cron: '23 23 * * *' permissions: contents: read jobs: - prepare: + check_nightly: if: vars.BUILD_NIGHTLY != '' runs-on: ubuntu-latest outputs: - version: ${{ steps.get_version.outputs.version }} - + commit: ${{ steps.check_for_new_commits.outputs.commit }} steps: - uses: actions/checkout@v3 - - name: Get version - id: get_version + with: + fetch-depth: 0 + - name: Check for new commits + id: check_for_new_commits run: | - python devscripts/update-version.py "$(date -u +"%H%M%S")" | grep -Po "version=\d+(\.\d+){3}" >> "$GITHUB_OUTPUT" + relevant_files=("yt_dlp/*.py" ':!yt_dlp/version.py' "setup.py" "pyinst.py") + echo "commit=$(git log --format=%H -1 --since="24 hours ago" -- "${relevant_files[@]}")" | tee "$GITHUB_OUTPUT" - build: - needs: prepare - uses: ./.github/workflows/build.yml + release: + needs: [check_nightly] + if: ${{ needs.check_nightly.outputs.commit }} + uses: ./.github/workflows/release.yml with: - version: ${{ needs.prepare.outputs.version }} - channel: nightly - permissions: - contents: read - packages: write # For package cache - secrets: - GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} - - publish: - needs: [prepare, build] - uses: ./.github/workflows/publish.yml - secrets: - ARCHIVE_REPO_TOKEN: ${{ secrets.ARCHIVE_REPO_TOKEN }} + prerelease: true + source: nightly permissions: contents: write - with: - channel: nightly - prerelease: true - version: ${{ needs.prepare.outputs.version }} - target_commitish: ${{ github.sha }} + packages: write + id-token: write # mandatory for trusted publishing + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ada508be8..0e50b74e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,14 +1,45 @@ name: Release on: - workflow_dispatch: + workflow_call: inputs: - version: - description: Version tag (YYYY.MM.DD[.REV]) + prerelease: + required: false + default: true + type: boolean + source: required: false default: '' type: string - channel: - description: Update channel (stable/nightly/...) + target: + required: false + default: '' + type: string + version: + required: false + default: '' + type: string + 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 @@ -26,8 +57,14 @@ jobs: contents: write runs-on: ubuntu-latest outputs: - channel: ${{ steps.set_channel.outputs.channel }} - version: ${{ steps.update_version.outputs.version }} + 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 }} + pypi_token: ${{ steps.setup_variables.outputs.pypi_token }} head_sha: ${{ steps.get_target.outputs.head_sha }} steps: @@ -39,25 +76,132 @@ jobs: with: python-version: "3.10" - - name: Set channel - id: set_channel + - name: Process inputs + id: process_inputs run: | - CHANNEL="${{ github.repository == 'yt-dlp/yt-dlp' && 'stable' || github.repository }}" - echo "channel=${{ inputs.channel || '$CHANNEL' }}" > "$GITHUB_OUTPUT" + cat << EOF + ::group::Inputs + prerelease=${{ inputs.prerelease }} + source=${{ inputs.source }} + target=${{ inputs.target }} + version=${{ inputs.version }} + ::endgroup:: + EOF + IFS='@' read -r source_repo source_tag <<<"${{ inputs.source }}" + IFS='@' read -r target_repo target_tag <<<"${{ inputs.target }}" + cat << EOF >> "$GITHUB_OUTPUT" + source_repo=${source_repo} + source_tag=${source_tag} + target_repo=${target_repo} + target_tag=${target_tag} + EOF - - name: Update version - id: update_version + - name: Setup variables + id: setup_variables + env: + source_repo: ${{ steps.process_inputs.outputs.source_repo }} + source_tag: ${{ steps.process_inputs.outputs.source_tag }} + target_repo: ${{ steps.process_inputs.outputs.target_repo }} + target_tag: ${{ steps.process_inputs.outputs.target_tag }} run: | - REVISION="${{ vars.PUSH_VERSION_COMMIT == '' && '$(date -u +"%H%M%S")' || '' }}" - REVISION="${{ inputs.prerelease && '$(date -u +"%H%M%S")' || '$REVISION' }}" - python devscripts/update-version.py ${{ inputs.version || '$REVISION' }} | \ - grep -Po "version=\d+\.\d+\.\d+(\.\d+)?" >> "$GITHUB_OUTPUT" + # unholy bash monstrosity (sincere apologies) + fallback_token () { + if ${{ !secrets.ARCHIVE_REPO_TOKEN }}; then + echo "::error::Repository access secret ${target_repo_token^^} not found" + exit 1 + fi + target_repo_token=ARCHIVE_REPO_TOKEN + return 0 + } + + source_is_channel=0 + [[ "${source_repo}" == 'stable' ]] && source_repo='yt-dlp/yt-dlp' + if [[ -z "${source_repo}" ]]; then + source_repo='${{ github.repository }}' + elif [[ '${{ vars[format('{0}_archive_repo', env.source_repo)] }}' ]]; then + source_is_channel=1 + source_channel='${{ vars[format('{0}_archive_repo', env.source_repo)] }}' + elif [[ -z "${source_tag}" && "${source_repo}" != */* ]]; then + source_tag="${source_repo}" + source_repo='${{ github.repository }}' + fi + resolved_source="${source_repo}" + if [[ "${source_tag}" ]]; then + resolved_source="${resolved_source}@${source_tag}" + elif [[ "${source_repo}" == 'yt-dlp/yt-dlp' ]]; then + resolved_source='stable' + fi + + revision="${{ (inputs.prerelease || !vars.PUSH_VERSION_COMMIT) && '$(date -u +"%H%M%S")' || '' }}" + version="$( + python devscripts/update-version.py -c "${resolved_source}" ${{ inputs.version || '$revision' }} | \ + grep -Po "version=\K\d+\.\d+\.\d+(\.\d+)?")" + + if [[ "${target_repo}" ]]; then + if [[ -z "${target_tag}" ]]; then + if [[ '${{ vars[format('{0}_archive_repo', env.target_repo)] }}' ]]; then + target_tag="${source_tag:-${version}}" + else + target_tag="${target_repo}" + target_repo='${{ github.repository }}' + fi + fi + if [[ "${target_repo}" != '${{ github.repository}}' ]]; then + target_repo='${{ vars[format('{0}_archive_repo', env.target_repo)] }}' + target_repo_token='${{ env.target_repo }}_archive_repo_token' + ${{ !!secrets[format('{0}_archive_repo_token', env.target_repo)] }} || fallback_token + pypi_project='${{ vars[format('{0}_pypi_project', env.target_repo)] }}' + pypi_suffix='${{ vars[format('{0}_pypi_suffix', env.target_repo)] }}' + ${{ !secrets[format('{0}_pypi_token', env.target_repo)] }} || pypi_token='${{ env.target_repo }}_pypi_token' + fi + else + target_tag="${source_tag:-${version}}" + if ((source_is_channel)); then + target_repo="${source_channel}" + target_repo_token='${{ env.source_repo }}_archive_repo_token' + ${{ !!secrets[format('{0}_archive_repo_token', env.source_repo)] }} || fallback_token + pypi_project='${{ vars[format('{0}_pypi_project', env.source_repo)] }}' + pypi_suffix='${{ vars[format('{0}_pypi_suffix', env.source_repo)] }}' + ${{ !secrets[format('{0}_pypi_token', env.source_repo)] }} || pypi_token='${{ env.source_repo }}_pypi_token' + else + target_repo='${{ github.repository }}' + fi + fi + + if [[ "${target_repo}" == '${{ github.repository }}' ]] && ${{ !inputs.prerelease }}; then + pypi_project='${{ vars.PYPI_PROJECT }}' + fi + if [[ -z "${pypi_token}" && "${pypi_project}" ]]; then + if ${{ !secrets.PYPI_TOKEN }}; then + pypi_token=OIDC + else + pypi_token=PYPI_TOKEN + fi + fi + + echo "::group::Output variables" + cat << EOF | tee -a "$GITHUB_OUTPUT" + channel=${resolved_source} + version=${version} + target_repo=${target_repo} + target_repo_token=${target_repo_token} + target_tag=${target_tag} + pypi_project=${pypi_project} + pypi_suffix=${pypi_suffix} + pypi_token=${pypi_token} + EOF + echo "::endgroup::" - name: Update documentation + env: + version: ${{ steps.setup_variables.outputs.version }} + target_repo: ${{ steps.setup_variables.outputs.target_repo }} + if: | + !inputs.prerelease && env.target_repo == github.repository run: | make doc sed '/### /Q' Changelog.md >> ./CHANGELOG - echo '### ${{ steps.update_version.outputs.version }}' >> ./CHANGELOG + echo '### ${{ env.version }}' >> ./CHANGELOG python ./devscripts/make_changelog.py -vv -c >> ./CHANGELOG echo >> ./CHANGELOG grep -Poz '(?s)### \d+\.\d+\.\d+.+' 'Changelog.md' | head -n -1 >> ./CHANGELOG @@ -65,12 +209,16 @@ jobs: - name: Push to release id: push_release - if: ${{ !inputs.prerelease }} + env: + version: ${{ steps.setup_variables.outputs.version }} + target_repo: ${{ steps.setup_variables.outputs.target_repo }} + if: | + !inputs.prerelease && env.target_repo == github.repository run: | git config --global user.name github-actions - git config --global user.email github-actions@example.com + git config --global user.email github-actions@github.com git add -u - git commit -m "Release ${{ steps.update_version.outputs.version }}" \ + git commit -m "Release ${{ env.version }}" \ -m "Created by: ${{ github.event.sender.login }}" -m ":ci skip all :ci run dl" git push origin --force ${{ github.event.ref }}:release @@ -80,7 +228,10 @@ jobs: echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - name: Update master - if: vars.PUSH_VERSION_COMMIT != '' && !inputs.prerelease + env: + target_repo: ${{ steps.setup_variables.outputs.target_repo }} + if: | + vars.PUSH_VERSION_COMMIT != '' && !inputs.prerelease && env.target_repo == github.repository run: git push origin ${{ github.event.ref }} build: @@ -95,9 +246,12 @@ jobs: secrets: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} - publish_pypi_homebrew: + 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@v3 @@ -107,57 +261,137 @@ jobs: - name: Install Requirements run: | - sudo apt-get -y install pandoc man + sudo apt -y install pandoc man python -m pip install -U pip setuptools wheel twine python -m pip install -U -r requirements.txt - name: Prepare - run: | - python devscripts/update-version.py ${{ needs.prepare.outputs.version }} - python devscripts/make_lazy_extractors.py - - - name: Build and publish on PyPI env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - if: env.TWINE_PASSWORD != '' && !inputs.prerelease + 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 "${{ env.channel }}" -r "${{ env.target_repo }}" -s "${{ env.suffix }}" "${{ env.version }}" + python devscripts/make_lazy_extractors.py + sed -i -E "s/(name=')[^']+(', # package name)/\1${{ env.pypi_project }}\2/" setup.py + + - name: Build run: | rm -rf dist/* make pypi-files python devscripts/set-variant.py pip -M "You installed yt-dlp with pip or using the wheel from PyPi; Use that to update" python setup.py sdist bdist_wheel + + - name: Publish to PyPI via token + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets[needs.prepare.outputs.pypi_token] }} + if: | + needs.prepare.outputs.pypi_token != 'OIDC' && env.TWINE_PASSWORD + run: | twine upload dist/* - - name: Checkout Homebrew repository - env: - BREW_TOKEN: ${{ secrets.BREW_TOKEN }} - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - if: env.BREW_TOKEN != '' && env.PYPI_TOKEN != '' && !inputs.prerelease - uses: actions/checkout@v3 + - name: Publish to PyPI via trusted publishing + if: | + needs.prepare.outputs.pypi_token == 'OIDC' + uses: pypa/gh-action-pypi-publish@release/v1 with: - repository: yt-dlp/homebrew-taps - path: taps - ssh-key: ${{ secrets.BREW_TOKEN }} - - - name: Update Homebrew Formulae - env: - BREW_TOKEN: ${{ secrets.BREW_TOKEN }} - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - if: env.BREW_TOKEN != '' && env.PYPI_TOKEN != '' && !inputs.prerelease - run: | - python devscripts/update-formulae.py taps/Formula/yt-dlp.rb "${{ needs.prepare.outputs.version }}" - git -C taps/ config user.name github-actions - git -C taps/ config user.email github-actions@example.com - git -C taps/ commit -am 'yt-dlp: ${{ needs.prepare.outputs.version }}' - git -C taps/ push + verbose: true publish: needs: [prepare, build] - uses: ./.github/workflows/publish.yml permissions: contents: write - with: - channel: ${{ needs.prepare.outputs.channel }} - prerelease: ${{ inputs.prerelease }} - version: ${{ needs.prepare.outputs.version }} - target_commitish: ${{ needs.prepare.outputs.head_sha }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/download-artifact@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Generate release notes + env: + head_sha: ${{ needs.prepare.outputs.head_sha }} + target_repo: ${{ needs.prepare.outputs.target_repo }} + target_tag: ${{ needs.prepare.outputs.target_tag }} + run: | + printf '%s' \ + '[![Installation](https://img.shields.io/badge/-Which%20file%20should%20I%20download%3F-white.svg?style=for-the-badge)]' \ + '(https://github.com/${{ github.repository }}#installation "Installation instructions") ' \ + '[![Documentation](https://img.shields.io/badge/-Docs-brightgreen.svg?style=for-the-badge&logo=GitBook&labelColor=555555)]' \ + '(https://github.com/${{ github.repository }}' \ + '${{ env.target_repo == github.repository && format('/tree/{0}', env.target_tag) || '' }}#readme "Documentation") ' \ + '[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)]' \ + '(https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators "Donate") ' \ + '[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)]' \ + '(https://discord.gg/H5MNcFW63r "Discord") ' \ + ${{ env.target_repo == 'yt-dlp/yt-dlp' && '\ + "[![Nightly](https://img.shields.io/badge/Get%20nightly%20builds-purple.svg?style=for-the-badge)]" \ + "(https://github.com/yt-dlp/yt-dlp-nightly-builds/releases/latest \"Nightly builds\") " \ + "[![Master](https://img.shields.io/badge/Get%20master%20builds-lightblue.svg?style=for-the-badge)]" \ + "(https://github.com/yt-dlp/yt-dlp-master-builds/releases/latest \"Master builds\")"' || '' }} > ./RELEASE_NOTES + printf '\n\n' >> ./RELEASE_NOTES + cat >> ./RELEASE_NOTES << EOF + #### A description of the various files are in the [README](https://github.com/${{ github.repository }}#release-files) + --- + $(python ./devscripts/make_changelog.py -vv --collapsible) + EOF + 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/${{ github.repository }}/commit/${{ env.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 }} + version: ${{ needs.prepare.outputs.version }} + channel: ${{ needs.prepare.outputs.channel }} + if: | + inputs.prerelease && env.GH_TOKEN != '' && env.GH_REPO != '' && env.GH_REPO != github.repository + run: | + title="${{ startswith(env.GH_REPO, 'yt-dlp/') && 'yt-dlp ' || '' }}${{ env.channel }}" + gh release create \ + --notes-file ARCHIVE_NOTES \ + --title "${title} ${{ env.version }}" \ + ${{ env.version }} \ + artifact/* + + - name: Prune old release + env: + GH_TOKEN: ${{ github.token }} + version: ${{ needs.prepare.outputs.version }} + target_repo: ${{ needs.prepare.outputs.target_repo }} + target_tag: ${{ needs.prepare.outputs.target_tag }} + if: | + env.target_repo == github.repository && env.target_tag != env.version + run: | + gh release delete --yes --cleanup-tag "${{ env.target_tag }}" || true + git tag --delete "${{ env.target_tag }}" || true + sleep 5 # Enough time to cover deletion race condition + + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + version: ${{ needs.prepare.outputs.version }} + target_repo: ${{ needs.prepare.outputs.target_repo }} + target_tag: ${{ needs.prepare.outputs.target_tag }} + head_sha: ${{ needs.prepare.outputs.head_sha }} + if: | + env.target_repo == github.repository + run: | + title="${{ github.repository == 'yt-dlp/yt-dlp' && 'yt-dlp ' || '' }}" + title+="${{ env.target_tag != env.version && format('{0} ', env.target_tag) || '' }}" + gh release create \ + --notes-file ${{ inputs.prerelease && 'PRERELEASE_NOTES' || 'RELEASE_NOTES' }} \ + --target ${{ env.head_sha }} \ + --title "${title}${{ env.version }}" \ + ${{ inputs.prerelease && '--prerelease' || '' }} \ + ${{ env.target_tag }} \ + artifact/* diff --git a/devscripts/update-formulae.py b/devscripts/update-formulae.py deleted file mode 100644 index e79297f53..000000000 --- a/devscripts/update-formulae.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 - -""" -Usage: python3 ./devscripts/update-formulae.py -version can be either 0-aligned (yt-dlp version) or normalized (PyPi version) -""" - -# Allow direct execution -import os -import sys - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - - -import json -import re -import urllib.request - -from devscripts.utils import read_file, write_file - -filename, version = sys.argv[1:] - -normalized_version = '.'.join(str(int(x)) for x in version.split('.')) - -pypi_release = json.loads(urllib.request.urlopen( - 'https://pypi.org/pypi/yt-dlp/%s/json' % normalized_version -).read().decode()) - -tarball_file = next(x for x in pypi_release['urls'] if x['filename'].endswith('.tar.gz')) - -sha256sum = tarball_file['digests']['sha256'] -url = tarball_file['url'] - -formulae_text = read_file(filename) - -formulae_text = re.sub(r'sha256 "[0-9a-f]*?"', 'sha256 "%s"' % sha256sum, formulae_text, count=1) -formulae_text = re.sub(r'url "[^"]*?"', 'url "%s"' % url, formulae_text, count=1) - -write_file(filename, formulae_text) diff --git a/devscripts/update-version.py b/devscripts/update-version.py index 0144bd284..da54a6a25 100644 --- a/devscripts/update-version.py +++ b/devscripts/update-version.py @@ -20,7 +20,7 @@ def get_new_version(version, revision): version = datetime.now(timezone.utc).strftime('%Y.%m.%d') if revision: - assert revision.isdigit(), 'Revision must be a number' + assert revision.isdecimal(), 'Revision must be a number' else: old_version = read_version().split('.') if version.split('.') == old_version[:3]: @@ -46,6 +46,10 @@ VARIANT = None UPDATE_HINT = None CHANNEL = {channel!r} + +ORIGIN = {origin!r} + +_pkg_version = {package_version!r} ''' if __name__ == '__main__': @@ -53,6 +57,12 @@ if __name__ == '__main__': parser.add_argument( '-c', '--channel', default='stable', help='Select update channel (default: %(default)s)') + parser.add_argument( + '-r', '--origin', default='local', + help='Select origin/repository (default: %(default)s)') + parser.add_argument( + '-s', '--suffix', default='', + help='Add an alphanumeric suffix to the package version, e.g. "dev"') parser.add_argument( '-o', '--output', default='yt_dlp/version.py', help='The output file to write to (default: %(default)s)') @@ -66,6 +76,7 @@ if __name__ == '__main__': args.version if args.version and '.' in args.version else get_new_version(None, args.version)) write_file(args.output, VERSION_TEMPLATE.format( - version=version, git_head=git_head, channel=args.channel)) + version=version, git_head=git_head, channel=args.channel, origin=args.origin, + package_version=f'{version}{args.suffix}')) print(f'version={version} ({args.channel}), head={git_head}') diff --git a/devscripts/utils.py b/devscripts/utils.py index f75a84da9..a952c9fae 100644 --- a/devscripts/utils.py +++ b/devscripts/utils.py @@ -13,10 +13,11 @@ def write_file(fname, content, mode='w'): return f.write(content) -def read_version(fname='yt_dlp/version.py'): +def read_version(fname='yt_dlp/version.py', varname='__version__'): """Get the version without importing the package""" - exec(compile(read_file(fname), fname, 'exec')) - return locals()['__version__'] + items = {} + exec(compile(read_file(fname), fname, 'exec'), items) + return items[varname] def get_filename_args(has_infile=False, default_outfile=None): diff --git a/setup.py b/setup.py index 1740db27d..44055b0e9 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ except ImportError: from devscripts.utils import read_file, read_version -VERSION = read_version() +VERSION = read_version(varname='_pkg_version') DESCRIPTION = 'A youtube-dl fork with additional features and patches' @@ -142,7 +142,7 @@ def main(): params = build_params() setup( - name='yt-dlp', + name='yt-dlp', # package name (do not change/remove comment) version=VERSION, maintainer='pukkandan', maintainer_email='pukkandan.ytdlp@gmail.com', diff --git a/yt_dlp/update.py b/yt_dlp/update.py index db79df127..bdaa0d9be 100644 --- a/yt_dlp/update.py +++ b/yt_dlp/update.py @@ -28,6 +28,7 @@ from .version import CHANNEL, UPDATE_HINT, VARIANT, __version__ UPDATE_SOURCES = { 'stable': 'yt-dlp/yt-dlp', 'nightly': 'yt-dlp/yt-dlp-nightly-builds', + 'master': 'yt-dlp/yt-dlp-master-builds', } REPOSITORY = UPDATE_SOURCES['stable'] diff --git a/yt_dlp/version.py b/yt_dlp/version.py index 9d0096316..a4b4d4101 100644 --- a/yt_dlp/version.py +++ b/yt_dlp/version.py @@ -9,3 +9,7 @@ VARIANT = None UPDATE_HINT = None CHANNEL = 'stable' + +ORIGIN = 'yt-dlp/yt-dlp' + +_pkg_version = '2023.10.13'