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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 131 additions & 51 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
test:
name: test (linux)
runs-on: ubuntu-latest
outputs:
release-version: ${{ steps.version.outputs.release-version }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
Expand All @@ -49,15 +51,17 @@ jobs:
toolchain: 1.98.0
components: rustfmt, clippy

- name: Verify release tag matches package versions
if: github.event_name == 'push'
- name: Verify release versions
id: version
shell: bash
run: |
EXPECTED="${GITHUB_REF_NAME#v}"
DECK_VERSION=$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json,sys; print(json.load(sys.stdin)["packages"][0]["version"])')
WASM_VERSION=$(cargo metadata --manifest-path wasm/Cargo.toml --no-deps --format-version 1 | python3 -c 'import json,sys; print(json.load(sys.stdin)["packages"][0]["version"])')
test "$DECK_VERSION" = "$EXPECTED"
test "$WASM_VERSION" = "$EXPECTED"
DECK_VERSION=$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json,sys; print(next(item["version"] for item in json.load(sys.stdin)["packages"] if item["name"] == "allium-deck"))')
WASM_VERSION=$(cargo metadata --manifest-path wasm/Cargo.toml --no-deps --format-version 1 | python3 -c 'import json,sys; print(next(item["version"] for item in json.load(sys.stdin)["packages"] if item["name"] == "allium-deck-wasm"))')
test "$DECK_VERSION" = "$WASM_VERSION"
if [ "$GITHUB_EVENT_NAME" = push ]; then
test "v$DECK_VERSION" = "$GITHUB_REF_NAME"
fi
echo "release-version=$DECK_VERSION" >> "$GITHUB_OUTPUT"

- name: Cache cargo registry and build
uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2
Expand All @@ -69,13 +73,13 @@ jobs:

- name: cargo clippy
run: |
cargo clippy --all-targets
cargo clippy --manifest-path wasm/Cargo.toml --all-targets
cargo clippy --locked --all-features --all-targets -- -D warnings
cargo clippy --manifest-path wasm/Cargo.toml --all-targets --locked -- -D warnings

- name: cargo test
run: |
cargo test --all-targets --release
cargo test --manifest-path wasm/Cargo.toml --all-targets --release
cargo test --locked --all-features --all-targets --release
cargo test --manifest-path wasm/Cargo.toml --all-targets --release --locked

- name: release packaging tests
run: python3 tests/test_release_packaging.py
Expand Down Expand Up @@ -110,7 +114,6 @@ jobs:
name: build (${{ matrix.target }})
needs: test
runs-on: ${{ matrix.os }}
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -178,45 +181,38 @@ jobs:
shell: bash
run: |
mkdir -p release
cp target/${{ matrix.target }}/release/${{ matrix.bin_name }} release/recommend_cli-${{ github.ref_name }}-${{ matrix.asset_name }}
cp target/${{ matrix.target }}/release/${{ matrix.bin_name }} release/recommend_cli-v${{ needs.test.outputs.release-version }}-${{ matrix.asset_name }}

- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: recommend_cli-${{ matrix.target }}
name: release-build-${{ matrix.target }}
path: release/*
if-no-files-found: error

build-wasm-release:
name: build WASM npm package
needs: test
runs-on: ubuntu-latest
outputs:
wasm-version: ${{ steps.inputs.outputs.wasm-version }}
# npm trusted publishing 需要 OIDC token,必须在能发出它的 job 层声明;
# 顶层 `permissions: contents: read` 会被这里的 job 级设置覆盖。
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set reproducible build timestamp
run: echo "SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)" >> "$GITHUB_ENV"

- name: Resolve WASM release version
id: inputs
env:
VERSION: ${{ needs.test.outputs.release-version }}
run: |
# wasm 与 masterdata 解耦,产物版本即 wasm crate 版本。
VERSION=$(cargo metadata --manifest-path wasm/Cargo.toml --no-deps --format-version 1 | python3 -c 'import json,sys; print(json.load(sys.stdin)["packages"][0]["version"])')
WASM_ZIP="allium-deck-wasm-${GITHUB_REF_NAME}-cn.zip"
NPM_TGZ="allium-deck-wasm-${GITHUB_REF_NAME}.tgz"
WASM_ZIP="allium-deck-wasm-v${VERSION}-cn.zip"
NPM_TGZ="allium-deck-wasm-v${VERSION}.tgz"
{
echo "DIST_DIR=dist/deck-wasm"
echo "WASM_VERSION=${VERSION}"
echo "WASM_ZIP=${WASM_ZIP}"
echo "NPM_TGZ=${NPM_TGZ}"
} >> "$GITHUB_ENV"
echo "wasm-version=${VERSION}" >> "$GITHUB_OUTPUT"

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
Expand All @@ -240,6 +236,11 @@ jobs:
CARGO_PROFILE_RELEASE_PANIC: "abort"
run: wasm-pack build wasm --target web --scope empty-sekai

- name: Set up Node for packaging and smoke tests
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Package release files
run: |
python3 scripts/package_wasm.py \
Expand All @@ -266,12 +267,6 @@ jobs:
(cd wasm/pkg && npm pack --pack-destination ../../release)
mv release/empty-sekai-allium-deck-wasm-*.tgz "release/$NPM_TGZ"

- name: Set up Node for npm trusted publishing
uses: actions/setup-node@v4
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'

- name: Smoke-test npm package
run: |
INSTALL_ROOT=$(mktemp -d)
Expand All @@ -280,29 +275,103 @@ jobs:
cp scripts/smoke_wasm_package.mjs "$INSTALL_ROOT/smoke.mjs"
node "$INSTALL_ROOT/smoke.mjs"

- name: Publish to npm (trusted publishing)
run: |
# trusted publishing 硬门槛:npm CLI >= 11.5.1 / Node >= 22.14。
# runner 自带版本未必满足,强制升到 latest。
npm install -g npm@latest
# trusted publishing 校验 package.json 的 repository.url 与 GitHub 仓库一致,
# 缺了会报 "Repository URL mismatch"。wasm-pack 生成的 package.json 不带该字段,
# 此处补上并显式设为 public(scoped 包默认 restricted)。
node -e "const fs=require('fs');const p='wasm/pkg/package.json';const j=JSON.parse(fs.readFileSync(p));j.repository={type:'git',url:'git+https://github.com/empty-sekai/allium-deck.git'};j.publishConfig={access:'public'};fs.writeFileSync(p,JSON.stringify(j,null,2)+'\n')"
cd wasm/pkg
npm publish --provenance

- name: Upload WASM release artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: allium-deck-wasm-${{ github.ref_name }}-cn
name: release-build-wasm
path: |
release/*.zip
release/*.tgz
if-no-files-found: error

release-assets:
name: verify complete release assets
needs: [test, build-binaries, build-wasm-release]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Download artifacts from this run
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: release-build-*
path: release
merge-multiple: true

- name: Create and verify SHA256SUMS
env:
VERSION: ${{ needs.test.outputs.release-version }}
run: |
python3 scripts/create_release_checksums.py --directory release --version "$VERSION"
(cd release && sha256sum --check SHA256SUMS)

- name: Upload verified release assets
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-assets
path: release/*
if-no-files-found: error

publish-npm:
name: publish npm package
needs: [test, release-assets]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Download verified release assets
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: release-assets
path: release

- name: Set up Node for npm trusted publishing
uses: actions/setup-node@v4
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'

- name: Publish the tested npm tarball (trusted publishing)
env:
VERSION: ${{ needs.test.outputs.release-version }}
run: |
(cd release && sha256sum --check SHA256SUMS)
set +e
node scripts/verify_npm_package.mjs \
--package @empty-sekai/allium-deck-wasm --version "$VERSION" \
--tarball "release/allium-deck-wasm-v${VERSION}.tgz" \
--attempts 3 --delay-ms 2000 --timeout-ms 5000
LOOKUP_STATUS=$?
set -e
case "$LOOKUP_STATUS" in
0) exit 0 ;;
3) ;;
*) exit "$LOOKUP_STATUS" ;;
esac
# Trusted publishing requires npm >= 11.5.1 and Node >= 22.14.
npm install -g npm@latest
set +e
npm publish --provenance --access public "release/allium-deck-wasm-v${VERSION}.tgz"
PUBLISH_STATUS=$?
set -e
if node scripts/verify_npm_package.mjs \
--package @empty-sekai/allium-deck-wasm --version "$VERSION" \
--tarball "release/allium-deck-wasm-v${VERSION}.tgz" \
--attempts 6 --delay-ms 3000 --timeout-ms 5000; then
if [ "$PUBLISH_STATUS" -ne 0 ]; then
echo "npm publish returned ${PUBLISH_STATUS}, but the registry has the tested tarball"
fi
exit 0
fi
exit 1

publish-crate:
name: publish crate
needs: [test, build-binaries, build-wasm-release]
needs: [test, release-assets]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
Expand Down Expand Up @@ -358,7 +427,7 @@ jobs:

release:
name: release
needs: [publish-crate, build-wasm-release]
needs: [publish-crate, publish-npm, release-assets]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
permissions:
Expand Down Expand Up @@ -388,6 +457,10 @@ jobs:
RANGE="${GITHUB_REF_NAME}"
fi
git-cliff --tag "${GITHUB_REF_NAME}" --output CHANGELOG.md "$RANGE"
if [ -f "docs/releases/${GITHUB_REF_NAME}.md" ]; then
printf '\n' >> CHANGELOG.md
cat "docs/releases/${GITHUB_REF_NAME}.md" >> CHANGELOG.md
fi
{
echo ""
echo "### Assets"
Expand All @@ -396,13 +469,18 @@ jobs:
echo "| \`recommend_cli-${GITHUB_REF_NAME}-*\` | Prebuilt CLI binaries (Linux x86_64 / musl / aarch64, Windows x86_64, macOS Apple Silicon) |"
echo "| \`allium-deck-wasm-${GITHUB_REF_NAME}.tgz\` | Installable npm package \`@empty-sekai/allium-deck-wasm\` |"
echo "| \`allium-deck-wasm-${GITHUB_REF_NAME}-cn.zip\` | Browser WASM bundle (ZIP) |"
echo "| \`SHA256SUMS\` | SHA-256 checksums for all five native binaries and both WASM packages |"
} >> CHANGELOG.md

- name: Download all artifacts
- name: Download verified release assets
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: release-assets
path: artifacts

- name: Verify downloaded release assets
run: (cd artifacts && sha256sum --check SHA256SUMS)

- name: Create GitHub Release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
Expand All @@ -412,6 +490,8 @@ jobs:
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
files: |
artifacts/**/recommend_cli-*
artifacts/**/allium-deck-wasm-*.zip
artifacts/**/allium-deck-wasm-*.tgz
artifacts/recommend_cli-*
artifacts/allium-deck-wasm-*.zip
artifacts/allium-deck-wasm-*.tgz
artifacts/SHA256SUMS
fail_on_unmatched_files: true
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "allium-deck"
version = "0.0.15"
version = "0.1.0"
edition = "2024"
rust-version = "1.89"
description = "Project Sekai deck recommendation engine: DFS / branch-and-bound exact search"
Expand All @@ -13,6 +13,7 @@ include = [
"/README.en.md",
"/LICENSE-MIT",
"/LICENSE-APACHE",
"/docs/game-rule-sources.md",
"/src/**",
"/data/**",
]
Expand Down
3 changes: 2 additions & 1 deletion README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Some in-game values and logic (power, skill bonuses, event points, support decks
- https://github.com/Team-Haruki/sekai-deck-recommend-cpp
- https://github.com/StarMoe-org/sekai-deck-recommend-cpp

See the individual commit messages for exactly what was ported and corrected.
The direct source of the JP7 business-rule port is **[Team-Haruki/sekai-deck-recommend-cpp](https://github.com/Team-Haruki/sekai-deck-recommend-cpp)** at [`496caed78a07ffe1f2d1f1553047dbf6b2ed0314`](https://github.com/Team-Haruki/sekai-deck-recommend-cpp/commit/496caed78a07ffe1f2d1f1553047dbf6b2ed0314). Area-item, MySEKAI gate, World Link finale, and event-limit rules are reimplemented in the existing Rust engine. See [Business-rule sources](docs/game-rule-sources.md) for source files, upstream attribution, and behavior differences.

On top of that, this implementation is not a line-by-line translation: the **low-level hot paths and search pruning have been thoroughly reworked in Rust**, with all core data structures aligned to cache lines:

Expand Down Expand Up @@ -170,6 +170,7 @@ Flags:
| `--world-bloom-character-id` / `--world-bloom-event-turn` / `--challenge-live-character-id` | value | World Bloom / Challenge Live special parameters. |
| `--skill-reference-strategy` / `--live-skill-order` / `--specific-skill-order` | value | Skill reference and activation order; a specific order is given as `0,1,2,3,4`. |
| `--multi-teammate-power` / `--multi-teammate-score-up` / `--multi-live-score-up-lower-bound` | value | Teammate power, effective skill score-up, and total skill lower bound for multi / Cheerful lives. |
| `--multi-unit-bonus-evaluation` | enum | Owned multi-unit area effects: `by_deck` (default), `force_on`, `force_off`. Shared by deck and area-upgrade recommendations; see the [parameter reference](docs/parameters.md#area-items-and-mysekai-gates). |
| `--other-score` / `--life` | value | Cheerful opponent score and life. |
| `--rarity4-config` / `--single-card-config` | value | Card training configs, e.g. `level_max,skill_max,master_max,episode_read,canvas` and `123:level_max,skill_max`. |

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Project Sekai 组卡推荐引擎的 Rust 实现,专攻 **DFS / 分支限界(
- https://github.com/Team-Haruki/sekai-deck-recommend-cpp
- https://github.com/StarMoe-org/sekai-deck-recommend-cpp

具体移植与修正内容见各 commit 说明。
本次 JP7 业务规则移植的直接来源是 **[Team-Haruki/sekai-deck-recommend-cpp](https://github.com/Team-Haruki/sekai-deck-recommend-cpp)**,对照提交为 [`496caed78a07ffe1f2d1f1553047dbf6b2ed0314`](https://github.com/Team-Haruki/sekai-deck-recommend-cpp/commit/496caed78a07ffe1f2d1f1553047dbf6b2ed0314)。区域道具、MySEKAI 大门、World Link 终章与活动上限等业务规则在现有 Rust 引擎中重新实现。来源文件、上游作者与行为差异见[业务规则来源](docs/game-rule-sources.md)。

在此基础上,本实现并非逐行翻译,而是对**底层热路径与搜索剪枝做了彻底的 Rust 重构**,核心数据结构全部按 cache line 对齐:

Expand Down Expand Up @@ -189,6 +189,7 @@ recommend_cli \
| `--mode area-items` / `--mode music` / `--mode exact-live` | 模式 | 辅助计算(不组卡):`area-items` 需 `--card-ids`;`music` 需 `--deck`;`exact-live` 需 `--power/--skills/--music-score`。 |
| `--skill-reference-strategy` / `--live-skill-order` / `--specific-skill-order` | 值 | 技能参考与发动顺序;指定顺序使用 `0,1,2,3,4`。 |
| `--multi-teammate-power` / `--multi-teammate-score-up` / `--multi-live-score-up-lower-bound` | 值 | 协力和 Cheerful 队友综合力、技能实效、技能总下限。 |
| `--multi-unit-bonus-evaluation` | 枚举 | 混编区域道具:`by_deck`(默认)/`force_on`/`force_off`;组卡与区域道具升级共用,规则见 [参数文档](docs/parameters.md#area-items-and-mysekai-gates)。 |
| `--other-score` / `--life` | 值 | Cheerful 对手分数和体力。 |
| `--rarity4-config` / `--single-card-config` | 值 | 养成配置,如 `level_max,skill_max,master_max,episode_read,canvas` 和 `123:level_max,skill_max`。 |

Expand Down
Loading
Loading