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