From 9b3edc8bd1120abfa69bc5b0b06b6924e37ba101 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Fri, 14 May 2021 17:18:54 +0200 Subject: [PATCH 01/10] migrate Azure Pipelines to GitHub Actions --- .ci/azure-pipelines.yml | 35 ------------ .ci/build.yml | 46 ---------------- .ci/publish.yml | 27 ---------- .ci/validate.yml | 58 -------------------- .config/generate_xml.py | 4 +- .github/workflows/build-publish.yaml | 81 ++++++++++++++++++++++++++++ .github/workflows/codeql.yaml | 41 ++++++++++++++ .github/workflows/test.yaml | 77 ++++++++++++++++++++++++++ 8 files changed, 201 insertions(+), 168 deletions(-) delete mode 100644 .ci/azure-pipelines.yml delete mode 100644 .ci/build.yml delete mode 100644 .ci/publish.yml delete mode 100644 .ci/validate.yml create mode 100644 .github/workflows/build-publish.yaml create mode 100644 .github/workflows/codeql.yaml create mode 100644 .github/workflows/test.yaml diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml deleted file mode 100644 index a76bfc1d..00000000 --- a/.ci/azure-pipelines.yml +++ /dev/null @@ -1,35 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - tags: - include: - - '*' - -jobs: - - job: 'Validate' - pool: - vmImage: 'ubuntu-18.04' - strategy: - matrix: - Python27: - python.version: '2.7' - Python36: - python.version: '3.6' - steps: - # Run tests and linting - - template: validate.yml - - - job: Build - steps: - # On every PR, build the addon and make it available for download as an artifact - - template: build.yml - parameters: - py_versions: [ 'py2', 'py3' ] - - # When triggered by a tag, publish the built addon to the repo server - - ${{ if startsWith(variables['Build.SourceBranch'], 'refs/tags') }}: - - template: publish.yml - parameters: - py_versions: [ 'py2', 'py3' ] diff --git a/.ci/build.yml b/.ci/build.yml deleted file mode 100644 index 14865b1e..00000000 --- a/.ci/build.yml +++ /dev/null @@ -1,46 +0,0 @@ -parameters: - python_versions : [] - -steps: - - ${{ each py_version in parameters.py_versions }}: - - task: usePythonVersion@0 - inputs: - versionSpec: '3.6' - - - checkout: self - clean: true - - - script: python3 -m pip install --user pyyaml - displayName: 'Install PyYaml' - - - script: python3 jellyfin-kodi/.config/generate_xml.py ${{ py_version }} - displayName: 'Create ${{ py_version }} addon.xml' - - - task: CopyFiles@2 - displayName: 'Create clean addon directory' - inputs: - sourceFolder: 'jellyfin-kodi' - cleanTargetFolder: true - contents: | - **/* - !.ci/* - !.config/* - !.git/**/* - !.github/* - TargetFolder: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin' - - - task: ArchiveFiles@2 - displayName: 'Create ${{ py_version }} zip file' - inputs: - rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin' - includeRootFolder: True - archiveType: 'zip' - tarCompression: 'none' - archiveFile: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin-${{ py_version }}.zip' - - - task: PublishPipelineArtifact@1 - displayName: 'Publish ${{ py_version }} artifact' - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin' - artifactName: 'plugin.video.jellyfin-${{ py_version }}-$(Build.BuildNumber)' - diff --git a/.ci/publish.yml b/.ci/publish.yml deleted file mode 100644 index fddc79a4..00000000 --- a/.ci/publish.yml +++ /dev/null @@ -1,27 +0,0 @@ -parameters: - python_version : [] - -steps: - - ${{ each py_version in parameters.py_versions }}: - - task: CopyFilesOverSSH@0 - displayName: 'Upload to repo server' - inputs: - sshEndpoint: repository - sourceFolder: '$(Build.ArtifactStagingDirectory)' - contents: 'plugin.video.jellyfin-${{ py_version }}.zip' - targetFolder: '/srv/repository/incoming/kodi' - - - task: SSH@0 - displayName: 'Add to Kodi repo' - inputs: - sshEndpoint: repository - runOptions: 'commands' - commands: 'python3 /usr/local/bin/kodirepo add /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ py_version }}.zip --datadir /srv/repository/releases/client/kodi/${{ py_version }}' - failOnStdErr: false - - - task: SSH@0 - displayName: 'Clean up zip files' - inputs: - sshEndpoint: repository - runOptions: 'commands' - commands: 'rm /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ py_version }}.zip' diff --git a/.ci/validate.yml b/.ci/validate.yml deleted file mode 100644 index a4313c3e..00000000 --- a/.ci/validate.yml +++ /dev/null @@ -1,58 +0,0 @@ -steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '$(python.version)' - - - script: python -m pip install -r requirements-dev.txt - displayName: 'Install dev tools' - - - script: | - # Azure pipelines: https://github.com/microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md - # GitHub actions: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions - - rm -f flake8.output - flake8 . --statistics --output-file=flake8.output && { - echo "##vso[task.complete result=Succeeded;]" - } || { - echo "##vso[task.complete result=Failed;]" - } - - cat flake8.output | awk -F: '{ - if ($0 ~ /^\./) { - gsub(/^[ \t]+/,"",$4); - codesep=index($4, " "); - code=substr($4, 1, codesep-1); - msg=substr($4, codesep+1); - print "##vso[task.logissue type=error;sourcepath=" $1 ";linenumber=" $2 ";columnnumber=" $3 ";code=" code ";]" msg; - } else { - print $0 - } - }' - displayName: 'Run Linter' - failOnStderr: false - - - script: | - rm -f test.xml coverage.xml - coverage run && { - echo "##vso[task.complete result=Succeeded;]" - } || { - echo "##vso[task.complete result=Failed;]" - } - coverage xml - coverage html - coverage report - displayName: 'Run Tests' - failOnStderr: false - condition: succeededOrFailed() - - - task: PublishTestResults@2 - condition: succeededOrFailed() - inputs: - testResultsFiles: 'test.xml' - testRunTitle: 'Publish test results for Python $(python.version)' - - - task: PublishCodeCoverageResults@1 - condition: succeededOrFailed() - inputs: - codeCoverageTool: 'cobertura' - summaryFileLocation: 'coverage.xml' diff --git a/.config/generate_xml.py b/.config/generate_xml.py index a854960b..025e8f74 100644 --- a/.config/generate_xml.py +++ b/.config/generate_xml.py @@ -44,7 +44,7 @@ with open('{dir_path}/{py_version}.yaml'.format(**locals()), 'r') as f: deps = yaml.safe_load(f) # Load version and changelog -with open('jellyfin-kodi/release.yaml', 'r') as f: +with open('release.yaml', 'r') as f: data = yaml.safe_load(f) # Populate xml template @@ -67,4 +67,4 @@ for section in root.findall('extension'): indent(root) # Write addon.xml -tree.write('jellyfin-kodi/addon.xml', encoding='utf-8', xml_declaration=True) +tree.write('addon.xml', encoding='utf-8', xml_declaration=True) diff --git a/.github/workflows/build-publish.yaml b/.github/workflows/build-publish.yaml new file mode 100644 index 00000000..d858df07 --- /dev/null +++ b/.github/workflows/build-publish.yaml @@ -0,0 +1,81 @@ +name: Build Jellyfin-Kodi + +on: + push: + branches: + - master + tags: + - '*' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + py_version: [ 'py2', 'py3' ] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python 3.x + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pyyaml + + - name: Create ${{ matrix.py_version }} addon.xml + run: python .config/generate_xml.py ${{ matrix.py_version }} + + - name: Publish Build Atrifact + uses: actions/upload-artifact@v2 + with: + retention-days: 14 + name: ${{ matrix.py_version }}-build-artifact + path: | + **/* + !.ci/* + !.config/* + !.git/**/* + !.github/* + + publish: + runs-on: ubuntu-latest + needs: build + if: ${{ startsWith(github.ref, 'refs/tags/') && github.repository == 'jellyfin/jellyfin-kodi' }} + strategy: + matrix: + py_version: [ 'py2', 'py3' ] + steps: + - uses: actions/download-artifact@v2 + with: + name: ${{ matrix.py_version }}-build-artifact + path: plugin.video.jellyfin-${{ matrix.py_version }} + + - name: Create release Zip + run: zip -rq plugin.video.jellyfin-${{ matrix.py_version }}.zip plugin.video.jellyfin-${{ matrix.py_version }} + + - name: Upload to repo server + uses: burnett01/rsync-deployments@4.1 + with: + switches: -rltgoDzvO --delete --exclude='*' --include='**/*.apk' --include='*.txt' + path: plugin.video.jellyfin-${{ matrix.py_version }}.zip + remote_path: /srv/repository/incoming/kodi + remote_host: ${{ secrets.DEPLOY_HOST }} + remote_user: ${{ secrets.DEPLOY_USER }} + remote_key: ${{ secrets.DEPLOY_KEY }} + + - name: Add to Kodi repo and clean up + uses: appleboy/ssh-action@v0.1.4 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_KEY }} + envs: JELLYFIN_VERSION + script_stop: true + script: | + python3 /usr/local/bin/kodirepo add /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ matrix.py_version }}.zip --datadir /srv/repository/releases/client/kodi/${{ matrix.py_version }}; + rm /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ matrix.py_version }}.zip; diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 00000000..701c2aef --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,41 @@ +name: CodeQL Analysis + +on: + push: + branches: + - master + pull_request: + branches: + - master + schedule: + - cron: '38 8 * * 6' + +jobs: + analyze: + runs-on: ubuntu-latest + if: ${{ github.repository == 'jellyfin/jellyfin-kodi' }} + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + version: ['2.7', '3.9'] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.version }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..bab6f152 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,77 @@ +name: Test Jellyfin-Kodi + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PR_TRIGGERED: ${{ github.event_name == 'pull_request' && github.repository == 'jellyfin/jellyfin-kodi' }} + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + py_version: ['2.7', '3.9'] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.py_version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.py_version }} + + - name: Setup reviewdog + uses: reviewdog/action-setup@v1 + if: ${{ env.PR_TRIGGERED == 'true' && matrix.py_version == '3.9' }} + with: + reviewdog_version: nightly + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements-dev.txt + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --output-file=flake8.output + cat flake8.output + + - name: Test with Coverage + run: | + coverage run + coverage xml + coverage report + + - name: Run reviewdog for PR checks-api + if: ${{ env.PR_TRIGGERED == 'true' && matrix.py_version == '3.9' }} + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cat flake8.output | reviewdog -reporter=github-pr-check -f=flake8 -name="flake8" + + - name: Publish Coverage to PR + uses: 5monkeys/cobertura-action@v8 + if: ${{ env.PR_TRIGGERED == 'true' && matrix.py_version == '3.9' }} + with: + path: coverage.xml + repo_token: ${{ secrets.JF_BOT_TOKEN }} + minimum_coverage: 1 + + - name: Publish Test Atrifact + uses: actions/upload-artifact@v2 + with: + retention-days: 14 + name: ${{ matrix.py_version }}-test-results + path: | + flake8.output + test.xml + coverage.xml From bc07b8af53afcb85bd4c2b4370a4acb6f2c62e92 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Fri, 14 May 2021 17:19:31 +0200 Subject: [PATCH 02/10] add release-drafter for easy GitHub release management --- .github/release-drafter.yml | 22 ++++++++++++++++++++++ .github/workflows/release-drafter.yaml | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yaml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..abf57330 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,22 @@ +_extends: jellyfin/jellyfin-meta-plugins + +name-template: "Release $RESOLVED_VERSION" +tag-template: "v$RESOLVED_VERSION" +version-template: "$MAJOR.$MINOR.$PATCH" + +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch + +template: | + ## :sparkles: What's New + + $CHANGES diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml new file mode 100644 index 00000000..6e96ae70 --- /dev/null +++ b/.github/workflows/release-drafter.yaml @@ -0,0 +1,20 @@ +name: Release Drafter + +on: + push: + branches: + - master + tags: + - '*' + +jobs: + update_release_draft: + name: Update release draft + runs-on: ubuntu-latest + steps: + - name: Update Release Draft + uses: release-drafter/release-drafter@v5.15.0 + with: + publish: ${{ startsWith(github.ref, 'refs/tags/') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From bedefb494f9375fbd89d667940cec8bbcf192e09 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Fri, 14 May 2021 17:20:36 +0200 Subject: [PATCH 03/10] fix unsupported editorconfig setting --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 0e9268c0..e799a602 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,7 +14,7 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf -max_line_length = null +max_line_length = 9999 # YAML indentation [*.{yml,yaml}] From 1bd73881cb6bc4539357a04c63674ef87094dd6b Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Fri, 14 May 2021 17:27:10 +0200 Subject: [PATCH 04/10] add dependabot config --- .github/dependabot.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..ba738cf7 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + labels: + - ci + - github-actions + - dependencies + - package-ecosystem: pip + directory: / + schedule: + interval: weekly + labels: + - ci + - pip + - dependencies From 632fac78093758911a1d00700809085f24ff489d Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Sun, 23 May 2021 20:17:06 +0200 Subject: [PATCH 05/10] migrate from generate_xml to build a full bundle zip --- .config/generate_xml.py | 70 --------------- .config/py2.yaml | 16 ---- .config/py3.yaml | 14 --- .github/workflows/build-publish.yaml | 14 +-- build.py | 125 +++++++++++++++++++++++++++ release.yaml | 35 +++++++- 6 files changed, 162 insertions(+), 112 deletions(-) delete mode 100644 .config/generate_xml.py delete mode 100644 .config/py2.yaml delete mode 100644 .config/py3.yaml create mode 100644 build.py diff --git a/.config/generate_xml.py b/.config/generate_xml.py deleted file mode 100644 index 025e8f74..00000000 --- a/.config/generate_xml.py +++ /dev/null @@ -1,70 +0,0 @@ -import xml.etree.ElementTree as ET -import sys -import os -from datetime import datetime - -import yaml - - -def indent(elem, level=0): - ''' - Nicely formats output xml with newlines and spaces - https://stackoverflow.com/a/33956544 - ''' - i = "\n" + level*" " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - indent(elem, level+1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - - -try: - py_version = sys.argv[1] -except IndexError: - print('No version specified') - sys.exit(1) - -dir_path = os.path.dirname(os.path.realpath(__file__)) - -# Load template file -with open('{dir_path}/template.xml'.format(**locals()), 'r') as f: - tree = ET.parse(f) - root = tree.getroot() - -# Load version dependencies -with open('{dir_path}/{py_version}.yaml'.format(**locals()), 'r') as f: - deps = yaml.safe_load(f) - -# Load version and changelog -with open('release.yaml', 'r') as f: - data = yaml.safe_load(f) - -# Populate xml template -for dep in deps: - ET.SubElement(root.find('requires'), 'import', attrib=dep) - -# Update version string -addon_version = data.get('version') -root.attrib['version'] = '{addon_version}+{py_version}'.format(**locals()) - -# Changelog -date = datetime.today().strftime('%Y-%m-%d') -changelog = data.get('changelog') -for section in root.findall('extension'): - news = section.findall('news') - if news: - news[0].text = 'v{addon_version} ({date}):\n{changelog}'.format(**locals()) - -# Format xml tree -indent(root) - -# Write addon.xml -tree.write('addon.xml', encoding='utf-8', xml_declaration=True) diff --git a/.config/py2.yaml b/.config/py2.yaml deleted file mode 100644 index 244f1c64..00000000 --- a/.config/py2.yaml +++ /dev/null @@ -1,16 +0,0 @@ -- addon: 'xbmc.python' - version: '2.25.0' -- addon: 'script.module.requests' - version: '2.22.0' -- addon: 'script.module.dateutil' - version: '2.8.1' -- addon: 'script.module.six' - version: '1.13.0' -- addon: 'script.module.kodi-six' - version: '0.0.7' -- addon: 'script.module.addon.signals' - version: '0.0.5' -- addon: 'script.module.futures' - version: '2.2.0' -- addon: 'script.module.websocket' - version: '0.57.0' diff --git a/.config/py3.yaml b/.config/py3.yaml deleted file mode 100644 index c8a12418..00000000 --- a/.config/py3.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- addon: 'xbmc.python' - version: '3.0.0' -- addon: 'script.module.requests' - version: '2.22.0+matrix.1' -- addon: 'script.module.dateutil' - version: '2.8.1+matrix.1' -- addon: 'script.module.six' - version: '1.14.0+matrix.2' -- addon: 'script.module.kodi-six' - version: '0.1.3+1' -- addon: 'script.module.addon.signals' - version: '0.0.5+matrix.1' -- addon: 'script.module.websocket' - version: '0.58.0+matrix.1' diff --git a/.github/workflows/build-publish.yaml b/.github/workflows/build-publish.yaml index d858df07..6680830f 100644 --- a/.github/workflows/build-publish.yaml +++ b/.github/workflows/build-publish.yaml @@ -28,19 +28,15 @@ jobs: python -m pip install pyyaml - name: Create ${{ matrix.py_version }} addon.xml - run: python .config/generate_xml.py ${{ matrix.py_version }} + run: python build.py --version ${{ matrix.py_version }} - - name: Publish Build Atrifact + - name: Publish Build Artifact uses: actions/upload-artifact@v2 with: retention-days: 14 name: ${{ matrix.py_version }}-build-artifact path: | - **/* - !.ci/* - !.config/* - !.git/**/* - !.github/* + *.zip publish: runs-on: ubuntu-latest @@ -53,10 +49,6 @@ jobs: - uses: actions/download-artifact@v2 with: name: ${{ matrix.py_version }}-build-artifact - path: plugin.video.jellyfin-${{ matrix.py_version }} - - - name: Create release Zip - run: zip -rq plugin.video.jellyfin-${{ matrix.py_version }}.zip plugin.video.jellyfin-${{ matrix.py_version }} - name: Upload to repo server uses: burnett01/rsync-deployments@4.1 diff --git a/build.py b/build.py new file mode 100644 index 00000000..762d14ea --- /dev/null +++ b/build.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +import argparse +import os +import xml.etree.ElementTree as ET +import zipfile +from datetime import datetime +from pathlib import Path + +import yaml + + +def indent(elem: ET.Element, level: int = 0) -> None: + """ + Nicely formats output xml with newlines and spaces + https://stackoverflow.com/a/33956544 + """ + i = "\n" + level * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + indent(elem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + +def create_addon_xml(config: dict, source: str, py_version: str) -> None: + """ + Create addon.xml from template file + """ + # Load template file + with open('{}/.config/template.xml'.format(source), 'r') as f: + tree = ET.parse(f) + root = tree.getroot() + + # Populate dependencies in template + dependencies = config['dependencies'].get(py_version) + for dep in dependencies: + ET.SubElement(root.find('requires'), 'import', attrib=dep) + + # Populate version string + addon_version = config.get('version') + root.attrib['version'] = '{}+{}'.format(addon_version, py_version) + + # Populate Changelog + date = datetime.today().strftime('%Y-%m-%d') + changelog = config.get('changelog') + for section in root.findall('extension'): + news = section.findall('news') + if news: + news[0].text = 'v{} ({}):\n{}'.format(addon_version, date, changelog) + + # Format xml tree + indent(root) + + # Write addon.xml + tree.write('{}/addon.xml'.format(source), encoding='utf-8', xml_declaration=True) + + +def zip_files(py_version: str, source: str, target: str) -> None: + """ + Create installable addon zip archive + """ + archive_name = 'plugin.video.jellyfin+{}.zip'.format(py_version) + + with zipfile.ZipFile('{}/{}'.format(target, archive_name), 'w') as z: + for root, dirs, files in os.walk(args.source): + for filename in filter(file_filter, files): + file_path = os.path.join(root, filename) + if folder_filter(file_path): + relative_path = os.path.join('plugin.video.jellyfin', os.path.relpath(file_path, source)) + z.write(file_path, relative_path) + + +def file_filter(file_name: str) -> bool: + """ + True if file_name is meant to be included + """ + return 'plugin.video.jellyfin' not in file_name and 'pyo' not in file_name + + +def folder_filter(folder_name: str) -> bool: + """ + True if folder_name is meant to be included + """ + return ('.ci' not in folder_name + and '.git' not in folder_name + and '.github' not in folder_name + and '.config' not in folder_name) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Build flags:') + parser.add_argument( + '--version', + type=str, + choices=('py2', 'py3'), + default='py3') + + parser.add_argument( + '--source', + type=Path, + default=Path(__file__).absolute().parent) + + parser.add_argument( + '--target', + type=Path, + default=Path(__file__).absolute().parent) + + args = parser.parse_args() + + # Load config file + config_path = os.path.join(args.source, 'release.yaml') + with open(config_path, 'r') as fh: + release_config = yaml.safe_load(fh) + + create_addon_xml(release_config, args.source, args.version) + + zip_files(args.version, args.source, args.target) diff --git a/release.yaml b/release.yaml index fa4c7ada..cfb8cd95 100644 --- a/release.yaml +++ b/release.yaml @@ -2,4 +2,37 @@ version: '0.7.3' changelog: | - #508 Use jellyfin's DisplayTitle for audio and subtitle streams - #513 Fix subsequent syncs from the addon menu better - - #515 Allow multiple subtitles with identical names + - #515 Allow multiple subtitles with identical names +dependencies: + py2: + - addon: 'xbmc.python' + version: '2.25.0' + - addon: 'script.module.requests' + version: '2.22.0' + - addon: 'script.module.dateutil' + version: '2.8.1' + - addon: 'script.module.six' + version: '1.13.0' + - addon: 'script.module.kodi-six' + version: '0.0.7' + - addon: 'script.module.addon.signals' + version: '0.0.5' + - addon: 'script.module.futures' + version: '2.2.0' + - addon: 'script.module.websocket' + version: '0.57.0' + py3: + - addon: 'xbmc.python' + version: '3.0.0' + - addon: 'script.module.requests' + version: '2.22.0+matrix.1' + - addon: 'script.module.dateutil' + version: '2.8.1+matrix.1' + - addon: 'script.module.six' + version: '1.14.0+matrix.2' + - addon: 'script.module.kodi-six' + version: '0.1.3+1' + - addon: 'script.module.addon.signals' + version: '0.0.5+matrix.1' + - addon: 'script.module.websocket' + version: '0.58.0+matrix.1' From afeb520433eacecd5f677c75ad04ebe52eea90dc Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Sun, 23 May 2021 20:17:34 +0200 Subject: [PATCH 06/10] change to use codecov for coverage report --- .github/workflows/test.yaml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index bab6f152..4908d794 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -58,13 +58,9 @@ jobs: run: | cat flake8.output | reviewdog -reporter=github-pr-check -f=flake8 -name="flake8" - - name: Publish Coverage to PR - uses: 5monkeys/cobertura-action@v8 - if: ${{ env.PR_TRIGGERED == 'true' && matrix.py_version == '3.9' }} - with: - path: coverage.xml - repo_token: ${{ secrets.JF_BOT_TOKEN }} - minimum_coverage: 1 + - name: Upload coverage + uses: codecov/codecov-action@v1 + if: ${{ matrix.py_version == '3.9' }} - name: Publish Test Atrifact uses: actions/upload-artifact@v2 From 326f00925a2f947a77a6bdc9312fb08814471668 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Thu, 27 May 2021 23:33:54 +0200 Subject: [PATCH 07/10] move build.py --- build.py => .build/build.py | 4 ++-- {.config => .build}/template.xml | 0 .github/workflows/build-publish.yaml | 10 +++++----- tox.ini | 3 +-- 4 files changed, 8 insertions(+), 9 deletions(-) rename build.py => .build/build.py (96%) rename {.config => .build}/template.xml (100%) diff --git a/build.py b/.build/build.py similarity index 96% rename from build.py rename to .build/build.py index 762d14ea..1fc528d7 100644 --- a/build.py +++ b/.build/build.py @@ -35,7 +35,7 @@ def create_addon_xml(config: dict, source: str, py_version: str) -> None: Create addon.xml from template file """ # Load template file - with open('{}/.config/template.xml'.format(source), 'r') as f: + with open('{}/.build/template.xml'.format(source), 'r') as f: tree = ET.parse(f) root = tree.getroot() @@ -92,7 +92,7 @@ def folder_filter(folder_name: str) -> bool: return ('.ci' not in folder_name and '.git' not in folder_name and '.github' not in folder_name - and '.config' not in folder_name) + and '.build' not in folder_name) if __name__ == '__main__': diff --git a/.config/template.xml b/.build/template.xml similarity index 100% rename from .config/template.xml rename to .build/template.xml diff --git a/.github/workflows/build-publish.yaml b/.github/workflows/build-publish.yaml index 6680830f..35252a3d 100644 --- a/.github/workflows/build-publish.yaml +++ b/.github/workflows/build-publish.yaml @@ -28,7 +28,7 @@ jobs: python -m pip install pyyaml - name: Create ${{ matrix.py_version }} addon.xml - run: python build.py --version ${{ matrix.py_version }} + run: python .build/build.py --version ${{ matrix.py_version }} --source . --target . - name: Publish Build Artifact uses: actions/upload-artifact@v2 @@ -53,8 +53,8 @@ jobs: - name: Upload to repo server uses: burnett01/rsync-deployments@4.1 with: - switches: -rltgoDzvO --delete --exclude='*' --include='**/*.apk' --include='*.txt' - path: plugin.video.jellyfin-${{ matrix.py_version }}.zip + switches: -vrptz + path: '*.zip' remote_path: /srv/repository/incoming/kodi remote_host: ${{ secrets.DEPLOY_HOST }} remote_user: ${{ secrets.DEPLOY_USER }} @@ -69,5 +69,5 @@ jobs: envs: JELLYFIN_VERSION script_stop: true script: | - python3 /usr/local/bin/kodirepo add /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ matrix.py_version }}.zip --datadir /srv/repository/releases/client/kodi/${{ matrix.py_version }}; - rm /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ matrix.py_version }}.zip; + python3 /usr/local/bin/kodirepo add /srv/repository/incoming/kodi/plugin.video.jellyfin+${{ matrix.py_version }}.zip --datadir /srv/repository/releases/client/kodi/${{ matrix.py_version }}; + rm /srv/repository/incoming/kodi/plugin.video.jellyfin+${{ matrix.py_version }}.zip; diff --git a/tox.ini b/tox.ini index fdb7a179..2001112e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [flake8] max-line-length = 9999 import-order-style = pep8 -exclude = ./.git,./.vscode,./libraries +exclude = .git,.vscode,.build,libraries extend-ignore = I202 per-file-ignores = @@ -19,7 +19,6 @@ source = context_play.py default.py service.py - .config/generate_xml.py omit = tests/* branch = True command_line = -m pytest --junitxml=test.xml From 1526e0be5469ec426e120757ea0d6f46fa5aeb96 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Fri, 28 May 2021 01:18:11 +0200 Subject: [PATCH 08/10] add create-release workflow --- .github/workflows/create-release-pr.yaml | 66 ++++++++++++++++++++++++ .github/workflows/on-pr-merge.yaml | 31 +++++++++++ 2 files changed, 97 insertions(+) create mode 100644 .github/workflows/create-release-pr.yaml create mode 100644 .github/workflows/on-pr-merge.yaml diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml new file mode 100644 index 00000000..17d2266e --- /dev/null +++ b/.github/workflows/create-release-pr.yaml @@ -0,0 +1,66 @@ +name: Create Release PR + +on: + workflow_dispatch: + +jobs: + create_pr: + name: "Create Pump Version PR" + runs-on: ubuntu-latest + steps: + + - name: Update Draft + uses: release-drafter/release-drafter@v5.15.0 + id: draft + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup YQ + uses: chrisdickinson/setup-yq@latest + with: + yq-version: v4.9.1 + + - name: Pars Changhelog + run: | + cat << EOF >> cl.md + ${{ steps.draft.outputs.body }} + EOF + TAG="${{ steps.draft.outputs.tag_name }}" + echo "VERSION=${TAG#v}" >> $GITHUB_ENV + echo "CHANGELOG=`cat cl.md | grep '*'`" >> $GITHUB_ENV + rm cl.md + + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Update release.yaml + run: | + yq eval '.version = env(VERSION) | .changelog = strenv(CHANGELOG) | .changelog style="literal"' -i release.yaml + + - name: Commit Changes + run: | + git config user.name "jellyfin-bot" + git config user.email "team@jellyfin.org" + + git checkout -b prepare-${{ env.VERSION }} + git commit -am "bump version" + + if [[ -z "$(git ls-remote --heads origin prepare-${{ env.VERSION }})" ]]; then + git push origin prepare-${{ env.VERSION }} + else + git push -f origin prepare-${{ env.VERSION }} + fi + + - name: Create or Update PR + uses: k3rnels-actions/pr-update@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + pr_title: Prepare for release ${{ steps.draft.outputs.tag_name }} + pr_source: prepare-${{ env.VERSION }} + pr_labels: 'release-prep,skip-changelog' + pr_body: | + :robot: This is a generated PR to bump the `release.yaml` version and update the changelog. + + --- + + ${{ env.CHANGELOG }} diff --git a/.github/workflows/on-pr-merge.yaml b/.github/workflows/on-pr-merge.yaml new file mode 100644 index 00000000..acd4c6e3 --- /dev/null +++ b/.github/workflows/on-pr-merge.yaml @@ -0,0 +1,31 @@ +name: On PR Merge + +on: + pull_request_target: + branches: + - master + types: + - closed + +jobs: + trigger_release: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'release-prep') }} + steps: + - name: Setup YQ + uses: chrisdickinson/setup-yq@latest + with: + yq-version: v4.9.1 + + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Create Tag + run: | + git config user.name "jellyfin-bot" + git config user.email "team@jellyfin.org" + + VERSION="$(yq e -M '.version' release.yaml)" + + git tag v${VERSION} + git push origin v${VERSION} From 14f500e8560a28177d6805349d846e0cafa219ae Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Tue, 1 Jun 2021 21:26:59 +0200 Subject: [PATCH 09/10] overhaul workflows to be less complex and more reliable --- .github/workflows/build.yaml | 39 +++++++++++++++++++ .github/workflows/create-release-pr.yaml | 8 ++-- .github/workflows/on-pr-merge.yaml | 31 --------------- .../{build-publish.yaml => publish.yaml} | 29 +++++--------- .github/workflows/release-drafter.yaml | 6 +-- .github/workflows/test.yaml | 4 +- 6 files changed, 56 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/build.yaml delete mode 100644 .github/workflows/on-pr-merge.yaml rename .github/workflows/{build-publish.yaml => publish.yaml} (81%) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 00000000..66f2e3d7 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,39 @@ +name: Build Jellyfin-Kodi + +on: + push: + branches: + - master + tags: + - '*' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + py_version: [ 'py2', 'py3' ] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python 3.x + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pyyaml + + - name: Create ${{ matrix.py_version }} addon.xml + run: python .build/build.py --version ${{ matrix.py_version }} --source . --target . + + - name: Publish Build Artifact + uses: actions/upload-artifact@v2 + with: + retention-days: 14 + name: ${{ matrix.py_version }}-build-artifact + path: | + *.zip diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index 17d2266e..b9cb6938 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -13,14 +13,14 @@ jobs: uses: release-drafter/release-drafter@v5.15.0 id: draft env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} - name: Setup YQ uses: chrisdickinson/setup-yq@latest with: yq-version: v4.9.1 - - name: Pars Changhelog + - name: Parse Changlog run: | cat << EOF >> cl.md ${{ steps.draft.outputs.body }} @@ -43,7 +43,7 @@ jobs: git config user.email "team@jellyfin.org" git checkout -b prepare-${{ env.VERSION }} - git commit -am "bump version" + git commit -am "bump version to ${{ env.VERSION }}" if [[ -z "$(git ls-remote --heads origin prepare-${{ env.VERSION }})" ]]; then git push origin prepare-${{ env.VERSION }} @@ -54,7 +54,7 @@ jobs: - name: Create or Update PR uses: k3rnels-actions/pr-update@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.JF_BOT_TOKEN }} pr_title: Prepare for release ${{ steps.draft.outputs.tag_name }} pr_source: prepare-${{ env.VERSION }} pr_labels: 'release-prep,skip-changelog' diff --git a/.github/workflows/on-pr-merge.yaml b/.github/workflows/on-pr-merge.yaml deleted file mode 100644 index acd4c6e3..00000000 --- a/.github/workflows/on-pr-merge.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: On PR Merge - -on: - pull_request_target: - branches: - - master - types: - - closed - -jobs: - trigger_release: - runs-on: ubuntu-latest - if: ${{ github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'release-prep') }} - steps: - - name: Setup YQ - uses: chrisdickinson/setup-yq@latest - with: - yq-version: v4.9.1 - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Create Tag - run: | - git config user.name "jellyfin-bot" - git config user.email "team@jellyfin.org" - - VERSION="$(yq e -M '.version' release.yaml)" - - git tag v${VERSION} - git push origin v${VERSION} diff --git a/.github/workflows/build-publish.yaml b/.github/workflows/publish.yaml similarity index 81% rename from .github/workflows/build-publish.yaml rename to .github/workflows/publish.yaml index 35252a3d..df5c2b38 100644 --- a/.github/workflows/build-publish.yaml +++ b/.github/workflows/publish.yaml @@ -1,19 +1,22 @@ -name: Build Jellyfin-Kodi +name: Publish Jellyfin-Kodi on: - push: - branches: - - master - tags: - - '*' + workflow_dispatch: jobs: - build: + publish: runs-on: ubuntu-latest strategy: matrix: py_version: [ 'py2', 'py3' ] steps: + - name: Update Draft + uses: release-drafter/release-drafter@v5.15.0 + with: + publish: true + env: + GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} + - name: Checkout repository uses: actions/checkout@v2 @@ -38,18 +41,6 @@ jobs: path: | *.zip - publish: - runs-on: ubuntu-latest - needs: build - if: ${{ startsWith(github.ref, 'refs/tags/') && github.repository == 'jellyfin/jellyfin-kodi' }} - strategy: - matrix: - py_version: [ 'py2', 'py3' ] - steps: - - uses: actions/download-artifact@v2 - with: - name: ${{ matrix.py_version }}-build-artifact - - name: Upload to repo server uses: burnett01/rsync-deployments@4.1 with: diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml index 6e96ae70..9a451900 100644 --- a/.github/workflows/release-drafter.yaml +++ b/.github/workflows/release-drafter.yaml @@ -4,8 +4,6 @@ on: push: branches: - master - tags: - - '*' jobs: update_release_draft: @@ -14,7 +12,5 @@ jobs: steps: - name: Update Release Draft uses: release-drafter/release-drafter@v5.15.0 - with: - publish: ${{ startsWith(github.ref, 'refs/tags/') }} env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4908d794..9701793c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -4,7 +4,7 @@ on: push: branches: - master - pull_request: + pull_request_target: branches: - master @@ -54,7 +54,7 @@ jobs: - name: Run reviewdog for PR checks-api if: ${{ env.PR_TRIGGERED == 'true' && matrix.py_version == '3.9' }} env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.JF_BOT_TOKEN }} run: | cat flake8.output | reviewdog -reporter=github-pr-check -f=flake8 -name="flake8" From 40fe1402c3a65886a88f6d0c1a0797c8ac733752 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Tue, 1 Jun 2021 21:39:51 +0200 Subject: [PATCH 10/10] move build.py back to root --- .github/workflows/build.yaml | 2 +- .github/workflows/publish.yaml | 2 +- .build/build.py => build.py | 0 tox.ini | 6 ++++-- 4 files changed, 6 insertions(+), 4 deletions(-) rename .build/build.py => build.py (100%) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 66f2e3d7..c0df5959 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ jobs: python -m pip install pyyaml - name: Create ${{ matrix.py_version }} addon.xml - run: python .build/build.py --version ${{ matrix.py_version }} --source . --target . + run: python build.py --version ${{ matrix.py_version }} - name: Publish Build Artifact uses: actions/upload-artifact@v2 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index df5c2b38..8c18a0df 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -31,7 +31,7 @@ jobs: python -m pip install pyyaml - name: Create ${{ matrix.py_version }} addon.xml - run: python .build/build.py --version ${{ matrix.py_version }} --source . --target . + run: python build.py --version ${{ matrix.py_version }} - name: Publish Build Artifact uses: actions/upload-artifact@v2 diff --git a/.build/build.py b/build.py similarity index 100% rename from .build/build.py rename to build.py diff --git a/tox.ini b/tox.ini index 2001112e..a8463728 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [flake8] max-line-length = 9999 import-order-style = pep8 -exclude = .git,.vscode,.build,libraries +exclude = .git,.vscode,libraries,build.py extend-ignore = I202 per-file-ignores = @@ -19,6 +19,8 @@ source = context_play.py default.py service.py -omit = tests/* +omit = + tests/* + build.py branch = True command_line = -m pytest --junitxml=test.xml