Files
strongswan-ext/src/frontends/android/scripts/upload-apk.mjs
T
DenozordecandClaude Opus 5.5 68ee051ab2
Android / pre-check (push) Failing after 8s
Android / android (push) Skipped
CIFuzz / pre-check (push) Failing after 3s
CIFuzz / fuzzing (address) (push) Skipped
CIFuzz / fuzzing (undefined) (push) Skipped
CodeQL / pre-check (push) Failing after 4s
CodeQL / analyze (python) (push) Skipped
CodeQL / analyze (ruby) (push) Skipped
CodeQL / analyze-cpp (push) Skipped
FreeBSD / pre-check (push) Failing after 3s
FreeBSD / freebsd (14.4) (push) Skipped
FreeBSD / freebsd (15.0) (push) Skipped
Linux / pre-check (push) Failing after 4s
Linux / latest (apidoc) (push) Skipped
Linux / latest (clang, no, all) (push) Skipped
Linux / latest (clang, no, default) (push) Skipped
Linux / latest (clang, no, printf-builtin) (push) Skipped
Linux / latest (clang, no-dbg) (push) Skipped
Linux / latest (clang, no-testable-ke) (push) Skipped
Linux / latest (clang, yes, all) (push) Skipped
Linux / latest (clang, yes, default) (push) Skipped
Linux / latest (clang, yes, fuzzing) (push) Skipped
Linux / latest (coverage) (push) Skipped
Linux / latest (dist) (push) Skipped
Linux / latest (gcc, no, all) (push) Skipped
Linux / latest (gcc, no, default) (push) Skipped
Linux / latest (gcc, no, printf-builtin) (push) Skipped
Linux / latest (gcc, yes, all) (push) Skipped
Linux / latest (gcc, yes, default) (push) Skipped
Linux / latest (nm) (push) Skipped
Linux / latest (no-dbg) (push) Skipped
Linux / latest (no-testable-ke) (push) Skipped
Linux / latest (yes, ld) (push) Skipped
Linux / crypto (ubuntu-22.04, openssl-sys) (push) Skipped
Linux / crypto (ubuntu-latest, botan) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-3) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-4) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-awslc) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-sys) (push) Skipped
Linux / crypto (ubuntu-latest, wolfssl) (push) Skipped
Linux / older (clang, ubuntu-22.04, all) (push) Skipped
Linux / older (gcc, ubuntu-22.04, all) (push) Skipped
Linux / older (gcc, ubuntu-22.04, nm) (push) Skipped
Linux / alpine (push) Skipped
macOS / pre-check (push) Failing after 4s
macOS / macos (macos-14) (push) Skipped
macOS / macos (macos-latest) (push) Skipped
SonarCloud / pre-check (push) Failing after 3s
SonarCloud / sonarcloud (push) Skipped
TKM / pre-check (push) Failing after 3s
TKM / tkm (push) Skipped
Windows / pre-check (push) Failing after 4s
Windows / cross-compile (win32) (push) Skipped
Windows / cross-compile (win64) (push) Skipped
Windows / native (i686, mingw32, win32) (push) Skipped
Windows / native (x86_64, mingw64, win64) (push) Skipped
android: version 2.19.4 (172) — обновления и отчёты через панель SHX VPN
- проверка обновлений через API панели (get.shnt.top/api/v1): подписанный запрос (HMAC),
  манифест с подписью Ed25519; старый бакет — только запасной путь при сетевой ошибке
- экраны обновления по макету: страница «Обновления» с каналом и кодом беты, шторки
  «Что нового», «Скачивание» (с отменой), «Обязательное обновление», «Не установлено»
- отправка отчёта из журнала в панель: код отчёта, запись в «Событиях», письмо как запасной путь;
  к отчёту прикладываются результаты проверки сети (checks.json) и diag.log
- исправлен вылет при скачивании обновления на Android 16.0 (setRequestPromotedOngoing есть только с API 36.1)
- кнопки шторок переносят текст на вторую строку, однословные уменьшаются
- задача Gradle uploadShx: сборка и загрузка APK в панель (scripts/upload-apk.mjs)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 22:38:45 +07:00

108 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
// Загрузка собранного APK в панель SHX VPN с компьютера сборки. Нужен только Node 18+, без зависимостей.
//
// node upload-apk.mjs app-release.apk --type phone --channel beta [--publish] [--rollout 100] [--notes "Что нового"]
//
// Окружение:
// SHX_URL адрес консоли, например https://apk.shnt.top
// SHX_TOKEN токен CI из «Настройки → Токены» (upload — только черновик, publish — можно публиковать)
//
// versionName и versionCode берутся из output-metadata.json, который Gradle кладёт рядом с APK,
// либо из --version-name / --version-code.
//
// Шаги: билет на загрузку → файл напрямую в S3 по presigned PUT → сервер проверяет SHA-256 и оформляет версию.
import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { existsSync, readFileSync, statSync } from "node:fs";
import { dirname, join } from "node:path";
import { parseArgs } from "node:util";
const { values: o, positionals } = parseArgs({
allowPositionals: true,
options: {
type: { type: "string" },
channel: { type: "string", default: "beta" },
"version-name": { type: "string" },
"version-code": { type: "string" },
notes: { type: "string", default: "" },
"notes-en": { type: "string", default: "" },
publish: { type: "boolean", default: false },
rollout: { type: "string", default: "100" },
help: { type: "boolean", short: "h", default: false },
},
});
const die = (msg) => { console.error("✗ " + msg); process.exit(1); };
if (o.help || positionals.length !== 1) {
console.log("node upload-apk.mjs <файл.apk> --type <id типа> [--channel beta|release] [--publish] [--rollout 1-100] [--notes текст] [--version-name 2.20.0 --version-code 220]");
process.exit(o.help ? 0 : 1);
}
// Windows: переменные, заданные через [Environment]::SetEnvironmentVariable(..., "User"), видны только новым
// процессам. Если терминал (или Gradle daemon) открыт раньше, читаем их из реестра пользователя.
function userEnv(name) {
if (process.env[name]) return process.env[name];
if (process.platform !== "win32") return "";
try {
const out = execFileSync("reg", ["query", "HKCU\\Environment", "/v", name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
return /REG_(?:EXPAND_)?SZ\s+(.*)$/m.exec(out)?.[1]?.trim() ?? "";
} catch {
return "";
}
}
const base = userEnv("SHX_URL").replace(/\/$/, "");
const token = userEnv("SHX_TOKEN");
if (!base) die("Задайте SHX_URL (адрес консоли)");
if (!token) die("Задайте SHX_TOKEN (Настройки → Токены в консоли)");
if (!o.type) die("Укажите --type (id типа приложения из раздела «Типы приложений»)");
if (!["beta", "release"].includes(o.channel)) die("--channel: beta или release");
const rolloutPct = Number(o.rollout);
if (!Number.isInteger(rolloutPct) || rolloutPct < 1 || rolloutPct > 100) die("--rollout: целое от 1 до 100");
const file = positionals[0];
if (!existsSync(file)) die("Нет файла " + file);
let versionName = o["version-name"];
let versionCode = o["version-code"] ? Number(o["version-code"]) : undefined;
const meta = join(dirname(file), "output-metadata.json");
if ((!versionName || !versionCode) && existsSync(meta)) {
const el = JSON.parse(readFileSync(meta, "utf8")).elements?.[0];
versionName ??= el?.versionName;
versionCode ??= el?.versionCode;
}
if (!versionName || !Number.isInteger(versionCode)) die("Не удалось определить версию: передайте --version-name и --version-code");
const bytes = readFileSync(file);
const sha256 = createHash("sha256").update(bytes).digest("hex");
const mb = (statSync(file).size / 1048576).toFixed(1);
async function api(path, body) {
const r = await fetch(base + path, {
method: "POST",
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
body: JSON.stringify(body),
});
const data = await r.json().catch(() => ({}));
if (!r.ok) die(`${path}: ${r.status} ${data?.error?.message ?? r.statusText}`);
return data;
}
console.log(`→ ${o.type} / ${o.channel} / ${versionName} (${versionCode}), ${mb} МБ, sha256 ${sha256.slice(0, 12)}…`);
const ticket = await api("/api/admin/versions/uploads", { typeId: o.type, channel: o.channel, versionName, versionCode });
if (bytes.length > ticket.maxBytes) die("Файл больше допустимого размера");
const put = await fetch(ticket.url, { method: ticket.method, headers: ticket.headers, body: bytes });
if (!put.ok) die(`Загрузка в хранилище: ${put.status} ${await put.text().catch(() => "")}`.slice(0, 300));
console.log("✓ файл в хранилище");
const v = await api("/api/admin/versions", {
uploadId: ticket.uploadId,
sha256,
notes: { ru: o.notes, en: o["notes-en"] },
publish: o.publish,
rolloutPct,
});
console.log(`✓ версия оформлена: ${v.versionName}, статус ${v.status}${o.publish ? `, раскатка ${rolloutPct}%` : " (черновик: опубликуйте в консоли)"}`);
console.log(` ${base}/versions/${v.id}`);