[WIP] Move the release process to github actions.

This commit is contained in:
Matthieu Gautier 2020-05-19 12:06:53 +02:00
parent 763fe1432d
commit e020eb35ac
10 changed files with 615 additions and 31 deletions

View File

@ -24,7 +24,7 @@ RUN apt update -q \
# vim less grep \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc/* /var/cache/debconf/* \
&& pip3 install meson==0.52.1 pytest gcovr
&& pip3 install meson==0.52.1 pytest gcovr requests
# Create user
RUN useradd --create-home runner

View File

@ -12,13 +12,13 @@ RUN dnf install -y --nodocs \
mingw32-winpthreads-static mingw32-zlib-static mingw32-xz-libs-static \
mingw32-libmicrohttpd \
# python3
python-unversioned-command \
python3-pip python-unversioned-command \
# Other tools (to remove)
# vim less grep
&& dnf remove -y "*-doc" \
&& dnf autoremove -y \
&& dnf clean all \
&& pip3 install meson==0.52.1 pytest
&& pip3 install meson==0.52.1 pytest requests
# Create user
RUN useradd --create-home runner

View File

@ -2,6 +2,7 @@ FROM ubuntu:focal
ENV LANG C.UTF-8
ENV OS_NAME focal
ENV DEBIAN_FRONTEND noninteractive
RUN apt update -q \
&& apt install -q -y --no-install-recommends \
@ -24,7 +25,7 @@ RUN apt update -q \
# vim less grep \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc/* /var/cache/debconf/* \
&& pip3 install meson==0.52.1 pytest gcovr
&& pip3 install meson==0.52.1 pytest gcovr requests
# Create user
RUN useradd --create-home runner

View File

@ -21,7 +21,7 @@ RUN apt update -q \
# vim less grep \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc/* /var/cache/debconf/* \
&& pip3 install meson==0.52.1 pytest gcovr
&& pip3 install meson==0.52.1 pytest gcovr requests
# Create user
RUN useradd --create-home runner

117
.github/scripts/build_release_nightly.py vendored Executable file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env python3
import os
import json
import shutil
from common import (
run_kiwix_build,
main_project_versions,
release_versions,
get_postfix,
make_archive,
create_desktop_image,
update_flathub_git,
upload_archive,
BASE_DIR,
TMP_DIR,
HOME,
OS_NAME,
PLATFORM_TARGET,
DESKTOP,
)
from upload_to_bintray import upload_from_json
if os.environ.get('GITHUB_EVENT_NAME') == 'schedule':
RELEASE = False
else:
RELEASE = True
if PLATFORM_TARGET == "android":
TARGETS = ("kiwix-lib-app",)
elif PLATFORM_TARGET.startswith("iOS"):
TARGETS = ("libzim", "kiwix-lib")
elif PLATFORM_TARGET.startswith("native_"):
if OS_NAME == "osx":
TARGETS = ("libzim", "zimwriterfs", "zim-tools", "kiwix-lib")
else:
if DESKTOP:
TARGETS = ("kiwix-desktop",)
elif PLATFORM_TARGET == "native_mixed":
TARGETS = ("libzim",)
else:
TARGETS = ("zimwriterfs", "zim-tools", "kiwix-tools")
elif PLATFORM_TARGET in ("win32_static", "armhf_static", "i586_static"):
TARGETS = ("kiwix-tools",)
elif PLATFORM_TARGET == "flatpak":
TARGETS = ("kiwix-desktop",)
else:
TARGETS = ("libzim", "zim-tools", "kiwix-lib", "kiwix-tools")
# Filter what to build if we are doing a release.
if RELEASE:
TARGETS = tuple(filter(lambda t: release_versions.get(t) is not None, TARGETS))
for target in TARGETS:
run_kiwix_build(target, platform=PLATFORM_TARGET, make_release=RELEASE)
if target == "kiwix-desktop":
archive = create_desktop_image(make_release=RELEASE)
else:
archive = make_archive(target, make_release=RELEASE)
if archive:
upload_archive(archive, target, make_release=RELEASE)
# We have few more things to do for release:
if RELEASE:
# Publish source archives
if PLATFORM_TARGET == "native_dyn" and OS_NAME != "osx":
for target in TARGETS:
if release_versions.get(target) != 0:
continue
run_kiwix_build(
target, platform=PLATFORM_TARGET, make_release=RELEASE, make_dist=True
)
full_target_name = "{}-{}".format(target, main_project_versions[target])
if target == "kiwix-desktop":
archive = (
BASE_DIR / full_target_name / "{}.tar.gz".format(full_target_name)
)
else:
archive = (
BASE_DIR
/ full_target_name
/ "meson-dist"
/ "{}.tar.xz".format(full_target_name)
)
upload_archive(archive, target, make_release=RELEASE)
# Publish flathub
if PLATFORM_TARGET == "flatpak" and "kiwix-desktop" in TARGETS:
update_flathub_git()
if PLATFORM_TARGET == "android" and "kiwix-lib-app" in TARGETS:
postfix = get_postfix("kiwix-lib")
basename = "kiwixlib-{}".format(postfix)
output_release_dir = (
HOME / "BUILD_android" / "kiwix-lib-app" / "kiwixLibAndroid" / "build"
)
shutil.copy(
str(output_release_dir / "outputs" / "aar" / "kiwixLibAndroid-release.aar"),
str(TMP_DIR / (basename + ".aar")),
)
shutil.copy(
str(output_release_dir / "pom.xml"), str(TMP_DIR / (basename + ".pom"))
)
json_filename = "{}_bintray_info.json".format(basename)
data = {
"version": postfix,
"files": [basename + ext for ext in (".aar", ".pom")],
}
with open(str(TMP_DIR / json_filename), "w") as f:
json.dump(data, f)
upload_from_json(TMP_DIR / json_filename)

