Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/ci_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: "CICD - Development"
on:
push:
branches:
- devel/*
paths:
- container/**
- container/**
workflow_dispatch:

jobs:
############################################################
# Base Image Check
############################################################
tc_docker:
name: "Development Build - tc_docker"
runs-on: ubuntu-latest
strategy:
matrix:
container: [ tc_docker ]
env:
CONTAINER: ${{ matrix.container }}
steps:
- name: Check out from ${{ github.ref }}
id: checkout
uses: actions/checkout@v3
- name: Run pre-build hooks
id: hooks
run: |
$GITHUB_WORKSPACE/ci/hooks/pre_build
- name: Set tag
id: tag
run: |
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
[[ "${{ github.ref }} == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
[ "$VERSION" == "main" ] && VERSION="latest"
[ "$VERSION" == "latest" ] && VERSION="latest"
[ "$VERSION" == "devel" ] && VERSION="devel"
echo "::set-output name=VERSION::$(echo $VERSION)"
- name: Setup qemu environment
uses: docker/setup-qemu-action@v2
- name: Setup buildx environment
uses: docker/setup-buildx-action@v2
- name: Log into ghcr.io
id: docker_login
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USER }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Build ${{ matrix.container }}
id: docker_push
uses: docker/build-push-action@v3
with:
tags: rootwyrm/${{ matrix.container }}:${{ steps.tag.outputs.VERSION }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ SYSTEMS.md
todo
dbt*
*/Dockerfile.test
vpn*
12 changes: 2 additions & 10 deletions container/tc_sonarr/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# See /LICENSE for details
################################################################################
ARG TALECASTER_BASE=latest
FROM docker.io/rootwyrm/tc_mono:$TALECASTER_BASE
FROM docker.io/rootwyrm/tc_docker:$TALECASTER_BASE

## Labels
LABEL maintainer="Phillip 'RootWyrm' Jaenke <talecaster@rootwyrm.com>" \
Expand All @@ -17,14 +17,6 @@ LABEL maintainer="Phillip 'RootWyrm' Jaenke <talecaster@rootwyrm.com>" \
com.rootwyrm.license="CC-BY-NC-4.0" \
com.rootwyrm.vcs-type="github" \
com.rootwyrm.vcs.url="%%GITHUB_REPOSITORY%%" \
## label-schema.org
org.label-schema.schema-version="1.0.0-rc1" \
org.label-schema.vendor="RootWyrm" \
org.label-schema.name="tc_sonarr" \
org.label-schema.url="%%GITHUB_REPOSITORY%%" \
org.label-schema.vcs-ref="%%VCS_REF%%" \
org.label-schema.version="%%REF%%" \
org.label-schema.build-date="%%RW_BUILDDATE%%" \
## OCI
org.opencontainers.image.authors="RootWyrm" \
org.opencontainers.image.vendor="RootWyrm" \
Expand All @@ -43,4 +35,4 @@ COPY [ "cron/", "/etc/periodic/" ]
## Volumes
VOLUME [ "/talecaster/blackhole", "/talecaster/downloads", "/talecaster/television", "/talecaster/archive" ]

# vim:sw=4:ts=4
# vim:sw=4:ts=4
24 changes: 19 additions & 5 deletions container/tc_sonarr/application/build/20.sonarr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,30 @@
# Licensed under CC-BY-NC-4.0
# See /LICENSE for details
################################################################################
## build/20.nzbget.sh
## build/20.sonarr.sh
. /opt/talecaster/lib/talecaster.lib.sh

export app_name="Sonarr"
export app_url="http://www.sonarr.tv/"
export app_destdir="/opt/Sonarr"

export BRANCH="phantom-develop"
export VERSION="3"
export APPURL="https://services.sonarr.tv/v1/download/main/latest?version=3&os=linux"
#https://services.sonarr.tv/v1/download/develop/latest?version=4&os=linux-musl&arch=arm64
export OSARCH="linux-musl"
export ARCH=$(uname -m)
case $ARCH in
x86*)
export ARCH="x64"
;;
aarch64*)
export ARCH="arm64"
;;
*)
echo "Unsupported architecture!"
exit 255
;;
esac
export VERSION="4"
export APPURL='https://services.sonarr.tv/v1/download/develop/latest?version='${VERSION}'&os='${OSARCH}'&arch='${ARCH}''

######################################################################
## Application Install
Expand Down Expand Up @@ -44,4 +58,4 @@ echo "Entering $0"
load_config

LOG "[BUILD] Installing ${app_name}"
application_install
application_install
2 changes: 1 addition & 1 deletion container/tc_sonarr/application/sonarr.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.7.1477
4.0.0.443
4 changes: 0 additions & 4 deletions packages.json

This file was deleted.

168 changes: 168 additions & 0 deletions python/TaleCaster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import os
import sys
import subprocess
import threading
import rich

## TaleCasterApplication object
class TaleCasterApplication(object):
def indirect(self,service):
method_name=service
method=getattr(self,method_name,lambda :'Invalid')
return method()

