feat: deploy and configure forgejo instance for testing

This commit is contained in:
Aravinth Manivannan 2023-09-17 01:59:08 +05:30
parent 61aefb8e56
commit 685e6f44f7
Signed by: realaravinth
GPG key ID: F8F50389936984FF
10 changed files with 639 additions and 0 deletions

9
Makefile Normal file
View file

@ -0,0 +1,9 @@
env.up:
docker-compose -f ./docker-compose-dev-deps.yml up -d
env.down:
docker-compose -f ./docker-compose-dev-deps.yml down --remove-orphans -v
test:
./integration/tests.sh
pnpm run test

View file

@ -0,0 +1,15 @@
version: "3"
services:
forgejo:
image: codeberg.org/forgejo/forgejo:1.18.0-1
container_name: hostea-dash-forgejo
network_mode: host
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro

0
integration/__init__.py Normal file
View file

13
integration/__main__.py Normal file
View file

@ -0,0 +1,13 @@
import argparse
from .cli import Cli
def admin(args):
print(args)
if __name__ == "__main__":
cli = Cli()
opts = cli.parse()
opts.func(opts, c=cli.c)

35
integration/ci.sh Executable file
View file

@ -0,0 +1,35 @@
#!/bin/bash
set -Exeuo pipefail
source integration/lib.sh
init() {
# if is_ci
# then
# echo "[*] CI environment detected"
# else
docker_compose_up
setup_env
sleep 5
# wait_for_env
# fi
forgejo_root
init_users_repo
fleet_repo_init
}
teardown() {
if ! is_ci
then
docker_compose_down
teardown_env
sed -i /localhost.*/d ~/.ssh/known_hosts
fi
}
new_fleet_repo() {
new_fleet_repo_init $2
}
$1 $@

155
integration/cli.py Normal file
View file

@ -0,0 +1,155 @@
import argparse
from requests import Session
def forgejo_from_args(args, c: Session):
from .forgejo import Forgejo
return Forgejo(
host=args.host,
username=args.username,
password=args.password,
email=args.email,
c=c,
)
class Forgejo:
def __init__(self, parser, c: Session):
self.c = c
self.parser = parser
self.subparser = self.parser.add_subparsers()
self.install()
self.register()
self.login()
self.create_repository()
self.create_issue()
def __add_credentials_parser(self, parser):
group = parser.add_argument_group("credentials", "User credentials")
group.add_argument("username", type=str, help="Forgejo user's username")
group.add_argument("password", type=str, help="Forgejo user's password")
group.add_argument("email", type=str, help="Forgejo user's email")
group.add_argument("host", type=str, help="URI at which Forgejo is running")
def install(self):
def run(args, c: Session):
forgejo = forgejo_from_args(args, c=c)
forgejo.install()
self.install_parser = self.subparser.add_parser(
name="install", description="Install Forgejo", help="Install Forgejo"
)
self.__add_credentials_parser(self.install_parser)
self.install_parser.set_defaults(func=run)
def register(self):
def run(args, c: Session):
forgejo = forgejo_from_args(args, c=c)
forgejo.register()
self.register_parser = self.subparser.add_parser(
name="register",
description="Forgejo user registration",
help="Register a user on Forgejo",
)
self.__add_credentials_parser(self.register_parser)
self.register_parser.set_defaults(func=run)
def login(self):
def run(args, c: Session):
forgejo = forgejo_from_args(args, c=c)
forgejo.login()
self.login_parser = self.subparser.add_parser(
name="login", description="Forgejo user login", help="Login on Forgejo"
)
self.__add_credentials_parser(self.login_parser)
self.login_parser.set_defaults(func=run)
def create_repository(self):
def run(args, c: Session):
forgejo = forgejo_from_args(args, c=c)
forgejo.login()
forgejo.create_repository(name=args.repo_name)
self.create_repository_parser = self.subparser.add_parser(
name="create_repo",
description="Create repository on Forgejo",
help="Create repository on Forgejo",
)
self.__add_credentials_parser(self.create_repository_parser)
self.create_repository_parser.set_defaults(func=run)
self.create_repository_parser.add_argument(
"repo_name", type=str, help="Name of the repository to be created"
)
def create_issue(self):
def run(args, c: Session):
forgejo = forgejo_from_args(args, c=c)
forgejo.login()
forgejo.create_issue(owner=args.owner, repo=args.repo)
self.create_issue_parser = self.subparser.add_parser(
name="create_issue",
description="Create issue on a repository owned by someone on Forgejo",
help="Create issue on Forgejo",
)
self.__add_credentials_parser(self.create_issue_parser)
self.create_issue_parser.set_defaults(func=run)
self.create_issue_parser.add_argument(
"owner", type=str, help="Owner of the repo"
)
self.create_issue_parser.add_argument(
"repo", type=str, help="Name of the repository"
)
class Cli:
def __init__(self):
c = Session()
self.c = c
self.parser = argparse.ArgumentParser(
description="Install and Bootstrap Forgejo and Hostea Dashboard"
)
self.subparser = self.parser.add_subparsers()
self.check_env()
self.forgejo()
def __add_credentials_parser(self, parser):
group = parser.add_argument_group("credentials", "User credentials")
group.add_argument("username", type=str, help="Forgejo user's username")
group.add_argument("password", type=str, help="Forgejo user's password")
group.add_argument("email", type=str, help="Forgejo user's email")
def check_env(self):
def run(args, c: Session):
from .forgejo import Forgejo
Forgejo.check_online(host=args.forgejo_host)
self.check_env_parser = self.subparser.add_parser(
name="check_env",
description="Check and block until environment is ready",
help="Check and block until environment is ready",
)
self.check_env_parser.add_argument(
"forgejo_host", type=str, help="URI at which Forgejo is running"
)
self.check_env_parser.set_defaults(func=run)
def forgejo(self):
self.forgejo = self.subparser.add_parser(
name="forgejo",
description="Forgejo",
help="Forgejo-related functionality",
)
Forgejo(parser=self.forgejo, c=self.c)
def parse(self):
return self.parser.parse_args()