View File

@ -3,11 +3,16 @@ from os import environ as _environ
from pathlib import Path
from datetime import date
import tarfile
import zipfile
import subprocess
import re
import shutil
from kiwixbuild.versions import base_deps_versions
from kiwixbuild.versions import (
main_project_versions,
release_versions,
base_deps_versions,
)
PLATFORM_TARGET = _environ["PLATFORM_TARGET"]
if PLATFORM_TARGET == "native_desktop":
@ -23,6 +28,7 @@ SOURCE_DIR = HOME / "SOURCE"
ARCHIVE_DIR = HOME / "ARCHIVE"
INSTALL_DIR = BASE_DIR / "INSTALL"
TMP_DIR = Path("/tmp")
KBUILD_SOURCE_DIR = HOME / "kiwix-build"
# [TODO]
KIWIX_DESKTOP_ONLY = False
@ -30,6 +36,58 @@ KIWIX_DESKTOP_ONLY = False
_ref = _environ.get("GITHUB_REF", "").split("/")[-1]
MAKE_RELEASE = re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", _ref) is not None
PLATFORM_TO_RELEASE = {
"native_mixed": "linux-x86_64",
"native_static": "linux-x86_64",
"win32_static": "win-i686",
"armhf_static": "linux-armhf",
"i586_static": "linux-i586",
}
FLATPAK_HTTP_GIT_REMOTE = "https://github.com/flathub/org.kiwix.desktop.git"
FLATPAK_GIT_REMOTE = "git@github.com:flathub/org.kiwix.desktop.git"
BIN_EXT = ".exe" if PLATFORM_TARGET.startswith("win32_") else ""
# We have build everything. Now create archives for public deployement.
EXPORT_FILES = {
"kiwix-tools": (
INSTALL_DIR / "bin",
[
f + BIN_EXT
for f in ("kiwix-manage", "kiwix-read", "kiwix-search", "kiwix-serve")
],
),
"zim-tools": (
INSTALL_DIR / "bin",
[
f + BIN_EXT
for f in (
"zimbench",
"zimcheck",
"zimdump",
"zimsearch",
"zimdiff",
"zimpatch",
"zimsplit",
)
],
),
"zimwriterfs": (INSTALL_DIR / "bin", ["zimwriterfs" + BIN_EXT]),
"libzim": (
INSTALL_DIR,
(
"lib/x86_64-linux-gnu/libzim.so.{}".format(main_project_versions["libzim"]),
"lib/x86_64-linux-gnu/libzim.so.{}".format(
main_project_versions["libzim"][0]
),
"include/zim/**/*.h",
),
),
}
DATE = date.today().isoformat()
def print_message(message, *args, **kwargs):
message = message.format(*args, **kwargs)
@ -49,10 +107,7 @@ def write_manifest(manifest_file, archive_name, target, platform):
with manifest_file.open(mode="w") as f:
f.write(
MANIFEST_TEMPLATE.format(
archive_name=archive_name,
target=target,
platform=platform,
date=date.today().isoformat(),
archive_name=archive_name, target=target, platform=platform, date=DATE,
)
)
@ -95,12 +150,28 @@ def run_kiwix_build(
make_dist,
)
env = dict(_environ)
env['SKIP_BIG_MEMORY_TEST'] = '1'
env["SKIP_BIG_MEMORY_TEST"] = "1"
subprocess.check_call(command, cwd=str(HOME), env=env)
print_message("Build ended")
def upload(file_to_upload, host, dest_path):
if not file_to_upload.exists():
print_message("No {} to upload!", file_to_upload)
return
command = [
"ssh",
"-i",
_environ.get("SSH_KEY"),
"-o",
"StrictHostKeyChecking=no",
host,
"mkdir -p {}".format(dest_path),
]
print_message("Creating dest path {}", dest_path)
subprocess.check_call(command)
command = [
"scp",
"-i",
@ -114,6 +185,22 @@ def upload(file_to_upload, host, dest_path):
subprocess.check_call(command)
def upload_archive(archive, project, make_release):
if project.startswith("kiwix-"):
host = "ci@download.kiwix.org"
dest_path = "/data/download/"
else:
host = "ci@download.openzim.org"
dest_path = "/data/openzim/"
if make_release:
dest_path = dest_path + "release/" + project
else:
dest_path = dest_path + "nightly/" + DATE
upload(archive, host, dest_path)
def make_deps_archive(target=None, name=None, full=False):
archive_name = name or "deps2_{}_{}_{}.tar.xz".format(
OS_NAME, PLATFORM_TARGET, target
@ -162,3 +249,123 @@ def make_deps_archive(target=None, name=None, full=False):
tar.add(str(name), arcname=str(name.relative_to(relative_path)))
return archive_file
def get_postfix(project):
postfix = main_project_versions[project]
extra = release_versions.get(project)
if extra is not None:
postfix = "{}-{}".format(postfix, extra)
return postfix
def make_archive(project, make_release):
try:
platform = PLATFORM_TO_RELEASE[PLATFORM_TARGET]
except KeyError:
# We don't know how to name the release.
return None
base_dir, export_files = EXPORT_FILES[project]
if make_release:
postfix = get_postfix(project)
else:
postfix = DATE
archive_name = "{}_{}-{}".format(project, platform, postfix)
files_to_archive = []
for export_file in export_files:
files_to_archive.extend(base_dir.glob(export_file))
if platform == "win-i686":
open_archive = lambda a: zipfile.ZipFile(
str(a), "w", compression=zipfile.ZIP_DEFLATED
)
archive_add = lambda a, f: a.write(str(f), arcname=str(f.relative_to(base_dir)))
archive_ext = ".zip"
else:
open_archive = lambda a: tarfile.open(str(a), "w:gz")
archive_add = lambda a, f: a.add(
str(f), arcname="{}/{}".format(archive_name, str(f.relative_to(base_dir)))
)
archive_ext = ".tar.gz"
archive = TMP_DIR / "{}{}".format(archive_name, archive_ext)
print_message("create archive {} with {}", archive, files_to_archive)
with open_archive(archive) as arch:
for f in files_to_archive:
archive_add(arch, f)
return archive
def create_desktop_image(make_release):
print_message("creating desktop image")
if make_release:
postfix = get_postfix("kiwix-desktop")
src_dir = SOURCE_DIR / "kiwix-desktop_release"
else:
postfix = DATE
src_dir = SOURCE_DIR / "kiwix-desktop"
if PLATFORM_TARGET == "flatpak":
build_path = BASE_DIR / "org.kiwix.desktop.flatpak"
app_name = "org.kiwix.desktop.{}.flatpak".format(postfix)
print_message("archive is {}", build_path)
else:
build_path = HOME / "Kiwix-{}-x86_64.AppImage".format(postfix)
app_name = "kiwix-desktop_x86_64_{}.appimage".format(postfix)
command = [
"kiwix-build/scripts/create_kiwix-desktop_appImage.sh",
str(INSTALL_DIR),
str(src_dir),
str(HOME / "AppDir"),
]
env = dict(os.environ)
env["VERSION"] = postfix
print_message("Build AppImage of kiwix-desktop")
subprocess.check_call(command, cwd=str(HOME), env=env)
print_message("Copy Build to {}".format(TMP_DIR / app_name))
shutil.copy(str(build_path), str(TMP_DIR / app_name))
return TMP_DIR / app_name
def update_flathub_git():
git_repo_dir = TMP_DIR / "org.kiwix.desktop"
env = dict(os.environ)
env["GIT_AUTHOR_NAME"] = env["GIT_COMMITTER_NAME"] = "KiwixBot"
env["GIT_AUTHOR_EMAIL"] = env["GIT_COMMITTER_EMAIL"] = "kiwixbot@kymeria.fr"
def call(command, cwd=None):
cwd = cwd or git_repo_dir
print_message("call {}", command)
subprocess.check_call(command, env=env, cwd=str(cwd))
command = ["git", "clone", FLATPAK_HTTP_GIT_REMOTE]
call(command, cwd=TMP_DIR)
shutil.copy(str(BASE_DIR / "org.kiwix.desktop.json"), str(git_repo_dir))
patch_dir = KBUILD_SOURCE_DIR / "kiwixbuild" / "patches"
for dep in ("libaria2", "mustache", "pugixml", "xapian", "zstd"):
for f in patch_dir.glob("{}_*.patch".format(dep)):
shutil.copy(str(f), str(git_repo_dir / "patches"))
command = ["git", "add", "-A", "."]
call(command)
command = [
"git",
"commit",
"-m",
"Update to version {}".format(main_project_versions["kiwix-desktop"]),
]
try:
call(command)
except subprocess.CalledProcessError:
# This may fail if there is nothing to commit (a rebuild of the CI for exemple)
return
command = ["git", "config", "remote.origin.pushurl", FLATPAK_GIT_REMOTE]
call(command)
command = ["git", "push"]
env["GIT_SSH_COMMAND"] = "ssh -o StrictHostKeyChecking=no -i " + _environ.get(
"SSH_KEY"
)
call(command)

View File

@ -15,7 +15,6 @@ from common import (
HOME,
PLATFORM_TARGET,
OS_NAME,
MAKE_RELEASE,
)
def download_base_archive(base_name):
@ -31,14 +30,16 @@ def download_base_archive(base_name):
file.write(batch)
return file_path
ARCHIVE_NAME_TEMPLATE = "base_deps2_{os}_{platform}_{version}.tar.xz"
ARCHIVE_NAME_TEMPLATE = "base_deps2_{os}_{platform}_{version}_{release}.tar.xz"
base_dep_archive_name = ARCHIVE_NAME_TEMPLATE.format(
os=OS_NAME,
platform=PLATFORM_TARGET,
version=base_deps_meta_version,
release="release" if MAKE_RELEASE else "debug",
)
if PLATFORM_TARGET == 'flatpak':
base_dep_archive_name = "base_deps2_{}_flatpak.tar.xz".format(OS_NAME)
else:
base_dep_archive_name = ARCHIVE_NAME_TEMPLATE.format(
os=OS_NAME,
platform=PLATFORM_TARGET,
version=base_deps_meta_version,
)
print_message("Getting archive {}", base_dep_archive_name)
try:
@ -54,7 +55,6 @@ except URLError:
os=OS_NAME,
platform="android_{}".format(arch),
version=base_deps_meta_version,
release="release" if MAKE_RELEASE else "debug",
)
print_message("Getting archive {}", archive_name)
try:
@ -64,6 +64,8 @@ except URLError:
os.remove(str(local_filename))
except URLError:
pass
elif PLATFORM_TARGET == "flatpak":
print_message("Cannot get archive. Move on")
else:
run_kiwix_build("alldependencies", platform=PLATFORM_TARGET)
archive_file = make_deps_archive(name=base_dep_archive_name, full=True)

83
.github/scripts/upload_to_bintray.py vendored Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env python3
import os, sys
import json
import requests
bintray_auth = (os.environ.get('BINTRAY_USER'), os.environ.get('BINTRAY_PASS'))
def create_version(version):
url = "https://api.bintray.com/packages/kiwix/kiwix/kiwixlib/versions"
payload = {
'name': version,
'desc': 'Release of kiwix-lib'
}
headers = {
'Content-Type': 'application/json'
}
r = requests.post(url, data=json.dumps(payload), headers=headers, auth=bintray_auth)
rcode = r.status_code
if rcode == 409:
print("Bintray version %s already exists, skipping." % version)
return True
rcode_family = rcode // 100
if rcode_family in (2, 3):
print("Bintray Version created!")
return True
print("ERROR : Bintray API response {}".format(rcode))
return False
def upload(version, filepath, artefact):
url_template = "https://api.bintray.com/content/kiwix/kiwix/kiwixlib/{version}/org/kiwix/kiwixlib/kiwixlib/{version}/{artefact}"
parameters = {
'publish': 1,
'override': 1
}
# Upload the main artefact
url = url_template.format(version=version, artefact=artefact)
with open(filepath, 'rb') as f:
r = requests.put(url, data=f, auth=bintray_auth, params=parameters)
rcode = r.status_code
rcode_family = rcode // 100
if rcode_family not in (2, 3):
print("ERROR: Fail to upload artefact")
print(r.text)
return False
return True
def upload_from_json(json_path):
basedir = os.path.dirname(str(json_path))
with open(str(json_path)) as f:
options = json.load(f)
if not create_version(options['version']):
raise RuntimeError("Cannot create version")
for file_ in options['files']:
path = os.path.join(basedir, file_)
if not upload(options['version'], path, file_):
raise RuntimeError("Cannot upload file {}".format(file_))
if __name__ == "__main__":
try:
info_file = sys.argv[1]
except IndexError:
print("Usage {} infofile".format(sys.argv[0]))
sys.exit(-1)
print("Use info file {}".format(info_file))
try:
upload_from_json(info_file)
except RuntimeError as e:
sys.exit(str(e))

167
.github/workflows/releaseNigthly.yml vendored Normal file
View File

@ -0,0 +1,167 @@
name: Release&Nigthly
on:
push:
tags:
- r_[0-9]+i
schedule:
- cron: '0 1 * * *'
env:
DOCKER_VERSION: 28
jobs:
Docker:
strategy:
fail-fast: false
matrix:
variant: [xenial, bionic, f31, focal]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build docker image
shell: bash
run: |
TAGNAME=${{matrix.variant}}-${DOCKER_VERSION}
if [ ! $(curl -sflL https://hub.docker.com/v2/repositories/kiwix/kiwix-build_ci/tags/${TAGNAME}) ]
then
echo "${{secrets.docker_password}}" | docker login -u "${{secrets.docker_username}}" --password-stdin
FULLTAGNAME=kiwix/kiwix-build_ci:${TAGNAME}
docker build -t ${FULLTAGNAME} - < ${GITHUB_WORKSPACE}/.github/ci_images/${{matrix.variant}}_builder.dockerfile
docker push ${FULLTAGNAME}
fi
Linux:
strategy:
fail-fast: false
matrix:
target:
- native_static
- native_dyn
- native_mixed
- native_desktop
- armhf_static
- win32_static
- i586_static
- android
- flatpak
include:
- target: native_static
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: native_dyn
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: native_mixed
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: native_desktop
image_variant: bionic
lib_postfix: '/x86_64-linux-gnu'
- target: armhf_static
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: win32_static
image_variant: f31
lib_postfix: '64'
- target: i586_static
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: android
image_variant: xenial
lib_postfix: '/x86_64-linux-gnu'
- target: flatpak
image_variant: bionic
lib_postfix: '/x86_64-linux-gnu'
env:
HOME: /home/runner
SSH_KEY: /tmp/id_rsa
runs-on: ubuntu-latest
needs: Docker
container:
image: "kiwix/kiwix-build_ci:${{matrix.image_variant}}-28"
options: "--device /dev/fuse --privileged"
steps:
- name: Checkout code
shell: bash
run: |
cd $HOME
git clone https://github.com/${REP} --depth=1 --branch ${GITHUB_REF##*heads/}
pip3 install --user --no-deps ./${REP##*/}
env:
REP: ${{github.repository}}
- name: secret
shell: bash
run: |
echo "${{secrets.ssh_key}}" > $SSH_KEY
chmod 600 $SSH_KEY
- name: Ensure base deps
shell: bash
run: |
cd $HOME
kiwix-build/.github/scripts/ensure_base_deps.py
env:
PLATFORM_TARGET: ${{matrix.target}}
- name: Build release
shell: bash
run: |
cd $HOME
kiwix-build/.github/scripts/build_release_nightly.py
env:
PLATFORM_TARGET: ${{matrix.target}}
- name: Upload failure logs
if: failure()
run: $HOME/kiwix-build/.github/scripts/upload_failure_logs.sh
env:
PLATFORM_TARGET: ${{matrix.target}}
Macos:
strategy:
fail-fast: false
matrix:
target:
- native_dyn
runs-on: macos-latest
env:
SSH_KEY: /tmp/id_rsa
OS_NAME: osx
steps:
- name: Checkout code
uses: actions/checkout@v1
- name: Setup python 3.6
uses: actions/setup-python@v1
with:
python-version: '3.6'
- name: Install packages
uses: mstksg/get-package@v1
with:
brew: pkg-config ninja
- name: Install python modules
run: |
pip3 install meson==0.52.1 pytest requests
pip3 install --no-deps $GITHUB_WORKSPACE
- name: secret
shell: bash
run: |
echo "${{secrets.ssh_key}}" > $SSH_KEY
chmod 600 $SSH_KEY
- name: Ensure base deps
shell: bash
run: |
cd $HOME
$GITHUB_WORKSPACE/.github/scripts/ensure_base_deps.py
env:
PLATFORM_TARGET: ${{matrix.target}}
- name: Build release
shell: bash
run: |
cd $HOME
$GITHUB_WORKSPACE/.github/scripts/build_release_nightly.py
env:
PLATFORM_TARGET: ${{matrix.target}}
- name: Upload failure logs
if: failure()
run: $GITHUB_WORKSPACE/.github/scripts/upload_failure_logs.sh
env:
PLATFORM_TARGET: ${{matrix.target}}

View File

@ -54,6 +54,19 @@ def upload(version, filepath, artefact):
return True
def upload_from_json(json_path):
basedir = os.path.dirname(json_path)
with open(str(json_path)) as f:
options = json.load(f)
if not create_version(options['version']):
raise RuntimeError("Cannot create version")
for file_ in options['files']:
path = os.path.join(basedir, file_)
if not upload(options['version'], path, file_):
raise RuntimeError("Cannot upload file {}".format(file_))
if __name__ == "__main__":
try:
@ -63,14 +76,8 @@ if __name__ == "__main__":
sys.exit(-1)
print("Use info file {}".format(info_file))
with open(info_file) as f:
options = json.load(f)
try:
upload_from_json(info_file)
except RuntimeError as e:
sys.exit(str(e))
if not create_version(options['version']):
sys.exit("Cannot create version")
basedir = os.path.dirname(sys.argv[1])
for file_ in options['files']:
path = os.path.join(basedir, file_)
if not upload(options['version'], path, file_):
sys.exit("Cannot upload file {}".format(file_))