chore: prune unused apps and services for the moeka fork
CI / Lint (push) Canceled after 0s
CI / Build Test (stage-web) (push) Canceled after 0s
CI / Build Test (ui-loading-screens) (push) Canceled after 0s
CI / Build Test (ui-transitions) (push) Canceled after 0s
CI / Unit Test (push) Canceled after 0s
CI / Type Check (push) Canceled after 0s
CI / Check Provenance (push) Canceled after 0s
Sync Labels / sync-labels (push) Successful in 1m43s

- Remove stage-tamagotchi, stage-pocket, server, and engine workspaces with their workflows, addons, and docs
- Switch the toolchain from pnpm to bun, replace lockfiles with bun.lock
- Rewrite remaining product references to Moeka
This commit is contained in:
2026-09-14 16:40:49 +07:00
parent 00c6867b7f
commit dc26878386
1981 changed files with 10974 additions and 260677 deletions
+27 -87
View File
@@ -1,4 +1,4 @@
# Start contributing to [Project AIRI](https://github.com/moeru-ai/airi)
# Start contributing to [Project Moeka](https://github.com/cuwayo/moeka)
Hello! Thank you for your interest in contributing to this project. This guide will help you get started.
@@ -6,44 +6,31 @@ Hello! Thank you for your interest in contributing to this project. This guide w
- [Git](https://git-scm.com/downloads)
- [Node.js 23+](https://nodejs.org/en/download/)
- [corepack](https://github.com/nodejs/corepack)
- [pnpm](https://pnpm.io/installation)
- [Bun 1.4+](https://bun.sh/docs/installation)
<details>
<summary>Windows setup</summary>
0. Download [Visual Studio](https://visualstudio.microsoft.com/downloads/) and follow the instructions here: https://rust-lang.github.io/rustup/installation/windows-msvc.html#walkthrough-installing-visual-studio-2022
> Make sure to install Windows SDK and C++ build tools when installing Visual Studio.
1. Open PowerShell
2. Install [`scoop`](https://scoop.sh/)
0. Open PowerShell
1. Install [`scoop`](https://scoop.sh/)
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
```
3. Install `git`, Node.js, `rustup`, `msvc` through `scoop`
2. Install `git` and Node.js through `scoop`
```powershell
scoop install git nodejs rustup
# For Rust dependencies
# Not required if you are not going to develop on either crates or apps/tamagotchi
scoop install main/rust-msvc
# Rust & Windows specific
rustup toolchain install stable-x86_64-pc-windows-msvc
rustup default stable-x86_64-pc-windows-msvc
scoop install git nodejs
```
> https://stackoverflow.com/a/64121601
4. Install `pnpm` through `corepack`
3. Install `bun` through the official script
```powershell
corepack enable
corepack prepare pnpm@latest --activate
powershell -c "irm bun.sh/install.ps1 | iex"
```
</details>
@@ -58,11 +45,10 @@ Hello! Thank you for your interest in contributing to this project. This guide w
brew install git node
```
2. Install `pnpm` through `corepack`
2. Install `bun` through `brew`
```shell
corepack enable
corepack prepare pnpm@latest --activate
brew install oven-sh/bun/bun
```
</details>
@@ -73,22 +59,10 @@ Hello! Thank you for your interest in contributing to this project. This guide w
0. Open terminal
1. Follow [nodesource/distributions: NodeSource Node.js Binary Distributions](https://github.com/nodesource/distributions?tab=readme-ov-file#table-of-contents) to install `node`
2. Follow [Git](https://git-scm.com/downloads/linux) to install `git`
3. Install `pnpm` through `corepack`
3. Install `bun` through the official script
```shell
corepack enable
corepack prepare pnpm@latest --activate
```
4. If you would love to help to develop the desktop version, you will need those dependencies:
```shell
sudo apt install \
libssl-dev \
libglib2.0-dev \
libgtk-3-dev \
libjavascriptcoregtk-4.1-dev \
libwebkit2gtk-4.1-dev
curl -fsSL https://bun.sh/install | bash
```
</details>
@@ -121,8 +95,8 @@ Click on the **Fork** button on the top right corner of the [moeru-ai/airi](http
## Clone
```shell
git clone https://github.com/<your-github-username>/airi.git
cd airi
git clone https://github.com/<your-github-username>/moeka.git
cd moeka
```
## Create your working branch
@@ -134,12 +108,7 @@ git checkout -b <your-branch-name>
## Install dependencies
```shell
corepack enable
pnpm install
# For Rust dependencies
# Not required if you are not going to develop on either crates or apps/tamagotchi
cargo fetch
bun install
```
> [!NOTE]
@@ -147,51 +116,22 @@ cargo fetch
> We would recommend to install [@antfu/ni](https://github.com/antfu-collective/ni) to make your script simpler.
>
> ```shell
> corepack enable
> npm i -g @antfu/ni
> ```
>
> Once installed, you can
>
> - use `ni` for `pnpm install`, `npm install` and `yarn install`.
> - use `nr` for `pnpm run`, `npm run` and `yarn run`.
> - use `ni` for `bun install`, `npm install` and `yarn install`.
> - use `nr` for `bun run`, `npm run` and `yarn run`.
>
> You don't need to care about the package manager, `ni` will help you choose the right one.
## Choose the application you want to develop on
### Stage Tamagotchi (Desktop version)
### Stage Web
```shell
pnpm dev:tamagotchi
```
> [!NOTE]
>
> For [@antfu/ni](https://github.com/antfu-collective/ni) users, you can
>
> ```shell
> nr dev:tamagotchi
> ```
> [!NOTE]
>
> The `dev` and `start` scripts run `install-electron` before `electron-vite`.
>
> Electron 42 removed the `postinstall` script. The `electron` package now downloads its binary
> when you first run its `bin` entry. `electron-vite` reads `node_modules/electron/path.txt`
> directly, so it never starts that download. A fresh install therefore fails with
> `Error: Electron uninstall`.
>
> `install-electron` runs the same code as the removed `postinstall` script. It returns
> immediately when the binary is already present.
>
> Remove this step after `electron-vite` supports the lazy download.
### Stage Web (Browser version for [airi.moeru.ai](https://airi.moeru.ai))
```shell
pnpm dev
bun run dev
```
> [!NOTE]
@@ -209,7 +149,7 @@ Browse the live UI component storyboard at [airi.moeru.ai/ui](https://airi.moeru
### Documentation site
```shell
pnpm dev:docs
bun run dev:docs
```
> [!NOTE]
@@ -240,14 +180,14 @@ Edit the credentials in `.env.local`.
Migrate the database
```shell
pnpm -F @proj-airi/telegram-bot db:generate
pnpm -F @proj-airi/telegram-bot db:push
bun run --filter @proj-airi/telegram-bot db:generate
bun run --filter @proj-airi/telegram-bot db:push
```
Run the bot
```shell
pnpm -F @proj-airi/telegram-bot start
bun run --filter @proj-airi/telegram-bot start
```
> [!NOTE]
@@ -275,7 +215,7 @@ Edit the credentials in `.env.local`.
Run the bot
```shell
pnpm -F @proj-airi/discord-bot start
bun run --filter @proj-airi/discord-bot start
```
> [!NOTE]
@@ -305,7 +245,7 @@ Edit the credentials in `.env.local`.
Run the bot
```shell
pnpm -F @proj-airi/minecraft-bot start
bun run --filter @proj-airi/minecraft-bot start
```
> [!NOTE]
@@ -323,13 +263,13 @@ pnpm -F @proj-airi/minecraft-bot start
Please make sure lint (static checkers) and TypeScript compilers are satisfied:
```shell
pnpm lint && pnpm typecheck
bun run lint && bun run typecheck
```
If you are committing images, consider using AVIF format instead of PNG, JPG etc. You can convert existing images to AVIF by running:
```shell
pnpm to-avif <PATH_TO_IMAGE_OR_DIRECTORY1> <PATH_2> <PATH_3> ...
bun run to-avif <PATH_TO_IMAGE_OR_DIRECTORY1> <PATH_2> <PATH_3> ...
```
> [!NOTE]
+2 -2
View File
@@ -30,13 +30,13 @@ Describe the expected behavior or evidence target.
Run the narrowest relevant command first:
```bash
pnpm -F <package-name> exec vitest run <path/to/test>
bun run --filter <package-name> exec vitest run <path/to/test>
```
If runtime contracts changed, also run:
```bash
pnpm -F <package-name> typecheck
bun run --filter <package-name> typecheck
```
## Output required
+2 -2
View File
@@ -1,6 +1,6 @@
# Copilot repository instructions
This is the `moeru-ai/airi` pnpm monorepo. Prefer the smallest safe change and keep every task inside its stated scope.
This is the `cuwayo/moeka` Bun monorepo. Prefer the smallest safe change and keep every task inside its stated scope.
## General rules
@@ -14,7 +14,7 @@ This is the `moeru-ai/airi` pnpm monorepo. Prefer the smallest safe change and k
## Validation
- Use package-scoped pnpm commands where possible.
- Use package-scoped Bun commands where possible.
- Run the narrowest relevant test first.
- Run typecheck if runtime contracts or exported types changed.
- Run affected package tests if shared logic changed.
+1 -7
View File
@@ -1,9 +1,3 @@
- name: 'apps/stage-pocket'
description: 'Mobile App: iOS / Android'
color: 'b8d5ff'
- name: 'apps/stage-tamagotchi'
description: 'Desktop App: Windows & macOS & Linux'
color: 'a1f7ef'
- name: 'apps/stage-web'
description: 'Web App: PWA & Browser'
color: 'abedff'
@@ -137,7 +131,7 @@
description: 'Scope related to providers we support'
color: '4468eb'
- name: 'scope/server-api'
description: 'Scope related to the server api we maintained, the public service of AIRI'
description: 'Scope related to the server api we maintained, the public service of Moeka'
color: '4468eb'
- name: 'scope/ui'
description: 'Scope related to UI/UX, or interface improve, perf, and bugs'
+3 -3
View File
@@ -123,16 +123,16 @@ write_expected_github_asset_metadata() {
gh api "repos/${GITHUB_REPOSITORY_NAME}/releases/tags/${encoded_tag}" \
> "${release_assets_json}"
jq -r '.assets[].name | select(test("^(AIRI-.*|latest-.*\\.yml)$"))' "${release_assets_json}" \
jq -r '.assets[].name | select(test("^(Moeka-.*|latest-.*\\.yml)$"))' "${release_assets_json}" \
| sort \
> "${WORK_DIR}/expected-asset-names.txt"
jq -r '.assets[] | select(.name | test("^(AIRI-.*|latest-.*\\.yml)$")) | [.name, (.digest // "")] | @tsv' "${release_assets_json}" \
jq -r '.assets[] | select(.name | test("^(Moeka-.*|latest-.*\\.yml)$")) | [.name, (.digest // "")] | @tsv' "${release_assets_json}" \
| sort \
> "${WORK_DIR}/expected-asset-digests.tsv"
if [[ ! -s "${WORK_DIR}/expected-asset-names.txt" ]]; then
echo "::error::No GitHub release assets matched AIRI-* or latest-*.yml."
echo "::error::No GitHub release assets matched Moeka-* or latest-*.yml."
exit 1
fi
+10 -10
View File
@@ -16,20 +16,20 @@ jobs:
- uses: actions/checkout@v6
# NOTICE:
# Pin Node.js because 26.8.0 reports 26.8.0-alpha.0.0.0.
# sharp 0.29.3 rejects this prerelease version during pnpm install.
# sharp 0.29.3 rejects this prerelease version during bun install.
# Source: https://github.com/nodejs/node/blob/v26.8.0/src/node_version.h
# Remove this pin when Node.js 26 reports a stable version.
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
- run: pnpm install
- run: pnpm prune && pnpm dedupe
- run: pnpm run lint:fix
- run: docker run -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest --fix
working-directory: ./apps/stage-pocket
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- run: bun install
- run: bun prune && bun dedupe
- run: bun run lint:fix
# - name: AutoCorrect
# uses: huacnlee/autocorrect-action@main
+33 -84
View File
@@ -18,33 +18,19 @@ jobs:
- uses: actions/checkout@v6
# NOTICE:
# Pin Node.js because 26.8.0 reports 26.8.0-alpha.0.0.0.
# sharp 0.29.3 rejects this prerelease version during pnpm install.
# sharp 0.29.3 rejects this prerelease version during bun install.
# Source: https://github.com/nodejs/node/blob/v26.8.0/src/node_version.h
# Remove this pin when Node.js 26 reports a stable version.
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
# Setup .NET for Godot C# linting
- name: Setup .NET
uses: actions/setup-dotnet@v4
- uses: oven-sh/setup-bun@v2
with:
dotnet-version: '10.0.x'
bun-version: 1.4.2
# Lint Godot C# code
- name: Lint Godot C#
working-directory: ./engines/stage-tamagotchi-godot
run: dotnet format stage-tamagotchi-godot.slnx --verify-no-changes
- run: docker run -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest
working-directory: ./apps/stage-pocket
- run: pnpm install --frozen-lockfile
- run: pnpm run lint
- run: docker run -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest
working-directory: ./apps/stage-pocket
- run: bun install --frozen-lockfile
- run: bun run lint
build-test:
name: Build Test (${{ matrix.app_name }})
@@ -53,19 +39,13 @@ jobs:
matrix:
include:
- app_name: stage-web
command: pnpm -F @proj-airi/stage-web run build && pnpm -F @proj-airi/docs run build:base && mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs && pnpm -F @proj-airi/stage-ui run story:build && mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
- app_name: stage-tamagotchi
command: pnpm -F @proj-airi/stage-tamagotchi run build
- app_name: stage-tamagotchi-godot
command: cd engines/stage-tamagotchi-godot && dotnet restore && dotnet build -c ExportRelease
command: bun run --filter @proj-airi/stage-web build && bun run --filter @proj-airi/docs build:base && mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs && bun run --filter @proj-airi/stage-ui story:build && mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
- app_name: ui-transitions
command: pnpm -F @proj-airi/ui-transitions run play:build
command: bun run --filter @proj-airi/ui-transitions play:build
- app_name: ui-loading-screens
command: pnpm -F @proj-airi/ui-loading-screens run play:build
command: bun run --filter @proj-airi/ui-loading-screens play:build
runs-on: ubuntu-latest
steps:
@@ -80,40 +60,20 @@ jobs:
restore-keys: |
${{ runner.os }}-turbo-
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
# Setup .NET and Godot
- name: Setup .NET
if: matrix.app_name == 'stage-tamagotchi-godot'
uses: actions/setup-dotnet@v4
- uses: oven-sh/setup-bun@v2
with:
dotnet-version: '10.0.x'
bun-version: 1.4.2
- name: Setup Godot
if: matrix.app_name == 'stage-tamagotchi-godot'
uses: chickensoft-games/setup-godot@v2
with:
version: 4.7.1
use-dotnet: true
include-templates: true
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- run: bun install --frozen-lockfile
- run: bun run build:packages
- name: Build App
run: ${{ matrix.command }}
- name: Export Godot Linux sidecar
if: matrix.app_name == 'stage-tamagotchi-godot'
working-directory: ./engines/stage-tamagotchi-godot
run: |
mkdir -p out/linux
godot --headless --export-release "Linux x64" out/linux/godot-stage
unit-test:
name: Unit Test
runs-on: ubuntu-latest
@@ -129,34 +89,21 @@ jobs:
restore-keys: |
${{ runner.os }}-turbo-
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
# # Setup .NET and Godot
# - name: Setup .NET
# if: matrix.app_name == 'stage-tamagotchi-godot'
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: '10.0.x'
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
# - name: Setup Godot
# if: matrix.app_name == 'stage-tamagotchi-godot'
# uses: chickensoft-games/setup-godot@v2
# with:
# version: 4.7.1
# use-dotnet: true
# include-templates: true
- run: pnpm install --frozen-lockfile
- run: bun install --frozen-lockfile
- name: Setup Playwright
run: npx playwright install chromium
- run: pnpm run build:packages
- run: pnpm run test:run
- run: bun run build:packages
- run: bun run test:run
env:
NODE_OPTIONS: --no-experimental-webstorage
@@ -168,14 +115,16 @@ jobs:
- uses: actions/checkout@v6
# Node.js
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
- run: pnpm install --frozen-lockfile
- run: pnpm run typecheck
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- run: bun install --frozen-lockfile
- run: bun run typecheck
check-provenance:
name: Check Provenance
@@ -188,7 +137,7 @@ jobs:
id: check
with:
fail-on-provenance-change: true # optional, default: false
# lockfile: pnpm-lock.yaml # optional
# lockfile: bun.lock # optional
# base-ref: origin/main # optional, default: origin/main
# fail-on-downgrade: true # optional, default: true
- name: Print result
@@ -1,60 +0,0 @@
name: Cloudflare Pages (Auth UI)
on:
push:
branches:
- 'main'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy - ui-server-auth
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
environment:
name: Auth Production
url: https://accounts.airi.build/ui/
steps:
- uses: actions/checkout@v6
# Turborepo
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
# NOTICE:
#
# Keep the wrangler setup consistent with the stage-web deployment
# workflow. cloudflare/wrangler-action expects a usable wrangler binary
# when running repository-local pnpm workspaces.
- run: pnpm i -g wrangler@4
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Build ui-server-auth
run: pnpm -F @proj-airi/ui-server-auth run build
env:
VITE_ENABLE_ANALYTICS: 'true'
VITE_SERVER_URL: 'https://api.airi.build'
- uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy ./apps/ui-server-auth/dist --project-name=airi-accounts-ui --branch=main
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
@@ -1,132 +0,0 @@
name: Cloudflare Workers (server-dev)
on:
push:
branches:
- 'server-dev'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy-stage-web:
name: Deploy - stage-web (server-dev)
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
environment:
name: ServerDev
url: ${{ steps.preview-urls.outputs.preview_url }}
steps:
- uses: actions/checkout@v6
# Turborepo
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
- run: pnpm i -g wrangler@4
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Build stage-web
run: |
pnpm -F @proj-airi/stage-web run build
pnpm -F @proj-airi/docs run build:base
mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs
cp ./apps/stage-web/dist/docs/sitemap.xml ./apps/stage-web/dist/sitemap.xml
pnpm -F @proj-airi/stage-ui run story:build
mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
env:
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
S3_REGION: ${{ secrets.S3_REGION }}
WARP_DRIVE_PUBLIC_BASE: ${{ secrets.WARP_DRIVE_PUBLIC_BASE }}
VITE_SERVER_URL: 'https://airi-server-dev.up.railway.app'
- name: Wrangler Upload
id: wrangler-versions-upload
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: versions upload -c ./apps/stage-web/wrangler.toml --preview-alias server-dev --message "GitHub Actions uploaded server-dev branch preview" --tag v0.0.1-server-dev
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare preview URL
id: preview-urls
run: |
deployment_url="${{ steps.wrangler-versions-upload.outputs.deployment-url }}"
if [ -z "$deployment_url" ]; then
echo "Deployment URL from wrangler upload is empty." >&2
exit 1
fi
host_without_scheme="${deployment_url#https://}"
host_without_scheme="${host_without_scheme#http://}"
preview_url="https://server-dev-${host_without_scheme#*-}"
echo "preview_url=$preview_url" >> "$GITHUB_OUTPUT"
echo "Preview URL: $preview_url"
deploy-auth-ui:
name: Deploy - ui-server-auth (server-dev)
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
environment:
name: AuthServerDev
url: https://server-dev.airi-server-auth.pages.dev/ui/
steps:
- uses: actions/checkout@v6
# Turborepo
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-auth-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-auth-
${{ runner.os }}-turbo-
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
- run: pnpm i -g wrangler@4
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Build ui-server-auth
run: pnpm -F @proj-airi/ui-server-auth run build
env:
VITE_SERVER_URL: 'https://airi-server-dev.up.railway.app'
- name: Wrangler Pages Deploy
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy ./apps/ui-server-auth/dist --project-name=airi-accounts-ui --branch=server-dev
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
- name: Print preview URL
run: |
echo "Preview URL: https://server-dev.airi-server-auth.pages.dev/ui/"
@@ -1,260 +0,0 @@
name: Cloudflare Workers (Preview) Deploy
on:
workflow_run:
workflows:
- Cloudflare Workers (Preview) Prepare
types:
- completed
jobs:
ask-for-approval:
name: Ask for approval to deploy - ${{ matrix.app_name }}
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
strategy:
matrix:
include:
- app_name: stage-web
steps:
- name: Download artifact - metadata
uses: dawidd6/action-download-artifact@v19
with:
workflow_conclusion: success
run_id: ${{ github.event.workflow_run.id }}
name: preview-meta
path: preview-meta
allow_forks: true
- name: Parse metadata
id: meta
run: |
node --input-type=module <<'NODE'
import { appendFile, readFile } from 'node:fs/promises'
const metadata = JSON.parse(await readFile('preview-meta/preview-meta.json', 'utf8'))
if (
!/^\d+$/.test(metadata.prNumber)
|| !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(metadata.repositoryFullName)
|| !/^[a-f0-9]{40}$/i.test(metadata.headSha)
) {
throw new Error('Preview metadata has an invalid shape')
}
await appendFile(process.env.GITHUB_OUTPUT, [
`PR_NUM=${metadata.prNumber}`,
`REPO_FULL_NAME=${metadata.repositoryFullName}`,
`HEAD_SHA=${metadata.headSha}`,
'',
].join('\n'))
NODE
- name: Find Comment
if: ${{ always() }}
uses: peter-evans/find-comment@v4
id: fc
with:
issue-number: ${{ steps.meta.outputs.PR_NUM }}
comment-author: 'github-actions[bot]'
body-includes: to Cloudflare Workers (Preview) for *${{ matrix.app_name }}*
- name: Comment on require approval
uses: peter-evans/create-or-update-comment@v5
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ steps.meta.outputs.PR_NUM }}
edit-mode: replace
body: |
## ⏳ Approval required for deploying to Cloudflare Workers (Preview) for *${{ matrix.app_name }}*.
| Name | Link |
|:------------------------|:---------------------------------------------------------------------------------------|
| 🔭 Waiting for approval | For maintainers, approve [here](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
Hey, maintainers, kindly take some time to review and approve this deployment when you are available. Thank you! 🙏
on-success:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
name: Deploy - ${{ matrix.app_name }}
runs-on: ubuntu-latest
environment: Preview
permissions:
contents: read
pull-requests: write
strategy:
matrix:
include:
- app_name: stage-web
wrangler_config_path: ./apps/stage-web/wrangler.toml
build_directory: ./apps/stage-web/dist
build_command: |
pnpm -F @proj-airi/stage-web run build
pnpm -F @proj-airi/docs run build:base
mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs
cp ./apps/stage-web/dist/docs/sitemap.xml ./apps/stage-web/dist/sitemap.xml
pnpm -F @proj-airi/stage-ui run story:build
mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
steps:
- name: Download artifact - metadata
uses: dawidd6/action-download-artifact@v19
with:
workflow_conclusion: success
run_id: ${{ github.event.workflow_run.id }}
name: preview-meta
path: preview-meta
allow_forks: true
- name: Parse metadata
id: meta
run: |
node --input-type=module <<'NODE'
import { appendFile, readFile } from 'node:fs/promises'
const metadata = JSON.parse(await readFile('preview-meta/preview-meta.json', 'utf8'))
if (
!/^\d+$/.test(metadata.prNumber)
|| !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(metadata.repositoryFullName)
|| !/^[a-f0-9]{40}$/i.test(metadata.headSha)
) {
throw new Error('Preview metadata has an invalid shape')
}
await appendFile(process.env.GITHUB_OUTPUT, [
`PR_NUM=${metadata.prNumber}`,
`REPO_FULL_NAME=${metadata.repositoryFullName}`,
`HEAD_SHA=${metadata.headSha}`,
'',
].join('\n'))
NODE
- name: Checkout repository
uses: actions/checkout@v6
with:
repository: ${{ steps.meta.outputs.REPO_FULL_NAME }}
ref: ${{ steps.meta.outputs.HEAD_SHA }}
fetch-depth: 1
persist-credentials: false
# https://gh.io/securely-using-pull_request_target
allow-unsafe-pr-checkout: true
- name: Setup pnpm
uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
# NOTICE:
#
# Here installing wrangler to global is required, or otherwise:
# ERR_PNPM_ADDING_TO_ROOT Running this command will add the dependency to the workspace root...
# error occurs.
#
# Since https://github.com/cloudflare/wrangler-action/pull/339#issuecomment-2667622947 rejected the -g support
# by saying un-reasonable 'I'm not sure if it's common ... to install packages to the global scope, ... might be introducing some unintended side effects.'
#
# Clearly I think installing with `<packageManager> install` brings more unintended side effects...
#
# As suggested by https://github.com/cloudflare/wrangler-action/issues/181#issuecomment-2127990708, we should pre-install
# with our package manager and then use it in the action.
- run: pnpm i -g wrangler@4
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm run build:packages
- name: Build ${{ matrix.app_name }}
id: build
run: ${{ matrix.build_command }}
continue-on-error: true
env:
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
S3_REGION: ${{ secrets.S3_REGION }}
WARP_DRIVE_PUBLIC_BASE: ${{ secrets.WARP_DRIVE_PUBLIC_BASE }}
STAGE_WEB_WARP_DRIVE_PREFIX: proj-airi/stage-web/pr-${{ steps.meta.outputs.PR_NUM }}/
STAGE_UI_WARP_DRIVE_PREFIX: proj-airi/stage-ui/pr-${{ steps.meta.outputs.PR_NUM }}/
- name: Find Comment
if: ${{ always() }}
uses: peter-evans/find-comment@v4
id: fc
with:
issue-number: ${{ steps.meta.outputs.PR_NUM }}
comment-author: 'github-actions[bot]'
body-includes: to Cloudflare Workers (Preview) for *${{ matrix.app_name }}*
- name: Comment on build failure
if: ${{ steps.build.outcome != 'success' }}
uses: peter-evans/create-or-update-comment@v5
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ steps.meta.outputs.PR_NUM }}
edit-mode: replace
body: |
## ❌ Deploy to Cloudflare Workers (Preview) for *${{ matrix.app_name }}* failed.
| Name | Link |
|:---------------------|:---------------------------------------------------------------------------------------|
| 🔍 Latest deploy log | https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
- name: Fail if build failed
if: ${{ steps.build.outcome != 'success' }}
run: |
echo "Build step failed; marking workflow as failed."
exit 1
- name: Wrangler Upload
if: ${{ steps.build.outcome == 'success' }}
id: wrangler-versions-upload
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: versions upload -c ${{ matrix.wrangler_config_path }} --preview-alias pr-${{ steps.meta.outputs.PR_NUM }} --message "GitHub Actions uploaded preview for Pull Request ${{ steps.meta.outputs.PR_NUM }}" --tag v0.0.1-pr.${{ steps.meta.outputs.PR_NUM }}
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare preview URLs
if: ${{ steps.build.outcome == 'success' }}
id: preview-urls
run: |
deployment_url="${{ steps.wrangler-versions-upload.outputs.deployment-url }}"
if [ -z "$deployment_url" ]; then
echo "Deployment URL from wrangler upload is empty." >&2
exit 1
fi
host_without_scheme="${deployment_url#https://}"
host_without_scheme="${host_without_scheme#http://}"
preview_alias_url="https://pr-${{ steps.meta.outputs.PR_NUM }}-${host_without_scheme#*-}"
echo "deploy_preview_url=$deployment_url" >> "$GITHUB_OUTPUT"
echo "pull_request_preview_url=$preview_alias_url" >> "$GITHUB_OUTPUT"
- name: Create or update comment
if: ${{ steps.build.outcome == 'success' }}
uses: peter-evans/create-or-update-comment@v5
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ steps.meta.outputs.PR_NUM }}
edit-mode: replace
body: |
## ✅ Deploy to Cloudflare Workers (Preview) for *${{ matrix.app_name }}* ready!
| Name | Link |
|:------------------------|:--------------------------------------------------------------------------------------------|
| 🔍 Latest deploy log | https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
| 😎 Deploy Preview | ${{ steps.preview-urls.outputs.deploy_preview_url }} |
| 🚀 Pull Request Preview | ${{ steps.preview-urls.outputs.pull_request_preview_url }} |
@@ -1,37 +0,0 @@
name: Cloudflare Workers (Preview) Prepare
on:
pull_request:
branches:
- main
workflow_dispatch:
jobs:
prepare:
name: Prepare preview sources
runs-on: ubuntu-latest
steps:
- name: Persist checkout metadata
env:
PR_NUM: ${{ github.event.number }}
REPO_FULL_NAME: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
node --input-type=module <<'NODE'
import { writeFile } from 'node:fs/promises'
await writeFile('preview-meta.json', JSON.stringify({
prNumber: process.env.PR_NUM,
repositoryFullName: process.env.REPO_FULL_NAME,
headSha: process.env.HEAD_SHA,
}))
NODE
- name: Upload metadata artifact
uses: actions/upload-artifact@v7
with:
name: preview-meta
path: ./preview-meta.json
overwrite: true
@@ -1,81 +0,0 @@
name: Cloudflare Workers
on:
push:
branches:
- 'main'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy - stage-web
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
environment:
name: Production
url: https://airi.moeru.ai/docs/
steps:
- uses: actions/checkout@v6
# Turborepo
- name: Cache turbo build setup
uses: actions/cache@v5
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
# NOTICE:
#
# Here installing wrangler to global is required, or otherwise:
# ERR_PNPM_ADDING_TO_ROOT Running this command will add the dependency to the workspace root...
# error occurs.
#
# Since https://github.com/cloudflare/wrangler-action/pull/339#issuecomment-2667622947 rejected the -g support
# by saying un-reasonable 'I'm not sure if it's common ... to install packages to the global scope, ... might be introducing some unintended side effects.'
#
# Clearly I think installing with `<packageManager> install` brings more unintended side effects...
#
# As suggested by https://github.com/cloudflare/wrangler-action/issues/181#issuecomment-2127990708, we should pre-install
# with our package manager and then use it in the action.
- run: pnpm i -g wrangler@4
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Build stage-web
run: |
pnpm -F @proj-airi/stage-web run build
pnpm -F @proj-airi/docs run build:base
mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs
cp ./apps/stage-web/dist/docs/sitemap.xml ./apps/stage-web/dist/sitemap.xml
pnpm -F @proj-airi/stage-ui run story:build
mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
env:
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
S3_REGION: ${{ secrets.S3_REGION }}
VITE_ENABLE_ANALYTICS: 'true'
VITE_SERVER_URL: 'https://api.airi.build'
WARP_DRIVE_PUBLIC_BASE: ${{ secrets.WARP_DRIVE_PUBLIC_BASE }}
- uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy -c ./apps/stage-web/wrangler.toml
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
@@ -22,14 +22,16 @@ jobs:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
HF_USERNAME: ${{ secrets.HF_USERNAME }}
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
- run: pnpm install --frozen-lockfile
- run: pnpm build
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- run: bun install --frozen-lockfile
- run: bun run build
env:
TARGET_HUGGINGFACE_SPACE: 'true'
VITE_APP_TARGET_HUGGINGFACE_SPACE: 'true'
+4 -4
View File
@@ -351,15 +351,15 @@ jobs:
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7cd577a1928d35f4_EOF'
{"add_labels":{"allowed":["bug","feature","pending triage","apps/stage-pocket","apps/stage-tamagotchi","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":12,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["bug","feature","pending triage","apps/stage-pocket","apps/stage-tamagotchi","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":24,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"report_incomplete":{}}
{"add_labels":{"allowed":["bug","feature","pending triage","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":12,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["bug","feature","pending triage","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":24,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_7cd577a1928d35f4_EOF
- name: Write Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
"add_labels": " CONSTRAINTS: Maximum 12 label(s) can be added. Only these labels are allowed: [\"bug\" \"feature\" \"pending triage\" \"apps/stage-pocket\" \"apps/stage-tamagotchi\" \"apps/stage-web\" \"env/os-all\" \"env/os-linux\" \"env/os-macos\" \"env/os-windows\" \"priority/general\" \"priority/nice-to-have\" \"priority/urgent\" \"scope/agent\" \"scope/audio-input\" \"scope/audio-output\" \"scope/avatar\" \"scope/avatar/live2d\" \"scope/avatar/vrm\" \"scope/documentation\" \"scope/engineering\" \"scope/extension\" \"scope/game-playing-ai\" \"scope/i18n\" \"scope/providers\" \"scope/server-api\" \"scope/ui\"]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}.",
"remove_labels": " CONSTRAINTS: Maximum 24 label(s) can be removed. Only these labels can be removed: [bug feature pending triage apps/stage-pocket apps/stage-tamagotchi apps/stage-web env/os-all env/os-linux env/os-macos env/os-windows priority/general priority/nice-to-have priority/urgent scope/agent scope/audio-input scope/audio-output scope/avatar scope/avatar/live2d scope/avatar/vrm scope/documentation scope/engineering scope/extension scope/game-playing-ai scope/i18n scope/providers scope/server-api scope/ui]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}."
"add_labels": " CONSTRAINTS: Maximum 12 label(s) can be added. Only these labels are allowed: [\"bug\" \"feature\" \"pending triage\" \"apps/stage-web\" \"env/os-all\" \"env/os-linux\" \"env/os-macos\" \"env/os-windows\" \"priority/general\" \"priority/nice-to-have\" \"priority/urgent\" \"scope/agent\" \"scope/audio-input\" \"scope/audio-output\" \"scope/avatar\" \"scope/avatar/live2d\" \"scope/avatar/vrm\" \"scope/documentation\" \"scope/engineering\" \"scope/extension\" \"scope/game-playing-ai\" \"scope/i18n\" \"scope/providers\" \"scope/server-api\" \"scope/ui\"]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}.",
"remove_labels": " CONSTRAINTS: Maximum 24 label(s) can be removed. Only these labels can be removed: [bug feature pending triage apps/stage-web env/os-all env/os-linux env/os-macos env/os-windows priority/general priority/nice-to-have priority/urgent scope/agent scope/audio-input scope/audio-output scope/avatar scope/avatar/live2d scope/avatar/vrm scope/documentation scope/engineering scope/extension scope/game-playing-ai scope/i18n scope/providers scope/server-api scope/ui]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}."
},
"repo_params": {},
"dynamic_tools": []
@@ -1112,7 +1112,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-pocket\",\"apps/stage-tamagotchi\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":12,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-pocket\",\"apps/stage-tamagotchi\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":24,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":12,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":24,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"report_incomplete\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
+2 -8
View File
@@ -33,8 +33,6 @@ safe-outputs:
- bug
- feature
- pending triage
- apps/stage-pocket
- apps/stage-tamagotchi
- apps/stage-web
- env/os-all
- env/os-linux
@@ -64,8 +62,6 @@ safe-outputs:
- bug
- feature
- pending triage
- apps/stage-pocket
- apps/stage-tamagotchi
- apps/stage-web
- env/os-all
- env/os-linux
@@ -105,7 +101,7 @@ The target pull request is:
Managed labels:
- Type: `bug`, `feature`
- App surface: `apps/stage-pocket`, `apps/stage-tamagotchi`, `apps/stage-web`
- App surface: `apps/stage-web`
- Environment: `env/os-all`, `env/os-linux`, `env/os-macos`, `env/os-windows`
- Scope: `scope/agent`, `scope/audio-input`, `scope/audio-output`, `scope/avatar`, `scope/avatar/live2d`, `scope/avatar/vrm`, `scope/documentation`, `scope/engineering`, `scope/extension`, `scope/game-playing-ai`, `scope/i18n`, `scope/providers`, `scope/server-api`, `scope/ui`
- Priority: `priority/general`, `priority/nice-to-have`, `priority/urgent`
@@ -160,9 +156,7 @@ Do not use web search. Do not use bash to modify files, create branches, post co
### App labels
- Apply `apps/stage-web` when files under `apps/stage-web/` change, or the PR text explicitly says the change is for the web app or PWA/browser surface.
- Apply `apps/stage-tamagotchi` when files under `apps/stage-tamagotchi/` change, or the PR text explicitly says desktop/Electron/Windows/macOS/Linux app.
- Apply `apps/stage-pocket` when files under `apps/stage-pocket/` change, or the PR text explicitly says mobile/iOS/Android app.
- Multiple app labels are allowed when the evidence is explicit.
- Only `apps/stage-web` exists in this fork. Never apply a desktop or mobile app label.
### Environment labels
+10 -8
View File
@@ -21,11 +21,13 @@ jobs:
with:
fetch-depth: 0
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- name: Configure npm registry
uses: actions/setup-node@v6
@@ -39,9 +41,9 @@ jobs:
# https://github.com/e18e/ecosystem-issues/issues/201
- run: npm install -g npm@latest
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- run: bun install --frozen-lockfile
- run: bun run build:packages
- run: pnpm publish -r --access public --no-git-checks --dry-run
- run: bun run publish:packages -- --dry-run
- run: pnpm publish -r --access public --no-git-checks
- run: bun run publish:packages
@@ -1,136 +0,0 @@
name: Release Pocket (Android)
permissions:
contents: write
actions: read
on:
release:
types:
- prereleased
workflow_dispatch:
inputs:
build_only:
description: Build only (no upload to release)
required: false
default: false
type: boolean
tag:
description: Specific tag for the release upload
required: false
type: string
schedule:
- cron: '0 1 * * *'
jobs:
build:
name: Build Android APK
runs-on: ubuntu-latest
env:
PRODUCT_NAME: 'AIRI'
# Every current trigger path produces an installable APK artifact, so ship the analytics-enabled build consistently.
VITE_ENABLE_ANALYTICS: 'true'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
- name: Install Android build tools
run: |
sdkmanager "build-tools;36.1.0"
echo "${ANDROID_SDK_ROOT:-$ANDROID_HOME}/build-tools/36.1.0" >> "$GITHUB_PATH"
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Validate workflow dispatch release tag
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.tag == '' }}
run: |
echo "workflow_dispatch release uploads require the tag input"
exit 1
- name: Build web assets
run: pnpm -F @proj-airi/stage-pocket run build
- name: Capacitor sync
run: pnpm -F @proj-airi/stage-pocket exec cap sync android
- name: Extract android keystore
run: echo "${{ secrets.CAPACITOR_ANDROID_KEYSTORE_BASE64 }}" | base64 -d > ${{ github.workspace }}/apps/stage-pocket/airi-pocket.jks
- name: Build APK
working-directory: apps/stage-pocket/android
run: pnpm -F @proj-airi/stage-pocket exec cap build android
env:
CAPACITOR_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/apps/stage-pocket/airi-pocket.jks
CAPACITOR_ANDROID_KEYSTORE_ALIAS: ${{ secrets.CAPACITOR_ANDROID_KEYSTORE_ALIAS }}
CAPACITOR_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.CAPACITOR_ANDROID_KEYSTORE_PASSWORD }}
CAPACITOR_ANDROID_KEYSTORE_ALIAS_PASSWORD: ${{ secrets.CAPACITOR_ANDROID_KEYSTORE_ALIAS_PASSWORD }}
- name: Rename APK for distribution
env:
RELEASE_TAG_NAME: ${{ github.event.release.tag_name || inputs.tag }}
run: |
VERSION="${RELEASE_TAG_NAME#v}"
if [ -z "$VERSION" ]; then
VERSION="$(sed -n 's/^AIRI_VERSION_NAME=//p' apps/stage-pocket/android/app-version.properties)"
fi
APK_NAME="${PRODUCT_NAME}-${VERSION}-android.apk"
APK_SOURCE="apps/stage-pocket/android/app/build/outputs/apk/release/app-release-signed.apk"
APK_BUNDLE_DIR="apps/stage-pocket/android/bundle"
mkdir -p "$APK_BUNDLE_DIR"
mv "$APK_SOURCE" "$APK_BUNDLE_DIR/$APK_NAME"
echo "APK_NAME=$APK_NAME" >> "$GITHUB_ENV"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.APK_NAME }}
path: apps/stage-pocket/android/bundle/${{ env.APK_NAME }}
if-no-files-found: error
- name: Upload to GitHub Releases
id: upload-github-release
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.build_only) }}
uses: softprops/action-gh-release@v2
with:
files: apps/stage-pocket/android/bundle/${{ env.APK_NAME }}
append_body: true
tag_name: ${{ github.event.release.tag_name || inputs.tag }}
- name: Setup butler
uses: remarkablegames/setup-butler@v2
# https://itch.io/docs/butler/pushing.html
- name: Upload Android to itch.io
if: ${{ github.event_name == 'release' }}
run: butler push apps/stage-pocket/android/bundle/${{ env.APK_NAME }} nekomeowww/airi:android --userversion ${{ env.VERSION }}
env:
BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
- name: Publish release assets to GitCode
if: ${{ always() && github.repository == 'moeru-ai/airi' && steps.upload-github-release.outcome == 'success' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.build_only)) }}
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }}
GITCODE_REPO: ${{ secrets.GITCODE_REPO }}
GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
run: bash .github/scripts/publish-gitcode-release.sh
-180
View File
@@ -1,180 +0,0 @@
name: Release Pocket (iOS)
permissions:
contents: write
actions: read
on:
release:
types:
- prereleased
workflow_dispatch:
inputs:
testflight:
description: Upload to TestFlight
type: boolean
default: false
tag:
description: GitHub Release tag (empty to skip)
type: string
required: false
default: ''
jobs:
build:
name: Build iOS
runs-on: macos-26
env:
PRODUCT_NAME: 'AIRI'
VITE_ENABLE_ANALYTICS: 'true'
UPLOAD_TESTFLIGHT: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.testflight) }}
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
- uses: actions/checkout@v6
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Build assets
run: pnpm -F @proj-airi/stage-pocket run build
- name: Sync assets to iOS
run: pnpm -F @proj-airi/stage-pocket exec cap sync ios
- name: Install Apple certificate
env:
CERT_BASE64: ${{ secrets.APPLE_DISTRIBUTION_CERT_BASE64 }}
CERT_PASSWORD: ${{ secrets.APPLE_DISTRIBUTION_CERT_PASSWORD }}
run: |
CERT_PATH="$RUNNER_TEMP/certificate.p12"
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
echo "$CERT_BASE64" | base64 --decode -o "$CERT_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$CERT_PASSWORD" \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
- name: Install provisioning profile
env:
PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
run: |
PROFILE_PATH="$RUNNER_TEMP/profile.mobileprovision"
echo "$PROFILE_BASE64" | base64 --decode -o "$PROFILE_PATH"
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Read version from Xcode project
run: |
PBXPROJ="apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj"
VERSION="$(sed -n 's/.*MARKETING_VERSION = \(.*\);/\1/p' "$PBXPROJ" | head -1 | tr -d ' ')"
BUILD_NUMBER="$(sed -n 's/.*CURRENT_PROJECT_VERSION = \(.*\);/\1/p' "$PBXPROJ" | head -1 | tr -d ' ')"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "BUILD_NUMBER=$BUILD_NUMBER" >> "$GITHUB_ENV"
- name: Archive iOS
run: |
xcodebuild archive \
-project apps/stage-pocket/ios/App/App.xcodeproj \
-scheme App \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath "$RUNNER_TEMP/App.xcarchive" \
CURRENT_PROJECT_VERSION=${{ env.BUILD_NUMBER }}
- name: Export IPA
run: |
xcodebuild -exportArchive \
-archivePath "$RUNNER_TEMP/App.xcarchive" \
-exportPath "$RUNNER_TEMP/output" \
-exportOptionsPlist apps/stage-pocket/ios/ExportOptions.plist
- name: Rename IPA for distribution
run: |
IPA_SOURCE="$(find "$RUNNER_TEMP/output" -name '*.ipa' -print -quit)"
IPA_NAME="${PRODUCT_NAME}-${VERSION}_(${BUILD_NUMBER})-ios.ipa"
mv "$IPA_SOURCE" "$RUNNER_TEMP/output/$IPA_NAME"
echo "IPA_NAME=$IPA_NAME" >> "$GITHUB_ENV"
- name: Upload IPA artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.IPA_NAME }}
path: ${{ runner.temp }}/output/${{ env.IPA_NAME }}
if-no-files-found: error
- name: Install Transporter
if: ${{ env.UPLOAD_TESTFLIGHT == 'true' }}
run: |
curl -fsSL "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/resources/download/public/Transporter__OSX/bin/" -o "$RUNNER_TEMP/itmstransporter.pkg"
sudo installer -pkg "$RUNNER_TEMP/itmstransporter.pkg" -target /
rm -f "$RUNNER_TEMP/itmstransporter.pkg"
- name: Upload to TestFlight
if: ${{ env.UPLOAD_TESTFLIGHT == 'true' }}
uses: apple-actions/upload-testflight-build@v4
with:
app-path: ${{ runner.temp }}/output/${{ env.IPA_NAME }}
issuer-id: ${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_CONNECT_API_KEY }}
- name: Upload to GitHub Releases
if: ${{ env.RELEASE_TAG != '' }}
uses: softprops/action-gh-release@v2
with:
files: ${{ runner.temp }}/output/${{ env.IPA_NAME }}
append_body: true
fail_on_unmatched_files: true
tag_name: ${{ env.RELEASE_TAG }}
- name: Clean up signing
if: always()
run: |
if [ -n "$KEYCHAIN_PATH" ]; then
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
fi
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision
publish-gitcode-release:
name: Publish GitCode Release Mirror
needs: build
if: ${{ always() && github.repository == 'moeru-ai/airi' && needs.build.result == 'success' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.tag != '')) }}
continue-on-error: true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Validate release mirror tools
run: |
gh --version
jq --version
curl --version
- name: Publish release assets to GitCode
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }}
GITCODE_REPO: ${{ secrets.GITCODE_REPO }}
GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
run: bash .github/scripts/publish-gitcode-release.sh
@@ -1,278 +0,0 @@
name: Release Tamagotchi to Steam
permissions:
contents: read
actions: read
on:
release:
types:
- prereleased
workflow_dispatch:
inputs:
upload_to_steam:
description: Upload the completed build to Steam
required: true
default: false
type: boolean
release_branch:
description: Steam branch to set live
required: true
default: internal-test
type: string
build_description:
description: Optional Steam build description
required: false
type: string
concurrency:
group: steam-deploy-tamagotchi-${{ github.event_name == 'workflow_dispatch' && inputs.release_branch || 'internal-test' }}
cancel-in-progress: false
jobs:
build:
name: Build ${{ matrix.depot }} depot
runs-on: ${{ matrix.os }}
env:
VITE_DISTRIBUTION: steam
VITE_DISABLE_CUSTOM_PROVIDERS: 'true'
VITE_DISABLE_FLUX_PURCHASE: 'true'
VITE_ENABLE_ANALYTICS: 'false'
strategy:
fail-fast: false
matrix:
include:
- depot: windows
os: windows-latest
builder-args: --windows --x64
arch: x64
- depot: macos
os: macos-26
builder-args: --macos --arm64
arch: arm64
- depot: linux
os: ubuntu-latest
builder-args: --linux --x64
arch: x64
steps:
# The Linux desktop build needs more space than the default hosted runner provides.
- name: Free Linux disk space
if: matrix.depot == 'linux'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
- uses: actions/checkout@v6
- name: Select Xcode 26.2
if: matrix.depot == 'macos'
run: |
sudo xcode-select -s /Applications/Xcode_26.2.app
xcodebuild -version
- name: Show macOS toolchain
if: matrix.depot == 'macos'
run: |
xcodebuild -version
actool --version
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
- name: Install macOS system dependencies for node-canvas
if: matrix.depot == 'macos'
run: brew install pkg-config cairo pango jpeg giflib librsvg
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup Godot
uses: chickensoft-games/setup-godot@v2
with:
version: '4.7.1'
use-dotnet: true
include-templates: true
- name: Export Godot Stage for Windows
if: matrix.depot == 'windows'
working-directory: ./engines/stage-tamagotchi-godot
shell: bash
run: |
mkdir -p out/win
godot --headless --export-release "Windows Desktop" out/win/godot-stage.exe
- name: Export Godot Stage for macOS
if: matrix.depot == 'macos'
working-directory: ./engines/stage-tamagotchi-godot
run: |
mkdir -p out/mac
godot --headless --export-release "macOS" out/mac/godot-stage.app
- name: Clean Godot Stage for Steam on macOS
if: matrix.depot == 'macos'
working-directory: ./engines/stage-tamagotchi-godot
run: |
# NOTICE:
# SteamPipe excludes *.pdb files after Electron signs the parent app.
# Removing Godot debug symbols and AppleDouble files before signing keeps macOS sealed resources aligned with the installed bundle.
# Source/context: Steam internal-test BuildID 24160350 and the game-ci SteamPipe FileExclusion "*.pdb" behavior.
# Removal condition: SteamPipe preserves these resources or the embedded Godot bundle no longer contains them.
find out/mac/godot-stage.app -name '*.pdb' -delete
find out/mac/godot-stage.app -name '._*' -delete
- name: Export Godot Stage for Linux
if: matrix.depot == 'linux'
working-directory: ./engines/stage-tamagotchi-godot
run: |
mkdir -p out/linux
godot --headless --export-release "Linux ${{ matrix.arch }}" out/linux/godot-stage
- name: Build Windows app
if: matrix.depot == 'windows'
run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=never
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build macOS app
if: matrix.depot == 'macos'
run: |
echo "$CSC_CONTENT" | base64 --decode > apps/stage-tamagotchi/apple-developer-code-signing.p12
export CSC_LINK="./apple-developer-code-signing.p12"
pnpm run -F @proj-airi/stage-tamagotchi build
pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=never
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_CONTENT: ${{ secrets.CSC_CONTENT }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_DEVELOPER_APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
- name: Build Linux app
if: matrix.depot == 'linux'
run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=never
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Package Windows depot
if: matrix.depot == 'windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force steam-content/windows | Out-Null
Copy-Item apps/stage-tamagotchi/dist/win-unpacked/* steam-content/windows -Recurse -Force
tar -cf steam-content-windows.tar -C steam-content windows
- name: Package macOS depot
if: matrix.depot == 'macos'
run: |
app_path="apps/stage-tamagotchi/dist/mac-arm64/AIRI.app"
if [[ ! -d "$app_path" ]]; then
echo "::error::No macOS app bundle was produced."
exit 1
fi
mkdir -p steam-content/macos
ditto --norsrc "$app_path" steam-content/macos/AIRI.app
find steam-content/macos -name '._*' -delete
COPYFILE_DISABLE=1 tar -cf steam-content-macos.tar -C steam-content macos
- name: Package Linux depot
if: matrix.depot == 'linux'
run: |
mkdir -p steam-content/linux
cp -a apps/stage-tamagotchi/dist/linux-unpacked/. steam-content/linux/
tar -cf steam-content-linux.tar -C steam-content linux
- name: Upload depot artifact
uses: actions/upload-artifact@v7
with:
name: steam-content-${{ matrix.depot }}
path: steam-content-${{ matrix.depot }}.tar
if-no-files-found: error
overwrite: true
deploy:
name: Upload to Steam
if: ${{ github.event_name == 'release' || inputs.upload_to_steam }}
needs: build
runs-on: ubuntu-latest
env:
STEAM_APP_ID: '3885340'
STEAM_DEBUG_BRANCH: 'false'
STEAM_RELEASE_BRANCH: ${{ github.event_name == 'workflow_dispatch' && inputs.release_branch || 'internal-test' }}
STEAM_WINDOWS_DEPOT_ID: '3885342'
steps:
- name: Validate Steam branch
env:
RELEASE_BRANCH: ${{ env.STEAM_RELEASE_BRANCH }}
run: |
if [[ ! "$RELEASE_BRANCH" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "::error::Steam release branch may contain only letters, numbers, dots, underscores, and hyphens."
exit 1
fi
if [[ "$RELEASE_BRANCH" == "default" ]]; then
echo '::error::Steam release branch "default" must be promoted manually.'
exit 1
fi
- name: Normalize Steam build description
id: metadata
uses: actions/github-script@v9
env:
INPUT_BUILD_DESCRIPTION: ${{ inputs.build_description }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
with:
result-encoding: string
script: |
const source = process.env.RELEASE_TAG || process.env.GITHUB_SHA
const fallback = `AIRI ${source} Steam candidate`
const normalizedDescription = (process.env.INPUT_BUILD_DESCRIPTION || fallback)
.replace(/[\r\n\t]+/g, ' ')
.replace(/[\u0000-\u001f\u007f]+/g, ' ')
.replaceAll('\\', '/')
.replaceAll('"', "'")
.replace(/ {2,}/g, ' ')
.trim()
.slice(0, 512)
return normalizedDescription || fallback
- name: Download Steam depots
uses: actions/download-artifact@v5
with:
pattern: steam-content-*
path: steam-artifacts
merge-multiple: true
- name: Prepare Steam upload root
run: |
mkdir -p steam-build
tar -xf steam-artifacts/steam-content-windows.tar -C steam-build
tar -xf steam-artifacts/steam-content-macos.tar -C steam-build
tar -xf steam-artifacts/steam-content-linux.tar -C steam-build
- name: Deploy desktop builds to Steam
uses: game-ci/steam-deploy@v3.2.0
with:
username: ${{ secrets.STEAM_USERNAME }}
configVdf: ${{ secrets.STEAM_CONFIG_VDF }}
appId: ${{ env.STEAM_APP_ID }}
firstDepotIdOverride: ${{ env.STEAM_WINDOWS_DEPOT_ID }}
buildDescription: ${{ steps.metadata.outputs.result }}
rootPath: steam-build
depot1Path: windows
depot2Path: macos
depot3Path: linux
releaseBranch: ${{ env.STEAM_RELEASE_BRANCH }}
debugBranch: ${{ env.STEAM_DEBUG_BRANCH }}
-665
View File
@@ -1,665 +0,0 @@
name: Release Tamagotchi
permissions:
contents: write
actions: read
env:
BUNDLE_NAME: ''
DEB_BUNDLE_NAME: ''
RPM_BUNDLE_NAME: ''
FLATPAK_BUNDLE_NAME: ''
PRODUCT_NAME: 'AIRI'
VERSION: ''
on:
release:
types:
- prereleased
workflow_dispatch:
inputs:
build_only:
description: Build only
required: false
default: false
type: boolean
artifacts_only:
description: Build and upload artifacts only
required: false
default: false
type: boolean
tag:
description: Specific tag/commit for the release (leave empty to auto-detect latest tag)
required: false
type: string
platform:
description: Platform
type: choice
options:
- all
- windows
- macos
- linux
schedule:
- cron: '0 0 * * *'
jobs:
build:
name: Build
continue-on-error: ${{ matrix.skip }} # WORKAROUND: Skipping subsequent steps without failing the whole workflow
env:
VITE_ENABLE_ANALYTICS: ${{ ((github.event_name == 'release') || (github.event_name == 'workflow_dispatch' && !inputs.build_only)) && 'true' || 'false' }}
strategy:
matrix:
include:
- os: macos-15-intel
artifact: darwin-x64
target: x86_64-apple-darwin
arch: x64
builder-args: --macos --x64
skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'macos' }}
- os: macos-26
artifact: darwin-arm64
target: aarch64-apple-darwin
builder-args: --macos --arm64
arch: arm64
skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'macos' }}
- os: ubuntu-latest
artifact: linux-x64
target: x86_64-unknown-linux-gnu
builder-args: --linux --x64
arch: x64
skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'linux' }}
- os: ubuntu-24.04-arm
artifact: linux-arm64
target: aarch64-unknown-linux-gnu
builder-args: --linux --arm64
arch: arm64
skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'linux' }}
- os: windows-latest
artifact: windows-x64-setup
target: x86_64-pc-windows-msvc
builder-args: --windows --x64
arch: x64
skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'windows' }}
runs-on: ${{ matrix.os }}
steps:
- name: Skip the build (fail fast)
run: |
echo "Skipping build for ${{ matrix.os }}..."
echo "This will fail the job, but don't panic—this is expected when not building for all."
echo "This will be improved in the future."
exit ${{ matrix.skip && '1' || '0' }}
# Why?
#
# failed to build archive at `/home/runner/work/airi/airi/target/x86_64-unknown-linux-gnu/release/deps/libapp_lib.rlib`:
# No space left on device (os error 28)
- name: Free Disk Space
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
- uses: actions/checkout@v6
- name: macOS Select Xcode 26.2
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
run: |
sudo xcode-select -s /Applications/Xcode_26.2.app
xcodebuild -version
- name: macOS Show Toolchain
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
run: |
xcodebuild -version
actool --version
# NOTICE:
# pnpm 11 does not ship a working standalone executable for Intel macOS.
# Install pnpm through npm so it uses the Node.js runtime from setup-node.
# https://github.com/pnpm/pnpm/issues/11423
# Remove this branch when the project uses pnpm 12 or later.
- uses: actions/setup-node@v6
if: matrix.os == 'macos-15-intel'
with:
node-version: 26.7.0
- name: Install pnpm with npm
if: matrix.os == 'macos-15-intel'
run: npm install --global pnpm@11.24.0
- name: Get pnpm store path
if: matrix.os == 'macos-15-intel'
id: pnpm-store
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Cache pnpm store
if: matrix.os == 'macos-15-intel'
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-${{ runner.arch }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-pnpm-store-
- uses: pnpm/setup@v2
if: matrix.os != 'macos-15-intel'
with:
runtime: node@26.7.0
cache: true
install: false
- name: Install macOS System Dependencies for node-canvas
if: runner.os == 'macOS'
run: |
brew install pkg-config cairo pango jpeg giflib librsvg
- run: pnpm install --frozen-lockfile
# ---------
# Build
# ---------
- run: pnpm run build:packages
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup Godot
uses: chickensoft-games/setup-godot@v2
with:
version: '4.7.1'
use-dotnet: true
include-templates: true
- name: Export Godot Stage (Windows)
if: matrix.os == 'windows-latest'
working-directory: ./engines/stage-tamagotchi-godot
shell: bash
run: |
mkdir -p out/win
godot --headless --export-release "Windows Desktop" out/win/godot-stage.exe
- name: Export Godot Stage (macOS)
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
working-directory: ./engines/stage-tamagotchi-godot
shell: bash
run: |
mkdir -p out/mac
godot --headless --export-release "macOS" out/mac/godot-stage.app
- name: Export Godot Stage (Linux)
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
working-directory: ./engines/stage-tamagotchi-godot
shell: bash
run: |
mkdir -p out/linux
godot --headless --export-release "Linux ${{ matrix.arch }}" out/linux/godot-stage
- name: Build (Windows Only) # Windows
if: matrix.os == 'windows-latest'
run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build (macOS Only) # macOS
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
run: |
echo "$CSC_CONTENT" | base64 --decode > apps/stage-tamagotchi/apple-developer-code-signing.p12
export CSC_LINK="./apple-developer-code-signing.p12"
pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_CONTENT: ${{ secrets.CSC_CONTENT }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_DEVELOPER_APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
- name: Build (Linux Only) # Linux
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Flatpak (Linux Only)
if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' }}
run: |
sudo apt update
sudo apt install -y flatpak flatpak-builder elfutils
flatpak --version
flatpak --user remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
- name: Build Flatpak (Linux Only) # Flatpak
if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' }}
working-directory: ./apps/stage-tamagotchi
run: |
mkdir -p dist
# Auto-install required SDK/Platform/BaseApp from Flathub in user scope
flatpak-builder --user --install-deps-from=flathub ./flatpak ai.moeru.airi.flatpak.yml --force-clean
flatpak build-export ./flatpak-repo ./flatpak
export FLATPAK_OUTPUT_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-output-filename flatpak)
flatpak build-bundle ./flatpak-repo dist/${FLATPAK_OUTPUT_NAME} ai.moeru.airi
# ---------
# Nightly (schedule) builds only
# ---------
- name: Get Artifacts Envs (Nightly + Windows Only)
if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name)" >> $env:GITHUB_ENV
echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version)" >> $env:GITHUB_ENV
- name: Get Artifacts Envs (Nightly + Non-Windows)
if: ${{ github.event_name == 'schedule' && matrix.os != 'windows-latest' }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version)" >> $GITHUB_ENV
- name: Get Artifacts Envs (Nightly + macOS Only)
if: ${{ github.event_name == 'schedule' && (matrix.os == 'macos-26' || matrix.os == 'macos-15-intel') }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name)" >> $GITHUB_ENV
- name: Rename Artifacts (Nightly)
if: ${{ github.event_name == 'schedule' }}
run:
pnpm run -F @proj-airi/stage-tamagotchi rename-artifacts ${{ matrix.target }}
- name: Get Linux Artifact Names (Nightly + Linux Only)
if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "DEB_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename deb)" >> $GITHUB_ENV
echo "RPM_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename rpm)" >> $GITHUB_ENV
echo "FLATPAK_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename flatpak)" >> $GITHUB_ENV
- name: Upload Artifacts (Nightly + Non-Linux)
if: ${{ github.event_name == 'schedule' && (matrix.os != 'ubuntu-latest' && matrix.os != 'ubuntu-24.04-arm') }}
id: unsigned-artifacts-nightly
uses: actions/upload-artifact@v7
with:
name: ${{ env.BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload Artifacts (Nightly + Linux deb)
if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.DEB_BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.DEB_BUNDLE_NAME }}
- name: Upload Artifacts (Nightly + Linux rpm)
if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.RPM_BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.RPM_BUNDLE_NAME }}
- name: Upload Flatpak Artifact (Nightly + Linux Only)
if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.FLATPAK_BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.FLATPAK_BUNDLE_NAME }}
- name: Sign Windows Artifacts with SignPath (Nightly + Windows Only)
id: signpath-nightly-windows
continue-on-error: true
uses: signpath/github-action-submit-signing-request@v2
if: ${{ github.event_name == 'schedule' && (matrix.os == 'windows-latest' && github.repository == 'moeru-ai/airi') }}
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
project-slug: 'airi'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: ci-github-actions-artifacts-windows
github-artifact-id: '${{ steps.unsigned-artifacts-nightly.outputs.artifact-id }}'
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 900 # 15 minutes
download-signed-artifact-timeout-in-seconds: 900 # 15 minutes
output-artifact-directory: apps/stage-tamagotchi/bundle/signed/windows/
- name: Warn When Windows Signing Fails (Nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome != 'success' }}
run: |
echo "::warning::SignPath Windows signing failed for nightly build. Continuing with unsigned artifact at apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}."
- name: Upload Signed Artifacts (Nightly + Non-Linux)
if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome == 'success' }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }}
overwrite: true
- name: Upload Unsigned Windows Artifacts Fallback (Nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome != 'success' }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
overwrite: true
# NOTICE: Electron Builder generates latest-x64.yml during the build step, before this workflow
# submits the Windows installer to SignPath. SignPath re-signs the .exe and changes its bytes,
# which changes the updater hashes too. Regenerating latest-x64.yml from the signed installer keeps
# the published Windows update metadata aligned with the final released artifact.
- name: Regenerate Windows latest-x64.yml (Nightly + Windows Only)
if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome == 'success' }}
run: |
pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
- name: Regenerate Windows latest-x64.yml From Unsigned Artifact (Nightly Fallback)
if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome != 'success' }}
run: |
pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
# ---------
# Workflow Dispatch only
# ---------
- name: Get Artifacts Envs (Manual + Windows Only)
if: ${{ github.event_name == 'workflow_dispatch' && matrix.os == 'windows-latest' }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
- name: Get Artifacts Envs (Manual + Non-Windows)
if: ${{ github.event_name == 'workflow_dispatch' && matrix.os != 'windows-latest' }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
- name: Get Artifacts Envs (Manual + macOS Only)
if: ${{ github.event_name == 'workflow_dispatch' && (matrix.os == 'macos-26' || matrix.os == 'macos-15-intel') }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
- name: Rename Artifacts (Manual)
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
pnpm run -F @proj-airi/stage-tamagotchi rename-artifacts ${{ matrix.target }} --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }}
- name: Get Linux Artifact Names (Manual + Non-Release + Linux Only)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "DEB_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename deb --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
echo "RPM_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename rpm --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
echo "FLATPAK_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename flatpak --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
- name: Upload Artifacts (Manual + Non-Release + Non-Linux)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os != 'ubuntu-latest' && matrix.os != 'ubuntu-24.04-arm') }}
id: unsigned-artifacts-workflow-dispatch
uses: actions/upload-artifact@v7
with:
name: ${{ env.BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload Artifacts (Manual + Non-Release + Linux deb)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.DEB_BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.DEB_BUNDLE_NAME }}
- name: Upload Artifacts (Manual + Non-Release + Linux rpm)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.RPM_BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.RPM_BUNDLE_NAME }}
- name: Upload Flatpak Artifact (Manual + Non-Release + Linux)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.FLATPAK_BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.FLATPAK_BUNDLE_NAME }}
- name: Sign Windows Artifacts with SignPath (Manual + Windows Only)
id: signpath-manual-windows
continue-on-error: true
uses: signpath/github-action-submit-signing-request@v2
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'windows-latest' && github.repository == 'moeru-ai/airi') }}
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
project-slug: 'airi'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: ci-github-actions-artifacts-windows
github-artifact-id: '${{ steps.unsigned-artifacts-workflow-dispatch.outputs.artifact-id }}'
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 900 # 15 minutes
download-signed-artifact-timeout-in-seconds: 900 # 15 minutes
output-artifact-directory: apps/stage-tamagotchi/bundle/signed/windows/
- name: Warn When Windows Signing Fails (Manual)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && matrix.os == 'windows-latest' && steps.signpath-manual-windows.outcome != 'success' }}
run: |
echo "::warning::SignPath Windows signing failed for workflow_dispatch artifact build. Continuing with unsigned artifact at apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}."
- name: Move Signed Artifacts (Manual + Release + Windows Only)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && matrix.os == 'windows-latest' && steps.signpath-manual-windows.outcome == 'success' }}
run: |
Move-Item -Force apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }} apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload Signed Artifacts (Manual + Windows Only)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'windows-latest') }}
uses: actions/upload-artifact@v7
with:
name: ${{ env.BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
overwrite: true
# NOTICE: Electron Builder already wrote latest-x64.yml before SignPath signed the installer.
# After the signed .exe replaces the unsigned one, we must rebuild latest-x64.yml so its hashes
# describe the final Windows artifact that users will actually download.
- name: Regenerate Windows latest-x64.yml (Manual + Windows Only)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && matrix.os == 'windows-latest' }}
run: |
pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
- name: Upload To GitHub Releases (Manual + Release + Overwrite Release)
if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && !inputs.artifacts_only }}
uses: softprops/action-gh-release@v2
with:
files: |
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.exe
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.zip
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.dmg
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.deb
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.rpm
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.flatpak
apps/stage-tamagotchi/bundle/latest-*.yml
append_body: true
tag_name: ${{ inputs.tag }}
# ---------
# Version push
# ---------
- name: Rename Artifacts (Automatic)
if: ${{ github.event_name == 'release' }}
run: |
pnpm run -F @proj-airi/stage-tamagotchi rename-artifacts ${{ matrix.target }} --release --auto-tag
- name: Get Artifacts Envs (Automatic + Windows Only)
if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
- name: Get Artifacts Envs (Automatic + Non-Windows)
if: ${{ github.event_name == 'release' && matrix.os != 'windows-latest' }}
working-directory: ./apps/stage-tamagotchi
run: |
echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release --auto-tag)" >> $GITHUB_ENV
- name: Upload Artifacts (Automatic + Windows Only)
if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
id: unsigned-artifacts-release
uses: actions/upload-artifact@v7
with:
name: ${{ env.BUNDLE_NAME }}
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Sign Windows Artifacts with SignPath (Nightly + Windows Only)
id: signpath-release-windows
continue-on-error: true
uses: signpath/github-action-submit-signing-request@v2
if: ${{ github.event_name == 'release' && (matrix.os == 'windows-latest' && github.repository == 'moeru-ai/airi') }}
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
project-slug: 'airi'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: ci-github-actions-artifacts-windows
github-artifact-id: '${{ steps.unsigned-artifacts-release.outputs.artifact-id }}'
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 900 # 15 minutes
download-signed-artifact-timeout-in-seconds: 900 # 15 minutes
output-artifact-directory: apps/stage-tamagotchi/bundle/signed/windows/
- name: Warn When Windows Signing Fails (Release)
if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' && steps.signpath-release-windows.outcome != 'success' }}
run: |
echo "::warning::SignPath Windows signing failed for release build. Continuing with unsigned artifact at apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}."
- name: Move Signed Artifacts (Automatic + Windows Only)
if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' && steps.signpath-release-windows.outcome == 'success' }}
run: |
Move-Item -Force apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }} apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
# NOTICE: The root cause here is the build/signing order: latest-x64.yml is produced by Electron
# Builder before SignPath re-signs the installer. That post-build signing step mutates the .exe,
# so the updater metadata becomes stale. Recomputing latest-x64.yml after the signed file is moved
# into its final bundle path makes the release upload self-consistent again.
- name: Regenerate Windows latest manifest (Automatic + Windows Only)
if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
run: |
pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
- name: Upload Distribution Artifact (Automatic + macOS arm64 Only)
if: ${{ github.event_name == 'release' && matrix.os == 'macos-26' }}
uses: actions/upload-artifact@v7
with:
name: distribution-osx
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload Distribution Artifact (Automatic + Windows Only)
if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
uses: actions/upload-artifact@v7
with:
name: distribution-win
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload Distribution Artifact (Automatic + Linux x64 Only)
if: ${{ github.event_name == 'release' && matrix.os == 'ubuntu-latest' }}
uses: actions/upload-artifact@v7
with:
name: distribution-linux-x64
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload Distribution Artifact (Automatic + Linux arm64 Only)
if: ${{ github.event_name == 'release' && matrix.os == 'ubuntu-24.04-arm' }}
uses: actions/upload-artifact@v7
with:
name: distribution-linux-arm64
path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- name: Upload To GitHub Releases (Automatic)
if: ${{ github.event_name == 'release' }}
uses: softprops/action-gh-release@v2
with:
# Possible auto updater files:
# Windows: latest-x64.yml
# macOS: latest-arm64-mac.yml, latest-x64-mac.yml
# Linux: latest-arm64-linux.yml (arm64), latest-x64-linux.yml (x64)
files: |
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.exe
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.zip
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.dmg
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.deb
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.rpm
apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.flatpak
apps/stage-tamagotchi/bundle/latest-*.yml
append_body: true
publish-gitcode-release:
name: Publish GitCode Release Mirror
needs: build
if: ${{ always() && github.repository == 'moeru-ai/airi' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.build_only && !inputs.artifacts_only && inputs.tag != '')) }}
continue-on-error: true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Validate release mirror tools
run: |
gh --version
jq --version
curl --version
- name: Publish release assets to GitCode
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }}
GITCODE_REPO: ${{ secrets.GITCODE_REPO }}
GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
run: bash .github/scripts/publish-gitcode-release.sh
publish-distribution:
name: Publish Distribution
needs: build
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- artifact_name: distribution-osx
platform: osx
- artifact_name: distribution-win
platform: win
- artifact_name: distribution-linux-x64
platform: linux
- artifact_name: distribution-linux-arm64
platform: linux
steps:
- name: Resolve release version
run: |
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
- name: Download distribution artifact
uses: actions/download-artifact@v5
with:
name: ${{ matrix.artifact_name }}
path: dist/${{ matrix.artifact_name }}
- name: Resolve artifact path
run: |
echo "ARTIFACT_PATH=$(find dist/${{ matrix.artifact_name }} -maxdepth 1 -mindepth 1 | head -n 1)" >> $GITHUB_ENV
- name: Setup butler
uses: remarkablegames/setup-butler@v2
# https://itch.io/docs/butler/pushing.html
- name: Publish distribution artifact to itch.io
run: butler push "${{ env.ARTIFACT_PATH }}" nekomeowww/airi:${{ matrix.platform }} --userversion "${{ env.VERSION }}"
env:
BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
+11 -9
View File
@@ -16,11 +16,13 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- name: Configure npm registry
uses: actions/setup-node@v6
@@ -29,11 +31,11 @@ jobs:
# https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
registry-url: 'https://registry.npmjs.org'
- run: pnpm install --frozen-lockfile
- run: pnpm run build:packages
- run: pnpm run -F @proj-airi/airi-plugin-vscode build
- run: pnpm run -F vscode-airi build
- run: pnpm run publish
- run: bun install --frozen-lockfile
- run: bun run build:packages
- run: bun run --filter @proj-airi/airi-plugin-vscode build
- run: bun run --filter vscode-airi build
- run: bun run publish
working-directory: ./integrations/vscode/vscode-airi
env:
VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
+8 -6
View File
@@ -21,18 +21,20 @@ jobs:
with:
ref: main
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
- run: pnpm install --frozen-lockfile
node-version: 26.7.0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- run: bun install --frozen-lockfile
- name: Pull with rebase
run: git pull origin main --rebase --autostash
- name: Generate sponsors svg
run: pnpm run sponsors:generate
run: bun run sponsors:generate
- name: Remove private SponsorKit cache
run: rm -f docs/content/public/assets/sponsors/.cache.json
@@ -1,75 +0,0 @@
name: Update Nix assets Hash
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'apps/stage-web/package.json'
- 'apps/stage-web/vite.config.ts'
permissions:
contents: write
pull-requests: write
jobs:
update:
if: github.event_name == 'workflow_dispatch' || !github.event.repository.fork
runs-on: ubuntu-latest
steps:
# Why?
#
# failed to
# $ nix build .#airi-pnpm-deps
# > airi-pnpm-deps> Running phase: fixupPhase
# > error: writing to file: No space left on device
- name: Free Disk Space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
- uses: actions/checkout@v6
with:
ref: main # Use main regardless of workflow_dispatch branch
# Authenticate git with PAT so that pushing to head retriggers CI
token: ${{ secrets.HASH_UPDATE_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
extra_nix_config: experimental-features = nix-command flakes
- name: Update Hash
run: nix/update-assets-hash.sh
- name: Check for changes
id: changes
run: |
if git diff --quiet; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Create PR
if: steps.changes.outputs.has_changes == 'true'
env:
# Create PR with PAT to trigger CI
GH_TOKEN: ${{ secrets.HASH_UPDATE_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add nix/assets-hash.txt
git commit -m 'chore(nix): update assets hash'
git push -f origin HEAD:chore/update-nix-assets-hash
# Create PR if it doesn't already exist
existing_pr=$(gh pr list --head chore/update-nix-assets-hash --json number -q '.[0].number' 2>/dev/null || true)
if [ -z "$existing_pr" ]; then
gh pr create \
--base main \
--head chore/update-nix-assets-hash \
--title 'chore(nix): update assets hash' \
--body 'Auto-generated by CI to keep Nix asset hash up-to-date.'
fi
# Enable auto-merge so PR merges once checks pass
gh pr merge --squash --auto --delete-branch chore/update-nix-assets-hash
@@ -1,74 +0,0 @@
name: Update Nix pnpmDeps Hash
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'pnpm-lock.yaml'
permissions:
contents: write
pull-requests: write
jobs:
update:
if: github.event_name == 'workflow_dispatch' || !github.event.repository.fork
runs-on: ubuntu-latest
steps:
# Why?
#
# failed to
# $ nix build .#airi-pnpm-deps
# > airi-pnpm-deps> Running phase: fixupPhase
# > error: writing to file: No space left on device
- name: Free Disk Space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
- uses: actions/checkout@v6
with:
ref: main # Use main regardless of workflow_dispatch branch
# Authenticate git with PAT so that pushing to head retriggers CI
token: ${{ secrets.HASH_UPDATE_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
extra_nix_config: experimental-features = nix-command flakes
- name: Update Hash
run: nix/update-pnpm-deps-hash.sh
- name: Check for changes
id: changes
run: |
if git diff --quiet; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Create PR
if: steps.changes.outputs.has_changes == 'true'
env:
# Create PR with PAT to trigger CI
GH_TOKEN: ${{ secrets.HASH_UPDATE_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add nix/pnpm-deps-hash.txt
git commit -m 'chore(nix): update pnpmDeps hash'
git push -f origin HEAD:chore/update-nix-pnpm-deps-hash
# Create PR if it doesn't already exist
existing_pr=$(gh pr list --head chore/update-nix-pnpm-deps-hash --json number -q '.[0].number' 2>/dev/null || true)
if [ -z "$existing_pr" ]; then
gh pr create \
--base main \
--head chore/update-nix-pnpm-deps-hash \
--title 'chore(nix): update pnpmDeps hash' \
--body 'Auto-generated by CI to keep Nix pnpmDeps hash up-to-date.'
fi
# Enable auto-merge so PR merges once checks pass
gh pr merge --squash --auto --delete-branch chore/update-nix-pnpm-deps-hash
@@ -1,165 +0,0 @@
name: Update (Post Release) README URLs
on:
release:
types:
- published
- released
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.10.2)'
required: true
type: string
permissions:
contents: read
concurrency:
group: update-post-release-readme-urls
cancel-in-progress: true
jobs:
update:
# Keep prereleases from replacing the stable README download buttons.
if: github.event_name == 'workflow_dispatch' || !github.event.release.prerelease
runs-on: ubuntu-latest
env:
README_BRANCH: automation/update-readme-release-links
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
- uses: actions/checkout@v6
with:
ref: main
persist-credentials: false
- uses: pnpm/setup@v2
with:
runtime: node@26.7.0
cache: true
install: false
- run: pnpm install --frozen-lockfile
- name: Update README download links
run: pnpm -F @proj-airi/stage-tamagotchi exec tsx scripts/update-readme-download-links.ts "$RELEASE_TAG"
- name: Pull with rebase
run: git pull origin main --rebase --autostash
- name: Prepare README release update
id: prepare
env:
GH_TOKEN: ${{ secrets.PAT_SLASH_COMMAND_DISPATCH }}
run: |
# The updater is only allowed to publish the generated README link changes.
if ! git diff --quiet -- . ':(exclude)README.md' ':(exclude)docs/README*.md'; then
echo "::error::Unexpected non-README changes were generated."
git status --short
exit 1
fi
git add README.md 'docs/README*.md'
if git diff --cached --quiet; then
echo "No README release links changed."
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ -z "$GH_TOKEN" ]; then
echo "::error::PAT_SLASH_COMMAND_DISPATCH is required so the README update PR triggers required checks."
exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
REMOTE_BRANCH="$(git ls-remote --heads origin "refs/heads/${README_BRANCH}")"
if [ -n "$REMOTE_BRANCH" ]; then
git fetch origin "${README_BRANCH}:refs/remotes/origin/${README_BRANCH}"
fi
git checkout -B "$README_BRANCH"
PR_TITLE="chore: update README release artifacts to ${RELEASE_TAG}"
git commit -m "$PR_TITLE"
git push --force-with-lease origin "$README_BRANCH"
echo "changed=true" >> "$GITHUB_OUTPUT"
echo "pr_title=$PR_TITLE" >> "$GITHUB_OUTPUT"
- name: Create or update README release PR
if: steps.prepare.outputs.changed == 'true'
uses: actions/github-script@v8
env:
PR_TITLE: ${{ steps.prepare.outputs.pr_title }}
with:
github-token: ${{ secrets.PAT_SLASH_COMMAND_DISPATCH }}
script: |
const { owner, repo } = context.repo;
const head = `${owner}:${process.env.README_BRANCH}`;
const body = [
'## Description',
'',
`Updates stable desktop download links after the ${process.env.RELEASE_TAG} release.`,
'',
'## Linked Issues',
'',
'None.',
'',
'## Additional Context',
'',
'Generated by the post-release README workflow.',
].join('\n');
const { data: openPullRequests } = await github.rest.pulls.list({
owner,
repo,
state: 'open',
base: 'main',
head,
per_page: 1,
});
let pullRequest;
if (openPullRequests.length > 0) {
const { data } = await github.rest.pulls.update({
owner,
repo,
pull_number: openPullRequests[0].number,
title: process.env.PR_TITLE,
body,
});
pullRequest = data;
core.info(`Updated pull request #${pullRequest.number}.`);
}
else {
const { data } = await github.rest.pulls.create({
owner,
repo,
base: 'main',
head: process.env.README_BRANCH,
title: process.env.PR_TITLE,
body,
});
pullRequest = data;
core.info(`Created pull request #${pullRequest.number}.`);
}
if (!pullRequest.auto_merge) {
await github.graphql(
`mutation EnableAutoMerge($pullRequestId: ID!) {
enablePullRequestAutoMerge(input: {
pullRequestId: $pullRequestId
mergeMethod: SQUASH
}) {
pullRequest {
number
}
}
}`,
{
pullRequestId: pullRequest.node_id,
},
);
core.info(`Enabled squash auto-merge for pull request #${pullRequest.number}.`);
}
@@ -19,18 +19,20 @@ jobs:
fi
- uses: actions/checkout@v6
- uses: pnpm/setup@v2
- uses: actions/setup-node@v6
with:
runtime: node@26.7.0
cache: true
install: false
node-version: 26.7.0
- run: pnpm install --frozen-lockfile
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- run: bun install --frozen-lockfile
# The TBX file is generated, never committed, so it cannot drift from terms.yaml.
# The schema also rejects a malformed entry here, before anything reaches Crowdin.
- name: Generate TBX File
run: pnpm -F @proj-airi/i18n glossary:build
run: bun run --filter @proj-airi/i18n glossary:build
# NOTICE: Crowdin does not document whether an import merges into the existing
# concepts or adds duplicates beside them, and a merge cannot be undone. This