38
integration/csrf.py Normal file
View file

@ -0,0 +1,38 @@
from html.parser import HTMLParser
class ParseCSRF(HTMLParser):
token: str = None
def __init__(self, name):
HTMLParser.__init__(self)
self.name = name
# @classmethod
# def dashboard_parser(cls) -> "ParseCSRF":
# return cls(name="csrfmiddlewaretoken")
#
# @classmethod
# def forgejo_parser(cls) -> "ParseCSRF":
# return cls(name="_csrf")
#
def handle_starttag(self, tag: str, attrs: (str, str)):
if self.token:
return
if tag != "input":
return
token = None
for index, (k, v) in enumerate(attrs):
if k == "value":
token = v
if all([k == "name", v == self.name]):
if token:
self.token = token
return
for inner_index, (nk, nv) in enumerate(attrs, start=index):
if nk == "value":
self.token = nv
return

244
integration/forgejo.py Executable file
View file

@ -0,0 +1,244 @@
import os
import random
from urllib.parse import urlunparse, urlparse
from html.parser import HTMLParser
from time import sleep
from requests import Session
from requests.auth import HTTPBasicAuth
import requests
from .csrf import ParseCSRF
# FORGEJO_USER = "root"
# FORGEJO_EMAIL = "root@example.com"
# FORGEJO_PASSWORD = "foobarpassword"
# HOST = "http://localhost:8080"
#
# REPOS = []
class Forgejo:
def __init__(self, host: str, username: str, password: str, email: str, c: Session):
self.host = host
self.username = username
self.password = password
self.email = email
self.c = c
self.__csrf_key = "_csrf"
self.__logged_in = False
def get_uri(self, path: str):
parsed = urlparse(self.host)
return urlunparse((parsed.scheme, parsed.netloc, path, "", "", ""))
def get_api_uri(self, path: str):
parsed = urlparse(self.host)
return urlunparse(
(
parsed.scheme,
f"{self.username}:{self.password}@{parsed.netloc}",
path,
"",
"",
"",
)
)
@staticmethod
def check_online(host: str):
"""
Check if Forgejo instance is online
"""
count = 0
parsed = urlparse(host)
url = urlunparse((parsed.scheme, parsed.netloc, "api/v1/nodeinfo", "", "", ""))
while True:
try:
res = requests.get(url, allow_redirects=False)
if any([res.status_code == 302, res.status_code == 200]):
break
except:
sleep(2)
print(f"Retrying {count} time")
count += 1
continue
def install(self):
"""
Install Forgejo, first form that a user sees when a new instance is
deployed
"""
# cwd = os.environ.get("PWD")
# user = os.environ.get("USER")
payload = {
"db_type": "sqlite3",
"db_host": "localhost:3306",
"db_user": "root",
"db_passwd": "",
"db_name": "forgejo",
"ssl_mode": "disable",
"db_schema": "",
"charset": "utf8",
"db_path": "/data/gitea/gitea.db",
"app_name": "Forgejo:+Beyond+Coding+We+Forge",
"repo_root_path": "/data/git/repositories",
"lfs_root_path": "/data/git/lfs",
"run_user": "git",
"domain": "localhost",
"ssh_port": "22",
"http_port": "3000",
"app_url": "http://localhost:3000/",
"log_root_path": "/data/gitea/log",
"smtp_host": "",
"smtp_from": "",
"smtp_user": "",
"smtp_passwd": "",
"enable_federated_avatar": "on",
"enable_open_id_sign_in": "on",
"enable_open_id_sign_up": "on",
"default_allow_create_organization": "on",
"default_enable_timetracking": "on",
"no_reply_address": "noreply.localhost",
"password_algorithm": "pbkdf2",
"admin_name": "",
"admin_passwd": "",
"admin_confirm_passwd": "",
"admin_email": "",
}
resp = self.c.post(self.get_uri(""), data=payload)
sleep(10)
def get_csrf_token(self, url: str) -> str:
"""
Get CSRF token at a URI
"""
resp = self.c.get(url, allow_redirects=False)
if resp.status_code != 200 and resp.status_code != 302:
print(resp.status_code, resp.text)
raise Exception(f"Can't get csrf token: {resp.status_code}")
parser = ParseCSRF(name=self.__csrf_key)
parser.feed(resp.text)
csrf = parser.token
return csrf
def register(self):
"""
Register User
"""
url = self.get_uri("/user/sign_up")
csrf = self.get_csrf_token(url)
payload = {
"_csrf": csrf,
"user_name": self.username,
"password": self.password,
"retype": self.password,
"email": self.email,
}
self.c.post(url, data=payload, allow_redirects=False)
def login(self):
"""
Login, must be called at least once before performing authenticated
operations
"""
if self.__logged_in:
return
url = self.get_uri("/user/login")
csrf = self.get_csrf_token(url)
payload = {
"_csrf": csrf,
"user_name": self.username,
"password": self.password,
"remember": "on",
}
resp = self.c.post(url, data=payload, allow_redirects=False)
if any(
[resp.status_code == 302, resp.status_code == 200, resp.status_code == 303]
):
print("User logged in")
self.__logged_in = True
return
raise Exception(
f"[ERROR] Authentication failed. status code {resp.status_code}"
)
def create_repository(self, name: str):
"""
Create repository
"""
self.login()
def get_repository_payload(csrf: str, name: str, user_id: str):
data = {
"_csrf": csrf,
"uid": user_id,
"repo_name": name,
"description": f"this repository is named {name}",
"repo_template": "",
"issue_labels": "",
"gitignores": "",
"license": "",
"readme": "Default",
"default_branch": "master",
"trust_model": "default",
}
return data
url = self.get_uri("/repo/create")
user_id = self.c.get(self.get_api_uri("/api/v1/user")).json()["id"]
csrf = self.get_csrf_token(url)
data = get_repository_payload(csrf, name, user_id=user_id)
resp = self.c.post(url, data=data, allow_redirects=False)
print(f"Created repository {name}")
if (
resp.status_code != 302
and resp.status_code != 200
and resp.status_code != 303
):
raise Exception(
f"Error while creating repository: {name} {resp.status_code}"
)
def create_issue(self, owner: str, repo: str):
"""
Create issue
"""
self.login()
def create_issue_payload(csrf: str, title: str, body: str):
data = {
"_csrf": csrf,
"title": title,
"content": body,
"search": "",
"label_ids": "",
"milestone_id": "",
"project_id": "",
"assignee_ids": "",
"redirect_after_creation": "",
}
return data
url = self.get_uri(f"/{owner}/{repo}/issues/new")
csrf = self.get_csrf_token(url)
data = create_issue_payload(csrf, "my issue", "my body")
resp = self.c.post(url, data=data, allow_redirects=False)
print(f"Created issue")
if (
resp.status_code != 302
and resp.status_code != 200
and resp.status_code != 303
):
raise Exception(
f"Error while creating issue: {resp.status_code}"
)

