From 634946285bd3f1c2997e14467ee7756b2141e0c8 Mon Sep 17 00:00:00 2001 From: Alya Sirko Date: Sun, 4 Sep 2022 09:30:24 +0300 Subject: [PATCH 1/2] Ops: add Drone CI pipelines (debug and release) and Flakes-powered Nix shell environment (#110) Co-authored-by: Alya Sirko Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/110 Co-authored-by: Alya Sirko Co-committed-by: Alya Sirko --- .drone.yml | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.lock | 26 ++++++++++++ flake.nix | 22 ++++++++++ 3 files changed, 163 insertions(+) create mode 100644 .drone.yml create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 00000000..eedcec9b --- /dev/null +++ b/.drone.yml @@ -0,0 +1,115 @@ +kind: pipeline +type: exec +name: Continuous Integration + +steps: + - name: Build Debug Artifacts + commands: + - flutter build apk --debug --split-per-abi + - mv build/app/outputs/flutter-apk/*-debug.apk . + - rename app pro.kherel.selfprivacy *.apk && rename debug "$DRONE_COMMIT" *.apk + - ls *.apk + +trigger: + event: + - push + - pull_request + +node: + server: builder + +--- + +kind: pipeline +type: exec +name: Release + +steps: + - name: Prepare for Build + commands: + # Reset building environment + - nixos-container stop isolated + - nixos-container start isolated + # Prepare SSH keys + - eval `ssh-agent -s` + - echo "$SSH_PRIVATE_KEY" | ssh-add - + # Copy sources to the building environment + - scp -r `pwd` builder@isolated:~ + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + + - name: Build Intermediate Release Artifact + commands: + # Prepare SSH keys + - eval `ssh-agent -s` + - echo "$SSH_PRIVATE_KEY" | ssh-add - + # Build intermediate release artifact + - ssh builder@isolated "cd src && flutter build apk --release" + # Fetch the release artifact + - scp builder@isolated:src/build/app/outputs/flutter-apk/app-release.apk . + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + + - name: Sign Release Artifact for Standalone Use + commands: + # Get app build ID + - export APP_BUILD_ID=`yq '.version' pubspec.yaml | cut -d "+" -f2` + # Prepare SSH keys + - eval `ssh-agent -s` + - echo "$SSH_PRIVATE_KEY" | ssh-add - + # Upload and sign the artifact + - scp app-release.apk builder@isolated:~ + - ssh builder@isolated "zipalign -f -v 4 app-release.apk standalone_app-release.apk && apksigner sign --ks /run/secrets/standalone-keystore --ks-key-alias standalone --ks-pass file:/run/secrets/standalone-keystore-pass standalone_app-release.apk" + # Fetch the signed artifact + - scp builder@isolated:standalone_app-release.apk standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk + - scp builder@isolated:standalone_app-release.apk.idsig standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk.idsig + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + + - name: Sign Release Artifact for F-Droid Repository + commands: + # Get app build ID + - export APP_BUILD_ID=`yq '.version' pubspec.yaml | cut -d "+" -f2` + # Prepare SSH keys + - eval `ssh-agent -s` + - echo "$SSH_PRIVATE_KEY" | ssh-add - + # Upload and sign the artifact + - scp app-release.apk fdroid@isolated:unsigned/pro.kherel.selfprivacy_"$APP_BUILD_ID".apk + - ssh fdroid@isolated 'export FDROID_KEY_STORE_PASS=`cat /run/secrets/fdroid-keystore-pass` && fdroid publish && fdroid update' + - scp -r fdroid@isolated:repo . + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + + - name: Create Release on Gitea Repository + commands: + # Get app build ID + - export APP_BUILD_ID=`yq '.version' pubspec.yaml | cut -d "+" -f2` + # Prepare tea CLI + - tea login add --token "$GITEA_RELEASE_TOKEN" --url https://git.selfprivacy.org + # Create release and push artifacts + - tea releases create --repo "$DRONE_REPO" --tag "$DRONE_SEMVER" --title "$DRONE_SEMVER" --asset standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk --asset standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk.idsig + environment: + GITEA_RELEASE_TOKEN: + from_secret: GITEA_RELEASE_TOKEN + + - name: Deploy F-Droid Repository + commands: + # Prepare SSH keys + - eval `ssh-agent -s` + - echo "$SSH_PRIVATE_KEY" | ssh-add - + # Copy the repository to the production server + - scp -r repo/* deployer@production:/var/www/fdroid.selfprivacy.org + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + +trigger: + event: + - tag + +node: + server: builder diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..ca75926d --- /dev/null +++ b/flake.lock @@ -0,0 +1,26 @@ +{ + "nodes": { + "nixpkgs-unstable": { + "locked": { + "lastModified": 1662019588, + "narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2da64a81275b68fdad38af669afeda43d401e94b", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs-unstable": "nixpkgs-unstable" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..ed8dad75 --- /dev/null +++ b/flake.nix @@ -0,0 +1,22 @@ +{ + inputs.nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs-unstable }: + let + pkgs = import nixpkgs-unstable { config.allowUnfree = true; config.android_sdk.accept_license = true; }; + androidComposition = pkgs.androidenv.composeAndroidPackages { + toolsVersion = "26.1.1"; + platformToolsVersion = "33.0.2"; + buildToolsVersions = [ "30.0.3" ]; + platformVersions = [ "31" "30" "29" ]; + }; + in { + devShell.x86_64-linux = pkgs.mkShell { + JAVA_HOME = "${pkgs.openjdk11_headless.home}"; + ANDROID_HOME = "${androidComposition.androidsdk}/libexec/android-sdk"; + ANDROID_SDK_ROOT = "${androidComposition.androidsdk}/libexec/android-sdk"; + + buildInputs = with pkgs; [ bash git androidComposition.androidsdk flutter openjdk11_headless ]; + }; + }; +} From 78f6dff028e4603f110edfb43527864d5fe48c6b Mon Sep 17 00:00:00 2001 From: Alya Sirko Date: Sat, 10 Sep 2022 18:10:27 +0300 Subject: [PATCH 2/2] Ops: refactor CI/CD pipeline, isolated Podman environment, various Linux builds (#112) - New Podman-based building environment - caches everything - can build artifacts without the Internet - rootless, needs no system capabilities (only when uses FUSE, well) - the cost is it's VERY HUGE, but useful - Refactored CI/CD pipeline - now it's a Python script, not a bunch of shell commands - optional nix flake for development environment and building a derivation - uploads all artifacts as a Gitea release - New targets - AppImage bundle - Flatpak bundle - simple archive with binaries Co-authored-by: Alya Sirko Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/112 Reviewed-by: Inex Code Co-authored-by: Alya Sirko Co-committed-by: Alya Sirko --- .drone.yml | 141 +++++++++++++++++++----------------- Dockerfile | 48 ++++++++++++ appimage.yml | 53 ++++++++++++++ assets/images/icon/logo.svg | 9 +++ ci.py | 139 +++++++++++++++++++++++++++++++++++ flake.lock | 78 ++++++++++++++++++-- flake.nix | 136 +++++++++++++++++++++++++++++----- flatpak.yml | 46 ++++++++++++ 8 files changed, 560 insertions(+), 90 deletions(-) create mode 100644 Dockerfile create mode 100644 appimage.yml create mode 100644 assets/images/icon/logo.svg create mode 100755 ci.py create mode 100644 flatpak.yml diff --git a/.drone.yml b/.drone.yml index eedcec9b..13ec2c42 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,12 +3,13 @@ type: exec name: Continuous Integration steps: - - name: Build Debug Artifacts + - name: Prepare commands: - - flutter build apk --debug --split-per-abi - - mv build/app/outputs/flutter-apk/*-debug.apk . - - rename app pro.kherel.selfprivacy *.apk && rename debug "$DRONE_COMMIT" *.apk - - ls *.apk + - ln -s /var/lib/drone-runner-exec/.local $HOME/.local + + - name: Build + commands: + - ./ci.py --run-ci-build trigger: event: @@ -25,84 +26,89 @@ type: exec name: Release steps: - - name: Prepare for Build + - name: Prepare commands: - # Reset building environment - - nixos-container stop isolated - - nixos-container start isolated - # Prepare SSH keys - - eval `ssh-agent -s` - - echo "$SSH_PRIVATE_KEY" | ssh-add - - # Copy sources to the building environment - - scp -r `pwd` builder@isolated:~ - environment: - SSH_PRIVATE_KEY: - from_secret: SSH_PRIVATE_KEY + - ln -s /var/lib/drone-runner-exec/.local $HOME/.local + - if podman volume exists src; then podman volume rm -f src; podman volume create src; else podman volume create src; fi + - git archive --format=tar HEAD | podman volume import src - - - name: Build Intermediate Release Artifact + - name: Build Intermediate Linux Release Artifact (Binary) commands: - # Prepare SSH keys - - eval `ssh-agent -s` - - echo "$SSH_PRIVATE_KEY" | ssh-add - - # Build intermediate release artifact - - ssh builder@isolated "cd src && flutter build apk --release" - # Fetch the release artifact - - scp builder@isolated:src/build/app/outputs/flutter-apk/app-release.apk . + - ./ci.py --build-linux environment: - SSH_PRIVATE_KEY: - from_secret: SSH_PRIVATE_KEY + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS - - name: Sign Release Artifact for Standalone Use + - name: Build Intermediate Android Release Artifact (.APK) commands: - # Get app build ID - - export APP_BUILD_ID=`yq '.version' pubspec.yaml | cut -d "+" -f2` - # Prepare SSH keys - - eval `ssh-agent -s` - - echo "$SSH_PRIVATE_KEY" | ssh-add - - # Upload and sign the artifact - - scp app-release.apk builder@isolated:~ - - ssh builder@isolated "zipalign -f -v 4 app-release.apk standalone_app-release.apk && apksigner sign --ks /run/secrets/standalone-keystore --ks-key-alias standalone --ks-pass file:/run/secrets/standalone-keystore-pass standalone_app-release.apk" - # Fetch the signed artifact - - scp builder@isolated:standalone_app-release.apk standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk - - scp builder@isolated:standalone_app-release.apk.idsig standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk.idsig + - ./ci.py --build-apk environment: - SSH_PRIVATE_KEY: - from_secret: SSH_PRIVATE_KEY + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS - - name: Sign Release Artifact for F-Droid Repository + - name: Sign Android Release Artifact (.APK) for Standalone Use commands: - # Get app build ID - - export APP_BUILD_ID=`yq '.version' pubspec.yaml | cut -d "+" -f2` - # Prepare SSH keys - - eval `ssh-agent -s` - - echo "$SSH_PRIVATE_KEY" | ssh-add - - # Upload and sign the artifact - - scp app-release.apk fdroid@isolated:unsigned/pro.kherel.selfprivacy_"$APP_BUILD_ID".apk - - ssh fdroid@isolated 'export FDROID_KEY_STORE_PASS=`cat /run/secrets/fdroid-keystore-pass` && fdroid publish && fdroid update' - - scp -r fdroid@isolated:repo . + - ./ci.py --sign-apk-standalone environment: - SSH_PRIVATE_KEY: - from_secret: SSH_PRIVATE_KEY + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS - - name: Create Release on Gitea Repository + - name: Sign Android Release Artifact (.APK) for F-Droid Repository commands: - # Get app build ID - - export APP_BUILD_ID=`yq '.version' pubspec.yaml | cut -d "+" -f2` - # Prepare tea CLI - - tea login add --token "$GITEA_RELEASE_TOKEN" --url https://git.selfprivacy.org - # Create release and push artifacts - - tea releases create --repo "$DRONE_REPO" --tag "$DRONE_SEMVER" --title "$DRONE_SEMVER" --asset standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk --asset standalone_pro.kherel.selfprivacy_"$APP_BUILD_ID".apk.idsig + - ./ci.py --sign-apk-fdroid + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + + - name: Package Linux AppImage Artifact + commands: + - ./ci.py --package-linux-appimage + + - name: Package Linux Flatpak Artifact + commands: + - ./ci.py --package-linux-flatpak + + - name: Package Linux Archive Artifact + commands: + - ./ci.py --package-linux-archive + +trigger: + event: + - tag + +node: + server: builder + +--- + +kind: pipeline +type: exec +name: Deploy + +steps: + - name: Prepare + commands: + - ln -s /var/lib/drone-runner-exec/.local $HOME/.local + - podman unshare podman volume mount src + + - name: Create Release and Deploy Artifacts + commands: + - podman unshare ./ci.py --deploy-gitea-release environment: GITEA_RELEASE_TOKEN: from_secret: GITEA_RELEASE_TOKEN - - name: Deploy F-Droid Repository + - name: Deploy F-Droid Repo commands: - # Prepare SSH keys - - eval `ssh-agent -s` - - echo "$SSH_PRIVATE_KEY" | ssh-add - - # Copy the repository to the production server - - scp -r repo/* deployer@production:/var/www/fdroid.selfprivacy.org + - podman unshare ./ci.py --deploy-fdroid-repo environment: SSH_PRIVATE_KEY: from_secret: SSH_PRIVATE_KEY @@ -113,3 +119,6 @@ trigger: node: server: builder + +depends_on: + - Release diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..7efdf1ed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,48 @@ +FROM ubuntu:22.04 + +ENV PACKAGES "build-essential openjdk-11-jdk-headless clang cmake curl git jq libblkid1 libblkid-dev libc6 libc6-dev libc-bin libcrypt1 libdbus-1-3 libexpat1 libffi7 libgcc-s1 libgcrypt20 libgcrypt20-dev libglib2.0-0 libglib2.0-dev libglu1-mesa libgpg-error0 libgtk-3-0 libgtk-3-dev liblz4-1 liblz4-dev liblzma5 liblzma-dev libmount1 libpcre3 libselinux1 libsepol2 libstdc++-10-dev libstdc++6 libuuid1 ninja-build pkg-config rsync unzip xz-utils zlib1g unzip libsecret-1-dev libsecret-tools libsecret-1-0 libjsoncpp-dev fuse flatpak-builder binutils coreutils desktop-file-utils fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools strace util-linux zsync" +ENV ANDROID_SDK_TOOLS_VERSION "commandlinetools-linux-8512546_latest" +ENV ANDROID_SDK_TOOLS_URL "https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip" +ENV FLUTTER_VERSION "flutter_linux_3.3.1-stable" +ENV FLUTTER_URL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.3.1-stable.tar.xz" +ENV FREEDESKTOP_SDK_VERSION "22.08" + +# Update packages +RUN apt-get update && apt-get upgrade -y && apt-get install -y $PACKAGES + +# Add a non-privileged user +RUN useradd -d /var/lib/builder -m -r -s /bin/bash builder +USER builder +WORKDIR /var/lib/builder + +# Install Android SDK +ADD --chown=builder $ANDROID_SDK_TOOLS_URL . +RUN mkdir -p android-sdk/cmdline-tools && unzip $ANDROID_SDK_TOOLS_VERSION.zip \ + && rm $ANDROID_SDK_TOOLS_VERSION.zip && mv cmdline-tools android-sdk/cmdline-tools/latest + +# Install Flutter +ADD --chown=builder $FLUTTER_URL . +RUN tar -vxf $FLUTTER_VERSION.tar.xz && rm $FLUTTER_VERSION.tar.xz + +ENV ANDROID_HOME "/var/lib/builder/android-sdk" +ENV ANDROID_SDK_ROOT "/var/lib/builder/android-sdk" +ENV PATH "$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:/var/lib/builder/flutter/bin:/var/lib/builder/.local/bin" + +# Install needed Android SDK packages +RUN yes | sdkmanager 'build-tools;30.0.3' 'platforms;android-29' 'platforms;android-30' 'platforms;android-31' + +# Prepare dependencies for offline build +RUN git clone --depth=1 --single-branch https://git.selfprivacy.org/kherel/selfprivacy.org.app.git deps +WORKDIR /var/lib/builder/deps +RUN flutter build linux +RUN flutter build apk +WORKDIR /var/lib/builder +RUN rm -rf deps + +# Install AppImage Builder +RUN pip3 install --user appimage-builder + +# Install Flatpak dependencies +RUN flatpak --user remote-add flathub https://flathub.org/repo/flathub.flatpakrepo \ + && flatpak --user install -y org.freedesktop.Sdk/x86_64/$FREEDESKTOP_SDK_VERSION \ + org.freedesktop.Platform/x86_64/$FREEDESKTOP_SDK_VERSION diff --git a/appimage.yml b/appimage.yml new file mode 100644 index 00000000..8e7a3b5c --- /dev/null +++ b/appimage.yml @@ -0,0 +1,53 @@ +# appimage-builder recipe see https://appimage-builder.readthedocs.io for details +version: 1 +script: + - rm -rf AppDir || true + - cp -r build/linux/x64/release/bundle AppDir + - install -Dm644 assets/images/icon/logo.svg AppDir/usr/share/icons/hicolor/scalable/apps/pro.kherel.selfprivacy.svg +AppDir: + path: AppDir + app_info: + id: pro.kherel.selfprivacy + name: SelfPrivacy + icon: pro.kherel.selfprivacy + version: 0.6.0 + exec: selfprivacy + exec_args: $@ + apt: + arch: + - amd64 + allow_unauthenticated: true + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse + - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted + - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security universe + - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security multiverse + include: + - libjsoncpp-dev + - libsecret-1-0 + - xdg-desktop-portal + test: + fedora-30: + image: appimagecrafters/tests-env:fedora-30 + command: ./AppRun + debian-stable: + image: appimagecrafters/tests-env:debian-stable + command: ./AppRun + archlinux-latest: + image: appimagecrafters/tests-env:archlinux-latest + command: ./AppRun + centos-7: + image: appimagecrafters/tests-env:centos-7 + command: ./AppRun + ubuntu-xenial: + image: appimagecrafters/tests-env:ubuntu-xenial + command: ./AppRun +AppImage: + arch: x86_64 + update-information: guess diff --git a/assets/images/icon/logo.svg b/assets/images/icon/logo.svg new file mode 100644 index 00000000..881a57fe --- /dev/null +++ b/assets/images/icon/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ci.py b/ci.py new file mode 100755 index 00000000..d8412580 --- /dev/null +++ b/ci.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import yaml +import argparse + +CONTAINER_IMAGE = "localhost/flutter-build-env" +HOST_HOME = "/var/lib/drone-runner-exec" +CONTAINER_HOME = "/var/lib/builder" + +APP_NAME = "pro.kherel.selfprivacy" +APP_VERSION_FULL = yaml.safe_load(open("pubspec.yaml", "r"))['version'] +APP_SEMVER = APP_VERSION_FULL[:APP_VERSION_FULL.find("+")] +APP_BUILD_ID = APP_VERSION_FULL[APP_VERSION_FULL.find("+"):][1::] + +HOST_MOUNTED_VOLUME = f"{HOST_HOME}/.local/share/containers/storage/volumes/src/_data" + +# Environments + +def podman_offline(dir, *args): + subprocess.run(["podman", "run", "--rm", "--network=none", f"--workdir={dir}", + "-v", f"src:{CONTAINER_HOME}/src:U", + "-v", f"{HOST_HOME}/fdroid:{CONTAINER_HOME}/fdroid:U", + "-v", f"{HOST_HOME}/fdroid-keystore:{CONTAINER_HOME}/fdroid/fdroid-keystore:U", + "-v", f"{HOST_HOME}/standalone-keystore:{CONTAINER_HOME}/fdroid/standalone-keystore:U", + "--env", "FDROID_KEYSTORE_PASS=" + os.environ.get('FDROID_KEYSTORE_PASS'), + "--env", "STANDALONE_KEYSTORE_PASS=" + os.environ.get('STANDALONE_KEYSTORE_PASS'), + CONTAINER_IMAGE, "bash", "-c", ' '.join(args) + ]) + +def podman_online(dir, *args): + subprocess.run(["podman", "run", "--rm", "--privileged", f"--workdir={dir}", + "-v", f"src:{CONTAINER_HOME}/src:U", + CONTAINER_IMAGE, "bash", "-c", ' '.join(args) + ]) + +def podman_ci(dir, *args): + subprocess.run(["podman", "run", "--rm", "--privileged", f"--workdir={dir}", + "-v", os.getcwd() + f":{CONTAINER_HOME}/src:U", + CONTAINER_IMAGE, "bash", "-c", ' '.join(args) + ]) + +# Targets + +def build_linux(): + podman_offline(f"{CONTAINER_HOME}/src", "flutter pub get --offline") + podman_offline(f"{CONTAINER_HOME}/src", "flutter build linux") + +def build_apk(): + podman_offline(f"{CONTAINER_HOME}/src", "flutter pub get --offline") + podman_offline(f"{CONTAINER_HOME}/src", "flutter build apk") + +def sign_apk_standalone(): + podman_offline(f"{CONTAINER_HOME}/fdroid", + "zipalign -f -v 4 ../src/build/app/outputs/flutter-apk/app-release.apk", + f"standalone_{APP_NAME}-{APP_SEMVER}.apk") + podman_offline(f"{CONTAINER_HOME}/fdroid", + "apksigner sign --ks standalone-keystore --ks-key-alias standalone --ks-pass", + f"env:STANDALONE_KEYSTORE_PASS standalone_{APP_NAME}-{APP_SEMVER}.apk") + +def sign_apk_fdroid(): + podman_offline(f"{CONTAINER_HOME}/fdroid", f"rm -rf {CONTAINER_HOME}/fdroid/unsigned/*") + podman_offline(f"{CONTAINER_HOME}/fdroid", + f"test ! -f {CONTAINER_HOME}/fdroid/repo/{APP_NAME}_{APP_BUILD_ID}.apk", + "&& cp ../src/build/app/outputs/flutter-apk/app-release.apk", + f"unsigned/{APP_NAME}_{APP_BUILD_ID}.apk || echo exist") + podman_offline(f"{CONTAINER_HOME}/fdroid", "fdroid publish") + podman_offline(f"{CONTAINER_HOME}/fdroid", "fdroid update") + +def package_linux_appimage(): + podman_online(f"{CONTAINER_HOME}/src", "appimage-builder --recipe appimage.yml") + +def package_linux_flatpak(): + podman_online(f"{CONTAINER_HOME}/src", "flatpak-builder --disable-rofiles-fuse --force-clean --repo=flatpak-repo flatpak-build flatpak.yml") + podman_online(f"{CONTAINER_HOME}/src", f"flatpak build-bundle flatpak-repo {APP_NAME}-{APP_SEMVER}.flatpak pro.kherel.selfprivacy") + +def package_linux_archive(): + podman_online(f"{CONTAINER_HOME}/src", f"tar -C build/linux/x64/release/bundle -vacf {APP_NAME}-{APP_SEMVER}.tar.zstd .") + +def deploy_gitea_release(): + subprocess.run(["tea", "login", "add", "--token", os.environ.get('GITEA_RELEASE_TOKEN'), + "--url", "https://git.selfprivacy.org"]) + subprocess.run(["tea", "releases", "create", "--repo", os.environ.get('DRONE_REPO'), + "--tag", os.environ.get('DRONE_SEMVER'), "--title", os.environ.get('DRONE_SEMVER'), + "--asset", f"{HOST_HOME}/fdroid/standalone_{APP_NAME}-{APP_SEMVER}.apk", + "--asset", f"{HOST_HOME}/fdroid/standalone_{APP_NAME}-{APP_SEMVER}.apk.idsig", + "--asset", f"{HOST_MOUNTED_VOLUME}/SelfPrivacy-{APP_SEMVER}-x86_64.AppImage", + "--asset", f"{HOST_MOUNTED_VOLUME}/SelfPrivacy-{APP_SEMVER}-x86_64.AppImage.zsync", + "--asset", f"{HOST_MOUNTED_VOLUME}/{APP_NAME}-{APP_SEMVER}.flatpak", + "--asset", f"{HOST_MOUNTED_VOLUME}/{APP_NAME}-{APP_SEMVER}.tar.zstd"]) + +def deploy_fdroid_repo(): + subprocess.run([f"""eval $(ssh-agent -s) && + echo \"$SSH_PRIVATE_KEY\" | tr -d '\r' | ssh-add - && + scp -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -r {HOST_HOME}/fdroid/repo/* deployer@selfprivacy.org:/var/www/fdroid.selfprivacy.org + """], shell=True) + +def run_ci_build(): + podman_ci(f"{CONTAINER_HOME}/src", "flutter build linux --debug") + podman_ci(f"{CONTAINER_HOME}/src", "flutter build apk --debug") + +# Arguments + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group() + group.add_argument("--build-linux", action="store_true", help="depends on podman src volume") + group.add_argument("--build-apk", action="store_true", help="depends on podman src volume") + group.add_argument("--sign-apk-standalone", action="store_true", help="depends on $STANDALONE_KEYSTORE_PASS") + group.add_argument("--sign-apk-fdroid", action="store_true", help="depends on $FDROID_KEYSTORE_PASS") + group.add_argument("--package-linux-appimage", action="store_true") + group.add_argument("--package-linux-flatpak", action="store_true") + group.add_argument("--package-linux-archive", action="store_true") + group.add_argument("--deploy-gitea-release", action="store_true", help="depends on $GITEA_RELEASE_TOKEN") + group.add_argument("--deploy-fdroid-repo", action="store_true", help="depends on $SSH_PRIVATE_KEY") + group.add_argument("--run-ci-build", action="store_true") + args = parser.parse_args() + +if args.build_linux: + build_linux() +elif args.build_apk: + build_apk() +elif args.sign_apk_standalone: + sign_apk_standalone() +elif args.sign_apk_fdroid: + sign_apk_fdroid() +elif args.package_linux_appimage: + package_linux_appimage() +elif args.package_linux_flatpak: + package_linux_flatpak() +elif args.package_linux_archive: + package_linux_archive() +elif args.deploy_gitea_release: + deploy_gitea_release() +elif args.deploy_fdroid_repo: + deploy_fdroid_repo() +elif args.run_ci_build: + run_ci_build() diff --git a/flake.lock b/flake.lock index ca75926d..cfd8ecfe 100644 --- a/flake.lock +++ b/flake.lock @@ -1,23 +1,89 @@ { "nodes": { - "nixpkgs-unstable": { + "flake-utils": { "locked": { - "lastModified": 1662019588, - "narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=", + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixgl": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1661367362, + "narHash": "sha256-Qc8MXcV+YCPREu8kk6oggk23ZBKLqeQRAIsLbHEviPE=", + "owner": "guibou", + "repo": "nixGL", + "rev": "7165ffbccbd2cf4379b6cd6d2edd1620a427e5ae", + "type": "github" + }, + "original": { + "owner": "guibou", + "repo": "nixGL", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1660551188, + "narHash": "sha256-a1LARMMYQ8DPx1BgoI/UN4bXe12hhZkCNqdxNi6uS0g=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "441dc5d512153039f19ef198e662e4f3dbb9fd65", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1662096612, + "narHash": "sha256-R+Q8l5JuyJryRPdiIaYpO5O3A55rT+/pItBrKcy7LM4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2da64a81275b68fdad38af669afeda43d401e94b", + "rev": "21de2b973f9fee595a7a1ac4693efff791245c34", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-unstable", + "ref": "nixpkgs-unstable", "type": "indirect" } }, "root": { "inputs": { - "nixpkgs-unstable": "nixpkgs-unstable" + "flake-utils": "flake-utils", + "nixgl": "nixgl", + "nixpkgs": "nixpkgs_2" } } }, diff --git a/flake.nix b/flake.nix index ed8dad75..d1895b0a 100644 --- a/flake.nix +++ b/flake.nix @@ -1,22 +1,122 @@ { - inputs.nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; + nixConfig.bash-prompt = "\[selfprivacy\]$ "; - outputs = { self, nixpkgs-unstable }: - let - pkgs = import nixpkgs-unstable { config.allowUnfree = true; config.android_sdk.accept_license = true; }; - androidComposition = pkgs.androidenv.composeAndroidPackages { - toolsVersion = "26.1.1"; - platformToolsVersion = "33.0.2"; - buildToolsVersions = [ "30.0.3" ]; - platformVersions = [ "31" "30" "29" ]; - }; - in { - devShell.x86_64-linux = pkgs.mkShell { - JAVA_HOME = "${pkgs.openjdk11_headless.home}"; - ANDROID_HOME = "${androidComposition.androidsdk}/libexec/android-sdk"; - ANDROID_SDK_ROOT = "${androidComposition.androidsdk}/libexec/android-sdk"; + inputs.nixpkgs.url = "nixpkgs/nixpkgs-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.nixgl.url = "github:guibou/nixGL"; - buildInputs = with pkgs; [ bash git androidComposition.androidsdk flutter openjdk11_headless ]; - }; - }; + outputs = { self, nixpkgs, flake-utils, nixgl }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + config.allowUnfree = true; + config.android_sdk.accept_license = true; + system = "x86_64-linux"; + overlays = [ nixgl.overlay ]; + }; + + androidComposition = pkgs.androidenv.composeAndroidPackages { + toolsVersion = "26.1.1"; + platformToolsVersion = "33.0.2"; + buildToolsVersions = [ "30.0.3" ]; + platformVersions = [ "31" "30" "29" ]; + }; + + buildDeps = with pkgs; [ + gtk3 + glib + pcre + util-linux + libselinux + libsepol + libthai + libdatrie + xorg.libXdmcp + xorg.libXtst + libxkbcommon + dbus + at-spi2-core + libsecret + jsoncpp + xorg.libX11 + libepoxy + libgcrypt + libgpg-error + ]; + + nativeBuildDeps = with pkgs; [ + flutter.unwrapped + bash + curl + flutter.dart + git + unzip + which + xz + cmake + ninja + pkg-config + wrapGAppsHook + autoPatchelfHook + androidComposition.androidsdk + openjdk11_headless + clang + ]; + + releaseDerivation = pkgs.flutter.mkFlutterApp rec { + pname = "selfprivacy"; + version = "0.6.0"; + + vendorHash = "sha256-7cbiAyIlaz3HqEsZN/nZxaLZjseJv5CmiIHqsoGa4ZI="; + + nativeBuildInputs = [ pkgs.nixgl.auto.nixGLDefault ]; + + src = ./.; + + desktopItem = pkgs.makeDesktopItem { + name = "${pname}"; + exec = "@out@/bin/${pname}"; + desktopName = "SelfPrivacy"; + }; + + postInstall = '' + rm $out/bin/$pname + + printf "#!/bin/sh\n${pkgs.nixgl.auto.nixGLDefault}/bin/nixGL $out/app/${pname}" > $out/bin/$pname + patchShebangs $out/bin/$pname + chmod +x $out/bin/$pname + wrapProgram $out/bin/$pname --set PATH ${pkgs.lib.makeBinPath [ pkgs.xdg-user-dirs ]} + + mkdir -p $out/share/applications + cp $desktopItem/share/applications/*.desktop $out/share/applications + substituteInPlace $out/share/applications/*.desktop --subst-var out + ''; + }; + in + { + packages = { + release = releaseDerivation; + }; + defaultPackage = releaseDerivation; + + devShell = pkgs.mkShell { + buildInputs = buildDeps; + nativeBuildInputs = nativeBuildDeps; + + JAVA_HOME = "${pkgs.openjdk11_headless.home}"; + ANDROID_HOME = "${androidComposition.androidsdk}/libexec/android-sdk"; + ANDROID_SDK_ROOT = "${androidComposition.androidsdk}/libexec/android-sdk"; + + NIX_LDFLAGS = "-rpath ${pkgs.lib.makeLibraryPath buildDeps}"; + NIX_CFLAGS_COMPILE = "-I${pkgs.xorg.libX11}/include"; + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildDeps; + + shellHook = '' + export TMP=$(mktemp -d) + export HOME="$TMP" + export PUB_CACHE=''${PUB_CACHE:-"$HOME/.pub-cache"} + export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1 + ''; + }; + }); } diff --git a/flatpak.yml b/flatpak.yml new file mode 100644 index 00000000..6808b84e --- /dev/null +++ b/flatpak.yml @@ -0,0 +1,46 @@ +app-id: pro.kherel.selfprivacy +runtime: org.freedesktop.Platform +runtime-version: '22.08' +sdk: org.freedesktop.Sdk +command: selfprivacy +finish-args: + - "--share=ipc" + - "--socket=x11" + - "--socket=fallback-x11" + - "--socket=wayland" + - "--share=network" + - "--own-name=pro.kherel.selfprivacy" +modules: + - name: selfprivacy + buildsystem: simple + build-commands: + - cp -r * /app + - mkdir /app/bin + - ln -s /app/selfprivacy /app/bin/selfprivacy + - install -Dm644 logo.svg /app/share/icons/hicolor/scalable/apps/pro.kherel.selfprivacy.svg + - install -Dm644 pro.kherel.selfprivacy.desktop /app/share/applications/pro.kherel.selfprivacy.desktop + sources: + - type: dir + path: build/linux/x64/release/bundle + - type: file + path: assets/images/icon/logo.svg + - type: file + path: pro.kherel.selfprivacy.desktop + - name: libsecret + buildsystem: meson + config-opts: + - "-Dvapi=false" + - "-Dgtk_doc=false" + sources: + - type: git + url: https://gitlab.gnome.org/GNOME/libsecret.git + tag: 0.20.5 + - name: libjsoncpp + buildsystem: meson + config-opts: + - "--buildtype=release" + - "--default-library=shared" + sources: + - type: git + url: https://github.com/open-source-parsers/jsoncpp.git + tag: 1.9.5