From 26c1d65eeffc141010581f96bcaadd3f8adbe546 Mon Sep 17 00:00:00 2001 From: sip21 Date: Thu, 14 Mar 2024 00:10:56 -0600 Subject: [PATCH] Add CI workflow columns in README --- .ci/test.py | 128 +++++++++++++++++++++----- .ci/update_badges.py | 168 ++++++++++++++++++++++++++++++++++ .github/workflows/main.yml | 26 +++++- .github/workflows/nightly.yml | 18 +++- README.md | 78 ++++++++-------- 5 files changed, 348 insertions(+), 70 deletions(-) create mode 100644 .ci/update_badges.py diff --git a/.ci/test.py b/.ci/test.py index 946210143..822aeaa47 100644 --- a/.ci/test.py +++ b/.ci/test.py @@ -1,15 +1,15 @@ -from pathlib import Path -import subprocess -from pprint import pprint -from collections import namedtuple -from typing import Generator - +import json import logging +import os +import shlex import shutil +import subprocess import sys import tempfile -import shlex -import os +from collections import namedtuple +from pathlib import Path, PosixPath +from typing import Generator, List +from itertools import chain logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) @@ -18,8 +18,8 @@ '.ci', '.git', '.github', + 'archived', 'lightning', - 'feeadjuster' ] global_dependencies = [ @@ -42,6 +42,10 @@ ) +def list_plugins(plugins): + return ", ".join([p.name for p in sorted(plugins)]) + + def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]: plugins = list([ x for x in basedir.iterdir() \ @@ -50,13 +54,17 @@ def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]: pip_pytest = [ x for x in plugins if (x / Path('requirements.txt')).exists() ] + print(f"Pip plugins: {list_plugins(pip_pytest)}") poetry_pytest = [ x for x in plugins if (x / Path("pyproject.toml")).exists() ] - print(poetry_pytest) + print(f"Poetry plugins: {list_plugins(poetry_pytest)}") - other_plugins = [x for x in plugins if x not in pip_pytest and x not in poetry_pytest] + other_plugins = [ + x for x in plugins if x not in pip_pytest and x not in poetry_pytest + ] + print(f"Other plugins: {list_plugins(other_plugins)}") for p in sorted(pip_pytest): yield Plugin( @@ -227,20 +235,24 @@ def install_pyln_testing(pip_path): stderr=subprocess.STDOUT, ) -def run_one(p: Plugin) -> bool: - print("Running tests on plugin {p.name}".format(p=p)) - - testfiles = [ +def get_testfiles(p: Plugin) -> List[PosixPath]: + return [ x for x in p.path.iterdir() if (x.is_dir() and x.name == 'tests') or (x.name.startswith("test_") and x.name.endswith('.py')) ] - if len(testfiles) == 0: +def has_testfiles(p: Plugin) -> bool: + return len(get_testfiles(p)) > 0 + +def run_one(p: Plugin) -> bool: + print("Running tests on plugin {p.name}".format(p=p)) + + if not has_testfiles(p): print("No test files found, skipping plugin {p.name}".format(p=p)) return True - print("Found {ctestfiles} test files, creating virtualenv and running tests".format(ctestfiles=len(testfiles))) + print("Found {ctestfiles} test files, creating virtualenv and running tests".format(ctestfiles=len(get_testfiles(p)))) print("##[group]{p.name}".format(p=p)) # Create a virtual env @@ -295,8 +307,65 @@ def run_one(p: Plugin) -> bool: finally: print("##[endgroup]") - -def run_all(args): +def configure_git(): + # Git requires some user and email to be configured in order to work in the context of GitHub Actions. + subprocess.run( + ["git", "config", "--global", "user.email", '"lightningd@github.plugins.repo"'] + ) + subprocess.run(["git", "config", "--global", "user.name", '"lightningd"']) + + +# gather data +def collect_gather_data(results, success): + gather_data = {} + for t in results: + p = t[0] + if has_testfiles(p): + if success or t[1]: + gather_data[p.name] = "passed" + else: + gather_data[p.name] = "failed" + return gather_data + + +def push_gather_data(data, workflow, python_version): + print("Pushing gather data...") + configure_git() + subprocess.run(["git", "fetch"]) + subprocess.run(["git", "checkout", "badges"]) + filenames_to_add = [] + for plugin_name, result in data.items(): + filenames_to_add.append(git_add_gather_data( + plugin_name, result, workflow, python_version + )) + output = subprocess.check_output(list(chain(["git", "add", "-v"], filenames_to_add))).decode("utf-8") + print(f"output from git add: {output}") + if output != "": + output = subprocess.check_output( + [ + "git", + "commit", + "-m", + f"Update test result for Python{python_version} to ({workflow} workflow)", + ] + ).decode("utf-8") + print(f"output from git commit: {output}") + subprocess.run(["git", "push", "origin", "badges"]) + print("Done.") + + +def git_add_gather_data(plugin_name, result, workflow, python_version): + _dir = f".badges/gather_data/{workflow}/{plugin_name}" + filename = os.path.join(_dir, f"python{python_version}.txt") + os.makedirs(_dir, exist_ok=True) + with open(filename, "w") as file: + print(f"Writing {filename}") + file.write(result) + + return filename + + +def run_all(workflow, python_version, update_badges, plugin_names): root_path = subprocess.check_output([ 'git', 'rev-parse', @@ -306,8 +375,8 @@ def run_all(args): root = Path(root_path) plugins = list(enumerate_plugins(root)) - if args != []: - plugins = [p for p in plugins if p.name in args] + if plugin_names != []: + plugins = [p for p in plugins if p.name in plugin_names] print("Testing the following plugins: {names}".format(names=[p.name for p in plugins])) else: print("Testing all plugins in {root}".format(root=root)) @@ -315,11 +384,26 @@ def run_all(args): results = [(p, run_one(p)) for p in plugins] success = all([t[1] for t in results]) + if update_badges: + push_gather_data(collect_gather_data(results, success), workflow, python_version) + if not success: print("The following tests failed:") for t in filter(lambda t: not t[1], results): print(" - {p.name} ({p.path})".format(p=t[0])) sys.exit(1) + else: + print("All tests passed.") + if __name__ == "__main__": - run_all(sys.argv[1:]) + import argparse + + parser = argparse.ArgumentParser(description='Plugins test script') + parser.add_argument("workflow", type=str, help="Name of the GitHub workflow") + parser.add_argument("python_version", type=str, help="Python version") + parser.add_argument("--update-badges", action='store_true', help="Whether badges data should be updated") + parser.add_argument("plugins", nargs="*", default=[], help="List of plugins") + args = parser.parse_args() + + run_all(args.workflow, args.python_version, args.update_badges, args.plugins) diff --git a/.ci/update_badges.py b/.ci/update_badges.py new file mode 100644 index 000000000..5db917cda --- /dev/null +++ b/.ci/update_badges.py @@ -0,0 +1,168 @@ +import json +import os +import subprocess +from collections import namedtuple +from pathlib import Path, PosixPath +from typing import Generator, List + +Plugin = namedtuple( + "Plugin", + [ + "name", + "path", + "language", + "framework", + "details", + ], +) + +exclude = [ + ".ci", + ".git", + ".github", + "archived", + "lightning", +] + + +def configure_git(): + # Git needs some user and email to be configured in order to work in the context of GitHub Actions. + subprocess.run( + ["git", "config", "--global", "user.email", '"lightningd@github.plugins.repo"'] + ) + subprocess.run(["git", "config", "--global", "user.name", '"lightningd"']) + + +def get_testfiles(p: Plugin) -> List[PosixPath]: + return [ + x + for x in p.path.iterdir() + if (x.is_dir() and x.name == "tests") + or (x.name.startswith("test_") and x.name.endswith(".py")) + ] + + +def has_testfiles(p: Plugin) -> bool: + return len(get_testfiles(p)) > 0 + + +def list_plugins(plugins): + return ", ".join([p.name for p in sorted(plugins)]) + + +def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]: + plugins = list( + [x for x in basedir.iterdir() if x.is_dir() and x.name not in exclude] + ) + pip_pytest = [x for x in plugins if (x / Path("requirements.txt")).exists()] + print(f"Pip plugins: {list_plugins(pip_pytest)}") + + poetry_pytest = [x for x in plugins if (x / Path("pyproject.toml")).exists()] + print(f"Poetry plugins: {list_plugins(poetry_pytest)}") + + other_plugins = [ + x for x in plugins if x not in pip_pytest and x not in poetry_pytest + ] + print(f"Other plugins: {list_plugins(other_plugins)}") + + for p in sorted(pip_pytest): + yield Plugin( + name=p.name, + path=p, + language="python", + framework="pip", + details={ + "requirements": p / Path("requirements.txt"), + "devrequirements": p / Path("requirements-dev.txt"), + }, + ) + + for p in sorted(poetry_pytest): + yield Plugin( + name=p.name, + path=p, + language="python", + framework="poetry", + details={ + "pyproject": p / Path("pyproject.toml"), + }, + ) + + for p in sorted(other_plugins): + yield Plugin( + name=p.name, + path=p, + language="other", + framework="generic", + details={ + "requirements": p / Path("tests/requirements.txt"), + "setup": p / Path("tests/setup.sh"), + }, + ) + + +def update_and_commit_badge(plugin_name, passed, workflow): + json_data = { "schemaVersion": 1, "label": "", "message": " ✔ ", "color": "green" } + if not passed: + json_data.update({"message": "✗", "color": "red"}) + + filename = os.path.join(".badges", f"{plugin_name}_{workflow}.json") + with open(filename, "w") as file: + file.write(json.dumps(json_data)) + + output = subprocess.check_output(["git", "add", "-v", filename]).decode("utf-8") + if output != "": + subprocess.run(["git", "commit", "-m", f'Update {plugin_name} badge to {"passed" if passed else "failed"} ({workflow})']) + return True + return False + + +def push_badges_data(workflow, num_of_python_versions): + print("Pushing badges data...") + configure_git() + subprocess.run(["git", "fetch"]) + subprocess.run(["git", "checkout", "badges"]) + subprocess.run(["git", "pull"]) + + root_path = ( + subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) + .decode("ASCII") + .strip() + ) + plugins = list(enumerate_plugins(Path(root_path))) + + any_changes = False + for plugin in plugins: + results = [] + _dir = f".badges/gather_data/main/{plugin.name}" + if os.path.exists(_dir): + for child in Path(_dir).iterdir(): + result = child.read_text().strip() + results.append(result) + print(f"Results for {child}: {result}") + + passed = False + if ( + len(set(results)) == 1 + and results[0] == "passed" + # and len(results) == num_of_python_versions # TODO: Disabled as gather data for python versions is missing sporadingly. + ): + passed = True + any_changes |= update_and_commit_badge(plugin.name, passed, workflow) + + if any_changes: + subprocess.run(["git", "push", "origin", "badges"]) + print("Done.") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Plugins completion script") + parser.add_argument("workflow", type=str, help="Name of the GitHub workflow") + parser.add_argument( + "num_of_python_versions", type=str, help="Number of Python versions" + ) + args = parser.parse_args() + + push_badges_data(args.workflow, int(args.num_of_python_versions)) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e8d8c8ed4..4a99a6c2d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -82,6 +82,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Run pytest tests + id: pytest_tests run: | export CLN_PATH=${{ github.workspace }}/lightning export COMPAT=${{ matrix.deprecated }} @@ -98,17 +99,32 @@ jobs: # Run the tests: In the case of a 'pull_request' event only the plugins in `plugin_dirs` # are going to be tested; otherwise ('push' event) we test all plugins. - python3 .ci/test.py $(echo "$plugin_dirs") + + update_badges='' + if [[ "${{ github.event_name }}" == 'push' && "${{ github.ref }}" == 'refs/heads/master' ]] || [[ "${{ github.event_name }}" == 'schedule' ]] + then + update_badges='--update-badges' + fi + + python3 .ci/test.py main ${{ matrix.python-version }} $update_badges $(echo "$plugin_dirs") gather: - # A dummy task that depends on the full matrix of tests, and - # signals successful completion. Used for the PR status to pass - # before merging. + # A dummy task that depends on the full matrix of tests, and signals completion. name: CI completion runs-on: ubuntu-22.04 + if: ${{ always() }} needs: - build-and-test steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - name: Complete run: | - echo CI completed successfully + echo "Updating badges data..." + python3 .ci/update_badges.py main 5 # We test for 5 distinct Python versions + echo "CI completed" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8abfde298..0416b275f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -91,17 +91,25 @@ jobs: export CLN_PATH=${{ github.workspace }}/lightning pip3 install --upgrade pip pip3 install --user -U virtualenv pip > /dev/null - python3 .ci/test.py + python3 .ci/test.py nightly --update-badges gather: - # A dummy task that depends on the full matrix of tests, and - # signals successful completion. Used for the PR status to pass - # before merging. + # A dummy task that depends on the full matrix of tests, and signals completion. name: CI completion runs-on: ubuntu-22.04 + if: ${{ always() }} needs: - nightly-build-and-test steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - name: Complete run: | - echo Nightly CI completed successfully + echo "Updating badges data..." + python3 .ci/update_badges.py main 5 # We test for 5 distinct Python versions + echo "CI completed" diff --git a/README.md b/README.md index 25e5e3503..25acf8b70 100644 --- a/README.md +++ b/README.md @@ -2,45 +2,46 @@ Community curated plugins for Core-Lightning. -![Integration Tests (latest)](https://github.com/lightningd/plugins/actions/workflows/main.yml/badge.svg) -![Nightly Integration Tests (master)](https://github.com/lightningd/plugins/actions/workflows/nightly.yml/badge.svg) +[![Integration Tests (latest)](https://github.com/lightningd/plugins/actions/workflows/main.yml/badge.svg)](https://github.com/lightningd/plugins/actions/workflows/main.yml) +[![Nightly Integration Tests (master)](https://github.com/lightningd/plugins/actions/workflows/nightly.yml/badge.svg)](https://github.com/lightningd/plugins/actions/workflows/nightly.yml) ## Available plugins -| Name | Short description | -| ------------------------------------ | ------------------------------------------------------------------------------------------- | -| [backup][backup] | A simple and reliable backup plugin | -| [btcli4j][btcli4j] | A Bitcoin Backend to enable safely the pruning mode, and support also rest APIs. | -| [circular][circular] | A smart rebalancing plugin for Core Lightning routing nodes | -| [clearnet][clearnet] | A plugin that can be used to enforce clearnet connections when possible | -| [cln-ntfy][cln-ntfy] | Core Lightning plugin for sending `ntfy` alerts. | -| [currencyrate][currencyrate] | A plugin to convert other currencies to BTC using web requests | -| [donations][donations] | A simple donations page to accept donations from the web | -| [event-websocket][event-websocket] | Exposes notifications over a Websocket | -| [feeadjuster][feeadjuster] | Dynamic fees to keep your channels more balanced | -| [go-lnmetrics.reporter][reporter] | Collect and report of the lightning node metrics | -| [graphql][graphql] | Exposes the Core-Lightning API over [graphql][graphql-spec] | -| [holdinvoice][holdinvoice] | Holds htlcs for invoices until settle or cancel is called (aka Hodlinvoices) via RPC/GRPC | -| [invoice-queue][invoice-queue] | Listen to lightning invoices from multiple nodes and send to a redis queue for processing | -| [lightning-qt][lightning-qt] | A bitcoin-qt-like GUI for lightningd | -| [listmempoolfunds][listmempoolfunds] | Track unconfirmed wallet deposits | -| [monitor][monitor] | helps you analyze the health of your peers and channels | -| [nloop][nloop] | Generic Lightning Loop for boltz | -| [paythrough][paythrough] | Pay an invoice through a specific channel, regardless of better routes | -| [persistent-channels][pers-chans] | Maintains a number of channels to peers | -| [poncho][poncho] | Turns CLN into a [hosted channels][blip12] provider | -| [pruning][pruning] | This plugin manages pruning of bitcoind such that it can always sync | -| [rebalance][rebalance] | Keeps your channels balanced | -| [reckless][reckless] | An **experimental** plugin manager (search/install plugins) | -| [sauron][sauron] | A Bitcoin backend relying on [Esplora][esplora]'s API | -| [sitzprobe][sitzprobe] | A Lightning Network payment rehearsal utility | -| [sling][sling] | Rebalance your channels with smart rules and built-in background tasks | -| [sparko][sparko] | RPC over HTTP with fine-grained permissions, SSE and spark-wallet support | -| [summars][summars] | Print configurable summary of node, channels and optionally forwards, invoices, payments | -| [trustedcoin][trustedcoin] | Replace your Bitcoin Core with data from public block explorers | -| [watchtower-client][watchtower-client] | Watchtower client for The Eye of Satoshi | -| [webhook][webhook] | Dispatches webhooks based from [event notifications][event-notifications] | -| [zmq][zmq] | Publishes notifications via [ZeroMQ][zmq-home] to configured endpoints | +| Name | Short description | CLN
![GitHub Release](https://img.shields.io/github/v/release/ElementsProject/lightning?label=%20&color=393D47) | CLN
![Static Badge](https://img.shields.io/badge/master-master?color=393D47) | +| ------------------------------------ | ------------------------------------------------------------------------------------------- | :----: | :-----: | +| [backup][backup] | A simple and reliable backup plugin | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fbackup_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fbackup_nightly.json) | +| [btcli4j][btcli4j] | A Bitcoin Backend to enable safely the pruning mode, and support also rest APIs. | | | +| [circular][circular] | A smart rebalancing plugin for Core Lightning routing nodes | | | +| [clearnet][clearnet] | A plugin that can be used to enforce clearnet connections when possible | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fclearnet_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fclearnet_nightly.json) | +| [cln-ntfy][cln-ntfy] | Core Lightning plugin for sending `ntfy` alerts. | | | +| [currencyrate][currencyrate] | A plugin to convert other currencies to BTC using web requests | | | +| [datastore][datastore] | The Datastore Plugin | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fdatastore_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fdatastore_nightly.json) | +| [donations][donations] | A simple donations page to accept donations from the web | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fdonations_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fdonations_nightly.json) | +| [event-websocket][event-websocket] | Exposes notifications over a Websocket | | | +| [feeadjuster][feeadjuster] | Dynamic fees to keep your channels more balanced | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Ffeeadjuster_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Ffeeadjuster_nightly.json) | +| [go-lnmetrics.reporter][reporter] | Collect and report of the lightning node metrics | | | +| [graphql][graphql] | Exposes the Core-Lightning API over [graphql][graphql-spec] | | | +| [holdinvoice][holdinvoice] | Holds htlcs for invoices until settle or cancel is called (aka Hodlinvoices) via RPC/GRPC | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fholdinvoice_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fholdinvoice_nightly.json) | +| [invoice-queue][invoice-queue] | Listen to lightning invoices from multiple nodes and send to a redis queue for processing | | | +| [lightning-qt][lightning-qt] | A bitcoin-qt-like GUI for lightningd | | | +| [listmempoolfunds][listmempoolfunds] | Track unconfirmed wallet deposits | | | +| [monitor][monitor] | helps you analyze the health of your peers and channels | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fmonitor_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fmonitor_nightly.json) | +| [nloop][nloop] | Generic Lightning Loop for boltz | | | +| [paythrough][paythrough] | Pay an invoice through a specific channel, regardless of better routes | | | +| [persistent-channels][pers-chans] | Maintains a number of channels to peers | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fpersistent-channels_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fpersistent-channels_nightly.json) | +| [poncho][poncho] | Turns CLN into a [hosted channels][blip12] provider | | | +| [pruning][pruning] | This plugin manages pruning of bitcoind such that it can always sync | | | +| [rebalance][rebalance] | Keeps your channels balanced | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Frebalance_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Frebalance_nightly.json) | +| [reckless][reckless] | An **experimental** plugin manager (search/install plugins) | | | +| [sauron][sauron] | A Bitcoin backend relying on [Esplora][esplora]'s API | | | +| [sitzprobe][sitzprobe] | A Lightning Network payment rehearsal utility | | | +| [sling][sling] | Rebalance your channels with smart rules and built-in background tasks | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fsling_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fsling_nightly.json) | +| [sparko][sparko] | RPC over HTTP with fine-grained permissions, SSE and spark-wallet support | | | +| [summars][summars] | Print configurable summary of node, channels and optionally forwards, invoices, payments | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fsummars_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fsummars_nightly.json) | +| [trustedcoin][trustedcoin] | Replace your Bitcoin Core with data from public block explorers | | | +| [watchtower-client][watchtower-client] | Watchtower client for The Eye of Satoshi | | | +| [webhook][webhook] | Dispatches webhooks based from [event notifications][event-notifications] | | | +| [zmq][zmq] | Publishes notifications via [ZeroMQ][zmq-home] to configured endpoints | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fzmq_main.json) | ![](https://img.shields.io/endpoint?url=https%3A%2F%2Flightningd.github.io%2Fplugins%2F.badges%2Fzmq_nightly.json) | ## Archived plugins @@ -194,7 +195,7 @@ Python plugins developers must ensure their plugin to work with all Python versi - [Kotlin plugin guideline and example][kotlin-example] by @vincenzopalazzo [autopilot]: https://github.com/lightningd/plugins/tree/master/archived/autopilot -[backup]: https://github.com/lightningd/plugins/tree/master/archived/backup +[backup]: https://github.com/lightningd/plugins/tree/master/backup [blip12]: https://github.com/lightning/blips/blob/42cec1d0f66eb68c840443abb609a5a9acb34f8e/blip-0012.md [btcli4j]: https://github.com/clightning4j/btcli4j [c-api]: https://github.com/ElementsProject/lightning/blob/master/plugins/libplugin.h @@ -205,7 +206,8 @@ Python plugins developers must ensure their plugin to work with all Python versi [cpp-api]: https://github.com/darosior/lightningcpp [csharp-example]: https://github.com/joemphilips/DotNetLightning/tree/master/examples/HelloWorldPlugin [currencyrate]: https://github.com/lightningd/plugins/tree/master/currencyrate -[donations]: https://github.com/lightningd/plugins/tree/master/archived/donations +[datastore]: https://github.com/lightningd/plugins/tree/master/datastore +[donations]: https://github.com/lightningd/plugins/tree/master/donations [drain]: https://github.com/lightningd/plugins/tree/master/archived/drain [esplora]: https://github.com/Blockstream/esplora [event-notifications]: https://lightning.readthedocs.io/PLUGINS.html#event-notifications