113
integration/lib.sh Executable file
View file

@ -0,0 +1,113 @@
is_ci(){
if [ -z ${CI+x} ];
then
return 1
else
return 0
fi
}
if is_ci
then
FORGEJO_URL="http://forgejo:3000"
FORGEJO_SSH_URL="ssh://git@forgejo:22"
else
FORGEJO_URL="http://localhost:3000"
FORGEJO_SSH_URL="ssh://git@localhost:22"
fi
readonly FORGEJO_ROOT_USERNAME=root
readonly FORGEJO_ROOT_EMAIL="$FORGEJO_ROOT_USERNAME@example.org"
readonly FORGEJO_ROOT_PASSOWRD=supercomplicatedpassword
readonly FORGEJO_USER1_USERNAME=owner_user
readonly FORGEJO_USER1_PASSWORD=supercomplicatedpassword
readonly FORGEJO_USER1_EMAIL="$FORGEJO_USER1_USERNAME@example.org"
readonly FORGEJO_USER1_SUPPORT_REPO="support"
readonly FORGEJO_TESTUSER_USERNAME=test_user
readonly FORGEJO_TESTUSER_PASSWORD=supercomplicatedpassword
readonly FORGEJO_TESTUSER_EMAIL="$FORGEJO_TESTUSER_USERNAME@example.org"
wait_for_env() {
python -m integration \
check_env $FORGEJO_URL
}
# register root user on Forgejo to simulate Hoste admin and integrate SSO
forgejo_root(){
python -m integration \
forgejo install \
$FORGEJO_ROOT_USERNAME $FORGEJO_ROOT_PASSOWRD \
$FORGEJO_ROOT_EMAIL \
$FORGEJO_URL
python -m integration \
forgejo register \
$FORGEJO_ROOT_USERNAME $FORGEJO_ROOT_PASSOWRD \
$FORGEJO_ROOT_EMAIL \
$FORGEJO_URL
python -m integration \
forgejo login \
$FORGEJO_ROOT_USERNAME $FORGEJO_ROOT_PASSOWRD \
$FORGEJO_ROOT_EMAIL \
$FORGEJO_URL
}
# register user "Hostea" on Forgejo and create support repository
init_users_repo() {
python -m integration \
forgejo register \
$FORGEJO_USER1_USERNAME $FORGEJO_USER1_PASSWORD \
$FORGEJO_USER1_EMAIL \
$FORGEJO_URL
python -m integration \
forgejo login \
$FORGEJO_USER1_USERNAME $FORGEJO_USER1_PASSWORD \
$FORGEJO_USER1_EMAIL \
$FORGEJO_URL
python -m integration \
forgejo create_repo \
$FORGEJO_USER1_USERNAME $FORGEJO_USER1_PASSWORD \
$FORGEJO_USER1_EMAIL \
$FORGEJO_URL \
$FORGEJO_USER1_SUPPORT_REPO
python -m integration \
forgejo register \
$FORGEJO_TESTUSER_USERNAME $FORGEJO_TESTUSER_PASSWORD \
$FORGEJO_TESTUSER_EMAIL \
$FORGEJO_URL
python -m integration \
forgejo create_issue \
$FORGEJO_TESTUSER_USERNAME $FORGEJO_TESTUSER_PASSWORD \
$FORGEJO_USER1_EMAIL \
$FORGEJO_URL \
$FORGEJO_USER1_USERNAME \
$FORGEJO_USER1_SUPPORT_REPO
}
setup_env() {
# mkdir tmp/ || true
# nohup python manage.py runserver > /dev/null 2>&1 &
# SERVER_PID=$!
# echo $SERVER_PID > $SERVER_PID_FILE
echo "TODO"
}
teardown_env() {
echo "TODO"
#kill $(cat $SERVER_PID_FILE)
}
docker_compose_up() {
echo "[*] Starting Forgejo"
docker-compose -f docker-compose-dev-deps.yml up
}
docker_compose_down() {
docker-compose -f docker-compose-dev-deps.yml down
docker-compose -f docker-compose-dev-deps.yml down --remove-orphans
}

17
integration/tests.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
set -Exeuo pipefail
source integration/lib.sh
main() {
teardown_env || true
setup_env
wait_for_env
forgejo_root
init_users_repo
teardown_env
echo "All Good! :)"
}
main