## Only supports Sonarr
def television(self):
self.application = 'television'
self.application_method = 'runtime'
self.application_bin = '/opt/Sonarr/Sonarr.exe'
self.application_args = [ f'-appdata={self.application_configdir}', f'-data={self.application_configdir}', '-nobrowser' ]
## Only supports Radarr
def movies(self):
self.application = 'movies'
self.application_method = 'runtime'
self.application_bin = '/opt/Radarr/Radarr.exe'
self.application_args = [ f'-appdata={self.application_configdir}', f'-data={self.application_configdir}', '-nobrowser' ]
## Only supports Lidarr
def music(self):
self.application = 'music'
self.application_method = 'runtime'
self.application_bin = '/opt/Lidarr/Lidarr.exe'
self.application_args = [ f'-appdata={self.application_configdir}', f'-data={self.application_configdir}', '-nobrowser' ]
## Only supports Readarr
def books(self):
self.application = 'books'
self.application_method = 'runtime'
self.application_bin = '/opt/Readarr/Readarr.exe'
self.application_args = [ f'-appdata={self.application_configdir}', f'-data={self.application_configdir}', '-nobrowser' ]
## Only supports Mylar (python)
def comics(self):
self.application = 'comics'
self.application_method = 'python'
self.application_bin = '/opt/mylar/mylar.py'
self.application_args = ''
self.python_venv_use = True
self.python_venv_dir = '/opt/talecaster/venv'
## Only supports Prowlarr
def indexer(self):
self.application = 'indexer'
self.application_method = 'runtime'
self.application_bin = '/opt/Prowlarr/Prowlarr'
self.application_config = '/talecaster/config/config.xml'
self.application_configdir = '/talecaster/config'
self.application_pidfile = '/talecaster/config/prowlarr.pid'
self.application_args = [ f'-appdata={self.application_configdir}', f'-data={self.application_configdir}', '-nobrowser' ]
## Uses nginx always
def frontend(self):
self.application = 'frontend'
self.application_method = 'direct'
self.application_bin = '/usr/sbin/nginx'
self.application_config = '/etc/nginx/nginx.conf'
self.application_args = [ f'-c {self.application_config}' ]
self.application_selftest = os.system(["", self.application_bin, self.application_args, '-t', '-q' ])

def nntp(self):
self.application = 'nntp'
self.application_method = 'direct'
self.application_bin = '/opt/nzbget/nzbget'
self.application_config = '/talecaster/config/nzbget.conf'
self.application_args = [ f"-c {self.application_config}" ]

## The torrent stuff is more complicated...
def qbittorrent(self):
self.application = 'torrent'
self.application_method = 'direct'
self.application_bin = '/usr/local/bin/qbittorrent-nox'
self.application_configdir = '/talecaster/config'
self.application_args = [ f'--profile={self.application_configdir}' ]

## XXX: Not Yet Implemented
def transmission(self):
self.application = 'torrent'
self.application_real = 'transmission'
self.application_method = 'direct'
self.application_bin = '/usr/local/bin/transmission'
self.application_configdir = '/talecaster/config'

## XXX: Not Yet Implemented
## need to set default UI to web before running
## --config /talecaster/config
def deluge(self):
self.application = 'torrent'
self.application_real = 'deluge'
self.application_method = 'direct'
self.application_bin = '/usr/local/bin/deluge'
self.application_configdir = '/talecaster/config'

class TaleCaster_run_service(threading.Thread):
def __init__(self, service, queue):
super().__init__()

self.queue = queue
self.service = service
global app
app=TaleCasterApplication()
app.indirect(service)

method = (app.application_method)
#if app.application_selftest is not None:
# selftest = (app.application_selftest)
print(f"service {self.service}")
self.run()

def run(self):
print("enter run")
if app.application_method == 'runtime':
print("os chdir")
os.chdir(app.application_configdir)
os.setgid(int(os.environ["tcgid"]))
os.setuid(int(os.environ["tcuid"]))
service_process = subprocess.Popen(executable=app.application_bin, args=app.application_args, shell=False)#, stdout=subprocess.STDOUT, stderr=subprocess.STDOUT)
try:
self.queue.put(service_process.communicate())
except OSError:
rich.print(prefix_service, "failed to start service {app.application}!")
os._exit(service_process.returncode)

class TaleCaster_run_openvpn(threading.Thread):
def __init__(self, openvpn_config, queue):
super().__init__()

self.openvpn_config = openvpn_config
self.queue = queue

global prefix_openvpn
prefix_openvpn = "[[bold dark_orange]OpenVPN[/]]"
## Test for /dev/tun during __init__ to bail out quickly
try:
open("/dev/net/tun", "r")
except:
rich.print(prefix_openvpn, "[bold red]FATAL:[/] unable to open /dev/tun, check compose configuration.")
## Bail out quickly
os._exit(100)

self.run()

## This check should be simplified and before calling class.
def run(self):
message_prefix = "[[bold dark_orange]OpenVPN[/]]"
ovpn_pidfile = "/run/openvpn.pid"
openvpn_cmd = f'/usr/sbin/openvpn --config {self.openvpn_config} --log /var/log/openvpn.log --cd /talecaster/shared'

## XXX: needs /dev/tun check
if self.openvpn_config is None:
rich.print(prefix_openvpn, "[bold red]FATAL:[/] openvpn_config is undefined!")
os._exit(200)
try:
## Always run as root in the shared directory
os.chdir("/talecaster/shared")
os.setgid(0)
os.setuid(0)
## Has to be done this way; if args aren't a single string, OpenVPN bails out.
openvpn_process = subprocess.Popen(openvpn_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, user="root", group="root")
rich.print(prefix_openvpn, "running as PID", openvpn_process.pid)
self.queue.put(openvpn_process.pid)
self.queue.put(openvpn_process.communicate())
except:
rich.print(prefix_openvpn, "[bold red]FATAL:[/] OpenVPN failed to start!")
os._exit(100)
Loading