From 35030b7618bc195e4cc3ea3dd7ea99b76ca8a4c8 Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Wed, 13 Jul 2022 12:50:21 +0200 Subject: [PATCH] scripts/obs: rewrite pushing source pkgs to OBS Harald requested that the OBS scripts should not stop if building one specific source package fails, instead it should keep going and report at the end a non-success exit code. Given that the shell script code has historically grown and became hard to maintain, I decided to rewrite the scripts for implementing this feature. This rewrite solves additional problems: * No full checkout of an OBS project like network:osmocom:latest anymore, with lots of packages that won't get updated (e.g. the uhd package has a uhd-images_3.14.1.1.tar.xz file that is 108 MB). With the old code, developers had to wait minutes during the checkout before the script reaches code that is currently being developed. Now only single packages get checked out right before they get updated. * No need to clone git repositories over and over. With the new code, git repos only get cloned if needed (for latest it is not needed if the remote git tag is the same as the version in OBS). During development, the cloned git repositories are cached. * Output from commands like "git tag -l" is not written to the log unless they failed. This makes the log more readable, which is especially important when a package fails to build, we keep going and need to spot the build error in the middle of the log later on. * No more duplicated code for nightly and latest scripts that worked similar but had slight differences. Also the list of packages is not duplicated for nightly and latest anymore; nightly uses all packages and latest uses packages that have at least one git tag. * Building source packages is decoupled from uploading them. A separate script build_srcpkg.py can be used to just build the deb + rpm spec source packages, without interacting with the OBS server. * The scripts can optionally run in docker with a command-line switch, and this is used by jenkins. This way we don't need to install more dependencies on the host such as rebar3 which is now needed for erlang/osmo_dia2gsup. * Add erlang/osmo_dia2gsup and run its generate_build_dep.sh (SYS#6006) I have done the new implementation in python to make use of argparse and to be able to use try/except and print a trace when building one package fails. Example output: * https://jenkins.osmocom.org/jenkins/job/Osmocom_OBS_nightly_obs.osmocom.org/48/console * https://jenkins.osmocom.org/jenkins/job/Osmocom_OBS_latest_obs.osmocom.org/46/console Change-Id: I45a555d05a9da808c0fe0145aae665f583cb80d9 --- .gitignore | 4 +- jobs/osmocom-obs.yml | 21 +- scripts/common-obs-conflict.sh | 146 ------------- scripts/common-obs.sh | 326 ---------------------------- scripts/obs/build_srcpkg.py | 48 ++++ scripts/obs/data/Dockerfile | 35 +++ scripts/obs/data/Release.key | 26 +++ scripts/obs/data/rpmlintrc | 5 + scripts/obs/lib/__init__.py | 145 +++++++++++++ scripts/obs/lib/config.py | 103 +++++++++ scripts/obs/lib/debian.py | 109 ++++++++++ scripts/obs/lib/docker.py | 62 ++++++ scripts/obs/lib/git.py | 109 ++++++++++ scripts/obs/lib/metapkg.py | 104 +++++++++ scripts/obs/lib/osc.py | 120 ++++++++++ scripts/obs/lib/rpm_spec.py | 84 +++++++ scripts/obs/lib/srcpkg.py | 157 ++++++++++++++ scripts/obs/update_obs_project.py | 215 ++++++++++++++++++ scripts/osmocom-latest-packages.sh | 247 --------------------- scripts/osmocom-next-packages.sh | 6 - scripts/osmocom-nightly-packages.sh | 282 ------------------------ 21 files changed, 1338 insertions(+), 1016 deletions(-) delete mode 100644 scripts/common-obs-conflict.sh delete mode 100644 scripts/common-obs.sh create mode 100755 scripts/obs/build_srcpkg.py create mode 100644 scripts/obs/data/Dockerfile create mode 100644 scripts/obs/data/Release.key create mode 100644 scripts/obs/data/rpmlintrc create mode 100644 scripts/obs/lib/__init__.py create mode 100644 scripts/obs/lib/config.py create mode 100644 scripts/obs/lib/debian.py create mode 100644 scripts/obs/lib/docker.py create mode 100644 scripts/obs/lib/git.py create mode 100644 scripts/obs/lib/metapkg.py create mode 100644 scripts/obs/lib/osc.py create mode 100644 scripts/obs/lib/rpm_spec.py create mode 100644 scripts/obs/lib/srcpkg.py create mode 100755 scripts/obs/update_obs_project.py delete mode 100755 scripts/osmocom-latest-packages.sh delete mode 100755 scripts/osmocom-next-packages.sh delete mode 100755 scripts/osmocom-nightly-packages.sh diff --git a/.gitignore b/.gitignore index 78e48b5e..1009a648 100644 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,10 @@ tokens.txt jenkins_jobs.ini jenkins-jobs.ini __pycache__/ +_cache/ _temp/ _deps/ _release_tarballs/ _docker_playground _repo_install_test_data/ _repo_install_test_cache/ - -# osmocom-nightly-packages.sh -nightly-3g_* diff --git a/jobs/osmocom-obs.yml b/jobs/osmocom-obs.yml index cfd3bff7..ddb71f13 100644 --- a/jobs/osmocom-obs.yml +++ b/jobs/osmocom-obs.yml @@ -4,9 +4,12 @@ jobs: - Osmocom_OBS_{type}_{server} type: - - nightly - - latest - # - next (disabled: OS#5322) + - nightly: + # For nightly we don't provide ABI compatibility, make sure packages + # from different build dates are not mixed by accident + conflict_version: "$(date +%Y%m%d%H%M)" + - latest: + conflict_version: "" server: - build.opensuse.org: proj: "network:osmocom" @@ -28,10 +31,16 @@ default: 'refs/remotes/origin/master' builders: - shell: | - export PROJ={proj}:{type} - export OBS_SERVER={server} + export PYTHONUNBUFFERED=1 - ./scripts/osmocom-{type}-packages.sh + ./scripts/obs/update_obs_project.py \ + --apiurl {server} \ + --conflict-version {conflict_version} \ + --docker \ + --feed {type} \ + --git-fetch \ + --meta \ + {proj}:{type} scm: - git: branches: diff --git a/scripts/common-obs-conflict.sh b/scripts/common-obs-conflict.sh deleted file mode 100644 index 98c8b70b..00000000 --- a/scripts/common-obs-conflict.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/sh -# Create conflicting dummy packages in OBS (opensuse build service), so users can't mix packages -# built from different branches by accident - -OSMO_OBS_CONFLICT_PKGVER="${OSMO_OBS_CONFLICT_PKGVER:-1.0.0}" - -# Create the conflicting package for debian -# -# $1: name of dummy package (e.g. "osmocom-nightly") -# $2-*: name of conflicting packages (e.g. "osmocom-latest") -# -# Generates the following directory structure: -# debian -# ├── changelog -# ├── compat -# ├── control -# ├── copyright -# ├── rules -# └── source -# └── format -osmo_obs_prepare_conflict_deb() { - local pkgname="$1" - shift - local oldpwd="$PWD" - - mkdir -p "debian/source" - cd "debian" - - # Fill control - cat << EOF > control -Source: ${pkgname} -Section: unknown -Priority: optional -Maintainer: Oliver Smith -Build-Depends: debhelper (>= 9) -Standards-Version: 3.9.8 - -Package: ${pkgname} -Depends: \${misc:Depends} -Architecture: any -EOF - printf "Conflicts: " >> control - first=1 - for i in "$@"; do - if [ "$first" -eq 1 ]; then - first=0 - else - printf ", " >> control - fi - printf "%s" "$i" >> control - done - printf "\n" >> control - cat << EOF >> control -Description: Dummy package, which conflicts with: $@ -EOF - - # Fill changelog - cat << EOF > changelog -${pkgname} (${OSMO_OBS_CONFLICT_PKGVER}) unstable; urgency=medium - - * Dummy package, which conflicts with: $@ - - -- Oliver Smith Thu, 13 Jun 2019 12:50:19 +0200 -EOF - - # Fill rules - cat << EOF > rules -#!/usr/bin/make -f -%: - dh \$@ -EOF - - # Finish up debian dir - chmod +x rules - echo "9" > compat - echo "3.0 (native)" > source/format - touch copyright - - cd "$oldpwd" -} - -# Create the conflicting package for rpm (e.g. contrib/osmocom-nightly.spec.in). The remaining -# placeholders are replaced in osmo_obs_add_rpm_spec(). -# -# $1: name of dummy package (e.g. "osmocom-nightly") -# $2-*: name of conflicting packages (e.g. "osmocom-latest") -osmo_obs_prepare_conflict_rpm() { - local pkgname="$1" - shift - local spec_in="contrib/$pkgname.spec.in" - - mkdir -p contrib - - cat << EOF > "$spec_in" -Name: $pkgname -Version: @VERSION@ -Release: 0 -Summary: Dummy package, which conflicts with: $@ -License: AGPL-3.0-or-later -Group: Hardware/Mobile -Source: @SOURCE@ -EOF - - for i in "$@"; do - echo "Conflicts: $i" >> "$spec_in" - done - - cat << EOF >> "$spec_in" -%description -Dummy package, which conflicts with: $@ -%files -EOF - -} - -# Print names of packages that the conflict package from the current feed -# (e.g. osmocom-nightly) should conflict with (e.g. osmocom-latest, -# osmocom-next, osmocom-2021q1) -osmo_obs_prepare_conflict_args() { - for i in $FEEDS_ALL; do - if [ "$i" != "$FEED" ]; then - echo "osmocom-$i" - fi - done -} - -# Create conflicting packages, based on global $FEED and $FEEDS_ALL vars -osmo_obs_prepare_conflict() { - local pkgname="osmocom-$FEED" - local conflict_args="$(osmo_obs_prepare_conflict_args)" - local oldpwd="$PWD" - - mkdir -p "$pkgname" - cd "$pkgname" - - osmo_obs_prepare_conflict_deb "$pkgname" $conflict_args - osmo_obs_prepare_conflict_rpm "$pkgname" $conflict_args - - # Put in git repository - git init . - git add -A - git commit -m "auto-commit: $pkgname dummy package" || true - git tag -f "$OSMO_OBS_CONFLICT_PKGVER" - - cd "$oldpwd" -} diff --git a/scripts/common-obs.sh b/scripts/common-obs.sh deleted file mode 100644 index 6f96e95f..00000000 --- a/scripts/common-obs.sh +++ /dev/null @@ -1,326 +0,0 @@ -#!/bin/sh -# Various common code used in the OBS (opensuse build service) related osmo-ci shell scripts -. "$(dirname "$0")/common-obs-conflict.sh" - -FEEDS_ALL=" - 2021q1 - 2021q4 - 2022q1 - 2022q2 - latest - next - nightly -" - -osmo_cmd_require \ - dch \ - dh \ - dpkg-buildpackage \ - gbp \ - git \ - meson \ - mktemp \ - osc \ - patch \ - sed - -if [ -z "$PROJ" ]; then - echo "PROJ environment variable is not set" - exit 1 -fi - -if [ -z "$OBS_SERVER" ]; then - echo "OBS_SERVER environment variable is not set" - exit 1 -fi - -# Related configuration sections are in .oscrc (OS#5557) -echo "Using OBS server: $OBS_SERVER" -shopt -s expand_aliases -alias osc="osc -A '$OBS_SERVER'" - -# Add dependency to all (sub)packages in debian/control and commit the change. -# $1: path to debian/control file -# $2: package name (e.g. "libosmocore") -# $3: dependency package name (e.g. "osmocom-nightly") -# $4: dependency package version (optional, e.g. "1.0.0.202101151122") -osmo_obs_add_depend_deb() { - local d_control="$1" - local pkgname="$2" - local depend="$3" - local dependver="$4" - - if [ "$pkgname" = "$depend" ]; then - echo "NOTE: skipping dependency on itself: $depend" - return - fi - - if [ -n "$dependver" ]; then - depend="$depend (= $dependver)" - fi - - # Note: adding the comma at the end should be fine. If there is a Depends: line, it is most likely not empty. It - # should at least have ${misc:Depends} according to lintian. - sed "s/^Depends: /Depends: $depend, /g" -i "$d_control" - - git -C "$(dirname "$d_control")" commit -m "auto-commit: debian: depend on $depend" . -} - -# Add dependency to all (sub)packages in rpm spec file -# $1: path to rpm spec file -# $2: package name (e.g. "libosmocore") -# $3: dependency package name (e.g. "osmocom-nightly") -# $4: dependency package version (optional, e.g. "1.0.0.202101151122") -osmo_obs_add_depend_rpm() { - local spec="$1" - local pkgname="$2" - local depend="$3" - local dependver="$4" - - if [ "$pkgname" = "$depend" ]; then - echo "NOTE: skipping dependency on itself: $depend" - return - fi - - if [ -n "$dependver" ]; then - depend="$depend = $dependver" - fi - - ( while IFS= read -r line; do - echo "$line" - - case "$line" in - # Main package - "Name:"*) - echo "Requires: $depend" - ;; - # Subpackages - "%package"*) - echo "Requires: $depend" - ;; - # Build recipe - "%build"*) - if [ -n "$dependver" ]; then - cat << EOF -# HACK: don't let rpmlint abort the build when it finds that a library depends -# on a package with a specific version. The path used here is listed in: -# https://build.opensuse.org/package/view_file/devel:openSUSE:Factory:rpmlint/rpmlint-mini/rpmlint-mini.config?expand=1 -# Instead of writing to the SOURCES dir, we could upload osmocom-rpmlintrc as -# additional source for each package. But that's way more effort, not worth it. -echo "setBadness('shlib-fixed-dependency', 0)" \\ - > "%{_sourcedir}/osmocom-rpmlintrc" - -EOF - fi - ;; - esac - done < "$spec" ) > "$spec.new" - - mv "$spec.new" "$spec" -} - -# Copy a project's rpm spec.in file to the osc package dir, set the version/source, depend on the conflicting dummy -# package and 'osc add' it -# $1: oscdir (path to checked out OSC package) -# $2: repodir (path to git repository) -# $3: package name (e.g. "libosmocore") -# $4: dependency package name (e.g. "osmocom-nightly") -# $5: dependency package version (optional, e.g. "1.0.0.202101151122") -osmo_obs_add_rpm_spec() { - local oscdir="$1" - local repodir="$2" - local name="$3" - local depend="$4" - local dependver="$5" - local spec_in="$(find "$repodir" -name "$name.spec.in")" - local spec="$oscdir/$name.spec" - local tarball - local version - local epoch - - if [ -z "$spec_in" ]; then - echo "WARNING: RPM spec missing: $name.spec.in" - return - fi - - cp "$spec_in" "$spec" - - osmo_obs_add_depend_rpm "$spec" "$name" "$depend" "$dependver" - - # Set version and epoch from "Version: [EPOCH:]VERSION" in .dsc - version="$(grep "^Version: " "$oscdir"/*.dsc | cut -d: -f2- | xargs)" - case $version in - *:*) - epoch=$(echo "$version" | cut -d : -f 1) - version=$(echo "$version" | cut -d : -f 2) - ;; - esac - if [ -n "$epoch" ]; then - sed -i "s/^Version:.*/Version: $version\nEpoch: $epoch/g" "$spec" - else - sed -i "s/^Version:.*/Version: $version/g" "$spec" - fi - - # Set source file - tarball="$(cd "$oscdir" && ls -1 *_*.tar.*)" - sed -i "s/^Source:.*/Source: $tarball/g" "$spec" - - osc add "$spec" -} - -# Get the path to a distribution specific patch, either from osmo-ci.git or from the project repository. -# $PWD must be the project repository dir. -# $1: distribution name (e.g. "debian8") -# $2: project repository (e.g. "osmo-trx", "limesuite") -osmo_obs_distro_specific_patch() { - local distro="$1" - local repo="$2" - local ret - - ret="$OSMO_CI_DIR/obs-patches/$repo/build-for-$distro.patch" - if [ -f "$ret" ]; then - echo "$ret" - return - fi - - ret="debian/patches/build-for-$distro.patch" - if [ -f "$ret" ]; then - echo "$ret" - return - fi -} - -# Check if checkout or build of a given package should be skipped, based on the -# PACKAGES environment variable. -# $1: package name (e.g. "libosmocore") -osmo_obs_skip_pkg() { - local pkgname="$1" - - if [ -z "$PACKAGES" ]; then - # Don't skip - return 1 - fi - - for i in "osmocom-$FEED" $PACKAGES; do - if [ "$i" = "$pkgname" ]; then - return 1 - fi - done - - # Skip - return 0 -} - -# Copy an already checked out repository dir and apply a distribution specific patch. -# $PWD must be where all repositories are checked out in subdirs. -# $1: distribution name (e.g. "debian8") -# $2: project repository (e.g. "osmo-trx", "limesuite") -osmo_obs_checkout_copy() { - local distro="$1" - local repo="$2" - local patch - - if osmo_obs_skip_pkg "$repo"; then - return - fi - - echo - echo "====> Checking out $repo-$distro" - - # Verify distro name for consistency - local distros=" - debian8 - debian10 - " - local found=0 - local distro_i - for distro_i in $distros; do - if [ "$distro_i" = "$distro" ]; then - found=1 - break - fi - done - if [ "$found" -eq 0 ]; then - echo "ERROR: invalid distro name: $distro, should be one of: $distros" - exit 1 - fi - - # Copy - if [ -d "$repo-$distro" ]; then - rm -rf "$repo-$distro" - fi - cp -a "$repo" "$repo-$distro" - cd "$repo-$distro" - - # Commit patch - patch="$(osmo_obs_distro_specific_patch "$distro" "$repo")" - if [ -z "$patch" ]; then - echo "ERROR: no patch found for distro=$distro, repo=$repo" - exit 1 - fi - patch -p1 < "$patch" - git commit -m "auto-commit: apply $patch" debian/ - cd .. -} - -# Run git-version-gen inside Osmocom repositories, so the .tarball-version -# becomes part of the source repository. Usually this would be done with -# "make dist", but we use git-buildpackage instead. -osmo_obs_git_version_gen() { - if [ -x ./git-version-gen ]; then - ./git-version-gen . > .tarball-version 2>/dev/null - fi -} - -# Return a version based on the latest tag and commit (e.g. "1.5.1.93.47cc") -# or fall back to the last debian version (e.g. "2.2.6"). Run -# osmo_obs_git_version_gen before. $PWD must be inside a git repository. -osmo_obs_get_commit_version() { - local version="" - - if [ -e ".tarball-version" ]; then - version=$(cat .tarball-version) - # debian doesn't allow '-' in version. - version=$(echo "$version" | sed 's/-/./g' ) - fi - - # deb version - deb_version=$(head -1 debian/changelog | cut -d ' ' -f 2 | sed 's,(,,' | sed 's,),,') - if [ -z "$version" ] || [ "$version" = "UNKNOWN" ]; then - version="$deb_version" - else - # add epoch from debian/changelog - case $deb_version in - *:*) - epoch=$(echo "$deb_version" | cut -d : -f 1) - version=$epoch:$version - ;; - esac - fi - - echo -n "$version" -} - -# Verify that $FEED is in $FEEDS and $FEEDS_ALL -osmo_obs_verify_feed() { - local i - local j - - for i in $FEEDS; do - if [ "$i" != "$FEED" ]; then - continue - fi - - for j in $FEEDS_ALL; do - if [ "$j" = "$i" ]; then - return - fi - done - - echo "feed found in FEEDS but not FEEDS_ALL: $FEED" - exit 1 - done - - echo "unsupported feed: $FEED" - exit 1 -} diff --git a/scripts/obs/build_srcpkg.py b/scripts/obs/build_srcpkg.py new file mode 100755 index 00000000..ccf82d78 --- /dev/null +++ b/scripts/obs/build_srcpkg.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import argparse +import lib +import lib.config +import lib.docker +import lib.git +import lib.metapkg +import lib.srcpkg + + +def main(): + parser = argparse.ArgumentParser( + description="Clone the git repository and build the debian source" + " package as well as an rpm .spec file. This is the same" + " code that runs to generate source packages which we" + " upload to https://obs.osmocom.org." + f" Output dir: {lib.config.path_temp}/srcpkgs") + lib.add_shared_arguments(parser) + parser.add_argument("package", nargs="?", + help="package name, e.g. libosmocore or open5gs") + args = parser.parse_args() + + if not args.meta and not args.package: + print("ERROR: specify -m and/or a package. See -h for help.") + exit(1) + + lib.set_cmds_verbose(args.verbose) + + if args.docker: + lib.docker.run_in_docker_and_exit(__file__, args) + + lib.check_required_programs() + if args.package: + lib.check_package(args.package) + lib.remove_temp() + + if args.meta: + lib.metapkg.build(args.feed, args.conflict_version) + + if args.package: + lib.srcpkg.build(args.package, args.feed, args.conflict_version, + args.git_fetch) + + +if __name__ == "__main__": + main() diff --git a/scripts/obs/data/Dockerfile b/scripts/obs/data/Dockerfile new file mode 100644 index 00000000..d8bd9c55 --- /dev/null +++ b/scripts/obs/data/Dockerfile @@ -0,0 +1,35 @@ +FROM debian:bullseye +ARG UID + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + gnupg2 \ + && \ + apt-get clean + +COPY Release.key /tmp/Release.key +RUN apt-key add /tmp/Release.key && \ + rm /tmp/Release.key && \ + echo "deb https://downloads.osmocom.org/packages/osmocom:/latest/Debian_11/ ./" \ + > /etc/apt/sources.list.d/osmocom-latest.list + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + debhelper \ + dh-python \ + dpkg-dev \ + fakeroot \ + git \ + meson \ + osc \ + python3-setuptools \ + rebar3 \ + sed \ + && \ + apt-get clean + +RUN useradd --uid=${UID} -m user +USER user diff --git a/scripts/obs/data/Release.key b/scripts/obs/data/Release.key new file mode 100644 index 00000000..ecca0844 --- /dev/null +++ b/scripts/obs/data/Release.key @@ -0,0 +1,26 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.5 (GNU/Linux) + +mQENBGKzE1QBCADFcM3ZzggvgxNRNNqDGWf5xIDAiK5qzFLdGes7L6F9VCHdaPy0 +RAOB5bFb/Q1tSDFNEBLtaauXKz+4iGL6qMVjZcyjzpB5w4jKN+kkrFRhjDNUv/SH +BX6d+P7v5WBGSNArNgA8D1BGzckp5a99EZ0okMJFEqIcN40PD6OGugpq5XnVV5Nk +e93fLa2Cu8vhFBcVn6CuHeEhsmuMf6NLbQRNfNNCEEUYaZn7beMYtpZ7t1djsKx5 +1xGm50OzI22FLu8lELQ9d7qMVGRG3WHYawX9BDteRybiyqxfwUHm1haWazRJtlGt +UWyzvwAb80BK1J2Nu5fbAa3w5CoEPAbUuCyrABEBAAG0JW9zbW9jb20gT0JTIFBy +b2plY3QgPG9zbW9jb21Ab3Ntb2NvbT6JAVQEEwEIAD4WIQRrKp83ktFetw1Oao+G +pzC2U3JZcwUCYrMV4wIbAwUJBB6yjwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAK +CRCGpzC2U3JZc4FRCACQQkKIrnvQ7n2u7GSmyVZa3I+oLoFXSGqaGyey5TW/nrMm +vFDKU3qliHiuNSmUY35SnAhXUsvqOYppxVRoO1MLrqUvzMOnIWqkJpf8mtjGUnsW +jyVeto7Rsjs75y2i1Hk+e7ljb/V65J3NlfrfEYWbqR9AKd53ReNXTdrQ0J05A38N +GdI4Ld/2lNISAwaBmGhqdeKsLHpQw/JERU1TApVJR1whFiIwDF1rOCg9GPnNKIk7 +yRZdK267XzztrainX/cbPILyzUZEDhYs6wQuyACyQ1YUxZIxrwVfk7PMNay8CrLH +z42B73Ne5IAj8+op/3iJafFONLm7YXiDUFN+QDYAiQEzBBMBCAAdFiEExoiYhHND +S7aVYlnqa51NyAUyjdsFAmKzE1UACgkQa51NyAUyjdvuZgf+OXmr//i7u7Gg7eWB +7e0qUsyCId9lXS8J437x3K6ciJfD7/6RSy8TFW5Nglm/uSkbyq582I8t+SoOirMD +E6cg9U/5+h5s46bAf+Kd2XS/6tLGeNLM18i4el8CP06NpFzDrsKu76uYFpyRiiHD +otBdtgxeLJ83LugGfZslF+/5cigJkAJMhAdVvGO8h85R6fba8ZSOKtMKkaQRfi76 +nhyOrJPlLuS+DLEnHwdkOFgtKnxHdjM97K+Tx0gisb6uwaWroXfSLnhP8RTLLZZy +Z+noU1Hw3c+mn4c/NYbcC/uwHYHKRzuf9gHnQ3dGgv0Z5sbeLRVo92hjGj7Ftlyd +4hmKBg== +=HxK4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/scripts/obs/data/rpmlintrc b/scripts/obs/data/rpmlintrc new file mode 100644 index 00000000..aa3903cd --- /dev/null +++ b/scripts/obs/data/rpmlintrc @@ -0,0 +1,5 @@ +# Don't abort the build when finding a library that depends on a package with +# a specific version. This is intentional for nightly builds, we don't want +# libraries from different build dates to be mixed as they might have ABI +# incompatibilities. +setBadness('shlib-fixed-dependency', 0) diff --git a/scripts/obs/lib/__init__.py b/scripts/obs/lib/__init__.py new file mode 100644 index 00000000..193c2489 --- /dev/null +++ b/scripts/obs/lib/__init__.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import importlib +import os +import shutil +import subprocess +import sys +import tempfile +import lib.config + +cmds_verbose = False + + +def add_shared_arguments(parser): + parser.add_argument("-f", "--feed", help="package feed (default: nightly)", + metavar="FEED", default="nightly", + choices=lib.config.feeds) + parser.add_argument("-d", "--docker", + help="run in docker to avoid installing required pkgs", + action="store_true") + parser.add_argument("-g", "--git-fetch", + help="fetch already cloned git repositories", + action="store_true") + parser.add_argument("-m", "--meta", action="store_true", + help="build a meta package (e.g. osmocom-nightly)") + parser.add_argument("-c", "--conflict-version", nargs="?", + help="Of the generated source packages, all Osmocom" + " packages (not e.g. open5gs, see lib/config.py" + " for full list) depend on a meta-package such as" + " osmocom-nightly, osmocom-latest, osmocom-2021q1" + " etc. These meta-packages conflict with each" + " other to ensure that one does not mix e.g." + " latest and nightly packages by accident." + " With this -c argument, it is possible to let" + " these packages depend on a meta-package of a" + " specific version. This is used for nightly and" + " 20YYqX packages to ensure they are not mixed" + " from different build dates (ABI compatibility" + " is only on latest).") + parser.add_argument("-v", "--verbose", action="store_true", + help="always print shell commands and their output," + " instead of only printing them on error") + + +def set_cmds_verbose(new_val): + global cmds_verbose + cmds_verbose = new_val + + +def check_required_programs(): + ok = True + + for program in lib.config.required_programs: + if not shutil.which(program): + print(f"ERROR: missing program: {program}") + ok = False + + for module in lib.config.required_python_modules: + if not importlib.find_loader(module): + print(f"ERROR: missing python3 module: {module}") + ok = False + + if not ok: + print("Either install them or use the -d argument to run in docker") + exit(1) + + +def check_package(package): + if package in lib.config.projects_osmocom: + return + if package in lib.config.projects_other: + return + + print(f"ERROR: unknown package: {package}") + print("See packages_osmocom and packages_other in obs/lib/config.py") + exit(1) + + +def exit_error_cmd(completed, error_msg): + """ :param completed: return from run_cmd() below """ + print() + print(f"ERROR: {error_msg}") + print() + print(f"*** command ***\n{completed.args}\n") + print(f"*** returncode ***\n{completed.returncode}\n") + print(f"*** output ***\n{completed.output}") + print("*** python trace ***") + raise RuntimeError("shell command related error, find details right above" + " this python trace") + + +def run_cmd(cmd, check=True, *args, **kwargs): + """ Like subprocess.run, but has check=True and text=True by default and + allows capturing the output while displaying it at the same time. By + default the output is hidden unless there's an error, with -v the + output gets written to stdout. + :returns: subprocess.CompletedProcess instance, but with combined + stdout + stderr written to ret.output + :param check: stop with error if exit code is not 0 """ + global cmds_verbose + + if cmds_verbose: + print(f"+ {cmd}") + + with tempfile.TemporaryFile(encoding="utf8", mode="w+") as output_buf: + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, text=True, bufsize=1, + *args, **kwargs) + + while True: + out = p.stdout.read(1) + if out == "" and p.poll() is not None: + break + if out != "": + output_buf.write(out) + if cmds_verbose: + sys.stdout.write(out) + sys.stdout.flush() + + output_buf.seek(0) + setattr(p, "output", output_buf.read()) + + if p.returncode == 0 or not check: + return p + + exit_error_cmd(p, "command failed unexpectedly") + + +def remove_temp(): + run_cmd(["rm", "-rf", lib.config.path_temp]) + + +def remove_cache_extra_files(): + """ dpkg-buildpackage outputs all files to the top dir of the package + dir, so it will always put them in _cache when building e.g. the debian + source package of _cache/libosmocore. Clear all extra files from _cache + that don't belog to the git repositories which we actually want to + cache. """ + run_cmd(["find", lib.config.path_cache, "-maxdepth", "1", "-type", "f", + "-delete"]) + + +def get_output_path(project): + return f"{lib.config.path_temp}/srcpkgs/{os.path.basename(project)}" diff --git a/scripts/obs/lib/config.py b/scripts/obs/lib/config.py new file mode 100644 index 00000000..22c9c05e --- /dev/null +++ b/scripts/obs/lib/config.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import os + +# Lists are ordered alphabetically. + +path_top = os.path.normpath(f"{os.path.realpath(__file__)}/../..") +path_cache = f"{path_top}/_cache" +path_temp = f"{path_top}/_temp" + +# Keep in sync with packages installed in data/Dockerfile +required_programs = [ + "dh", + "dh_python3", + "dpkg-buildpackage", + "fakeroot", + "find", + "git", + "meson", + "osc", + "rebar3", + "sed", +] + +required_python_modules = [ + "setuptools", +] + +feeds = [ + "2022q1", + "2022q2", + "latest", + "nightly", +] + +# Osmocom projects: generated source packages will depend on a meta package, +# such as osmocom-nightly, osmocom-latest or osmocom-2022q1. This meta package +# prevents that packages from different feeds are mixed by accident. +projects_osmocom = [ + "erlang/osmo_dia2gsup", + "libasn1c", + "libgtpnl", + "libosmo-abis", + "libosmo-dsp", + "libosmo-netif", + "libosmo-sccp", + "libosmocore", + "libsmpp34", + "libusrp", + "osmo-bsc", + "osmo-bts", + "osmo-cbc", + "osmo-e1d", + "osmo-gbproxy", + "osmo-ggsn", + "osmo-gsm-manuals", + "osmo-hlr", + "osmo-hnbgw", + "osmo-hnodeb", + "osmo-iuh", + "osmo-mgw", + "osmo-msc", + "osmo-pcap", + "osmo-pcu", + "osmo-remsim", + "osmo-sgsn", + "osmo-sip-connector", + "osmo-smlc", + "osmo-sysmon", + "osmo-trx", + "osmo-uecups", + "python/osmo-python-tests", + "rtl-sdr", + "simtrace2", +] +projects_other = [ + "limesuite", + "neocon", + "open5gs", +] + +git_url_default = "https://gerrit.osmocom.org" # /project gets appended +git_url_other = { + "libosmo-dsp": "https://gitea.osmocom.org/sdr/libosmo-dsp", + "limesuite": "https://github.com/myriadrf/LimeSuite", + "neocon": "https://github.com/laf0rge/neocon", + "open5gs": "https://github.com/open5gs/open5gs", + "rtl-sdr": "https://gitea.osmocom.org/sdr/rtl-sdr", +} + +git_branch_default = "master" +git_branch_other = { + "open5gs": "main", +} + +git_latest_tag_pattern_default = "^[0-9]*\\.[0-9]*\\.[0-9]*$" +git_latest_tag_pattern_other = { + "limesuite": "^v[0-9]*\\.[0-9]*\\.[0-9]*$", + "open5gs": "^v[0-9]*\\.[0-9]*\\.[0-9]*$", +} + +docker_image_name = "debian-bullseye-osmocom-obs" diff --git a/scripts/obs/lib/debian.py b/scripts/obs/lib/debian.py new file mode 100644 index 00000000..af152daa --- /dev/null +++ b/scripts/obs/lib/debian.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import datetime +import os +import shlex +import lib +import lib.git + + +def control_add_depend(project, pkgname, version): + """ :param pkgname: of the meta-package to depend on (e.g. osmocom-nightly) + :param version: of the meta-pkgname to depend on or None """ + repo_path = lib.git.get_repo_path(project) + + if version: + depend = f"{pkgname} (= {version})" + else: + depend = pkgname + + cmd = ["sed", f"s/^Depends: /Depends: {depend}, /", "-i", "debian/control"] + lib.run_cmd(cmd, cwd=repo_path) + + +def changelog_add_entry(project, feed, version): + """ :param version: for the new changelog entry """ + repo_path = lib.git.get_repo_path(project) + changelog_path = f"{repo_path}/debian/changelog" + + changelog_old = open(changelog_path).read() + + # Package name might be different from project name, read it from changelog + pkgname = changelog_old.split(" ", 1)[0] + assert pkgname + + # Debian doesn't allow '-' in version + version = version.replace("-", ".") + + # Debian changelog requires this specific date format + date = datetime.datetime.now(datetime.timezone.utc) + date_str = date.strftime("%a, %d %b %Y %H:%M:%S %z") + + # Add new changelog entry + with open(changelog_path, "w") as f: + f.write(f"{pkgname} ({version}) unstable; urgency=medium\n") + f.write("\n") + f.write(" * Automatically generated changelog entry for building the" + f" Osmocom {feed} feed\n") + f.write("\n") + f.write(f" -- Osmocom OBS scripts {date_str}\n") + f.write("\n") + f.write(changelog_old) + + +def fix_source_format(project): + """ Always use format "3.0 (native)" (e.g. limesuite has "3.0 (quilt)").""" + repo_path = lib.git.get_repo_path(project) + format_path = f"{repo_path}/debian/source/format" + if not os.path.exists(format_path): + return + + expected = "3.0 (native)\n" + current = open(format_path, "r").read() + + if current == expected: + return + + print(f"{project}: fixing debian/source/format ({current.rstrip()} =>" + f" {expected.rstrip()})") + open(format_path, "w").write(expected) + + +def get_last_version_from_changelog(project): + repo_path = lib.git.get_repo_path(project) + changelog_path = f"{repo_path}/debian/changelog" + + if not os.path.exists(changelog_path): + return None + + changelog = open(changelog_path).read() + if not changelog: + return None + + return changelog.split("(", 1)[1].split(")", 1)[0] + + +def changelog_add_entry_if_needed(project, feed, version): + """ Adjust the changelog if the version in the changelog is different from + the given version. """ + version_changelog = get_last_version_from_changelog(project) + if version_changelog == version: + return + + print(f"{project}: adding debian/changelog entry ({version_changelog} =>" + f" {version})") + changelog_add_entry(project, feed, version) + + +def build_source_package(project): + fix_source_format(project) + print(f"{project}: building debian source package") + lib.run_cmd(["dpkg-buildpackage", "-S", "-uc", "-us", "-d"], + cwd=lib.git.get_repo_path(project)) + + +def move_files_to_output(project): + path_output = lib.get_output_path(project) + lib.run_cmd(f"mv *.tar* *.dsc {shlex.quote(path_output)}", shell=True, + cwd=lib.config.path_cache) diff --git a/scripts/obs/lib/docker.py b/scripts/obs/lib/docker.py new file mode 100644 index 00000000..a5dbcd95 --- /dev/null +++ b/scripts/obs/lib/docker.py @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import os +import shutil +import subprocess +import sys +import lib +import lib.config + + +def build_image(): + print(f"docker: building image {lib.config.docker_image_name}") + lib.run_cmd(["docker", "build", + "--build-arg", f"UID={os.getuid()}", + "-t", lib.config.docker_image_name, + f"{lib.config.path_top}/data"]) + + +def get_oscrc(): + ret = os.path.expanduser("~/.oscrc") + if "OSCRC" in os.environ: + ret = os.environ["OSCRC"] + + if os.path.exists(ret): + return os.path.realpath(ret) + + print("ERROR: couldn't find ~/.oscrc. Put it there or set OSCRC.") + exit(1) + + +def run_in_docker_and_exit(script_path, args, add_oscrc=False): + if "INSIDE_DOCKER" in os.environ: + return + + if not shutil.which("docker"): + print("ERROR: docker is not installed") + exit(1) + + oscrc = None + if add_oscrc: + oscrc = get_oscrc() + + # Build the docker image. Unless it is up-to-date, this will take a few + # minutes or so, therefore print the output. + lib.set_cmds_verbose(True) + build_image() + lib.set_cmds_verbose(args.verbose) + + cmd = ["docker", "run", + "-e", "INSIDE_DOCKER=1", + "-e", "PYTHONUNBUFFERED=1", + "--rm", "-v", f"{lib.config.path_top}:/obs"] + + if oscrc: + cmd += ["-v", f"{oscrc}:/home/user/.oscrc"] + + script_path = f"/obs/{os.path.basename(script_path)}" + cmd += [lib.config.docker_image_name, script_path] + sys.argv[1:] + + print(f"docker: running: {os.path.basename(script_path)} inside docker") + ret = subprocess.run(cmd) + exit(ret.returncode) diff --git a/scripts/obs/lib/git.py b/scripts/obs/lib/git.py new file mode 100644 index 00000000..67f1d5ee --- /dev/null +++ b/scripts/obs/lib/git.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import os +import re +import lib.config + + +def get_repo_path(project): + return f"{lib.config.path_cache}/{os.path.basename(project)}" + + +def get_repo_url(project): + if project in lib.config.git_url_other: + return lib.config.git_url_other[project] + return f"{lib.config.git_url_default}/{project}" + + +def get_latest_tag_pattern(project): + if project in lib.config.git_latest_tag_pattern_other: + return lib.config.git_latest_tag_pattern_other[project] + return lib.config.git_latest_tag_pattern_default + + +def clone(project, fetch=False): + repo_path = get_repo_path(project) + url = get_repo_url(project) + + if os.path.exists(repo_path): + if fetch: + print(f"{project}: cloning {url} (cached, fetching)") + lib.run_cmd(["git", "fetch"], cwd=repo_path) + else: + print(f"{project}: cloning {url} (cached, not fetching)") + return + + print(f"{project}: cloning {url}") + os.makedirs(lib.config.path_cache, exist_ok=True) + lib.run_cmd(["git", "clone", url, repo_path]) + + lib.run_cmd(["git", "config", "user.name", "Osmocom OBS scripts"], + cwd=repo_path) + lib.run_cmd(["git", "config", "user.email", "info@osmocom.org"], + cwd=repo_path) + + +def clean(project): + repo_path = get_repo_path(project) + lib.run_cmd(["git", "clean", "-ffxd"], cwd=repo_path) + + +def checkout(project, branch): + repo_path = get_repo_path(project) + print(f"{project}: checking out {branch}") + lib.run_cmd(["git", "checkout", "-f", branch], cwd=repo_path) + lib.run_cmd(["git", "reset", "--hard", branch], cwd=repo_path) + + +def checkout_default_branch(project): + branch = lib.config.git_branch_default + if project in lib.config.git_branch_other: + branch = lib.config.git_branch_other[project] + + checkout(project, f"origin/{branch}") + + +def get_latest_tag(project): + pattern_str = get_latest_tag_pattern(project) + pattern = re.compile(pattern_str) + repo_path = get_repo_path(project) + + git_tag_ret = lib.run_cmd(["git", "tag", "-l", "--sort=-v:refname"], + cwd=repo_path) + + for line in git_tag_ret.output.split('\n'): + line = line.strip('\r') + if pattern.match(line): + return line + + lib.exit_error_cmd(git_tag_ret, f"couldn't find latest tag for {project}," + f" regex used on output: {pattern_str}") + + +def get_latest_tag_remote(project): + pattern_str = get_latest_tag_pattern(project) + pattern = re.compile(pattern_str) + + print(f"{project}: getting latest tag from git remote") + ls_remote = lib.run_cmd(["git", "ls-remote", "--tags", "--sort=-v:refname", + get_repo_url(project)]) + for line in ls_remote.output.split('\n'): + # Tags are listed twice, skip the ones with ^{} at the end + if "^{}" in line: + continue + + if "refs/tags/" not in line: + continue + + line = line.rstrip().split("refs/tags/")[1] + if pattern.match(line): + return line + + # No tag found probably means the repository was just created and doesn't + # have a release tag yet + return None + + +def checkout_latest_tag(project): + checkout(project, get_latest_tag(project)) diff --git a/scripts/obs/lib/metapkg.py b/scripts/obs/lib/metapkg.py new file mode 100644 index 00000000..dd261de6 --- /dev/null +++ b/scripts/obs/lib/metapkg.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import os +import lib +import lib.config +import lib.debian +import lib.rpm_spec + + +def get_conflicts(feed): + ret = [] + for f in lib.config.feeds: + if f == feed: + continue + ret += [f"osmocom-{f}"] + return ret + + +def prepare_source_dir(feed): + path = f"{lib.config.path_cache}/osmocom-{feed}" + + if os.path.exists(path): + lib.run_cmd(["rm", "-rf", path]) + + os.makedirs(f"{path}/debian") + os.makedirs(f"{path}/contrib") + + +def generate_debian_pkg(feed, version): + path = f"{lib.config.path_cache}/osmocom-{feed}" + conflicts = get_conflicts(feed) + + with open(f"{path}/debian/control", "w") as f: + f.write(f"Source: osmocom-{feed}\n") + f.write("Section: unknown\n") + f.write("Priority: optional\n") + f.write("Maintainer: Osmocom OBS scripts \n") + f.write("Build-Depends: debhelper (>= 10)\n") + f.write("Standards-Version: 3.9.8\n") + f.write("\n") + f.write(f"Package: osmocom-{feed}\n") + f.write("Depends: ${misc:Depends}\n") + f.write("Architecture: any\n") + f.write(f"Conflicts: {', '.join(conflicts)}\n") + f.write(f"Description: Dummy package, conflicts with {conflicts}\n") + + with open(f"{path}/debian/changelog", "w") as f: + f.write(f"osmocom-{feed} ({version}) unstable; urgency=medium\n") + f.write("\n") + f.write(f" * Dummy package, which conflicts with: {conflicts}\n") + f.write("\n") + f.write(" -- Osmocom OBS scripts Tue, 25 Jul 2022" + " 15:48:00 +0200\n") + + with open(f"{path}/debian/rules", "w") as f: + f.write("#!/usr/bin/make -f\n") + f.write("%:\n") + f.write("\tdh $@\n") + + lib.run_cmd(["chmod", "+x", f"{path}/debian/rules"]) + + with open(f"{path}/debian/compat", "w") as f: + f.write("10\n") + + +def generate_rpm_spec(feed, version): + print(f"osmocom-{feed}: generating rpm spec file") + path = (f"{lib.config.path_cache}/osmocom-{feed}/contrib/osmocom-{feed}" + ".spec.in") + conflicts = get_conflicts(feed) + + with open(path, "w") as f: + f.write(f"Name: osmocom-{feed}\n") + f.write(f"Version: {version}\n") + f.write(f"Summary: Dummy package, conflicts with: {conflicts}\n") + f.write("Release: 0\n") + f.write("License: AGPL-3.0-or-later\n") + f.write("Group: Hardware/Mobile\n") + for conflict in conflicts: + f.write(f"Conflicts: {conflict}\n") + f.write("%description\n") + f.write(f"Dummy package, which conflicts with: {conflicts}\n") + f.write("%files\n") + + +def build(feed, conflict_version): + pkgname = f"osmocom-{feed}" + version = conflict_version if conflict_version else "1.0.0" + print(f"{pkgname}: generating meta package {version}") + + prepare_source_dir(feed) + generate_debian_pkg(feed, version) + + os.makedirs(lib.get_output_path(pkgname)) + lib.remove_cache_extra_files() + + lib.debian.build_source_package(pkgname) + lib.debian.move_files_to_output(pkgname) + + generate_rpm_spec(feed, version) + lib.rpm_spec.copy_to_output(pkgname) + + lib.remove_cache_extra_files() + return version diff --git a/scripts/obs/lib/osc.py b/scripts/obs/lib/osc.py new file mode 100644 index 00000000..2fcdbc69 --- /dev/null +++ b/scripts/obs/lib/osc.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH + +import glob +import os +import shlex +import shutil +import lib +import lib.config + +apiurl = None + + +def check_proj(obs_project): + if ":" in obs_project: + return + + print(f"ERROR: this doesn't look like a valid OBS project: {obs_project}") + exit(1) + + +def set_apiurl(url): + global apiurl + apiurl = url + + +def run_osc(cmd, *args, **kwargs): + global apiurl + + # For some osc commands like 'osc add *' it makes sense to use a flat + # string and shell=True, hence support both list and string in this wrapper + if isinstance(cmd, str): + if apiurl: + cmd = f"osc -A {shlex.quote(apiurl)} {cmd}" + else: + cmd = f"osc {cmd}" + else: + if apiurl: + cmd = ["osc", "-A", apiurl] + cmd + else: + cmd = ["osc"] + cmd + + return lib.run_cmd(cmd, *args, **kwargs) + + +def get_remote_pkgs(proj): + print(f"OBS: getting packages in {proj}") + ret = run_osc(["list", proj]) + return ret.output.rstrip().split("\n") + + +def get_package_version(proj, package): + print(f"{package}: getting OBS version") + ret = run_osc(["list", proj, os.path.basename(package)]) + + # Empty OBS package + if ret.output == '\n': + return "0" + + # Extract the version from the dsc filename + for line in ret.output.split('\n'): + line = line.rstrip() + + if line.endswith(".dsc"): + return line.split("_")[-1][:-4] + + lib.exit_error_cmd(ret, "failed to find package version on OBS by" + " extracting the version from the .dsc filename") + + +def create_package(proj, package): + print(f"{package}: creating new OBS package") + + # cut off repository prefix like in "python/osmo-python-tests" + package = os.path.basename(package) + + path_meta = f"{lib.config.path_temp}/_meta" + path_meta_obs = f"source/{proj}/{package}/_meta" + + with open(path_meta, "w") as f: + f.write(f'\n') + f.write(f'{package}\n') + f.write('\n') + f.write('\n') + + run_osc(["api", "-X", "PUT", "-T", path_meta, path_meta_obs]) + + os.unlink(path_meta) + + +def remove_temp_osc(): + lib.run_cmd(["rm", "-rf", f"{lib.config.path_temp}/osc"]) + + +def update_package(proj, package, version): + print(f"{package}: updating OBS package") + + # cut off repository prefix like in "python/osmo-python-tests" + package = os.path.basename(package) + + path_output = lib.get_output_path(package) + path_temp_osc = f"{lib.config.path_temp}/osc" + path_temp_osc_pkg = f"{path_temp_osc}/{proj}/{package}" + + remove_temp_osc() + os.makedirs(path_temp_osc) + + run_osc(["checkout", proj, package], cwd=path_temp_osc) + + if glob.glob(f"{path_temp_osc_pkg}/*"): + run_osc("del *", shell=True, cwd=path_temp_osc_pkg) + + lib.run_cmd(f"mv * {shlex.quote(path_temp_osc_pkg)}", shell=True, + cwd=path_output) + + run_osc("add *", shell=True, cwd=path_temp_osc_pkg) + run_osc(["commit", "-m", f"upgrade to {version}"], cwd=path_temp_osc_pkg) + + remove_temp_osc() diff --git a/scripts/obs/lib/rpm_spec.py b/scripts/obs/lib/rpm_spec.py new file mode 100644 index 00000000..a7c43793 --- /dev/null +++ b/scripts/obs/lib/rpm_spec.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import glob +import os +import shutil +import lib +import lib.git + + +def get_spec_in_path(project): + repo_path = lib.git.get_repo_path(project) + ret = glob.glob(f"{repo_path}/**/contrib/*.spec.in", recursive=True) + assert len(ret) < 2 + return ret[0] if ret else None + + +def get_source_name(project): + """ Get the name to the .tar.xz file generated by dpkg-buildpackage, that + we also use in the rpm spec file. This requires + lib.debian.move_files_to_output() to run first.""" + path_output = lib.get_output_path(project) + ret = glob.glob(f"{path_output}/*.tar.*") + assert len(ret) == 1 + return os.path.basename(ret[0]) + + +def add_depend(project, pkgname, version): + """ Add a 'Requires: ...' line below the 'Name:' and '%package' lines of + the .spec.in file. + :param pkgname: of the meta-package to depend on (e.g. osmocom-nightly) + :param version: of the meta-pkgname to depend on or None """ + path = get_spec_in_path(project) + + req_line = f"Requires: {pkgname}" + if version: + req_line += f" = {version}" + + # Main package + lib.run_cmd(["sed", "-e", "/^Name:/a\\", "-e", req_line, "-i", path]) + + # Subpackages + lib.run_cmd(["sed", "-e", "/^%package/a\\", "-e", req_line, "-i", path]) + + +def generate(project, version, epoch): + """ Update the version and source in the .spec.in file. + :param version: from get-version-gen script """ + print(f"{project}: generating rpm spec file") + path = get_spec_in_path(project) + source = get_source_name(project) + + # rpmbuild doesn't allow '-' in version + version = version.replace("-", ".") + + # Version + lib.run_cmd(["sed", f"s/^Version:.*/Version: {version}/", "-i", path]) + if epoch: + epoch_line = f"Epoch: {epoch}" + lib.run_cmd(["sed", "-e", "/^Version:/a\\", "-e", epoch_line, "-i", + path]) + + # Source: add the one generated by dpkg-buildpackage and another line with + # rpmlintrc + lib.run_cmd(["sed", f"s/^Source:.*/Source: {source}/", "-i", path]) + rpmlint_line = "Source1: rpmlintrc" + lib.run_cmd(["sed", "-e", "/^Source:/a\\", "-e", rpmlint_line, "-i", path]) + + # rpmbuild assumes the directory inside the source tarball has the version + # at the end, but dpkg-buildpackage doesn't generate it that way. Set the + # directory with "%setup -n dirname". + lib.run_cmd(["sed", f"s/^%setup/%setup -n {os.path.basename(project)}/", + "-i", path]) + + +def copy_to_output(project): + # rpm spec + path_spec_in = get_spec_in_path(project) + path_output = lib.get_output_path(project) + name_spec = os.path.basename(path_spec_in)[:-3] # remove '.in' + shutil.copy(path_spec_in, f"{path_output}/{name_spec}") + + # rpmlintrc + shutil.copy(f"{lib.config.path_top}/data/rpmlintrc", path_output) diff --git a/scripts/obs/lib/srcpkg.py b/scripts/obs/lib/srcpkg.py new file mode 100644 index 00000000..468a240d --- /dev/null +++ b/scripts/obs/lib/srcpkg.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import os +import lib.config +import lib.debian +import lib.rpm_spec + + +def checkout_for_feed(project, feed): + """ checkout a commit, either latest tag or master or 20YY branch """ + if feed == "latest": + lib.git.checkout_latest_tag(project) + elif feed == "nightly": + lib.git.checkout_default_branch(project) + else: # 2022q1 etc + lib.git.checkout(project, feed) + + +def get_git_version_gen_path(project): + # Use git-version-gen in the project's repository if available + repo_path = lib.git.get_repo_path(project) + ret = f"{repo_path}/git-version-gen" + if os.path.exists(ret): + return ret + + # Use git-version-gen script from libosmocore.git as fallback + print(f"{project}: has no git-version-gen, using the one from libosmocore") + repo_path = lib.git.get_repo_path("libosmocore") + ret = f"{repo_path}/git-version-gen" + if not os.path.exists(ret): + lib.git.clone("libosmocore") + if os.path.exists(ret): + return ret + + print(f"ERROR: {project}.git doesn't have a git-version-gen script and" + " couldn't find libosmocore.git's copy of the script here either: " + + ret) + exit(1) + + +def get_git_version(project): + """ :returns: the string from git-version-gen, e.g. '1.7.0.10-76bdb' """ + repo_path = lib.git.get_repo_path(project) + script_path = get_git_version_gen_path(project) + + ret = lib.run_cmd([script_path, "."], cwd=repo_path) + if not ret.output: + lib.exit_error_cmd(ret, "empty output from git-version-gen") + + return ret.output + + +def get_version_for_feed(project, feed, conflict_version): + if feed == "latest": + # There's always a tag if we are here. If there was none, the build + # would have been skipped for latest. + ret = lib.git.get_latest_tag(project) + return ret[1:] if ret.startswith("v") else ret + + ret = get_git_version(project) + + # Try to get the last version from the debian/changelog if we can't get + # it with git-version-gen, like it was done in the previous OBS scripts + if ret == "UNKNOWN": + ret = lib.debian.get_last_version_from_changelog(project) + # cut off epoch, we retrieve it separately in get_epoch() below + if ":" in ret: + ret = ret.split(":")[1] + + if not ret: + ret = "0.0.0" + + # Append the conflict_version to increase the version even if the commit + # did not change (OS#5135) + if conflict_version: + ret = f"{ret}.{conflict_version}" + + return ret + + +def get_epoch(project): + """ The osmo-gbproxy used to have the same package version as osmo-sgsn + until 2021 where it was split into its own git repository. From then + on, osmo-gbproxy has a 0.*.* package version, which is smaller than + the previous 1.*.* from osmo-sgsn. We had to set the epoch to 1 for + osmo-gbproxy so package managers know these 0.*.* versions are higher + than the previous 1.*.* ones that are still found in e.g. debian 11. + The epoch is set in debian/changelog, retrieve it from there. + :returns: the epoch number if set, e.g. "1" or an empty string """ + version_epoch = lib.debian.get_last_version_from_changelog(project) + + if ":" in version_epoch: + return version_epoch.split(":")[0] + + return "" + + +def prepare_project_osmo_dia2gsup(): + """ Run erlang/osmo_dia2gsup's generate_build_dep.sh script to download + sources for dependencies. """ + lib.run_cmd("contrib/generate_build_dep.sh", + cwd=lib.git.get_repo_path("erlang/osmo_dia2gsup")) + + +def prepare_project_open5gs(): + """ Build fails without downloading freeDiameter sources. Also we can't + just update all subprojects because it would fail with 'Subproject + exists but has no meson.build file' for promethous-client-c. """ + lib.run_cmd(["meson", "subprojects", "download", "freeDiameter"], + cwd=lib.git.get_repo_path("open5gs")) + + +def write_tarball_version(project, version): + repo_path = lib.git.get_repo_path(project) + + with open(f"{repo_path}/.tarball-version", "w") as f: + f.write(f"{version}\n") + + +def build(project, feed, conflict_version, fetch): + lib.git.clone(project, fetch) + lib.git.clean(project) + checkout_for_feed(project, feed) + version = get_version_for_feed(project, feed, conflict_version) + epoch = get_epoch(project) + version_epoch = f"{epoch}:{version}" if epoch else version + has_rpm_spec = lib.rpm_spec.get_spec_in_path(project) is not None + + print(f"{project}: building source package {version_epoch}") + write_tarball_version(project, version_epoch) + + if project in lib.config.projects_osmocom: + metapkg = f"osmocom-{feed}" + lib.debian.control_add_depend(project, metapkg, conflict_version) + if has_rpm_spec: + lib.rpm_spec.add_depend(project, metapkg, conflict_version) + + lib.debian.changelog_add_entry_if_needed(project, feed, version_epoch) + + os.makedirs(lib.get_output_path(project)) + lib.remove_cache_extra_files() + + project_specific_func = f"prepare_project_{os.path.basename(project)}" + if project_specific_func in globals(): + print(f"{project}: running {project_specific_func}") + globals()[project_specific_func]() + + lib.debian.build_source_package(project) + lib.debian.move_files_to_output(project) + + if has_rpm_spec: + lib.rpm_spec.generate(project, version, epoch) + lib.rpm_spec.copy_to_output(project) + + lib.remove_cache_extra_files() + return version_epoch diff --git a/scripts/obs/update_obs_project.py b/scripts/obs/update_obs_project.py new file mode 100755 index 00000000..759acfec --- /dev/null +++ b/scripts/obs/update_obs_project.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright 2022 sysmocom - s.f.m.c. GmbH +import argparse +import os +import traceback +import lib +import lib.config +import lib.docker +import lib.git +import lib.metapkg +import lib.osc +import lib.srcpkg + +srcpkgs_built = {} # dict of pkgname: version +srcpkgs_skipped = [] # list of pkgnames +srcpkgs_failed_build = [] # list of pkgnames +srcpkgs_failed_upload = [] # list of pkgnames +srcpkgs_updated = [] # list of pkgnames + + +def parse_packages(packages_arg): + if packages_arg: + for package in packages_arg: + lib.check_package(package) + return packages_arg + + # Default to all + ret = [] + ret += lib.config.projects_osmocom + ret += lib.config.projects_other + return ret + + +def build_srcpkg(feed, package, conflict_version, fetch, is_meta_pkg): + global srcpkgs_built + global srcpkgs_failed_build + + version = None + + try: + if is_meta_pkg: + version = lib.metapkg.build(feed, conflict_version) + else: + version = lib.srcpkg.build(package, feed, conflict_version, fetch) + srcpkgs_built[package] = version + except Exception as ex: + traceback.print_exception(type(ex), ex, ex.__traceback__) + print() + print(f"{package}: build failed") + srcpkgs_failed_build += [package] + + +def is_up_to_date(obs_version, git_latest_version): + if obs_version == git_latest_version: + return True + + # e.g. open5gs has "v" infront of version in git tag + if f"v{obs_version}" == git_latest_version: + return True + + return False + + +def build_srcpkg_if_needed(proj, feed, pkgs_remote, package, conflict_version, + fetch, is_meta_pkg, skip_up_to_date): + global srcpkgs_skipped + + if feed != "latest": + print(f"{package}: building source package (feed is {feed})") + else: + if is_meta_pkg: + latest_version = conflict_version if conflict_version else "1.0.0" + else: + latest_version = lib.git.get_latest_tag_remote(package) + + if latest_version is None: + print(f"{package}: skipping (no git tag found)") + srcpkgs_skipped += [package] + return + + if os.path.basename(package) not in pkgs_remote: + print(f"{package}: building source package (not in OBS)") + else: + obs_version = lib.osc.get_package_version(proj, package) + if is_up_to_date(obs_version, latest_version): + if skip_up_to_date: + print(f"{package}: skipping ({obs_version} is up-to-date)") + srcpkgs_skipped += [package] + return + else: + print(f"{package}: building source package" + f" ({obs_version} is up-to-date, but" + " --no-skip-up-to-date is set)") + else: + print(f"{package}: building source package (outdated:" + f" {latest_version} <=> {obs_version} in OBS)") + + build_srcpkg(feed, package, conflict_version, fetch, is_meta_pkg) + + +def upload_srcpkg(proj, feed, pkgs_remote, package, version): + if os.path.basename(package) not in pkgs_remote: + lib.osc.create_package(proj, package) + lib.osc.update_package(proj, package, version) + + +def build_srcpkgs(proj, feed, pkgs_remote, packages, conflict_version, fetch, + meta, skip_up_to_date): + print() + print("### Building source packages ###") + print() + + if meta: + build_srcpkg_if_needed(proj, feed, pkgs_remote, f"osmocom-{feed}", + conflict_version, fetch, True, skip_up_to_date) + + for package in packages: + build_srcpkg_if_needed(proj, feed, pkgs_remote, package, + conflict_version, fetch, False, skip_up_to_date) + + +def upload_srcpkgs(proj, feed, pkgs_remote): + global srcpkgs_built + global srcpkgs_failed_upload + global srcpkgs_updated + + srcpkgs_failed_upload = [] + srcpkgs_updated = [] + + if not srcpkgs_built: + return + + print() + print("### Uploading built packages ###") + print() + + for package, version in srcpkgs_built.items(): + try: + upload_srcpkg(proj, feed, pkgs_remote, package, version) + srcpkgs_updated += [package] + except Exception as ex: + traceback.print_exception(type(ex), ex, ex.__traceback__) + print() + print(f"{package}: upload failed") + srcpkgs_failed_upload += [package] + + +def exit_with_summary(): + global srcpkgs_updated + global srcpkgs_skipped + global srcpkgs_failed_build + global srcpkgs_failed_upload + + print() + print("### Summary ###") + print() + print(f"Updated: {len(srcpkgs_updated)}") + print(f"Skipped: {len(srcpkgs_skipped)}") + print(f"Failed (srcpkg build): {len(srcpkgs_failed_build)}") + print(f"Failed (srcpkg upload): {len(srcpkgs_failed_upload)}") + + if not srcpkgs_failed_build and not srcpkgs_failed_upload: + exit(0) + + print() + print("List of failed packages:") + for package in srcpkgs_failed_build: + print(f"* {package} (srcpkg build)") + for package in srcpkgs_failed_upload: + print(f"* {package} (srcpkg upload)") + + exit(1) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate source packages and upload them to OBS.") + lib.add_shared_arguments(parser) + parser.add_argument("-A", "--apiurl", help="OBS API URL or .oscrc alias" + " (e.g. https://obs.osmocom.org)") + parser.add_argument("-n", "--no-skip-up-to-date", + dest="skip_up_to_date", action="store_false", + help="for latest feed, build and upload packages even" + " if the version did not change") + parser.add_argument("obs_project", + help="OBS project, e.g. home:osmith:nightly") + parser.add_argument("package", nargs="*", + help="package name, e.g. libosmocore or open5gs," + " default is all packages") + args = parser.parse_args() + proj = args.obs_project + feed = args.feed + packages = parse_packages(args.package) + + lib.set_cmds_verbose(args.verbose) + + if args.docker: + lib.docker.run_in_docker_and_exit(__file__, args, True) + + lib.osc.check_proj(proj) + lib.osc.set_apiurl(args.apiurl) + lib.check_required_programs() + lib.remove_temp() + + pkgs_remote = lib.osc.get_remote_pkgs(proj) + + build_srcpkgs(proj, feed, pkgs_remote, packages, args.conflict_version, + args.git_fetch, args.meta, args.skip_up_to_date) + upload_srcpkgs(proj, feed, pkgs_remote) + exit_with_summary() + + +if __name__ == "__main__": + main() diff --git a/scripts/osmocom-latest-packages.sh b/scripts/osmocom-latest-packages.sh deleted file mode 100755 index 9055f5cf..00000000 --- a/scripts/osmocom-latest-packages.sh +++ /dev/null @@ -1,247 +0,0 @@ -#!/bin/sh -# Generate source packages and upload them to OBS, for the latest feed. -# New packages are only uploaded if the source changed. -# Environment variables: -# * PROJ: the OBS namespace to upload to (e.g. "network:osmocom:latest") -# * FEED: -# * "latest": use latest tagged release (default) -# * other: use last commit of branch of same name, exit with error if it does -# not exist -# * PACKAGES: set to a space-separated list of packages to skip all others -. "$(dirname "$0")/common.sh" -. "$(dirname "$0")/common-obs.sh" - -# Values for FEED env var. Adjust FEEDS_ALL in common-obs when changing. -FEEDS=" - latest -" - -set -e -set -x - -DT=$(date +%Y%m%d%H%M) -TOP=$(pwd) -DEBSRCDIR="$TOP/debsrc" -FEED="${FEED:-latest}" - -### OBS build -prepare() { - # start with a checkout of the project - if [ -d $PROJ ]; then - (cd $PROJ && osc up) - else - osc co $PROJ - fi - [ -d "$DEBSRCDIR" ] && rm -rf "$DEBSRCDIR" - mkdir "$DEBSRCDIR" - - cd "$TOP" - osmo_obs_prepare_conflict -} - -get_last_tag() { - project="$1" - if [ "$project" = "limesuite" ] || [ "$project" = "open5gs" ]; then - ver_regexp="^v[0-9]*.[0-9]*.[0-9]*$" - else - ver_regexp="^[0-9]*.[0-9]*.[0-9]*$" - fi - VER=$(git -C "${TOP}/${project}" tag -l --sort=v:refname | grep "$ver_regexp" | tail -n 1) - echo "${VER}" -} - -checkout() { - path=$1 - project="$(basename $path)" - url=$2 - gitbpargs="" - - if osmo_obs_skip_pkg "$project"; then - return - fi - - if [ -z "$url" ]; then - url="$(osmo_git_clone_url "$path")" - fi - - echo - echo "====> Checking out $project" - cd "$TOP" - [ -d "$project" ] || osmo_git_clone_date "$url" "$project" - cd "$project" - git fetch - - if [ "$FEED" = "latest" ]; then - VER=$(get_last_tag "$project") - git checkout -f -B "$VER" "refs/tags/$VER" - else - git checkout -f -B "$FEED" "origin/$FEED" - fi - - if [ "$project" = "open5gs" ]; then - meson subprojects download freeDiameter - fi -} - -# Generate a source package and upload it to OBS -# $1: package name (e.g. "libosmocore") -# $2: arguments to pass to "gbp buildpackage" -build() { - project=$1 - gitbpargs="$2" - output="$DEBSRCDIR/$project" - - if osmo_obs_skip_pkg "$project"; then - return - fi - - echo - echo "====> Building $project" - cd "$TOP/$project" - - osmo_obs_git_version_gen - - if [ "$FEED" = "latest" ]; then - debian_branch=$(get_last_tag "$project") - else - debian_branch="$FEED" - # Set new debian changelog version with commit appended. This version will - # become part of resulting filenames, and will change if commits have been - # added to the feed's branch. - VER="$(osmo_obs_get_commit_version)" - dch -b -v "$VER" -m "Snapshot build" - git commit -m "$VER snapshot" debian/ - fi - - osmo_obs_add_depend_deb "./debian/control" "$project" "osmocom-$FEED" - - if [ "$project" = "open5gs" ]; then - # we cannot control the output directory of the generated source :( - dpkg-buildpackage -S -uc -us -d - mkdir -p "$output" - mv "../$name"*.tar* "../$name"*.dsc "$output" - elif [ -x ./git-version-gen ]; then - gbp buildpackage -S -uc -us -d --git-ignore-branch "--git-export-dir=$output" \ - "--git-debian-branch=$debian_branch" --git-ignore-new $gitbpargs \ - --git-postexport='cp $GBP_GIT_DIR/../.tarball-version $GBP_TMP_DIR/' - else - gbp buildpackage -S -uc -us -d --git-ignore-branch "--git-export-dir=$output" \ - "--git-debian-branch=$debian_branch" --git-ignore-new $gitbpargs - fi - - if [ ! -d "$TOP/$PROJ/$project" ] ; then - # creating a new package is different from using old ones - mkdir "$TOP/$PROJ/$project" - mv "$output/"*.dsc "$TOP/$PROJ/$project/" - mv "$output/"*.tar* "$TOP/$PROJ/$project/" - cd "$TOP/$PROJ" - osc add "$project" - else - cd "$TOP/$PROJ/$project" - - # update OBS only if the filename doesn't match - file=$(cd "$output/" ; ls ./*.dsc) - if [ ! -e "$file" ] ; then - osc rm ./* || true - mv "$output/"*.dsc . - mv "$output/"*.tar* . - osc add ./* - fi - fi - - osmo_obs_add_rpm_spec "$TOP/$PROJ/$project" "$TOP/$project" "$project" "osmocom-$FEED" - - cd "$TOP" -} - -# add those once they have tagged any versions that include the 'debian' sub-dir: -#rtl-sdr -#osmo-fl2k - -build_osmocom() { - prepare - - # NOTE: when adding a repository that is not in gerrit, adjust osmo_git_clone_url() - checkout limesuite https://github.com/myriadrf/LimeSuite - checkout osmo-gsm-manuals - checkout libosmocore - checkout libosmo-sccp - checkout libosmo-abis - checkout libosmo-netif - checkout libsmpp34 - checkout libasn1c - checkout libgtpnl - checkout libusrp - checkout osmo-iuh - checkout osmo-ggsn - checkout osmo-sgsn - checkout osmo-pcap - checkout osmo-trx - checkout osmo-sip-connector - checkout osmo-bts - checkout osmo-pcu - checkout osmo-hlr - checkout osmo-mgw - checkout osmo-msc - checkout osmo-bsc - checkout python/osmo-python-tests - checkout simtrace2 - checkout libosmo-dsp - checkout osmo-sysmon - checkout osmo-remsim - checkout open5gs https://github.com/open5gs/open5gs - checkout neocon https://github.com/laf0rge/neocon - checkout osmo-uecups - checkout osmo-e1d - checkout osmo-smlc - checkout osmo-cbc - checkout osmo-gbproxy - checkout osmo-hnodeb - checkout osmo-hnbgw - - cd "$TOP" - - build osmocom-$FEED - build limesuite --git-upstream-tree="$(get_last_tag limesuite)" - build osmo-gsm-manuals - build libosmocore - build libosmo-sccp - build libosmo-abis - build libosmo-netif - build libsmpp34 - build libasn1c - build libgtpnl - build libusrp - build osmo-iuh - build osmo-ggsn - build osmo-sgsn - build osmo-pcap - build osmo-trx - build osmo-sip-connector - build osmo-bts - build osmo-pcu - build osmo-hlr - build osmo-mgw - build osmo-msc - build osmo-bsc - build osmo-python-tests - build simtrace2 - build libosmo-dsp - build osmo-sysmon - build osmo-remsim - build open5gs - build neocon - build osmo-uecups - build osmo-e1d - build osmo-smlc - build osmo-cbc - build osmo-gbproxy - build osmo-hnodeb - build osmo-hnbgw - - cd "$TOP/$PROJ" - osc ci -m "$FEED versions of $DT" --noservice -} - -osmo_obs_verify_feed -build_osmocom diff --git a/scripts/osmocom-next-packages.sh b/scripts/osmocom-next-packages.sh deleted file mode 100755 index aa9ab902..00000000 --- a/scripts/osmocom-next-packages.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# Generate source packages and upload them to OBS, for the next feed. -. "$(dirname "$0")/common.sh" - -export FEED="next" -$OSMO_CI_DIR/scripts/osmocom-nightly-packages.sh diff --git a/scripts/osmocom-nightly-packages.sh b/scripts/osmocom-nightly-packages.sh deleted file mode 100755 index 426495e6..00000000 --- a/scripts/osmocom-nightly-packages.sh +++ /dev/null @@ -1,282 +0,0 @@ -#!/bin/bash -# Generate source packages and upload them to OBS, for the nightly or next feed. -# New packages are always uploaded, even if the source does not change. Only -# packages of the same build date (DT) can be installed together. -# Environment variables: -# * PROJ: the OBS namespace to upload to (e.g. "network:osmocom:nightly") -# * FEED: controls the source branch that is used: -# * "nightly": use "master" branch (default) -# * "next": use "next" branch if it exists, otherwise use "master" branch -# * other (e.g. "2021q1"): use last commit of branch of same name, exit with -# error if it does not exist -# * PACKAGES: set to a space-separated list of packages to skip all others -. "$(dirname "$0")/common.sh" -. "$(dirname "$0")/common-obs.sh" - -# Values for FEED env var. Adjust FEEDS_ALL in common-obs when changing. -FEEDS=" - 2021q1 - 2021q4 - 2022q1 - 2022q2 - next - nightly -" - -set -e -set -x - -DT=$(date +%Y%m%d%H%M) -OSMO_OBS_CONFLICT_PKGVER="$OSMO_OBS_CONFLICT_PKGVER.$DT" -TOP=$(pwd)/$(mktemp -d nightly-3g_XXXXXX) -FEED="${FEED:-nightly}" - -### OBS build -prepare() { - # clean up the whole space - mkdir -p "$REPO/osc/" - cd "$REPO/osc" - osc co "$PROJ" - - cd "$REPO" - osmo_obs_prepare_conflict -} - -get_last_tag() { - project="$1" - if [ "$project" = "limesuite" ]; then - ver_regexp="^v[0-9]*.[0-9]*.[0-9]*$" - else - ver_regexp="^[0-9]*.[0-9]*.[0-9]*$" - fi - VER=$(git -C "${REPO}/${project}" tag -l --sort=v:refname | grep "$ver_regexp" | tail -n 1) - echo "${VER}" -} - -### common -checkout() { - local name=$1 - local url=$2 - local branch=$3 - - if osmo_obs_skip_pkg "$name"; then - return - fi - - if [ -z "$url" ]; then - url="$(osmo_git_clone_url "$name")" - fi - - if [ -z "$branch" ]; then - case "$FEED" in - nightly|next) ;; - *) branch="$FEED" ;; # e.g. 2021q1 - esac - fi - - cd "$REPO" - - if [ -n "$branch" ] ; then - osmo_git_clone_date "$url" -b "$branch" - else - osmo_git_clone_date "$url" - fi - - if [ "$FEED" = "next" ] && git -C "$name" show-branch remotes/origin/next >/dev/null 2>&1; then - git -C "$name" checkout next - fi - - cd - -} - -# Generate a source package and upload it to OBS -# $1: package name (e.g. "libosmocore") -# $2: update debian dir, unless set to "no_commit": -# * add dependency on osmocom-$FEED package -# * add new version to changelog (e.g. "1.5.1.96.c96d7.202104281354") -# $3: arguments to pass to "gbp buildpackage" -build() { - local name=$1 - local changelog=$2 - local gitbpargs=$3 - local repodir=$REPO/$name - local oscdir=$REPO/osc/$PROJ/$name - local dependver="$OSMO_OBS_CONFLICT_PKGVER" - - if osmo_obs_skip_pkg "$name"; then - return - fi - - if [ -d "$oscdir" ] ; then - # remove earlier version - cd "$oscdir" - osc rm -- * || true - else - # new package - mkdir -p "$oscdir/" - cd "$REPO/osc/$PROJ/" - osc add "$name" - fi - - cd "$repodir" - - if [ "$changelog" != "no_commit" ] ; then - osmo_obs_git_version_gen - # Add date to increase version even if commit did not change (OS#5135) - VER="$(osmo_obs_get_commit_version).$DT" - osmo_obs_add_depend_deb "./debian/control" "$name" "osmocom-$FEED" "$dependver" - dch -b -v "$VER" -m "Snapshot build" - git commit -m "$VER snapshot" debian/ - fi - - mkdir -p "$DATA/$name" - if [ "$name" = "open5gs" ]; then - # we cannot control the output directory of the generated source :( - dpkg-buildpackage -S -uc -us -d - mv "../$name"*.tar* "../$name"*.dsc "$DATA/$name/" - elif [ -f .tarball-version ]; then - gbp buildpackage -S -uc -us -d --git-ignore-branch "--git-export-dir=$DATA/$name" \ - --git-ignore-new $gitbpargs \ - --git-postexport='cp $GBP_GIT_DIR/../.tarball-version $GBP_TMP_DIR/' - else - gbp buildpackage -S -uc -us -d --git-ignore-branch "--git-export-dir=$DATA/$name" \ - --git-ignore-new $gitbpargs - fi - - mv "$DATA/$name/"*.tar* "$DATA/$name/"*.dsc "$oscdir/" - - cd "$oscdir" - osc add -- *.tar* *.dsc - osmo_obs_add_rpm_spec "$oscdir" "$repodir" "$name" "osmocom-$FEED" "$dependver" - osc ci -m "Snapshot $name $DT" --noservice -} - -post() { - cd "$REPO/osc/$PROJ/" - osc status -} - -checkout_limesuite() { - if osmo_obs_skip_pkg "limesuite"; then - return - fi - - cd "$REPO" - git clone https://github.com/myriadrf/LimeSuite limesuite - TAG="$(get_last_tag limesuite)" - cd limesuite - git checkout "$TAG" -} - -checkout_open5gs() { - if osmo_obs_skip_pkg "open5gs"; then - return - fi - - cd "$REPO" - git clone https://github.com/open5gs/open5gs - cd open5gs - meson subprojects download freeDiameter -} - -build_osmocom() { - DATA=$TOP/data - REPO=$TOP/repo - - # rather than including a dangerous 'rm -rf *' here, lets delegate to the user: - if [ -n "$(ls $TOP)" ]; then - echo "ERROR: I need to run in an empty directory." - exit 1 - fi - - prepare - - # NOTE: when adding a repository that is not in gerrit, adjust osmo_git_clone_url() - checkout_limesuite - checkout osmo-gsm-manuals - checkout libosmocore - checkout libosmo-sccp - checkout libosmo-abis - checkout libosmo-netif - checkout libsmpp34 - checkout libasn1c - checkout libgtpnl - checkout libusrp - checkout osmo-iuh - checkout osmo-ggsn - checkout osmo-sgsn - checkout osmo-pcap - checkout osmo-trx - checkout osmo-sip-connector - checkout osmo-bts - checkout osmo-pcu - checkout osmo-hlr - checkout osmo-mgw - checkout osmo-msc - checkout osmo-bsc - checkout python/osmo-python-tests - checkout rtl-sdr - checkout osmo-fl2k - checkout simtrace2 - checkout libosmo-dsp - checkout osmo-sysmon - checkout osmo-remsim - checkout_open5gs - checkout neocon https://github.com/laf0rge/neocon - checkout osmo-uecups - checkout osmo-e1d - checkout osmo-smlc - checkout osmo-cbc - checkout osmo-gbproxy - checkout osmo-hnodeb - checkout osmo-hnbgw - checkout osmo-bsc-nat - - cd "$REPO" - - build osmocom-$FEED no_commit - build limesuite no_commit --git-upstream-tree="$(get_last_tag limesuite)" - build osmo-gsm-manuals - build libosmocore - build libosmo-sccp - build libosmo-abis - build libosmo-netif - build libsmpp34 - build libasn1c - build libgtpnl - build libusrp - build osmo-iuh - build osmo-ggsn - build osmo-sgsn - build osmo-pcap - build osmo-trx - build osmo-sip-connector - build osmo-bts - build osmo-pcu - build osmo-hlr - build osmo-mgw - build osmo-msc - build osmo-bsc - build osmo-python-tests - build rtl-sdr - build osmo-fl2k - build simtrace2 - build libosmo-dsp - build osmo-sysmon - build osmo-remsim - build open5gs - build neocon - build osmo-uecups - build osmo-e1d - build osmo-smlc - build osmo-cbc - build osmo-gbproxy - build osmo-hnodeb - build osmo-hnbgw - build osmo-bsc-nat - - post -} - -osmo_obs_verify_feed -build_osmocom