Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"батчі",
"бекендову",
"білда",
"білдів",
"блокерів",
"Бриф",
"брифі",
Expand Down Expand Up @@ -183,7 +184,9 @@
"прокинуті",
"пропси",
"пропсом",
"пуші",
"ранів",
"релізні",
"релізній",
"рендер",
"рендериться",
Expand Down
72 changes: 72 additions & 0 deletions .github/actions/generate-scoped-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-action.json

name: Generate scoped release
description: >-
Створює або перевикористовує GitHub Release під поточним тегом (context.ref),
з release-notes, порахованими явно від попереднього тега тієї самої
продуктової сім'ї (спільний префікс до "@" — app@/owner@), а не
автовизначенням GitHub. Автовизначення "залипає" на найстарішій базі, коли
релізні коміти йдуть від github-actions[bot]: воно не бачить бот-коміти як
"попередній реліз" і щоразу рахує дельту від останнього людського тега.

inputs:
release-name-prefix:
description: Префікс людської назви релізу (напр. "task" для app, "owner" для owner)
required: true

outputs:
release-id:
description: ID створеного/перевикористаного GitHub Release
value: ${{ steps.create-release.outputs.result }}

runs:
using: composite
steps:
- name: Create or reuse release (scoped release-notes)
id: create-release
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
result-encoding: string
script: |
const tag = context.ref.replace('refs/tags/', '');
const params = { owner: context.repo.owner, repo: context.repo.repo };
// Продуктова сім'я — спільний префікс до "@" (app@0.6.11 → "app@"),
// щоб не переплутати з тегами іншого продукту в тому самому репо.
const prefix = tag.includes('@') ? tag.slice(0, tag.indexOf('@') + 1) : tag;

let release;
try {
release = (await github.rest.repos.getReleaseByTag({ ...params, tag })).data;
} catch {
const compareVersions = (a, b) => {
const pa = a.split('.').map(Number);
const pb = b.split('.').map(Number);
for (let i = 0; i < 3; i++) {
const diff = (pa[i] || 0) - (pb[i] || 0);
if (diff !== 0) return diff;
}
return 0;
};

const allTags = await github.paginate(github.rest.repos.listTags, { ...params, per_page: 100 });
const previousTag = allTags
.map((t) => t.name)
.filter((name) => name.startsWith(prefix) && name !== tag)
.sort((a, b) => compareVersions(b.slice(prefix.length), a.slice(prefix.length)))[0];

const notes = (await github.rest.repos.generateReleaseNotes({
...params,
tag_name: tag,
...(previousTag ? { previous_tag_name: previousTag } : {}),
})).data;

release = (await github.rest.repos.createRelease({
...params,
tag_name: tag,
name: `${{ inputs.release-name-prefix }} ${tag}`,
draft: false,
prerelease: false,
body: notes.body,
})).data;
}
return String(release.id);
18 changes: 9 additions & 9 deletions .github/workflows/changelog-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
#
# Автоматичний реліз із change-файлів: merge у main з {app,owner}/.changes/*.md →
# `n-cursor release` (бамп version + CHANGELOG.md + коміт "release: app@X, owner@Y" +
# теги app@X/owner@Y) → теги vX / owner-vX → dispatch release.yml /
# release-owner.yml (DMG-артефакти). Пуш тегів робиться GITHUB_TOKEN-ом і не
# тригерить workflows, тому build-workflow викликається явно через workflow_dispatch.
# теги app@X/owner@Y) → dispatch release.yml / release-owner.yml (--ref app@X /
# owner@Y) на DMG-артефакти. Пуш тегів робиться GITHUB_TOKEN-ом і не тригерить
# workflows, тому build-workflow викликається явно через workflow_dispatch.
# Один тег на продукт-реліз (app@X.Y.Z / owner@X.Y.Z) — він же changelog-якір,
# він же build-тригер; окремі vX/owner-vX теги (стара схема) більше не створюються.

name: Changelog Release

Expand Down Expand Up @@ -83,13 +85,11 @@ jobs:
TAGS=()
if [ -n "$APP_VER" ]; then
git tag "app@$APP_VER" 2>/dev/null || true
git tag "v$APP_VER"
TAGS+=("refs/tags/app@$APP_VER" "refs/tags/v$APP_VER")
TAGS+=("refs/tags/app@$APP_VER")
fi
if [ -n "$OWNER_VER" ]; then
git tag "owner@$OWNER_VER" 2>/dev/null || true
git tag "owner-v$OWNER_VER"
TAGS+=("refs/tags/owner@$OWNER_VER" "refs/tags/owner-v$OWNER_VER")
TAGS+=("refs/tags/owner@$OWNER_VER")
fi
git push origin HEAD:main "${TAGS[@]}"

