mirror of
https://github.com/progval/irctest.git
synced 2025-04-05 14:59:49 +00:00
Compare commits
15 Commits
mysql-subp
...
sopel-scra
Author | SHA1 | Date | |
---|---|---|---|
1d8d78e62a | |||
690aaf24a1 | |||
40385c112b | |||
9d4212504b | |||
cae3aec338 | |||
c1442c4301 | |||
507f5b7426 | |||
dbdadec677 | |||
6290825c64 | |||
f1c9218fbb | |||
6b6017b40c | |||
601f49a9ef | |||
e205cc1531 | |||
8a4f254a21 | |||
81dac6f582 |
17
.github/workflows/test-devel.yml
vendored
17
.github/workflows/test-devel.yml
vendored
@ -5,18 +5,22 @@ jobs:
|
||||
build-anope:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create directories
|
||||
run: cd ~/; mkdir -p .local/ go/
|
||||
- name: Cache Anope
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: 3-${{ runner.os }}-anope-2.0.9
|
||||
key: 3-${{ runner.os }}-anope-devel
|
||||
path: '~/.cache
|
||||
|
||||
${{ github.workspace }}/anope
|
||||
${ github.workspace }/anope
|
||||
|
||||
'
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Checkout Anope
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
@ -24,7 +28,7 @@ jobs:
|
||||
ref: 2.0.9
|
||||
repository: anope/anope
|
||||
- name: Build Anope
|
||||
run: |-
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/anope/
|
||||
cp $GITHUB_WORKSPACE/data/anope/* .
|
||||
CFLAGS=-O0 ./Config -quick
|
||||
@ -67,6 +71,7 @@ jobs:
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/Bahamut/
|
||||
patch src/s_user.c < $GITHUB_WORKSPACE/patches/bahamut_localhost.patch
|
||||
patch src/s_bsd.c < $GITHUB_WORKSPACE/patches/bahamut_mainloop.patch
|
||||
echo "#undef THROTTLE_ENABLE" >> include/config.h
|
||||
libtoolize --force
|
||||
aclocal
|
||||
@ -541,7 +546,7 @@ jobs:
|
||||
repository: ergochat/ergo
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.18.0
|
||||
go-version: ^1.19.0
|
||||
- run: go version
|
||||
- name: Build Ergo
|
||||
run: |
|
||||
|
14
.github/workflows/test-devel_release.yml
vendored
14
.github/workflows/test-devel_release.yml
vendored
@ -5,18 +5,22 @@ jobs:
|
||||
build-anope:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create directories
|
||||
run: cd ~/; mkdir -p .local/ go/
|
||||
- name: Cache Anope
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: 3-${{ runner.os }}-anope-2.0.9
|
||||
key: 3-${{ runner.os }}-anope-devel_release
|
||||
path: '~/.cache
|
||||
|
||||
${{ github.workspace }}/anope
|
||||
${ github.workspace }/anope
|
||||
|
||||
'
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Checkout Anope
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
@ -24,7 +28,7 @@ jobs:
|
||||
ref: 2.0.9
|
||||
repository: anope/anope
|
||||
- name: Build Anope
|
||||
run: |-
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/anope/
|
||||
cp $GITHUB_WORKSPACE/data/anope/* .
|
||||
CFLAGS=-O0 ./Config -quick
|
||||
|
17
.github/workflows/test-stable.yml
vendored
17
.github/workflows/test-stable.yml
vendored
@ -5,18 +5,22 @@ jobs:
|
||||
build-anope:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create directories
|
||||
run: cd ~/; mkdir -p .local/ go/
|
||||
- name: Cache Anope
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: 3-${{ runner.os }}-anope-2.0.9
|
||||
key: 3-${{ runner.os }}-anope-stable
|
||||
path: '~/.cache
|
||||
|
||||
${{ github.workspace }}/anope
|
||||
${ github.workspace }/anope
|
||||
|
||||
'
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Checkout Anope
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
@ -24,7 +28,7 @@ jobs:
|
||||
ref: 2.0.9
|
||||
repository: anope/anope
|
||||
- name: Build Anope
|
||||
run: |-
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/anope/
|
||||
cp $GITHUB_WORKSPACE/data/anope/* .
|
||||
CFLAGS=-O0 ./Config -quick
|
||||
@ -67,6 +71,7 @@ jobs:
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/Bahamut/
|
||||
patch src/s_user.c < $GITHUB_WORKSPACE/patches/bahamut_localhost.patch
|
||||
patch src/s_bsd.c < $GITHUB_WORKSPACE/patches/bahamut_mainloop.patch
|
||||
echo "#undef THROTTLE_ENABLE" >> include/config.h
|
||||
libtoolize --force
|
||||
aclocal
|
||||
@ -617,7 +622,7 @@ jobs:
|
||||
repository: ergochat/ergo
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.18.0
|
||||
go-version: ^1.19.0
|
||||
- run: go version
|
||||
- name: Build Ergo
|
||||
run: |
|
||||
|
@ -13,7 +13,7 @@ repos:
|
||||
- id: isort
|
||||
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.3
|
||||
rev: 5.0.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
||||
|
15
Makefile
15
Makefile
@ -122,7 +122,8 @@ bahamut:
|
||||
$(PYTEST) $(PYTEST_ARGS) \
|
||||
--controller=irctest.controllers.bahamut \
|
||||
-m 'not services' \
|
||||
-n 10 \
|
||||
-n 4 \
|
||||
-vv -s \
|
||||
-k '$(BAHAMUT_SELECTORS)'
|
||||
|
||||
bahamut-atheme:
|
||||
@ -130,7 +131,6 @@ bahamut-atheme:
|
||||
--controller=irctest.controllers.bahamut \
|
||||
--services-controller=irctest.controllers.atheme_services \
|
||||
-m 'services' \
|
||||
-n 10 \
|
||||
-k '$(BAHAMUT_SELECTORS)'
|
||||
|
||||
bahamut-anope:
|
||||
@ -138,7 +138,6 @@ bahamut-anope:
|
||||
--controller=irctest.controllers.bahamut \
|
||||
--services-controller=irctest.controllers.anope_services \
|
||||
-m 'services' \
|
||||
-n 10 \
|
||||
-k '$(BAHAMUT_SELECTORS)'
|
||||
|
||||
charybdis:
|
||||
@ -182,28 +181,28 @@ ircu2:
|
||||
$(PYTEST) $(PYTEST_ARGS) \
|
||||
--controller=irctest.controllers.ircu2 \
|
||||
-m 'not services and not IRCv3' \
|
||||
-n 10 \
|
||||
-n 4 \
|
||||
-k '$(IRCU2_SELECTORS)'
|
||||
|
||||
nefarious:
|
||||
$(PYTEST) $(PYTEST_ARGS) \
|
||||
--controller=irctest.controllers.nefarious \
|
||||
-m 'not services' \
|
||||
-n 10 \
|
||||
-n 4 \
|
||||
-k '$(NEFARIOUS_SELECTORS)'
|
||||
|
||||
snircd:
|
||||
$(PYTEST) $(PYTEST_ARGS) \
|
||||
--controller=irctest.controllers.snircd \
|
||||
-m 'not services and not IRCv3' \
|
||||
-n 10 \
|
||||
-n 4 \
|
||||
-k '$(SNIRCD_SELECTORS)'
|
||||
|
||||
irc2:
|
||||
$(PYTEST) $(PYTEST_ARGS) \
|
||||
--controller=irctest.controllers.irc2 \
|
||||
-m 'not services and not IRCv3' \
|
||||
-n 10 \
|
||||
-n 4 \
|
||||
-k '$(IRC2_SELECTORS)'
|
||||
|
||||
limnoria:
|
||||
@ -226,7 +225,7 @@ ngircd:
|
||||
$(PYTEST) $(PYTEST_ARGS) \
|
||||
--controller irctest.controllers.ngircd \
|
||||
-m 'not services' \
|
||||
-n 10 \
|
||||
-n 4 \
|
||||
-k "$(NGIRCD_SELECTORS)"
|
||||
|
||||
ngircd-anope:
|
||||
|
@ -23,7 +23,6 @@ cd ~
|
||||
git clone https://github.com/ProgVal/irctest.git
|
||||
cd irctest
|
||||
pip3 install --user -r requirements.txt
|
||||
python3 setup.py install --user
|
||||
```
|
||||
|
||||
Add `~/.local/bin/` (and/or `~/go/bin/` for Ergo)
|
||||
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
@ -87,7 +88,7 @@ class DirectoryBasedController(_BaseController):
|
||||
"""Helper for controllers whose software configuration is based on an
|
||||
arbitrary directory."""
|
||||
|
||||
directory: Optional[str]
|
||||
directory: Optional[Path]
|
||||
|
||||
def __init__(self, test_config: TestCaseControllerConfig):
|
||||
super().__init__(test_config)
|
||||
@ -110,22 +111,21 @@ class DirectoryBasedController(_BaseController):
|
||||
"""Open a file in the configuration directory."""
|
||||
assert self.directory
|
||||
if os.sep in name:
|
||||
dir_ = os.path.join(self.directory, os.path.dirname(name))
|
||||
if not os.path.isdir(dir_):
|
||||
os.makedirs(dir_)
|
||||
assert os.path.isdir(dir_)
|
||||
return open(os.path.join(self.directory, name), mode)
|
||||
dir_ = self.directory / os.path.dirname(name)
|
||||
dir_.mkdir(parents=True, exist_ok=True)
|
||||
assert dir_.is_dir()
|
||||
return (self.directory / name).open(mode)
|
||||
|
||||
def create_config(self) -> None:
|
||||
if not self.directory:
|
||||
self.directory = tempfile.mkdtemp()
|
||||
self.directory = Path(tempfile.mkdtemp())
|
||||
|
||||
def gen_ssl(self) -> None:
|
||||
assert self.directory
|
||||
self.csr_path = os.path.join(self.directory, "ssl.csr")
|
||||
self.key_path = os.path.join(self.directory, "ssl.key")
|
||||
self.pem_path = os.path.join(self.directory, "ssl.pem")
|
||||
self.dh_path = os.path.join(self.directory, "dh.pem")
|
||||
self.csr_path = self.directory / "ssl.csr"
|
||||
self.key_path = self.directory / "ssl.key"
|
||||
self.pem_path = self.directory / "ssl.pem"
|
||||
self.dh_path = self.directory / "dh.pem"
|
||||
subprocess.check_output(
|
||||
[
|
||||
self.openssl_bin,
|
||||
@ -222,6 +222,7 @@ class BaseServerController(_BaseController):
|
||||
raise NotImplementedByController("account registration")
|
||||
|
||||
def wait_for_port(self) -> None:
|
||||
started_at = time.time()
|
||||
while not self.port_open:
|
||||
self.check_is_alive()
|
||||
time.sleep(self._port_wait_interval)
|
||||
@ -244,11 +245,16 @@ class BaseServerController(_BaseController):
|
||||
# ircu2 cuts the connection without a message if registration
|
||||
# is not complete.
|
||||
pass
|
||||
except socket.timeout:
|
||||
# irc2 just keeps it open
|
||||
pass
|
||||
|
||||
c.close()
|
||||
self.port_open = True
|
||||
except Exception:
|
||||
continue
|
||||
except ConnectionRefusedError:
|
||||
if time.time() - started_at >= 60:
|
||||
# waited for 60 seconds, giving up
|
||||
raise
|
||||
|
||||
def wait_for_services(self) -> None:
|
||||
assert self.services_controller
|
||||
|
@ -1,4 +1,4 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Type
|
||||
@ -101,14 +101,11 @@ class AnopeController(BaseServicesController, DirectoryBasedController):
|
||||
pass
|
||||
|
||||
assert self.directory
|
||||
services_path = shutil.which("services")
|
||||
assert services_path
|
||||
|
||||
# Config and code need to be in the same directory, *obviously*
|
||||
os.symlink(
|
||||
os.path.join(
|
||||
os.path.dirname(shutil.which("services")), "..", "lib" # type: ignore
|
||||
),
|
||||
os.path.join(self.directory, "lib"),
|
||||
)
|
||||
(self.directory / "lib").symlink_to(Path(services_path).parent.parent / "lib")
|
||||
|
||||
self.proc = subprocess.Popen(
|
||||
[
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import subprocess
|
||||
from typing import Optional, Type
|
||||
|
||||
@ -81,11 +80,11 @@ class AthemeController(BaseServicesController, DirectoryBasedController):
|
||||
"atheme-services",
|
||||
"-n", # don't fork
|
||||
"-c",
|
||||
os.path.join(self.directory, "services.conf"),
|
||||
self.directory / "services.conf",
|
||||
"-l",
|
||||
f"/tmp/services-{server_port}.log",
|
||||
"-p",
|
||||
os.path.join(self.directory, "services.pid"),
|
||||
self.directory / "services.pid",
|
||||
"-D",
|
||||
self.directory,
|
||||
],
|
||||
|
@ -1,4 +1,4 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -80,6 +80,19 @@ oper {{
|
||||
"""
|
||||
|
||||
|
||||
def initialize_entropy(directory: Path) -> None:
|
||||
# https://github.com/DALnet/bahamut/blob/7fc039d403f66a954225c5dc4ad1fe683aedd794/include/dh.h#L35-L38
|
||||
nb_rand_bytes = 512 // 8
|
||||
# https://github.com/DALnet/bahamut/blob/7fc039d403f66a954225c5dc4ad1fe683aedd794/src/dh.c#L186
|
||||
entropy_file_size = nb_rand_bytes * 4
|
||||
|
||||
# Not actually random; but we don't care.
|
||||
entropy = b"\x00" * entropy_file_size
|
||||
|
||||
with (directory / ".ircd.entropy").open("wb") as fd:
|
||||
fd.write(entropy)
|
||||
|
||||
|
||||
class BahamutController(BaseServerController, DirectoryBasedController):
|
||||
software_name = "Bahamut"
|
||||
supported_sasl_mechanisms: Set[str] = set()
|
||||
@ -121,9 +134,14 @@ class BahamutController(BaseServerController, DirectoryBasedController):
|
||||
|
||||
assert self.directory
|
||||
|
||||
# Bahamut reads some bytes from /dev/urandom on startup, which causes
|
||||
# GitHub Actions to sometimes freeze and timeout.
|
||||
# This initializes the entropy file so Bahamut does not need to do it itself.
|
||||
initialize_entropy(self.directory)
|
||||
|
||||
# they are hardcoded... thankfully Bahamut reads them from the CWD.
|
||||
shutil.copy(self.pem_path, os.path.join(self.directory, "ircd.crt"))
|
||||
shutil.copy(self.key_path, os.path.join(self.directory, "ircd.key"))
|
||||
shutil.copy(self.pem_path, self.directory / "ircd.crt")
|
||||
shutil.copy(self.key_path, self.directory / "ircd.key")
|
||||
|
||||
with self.open_file("server.conf") as fd:
|
||||
fd.write(
|
||||
@ -150,7 +168,7 @@ class BahamutController(BaseServerController, DirectoryBasedController):
|
||||
"ircd",
|
||||
"-t", # don't fork
|
||||
"-f",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set
|
||||
@ -88,9 +87,9 @@ class BaseHybridController(BaseServerController, DirectoryBasedController):
|
||||
self.binary_name,
|
||||
"-foreground",
|
||||
"-configfile",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
"-pidfile",
|
||||
os.path.join(self.directory, "server.pid"),
|
||||
self.directory / "server.pid",
|
||||
],
|
||||
# stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
@ -185,21 +185,19 @@ class ErgoController(BaseServerController, DirectoryBasedController):
|
||||
bind_address = "127.0.0.1:%s" % (port,)
|
||||
listener_conf = None # plaintext
|
||||
if ssl:
|
||||
self.key_path = os.path.join(self.directory, "ssl.key")
|
||||
self.pem_path = os.path.join(self.directory, "ssl.pem")
|
||||
self.key_path = self.directory / "ssl.key"
|
||||
self.pem_path = self.directory / "ssl.pem"
|
||||
listener_conf = {"tls": {"cert": self.pem_path, "key": self.key_path}}
|
||||
config["server"]["listeners"][bind_address] = listener_conf # type: ignore
|
||||
|
||||
config["datastore"]["path"] = os.path.join( # type: ignore
|
||||
self.directory, "ircd.db"
|
||||
)
|
||||
config["datastore"]["path"] = str(self.directory / "ircd.db") # type: ignore
|
||||
|
||||
if password is not None:
|
||||
config["server"]["password"] = hash_password(password) # type: ignore
|
||||
|
||||
assert self.proc is None
|
||||
|
||||
self._config_path = os.path.join(self.directory, "server.yml")
|
||||
self._config_path = self.directory / "server.yml"
|
||||
self._config = config
|
||||
self._write_config()
|
||||
subprocess.call(["ergo", "initdb", "--conf", self._config_path, "--quiet"])
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -164,7 +163,7 @@ class InspircdController(BaseServerController, DirectoryBasedController):
|
||||
"inspircd",
|
||||
"--nofork",
|
||||
"--config",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
],
|
||||
stdout=subprocess.DEVNULL,
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -68,7 +67,7 @@ class Irc2Controller(BaseServerController, DirectoryBasedController):
|
||||
self.create_config()
|
||||
password_field = password if password else ""
|
||||
assert self.directory
|
||||
pidfile = os.path.join(self.directory, "ircd.pid")
|
||||
pidfile = self.directory / "ircd.pid"
|
||||
with self.open_file("server.conf") as fd:
|
||||
fd.write(
|
||||
TEMPLATE_CONFIG.format(
|
||||
@ -93,7 +92,7 @@ class Irc2Controller(BaseServerController, DirectoryBasedController):
|
||||
"-p",
|
||||
"on",
|
||||
"-f",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
],
|
||||
# stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -87,7 +86,7 @@ class Ircu2Controller(BaseServerController, DirectoryBasedController):
|
||||
self.create_config()
|
||||
password_field = 'password = "{}";'.format(password) if password else ""
|
||||
assert self.directory
|
||||
pidfile = os.path.join(self.directory, "ircd.pid")
|
||||
pidfile = self.directory / "ircd.pid"
|
||||
with self.open_file("server.conf") as fd:
|
||||
fd.write(
|
||||
TEMPLATE_CONFIG.format(
|
||||
@ -110,7 +109,7 @@ class Ircu2Controller(BaseServerController, DirectoryBasedController):
|
||||
"ircd",
|
||||
"-n", # don't detach
|
||||
"-f",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
"-x",
|
||||
"DEBUG",
|
||||
],
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import subprocess
|
||||
from typing import Optional, Type
|
||||
|
||||
@ -85,9 +84,7 @@ class LimnoriaController(BaseClientController, DirectoryBasedController):
|
||||
)
|
||||
)
|
||||
assert self.directory
|
||||
self.proc = subprocess.Popen(
|
||||
["supybot", os.path.join(self.directory, "bot.conf")]
|
||||
)
|
||||
self.proc = subprocess.Popen(["supybot", self.directory / "bot.conf"])
|
||||
|
||||
|
||||
def get_irctest_controller_class() -> Type[LimnoriaController]:
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -128,7 +127,7 @@ class MammonController(BaseServerController, DirectoryBasedController):
|
||||
"mammond",
|
||||
"--nofork", # '--debug',
|
||||
"--config",
|
||||
os.path.join(self.directory, "server.yml"),
|
||||
self.directory / "server.yml",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -94,7 +93,7 @@ class NgircdController(BaseServerController, DirectoryBasedController):
|
||||
password_field=password_field,
|
||||
key_path=self.key_path,
|
||||
pem_path=self.pem_path,
|
||||
empty_file=os.path.join(self.directory, "empty.txt"),
|
||||
empty_file=self.directory / "empty.txt",
|
||||
)
|
||||
)
|
||||
|
||||
@ -110,7 +109,7 @@ class NgircdController(BaseServerController, DirectoryBasedController):
|
||||
"ngircd",
|
||||
"--nodaemon",
|
||||
"--config",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
],
|
||||
# stdout=subprocess.DEVNULL,
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional, Set, Type
|
||||
@ -86,7 +85,7 @@ class SnircdController(BaseServerController, DirectoryBasedController):
|
||||
self.create_config()
|
||||
password_field = 'password = "{}";'.format(password) if password else ""
|
||||
assert self.directory
|
||||
pidfile = os.path.join(self.directory, "ircd.pid")
|
||||
pidfile = self.directory / "ircd.pid"
|
||||
with self.open_file("server.conf") as fd:
|
||||
fd.write(
|
||||
TEMPLATE_CONFIG.format(
|
||||
@ -109,7 +108,7 @@ class SnircdController(BaseServerController, DirectoryBasedController):
|
||||
"ircd",
|
||||
"-n", # don't detach
|
||||
"-f",
|
||||
os.path.join(self.directory, "server.conf"),
|
||||
self.directory / "server.conf",
|
||||
"-x",
|
||||
"DEBUG",
|
||||
],
|
||||
|
@ -1,4 +1,4 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Optional, TextIO, Type, cast
|
||||
@ -27,7 +27,11 @@ auth_password = {password}
|
||||
|
||||
class SopelController(BaseClientController):
|
||||
software_name = "Sopel"
|
||||
supported_sasl_mechanisms = {"PLAIN"}
|
||||
supported_sasl_mechanisms = {
|
||||
"PLAIN",
|
||||
"SCRAM-SHA-256",
|
||||
"EXTERNAL",
|
||||
}
|
||||
supports_sts = False
|
||||
|
||||
def __init__(self, test_config: TestCaseControllerConfig):
|
||||
@ -38,14 +42,14 @@ class SopelController(BaseClientController):
|
||||
super().kill()
|
||||
if self.filename:
|
||||
try:
|
||||
os.unlink(os.path.join(os.path.expanduser("~/.sopel/"), self.filename))
|
||||
except OSError: # File does not exist
|
||||
(Path("~/.sopel/").expanduser() / self.filename).unlink()
|
||||
except OSError: # File does not exist
|
||||
pass
|
||||
|
||||
def open_file(self, filename: str, mode: str = "a") -> TextIO:
|
||||
dir_path = os.path.expanduser("~/.sopel/")
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
return cast(TextIO, open(os.path.join(dir_path, filename), mode))
|
||||
dir_path = Path("~/.sopel/").expanduser()
|
||||
dir_path.mkdir(parents=True, exist_ok=True)
|
||||
return cast(TextIO, (dir_path / filename).open(mode))
|
||||
|
||||
def create_config(self) -> None:
|
||||
with self.open_file(self.filename):
|
||||
@ -61,6 +65,8 @@ class SopelController(BaseClientController):
|
||||
# Runs a client with the config given as arguments
|
||||
if tls_config is not None:
|
||||
raise NotImplementedByController("TLS configuration")
|
||||
if auth and len(auth.mechanisms) > 1:
|
||||
raise NotImplementedByController("multiple SASL mechanisms")
|
||||
assert self.proc is None
|
||||
self.create_config()
|
||||
with self.open_file(self.filename) as fd:
|
||||
@ -70,7 +76,12 @@ class SopelController(BaseClientController):
|
||||
port=port,
|
||||
username=auth.username if auth else "",
|
||||
password=auth.password if auth else "",
|
||||
auth_method="auth_method = sasl" if auth else "",
|
||||
auth_method=(
|
||||
f"auth_method = sasl\n"
|
||||
f"auth_target = {auth.mechanisms[0].to_string()}"
|
||||
)
|
||||
if auth
|
||||
else "",
|
||||
)
|
||||
)
|
||||
self.proc = subprocess.Popen(["sopel", "--quiet", "-c", self.filename])
|
||||
|
@ -1,11 +1,11 @@
|
||||
import contextlib
|
||||
import fcntl
|
||||
import functools
|
||||
import os
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import textwrap
|
||||
from typing import Optional, Set, Type
|
||||
from typing import Callable, ContextManager, Iterator, Optional, Set, Type
|
||||
|
||||
from irctest.basecontrollers import (
|
||||
BaseServerController,
|
||||
@ -125,6 +125,35 @@ oper "operuser" {{
|
||||
"""
|
||||
|
||||
|
||||
def _filelock(path: Path) -> Callable[[], ContextManager]:
|
||||
"""Alternative to :cls:`multiprocessing.Lock` that works with pytest-xdist"""
|
||||
|
||||
@contextlib.contextmanager
|
||||
def f() -> Iterator[None]:
|
||||
with open(path, "a") as fd:
|
||||
fcntl.flock(fd, fcntl.LOCK_EX)
|
||||
yield
|
||||
|
||||
return f
|
||||
|
||||
|
||||
_UNREALIRCD_BIN = shutil.which("unrealircd")
|
||||
if _UNREALIRCD_BIN:
|
||||
_UNREALIRCD_PREFIX = Path(_UNREALIRCD_BIN).parent.parent
|
||||
|
||||
# Try to keep that lock file specific to this Unrealircd instance
|
||||
_LOCK_PATH = _UNREALIRCD_PREFIX / "irctest-unrealircd-startstop.lock"
|
||||
else:
|
||||
# unrealircd not found; we are probably going to crash later anyway...
|
||||
_LOCK_PATH = Path("/tmp/irctest-unrealircd-startstop.lock")
|
||||
|
||||
_STARTSTOP_LOCK = _filelock(_LOCK_PATH)
|
||||
"""
|
||||
Unreal cleans its tmp/ directory after each run, which prevents
|
||||
multiple processes from starting/stopping at the same time.
|
||||
"""
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def installed_version() -> int:
|
||||
output = subprocess.check_output(["unrealircd", "-v"], universal_newlines=True)
|
||||
@ -170,18 +199,6 @@ class UnrealircdController(BaseServerController, DirectoryBasedController):
|
||||
self.port = port
|
||||
self.hostname = hostname
|
||||
self.create_config()
|
||||
(unused_hostname, unused_port) = find_hostname_and_port()
|
||||
(services_hostname, services_port) = find_hostname_and_port()
|
||||
|
||||
password_field = 'password "{}";'.format(password) if password else ""
|
||||
|
||||
self.gen_ssl()
|
||||
if ssl:
|
||||
(tls_hostname, tls_port) = (hostname, port)
|
||||
(hostname, port) = (unused_hostname, unused_port)
|
||||
else:
|
||||
# Unreal refuses to start without TLS enabled
|
||||
(tls_hostname, tls_port) = (unused_hostname, unused_port)
|
||||
|
||||
if installed_version() >= 6:
|
||||
extras = textwrap.dedent(
|
||||
@ -208,63 +225,60 @@ class UnrealircdController(BaseServerController, DirectoryBasedController):
|
||||
with self.open_file("empty.txt") as fd:
|
||||
fd.write("\n")
|
||||
|
||||
assert self.directory
|
||||
password_field = 'password "{}";'.format(password) if password else ""
|
||||
|
||||
with self.open_file("unrealircd.conf") as fd:
|
||||
fd.write(
|
||||
TEMPLATE_CONFIG.format(
|
||||
hostname=hostname,
|
||||
port=port,
|
||||
services_hostname=services_hostname,
|
||||
services_port=services_port,
|
||||
tls_hostname=tls_hostname,
|
||||
tls_port=tls_port,
|
||||
password_field=password_field,
|
||||
key_path=self.key_path,
|
||||
pem_path=self.pem_path,
|
||||
empty_file=os.path.join(self.directory, "empty.txt"),
|
||||
extras=extras,
|
||||
set_extras=set_extras,
|
||||
with _STARTSTOP_LOCK():
|
||||
(services_hostname, services_port) = find_hostname_and_port()
|
||||
(unused_hostname, unused_port) = find_hostname_and_port()
|
||||
|
||||
self.gen_ssl()
|
||||
if ssl:
|
||||
(tls_hostname, tls_port) = (hostname, port)
|
||||
(hostname, port) = (unused_hostname, unused_port)
|
||||
else:
|
||||
# Unreal refuses to start without TLS enabled
|
||||
(tls_hostname, tls_port) = (unused_hostname, unused_port)
|
||||
|
||||
assert self.directory
|
||||
|
||||
with self.open_file("unrealircd.conf") as fd:
|
||||
fd.write(
|
||||
TEMPLATE_CONFIG.format(
|
||||
hostname=hostname,
|
||||
port=port,
|
||||
services_hostname=services_hostname,
|
||||
services_port=services_port,
|
||||
tls_hostname=tls_hostname,
|
||||
tls_port=tls_port,
|
||||
password_field=password_field,
|
||||
key_path=self.key_path,
|
||||
pem_path=self.pem_path,
|
||||
empty_file=self.directory / "empty.txt",
|
||||
extras=extras,
|
||||
set_extras=set_extras,
|
||||
)
|
||||
)
|
||||
|
||||
if faketime and shutil.which("faketime"):
|
||||
faketime_cmd = ["faketime", "-f", faketime]
|
||||
self.faketime_enabled = True
|
||||
else:
|
||||
faketime_cmd = []
|
||||
|
||||
self.proc = subprocess.Popen(
|
||||
[
|
||||
*faketime_cmd,
|
||||
"unrealircd",
|
||||
"-t",
|
||||
"-F", # BOOT_NOFORK
|
||||
"-f",
|
||||
self.directory / "unrealircd.conf",
|
||||
],
|
||||
# stdout=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
proot_cmd = []
|
||||
self.using_proot = False
|
||||
if shutil.which("proot"):
|
||||
unrealircd_path = shutil.which("unrealircd")
|
||||
if unrealircd_path:
|
||||
unrealircd_prefix = pathlib.Path(unrealircd_path).parents[1]
|
||||
tmpdir = os.path.join(self.directory, "tmp")
|
||||
os.mkdir(tmpdir)
|
||||
# Unreal cleans its tmp/ directory after each run, which prevents
|
||||
# multiple processes from running at the same time.
|
||||
# Using PRoot, we can isolate them, with a tmp/ directory for each
|
||||
# process, so they don't interfere with each other, allowing use of
|
||||
# the -n option (of pytest-xdist) to speed-up tests
|
||||
proot_cmd = ["proot", "-b", f"{tmpdir}:{unrealircd_prefix}/tmp"]
|
||||
self.using_proot = True
|
||||
|
||||
if faketime and shutil.which("faketime"):
|
||||
faketime_cmd = ["faketime", "-f", faketime]
|
||||
self.faketime_enabled = True
|
||||
else:
|
||||
faketime_cmd = []
|
||||
|
||||
self.proc = subprocess.Popen(
|
||||
[
|
||||
*proot_cmd,
|
||||
*faketime_cmd,
|
||||
"unrealircd",
|
||||
"-t",
|
||||
"-F", # BOOT_NOFORK
|
||||
"-f",
|
||||
os.path.join(self.directory, "unrealircd.conf"),
|
||||
],
|
||||
# stdout=subprocess.DEVNULL,
|
||||
)
|
||||
self.wait_for_port()
|
||||
|
||||
if run_services:
|
||||
self.wait_for_port()
|
||||
self.services_controller = self.services_controller_class(
|
||||
self.test_config, self
|
||||
)
|
||||
@ -274,17 +288,13 @@ class UnrealircdController(BaseServerController, DirectoryBasedController):
|
||||
server_port=services_port,
|
||||
)
|
||||
|
||||
def kill(self) -> None:
|
||||
if self.using_proot:
|
||||
# Kill grandchild process, instead of killing proot, which takes more
|
||||
# time (and does not seem to always work)
|
||||
assert self.proc is not None
|
||||
output = subprocess.check_output(
|
||||
["ps", "-opid", "--no-headers", "--ppid", str(self.proc.pid)]
|
||||
)
|
||||
(grandchild_pid,) = [int(line) for line in output.decode().split()]
|
||||
os.kill(grandchild_pid, signal.SIGKILL)
|
||||
super().kill()
|
||||
def kill_proc(self) -> None:
|
||||
assert self.proc
|
||||
|
||||
with _STARTSTOP_LOCK():
|
||||
self.proc.kill()
|
||||
self.proc.wait(5) # wait for it to actually die
|
||||
self.proc = None
|
||||
|
||||
|
||||
def get_irctest_controller_class() -> Type[UnrealircdController]:
|
||||
|
@ -4,7 +4,13 @@ AWAY command (`RFC 2812 <https://datatracker.ietf.org/doc/html/rfc2812#section-4
|
||||
"""
|
||||
|
||||
from irctest import cases
|
||||
from irctest.numerics import RPL_AWAY, RPL_NOWAWAY, RPL_UNAWAY, RPL_USERHOST
|
||||
from irctest.numerics import (
|
||||
RPL_AWAY,
|
||||
RPL_NOWAWAY,
|
||||
RPL_UNAWAY,
|
||||
RPL_USERHOST,
|
||||
RPL_WHOISUSER,
|
||||
)
|
||||
from irctest.patma import StrRe
|
||||
|
||||
|
||||
@ -139,3 +145,33 @@ class AwayTestCase(cases.BaseServerTestCase):
|
||||
self.assertMessageMatch(
|
||||
self.getMessage(2), command=RPL_USERHOST, params=["qux", StrRe(r"bar=-.*")]
|
||||
)
|
||||
|
||||
@cases.mark_specifications("Modern")
|
||||
def testAwayEmptyMessage(self):
|
||||
"""
|
||||
"If [AWAY] is sent with a nonempty parameter (the 'away message')
|
||||
then the user is set to be away. If this command is sent with no
|
||||
parameters, or with the empty string as the parameter, the user is no
|
||||
longer away."
|
||||
-- https://modern.ircdocs.horse/#away-message
|
||||
"""
|
||||
self.connectClient("bar", name="bar")
|
||||
self.connectClient("qux", name="qux")
|
||||
|
||||
self.sendLine("bar", "AWAY :I'm not here right now")
|
||||
replies = self.getMessages("bar")
|
||||
self.assertIn(RPL_NOWAWAY, [msg.command for msg in replies])
|
||||
self.sendLine("qux", "WHOIS bar")
|
||||
replies = self.getMessages("qux")
|
||||
self.assertIn(RPL_WHOISUSER, [msg.command for msg in replies])
|
||||
self.assertIn(RPL_AWAY, [msg.command for msg in replies])
|
||||
|
||||
# empty final parameter to AWAY is treated the same as no parameter,
|
||||
# i.e., the client is considered to be no longer away
|
||||
self.sendLine("bar", "AWAY :")
|
||||
replies = self.getMessages("bar")
|
||||
self.assertIn(RPL_UNAWAY, [msg.command for msg in replies])
|
||||
self.sendLine("qux", "WHOIS bar")
|
||||
replies = self.getMessages("qux")
|
||||
self.assertIn(RPL_WHOISUSER, [msg.command for msg in replies])
|
||||
self.assertNotIn(RPL_AWAY, [msg.command for msg in replies])
|
||||
|
@ -2,10 +2,13 @@
|
||||
Regression tests for bugs in `Ergo <https://ergo.chat/>`_.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from irctest import cases, runner
|
||||
from irctest.numerics import ERR_ERRONEUSNICKNAME, ERR_NICKNAMEINUSE, RPL_WELCOME
|
||||
from irctest.numerics import (
|
||||
ERR_ERRONEUSNICKNAME,
|
||||
ERR_NICKNAMEINUSE,
|
||||
RPL_HELLO,
|
||||
RPL_WELCOME,
|
||||
)
|
||||
from irctest.patma import ANYDICT
|
||||
|
||||
|
||||
@ -111,8 +114,7 @@ class RegressionsTestCase(cases.BaseServerTestCase):
|
||||
self.sendLine(1, "NICK *")
|
||||
self.sendLine(1, "USER u s e r")
|
||||
replies = {"NOTICE"}
|
||||
time.sleep(2) # give time to slow servers, like irc2 to reply
|
||||
while replies == {"NOTICE"}:
|
||||
while replies <= {"NOTICE", RPL_HELLO}:
|
||||
replies = set(msg.command for msg in self.getMessages(1, synchronize=False))
|
||||
self.assertIn(ERR_ERRONEUSNICKNAME, replies)
|
||||
self.assertNotIn(RPL_WELCOME, replies)
|
||||
|
48
irctest/server_tests/time.py
Normal file
48
irctest/server_tests/time.py
Normal file
@ -0,0 +1,48 @@
|
||||
import math
|
||||
import time
|
||||
|
||||
from irctest import cases
|
||||
from irctest.numerics import RPL_TIME
|
||||
from irctest.patma import ANYSTR, StrRe
|
||||
|
||||
|
||||
class TimeTestCase(cases.BaseServerTestCase):
|
||||
def testTime(self):
|
||||
self.connectClient("user")
|
||||
|
||||
time_before = math.floor(time.time())
|
||||
self.sendLine(1, "TIME")
|
||||
|
||||
msg = self.getMessage(1)
|
||||
|
||||
time_after = math.ceil(time.time())
|
||||
|
||||
if len(msg.params) == 5:
|
||||
# ircu2, snircd
|
||||
self.assertMessageMatch(
|
||||
msg,
|
||||
command=RPL_TIME,
|
||||
params=["user", "My.Little.Server", StrRe("[0-9]+"), "0", ANYSTR],
|
||||
)
|
||||
self.assertIn(
|
||||
int(msg.params[2]),
|
||||
range(time_before, time_after + 1),
|
||||
"Timestamp not in expected range",
|
||||
)
|
||||
elif len(msg.params) == 4:
|
||||
# bahamut
|
||||
self.assertMessageMatch(
|
||||
msg,
|
||||
command=RPL_TIME,
|
||||
params=["user", "My.Little.Server", StrRe("[0-9]+"), ANYSTR],
|
||||
)
|
||||
self.assertIn(
|
||||
int(msg.params[2]),
|
||||
range(time_before, time_after + 1),
|
||||
"Timestamp not in expected range",
|
||||
)
|
||||
else:
|
||||
# Common case
|
||||
self.assertMessageMatch(
|
||||
msg, command=RPL_TIME, params=["user", "My.Little.Server", ANYSTR]
|
||||
)
|
@ -503,3 +503,34 @@ class WhoServicesTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
||||
command=RPL_ENDOFWHO,
|
||||
params=["otherNick", InsensitiveStr("coolNick"), ANYSTR],
|
||||
)
|
||||
|
||||
|
||||
class WhoInvisibleTestCase(cases.BaseServerTestCase):
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoInvisible(self):
|
||||
if self.controller.software_name == "Bahamut":
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self.connectClient("evan", name="evan")
|
||||
self.sendLine("evan", "MODE evan +i")
|
||||
self.getMessages("evan")
|
||||
|
||||
self.connectClient("shivaram", name="shivaram")
|
||||
self.getMessages("shivaram")
|
||||
self.sendLine("shivaram", "WHO eva*")
|
||||
reply_cmds = {msg.command for msg in self.getMessages("shivaram")}
|
||||
self.assertEqual(reply_cmds, {RPL_ENDOFWHO})
|
||||
|
||||
# invisibility should not be respected for plain nicknames, only for masks:
|
||||
self.sendLine("shivaram", "WHO evan")
|
||||
replies = self.getMessages("shivaram")
|
||||
reply_cmds = {msg.command for msg in replies}
|
||||
self.assertEqual(reply_cmds, {RPL_WHOREPLY, RPL_ENDOFWHO})
|
||||
|
||||
# invisibility should not be respected if the users share a channel
|
||||
self.joinChannel("evan", "#test")
|
||||
self.joinChannel("shivaram", "#test")
|
||||
self.sendLine("shivaram", "WHO eva*")
|
||||
replies = self.getMessages("shivaram")
|
||||
reply_cmds = {msg.command for msg in replies}
|
||||
self.assertEqual(reply_cmds, {RPL_WHOREPLY, RPL_ENDOFWHO})
|
||||
|
@ -144,11 +144,7 @@ def get_test_job(*, config, test_config, test_id, version_flavor, jobs):
|
||||
downloads = []
|
||||
install_steps = []
|
||||
for software_id in test_config.get("software", []):
|
||||
if software_id == "anope":
|
||||
# TODO: don't hardcode anope here
|
||||
software_config = {"separate_build_job": True}
|
||||
else:
|
||||
software_config = config["software"][software_id]
|
||||
software_config = config["software"][software_id]
|
||||
|
||||
env += test_config.get("env", {}).get(version_flavor.value, "") + " "
|
||||
if "prefix" in software_config:
|
||||
@ -245,47 +241,6 @@ def get_test_job(*, config, test_config, test_id, version_flavor, jobs):
|
||||
}
|
||||
|
||||
|
||||
def get_build_job_anope():
|
||||
return {
|
||||
"runs-on": "ubuntu-latest",
|
||||
"steps": [
|
||||
{"uses": "actions/checkout@v2"},
|
||||
{
|
||||
"name": "Create directories",
|
||||
"run": "cd ~/; mkdir -p .local/ go/",
|
||||
},
|
||||
{
|
||||
"name": "Cache Anope",
|
||||
"uses": "actions/cache@v2",
|
||||
"with": {
|
||||
"path": "~/.cache\n${{ github.workspace }}/anope\n",
|
||||
"key": "3-${{ runner.os }}-anope-2.0.9",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Checkout Anope",
|
||||
"uses": "actions/checkout@v2",
|
||||
"with": {
|
||||
"repository": "anope/anope",
|
||||
"ref": "2.0.9",
|
||||
"path": "anope",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "Build Anope",
|
||||
"run": script(
|
||||
"cd $GITHUB_WORKSPACE/anope/",
|
||||
"cp $GITHUB_WORKSPACE/data/anope/* .",
|
||||
"CFLAGS=-O0 ./Config -quick",
|
||||
"make -C build -j 4",
|
||||
"make -C build install",
|
||||
),
|
||||
},
|
||||
*upload_steps("anope"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def upload_steps(software_id):
|
||||
"""Make a tarball (to preserve permissions) and upload"""
|
||||
return [
|
||||
@ -326,7 +281,6 @@ def generate_workflow(config: dict, version_flavor: VersionFlavor):
|
||||
}
|
||||
|
||||
jobs = {}
|
||||
jobs["build-anope"] = get_build_job_anope()
|
||||
|
||||
for software_id in config["software"]:
|
||||
software_config = config["software"][software_id]
|
||||
|
15
patches/bahamut_mainloop.patch
Normal file
15
patches/bahamut_mainloop.patch
Normal file
@ -0,0 +1,15 @@
|
||||
Lower Bahamut's delay between processing incoming commands
|
||||
|
||||
diff --git a/src/s_bsd.c b/src/s_bsd.c
|
||||
index fcc1d02..951fd8c 100644
|
||||
--- a/src/s_bsd.c
|
||||
+++ b/src/s_bsd.c
|
||||
@@ -1458,7 +1458,7 @@ int do_client_queue(aClient *cptr)
|
||||
int dolen = 0, done;
|
||||
|
||||
while (SBufLength(&cptr->recvQ) && !NoNewLine(cptr) &&
|
||||
- ((cptr->status < STAT_UNKNOWN) || (cptr->since - timeofday < 10) ||
|
||||
+ ((cptr->status < STAT_UNKNOWN) || (cptr->since - timeofday < 20) ||
|
||||
IsNegoServer(cptr)))
|
||||
{
|
||||
/* If it's become registered as a server, just parse the whole block */
|
@ -105,6 +105,7 @@ software:
|
||||
build_script: |
|
||||
cd $GITHUB_WORKSPACE/Bahamut/
|
||||
patch src/s_user.c < $GITHUB_WORKSPACE/patches/bahamut_localhost.patch
|
||||
patch src/s_bsd.c < $GITHUB_WORKSPACE/patches/bahamut_mainloop.patch
|
||||
echo "#undef THROTTLE_ENABLE" >> include/config.h
|
||||
libtoolize --force
|
||||
aclocal
|
||||
@ -130,7 +131,7 @@ software:
|
||||
pre_deps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18.0'
|
||||
go-version: '^1.19.0'
|
||||
- run: go version
|
||||
separate_build_job: false
|
||||
build_script: |
|
||||
@ -300,6 +301,28 @@ software:
|
||||
separate_build_job: true
|
||||
build_script: *unrealircd_build_script
|
||||
|
||||
|
||||
#############################
|
||||
# Services:
|
||||
anope:
|
||||
name: Anope
|
||||
repository: anope/anope
|
||||
separate_build_job: true
|
||||
path: anope
|
||||
refs:
|
||||
stable: "2.0.9"
|
||||
release: "2.0.9"
|
||||
devel: "2.0.9"
|
||||
devel_release: "2.0.9"
|
||||
build_script: |
|
||||
cd $GITHUB_WORKSPACE/anope/
|
||||
cp $GITHUB_WORKSPACE/data/anope/* .
|
||||
CFLAGS=-O0 ./Config -quick
|
||||
make -C build -j 4
|
||||
make -C build install
|
||||
|
||||
|
||||
|
||||
#############################
|
||||
# Clients:
|
||||
|
||||
|
Reference in New Issue
Block a user