name: CI env: IMAGE_NAME: tubesync REGISTRY: ghcr.io on: workflow_dispatch: schedule: - cron: '33 14 * * 4' push: branches: - 'main' - 'test-*' pull_request: branches: - 'main' - 'test-*' types: - opened - reopened - synchronize - ready_for_review concurrency: group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false jobs: info: if: ${{ !cancelled() && 'pull_request' != github.event_name }} runs-on: ubuntu-slim outputs: IMAGE_NAME: ${{ env.IMAGE_NAME }} REGISTRY: ${{ env.REGISTRY }} ejs-latest-release: ${{ steps.ejs.outputs.latest-release }} ffmpeg-date: ${{ steps.ffmpeg.outputs.date }} # ffmpeg-releases: ${{ steps.ffmpeg.outputs.releases }} ffmpeg-version: ${{ steps.ffmpeg.outputs.version }} lowercase-github-actor: ${{ steps.github-actor.outputs.lowercase }} lowercase-github-repository_owner: ${{ steps.github-repository_owner.outputs.lowercase }} ytdlp-latest-release: ${{ steps.yt-dlp.outputs.latest-release }} # ytdlp-releases: ${{ steps.yt-dlp.outputs.releases }} steps: - uses: actions/checkout@v7 - name: Lowercase github username id: github-actor uses: ./.github/actions/string-case with: string: ${{ github.actor }} - name: Lowercase github repository owner id: github-repository_owner uses: ./.github/actions/string-case with: string: ${{ github.repository_owner }} - name: Retrieve yt-dlp/FFmpeg-Builds releases with GitHub CLI id: ffmpeg uses: ./.github/actions/FFmpeg - name: Retrieve yt-dlp/yt-dlp releases with GitHub CLI id: yt-dlp uses: ./.github/actions/yt-dlp - name: Retrieve yt-dlp/ejs releases with GitHub CLI id: ejs uses: ./.github/actions/yt-dlp with: repository_name: ejs scripts: runs-on: ubuntu-26.04-arm timeout-minutes: 20 env: GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v7 - name: Make directories shell: bash run: sudo install -v -d -o "$(id -un)" /app/common - name: Install `bun` shell: bash run: | printf '%s\n' '' '++++++++ START: install_bun.sh + gpg' '' ; ./tubesync/install_bun.sh printf '%s\n' '' '-------- END: install_bun.sh + gpg' '' ; sudo apt-get update && sudo apt-get install --no-install-recommends -y sq sqv printf '%s\n' '' '' '====== SQV ======' ; # sqv --help sqv --version printf '%s\n' '' '' '====== SQ ======' ; # sq --help # sq help verify sq version printf '%s\n' '' '' '====== BUN ======' ; bun --revision printf '%s\n' '' '++++++++ START: verify_bun.ts + sqv' '' ; bun run ./tubesync/verify_bun.ts printf '%s\n' '' '-------- END: verify_bun.ts + sqv' '' ; sudo apt-get purge -y sqv printf '%s\n' '' '++++++++ START: verify_bun.ts + sq' '' ; bun run ./tubesync/verify_bun.ts --install-dir /usr/local/bin printf '%s\n' '' '-------- END: verify_bun.ts + sq' '' ; printf '%s\n' '' '' '====== BUN ======' ; bun --revision - name: Install `deno` shell: bash run: ./tubesync/install_deno.sh - name: Install `tailwindcss` shell: bash run: ./tubesync/install_tailwindcss.sh test: if: ${{ !cancelled() && ( 'pull_request' != github.event_name || (! github.event.pull_request.draft) ) }} runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: python-version: ['3.12', '3.13', '3.14'] services: hat-syslog: image: bozokopic/hat-syslog ports: - 127.0.0.1:6514:6514/tcp - 127.0.0.1:6514:6514/udp steps: - uses: actions/checkout@v7 - name: Install Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Test shasum run: | python3 -B -W default tubesync/shasum.py --help python3 -B -W default tubesync/shasum.py --version python3 -B -W default tubesync/shasum_tests.py - name: Install dependencies run: | python -m pip install uv uv --no-config --no-managed-python --no-progress \ tool run pipenv requirements --dev --no-lock >| Pipfile-requirements.txt uv --no-config --no-managed-python --no-progress \ pip compile --format requirements.txt --generate-hashes --output-file Pipfile-requirements-with-hashes.txt Pipfile-requirements.txt uv --no-config --no-managed-python --no-progress \ pip install --system --strict --requirements Pipfile-requirements-with-hashes.txt - name: Set up Django environment run: | mkdir -v -p ~/.config/TubeSync/config mkdir -v -p ~/.config/TubeSync/downloads sudo ln -v -s -f -T ~/.config/TubeSync/config /config sudo ln -v -s -f -T ~/.config/TubeSync/downloads /downloads cp -v -p tubesync/tubesync/local_settings.py.example tubesync/tubesync/local_settings.py cp -v -a -t "${Python3_ROOT_DIR}"/lib/python3.*/site-packages/yt_dlp/ patches/yt_dlp/* cd tubesync && python3 -B manage.py collectstatic --no-input --link - name: Check with ruff continue-on-error: false run: | target_version='py312' ignore_csv_list='C408,C409,C410,E701,E722,E731,I001,UP017,UP018' cd tubesync # output formats: # "full" | "concise" | "grouped" | # "json" | "junit" | "github" | "gitlab" | # "pylint" | "azure" { echo '## Output from `ruff check` for `tubesync`' echo '' echo '### Formats' echo '' for fmt in full concise grouped pylint do echo '
' echo ''"${fmt}"'' echo '' echo '#### '"${fmt}"' output format' echo '' echo '```' uvx --no-config --no-managed-python --no-progress --isolated \ ruff check --exit-zero \ --target-version "${target_version}" \ --output-format "${fmt}" \ --extend-select 'E4,E7,E9,F,RUF100' \ --ignore "${ignore_csv_list}" echo '' echo '```' echo '' echo '
' echo '' done } >> "${GITHUB_STEP_SUMMARY}" uvx --no-config --no-managed-python --no-progress --isolated \ ruff check \ --target-version "${target_version}" \ --output-format github \ --select 'C4,E4,E7,E9,F' \ --ignore "${ignore_csv_list}" - name: Run Django tests timeout-minutes: 5 run: cd tubesync && TUBESYNC_DEBUG=True python3 -B -W default manage.py test --no-input --buffer --verbosity=2 containerise: if: ${{ !cancelled() && 'success' == needs.info.result }} needs: ['info'] runs-on: ubuntu-24.04 timeout-minutes: 30 concurrency: group: ${{ github.repository }}-${{ github.workflow }} cancel-in-progress: false steps: - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v4 - name: Build image for `dive` id: build-dive-image uses: docker/build-push-action@v7 with: build-args: | IMAGE_NAME=${{ env.IMAGE_NAME }} FFMPEG_DATE=${{ needs.info.outputs.ffmpeg-date }} FFMPEG_VERSION=${{ needs.info.outputs.ffmpeg-version }} YTDLP_DATE=${{ fromJSON(needs.info.outputs.ytdlp-latest-release).tag.name }} YTDLP_EJS_VERSION=${{ fromJSON(needs.info.outputs.ejs-latest-release).tag.name }} cache-from: type=gha,scope=buildkit-linux-amd64 load: true platforms: linux/amd64 push: false tags: ghcr.io/${{ needs.info.outputs.lowercase-github-repository_owner }}/${{ env.IMAGE_NAME }}:dive - name: Analysis with `dive` continue-on-error: false run: | docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ 'ghcr.io/wagoodman/dive' \ 'ghcr.io/${{ needs.info.outputs.lowercase-github-repository_owner }}/${{ env.IMAGE_NAME }}:dive' \ --ci \ --highestUserWastedPercent '0.05' \ --highestWastedBytes '25M' docker-image: if: ${{ !cancelled() && 'success' == needs.info.result && 'success' == needs.containerise.result }} concurrency: group: ${{ github.repository }}-${{ github.workflow }} cancel-in-progress: false needs: ['info', 'test', 'containerise'] uses: docker/github-builder/.github/workflows/build.yml@v1 permissions: contents: read # to fetch the repository content id-token: write # for signing attestation(s) with GitHub OIDC Token secrets: registry-auths: | - registry: ${{ needs.info.outputs.REGISTRY }} username: ${{ github.repository_owner }} password: ${{ 'meeb' == github.repository_owner && secrets.REGISTRY_ACCESS_TOKEN || secrets.GITHUB_TOKEN }} with: cache: true cache-mode: max meta-images: ${{ needs.info.outputs.REGISTRY }}/${{ needs.info.outputs.lowercase-github-repository_owner }}/${{ needs.info.outputs.IMAGE_NAME }} output: image platforms: linux/amd64,linux/arm64 push: ${{ 'success' == needs.test.result && 'meeb' == github.repository_owner && 'pull_request' != github.event_name }} setup-qemu: false meta-flavor: | latest=false meta-tags: | type=schedule,pattern=weekly type=raw,enable={{is_default_branch}},value=latest type=raw,enable={{is_not_default_branch}},value=test type=ref,event=tag type=ref,event=pr build-args: | IMAGE_NAME=${{ needs.info.outputs.IMAGE_NAME }} FFMPEG_DATE=${{ needs.info.outputs.ffmpeg-date }} FFMPEG_VERSION=${{ needs.info.outputs.ffmpeg-version }} YTDLP_DATE=${{ fromJSON(needs.info.outputs.ytdlp-latest-release).tag.name }} YTDLP_EJS_VERSION=${{ fromJSON(needs.info.outputs.ejs-latest-release).tag.name }}