Add build script and set up pipeline

This commit is contained in:
Matt
2021-02-28 16:58:59 -05:00
parent 757f0a411c
commit bad47421c0
8 changed files with 314 additions and 7 deletions

22
.ci/azure-pipelines.yml Normal file
View File

@@ -0,0 +1,22 @@
trigger:
batch: true
branches:
include:
- '*'
tags:
include:
- '*'
jobs:
- 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' ]

45
.ci/build.yml Normal file
View File

@@ -0,0 +1,45 @@
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 -r jellycon/requirements-dev.txt
displayName: 'Install dev dependencies'
- task: CopyFiles@2
displayName: 'Create clean addon directory'
inputs:
sourceFolder: 'jellyfin-kodi'
cleanTargetFolder: true
contents: |
**/*
!.ci/*
!.git/**/*
!.github/*
TargetFolder: '$(Build.ArtifactStagingDirectory)/plugin.video.jellycon'
- script: python3 '$(Build.ArtifactStagingDirectory)/plugin.video.jellycon/build.py' --version ${{ py_version }} --target '$(Build.ArtifactStagingDirectory)/'
displayName: 'Create ${{ py_version }} addon.xml'
- task: ArchiveFiles@2
displayName: 'Create ${{ py_version }} zip file'
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/plugin.video.jellycon'
includeRootFolder: True
archiveType: 'zip'
tarCompression: 'none'
archiveFile: '$(Build.ArtifactStagingDirectory)/plugin.video.jellycon-${{ py_version }}.zip'
- task: PublishPipelineArtifact@1
displayName: 'Publish ${{ py_version }} artifact'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/plugin.video.jellycon'
artifactName: 'plugin.video.jellycon-${{ py_version }}-$(Build.BuildNumber)'

27
.ci/publish.yml Normal file
View File

@@ -0,0 +1,27 @@
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.jellycon-${{ 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.jellycon-${{ 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.jellycon-${{ py_version }}.zip'

70
.config/generate_xml.py Normal file
View File

@@ -0,0 +1,70 @@
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)

View File

@@ -1,15 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.jellycon"
name="JellyCon"
version="0.3.1"
version=""
provider-name="Jellyfin Contributors">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="script.module.pil" version="1.1.7"/>
<import addon="script.module.requests" version="2.22.0"/>
<import addon="script.module.six" version="1.13.0"/>
<import addon="script.module.kodi-six" />
<import addon="script.module.websocket" />
</requires>
<extension point="xbmc.python.pluginsource" library="default.py">
<provides>video audio</provides>

110
build.py Executable file
View File

@@ -0,0 +1,110 @@
#!/usr/bin/env python
import argparse
from datetime import datetime
import os
from pathlib import Path
import xml.etree.ElementTree as ET
import zipfile
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
def create_addon_xml(config, source, py_version):
'''
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, source, target):
'''
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 files:
if 'plugin.video.jellyfin' not in filename and 'pyo' not in filename:
file_path = os.path.join(root, filename)
relative_path = os.path.join('plugin.video.jellyfin', os.path.relpath(file_path, source))
z.write(file_path, relative_path)
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:
config = yaml.safe_load(fh)
#py_version = 'py{}'.format(args.version)
create_addon_xml(config, args.source, args.version)
zip_files(args.version, args.source, args.target)

38
release.yaml Normal file
View File

@@ -0,0 +1,38 @@
version: '0.3.1'
changelog: |
- #37 - Don't urlencode auth json payload
- #39 - Fix clone skin function
- #40 - Show proper language names for external subs
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.57.0+matrix.1'

1
requirements-dev.txt Normal file
View File

@@ -0,0 +1 @@
pyyaml