diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 754303a7..25448ed1 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -1,9 +1,20 @@
-# Starter pipeline
-# Start with a minimal pipeline that you can customize to build and deploy your code.
-# Add steps that build, run tests, deploy, and more:
-# https://aka.ms/yaml
+trigger:
+  batch: true
+  branches:
+    include:
+      - '*'
+  tags:
+    include:
+      - '*'
 
 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
index 26006fc7..39fd9488 100644
--- a/.ci/build.yml
+++ b/.ci/build.yml
@@ -16,10 +16,26 @@ steps:
     - 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'
+        contents: |
+          **/*
+          !.ci
+          !.config
+          !.git
+          !.github
+          !.gitignore
+          !requirements-dev.txt
+          !tests
+          !tox.ini
+        TargetFolder: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin'
+
     - task: ArchiveFiles@2
       displayName: 'Create ${{ py_version }} zip file'
       inputs:
-        rootFolderOrFile: jellyfin-kodi
+        rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin'
         includeRootFolder: False
         archiveType: 'zip'
         tarCompression: 'none'
@@ -28,22 +44,6 @@ steps:
     - task: PublishPipelineArtifact@1
       displayName: 'Publish ${{ py_version }} artifact'
       inputs:
-        targetPath: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin-${{ py_version }}.zip'
-        artifactName: 'plugin.video.jellyfin-${{ py_version }}-$(Build.BuildNumber).zip'
+        targetPath: '$(Build.ArtifactStagingDirectory)/plugin.video.jellyfin'
+        artifactName: 'plugin.video.jellyfin-${{ py_version }}-$(Build.BuildNumber)'
 
-    - 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'
-      condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
-
-    - task: SSH@0
-      displayName: 'Add to Kodi repo'
-      inputs:
-        sshEndpoint: repository
-        runOptions: 'commands'
-        commands: 'sudo -n python3 /usr/local/bin/kodirepo add /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ py_version }} --datadir /srv/repository/releases/client/kodi/${{ py_version }}'
-      condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
diff --git a/.ci/publish.yml b/.ci/publish.yml
new file mode 100644
index 00000000..c52d1b60
--- /dev/null
+++ b/.ci/publish.yml
@@ -0,0 +1,19 @@
+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: 'sudo -n python3 /usr/local/bin/kodirepo add /srv/repository/incoming/kodi/plugin.video.jellyfin-${{ py_version }} --datadir /srv/repository/releases/client/kodi/${{ py_version }}'