Expand All @@ -98,11 +98,11 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
VER: ${{ steps.rel.outputs.app }}
run: gh workflow run release.yml --ref "v$VER"
run: gh workflow run release.yml --ref "app@$VER"

- name: Dispatch DMG release build (owner)
if: steps.rel.outputs.owner
env:
GH_TOKEN: ${{ github.token }}
VER: ${{ steps.rel.outputs.owner }}
run: gh workflow run release-owner.yml --ref "owner-v$VER"
run: gh workflow run release-owner.yml --ref "owner@$VER"
31 changes: 8 additions & 23 deletions .github/workflows/release-owner.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
#
# Реліз owner-застосунку (воркспейс owner/) — аналог release.yml для app.
# Тег owner-vX.Y.Z → GitHub-реліз "owner owner-vX.Y.Z" + підписаний DMG +
# Тег owner@X.Y.Z → GitHub-реліз "owner owner@X.Y.Z" + підписаний DMG +
# updater-артефакти (owner має tauri-plugin-updater, useUpdater()).

name: Release Owner

on:
push:
tags:
- 'owner@*'
- 'owner-v*'

workflow_dispatch: {}
Expand All @@ -23,34 +24,17 @@ jobs:
permissions:
contents: write
outputs:
release_id: ${{ steps.create-release.outputs.result }}
release_id: ${{ steps.create-release.outputs.release-id }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false

- name: Create or reuse release
id: create-release
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
uses: ./.github/actions/generate-scoped-release
with:
result-encoding: string
script: |
const tag = context.ref.replace('refs/tags/', '');
const params = { owner: context.repo.owner, repo: context.repo.repo };
let release;
try {
release = (await github.rest.repos.getReleaseByTag({ ...params, tag })).data;
} catch {
release = (await github.rest.repos.createRelease({
...params,
tag_name: tag,
name: `owner ${tag}`,
draft: false,
prerelease: false,
generate_release_notes: true,
})).data;
}
return String(release.id);
release-name-prefix: owner

build-desktop:
needs: create-release
Expand Down Expand Up @@ -95,7 +79,8 @@ jobs:

- name: Sync owner version from tag
run: |
VER="${GITHUB_REF_NAME#owner-v}"
VER="${GITHUB_REF_NAME#owner@}"
VER="${VER#owner-v}"
node -e "const fs=require('fs');const f='owner/src-tauri/tauri.conf.json';const c=JSON.parse(fs.readFileSync(f));c.version=process.argv[1];fs.writeFileSync(f,JSON.stringify(c,null,2)+'\n')" "$VER"
echo "tauri.conf.json version → $VER"

Expand Down Expand Up @@ -125,7 +110,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CHANNEL_TITLE: 'owner auto-update channel (службовий, не для людей)'
CHANNEL_NOTES: 'Стабільний тег для plugins.updater.endpoints у owner/src-tauri/tauri.conf.json. Автоматично пересувається кожним owner-v* релізом на свіжий latest.json. Дистрибутив бери з останнього справжнього owner-vX.Y.Z релізу.'
CHANNEL_NOTES: 'Стабільний тег для plugins.updater.endpoints у owner/src-tauri/tauri.conf.json. Автоматично пересувається кожним owner@* релізом на свіжий latest.json. Дистрибутив бери з останнього справжнього owner@X.Y.Z релізу.'
run: |
TAG="${GITHUB_REF_NAME}"
CHANNEL="owner-latest"
Expand Down
57 changes: 24 additions & 33 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ name: Release
on:
push:
tags:
- 'v*'
- app@*
- v*

workflow_dispatch: {}

Expand All @@ -19,34 +20,17 @@ jobs:
permissions:
contents: write
outputs:
release_id: ${{ steps.create-release.outputs.result }}
release_id: ${{ steps.create-release.outputs.release-id }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false

- name: Create or reuse release
id: create-release
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
uses: ./.github/actions/generate-scoped-release
with:
result-encoding: string
script: |
const tag = context.ref.replace('refs/tags/', '');
const params = { owner: context.repo.owner, repo: context.repo.repo };
let release;
try {
release = (await github.rest.repos.getReleaseByTag({ ...params, tag })).data;
} catch {
release = (await github.rest.repos.createRelease({
...params,
tag_name: tag,
name: `task ${tag}`,
draft: false,
prerelease: false,
generate_release_notes: true,
})).data;
}
return String(release.id);
release-name-prefix: task

build-desktop:
needs: create-release
Expand Down Expand Up @@ -91,7 +75,8 @@ jobs:

- name: Sync app version from tag
run: |
VER="${GITHUB_REF_NAME#v}"
VER="${GITHUB_REF_NAME#app@}"
VER="${VER#v}"
node -e "const fs=require('fs');const f='app/src-tauri/tauri.conf.json';const c=JSON.parse(fs.readFileSync(f));c.version=process.argv[1];fs.writeFileSync(f,JSON.stringify(c,null,2)+'\n')" "$VER"
echo "tauri.conf.json version → $VER"

Expand Down Expand Up @@ -120,25 +105,31 @@ jobs:

# app і owner ділять один репозиторій, тому GitHub-вбудований
# /releases/latest/ неоднозначний між продуктами (див. tauri.mdc release —
# той канон писаний під один продукт на репо). task-latest — стабільний
# той канон писаний під один продукт на репо). app-latest — стабільний
# службовий тег, який цей крок пересуває на свіжий latest.json щоразу;
# updater застосунку task (tauri.conf.json → plugins.updater.endpoints)
# звертається саме сюди, а не в спільний /latest/.
- name: Publish task-latest updater channel
# звертається саме сюди, а не в спільний /latest/. task-latest лишається
# постійним legacy-аліасом (той самий latest.json) для білдів, зібраних
# до переходу на app-latest — endpoint у них уже вшитий і не змінюється
# без перевстановлення.
- name: Publish app-latest / task-latest updater channels
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CHANNEL_TITLE: 'task auto-update channel (службовий, не для людей)'
CHANNEL_NOTES: 'Стабільний тег для plugins.updater.endpoints у app/src-tauri/tauri.conf.json. Автоматично пересувається кожним v* релізом на свіжий latest.json. Дистрибутив бери з останнього справжнього vX.Y.Z релізу.'
CHANNEL_NOTES: 'Стабільний тег для plugins.updater.endpoints у
app/src-tauri/tauri.conf.json. Автоматично пересувається кожним
app@* релізом на свіжий latest.json. Дистрибутив бери з останнього
справжнього app@X.Y.Z релізу.'
run: |
TAG="${GITHUB_REF_NAME}"
CHANNEL="task-latest"
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" -p latest.json -D /tmp/updater-channel --clobber
if gh release view "$CHANNEL" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release upload "$CHANNEL" /tmp/updater-channel/latest.json --repo "$GITHUB_REPOSITORY" --clobber
else
gh release create "$CHANNEL" /tmp/updater-channel/latest.json --repo "$GITHUB_REPOSITORY" --title "$CHANNEL_TITLE" --notes "$CHANNEL_NOTES" --prerelease --target main
fi

for CHANNEL in app-latest task-latest; do
if gh release view "$CHANNEL" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release upload "$CHANNEL" /tmp/updater-channel/latest.json --repo "$GITHUB_REPOSITORY" --clobber
else
gh release create "$CHANNEL" /tmp/updater-channel/latest.json --repo "$GITHUB_REPOSITORY" --title "$CHANNEL_TITLE" --notes "$CHANNEL_NOTES" --prerelease --target main
fi
done
# APK: вимагає ініціалізованого Android-проєкту (app/src-tauri/gen/android),
# якого в task ще немає. Після `bun --cwd=app run tauri android init` і коміту
# gen/android додай сюди job build-android (ідентичний до mlmail/myshare).
5 changes: 5 additions & 0 deletions app/.changes/260727-1511.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bump: minor
section: Changed
---
Реліз-пайплайн: build-тригер і changelog-якір об'єднано в один тег `app@X.Y.Z` (замість окремого `vX.Y.Z`); GitHub-реліз тепер рахує "What's Changed" явно від попереднього тега тієї самої продуктової сім'ї, а не від автовизначення GitHub (яке "залипало" на дуже старій базі). Updater переїхав на канал `app-latest` (`task-latest` лишається постійним legacy-аліасом для вже встановлених білдів).
2 changes: 1 addition & 1 deletion app/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDRGNjE4NzgyQ0E1NENFNjYKUldSbXpsVEtnb2RoVDBSTUViMXl1SXAzNDJyQ0xJMUNIR0F3THdzaEhpc091NjZsNFIrWlhhdGgK",
"endpoints": [
"https://github.com/nitra/task/releases/download/task-latest/latest.json",
"https://github.com/nitra/task/releases/download/app-latest/latest.json",
"https://github.com/nitra/task/releases/latest/download/latest.json"
]
}
Expand Down
Loading
Loading