Skip to content

Commit a0e158c

Browse files
authored
Merge pull request #14 from Al-Muhandis/deb-packaging
Deb packaging
2 parents 31f8d13 + 85420ba commit a0e158c

25 files changed

Lines changed: 77980 additions & 181 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ppas.bat
2727
*.7z
2828
*.mo
2929
*.lst
30+
*.deb
3031

3132
# Lazarus autogenerated files (duplicated info)
3233
*.rst
@@ -49,4 +50,4 @@ use/*/
4950
*.new
5051

5152
tests/users.sqlite3
52-
*.json
53+
debian/usr/bin/tgadmin

build-deb.sh

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# ---------------------------------------------------------------------------
5+
# Configuration
6+
# ---------------------------------------------------------------------------
7+
VER_MAJOR="0"
8+
VER_MINOR=$(grep -oP '<MinorVersionNr Value="\K[^"]+' src/adminhelperd.lpi)
9+
VER_REV=$(grep -oP '<RevisionNr Value="\K[^"]+' src/adminhelperd.lpi)
10+
VER_BUILD=$(grep -oP '<BuildNr Value="\K[^"]+' src/adminhelperd.lpi)
11+
12+
export app_VER="${VER_MAJOR}.${VER_MINOR}.${VER_REV}"
13+
export app_VERdeb="${VER_BUILD}"
14+
15+
MAINTAINER_NAME="Renat Suleymanov"
16+
MAINTAINER_EMAIL="mail@Renat.Su"
17+
PACKAGE_NAME="tgadmin"
18+
19+
# ---------------------------------------------------------------------------
20+
# Paths
21+
# ---------------------------------------------------------------------------
22+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23+
24+
# Build always happens in Linux fs to avoid NTFS chmod issues in WSL
25+
BUILD_DIR="/tmp/${PACKAGE_NAME}_build"
26+
27+
STAGING_DIR="${BUILD_DIR}/debian"
28+
DEB_NAME="${PACKAGE_NAME}_${app_VER}-${app_VERdeb}_amd64.deb"
29+
OUTPUT_DEB="${BUILD_DIR}/${DEB_NAME}"
30+
31+
# ---------------------------------------------------------------------------
32+
# Detect WSL
33+
# ---------------------------------------------------------------------------
34+
detect_wsl() {
35+
grep -qi microsoft /proc/version 2>/dev/null || \
36+
grep -qi wsl /proc/version 2>/dev/null
37+
}
38+
39+
# Where to copy the finished .deb.
40+
# Set via env: WINDOWS_OUTPUT_DIR="/mnt/c/Users/..." ./build-deb.sh
41+
if [ -n "${WINDOWS_OUTPUT_DIR:-}" ]; then
42+
COPY_TARGET="${WINDOWS_OUTPUT_DIR}"
43+
else
44+
COPY_TARGET="${SCRIPT_DIR}"
45+
fi
46+
47+
# ---------------------------------------------------------------------------
48+
# Dependencies' checking
49+
# ---------------------------------------------------------------------------
50+
check_deps() {
51+
local missing=()
52+
for cmd in dpkg-deb gzip du awk grep jq; do
53+
command -v "$cmd" &>/dev/null || missing+=("$cmd")
54+
done
55+
if [ ${#missing[@]} -gt 0 ]; then
56+
echo "ERROR: missing required tools: ${missing[*]}" >&2
57+
echo "Install with: sudo apt-get install dpkg-dev gzip jq" >&2
58+
exit 1
59+
fi
60+
}
61+
62+
# ---------------------------------------------------------------------------
63+
# Validate tgadmin.json before building
64+
# ---------------------------------------------------------------------------
65+
validate_config() {
66+
local config="${SCRIPT_DIR}/src/tgadmin.json"
67+
if [ ! -f "${config}" ]; then
68+
echo "ERROR: src/tgadmin.json not found" >&2
69+
exit 1
70+
fi
71+
if ! jq empty "${config}" 2>/dev/null; then
72+
echo "ERROR: src/tgadmin.json is not valid JSON" >&2
73+
exit 1
74+
fi
75+
}
76+
77+
# ---------------------------------------------------------------------------
78+
# Main
79+
# ---------------------------------------------------------------------------
80+
check_deps
81+
validate_config
82+
83+
echo "==> Building ${PACKAGE_NAME} v${app_VER}-${app_VERdeb}"
84+
echo "==> Build dir (Linux fs): ${BUILD_DIR}"
85+
86+
# Recreate build-dir in /tmp from scratch
87+
rm -rf "${BUILD_DIR}"
88+
mkdir -p "${BUILD_DIR}"
89+
90+
# Copy staging from sources in /tmp — here chmod works correctly
91+
cp -r "${SCRIPT_DIR}/debian" "${STAGING_DIR}"
92+
93+
# Include db_schema.sql — used by postinst to initialize the database
94+
mkdir -p "${STAGING_DIR}/usr/share/${PACKAGE_NAME}"
95+
cp "${SCRIPT_DIR}/src/db_schema.sql" "${STAGING_DIR}/usr/share/${PACKAGE_NAME}/db_schema.sql"
96+
97+
# Include tgadmin.json as default config
98+
# postinst will fill in DB credentials automatically
99+
mkdir -p "${STAGING_DIR}/etc/${PACKAGE_NAME}"
100+
cp "${SCRIPT_DIR}/src/tgadmin.json" "${STAGING_DIR}/etc/${PACKAGE_NAME}/tgadmin.json"
101+
102+
# Set permissions
103+
find "${STAGING_DIR}" -type d -exec chmod 0755 {} \;
104+
find "${STAGING_DIR}" -type f -exec chmod 0644 {} \;
105+
find "${STAGING_DIR}/usr/bin" -type f -exec chmod 0755 {} \;
106+
107+
# Maintainer scripts must be executable
108+
chmod 0755 "${STAGING_DIR}/DEBIAN/postinst"
109+
chmod 0755 "${STAGING_DIR}/DEBIAN/postrm"
110+
111+
# Config file should not be world-readable (contains credentials after install)
112+
chmod 0640 "${STAGING_DIR}/etc/${PACKAGE_NAME}/tgadmin.json"
113+
114+
# Version and size in control
115+
echo "Version: ${app_VER}.0-${app_VERdeb}" >> "${STAGING_DIR}/DEBIAN/control"
116+
SIZE_IN_KB="$(du -s "${STAGING_DIR}" | awk '{print $1}')"
117+
echo "Installed-Size: ${SIZE_IN_KB}" >> "${STAGING_DIR}/DEBIAN/control"
118+
119+
# Changelog
120+
CHANGELOG="${STAGING_DIR}/usr/share/doc/${PACKAGE_NAME}/changelog.Debian"
121+
DATE=$(date -R)
122+
{
123+
echo "${PACKAGE_NAME} (${app_VER}-${app_VERdeb}) unstable; urgency=medium"
124+
echo ""
125+
echo " * fixes"
126+
echo ""
127+
echo " -- ${MAINTAINER_NAME} <${MAINTAINER_EMAIL}> ${DATE}"
128+
} >> "${CHANGELOG}"
129+
gzip -9 -n "${CHANGELOG}"
130+
131+
# Package building
132+
dpkg-deb --root-owner-group --build "${STAGING_DIR}" "${OUTPUT_DEB}"
133+
134+
echo "==> Package built: ${OUTPUT_DEB}"
135+
136+
# Copy ready .deb to needed place
137+
mkdir -p "${COPY_TARGET}"
138+
cp "${OUTPUT_DEB}" "${COPY_TARGET}/"
139+
echo "==> Copied to: ${COPY_TARGET}/${DEB_NAME}"
140+
141+
echo "==> Done."

debian/DEBIAN/conffiles

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/etc/tgadmin/tgadmin.json
2+
/etc/default/tgadmin

debian/DEBIAN/control

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Package: tgadmin
2+
Priority: optional
3+
Maintainer: Renat Suleymanov <mail@Renat.Su>
4+
Architecture: amd64
5+
Section: utils
6+
Depends: dash, libc6 (>= 2.2.5)
7+
Origin: https://github.com/al-Muhandis/AdminHelper
8+
Description: Telegram admin helper
9+
Telegram bot service for groups' moderation

debian/DEBIAN/postinst

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
PACKAGE_NAME="tgadmin"
5+
CONFIG_FILE="/etc/${PACKAGE_NAME}/tgadmin.json"
6+
SCHEMA_FILE="/usr/share/${PACKAGE_NAME}/db_schema.sql"
7+
DEFAULTS_FILE="/etc/default/${PACKAGE_NAME}"
8+
SYSTEMD_DROPIN_DIR="/etc/systemd/system/${PACKAGE_NAME}.service.d"
9+
SYSTEMD_DROPIN_FILE="${SYSTEMD_DROPIN_DIR}/10-run-as.conf"
10+
11+
DB_NAME="tgadmin"
12+
DB_USER="tgadmin"
13+
14+
mysql_available() {
15+
command -v mysql &>/dev/null && mysqladmin ping --silent 2>/dev/null
16+
}
17+
18+
# Read current value from JSON (return empty string if null or missing)
19+
json_get() {
20+
jq -r "$1 // empty" "${CONFIG_FILE}" 2>/dev/null || true
21+
}
22+
23+
# Write values to JSON on the spot
24+
json_set() {
25+
local tmp
26+
tmp="$(mktemp)"
27+
jq "$1" "${CONFIG_FILE}" > "${tmp}" && mv "${tmp}" "${CONFIG_FILE}"
28+
}
29+
30+
resolve_service_identity() {
31+
SERVICE_USER="${PACKAGE_NAME}"
32+
SERVICE_GROUP="${PACKAGE_NAME}"
33+
34+
if [ -f "${DEFAULTS_FILE}" ]; then
35+
# shellcheck disable=SC1090
36+
. "${DEFAULTS_FILE}"
37+
38+
if [ -n "${TGADMIN_SERVICE_USER:-}" ]; then
39+
SERVICE_USER="${TGADMIN_SERVICE_USER}"
40+
fi
41+
if [ -n "${TGADMIN_SERVICE_GROUP:-}" ]; then
42+
SERVICE_GROUP="${TGADMIN_SERVICE_GROUP}"
43+
fi
44+
fi
45+
46+
if [ -z "${SERVICE_USER}" ] || [ -z "${SERVICE_GROUP}" ]; then
47+
echo "ERROR: TGADMIN_SERVICE_USER and TGADMIN_SERVICE_GROUP must be non-empty." >&2
48+
exit 1
49+
fi
50+
}
51+
52+
ensure_service_account() {
53+
if ! getent group "${SERVICE_GROUP}" >/dev/null; then
54+
groupadd --system "${SERVICE_GROUP}"
55+
fi
56+
57+
if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then
58+
useradd \
59+
--system \
60+
--gid "${SERVICE_GROUP}" \
61+
--no-create-home \
62+
--home-dir /nonexistent \
63+
--shell /usr/sbin/nologin \
64+
"${SERVICE_USER}"
65+
fi
66+
}
67+
68+
configure_systemd_service_user() {
69+
if ! command -v systemctl >/dev/null 2>&1; then
70+
return
71+
fi
72+
73+
if ! systemctl list-unit-files "${PACKAGE_NAME}.service" >/dev/null 2>&1; then
74+
return
75+
fi
76+
77+
mkdir -p "${SYSTEMD_DROPIN_DIR}"
78+
cat > "${SYSTEMD_DROPIN_FILE}" <<DROPIN
79+
[Service]
80+
User=${SERVICE_USER}
81+
Group=${SERVICE_GROUP}
82+
DROPIN
83+
84+
systemctl daemon-reload || true
85+
}
86+
87+
case "$1" in
88+
configure)
89+
# ----------------------------------------------------------------
90+
# Check dependencies of postinst
91+
# ----------------------------------------------------------------
92+
for cmd in jq mysql mysqladmin openssl; do
93+
if ! command -v "$cmd" &>/dev/null; then
94+
echo "ERROR: required tool not found: ${cmd}" >&2
95+
echo "Install with: sudo apt-get install ${cmd}" >&2
96+
exit 1
97+
fi
98+
done
99+
100+
if [ ! -f "${CONFIG_FILE}" ]; then
101+
echo "ERROR: config file not found: ${CONFIG_FILE}" >&2
102+
echo " Place tgadmin.json into /etc/tgadmin/ before installing." >&2
103+
exit 1
104+
fi
105+
106+
resolve_service_identity
107+
ensure_service_account
108+
configure_systemd_service_user
109+
110+
# ----------------------------------------------------------------
111+
# Data base
112+
# ----------------------------------------------------------------
113+
if mysql_available; then
114+
# Take password from config if set (package update)
115+
EXISTING_PASS="$(json_get '.AdminHelperDB.Password')"
116+
117+
if [ -z "${EXISTING_PASS}" ]; then
118+
DB_PASS="$(openssl rand -base64 16 | tr -d '/+=' | head -c 20)"
119+
echo "==> Generated new database password."
120+
else
121+
DB_PASS="${EXISTING_PASS}"
122+
echo "==> Using existing database password from config."
123+
fi
124+
125+
echo "==> Configuring MySQL/MariaDB for ${DB_NAME}..."
126+
127+
mysql -u root <<SQL
128+
CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
129+
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
130+
GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost';
131+
FLUSH PRIVILEGES;
132+
SQL
133+
134+
# Rolling out the scheme (IF NOT EXISTS — safe when updating)
135+
mysql -u root "${DB_NAME}" < "${SCHEMA_FILE}"
136+
137+
# Write credentials to tgadmin.json
138+
json_set \
139+
--arg user "${DB_USER}" \
140+
--arg pass "${DB_PASS}" \
141+
--arg db "${DB_NAME}" \
142+
'.AdminHelperDB.User = $user | .AdminHelperDB.Password = $pass | .AdminHelperDB.Database = $db'
143+
144+
echo "==> Database configured. Credentials saved to ${CONFIG_FILE}"
145+
else
146+
echo "WARNING: MySQL/MariaDB not found or not running." >&2
147+
echo " Install it, then run: sudo dpkg-reconfigure ${PACKAGE_NAME}" >&2
148+
fi
149+
150+
# Configuration rights — only root and the service group
151+
chown "root:${SERVICE_GROUP}" "${CONFIG_FILE}" 2>/dev/null || chown root:root "${CONFIG_FILE}"
152+
chmod 0640 "${CONFIG_FILE}"
153+
;;
154+
esac
155+
156+
exit 0

debian/DEBIAN/postrm

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
PACKAGE_NAME="tgadmin"
5+
CONFIG_FILE="/etc/${PACKAGE_NAME}/tgadmin.json"
6+
7+
DB_NAME="tgadmin"
8+
DB_USER="tgadmin"
9+
10+
mysql_available() {
11+
command -v mysql &>/dev/null && mysqladmin ping --silent 2>/dev/null
12+
}
13+
14+
case "$1" in
15+
purge)
16+
# purge = dpkg --purge, полное удаление с данными
17+
if mysql_available; then
18+
echo "==> Removing database and user for ${DB_NAME}..."
19+
mysql -u root <<SQL
20+
DROP DATABASE IF EXISTS \`${DB_NAME}\`;
21+
DROP USER IF EXISTS '${DB_USER}'@'localhost';
22+
FLUSH PRIVILEGES;
23+
SQL
24+
fi
25+
rm -f "${CONFIG_FILE}"
26+
rm -f "/etc/systemd/system/${PACKAGE_NAME}.service.d/10-run-as.conf"
27+
rmdir --ignore-fail-on-non-empty "/etc/systemd/system/${PACKAGE_NAME}.service.d"
28+
rmdir --ignore-fail-on-non-empty "/etc/${PACKAGE_NAME}"
29+
;;
30+
remove)
31+
# remove = dpkg --remove, конфиг и данные остаются
32+
;;
33+
esac
34+
35+
exit 0

debian/etc/default/tgadmin

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Service identity used by postinst when configuring tgadmin.service.
2+
#
3+
# By default package runs daemon as the system account "tgadmin".
4+
# Override only if you need a different existing or system account.
5+
#
6+
# Example:
7+
# TGADMIN_SERVICE_USER=mybot
8+
# TGADMIN_SERVICE_GROUP=mybot
9+
10+
TGADMIN_SERVICE_USER=tgadmin
11+
TGADMIN_SERVICE_GROUP=tgadmin
File renamed without changes.

0 commit comments

Comments
 (0)