From 0447232950640bc1fc6ad5f40ec333213da71406 Mon Sep 17 00:00:00 2001 From: s3lph Date: Tue, 26 Nov 2019 16:07:17 +0100 Subject: [PATCH] Archlinux packaging --- .gitlab-ci.yml | 53 +++++- package/archlinux/PKGBUILD | 27 +++ package/archlinux/spaceapi-server.install | 31 +++ .../etc/spaceapi-server/config.json | 7 + .../etc/spaceapi-server/plugins/.gitkeep | 0 .../etc/spaceapi-server/template.json | 19 ++ .../systemd/system/spaceapi-server.service | 10 + .../share/licenses/spaceapi-server/.gitkeep | 0 package/debian/spaceapi-server/DEBIAN/prerm | 2 + package/release.py | 178 ++++++++++++++++++ 10 files changed, 324 insertions(+), 3 deletions(-) create mode 100644 package/archlinux/PKGBUILD create mode 100644 package/archlinux/spaceapi-server.install create mode 100644 package/archlinux/spaceapi-server/etc/spaceapi-server/config.json create mode 100644 package/archlinux/spaceapi-server/etc/spaceapi-server/plugins/.gitkeep create mode 100644 package/archlinux/spaceapi-server/etc/spaceapi-server/template.json create mode 100644 package/archlinux/spaceapi-server/usr/lib/systemd/system/spaceapi-server.service create mode 100644 package/archlinux/spaceapi-server/usr/share/licenses/spaceapi-server/.gitkeep create mode 100755 package/release.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1d0c3b4..7d7ee76 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,7 @@ image: s3lph/spaceapi-server-ci:20191126-02 stages: - test - build + - release @@ -81,9 +82,8 @@ build_debian: # Remove compiled Python files - find usr/lib/python3/dist-packages -name __pycache__ -exec rm -r {} \; 2>/dev/null || true - find usr/lib/python3/dist-packages -name '*.pyc' -exec rm {} \; - # Move spaceapi-server binary to /usr/lib - - mv usr/bin/spaceapi-server usr/lib/spaceapi-server/spaceapi-server - - sed -re 's$#!/usr/local/bin/python3.*$#!/usr/bin/python3$' -i usr/lib/spaceapi-server/spaceapi-server + # Remove spaceapi-server script + - rm usr/bin/spaceapi-server # Fix file permissions - find . -type f -exec chmod 0644 {} \; - find . -type d -exec chmod 755 {} \; @@ -102,3 +102,50 @@ build_debian: - package/debian/SHA256SUMS only: - tags + +build_archlinux: + stage: build + image: archlinux/base:latest # Use an archlinux image instead of the customized debian image. + script: + - find package/archlinux -name .gitkeep -delete + # Install dependencies + - pacman -Sy --noconfirm namcap python python-setuptools python-pip python-wheel python-jinja python-bottle base-devel + - export SPACEAPI_SERVER_VERSION=$(python -c 'import spaceapi_server; print(spaceapi_server.__version__)') + # Copy example plugin + - install -m0644 examples/plugins/example.py package/archlinux/spaceapi-server/etc/spaceapi-server/plugins/example.py + # Create changelog + - | + for version in "$(cat CHANGELOG.md | grep '" | grep -B 1000 "<"'!'"-- END CHANGES ${version} -->" | tail -n +2 | head -n -1 | sed -re 's/^-/\t*/g' >> package/archlinux/spaceapi-server.changelog + echo >> package/archlinux/spaceapi-server.changelog + done + # Copy license + - install -m0644 LICENSE package/archlinux/spaceapi-server/usr/share/licenses/spaceapi-server/LICENSE + # Install spaceapi-server into pkgdir + - python setup.py egg_info -d -b +master install --root=package/archlinux/spaceapi-server/ --prefix=/usr --optimize=1 + - cd package/archlinux + # Remove spaceapi-server script + - rm -rf spaceapi-server/usr/bin + # Build the package + - sed -re "s/__VERSION__/${SPACEAPI_SERVER_VERSION}/g" -i PKGBUILD + - sudo -u nobody makepkg -c + # Run namcap + - sudo -u nobody namcap *.pkg.tar.xz + # Generate checksum + - sha256sum *.pkg.tar.xz > SHA256SUMS + artifacts: + paths: + - "package/archlinux/*.pkg.tar.xz" + - package/archlinux/SHA256SUMS + only: + - tags + + + +release: + stage: release + script: + - python3 package/release.py + only: + - tags \ No newline at end of file diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD new file mode 100644 index 0000000..a7d699f --- /dev/null +++ b/package/archlinux/PKGBUILD @@ -0,0 +1,27 @@ +# Maintainer: s3lph +pkgname=spaceapi-server +pkgver=__VERSION__ +pkgrel=1 +pkgdesc="Lightweight SpaceAPI endpoint server" +arch=('any') +url="https://gitlab.com/s3lph/spaceapi-server" +license=('MIT') +groups=() +depends=('python' + 'python-jinja' + 'python-bottle') +makedepends=('python-setuptools') +checkdepends=() +optdepends=() +provides=() +conflicts=() +replaces=() +backup=('etc/spaceapi-server/config.json' + 'etc/spaceapi-server/template.json' + 'etc/spaceapi-server/plugins') +install=$pkgname.install +changelog=$pkgname.changelog + +package() { + cp -r ../spaceapi-server/* ../pkg/spaceapi-server/ +} diff --git a/package/archlinux/spaceapi-server.install b/package/archlinux/spaceapi-server.install new file mode 100644 index 0000000..de8dc77 --- /dev/null +++ b/package/archlinux/spaceapi-server.install @@ -0,0 +1,31 @@ + +post_install() { + + if ! getent group spaceapi-server >/dev/null; then + groupadd --system spaceapi-server + fi + + if ! getent passwd spaceapi-server >/dev/null; then + useradd --system --create-home --gid spaceapi-server --home-dir /var/lib/spaceapi-server \ + --shell /usr/sbin/nologin spaceapi-server + fi + + chown root:spaceapi-server /etc/spaceapi-server + chmod 0750 /etc/spaceapi-server + + systemctl daemon-reload || true + +} + +pre_remove() { + + systemctl stop spaceapi-server.service + userdel spaceapi-server + +} + +post_remove() { + + systemctl daemon-reload + +} \ No newline at end of file diff --git a/package/archlinux/spaceapi-server/etc/spaceapi-server/config.json b/package/archlinux/spaceapi-server/etc/spaceapi-server/config.json new file mode 100644 index 0000000..bb5b41a --- /dev/null +++ b/package/archlinux/spaceapi-server/etc/spaceapi-server/config.json @@ -0,0 +1,7 @@ +{ + "address": "::", + "port": 8080, + "template": "/etc/spaceapi-server/template.json", + "plugins_dir": "/etc/spaceapi-server/plugins", + "plugins": {} +} \ No newline at end of file diff --git a/package/archlinux/spaceapi-server/etc/spaceapi-server/plugins/.gitkeep b/package/archlinux/spaceapi-server/etc/spaceapi-server/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package/archlinux/spaceapi-server/etc/spaceapi-server/template.json b/package/archlinux/spaceapi-server/etc/spaceapi-server/template.json new file mode 100644 index 0000000..2af901c --- /dev/null +++ b/package/archlinux/spaceapi-server/etc/spaceapi-server/template.json @@ -0,0 +1,19 @@ +{ + "api": "0.13", + "space": "Example Space", + "logo": "https://example.org/logo.png", + "url": "https://example.org", + "location": { + "lat": 0.0, + "lon": 0.0 + }, + "state": { + "open": null + }, + "contact": { + "email": "example@example.org" + }, + "issue_report_channels": [ + "email" + ] +} \ No newline at end of file diff --git a/package/archlinux/spaceapi-server/usr/lib/systemd/system/spaceapi-server.service b/package/archlinux/spaceapi-server/usr/lib/systemd/system/spaceapi-server.service new file mode 100644 index 0000000..3c2b9b7 --- /dev/null +++ b/package/archlinux/spaceapi-server/usr/lib/systemd/system/spaceapi-server.service @@ -0,0 +1,10 @@ +[Unit] +Description=Lightweight SpaceAPI Endpoint Server + +[Service] +ExecStart=/usr/bin/python -m spaceapi_server /etc/spaceapi-server/config.json +ExecReload=/usr/bin/kill -HUP $MAINPID +User=spaceapi-server + +[Install] +WantedBy=network-online.target \ No newline at end of file diff --git a/package/archlinux/spaceapi-server/usr/share/licenses/spaceapi-server/.gitkeep b/package/archlinux/spaceapi-server/usr/share/licenses/spaceapi-server/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package/debian/spaceapi-server/DEBIAN/prerm b/package/debian/spaceapi-server/DEBIAN/prerm index bc72f5d..7e8bfbe 100644 --- a/package/debian/spaceapi-server/DEBIAN/prerm +++ b/package/debian/spaceapi-server/DEBIAN/prerm @@ -4,6 +4,8 @@ set -e if [ "$1" = "remove" ]; then + invoke-rc.d spaceapi-server stop + userdel spaceapi-server fi \ No newline at end of file diff --git a/package/release.py b/package/release.py new file mode 100755 index 0000000..68a178f --- /dev/null +++ b/package/release.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 + +from typing import Any, Dict, List, Optional, Tuple + +import os +import sys +import json +import urllib.request +import http.client +from urllib.error import HTTPError + + +def parse_changelog(tag: str) -> Optional[str]: + release_changelog: str = '' + with open('CHANGELOG.md', 'r') as f: + in_target: bool = False + done: bool = False + for line in f.readlines(): + if in_target: + if f'' in line: + done = True + break + release_changelog += line + elif f'' in line: + in_target = True + continue + if not done: + return None + return release_changelog + + +def fetch_job_ids(project_id: int, pipeline_id: int, api_token: str) -> Dict[str, str]: + url: str = f'https://gitlab.com/api/v4/projects/{project_id}/pipelines/{pipeline_id}/jobs' + headers: Dict[str, str] = { + 'Private-Token': api_token + } + req = urllib.request.Request(url, headers=headers) + try: + resp: http.client.HTTPResponse = urllib.request.urlopen(req) + except HTTPError as e: + print(e.read().decode()) + sys.exit(1) + resp_data: bytes = resp.read() + joblist: List[Dict[str, Any]] = json.loads(resp_data.decode()) + + jobidmap: Dict[str, str] = {} + for job in joblist: + name: str = job['name'] + job_id: str = job['id'] + jobidmap[name] = job_id + return jobidmap + + +def fetch_single_shafile(url: str) -> str: + req = urllib.request.Request(url) + try: + resp: http.client.HTTPResponse = urllib.request.urlopen(req) + except HTTPError as e: + print(e.read().decode()) + sys.exit(1) + resp_data: bytes = resp.readline() + shafile: str = resp_data.decode() + filename: str = shafile.strip().split(' ')[-1].strip() + return filename + + +def fetch_wheel_url(base_url: str, job_ids: Dict[str, str]) -> Optional[Tuple[str, str]]: + mybase: str = f'{base_url}/jobs/{job_ids["build_wheel"]}/artifacts/raw' + wheel_sha_url: str = f'{mybase}/dist/SHA256SUMS' + wheel_filename: str = fetch_single_shafile(wheel_sha_url) + wheel_url: str = f'{mybase}/dist/{wheel_filename}' + return wheel_url, wheel_sha_url + + +def fetch_debian_url(base_url: str, job_ids: Dict[str, str]) -> Optional[Tuple[str, str]]: + mybase: str = f'{base_url}/jobs/{job_ids["build_debian"]}/artifacts/raw' + debian_sha_url: str = f'{mybase}/package/debian/SHA256SUMS' + debian_filename: str = fetch_single_shafile(debian_sha_url) + debian_url: str = f'{mybase}/package/debian/{debian_filename}' + return debian_url, debian_sha_url + + +def fetch_rpm_url(base_url: str, job_ids: Dict[str, str]) -> Optional[Tuple[str, str]]: + mybase: str = f'{base_url}/jobs/{job_ids["build_rpm"]}/artifacts/raw' + rpm_sha_url: str = f'{mybase}/package/rpm/SHA256SUMS' + rpm_filename: str = fetch_single_shafile(rpm_sha_url) + rpm_url: str = f'{mybase}/package/rpm/{rpm_filename}' + return rpm_url, rpm_sha_url + + +def fetch_arch_url(base_url: str, job_ids: Dict[str, str]) -> Optional[Tuple[str, str]]: + mybase: str = f'{base_url}/jobs/{job_ids["build_archlinux"]}/artifacts/raw' + arch_sha_url: str = f'{mybase}/package/archlinux/SHA256SUMS' + arch_filename: str = fetch_single_shafile(arch_sha_url) + arch_url: str = f'{mybase}/package/archlinux/{arch_filename}' + return arch_url, arch_sha_url + + +def main(): + api_token: Optional[str] = os.getenv('GITLAB_API_TOKEN') + release_tag: Optional[str] = os.getenv('CI_COMMIT_TAG') + project_name: Optional[str] = os.getenv('CI_PROJECT_PATH') + project_id: Optional[str] = os.getenv('CI_PROJECT_ID') + pipeline_id: Optional[str] = os.getenv('CI_PIPELINE_ID') + if api_token is None: + print('GITLAB_API_TOKEN is not set.', file=sys.stderr) + sys.exit(1) + if release_tag is None: + print('CI_COMMIT_TAG is not set.', file=sys.stderr) + sys.exit(1) + if project_name is None: + print('CI_PROJECT_PATH is not set.', file=sys.stderr) + sys.exit(1) + if project_id is None: + print('CI_PROJECT_ID is not set.', file=sys.stderr) + sys.exit(1) + if pipeline_id is None: + print('CI_PIPELINE_ID is not set.', file=sys.stderr) + sys.exit(1) + + changelog: Optional[str] = parse_changelog(release_tag) + if changelog is None: + print('Changelog could not be parsed.', file=sys.stderr) + sys.exit(1) + + job_ids: Dict[str, str] = fetch_job_ids(project_id, pipeline_id, api_token) + + base_url: str = f'https://gitlab.com/{project_name}/-' + + wheel_url, wheel_sha_url = fetch_wheel_url(base_url, job_ids) + debian_url, debian_sha_url = fetch_debian_url(base_url, job_ids) + rpm_url, rpm_sha_url = fetch_rpm_url(base_url, job_ids) + arch_url, arch_sha_url = fetch_arch_url(base_url, job_ids) + + augmented_changelog = f'''{changelog.strip()} + +### Download + +- [Python Wheel]({wheel_url}) ([sha256]({wheel_sha_url})) +- [Debian Package]({debian_url}) ([sha256]({debian_sha_url})) +- [RPM Package]({rpm_url}) ([sha256]({rpm_sha_url})) +- [Arch Linux Package]({arch_url}) ([sha256]({arch_sha_url})) +- Docker image: registry.gitlab.com/{project_name}:{release_tag}''' + + post_body: str = json.dumps({'description': augmented_changelog}) + + gitlab_release_api_url: str = \ + f'https://gitlab.com/api/v4/projects/{project_id}/repository/tags/{release_tag}/release' + headers: Dict[str, str] = { + 'Private-Token': api_token, + 'Content-Type': 'application/json; charset=utf-8' + } + + request = urllib.request.Request( + gitlab_release_api_url, + post_body.encode('utf-8'), + headers=headers, + method='POST' + ) + try: + response: http.client.HTTPResponse = urllib.request.urlopen(request) + except HTTPError as e: + print(e.read().decode()) + sys.exit(1) + response_bytes: bytes = response.read() + response_str: str = response_bytes.decode() + response_data: Dict[str, Any] = json.loads(response_str) + + if response_data['tag_name'] != release_tag: + print('Something went wrong...', file=sys.stderr) + print(response_str, file=sys.stderr) + sys.exit(1) + + print(response_data['description']) + + +if __name__ == '__main__': + main()