729 lines
26 KiB
YAML
729 lines
26 KiB
YAML
name: Desktop Client Build
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
paths:
|
|
- "apps/desktop-client/**"
|
|
workflow_dispatch:
|
|
inputs:
|
|
platforms:
|
|
description: "Build platforms: mac, win, or mac,win"
|
|
required: true
|
|
default: "mac,win"
|
|
mac_arches:
|
|
description: "macOS architectures: arm64, x64, or comma-separated. universal requires a macOS runner."
|
|
required: true
|
|
default: "arm64"
|
|
win_arches:
|
|
description: "Windows architectures: x64, arm64, ia32, or comma-separated"
|
|
required: true
|
|
default: "x64"
|
|
version_prefix:
|
|
description: "Client version prefix. Final version is <prefix>.<patch>."
|
|
required: true
|
|
default: "0.1"
|
|
version_patch:
|
|
description: "Optional fixed patch number. Empty means auto-increment."
|
|
required: false
|
|
default: ""
|
|
output_dir:
|
|
description: "NAS output directory"
|
|
required: true
|
|
default: "/vol1/1000/share/gl-test"
|
|
host:
|
|
description: "NAS SSH host"
|
|
required: true
|
|
default: "192.168.100.49"
|
|
port:
|
|
description: "NAS SSH port"
|
|
required: true
|
|
default: "22"
|
|
user:
|
|
description: "NAS SSH user"
|
|
required: true
|
|
default: "admin"
|
|
password_source:
|
|
description: "NAS SSH password source"
|
|
required: true
|
|
default: "repository_secret"
|
|
type: choice
|
|
options:
|
|
- repository_secret
|
|
- workflow_input
|
|
ssh_password:
|
|
description: "NAS SSH password. Used only when password_source is workflow_input."
|
|
required: false
|
|
default: ""
|
|
use_sudo:
|
|
description: "Use sudo for NAS file operations"
|
|
required: true
|
|
default: "true"
|
|
type: choice
|
|
options:
|
|
- "true"
|
|
- "false"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: desktop-client-build
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
DEFAULT_DESKTOP_PLATFORMS: "mac,win"
|
|
DEFAULT_MAC_ARCHES: "arm64"
|
|
DEFAULT_WIN_ARCHES: "x64"
|
|
DEFAULT_VERSION_PREFIX: "0.1"
|
|
DEFAULT_NAS_OUTPUT_DIR: "/vol1/1000/share/gl-test"
|
|
DEFAULT_NAS_HOST: "192.168.100.49"
|
|
DEFAULT_NAS_PORT: "22"
|
|
DEFAULT_NAS_USER: "admin"
|
|
DEFAULT_NAS_USE_SUDO: "true"
|
|
PNPM_VERSION: "10.33.0"
|
|
|
|
jobs:
|
|
resolve:
|
|
name: Resolve Build Metadata
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
outputs:
|
|
build_mac: ${{ steps.meta.outputs.build_mac }}
|
|
build_win: ${{ steps.meta.outputs.build_win }}
|
|
client_version: ${{ steps.meta.outputs.client_version }}
|
|
mac_arch_flags: ${{ steps.meta.outputs.mac_arch_flags }}
|
|
nas_host: ${{ steps.meta.outputs.nas_host }}
|
|
nas_port: ${{ steps.meta.outputs.nas_port }}
|
|
nas_user: ${{ steps.meta.outputs.nas_user }}
|
|
nas_use_sudo: ${{ steps.meta.outputs.nas_use_sudo }}
|
|
output_dir: ${{ steps.meta.outputs.output_dir }}
|
|
release_name: ${{ steps.meta.outputs.release_name }}
|
|
win_arch_flags: ${{ steps.meta.outputs.win_arch_flags }}
|
|
env:
|
|
REQUESTED_PLATFORMS: ${{ inputs.platforms }}
|
|
REQUESTED_MAC_ARCHES: ${{ inputs.mac_arches }}
|
|
REQUESTED_WIN_ARCHES: ${{ inputs.win_arches }}
|
|
REQUESTED_VERSION_PREFIX: ${{ inputs.version_prefix }}
|
|
REQUESTED_VERSION_PATCH: ${{ inputs.version_patch }}
|
|
REQUESTED_OUTPUT_DIR: ${{ inputs.output_dir }}
|
|
REQUESTED_NAS_HOST: ${{ inputs.host }}
|
|
REQUESTED_NAS_PORT: ${{ inputs.port }}
|
|
REQUESTED_NAS_USER: ${{ inputs.user }}
|
|
REQUESTED_NAS_USE_SUDO: ${{ inputs.use_sudo }}
|
|
NAS_SSH_PASSWORD_SOURCE: ${{ inputs.password_source }}
|
|
NAS_SSH_PASSWORD_INPUT: ${{ inputs.ssh_password }}
|
|
NAS_SSH_PASSWORD_SECRET: ${{ secrets.NAS_SSH_PASSWORD }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Install SSH tools
|
|
run: |
|
|
set -eu
|
|
apt-get update
|
|
apt-get install -y openssh-client sshpass
|
|
|
|
- name: Resolve version and targets
|
|
id: meta
|
|
run: |
|
|
set -eu
|
|
|
|
normalize_csv() {
|
|
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]'
|
|
}
|
|
|
|
arch_flags() {
|
|
value="$(normalize_csv "$1")"
|
|
allowed="$2"
|
|
result=""
|
|
|
|
if [ -z "${value}" ]; then
|
|
echo "::error::Architecture list cannot be empty"
|
|
exit 1
|
|
fi
|
|
|
|
for arch in $(printf '%s' "${value}" | tr ',' ' '); do
|
|
found=false
|
|
for candidate in ${allowed}; do
|
|
if [ "${arch}" = "${candidate}" ]; then
|
|
found=true
|
|
break
|
|
fi
|
|
done
|
|
if [ "${found}" != "true" ]; then
|
|
echo "::error::Unsupported architecture '${arch}'. Allowed: ${allowed}"
|
|
exit 1
|
|
fi
|
|
result="${result} --${arch}"
|
|
done
|
|
|
|
printf '%s' "${result# }"
|
|
}
|
|
|
|
platforms="$(normalize_csv "${REQUESTED_PLATFORMS:-${DEFAULT_DESKTOP_PLATFORMS}}")"
|
|
build_mac=false
|
|
build_win=false
|
|
for platform in $(printf '%s' "${platforms}" | tr ',' ' '); do
|
|
case "${platform}" in
|
|
mac|macos)
|
|
build_mac=true
|
|
;;
|
|
win|windows)
|
|
build_win=true
|
|
;;
|
|
*)
|
|
echo "::error::Unsupported platform '${platform}'. Use mac, win, or mac,win."
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ "${build_mac}" != "true" ] && [ "${build_win}" != "true" ]; then
|
|
echo "::error::At least one platform must be selected."
|
|
exit 1
|
|
fi
|
|
|
|
mac_arches="$(normalize_csv "${REQUESTED_MAC_ARCHES:-${DEFAULT_MAC_ARCHES}}")"
|
|
case ",${mac_arches}," in
|
|
*,universal,*)
|
|
echo "::error::mac universal cannot be built on the current ubuntu-latest runner. Use mac_arches=arm64 or x64, or run this job on a macOS runner."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
mac_arch_flags="$(arch_flags "${mac_arches}" "x64 arm64")"
|
|
win_arch_flags="$(arch_flags "${REQUESTED_WIN_ARCHES:-${DEFAULT_WIN_ARCHES}}" "x64 arm64 ia32")"
|
|
|
|
version_prefix="${REQUESTED_VERSION_PREFIX:-${DEFAULT_VERSION_PREFIX}}"
|
|
if ! printf '%s' "${version_prefix}" | grep -Eq '^[0-9]+\.[0-9]+$'; then
|
|
echo "::error::version_prefix must look like 0.1"
|
|
exit 1
|
|
fi
|
|
|
|
output_dir="${REQUESTED_OUTPUT_DIR:-${DEFAULT_NAS_OUTPUT_DIR}}"
|
|
output_dir="${output_dir%/}"
|
|
case "${output_dir}" in
|
|
/*) ;;
|
|
*)
|
|
echo "::error::output_dir must be an absolute NAS path"
|
|
exit 1
|
|
;;
|
|
esac
|
|
case "${output_dir}" in
|
|
*[!A-Za-z0-9_./-]*)
|
|
echo "::error::output_dir contains unsupported characters"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
nas_host="${REQUESTED_NAS_HOST:-${DEFAULT_NAS_HOST}}"
|
|
nas_port="${REQUESTED_NAS_PORT:-${DEFAULT_NAS_PORT}}"
|
|
nas_user="${REQUESTED_NAS_USER:-${DEFAULT_NAS_USER}}"
|
|
nas_use_sudo="${REQUESTED_NAS_USE_SUDO:-${DEFAULT_NAS_USE_SUDO}}"
|
|
|
|
case "${nas_port}" in
|
|
*[!0-9]*|"")
|
|
echo "::error::NAS SSH port must be numeric"
|
|
exit 1
|
|
;;
|
|
esac
|
|
case "${nas_host}" in
|
|
*[!A-Za-z0-9_.:-]*|"")
|
|
echo "::error::NAS host contains unsupported characters"
|
|
exit 1
|
|
;;
|
|
esac
|
|
case "${nas_user}" in
|
|
*[!A-Za-z0-9_.@-]*|"")
|
|
echo "::error::NAS user contains unsupported characters"
|
|
exit 1
|
|
;;
|
|
esac
|
|
case "${nas_use_sudo}" in
|
|
true|false) ;;
|
|
*)
|
|
echo "::error::use_sudo must be true or false"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${REQUESTED_VERSION_PATCH:-}" ]; then
|
|
if ! printf '%s' "${REQUESTED_VERSION_PATCH}" | grep -Eq '^[0-9]+$'; then
|
|
echo "::error::version_patch must be a non-negative integer"
|
|
exit 1
|
|
fi
|
|
version_patch="${REQUESTED_VERSION_PATCH}"
|
|
else
|
|
password_source="${NAS_SSH_PASSWORD_SOURCE:-repository_secret}"
|
|
case "${password_source}" in
|
|
repository_secret)
|
|
nas_ssh_password="${NAS_SSH_PASSWORD_SECRET:-}"
|
|
;;
|
|
workflow_input)
|
|
nas_ssh_password="${NAS_SSH_PASSWORD_INPUT:-}"
|
|
;;
|
|
*)
|
|
echo "::error::password_source must be repository_secret or workflow_input"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${nas_ssh_password}" ]; then
|
|
echo "::add-mask::${nas_ssh_password}"
|
|
fi
|
|
|
|
if [ -z "${nas_ssh_password}" ]; then
|
|
if [ "${password_source}" = "workflow_input" ]; then
|
|
echo "::error::Missing workflow input ssh_password"
|
|
else
|
|
echo "::error::Missing repository secret NAS_SSH_PASSWORD"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p ~/.ssh
|
|
chmod 700 ~/.ssh
|
|
ssh-keyscan -p "${nas_port}" "${nas_host}" >> ~/.ssh/known_hosts
|
|
sudo_password_b64="$(printf '%s' "${nas_ssh_password}" | base64 | tr -d '\n')"
|
|
|
|
remote_names="$(
|
|
{
|
|
printf 'OUTPUT_DIR=%s\n' "${output_dir}"
|
|
printf 'USE_SUDO=%s\n' "${nas_use_sudo}"
|
|
printf 'SUDO_PASSWORD_B64=%s\n' "${sudo_password_b64}"
|
|
cat <<'REMOTE_SCRIPT'
|
|
set -eu
|
|
SUDO_PASSWORD="$(printf '%s' "${SUDO_PASSWORD_B64}" | base64 -d 2>/dev/null || true)"
|
|
unset SUDO_PASSWORD_B64
|
|
|
|
run_cmd() {
|
|
if [ "${USE_SUDO}" = "true" ]; then
|
|
printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
run_cmd mkdir -p "${OUTPUT_DIR}"
|
|
run_cmd find "${OUTPUT_DIR}" -maxdepth 3 \( -type f -o -type d -o -type l \) -print 2>/dev/null | sed 's#.*/##'
|
|
REMOTE_SCRIPT
|
|
} | SSHPASS="${nas_ssh_password}" sshpass -e ssh \
|
|
-p "${nas_port}" \
|
|
-o BatchMode=no \
|
|
-o StrictHostKeyChecking=yes \
|
|
"${nas_user}@${nas_host}" \
|
|
"sh -s"
|
|
)"
|
|
|
|
prefix_regex="$(printf '%s' "${version_prefix}" | sed 's/\./\\./g')"
|
|
remote_patch="$(
|
|
printf '%s\n' "${remote_names}" \
|
|
| sed -n "s/.*${prefix_regex}\.\([0-9][0-9]*\).*/\1/p" \
|
|
| sort -n \
|
|
| tail -n1
|
|
)"
|
|
|
|
if [ -n "${remote_patch}" ]; then
|
|
version_patch=$((remote_patch + 1))
|
|
else
|
|
version_patch=0
|
|
fi
|
|
fi
|
|
|
|
client_version="${version_prefix}.${version_patch}"
|
|
release_name="desktop-client-${client_version}"
|
|
|
|
{
|
|
echo "build_mac=${build_mac}"
|
|
echo "build_win=${build_win}"
|
|
echo "client_version=${client_version}"
|
|
echo "mac_arch_flags=${mac_arch_flags}"
|
|
echo "nas_host=${nas_host}"
|
|
echo "nas_port=${nas_port}"
|
|
echo "nas_user=${nas_user}"
|
|
echo "nas_use_sudo=${nas_use_sudo}"
|
|
echo "output_dir=${output_dir}"
|
|
echo "release_name=${release_name}"
|
|
echo "win_arch_flags=${win_arch_flags}"
|
|
} >> "${GITHUB_OUTPUT}"
|
|
|
|
echo "Version: ${client_version}"
|
|
echo "Platforms: mac=${build_mac}, win=${build_win}"
|
|
echo "macOS arch flags: ${mac_arch_flags}"
|
|
echo "Windows arch flags: ${win_arch_flags}"
|
|
echo "Use sudo on NAS: ${nas_use_sudo}"
|
|
echo "NAS output: ${nas_user}@${nas_host}:${output_dir}/${release_name}"
|
|
|
|
build-client:
|
|
name: Build Desktop Client
|
|
needs: resolve
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 120
|
|
env:
|
|
BUILD_MAC: ${{ needs.resolve.outputs.build_mac }}
|
|
BUILD_WIN: ${{ needs.resolve.outputs.build_win }}
|
|
CLIENT_VERSION: ${{ needs.resolve.outputs.client_version }}
|
|
MAC_ARCH_FLAGS: ${{ needs.resolve.outputs.mac_arch_flags }}
|
|
WIN_ARCH_FLAGS: ${{ needs.resolve.outputs.win_arch_flags }}
|
|
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "20"
|
|
|
|
- name: Setup pnpm
|
|
run: |
|
|
set -eu
|
|
corepack enable
|
|
corepack prepare pnpm@${PNPM_VERSION} --activate
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Install Wine for Windows packaging
|
|
if: needs.resolve.outputs.build_win == 'true'
|
|
run: |
|
|
set -eu
|
|
if command -v wine >/dev/null 2>&1 && command -v xvfb-run >/dev/null 2>&1; then
|
|
wine --version
|
|
if dpkg --print-foreign-architectures | grep -qx i386 && dpkg -s wine32:i386 >/dev/null 2>&1; then
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
dpkg --add-architecture i386
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
wine \
|
|
wine64 \
|
|
wine32:i386 \
|
|
xvfb
|
|
wine --version
|
|
|
|
- name: Build unsigned desktop packages
|
|
working-directory: apps/desktop-client
|
|
run: |
|
|
set -eu
|
|
unset CSC_LINK CSC_NAME CSC_KEY_PASSWORD APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID WIN_CSC_LINK WIN_CSC_KEY_PASSWORD
|
|
export CSC_IDENTITY_AUTO_DISCOVERY=false
|
|
|
|
node -e 'const fs = require("fs"); const p = "package.json"; const pkg = JSON.parse(fs.readFileSync(p, "utf8")); pkg.version = process.env.CLIENT_VERSION; fs.writeFileSync(p, `${JSON.stringify(pkg, null, 2)}\n`); console.log(`Desktop client version: ${pkg.version}`);'
|
|
|
|
rm -rf release
|
|
pnpm run build:obf
|
|
|
|
if [ "${BUILD_MAC}" = "true" ]; then
|
|
pnpm exec electron-builder --mac zip ${MAC_ARCH_FLAGS} --publish never -c.mac.target=zip -c.extraMetadata.version="${CLIENT_VERSION}"
|
|
fi
|
|
|
|
if [ "${BUILD_WIN}" = "true" ]; then
|
|
xvfb-run -a --server-args="-screen 0 1024x768x24" \
|
|
pnpm exec electron-builder --win zip ${WIN_ARCH_FLAGS} --publish never -c.win.target=zip -c.extraMetadata.version="${CLIENT_VERSION}"
|
|
fi
|
|
|
|
- name: Collect desktop artifacts
|
|
working-directory: apps/desktop-client
|
|
run: |
|
|
set -eu
|
|
artifact_root="${RUNNER_TEMP}/desktop-client-artifacts"
|
|
mkdir -p "${artifact_root}"
|
|
|
|
if [ "${BUILD_MAC}" = "true" ]; then
|
|
mkdir -p "${artifact_root}/mac"
|
|
mac_zip_count="$(find release -maxdepth 1 -type f -name "*.zip" ! -iname "*win*" ! -iname "*windows*" | wc -l | tr -d ' ')"
|
|
if [ "${mac_zip_count}" = "0" ]; then
|
|
echo "::error::No macOS zip artifact found"
|
|
find release -maxdepth 1 -type f -name "*.zip" -print
|
|
exit 1
|
|
fi
|
|
if [ "${mac_zip_count}" != "1" ]; then
|
|
echo "::error::Expected exactly one macOS zip artifact, found ${mac_zip_count}."
|
|
find release -maxdepth 1 -type f -name "*.zip" -print
|
|
exit 1
|
|
fi
|
|
mac_zip="$(find release -maxdepth 1 -type f -name "*.zip" ! -iname "*win*" ! -iname "*windows*" | head -n1)"
|
|
cp -p "${mac_zip}" "${artifact_root}/mac/省心推-mac-${CLIENT_VERSION}.zip"
|
|
fi
|
|
|
|
if [ "${BUILD_WIN}" = "true" ]; then
|
|
mkdir -p "${artifact_root}/win"
|
|
win_zip_count="$(find release -maxdepth 1 -type f -name "*.zip" \( -iname "*win*" -o -iname "*windows*" \) | wc -l | tr -d ' ')"
|
|
if [ "${win_zip_count}" = "0" ]; then
|
|
echo "::error::No Windows zip artifact found"
|
|
find release -maxdepth 1 -type f -name "*.zip" -print
|
|
exit 1
|
|
fi
|
|
if [ "${win_zip_count}" != "1" ]; then
|
|
echo "::error::Expected exactly one Windows zip artifact, found ${win_zip_count}."
|
|
find release -maxdepth 1 -type f -name "*.zip" -print
|
|
exit 1
|
|
fi
|
|
win_zip="$(find release -maxdepth 1 -type f -name "*.zip" \( -iname "*win*" -o -iname "*windows*" \) | head -n1)"
|
|
cp -p "${win_zip}" "${artifact_root}/win/省心推-win-${CLIENT_VERSION}.zip"
|
|
fi
|
|
|
|
find "${artifact_root}" -maxdepth 3 -type f -print
|
|
|
|
- name: Upload desktop artifacts
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: desktop-client
|
|
path: ${{ runner.temp }}/desktop-client-artifacts
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
|
|
publish-to-nas:
|
|
name: Publish Client Artifacts to NAS
|
|
needs:
|
|
- resolve
|
|
- build-client
|
|
if: >-
|
|
always() &&
|
|
needs.resolve.result == 'success' &&
|
|
needs.build-client.result == 'success'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
env:
|
|
CLIENT_VERSION: ${{ needs.resolve.outputs.client_version }}
|
|
NAS_HOST: ${{ needs.resolve.outputs.nas_host }}
|
|
NAS_PORT: ${{ needs.resolve.outputs.nas_port }}
|
|
NAS_USER: ${{ needs.resolve.outputs.nas_user }}
|
|
NAS_USE_SUDO: ${{ needs.resolve.outputs.nas_use_sudo }}
|
|
OUTPUT_DIR: ${{ needs.resolve.outputs.output_dir }}
|
|
RELEASE_NAME: ${{ needs.resolve.outputs.release_name }}
|
|
NAS_SSH_PASSWORD_SOURCE: ${{ inputs.password_source }}
|
|
NAS_SSH_PASSWORD_INPUT: ${{ inputs.ssh_password }}
|
|
NAS_SSH_PASSWORD_SECRET: ${{ secrets.NAS_SSH_PASSWORD }}
|
|
steps:
|
|
- name: Install SSH tools
|
|
run: |
|
|
set -eu
|
|
apt-get update
|
|
apt-get install -y openssh-client sshpass tar
|
|
|
|
- name: Download client artifacts
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
name: desktop-client
|
|
path: desktop-artifacts
|
|
|
|
- name: Write manifest
|
|
run: |
|
|
set -eu
|
|
artifact_count="$(find desktop-artifacts -type f | wc -l | tr -d ' ')"
|
|
if [ "${artifact_count}" = "0" ]; then
|
|
echo "::error::No downloaded artifacts found"
|
|
exit 1
|
|
fi
|
|
|
|
printf '%s\n' "${CLIENT_VERSION}" > desktop-artifacts/VERSION
|
|
|
|
cat > desktop-artifacts/manifest.json <<EOF
|
|
{
|
|
"app": "desktop-client",
|
|
"version": "${CLIENT_VERSION}",
|
|
"releaseName": "${RELEASE_NAME}",
|
|
"commit": "${GITHUB_SHA}",
|
|
"runNumber": "${GITHUB_RUN_NUMBER}",
|
|
"runId": "${GITHUB_RUN_ID}",
|
|
"repository": "${GITHUB_REPOSITORY}",
|
|
"createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
}
|
|
EOF
|
|
|
|
find desktop-artifacts -maxdepth 3 -type f -print
|
|
|
|
- name: Upload artifacts to NAS
|
|
run: |
|
|
set -eu
|
|
|
|
password_source="${NAS_SSH_PASSWORD_SOURCE:-repository_secret}"
|
|
case "${password_source}" in
|
|
repository_secret)
|
|
nas_ssh_password="${NAS_SSH_PASSWORD_SECRET:-}"
|
|
;;
|
|
workflow_input)
|
|
nas_ssh_password="${NAS_SSH_PASSWORD_INPUT:-}"
|
|
;;
|
|
*)
|
|
echo "::error::password_source must be repository_secret or workflow_input"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${nas_ssh_password}" ]; then
|
|
echo "::add-mask::${nas_ssh_password}"
|
|
fi
|
|
|
|
if [ -z "${nas_ssh_password}" ]; then
|
|
if [ "${password_source}" = "workflow_input" ]; then
|
|
echo "::error::Missing workflow input ssh_password"
|
|
else
|
|
echo "::error::Missing repository secret NAS_SSH_PASSWORD"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p ~/.ssh
|
|
chmod 700 ~/.ssh
|
|
ssh-keyscan -p "${NAS_PORT}" "${NAS_HOST}" >> ~/.ssh/known_hosts
|
|
|
|
remote="${NAS_USER}@${NAS_HOST}"
|
|
ssh_opts="-p ${NAS_PORT} -o BatchMode=no -o StrictHostKeyChecking=yes"
|
|
scp_opts="-P ${NAS_PORT} -o BatchMode=no -o StrictHostKeyChecking=yes"
|
|
remote_release_dir="${OUTPUT_DIR}/${RELEASE_NAME}"
|
|
sudo_password_b64="$(printf '%s' "${nas_ssh_password}" | base64 | tr -d '\n')"
|
|
archive_path="${RUNNER_TEMP:-/tmp}/${RELEASE_NAME}.tar.gz"
|
|
remote_archive="/tmp/${RELEASE_NAME}-${GITHUB_RUN_ID:-manual}.tar.gz"
|
|
|
|
{
|
|
printf 'OUTPUT_DIR=%s\n' "${OUTPUT_DIR}"
|
|
printf 'RELEASE_NAME=%s\n' "${RELEASE_NAME}"
|
|
printf 'USE_SUDO=%s\n' "${NAS_USE_SUDO}"
|
|
printf 'SUDO_PASSWORD_B64=%s\n' "${sudo_password_b64}"
|
|
cat <<'REMOTE_SCRIPT'
|
|
set -eu
|
|
SUDO_PASSWORD="$(printf '%s' "${SUDO_PASSWORD_B64}" | base64 -d 2>/dev/null || true)"
|
|
unset SUDO_PASSWORD_B64
|
|
|
|
run_cmd() {
|
|
if [ "${USE_SUDO}" = "true" ]; then
|
|
printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
run_cmd mkdir -p "${OUTPUT_DIR}/${RELEASE_NAME}"
|
|
REMOTE_SCRIPT
|
|
} | SSHPASS="${nas_ssh_password}" sshpass -e ssh ${ssh_opts} "${remote}" "sh -s"
|
|
|
|
tar -C desktop-artifacts -czf "${archive_path}" .
|
|
SSHPASS="${nas_ssh_password}" sshpass -e scp ${scp_opts} "${archive_path}" "${remote}:${remote_archive}"
|
|
|
|
{
|
|
printf 'REMOTE_ARCHIVE=%s\n' "${remote_archive}"
|
|
printf 'REMOTE_RELEASE_DIR=%s\n' "${remote_release_dir}"
|
|
printf 'USE_SUDO=%s\n' "${NAS_USE_SUDO}"
|
|
printf 'SUDO_PASSWORD_B64=%s\n' "${sudo_password_b64}"
|
|
cat <<'REMOTE_SCRIPT'
|
|
set -eu
|
|
SUDO_PASSWORD="$(printf '%s' "${SUDO_PASSWORD_B64}" | base64 -d 2>/dev/null || true)"
|
|
unset SUDO_PASSWORD_B64
|
|
|
|
if [ "${USE_SUDO}" = "true" ]; then
|
|
printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" tar -xzf "${REMOTE_ARCHIVE}" -C "${REMOTE_RELEASE_DIR}"
|
|
rm -f "${REMOTE_ARCHIVE}"
|
|
else
|
|
tar -xzf "${REMOTE_ARCHIVE}" -C "${REMOTE_RELEASE_DIR}"
|
|
rm -f "${REMOTE_ARCHIVE}"
|
|
fi
|
|
REMOTE_SCRIPT
|
|
} | SSHPASS="${nas_ssh_password}" sshpass -e ssh ${ssh_opts} "${remote}" "sh -s"
|
|
|
|
{
|
|
printf 'OUTPUT_DIR=%s\n' "${OUTPUT_DIR}"
|
|
printf 'RELEASE_NAME=%s\n' "${RELEASE_NAME}"
|
|
printf 'USE_SUDO=%s\n' "${NAS_USE_SUDO}"
|
|
printf 'SUDO_PASSWORD_B64=%s\n' "${sudo_password_b64}"
|
|
cat <<'REMOTE_SCRIPT'
|
|
set -eu
|
|
SUDO_PASSWORD="$(printf '%s' "${SUDO_PASSWORD_B64}" | base64 -d 2>/dev/null || true)"
|
|
unset SUDO_PASSWORD_B64
|
|
|
|
if [ "${USE_SUDO}" = "true" ]; then
|
|
tmp_file="$(mktemp)"
|
|
printf '%s\n' "${RELEASE_NAME}" > "${tmp_file}"
|
|
printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" cp "${tmp_file}" "${OUTPUT_DIR}/latest-desktop-client.txt"
|
|
rm -f "${tmp_file}"
|
|
else
|
|
printf '%s\n' "${RELEASE_NAME}" > "${OUTPUT_DIR}/latest-desktop-client.txt"
|
|
fi
|
|
REMOTE_SCRIPT
|
|
} | SSHPASS="${nas_ssh_password}" sshpass -e ssh ${ssh_opts} "${remote}" "sh -s"
|
|
|
|
{
|
|
printf 'OUTPUT_DIR=%s\n' "${OUTPUT_DIR}"
|
|
printf 'CLIENT_VERSION=%s\n' "${CLIENT_VERSION}"
|
|
printf 'USE_SUDO=%s\n' "${NAS_USE_SUDO}"
|
|
printf 'SUDO_PASSWORD_B64=%s\n' "${sudo_password_b64}"
|
|
cat <<'REMOTE_SCRIPT'
|
|
set -eu
|
|
SUDO_PASSWORD="$(printf '%s' "${SUDO_PASSWORD_B64}" | base64 -d 2>/dev/null || true)"
|
|
unset SUDO_PASSWORD_B64
|
|
|
|
run_cmd() {
|
|
if [ "${USE_SUDO}" = "true" ]; then
|
|
printf '%s\n' "${SUDO_PASSWORD}" | sudo -S -p "" "$@"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
versioned_paths_desc() {
|
|
root="$1"
|
|
pattern="$2"
|
|
prefix="$3"
|
|
suffix="$4"
|
|
|
|
for candidate in "${root}"/${pattern}; do
|
|
[ -e "${candidate}" ] || continue
|
|
name="${candidate##*/}"
|
|
version="${name#${prefix}}"
|
|
if [ -n "${suffix}" ]; then
|
|
version="${version%${suffix}}"
|
|
fi
|
|
|
|
case "${version}" in
|
|
*[!0-9.]*|""|*.*.*.*)
|
|
continue
|
|
;;
|
|
esac
|
|
|
|
major="$(printf '%s' "${version}" | cut -d. -f1)"
|
|
minor="$(printf '%s' "${version}" | cut -d. -f2)"
|
|
patch="$(printf '%s' "${version}" | cut -d. -f3)"
|
|
|
|
case "${major}${minor}${patch}" in
|
|
*[!0-9]*|"")
|
|
continue
|
|
;;
|
|
esac
|
|
|
|
printf '%s.%s.%s %s\n' "${major}" "${minor}" "${patch}" "${candidate}"
|
|
done | sort -t. -k1,1nr -k2,2nr -k3,3nr | awk '{print $2}'
|
|
}
|
|
|
|
keep_latest_two() {
|
|
root="$1"
|
|
pattern="$2"
|
|
prefix="$3"
|
|
suffix="$4"
|
|
mode="$5"
|
|
keep=0
|
|
|
|
for path in $(versioned_paths_desc "${root}" "${pattern}" "${prefix}" "${suffix}"); do
|
|
keep=$((keep + 1))
|
|
if [ "${keep}" -gt 2 ]; then
|
|
if [ "${mode}" = "dir" ]; then
|
|
run_cmd rm -rf "${path}"
|
|
else
|
|
run_cmd rm -f "${path}"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
run_cmd rm -rf "${OUTPUT_DIR}/mac" "${OUTPUT_DIR}/win"
|
|
keep_latest_two "${OUTPUT_DIR}" "desktop-client-*" "desktop-client-" "" "dir"
|
|
REMOTE_SCRIPT
|
|
} | SSHPASS="${nas_ssh_password}" sshpass -e ssh ${ssh_opts} "${remote}" "sh -s"
|
|
|
|
echo "Uploaded desktop client ${CLIENT_VERSION} to ${remote}:${remote_release_dir}"
|