Skip to content

Commit

Permalink
setup: add launchd script
Browse files Browse the repository at this point in the history
  • Loading branch information
karlicoss committed May 30, 2020
1 parent 3d24b69 commit 64e146c
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 25 deletions.
6 changes: 4 additions & 2 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,11 @@ I'm working on improving the documentation on setting the server up, so please f
You only have to start it once, it will automatically detect further index changes.
- [optional] autostart the server with =promnesia install-server=

This sets it up to start via Systemd, which should be present in most common *Linux* distributions, so it won't work on Mac/Windows at the moment.
This sets it up to autostart and run in the background:

- via Systemd for Linux
- via Launchd for OSX. I don't have a Mac nearby, so if you have any issues with it, please report them!

I don't have a Mac nearby, so if you can contribute a Launchd script, or point me to a setup guide, I'll be very grateful!
I /think/ you can also use cron with =@reboot= attribute, or just create a manual autostart entry.

# TODO Frontend -- mention what settings are possible?
Expand Down
117 changes: 95 additions & 22 deletions src/promnesia/misc/install_server.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
#!/usr/bin/env python3
import argparse

import os
import sys
import time
from pathlib import Path
import platform
from subprocess import check_call, run
from typing import List

SYSTEM = platform.system()
UNSUPPORTED_SYSTEM = RuntimeError(f'Platform {SYSTEM} is not supported yet!')

from .. import root
from .. import server

SYSTEMD_CONFIG = """
SYSTEMD_TEMPLATE = '''
[Unit]
Description=Promnesia browser extension backend
Expand All @@ -19,7 +25,28 @@
ExecStart={launcher} {extra_args}
Type=simple
Restart=always
"""
'''

LAUNCHD_TEMPLATE = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>{service_name}</string>
<key>ProgramArguments</key>
<array>
{arguments}
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
'''


def systemd(*args, method=check_call):
Expand All @@ -28,24 +55,13 @@ def systemd(*args, method=check_call):
])


def install(args):
unit_name = args.unit_name
out = Path(f'~/.config/systemd/user/{unit_name}').expanduser()
print(f"Writing systemd config to {out}:")
def install_systemd(name: str, out: Path, launcher: str, largs: List[str]) -> None:
unit_name = name

# ugh. we want to know whether we're invoked 'properly' as an executable or ad-hoc via scripts/promnesia
if os.environ.get('DIRTY_RUN') is not None:
launcher = str(root() / 'scripts/promnesia')
else:
# must be installed, so available in PATH
import distutils.spawn
exe = distutils.spawn.find_executable('promnesia'); assert exe is not None
launcher = exe # older systemd wants absolute paths..
import shlex
extra_args = ' '.join(shlex.quote(str(a)) for a in largs)

extra_args = f'serve --db {args.db} --timezone {args.timezone} --port {args.port}'

out.parent.mkdir(parents=True, exist_ok=True) # sometimes systemd dir doesn't exist
out.write_text(SYSTEMD_CONFIG.format(
out.write_text(SYSTEMD_TEMPLATE.format(
launcher=launcher,
extra_args=extra_args,
))
Expand All @@ -57,10 +73,67 @@ def install(args):
systemd('start' , unit_name)
systemd('status', unit_name)
except Exception as e:
print(f"Something has gone wrong... you might want to use 'journalctl --user -u {unit_name}' to investigate")
print(f"Something has gone wrong... you might want to use 'journalctl --user -u {unit_name}' to investigate", file=sys.stderr)
raise e


def setup_parser(p: argparse.ArgumentParser):
p.add_argument('--unit-name', type=str, default='promnesia.service', help='Systemd unit name')
def install_launchd(name: str, out: Path, launcher: str, largs: List[str]) -> None:
service_name = name
arguments = '\n'.join(f'<string>{a}</string>' for a in [launcher, *largs])
out.write_text(LAUNCHD_TEMPLATE.format(
service_name=service_name,
arguments=arguments,
))
cmd = ['launchctl', 'load', '-w', str(out)]
print('Running: ' + ' '.join(cmd), file=sys.stderr)
check_call(cmd)

time.sleep(1) # to give it some time? not sure if necessary
check_call(f'launchctl list | grep {name}', shell=True)


def install(args) -> None:
name = args.name
if SYSTEM == 'Linux':
suf = '.service'
if Path(name).suffix != suf:
name = name + suf
out = Path(f'~/.config/systemd/user/{name}')
elif SYSTEM == 'Darwin': # osx
out = Path(f'~/Library/LaunchAgents/{name}.plist')
else:
raise UNSUPPORTED_SYSTEM
out = out.expanduser()
print(f"Writing launch script to {out}", file=sys.stderr)

# ugh. we want to know whether we're invoked 'properly' as an executable or ad-hoc via scripts/promnesia
if os.environ.get('DIRTY_RUN') is not None:
launcher = str(root() / 'scripts/promnesia')
else:
# must be installed, so available in PATH
import distutils.spawn
exe = distutils.spawn.find_executable('promnesia'); assert exe is not None
launcher = exe # older systemd wants absolute paths..

largs = ['serve', '--db', args.db, '--timezone', args.timezone, '--port', args.port]

out.parent.mkdir(parents=True, exist_ok=True) # sometimes systemd dir doesn't exist
if SYSTEM == 'Linux':
install_systemd(name=name, out=out, launcher=launcher, largs=largs)
elif SYSTEM == 'Darwin':
install_launchd(name=name, out=out, launcher=launcher, largs=largs)
else:
raise UNSUPPORTED_SYSTEM


def setup_parser(p: argparse.ArgumentParser) -> None:
if SYSTEM == 'Linux':
dflt = 'promnesia.service'
elif SYSTEM == 'Darwin':
dflt = 'com.github.karlicoss.promnesia'
else:
raise UNSUPPORTED_SYSTEM

p.add_argument('--name', type=str, default=dflt, help='Systemd/launchd service name')
p.add_argument('--unit-name', type=str, dest='name', help='DEPRECATED, same as --name')
server.setup_parser(p)
2 changes: 1 addition & 1 deletion tests/install_and_run
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def run(tdir: Path):

check_call([
promnesia, 'install-server',
'--unit-name', 'promnesia-test.service',
'--name' , 'promnesia-test', # should add .serice arg
'--db', str(tdir / 'promnesia.sqlite'),
'--timezone', 'Europe/Moscow',
'--port', '17777', # TODO get free port?
Expand Down

0 comments on commit 64e146c

Please sign in to comment.