mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-05-15 05:45:10 +00:00
Merge pull request #523 from h1dden-da3m0n/ci/migrate-to-actions
ci: migrate Azure Pipelines to GitHub Actions
This commit is contained in:
commit
399fc32b60
20 changed files with 503 additions and 271 deletions
|
@ -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' ]
|
|
|
@ -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)'
|
|
||||||
|
|
|
@ -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'
|
|
|
@ -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'
|
|
|
@ -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('jellyfin-kodi/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('jellyfin-kodi/addon.xml', encoding='utf-8', xml_declaration=True)
|
|
|
@ -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'
|
|
|
@ -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'
|
|
|
@ -14,7 +14,7 @@ charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
max_line_length = null
|
max_line_length = 9999
|
||||||
|
|
||||||
# YAML indentation
|
# YAML indentation
|
||||||
[*.{yml,yaml}]
|
[*.{yml,yaml}]
|
||||||
|
|
18
.github/dependabot.yaml
vendored
Normal file
18
.github/dependabot.yaml
vendored
Normal file
|
@ -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
|
22
.github/release-drafter.yml
vendored
Normal file
22
.github/release-drafter.yml
vendored
Normal file
|
@ -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
|
39
.github/workflows/build.yaml
vendored
Normal file
39
.github/workflows/build.yaml
vendored
Normal file
|
@ -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.py --version ${{ matrix.py_version }}
|
||||||
|
|
||||||
|
- name: Publish Build Artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
retention-days: 14
|
||||||
|
name: ${{ matrix.py_version }}-build-artifact
|
||||||
|
path: |
|
||||||
|
*.zip
|
41
.github/workflows/codeql.yaml
vendored
Normal file
41
.github/workflows/codeql.yaml
vendored
Normal file
|
@ -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
|
66
.github/workflows/create-release-pr.yaml
vendored
Normal file
66
.github/workflows/create-release-pr.yaml
vendored
Normal file
|
@ -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.JF_BOT_TOKEN }}
|
||||||
|
|
||||||
|
- name: Setup YQ
|
||||||
|
uses: chrisdickinson/setup-yq@latest
|
||||||
|
with:
|
||||||
|
yq-version: v4.9.1
|
||||||
|
|
||||||
|
- name: Parse Changlog
|
||||||
|
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 to ${{ env.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.JF_BOT_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 }}
|
64
.github/workflows/publish.yaml
vendored
Normal file
64
.github/workflows/publish.yaml
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
name: Publish Jellyfin-Kodi
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
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
|
||||||
|
|
||||||
|
- 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.py --version ${{ matrix.py_version }}
|
||||||
|
|
||||||
|
- name: Publish Build Artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
retention-days: 14
|
||||||
|
name: ${{ matrix.py_version }}-build-artifact
|
||||||
|
path: |
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
- name: Upload to repo server
|
||||||
|
uses: burnett01/rsync-deployments@4.1
|
||||||
|
with:
|
||||||
|
switches: -vrptz
|
||||||
|
path: '*.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;
|
16
.github/workflows/release-drafter.yaml
vendored
Normal file
16
.github/workflows/release-drafter.yaml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: Release Drafter
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
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
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
|
73
.github/workflows/test.yaml
vendored
Normal file
73
.github/workflows/test.yaml
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
name: Test Jellyfin-Kodi
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request_target:
|
||||||
|
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.JF_BOT_TOKEN }}
|
||||||
|
run: |
|
||||||
|
cat flake8.output | reviewdog -reporter=github-pr-check -f=flake8 -name="flake8"
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
if: ${{ matrix.py_version == '3.9' }}
|
||||||
|
|
||||||
|
- 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
|
125
build.py
Normal file
125
build.py
Normal file
|
@ -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('{}/.build/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 '.build' 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)
|
35
release.yaml
35
release.yaml
|
@ -2,4 +2,37 @@ version: '0.7.3'
|
||||||
changelog: |
|
changelog: |
|
||||||
- #508 Use jellyfin's DisplayTitle for audio and subtitle streams
|
- #508 Use jellyfin's DisplayTitle for audio and subtitle streams
|
||||||
- #513 Fix subsequent syncs from the addon menu better
|
- #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'
|
||||||
|
|
7
tox.ini
7
tox.ini
|
@ -1,7 +1,7 @@
|
||||||
[flake8]
|
[flake8]
|
||||||
max-line-length = 9999
|
max-line-length = 9999
|
||||||
import-order-style = pep8
|
import-order-style = pep8
|
||||||
exclude = ./.git,./.vscode,./libraries
|
exclude = .git,.vscode,libraries,build.py
|
||||||
extend-ignore =
|
extend-ignore =
|
||||||
I202
|
I202
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
|
@ -19,7 +19,8 @@ source =
|
||||||
context_play.py
|
context_play.py
|
||||||
default.py
|
default.py
|
||||||
service.py
|
service.py
|
||||||
.config/generate_xml.py
|
omit =
|
||||||
omit = tests/*
|
tests/*
|
||||||
|
build.py
|
||||||
branch = True
|
branch = True
|
||||||
command_line = -m pytest --junitxml=test.xml
|
command_line = -m pytest --junitxml=test.xml
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue