Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4b841a8d8 |
No files matched your search
@@ -1,5 +1,5 @@
|
||||
# Rust-часть десктопа: прокси и проверки URL. Полная сборка инсталлятора — локально
|
||||
# (`pnpm --filter @pipu/desktop bundle`): на раннере нет Windows и WebKitGTK.
|
||||
# Rust-часть десктопа: прокси и проверки URL. Установщики — в release.yml
|
||||
# (Linux на ubuntu-latest, Windows NSIS на windows-latest).
|
||||
name: ci / desktop
|
||||
|
||||
on:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Релиз по тегу vMAJOR.MINOR.PATCH[-rc.N]: Gitea Release с заметками → APK.
|
||||
# Релиз создаётся сразу (статус Android — «собирается»); сборка APK докладывает в него файлы
|
||||
# и переписывает статус: собран / не удалась (scripts/release.mjs).
|
||||
# Релиз по тегу vMAJOR.MINOR.PATCH[-rc.N]: Gitea Release с заметками → APK, Linux и Windows.
|
||||
# Релиз создаётся сразу (статусы Android/десктоп — «собирается»); сборки докладывают файлы
|
||||
# и переписывают статус: собран / не удалась (scripts/release.mjs).
|
||||
# Docker-образы не публикуются: сервер собирает их сам из клонированного репозитория (deploy/update.sh).
|
||||
#
|
||||
# Секреты (Settings → Actions → Secrets):
|
||||
@@ -197,3 +197,141 @@ jobs:
|
||||
- name: Статус в релизе — сборка отменена
|
||||
if: cancelled()
|
||||
run: node scripts/release.mjs android cancelled
|
||||
|
||||
desktop-linux:
|
||||
name: desktop (Linux в релиз)
|
||||
needs: release
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
env:
|
||||
PIPU_VERSION: ${{ needs.release.outputs.version }}
|
||||
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
PUBLIC_URL: ${{ vars.PUBLIC_URL || 'https://gitea.hsrv.site' }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
VERSION: ${{ needs.release.outputs.version }}
|
||||
APPIMAGE_EXTRACT_AND_RUN: "1"
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Статус в релизе — собирается
|
||||
run: node scripts/release.mjs desktop linux building
|
||||
- name: Rust
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
- run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file \
|
||||
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev \
|
||||
libdbus-1-dev libsecret-1-dev patchelf libfuse2
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
key: cargo-desktop-${{ hashFiles('apps/desktop/src-tauri/Cargo.toml') }}
|
||||
restore-keys: cargo-desktop-
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- name: Сборка AppImage и deb
|
||||
working-directory: apps/desktop
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pnpm bundle:linux
|
||||
bundle=src-tauri/target/release/bundle
|
||||
shopt -s nullglob
|
||||
for f in "$bundle"/appimage/*.AppImage; do
|
||||
cp "$f" "$GITHUB_WORKSPACE/pipu-music-${PIPU_VERSION}-linux.AppImage"
|
||||
done
|
||||
for f in "$bundle"/deb/*.deb; do
|
||||
cp "$f" "$GITHUB_WORKSPACE/pipu-music-${PIPU_VERSION}-linux.deb"
|
||||
done
|
||||
ls -lh "$GITHUB_WORKSPACE"/pipu-music-${PIPU_VERSION}-linux.*
|
||||
- name: Файлы в релиз
|
||||
run: |
|
||||
set -euo pipefail
|
||||
files=()
|
||||
for f in pipu-music-${PIPU_VERSION}-linux.AppImage pipu-music-${PIPU_VERSION}-linux.deb; do
|
||||
[ -f "$f" ] && files+=("$f")
|
||||
done
|
||||
if [ ${#files[@]} -eq 0 ]; then
|
||||
echo "::error::нет AppImage и deb после сборки"
|
||||
exit 1
|
||||
fi
|
||||
node scripts/release.mjs desktop linux success "${files[@]}"
|
||||
- name: Статус в релизе — сборка не удалась
|
||||
if: failure()
|
||||
run: node scripts/release.mjs desktop linux failure
|
||||
- name: Статус в релизе — сборка отменена
|
||||
if: cancelled()
|
||||
run: node scripts/release.mjs desktop linux cancelled
|
||||
|
||||
# Нужен act_runner с меткой windows-latest (Windows + MSVC Build Tools + WebView2).
|
||||
# Без такого раннера job останется в очереди; Tauri не кросс-компилирует NSIS с Linux.
|
||||
desktop-windows:
|
||||
name: desktop (Windows в релиз)
|
||||
needs: release
|
||||
timeout-minutes: 60
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
env:
|
||||
PIPU_VERSION: ${{ needs.release.outputs.version }}
|
||||
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
PUBLIC_URL: ${{ vars.PUBLIC_URL || 'https://gitea.hsrv.site' }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
VERSION: ${{ needs.release.outputs.version }}
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
|
||||
- name: Статус в релизе — собирается
|
||||
run: node scripts/release.mjs desktop windows building
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Rust
|
||||
run: |
|
||||
$init = Join-Path $env:TEMP 'rustup-init.exe'
|
||||
Invoke-WebRequest -Uri 'https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe' -OutFile $init
|
||||
& $init -y --default-toolchain stable --profile minimal --default-host x86_64-pc-windows-msvc --no-modify-path
|
||||
Add-Content -Path $env:GITHUB_PATH -Value "$env:USERPROFILE\.cargo\bin"
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
key: cargo-desktop-windows-${{ hashFiles('apps/desktop/src-tauri/Cargo.toml') }}
|
||||
restore-keys: cargo-desktop-windows-
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- name: Сборка NSIS
|
||||
working-directory: apps/desktop
|
||||
run: |
|
||||
pnpm bundle:windows
|
||||
$bundle = Join-Path (Get-Location) 'src-tauri/target/release/bundle/nsis'
|
||||
$src = Get-ChildItem -Path $bundle -Filter '*-setup.exe' | Select-Object -First 1
|
||||
if (-not $src) { throw "нет NSIS setup.exe после сборки в $bundle" }
|
||||
$dest = Join-Path $env:GITHUB_WORKSPACE "pipu-music-$env:PIPU_VERSION-windows-setup.exe"
|
||||
Copy-Item $src.FullName $dest
|
||||
Get-Item $dest | Format-List Name, Length
|
||||
- name: Файл в релиз
|
||||
run: |
|
||||
$file = "pipu-music-$env:PIPU_VERSION-windows-setup.exe"
|
||||
if (-not (Test-Path $file)) { throw "нет $file после сборки" }
|
||||
node scripts/release.mjs desktop windows success $file
|
||||
- name: Статус в релизе — сборка не удалась
|
||||
if: failure()
|
||||
run: node scripts/release.mjs desktop windows failure
|
||||
- name: Статус в релизе — сборка отменена
|
||||
if: cancelled()
|
||||
run: node scripts/release.mjs desktop windows cancelled
|
||||
@@ -5,12 +5,14 @@ import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = dirname(fileURLToPath(import.meta.url));
|
||||
const conf = JSON.parse(readFileSync(resolve(root, 'src-tauri/tauri.conf.json'), 'utf8'));
|
||||
let version = '0.0.0';
|
||||
try {
|
||||
let version = process.env.PIPU_VERSION ?? '0.0.0';
|
||||
if (!process.env.PIPU_VERSION) {
|
||||
try {
|
||||
const tag = execFileSync('git', ['describe', '--tags', '--abbrev=0'], { encoding: 'utf8' }).trim();
|
||||
version = tag.replace(/^v/, '').replace(/-rc\.\d+$/, '');
|
||||
} catch {
|
||||
version = tag.replace(/^v/, '');
|
||||
} catch {
|
||||
// local untagged build
|
||||
}
|
||||
}
|
||||
conf.version = version;
|
||||
writeFileSync(resolve(root, 'src-tauri/tauri.release.json'), JSON.stringify(conf, null, 2));
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "tauri dev",
|
||||
"bundle": "node inject-version.mjs && tauri build --config src-tauri/tauri.release.json",
|
||||
"bundle:linux": "node inject-version.mjs && tauri build --config src-tauri/tauri.release.json --bundles appimage,deb",
|
||||
"bundle:windows": "node inject-version.mjs && tauri build --config src-tauri/tauri.release.json --bundles nsis",
|
||||
"tauri": "tauri",
|
||||
"icons": "tauri icon ../web/public/favicon.svg"
|
||||
},
|
||||
|
||||
+3
-2
@@ -103,8 +103,9 @@
|
||||
|
||||
Android-приложение обновляется само: при запуске оно сверяется с сервером и предлагает новый APK.
|
||||
Раздать приложение — ссылка `https://<ваш-домен>/download` (без входа; `/android` перенаправляет сюда):
|
||||
сборка под систему посетителя (APK, Windows, Linux, macOS), остальные платформы ниже, адрес сервера
|
||||
для копирования и короткая инструкция. Та же страница — внизу экрана входа и в меню профиля.
|
||||
сборка под систему посетителя, остальные платформы ниже, адрес сервера для копирования и короткая инструкция.
|
||||
APK, Linux (AppImage, deb) и Windows (NSIS) прикладываются к релизу Gitea автоматически. Та же страница — внизу экрана входа
|
||||
и в меню профиля.
|
||||
|
||||
## 7. Наблюдение
|
||||
|
||||
|
||||
+1
-1
@@ -14,4 +14,4 @@ pnpm --filter @pipu/desktop bundle
|
||||
|
||||
На Windows получается NSIS-установщик, на Linux — AppImage, deb и rpm. Зависимости Linux: WebKitGTK 4.1, AppIndicator, libsecret, GStreamer.
|
||||
|
||||
Готовые файлы с последнего релиза Gitea сервер отдаёт на публичной странице `/download`: посетителю сразу предлагается сборка его ОС, остальные платформы и Android — ниже.
|
||||
Релиз по тегу прикладывает Linux AppImage/`.deb` (`pipu-music-<версия>-linux.*`) и Windows NSIS (`pipu-music-<версия>-windows-setup.exe`). Windows-job в `release.yml` ждёт act_runner с меткой `windows-latest` (MSVC + WebView2). Страница `/download` на сервере отдаёт те файлы, что есть в последнем релизе Gitea.
|
||||
+100
-15
@@ -11,7 +11,7 @@
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { basename, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
@@ -29,6 +29,12 @@ const COMMIT_RE = /^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/;
|
||||
|
||||
export const STATUS_START = '<!-- android-status -->';
|
||||
export const STATUS_END = '<!-- /android-status -->';
|
||||
export const DESKTOP_PLATFORMS = ['linux', 'windows'];
|
||||
|
||||
export function desktopStatusMarks(platform) {
|
||||
if (!DESKTOP_PLATFORMS.includes(platform)) throw new Error(`unknown desktop platform: ${platform}`);
|
||||
return { start: `<!-- desktop-${platform}-status -->`, end: `<!-- /desktop-${platform}-status -->` };
|
||||
}
|
||||
|
||||
/** Раскладывает коммиты (Conventional Commits) по разделам заметок. */
|
||||
export function groupCommits(commits) {
|
||||
@@ -76,16 +82,62 @@ export function renderAndroidStatus({ status, runUrl, versionCode, apk, signing
|
||||
return [STATUS_START, ...lines.map((l) => `> ${l}`), STATUS_END].join('\n');
|
||||
}
|
||||
|
||||
/** Заменяет блок статуса в тексте релиза (или дописывает его в начало, если блока нет). */
|
||||
export function replaceAndroidStatus(body, block) {
|
||||
const start = body.indexOf(STATUS_START);
|
||||
const end = body.indexOf(STATUS_END);
|
||||
if (start === -1 || end === -1 || end < start) return `${block}\n\n${body}`;
|
||||
return body.slice(0, start) + block + body.slice(end + STATUS_END.length);
|
||||
/** Блок статуса сборки десктопа в тексте релиза (отдельный для linux и windows). */
|
||||
export function renderDesktopStatus({ platform, status, runUrl, files }) {
|
||||
const label = platform === 'windows' ? 'Windows' : 'Linux';
|
||||
const pending = platform === 'windows' ? 'NSIS-установщик' : 'AppImage и .deb';
|
||||
const lines =
|
||||
status === 'success'
|
||||
? [
|
||||
`✅ **Десктоп (${label}):** собрано — ${files.map((f) => `\`${f.name}\` (${formatSize(f.size)})`).join(', ')}.`,
|
||||
]
|
||||
: status === 'cancelled'
|
||||
? [
|
||||
`⏹ **Десктоп (${label}):** сборка отменена — [лог сборки](${runUrl}).`,
|
||||
'Файлы появятся здесь после перезапуска сборки.',
|
||||
]
|
||||
: status === 'failure'
|
||||
? [
|
||||
`❌ **Десктоп (${label}):** сборка не удалась — [лог сборки](${runUrl}).`,
|
||||
'Остальные клиенты от этого не зависят; установщик появится после перезапуска сборки.',
|
||||
]
|
||||
: [
|
||||
`⏳ **Десктоп (${label}):** собирается — [ход сборки](${runUrl}).`,
|
||||
`${pending} появится в этом релизе автоматически.`,
|
||||
];
|
||||
const { start, end } = desktopStatusMarks(platform);
|
||||
return [start, ...lines.map((l) => `> ${l}`), end].join('\n');
|
||||
}
|
||||
|
||||
/** Текст релиза: статус Android, изменения, обновление сервера, установка APK. */
|
||||
export function renderNotes({ version, tag, prevTag, prerelease, repoUrl, groups, androidStatus }) {
|
||||
function replaceBlock(body, startMark, endMark, block) {
|
||||
const start = body.indexOf(startMark);
|
||||
const end = body.indexOf(endMark);
|
||||
if (start === -1 || end === -1 || end < start) return `${block}\n\n${body}`;
|
||||
return body.slice(0, start) + block + body.slice(end + endMark.length);
|
||||
}
|
||||
|
||||
/** Заменяет блок статуса Android в тексте релиза (или дописывает его в начало, если блока нет). */
|
||||
export function replaceAndroidStatus(body, block) {
|
||||
return replaceBlock(body, STATUS_START, STATUS_END, block);
|
||||
}
|
||||
|
||||
export function replaceDesktopStatus(body, platform, block) {
|
||||
const { start, end } = desktopStatusMarks(platform);
|
||||
return replaceBlock(body, start, end, block);
|
||||
}
|
||||
|
||||
/** Текст релиза: статусы сборок, изменения, обновление сервера, установка клиентов. */
|
||||
export function renderNotes({
|
||||
version,
|
||||
tag,
|
||||
prevTag,
|
||||
prerelease,
|
||||
repoUrl,
|
||||
groups,
|
||||
androidStatus,
|
||||
desktopLinuxStatus,
|
||||
desktopWindowsStatus,
|
||||
}) {
|
||||
const link = (it) => `([${it.sha.slice(0, 7)}](${repoUrl}/commit/${it.sha}))`;
|
||||
const item = (it) => `- ${it.scope ? `**${it.scope}:** ` : ''}${it.text} ${link(it)}`;
|
||||
const out = [];
|
||||
@@ -93,7 +145,10 @@ export function renderNotes({ version, tag, prevTag, prerelease, repoUrl, groups
|
||||
if (prerelease) {
|
||||
out.push('> 🧪 **Предварительная версия** — для проверки перед релизом, не для основной установки.', '');
|
||||
}
|
||||
out.push(androidStatus, '', '## Что изменилось', '');
|
||||
out.push(androidStatus, '');
|
||||
if (desktopLinuxStatus) out.push(desktopLinuxStatus, '');
|
||||
if (desktopWindowsStatus) out.push(desktopWindowsStatus, '');
|
||||
out.push('## Что изменилось', '');
|
||||
if (groups.breaking.length) {
|
||||
out.push('### ⚠️ Несовместимые изменения', '', ...groups.breaking.map(item), '');
|
||||
}
|
||||
@@ -124,11 +179,14 @@ export function renderNotes({ version, tag, prevTag, prerelease, repoUrl, groups
|
||||
'',
|
||||
`Ожидается \`"version":"${version}"\`.`,
|
||||
'',
|
||||
'## Android',
|
||||
'## Клиенты',
|
||||
'',
|
||||
`- **Установка или обновление:** скачайте \`pipu-music-${version}.apk\` ниже и откройте его на телефоне;`,
|
||||
' Android попросит разрешить установку из этого источника. Обновление ставится поверх, данные сохраняются.',
|
||||
`- **Проверка файла:** \`sha256sum pipu-music-${version}.apk\` должен совпасть с \`pipu-music-${version}.apk.sha256\`.`,
|
||||
'Скачайте с этой страницы или с `/download` на сервере (подскажет сборку под вашу систему).',
|
||||
'',
|
||||
`- **Android:** \`pipu-music-${version}.apk\` — откройте на телефоне; Android попросит разрешить установку из этого источника.`,
|
||||
`- **Windows:** \`pipu-music-${version}-windows-setup.exe\`.`,
|
||||
`- **Linux:** \`pipu-music-${version}-linux.AppImage\` (сделайте исполняемым) или \`.deb\`.`,
|
||||
`- **Проверка APK:** \`sha256sum pipu-music-${version}.apk\` должен совпасть с \`pipu-music-${version}.apk.sha256\`.`,
|
||||
'',
|
||||
`Первая установка сервера — [инструкция](${repoUrl}/src/tag/${tag}/docs/install-guide.md).`,
|
||||
'',
|
||||
@@ -202,6 +260,8 @@ async function create() {
|
||||
repoUrl,
|
||||
groups: groupCommits(commitsBetween(prevTag, tag)),
|
||||
androidStatus: renderAndroidStatus({ status: 'building', runUrl }),
|
||||
desktopLinuxStatus: renderDesktopStatus({ platform: 'linux', status: 'building', runUrl }),
|
||||
desktopWindowsStatus: renderDesktopStatus({ platform: 'windows', status: 'building', runUrl }),
|
||||
});
|
||||
const fields = { name: `Pipu Music ${env('VERSION')}`, body, prerelease, draft: false };
|
||||
const saved = release
|
||||
@@ -242,13 +302,38 @@ async function android(status, apkPath) {
|
||||
console.log(`статус Android: ${status}`);
|
||||
}
|
||||
|
||||
async function desktop(platform, status, paths) {
|
||||
const { release, runUrl } = await context();
|
||||
if (!release) throw new Error(`релиз для ${env('TAG')} не найден — сначала node scripts/release.mjs create`);
|
||||
const files = [];
|
||||
if (status === 'success') {
|
||||
if (!paths.length) throw new Error('desktop success: укажите файлы');
|
||||
for (const file of paths) {
|
||||
const data = readFileSync(file);
|
||||
files.push({ name: basename(file), size: data.length });
|
||||
const sum = `${file}.sha256`;
|
||||
if (!existsSync(sum)) writeFileSync(sum, `${createHash('sha256').update(data).digest('hex')}\n`);
|
||||
await upload(release, file);
|
||||
if (existsSync(sum)) await upload(release, sum);
|
||||
}
|
||||
}
|
||||
const block = renderDesktopStatus({ platform, status, runUrl, files });
|
||||
await api('PATCH', `/releases/${release.id}`, { json: { body: replaceDesktopStatus(release.body ?? '', platform, block) } });
|
||||
console.log(`статус десктопа (${platform}): ${status}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [cmd, ...args] = process.argv.slice(2);
|
||||
if (cmd === 'create') return create();
|
||||
if (cmd === 'android' && ['building', 'success', 'failure', 'cancelled'].includes(args[0])) {
|
||||
return android(args[0], args[1]);
|
||||
}
|
||||
throw new Error('использование: release.mjs create | android building|failure|cancelled | android success <file.apk>');
|
||||
if (cmd === 'desktop' && DESKTOP_PLATFORMS.includes(args[0]) && ['building', 'success', 'failure', 'cancelled'].includes(args[1])) {
|
||||
return desktop(args[0], args[1], args.slice(2));
|
||||
}
|
||||
throw new Error(
|
||||
'использование: release.mjs create | android building|failure|cancelled | android success <file.apk> | desktop linux|windows building|failure|cancelled | desktop linux|windows success <file...>',
|
||||
);
|
||||
}
|
||||
|
||||
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
||||
|
||||
@@ -4,8 +4,11 @@ import { test } from 'node:test';
|
||||
import {
|
||||
groupCommits,
|
||||
renderAndroidStatus,
|
||||
renderDesktopStatus,
|
||||
renderNotes,
|
||||
replaceAndroidStatus,
|
||||
replaceDesktopStatus,
|
||||
desktopStatusMarks,
|
||||
STATUS_END,
|
||||
STATUS_START,
|
||||
} from './release.mjs';
|
||||
@@ -77,6 +80,34 @@ test('adds a status block when the body has none', () => {
|
||||
assert.ok(replaceAndroidStatus('hand-written notes', block).startsWith(block));
|
||||
});
|
||||
|
||||
test('desktop status blocks', () => {
|
||||
const linuxMarks = desktopStatusMarks('linux');
|
||||
const winMarks = desktopStatusMarks('windows');
|
||||
const building = renderDesktopStatus({ platform: 'linux', status: 'building', runUrl: 'https://run' });
|
||||
assert.ok(building.startsWith(linuxMarks.start) && building.endsWith(linuxMarks.end));
|
||||
assert.match(building, /Десктоп \(Linux\):.*собирается — \[ход сборки\]\(https:\/\/run\)/);
|
||||
const ok = renderDesktopStatus({
|
||||
platform: 'linux',
|
||||
status: 'success',
|
||||
files: [{ name: 'pipu-music-1.4.0-linux.AppImage', size: 40 * 1024 * 1024 }],
|
||||
});
|
||||
assert.match(ok, /`pipu-music-1\.4\.0-linux\.AppImage` \(40,0 МБ\)/);
|
||||
assert.equal(replaceDesktopStatus(`a\n\n${building}\n\nb`, 'linux', ok).includes(building), false);
|
||||
|
||||
const winBuilding = renderDesktopStatus({ platform: 'windows', status: 'building', runUrl: 'https://run' });
|
||||
assert.ok(winBuilding.startsWith(winMarks.start) && winBuilding.endsWith(winMarks.end));
|
||||
const winOk = renderDesktopStatus({
|
||||
platform: 'windows',
|
||||
status: 'success',
|
||||
files: [{ name: 'pipu-music-1.4.0-windows-setup.exe', size: 12 * 1024 * 1024 }],
|
||||
});
|
||||
const both = `${building}\n\n${winBuilding}`;
|
||||
const patched = replaceDesktopStatus(both, 'windows', winOk);
|
||||
assert.match(patched, /Linux/);
|
||||
assert.match(patched, /windows-setup\.exe/);
|
||||
assert.doesNotMatch(patched, /Десктоп \(Windows\):.*собирается/);
|
||||
});
|
||||
|
||||
test('release notes', () => {
|
||||
const notes = renderNotes({
|
||||
version: '0.2.0',
|
||||
@@ -86,12 +117,16 @@ test('release notes', () => {
|
||||
repoUrl,
|
||||
groups: groupCommits([{ sha: sha(1), subject: 'feat(web): library screen' }]),
|
||||
androidStatus: renderAndroidStatus({ status: 'building', runUrl: 'https://run' }),
|
||||
desktopLinuxStatus: renderDesktopStatus({ platform: 'linux', status: 'building', runUrl: 'https://run' }),
|
||||
desktopWindowsStatus: renderDesktopStatus({ platform: 'windows', status: 'building', runUrl: 'https://run' }),
|
||||
});
|
||||
assert.match(notes, /### Новое\n\n- \*\*web:\*\* library screen \(\[1111111\]\(https:\/\/git\.example\/owner\/pipu-music\/commit\/1{40}\)\)/);
|
||||
assert.match(notes, /\[v0\.1\.7\.\.\.v0\.2\.0\]\(https:\/\/git\.example\/owner\/pipu-music\/compare\/v0\.1\.7\.\.\.v0\.2\.0\)/);
|
||||
assert.match(notes, /\.\/update\.sh v0\.2\.0/);
|
||||
assert.match(notes, /"version":"0\.2\.0"/);
|
||||
assert.match(notes, /pipu-music-0\.2\.0\.apk/);
|
||||
assert.match(notes, /pipu-music-0\.2\.0-windows-setup\.exe/);
|
||||
assert.match(notes, /pipu-music-0\.2\.0-linux\.AppImage/);
|
||||
assert.match(notes, /src\/tag\/v0\.2\.0\/docs\/install-guide\.md/);
|
||||
assert.doesNotMatch(notes, /Предварительная версия/);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user