Re-implement packaging for GitHub workflows (Linux ARM) (#7369)

* Debian 12 ARM64 target

* Fixed comments

* Don't check for server name when exiting because of args

* Exit with success when using `--version`, `--help`, etc

* Test Linux installation

* Add Windows ARM builder

* Extract magic string

* Use proper arch name

* Swap arch order

* Fixed test

* Bootstrap Windows ARM runner

* Install winget

* Conditionally install winget

* Winget take 2

* Use shell: pwsh

* Try `powershell`

* Try cmd to install winget

* Only build distro once

* Use enum for clarity

* Remove shell

* Back out Windows ARM testing

* Attempt arch matrix

* Add Fedora arm64

* Label all -amd64

* Special case for zypper

* Fixed bad var name on Windows

* Add missing /bin ignore

* Disable GPG check on OpenSUSE

* Fixed typo

* Update ChangeLog
This commit is contained in:
Nick Bolton
2024-07-08 10:32:55 +01:00
committed by GitHub
parent 51c7bcb138
commit bfaea34291
10 changed files with 179 additions and 64 deletions

View File

@ -35,6 +35,7 @@ jobs:
windows:
name: ${{ matrix.target.name }}
runs-on: ${{ matrix.target.runs-on }}
container: ${{ matrix.target.container }}
timeout-minutes: 20
env:
@ -43,9 +44,8 @@ jobs:
strategy:
matrix:
target:
- name: windows-2022
- name: windows-2022-x64
runs-on: windows-2022
arch: x64
steps:
- name: Checkout
@ -97,7 +97,7 @@ jobs:
with:
use_github: ${{ env.UPLOAD_TO_GITHUB }}
use_gdrive: ${{ env.UPLOAD_TO_GDRIVE }}
github-target-filename: "${{ env.SYNERGY_PACKAGE_PREFIX }}-windows-${{ matrix.target.name }}"
github-target-filename: "${{ env.SYNERGY_PACKAGE_PREFIX }}-${{ matrix.target.name }}"
gdrive-target-base-dir: ${{ vars.GDRIVE_TARGET_BASE_DIR }}
gdrive-secret-key: ${{ secrets.GOOGLE_DRIVE_KEY }}
gdrive-parent-folder-id: ${{ secrets.GOOGLE_DRIVE_TECH_DRIVE }}
@ -175,8 +175,8 @@ jobs:
linux:
name: linux-${{ matrix.distro.name }}
runs-on: ${{ matrix.distro.runs-on }}
timeout-minutes: 10
container: ${{ matrix.distro.container }}
timeout-minutes: 10
env:
# Prevent apt prompting for input.
@ -185,50 +185,60 @@ jobs:
strategy:
matrix:
distro:
- name: ubuntu-24.04
- name: ubuntu-24.04-amd64
container: ubuntu:24.04
runs-on: ubuntu-latest
install-deps-apt: true
extra-packages: true
- name: ubuntu-22.04
- name: ubuntu-22.04-amd64
container: ubuntu:22.04
runs-on: ubuntu-latest
install-deps-apt: true
- name: debian-12
- name: debian-12-arm64
container: arm64v8/debian:12
runs-on: buildjet-4vcpu-ubuntu-2204-arm
install-deps-apt: true
- name: debian-12-amd64
container: debian:12
runs-on: ubuntu-latest
install-deps-apt: true
- name: debian-11
- name: debian-11-amd64
container: debian:11
runs-on: ubuntu-latest
install-deps-apt: true
legacy-cmake: true
- name: fedora-40
- name: fedora-40-arm64
container: arm64v8/fedora:40
runs-on: buildjet-4vcpu-ubuntu-2204-arm
install-deps-dnf: true
- name: fedora-40-amd64
container: fedora:40
runs-on: ubuntu-latest
install-deps-dnf: true
- name: fedora-39
- name: fedora-39-amd64
container: fedora:39
runs-on: ubuntu-latest
install-deps-dnf: true
- name: opensuse
- name: opensuse-amd64
container: opensuse/tumbleweed:latest
runs-on: ubuntu-latest
install-deps-zypper: true
- name: arch
- name: arch-amd64
container: archlinux:latest
runs-on: ubuntu-latest
install-deps-pacman: true
package-user: build
- name: manjaro
- name: manjaro-amd64
container: manjarolinux/base:latest
runs-on: ubuntu-latest
install-deps-pacman: true

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# temp dirs created during build
/build
/bin
/dist
/deps
/tmp

View File

@ -40,6 +40,7 @@ Enhancements:
- #7363 Schedule CI daily at 5am to detect code rot early
- #7364 Format all source with Clang and introduce lint workflow
- #7368 Make version check URL v1-specific and configurable
- #7369 Re-implement packaging for GitHub workflows (Linux ARM)
# 1.14.6

View File

@ -1,35 +1,48 @@
import os, shutil, glob
import lib.cmd_utils as cmd_utils
import lib.env as env
from enum import Enum, auto
class PackageType(Enum):
DISTRO = auto()
TGZ = auto()
STGZ = auto()
dist_dir = "dist"
build_dir = "build"
package_name = "synergy"
test_cmd = "synergys --version"
def package(filename_base, build_distro=True, build_tgz=False, build_stgz=False):
def package(filename_base, package_type: PackageType):
extension, cmd = get_package_info(build_distro, build_tgz, build_stgz)
extension, cmd = get_package_info(package_type)
run_package_cmd(cmd)
package_filename = get_package_filename(extension)
target_file = f"{filename_base}.{extension}"
copy_to_dist_dir(package_filename, target_file)
target_path = copy_to_dist_dir(package_filename, target_file)
if package_type == PackageType.DISTRO:
test_install(target_path)
def get_package_info(build_distro, build_tgz, build_stgz):
def get_package_info(package_type: PackageType):
command = None
cpack_generator = None
file_extension = None
if build_tgz:
if package_type == PackageType.TGZ:
cpack_generator = "TGZ"
file_extension = "tar.gz"
elif build_stgz:
elif package_type == PackageType.STGZ:
cpack_generator = "STGZ"
file_extension = "sh"
elif build_distro:
elif package_type == PackageType.DISTRO:
distro, distro_like, _distro_version = env.get_linux_distro()
if not distro_like:
@ -45,7 +58,7 @@ def get_package_info(build_distro, build_tgz, build_stgz):
command = ["makepkg", "-s"]
file_extension = "pkg.tar.zst"
else:
raise RuntimeError(f"Linux distro not yet supported: {distro_like}")
raise RuntimeError(f"Linux distro not yet supported: {distro}")
if not cpack_generator and not command:
raise RuntimeError("No package generator or command found")
@ -59,7 +72,9 @@ def get_package_info(build_distro, build_tgz, build_stgz):
def run_package_cmd(command):
package_user = env.get_env("LINUX_PACKAGE_USER", required=False)
if package_user:
cmd_utils.run(["sudo", "chown", "-R", package_user, "build"], check=True)
cmd_utils.run(
["sudo", "chown", "-R", package_user, "build"], check=True, print_cmd=True
)
command = ["sudo", "-u", package_user] + command
cwd = os.getcwd()
@ -85,3 +100,47 @@ def copy_to_dist_dir(source_file, target_file):
target_path = f"{dist_dir}/{target_file}"
print(f"Copying to: {target_path}")
shutil.copy(source_file, target_path)
return target_path
def test_install(package_file):
distro, distro_like, _distro_version = env.get_linux_distro()
if not distro_like:
distro_like = distro
install_pre = None
remove_pre = None
if "debian" in distro_like:
install_pre = ["apt", "install", "-f", "-y"]
remove_pre = ["apt", "remove", "-y"]
elif "fedora" in distro_like:
install_pre = ["dnf", "install", "-y"]
remove_pre = ["dnf", "remove", "-y"]
elif "opensuse" in distro_like:
install_pre = ["zypper", "--no-gpg-checks", "install", "-y"]
remove_pre = ["zypper", "remove", "-y"]
elif "arch" in distro_like:
install_pre = ["pacman", "-U", "--noconfirm"]
remove_pre = ["pacman", "-R", "--noconfirm"]
else:
raise RuntimeError(f"Linux distro not yet supported: {distro}")
has_sudo = cmd_utils.has_command("sudo")
sudo = ["sudo"] if has_sudo else []
print("Testing installation...")
cmd_utils.run(
sudo + install_pre + [f"./{package_file}"],
check=True,
print_cmd=True,
)
try:
cmd_utils.run(test_cmd, shell=True, check=True, print_cmd=True)
except Exception:
raise RuntimeError("Unable to verify version")
finally:
cmd_utils.run(sudo + remove_pre + [package_name], check=True, print_cmd=True)
print("Installation test passed")

View File

@ -2,6 +2,7 @@
import platform
import lib.env as env
from lib.linux import PackageType
env_file = ".env"
default_package_prefix = "synergy"
@ -47,6 +48,11 @@ def get_filename_base(version, use_linux_distro=True):
return f"{package_base}-{distro}-{machine}-{version}"
else:
# some windows users get confused by 'amd64' and think it's 'arm64',
# so we'll use intel's 'x64' branding (even though it's wrong).
if machine == "amd64":
machine = "x64"
return f"{package_base}-{os}-{machine}-{version}"
@ -67,12 +73,12 @@ def linux_package(filename_base, version):
extra_packages = env.get_env_bool("LINUX_EXTRA_PACKAGES", False)
linux.package(filename_base)
linux.package(filename_base, PackageType.DISTRO)
if extra_packages:
filename_base = get_filename_base(version, use_linux_distro=False)
linux.package(filename_base, build_tgz=True)
linux.package(filename_base, build_stgz=True)
linux.package(filename_base, PackageType.TGZ)
linux.package(filename_base, PackageType.STGZ)
if __name__ == "__main__":

View File

@ -119,7 +119,7 @@ bool ArgParser::parseClientArgs(lib::synergy::ClientArgs &args, int argc,
}
// exactly one non-option argument (server-address)
if (i == argc) {
if (i == argc && !args.m_shouldExitFail && !args.m_shouldExitOk) {
LOG((CLOG_CRIT "%s: a server address or name is required" BYE, args.m_pname,
args.m_pname));
return false;
@ -138,7 +138,7 @@ bool ArgParser::parsePlatformArg(lib::synergy::ArgsBase &argsBase,
#if WINAPI_MSWINDOWS
if (isArg(i, argc, argv, nullptr, "--service")) {
LOG((CLOG_WARN "obsolete argument --service, use synergyd instead."));
argsBase.m_shouldExit = true;
argsBase.m_shouldExitFail = true;
} else if (isArg(i, argc, argv, nullptr, "--exit-pause")) {
argsBase.m_pauseOnExit = true;
} else if (isArg(i, argc, argv, nullptr, "--stop-on-desk-switch")) {
@ -220,12 +220,12 @@ bool ArgParser::parseGenericArgs(int argc, const char *const *argv, int &i) {
if (m_app) {
m_app->help();
}
argsBase().m_shouldExit = true;
argsBase().m_shouldExitOk = true;
} else if (isArg(i, argc, argv, nullptr, "--version")) {
if (m_app) {
m_app->version();
}
argsBase().m_shouldExit = true;
argsBase().m_shouldExitOk = true;
} else if (isArg(i, argc, argv, nullptr, "--no-tray")) {
argsBase().m_disableTray = true;
} else if (isArg(i, argc, argv, nullptr, "--ipc")) {
@ -298,7 +298,7 @@ bool ArgParser::isArg(int argi, int argc, const char *const *argv,
if (argi + minRequiredParameters >= argc) {
LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, argsBase().m_pname,
argv[argi], argsBase().m_pname));
argsBase().m_shouldExit = true;
argsBase().m_shouldExitFail = true;
return false;
}
return true;

View File

@ -36,35 +36,65 @@ public:
/// @brief This sets the type of the derived class
enum Type { kBase, kServer, kClient };
Type m_classType = kBase; /// @brief Stores what type of object this is
/// @brief Stores what type of object this is
Type m_classType = kBase;
bool m_daemon = true; /// @brief Should run as a daemon
bool m_restartable = true; /// @brief Should the app restart automatically
bool m_noHooks = false; /// @brief Should the app use hooks
const char *m_pname = nullptr; /// @brief The filename of the running process
const char *m_logFilter =
nullptr; /// @brief The logging level of the application
const char *m_logFile = nullptr; /// @brief The full path to the logfile
const char *m_display =
nullptr; /// @brief Contains the X-Server display to use
String m_name; /// @brief The name of the current computer
bool m_disableTray = false; /// @brief Should the app add a tray icon
bool m_enableIpc =
false; /// @brief Tell the client to talk through IPC to the daemon
bool m_enableDragDrop = false; /// @brief Should drag drop support be enabled
/// @brief Should run as a daemon
bool m_daemon = true;
bool m_shouldExit =
false; /// @brief Will cause the application to exit when set to true
String m_synergyAddress; /// @brief Bind to this address
bool m_enableCrypto =
false; /// @brief Should the connections be TLS encrypted
String
m_profileDirectory; /// @brief The profile DIR to use for the application
String m_pluginDirectory; /// @brief //TODO Plugins? Get set in ARCH but
/// doesn't seem to get used
String m_tlsCertFile; /// @brief Contains the location of the TLS certificate
/// file
bool m_preventSleep = false; /// @brief Stop this computer from sleeping
/// @brief Should the app restart automatically
bool m_restartable = true;
/// @brief Should the app use hooks
bool m_noHooks = false;
/// @brief The filename of the running process
const char *m_pname = nullptr;
/// @brief The logging level of the application
const char *m_logFilter = nullptr;
/// @brief The full path to the logfile
const char *m_logFile = nullptr;
/// @brief Contains the X-Server display to use
const char *m_display = nullptr;
/// @brief The name of the current computer
String m_name;
/// @brief Should the app add a tray icon
bool m_disableTray = false;
/// @brief Tell the client to talk through IPC to the daemon
bool m_enableIpc = false;
/// @brief Should drag drop support be enabled
bool m_enableDragDrop = false;
/// @brief Will cause the application to exit with OK code when set to true
bool m_shouldExitOk = false;
/// @brief Will cause the application to exit with fail code when set to true
bool m_shouldExitFail = false;
/// @brief Bind to this address
String m_synergyAddress;
/// @brief Should the connections be TLS encrypted
bool m_enableCrypto = false;
/// @brief The dir to load settings from
String m_profileDirectory;
/// @brief The dir to load plugins from
String m_pluginDirectory;
/// @brief Contains the location of the TLS certificate file
String m_tlsCertFile;
/// @brief Stop this computer from sleeping
bool m_preventSleep = false;
#if SYSAPI_WIN32
bool m_debugServiceWait = false;

View File

@ -75,8 +75,12 @@ void ClientApp::parseArgs(int argc, const char *const *argv) {
ArgParser argParser(this);
bool result = argParser.parseClientArgs(args(), argc, argv);
if (!result || args().m_shouldExit) {
m_bye(kExitArgs);
if (!result || args().m_shouldExitOk || args().m_shouldExitFail) {
if (args().m_shouldExitOk) {
m_bye(kExitSuccess);
} else {
m_bye(kExitArgs);
}
} else {
// save server address
if (!args().m_serverAddress.empty()) {

View File

@ -79,8 +79,12 @@ void ServerApp::parseArgs(int argc, const char *const *argv) {
ArgParser argParser(this);
bool result = argParser.parseServerArgs(args(), argc, argv);
if (!result || args().m_shouldExit) {
m_bye(kExitArgs);
if (!result || args().m_shouldExitOk || args().m_shouldExitFail) {
if (args().m_shouldExitOk) {
m_bye(kExitSuccess);
} else {
m_bye(kExitArgs);
}
} else {
if (!args().m_synergyAddress.empty()) {
try {

View File

@ -55,7 +55,7 @@ TEST(ArgParserTests, isArg_missingArgs_returnFalse) {
bool result = ArgParser::isArg(i, argc, argv, "-t", NULL, 1);
EXPECT_FALSE(result);
EXPECT_EQ(true, argsBase.m_shouldExit);
EXPECT_EQ(true, argsBase.m_shouldExitFail);
}
TEST(ArgParserTests, searchDoubleQuotes_doubleQuotedArg_returnTrue) {
@ -235,7 +235,7 @@ TEST(ArgParserTests, parseServerArgs_parses_each_category) {
"--res-w", "888"};
EXPECT_TRUE(
parser.parseServerArgs(args, sizeof(argv) / sizeof(argv[0]), argv));
EXPECT_EQ(args.m_shouldExit, true);
EXPECT_EQ(args.m_shouldExitOk, true);
}
TEST(ArgParserTests, parseClientArgs_parses_single_help) {
@ -257,5 +257,5 @@ TEST(ArgParserTests, parseClientArgs_parses_single_help) {
"127.0.0.1"};
EXPECT_TRUE(
parser.parseClientArgs(args, sizeof(argv) / sizeof(argv[0]), argv));
EXPECT_EQ(args.m_shouldExit, true);
EXPECT_EQ(args.m_shouldExitOk, true);
}