discourse.plugins: Make the updater able to package plugins

Let the update.py script handle the initial, repetitive task of
packaging new plugins. With this in place, the plugin only needs to be
added to the list in `update-plugins` and most of the work will be
done automatically when the script is run. Metadata still needs to be
filled in manually and some packages may of course require additional
work/patching.
This commit is contained in:
talyz 2021-08-13 18:42:56 +02:00
parent 4197b6dd14
commit f8096460bd
No known key found for this signature in database
GPG Key ID: 2DED2151F4671A2B
2 changed files with 65 additions and 3 deletions

View File

@ -284,11 +284,22 @@ services.discourse = {
Ruby dependencies are listed in its
<filename>plugin.rb</filename> file as function calls to
<literal>gem</literal>. To construct the corresponding
<filename>Gemfile</filename>, run <command>bundle
<filename>Gemfile</filename> manually, run <command>bundle
init</command>, then add the <literal>gem</literal> lines to it
verbatim.
</para>
<para>
Much of the packaging can be done automatically by the
<filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename>
script - just add the plugin to the <literal>plugins</literal>
list in the <function>update_plugins</function> function and run
the script:
<programlisting language="bash">
./update.py update-plugins
</programlisting>.
</para>
<para>
Some plugins provide <link
linkend="module-services-discourse-site-settings">site

View File

@ -12,6 +12,7 @@ import os
import stat
import json
import requests
import textwrap
from distutils.version import LooseVersion
from pathlib import Path
from typing import Iterable
@ -77,7 +78,11 @@ def _call_nix_update(pkg, version):
def _nix_eval(expr: str):
nixpkgs_path = Path(__file__).parent / '../../../../'
return json.loads(subprocess.check_output(['nix', 'eval', '--json', f'(with import {nixpkgs_path} {{}}; {expr})'], text=True))
try:
output = subprocess.check_output(['nix', 'eval', '--json', f'(with import {nixpkgs_path} {{}}; {expr})'], text=True)
except subprocess.CalledProcessError:
return None
return json.loads(output)
def _get_current_package_version(pkg: str):
@ -206,13 +211,59 @@ def update_plugins():
repo_name = plugin.get('repo_name') or name
repo = DiscourseRepo(owner=owner, repo=repo_name)
filename = _nix_eval(f'builtins.unsafeGetAttrPos "src" discourse.plugins.{name}')
if filename is None:
filename = Path(__file__).parent / 'plugins' / name / 'default.nix'
filename.parent.mkdir()
has_ruby_deps = False
for line in repo.get_file('plugin.rb', repo.latest_commit_sha).splitlines():
if 'gem ' in line:
has_ruby_deps = True
break
with open(filename, 'w') as f:
f.write(textwrap.dedent(f"""
{{ lib, mkDiscoursePlugin, fetchFromGitHub }}:
mkDiscoursePlugin {{
name = "{name}";"""[1:] + ("""
bundlerEnvArgs.gemdir = ./.;""" if has_ruby_deps else "") + f"""
src = {fetcher} {{
owner = "{owner}";
repo = "{repo_name}";
rev = "replace-with-git-rev";
sha256 = "replace-with-sha256";
}};
meta = with lib; {{
homepage = "";
maintainers = with maintainers; [ ];
license = licenses.mit; # change to the correct license!
description = "";
}};
}}"""))
all_plugins_filename = Path(__file__).parent / 'plugins' / 'all-plugins.nix'
with open(all_plugins_filename, 'r+') as f:
content = f.read()
pos = -1
while content[pos] != '}':
pos -= 1
content = content[:pos] + f' {name} = callPackage ./{name} {{}};' + os.linesep + content[pos:]
f.seek(0)
f.write(content)
f.truncate()
else:
filename = filename['file']
prev_commit_sha = _nix_eval(f'discourse.plugins.{name}.src.rev')
if prev_commit_sha == repo.latest_commit_sha:
click.echo(f'Plugin {name} is already at the latest revision')
continue
filename = _nix_eval(f'builtins.unsafeGetAttrPos "src" discourse.plugins.{name}')['file']
prev_hash = _nix_eval(f'discourse.plugins.{name}.src.outputHash')
new_hash = subprocess.check_output([
'nix-universal-prefetch', fetcher,