chore(build): consolidate packaging inputs and build output layout (#299)

This commit is contained in:
sudacode authored and GitHub committed 2026-10-24 23:28:38 -07:00
1 parent b0a8b6b3fc
commit 3549e0ed93
43 files changed
+195 -160

No files matched your search

+17 -17
View File
@@ -67,26 +67,26 @@ jobs:
- name: Build unversioned AppImage - name: Build unversioned AppImage
run: | run: |
shopt -s nullglob shopt -s nullglob
appimages=(release/SubMiner-*.AppImage) appimages=(dist/release/SubMiner-*.AppImage)
if [ "${#appimages[@]}" -eq 0 ]; then if [ "${#appimages[@]}" -eq 0 ]; then
echo "No versioned AppImage found to create unversioned artifact." echo "No versioned AppImage found to create unversioned artifact."
ls -la release ls -la dist/release
exit 1 exit 1
fi fi
cp "${appimages[0]}" release/SubMiner.AppImage cp "${appimages[0]}" dist/release/SubMiner.AppImage
- name: Smoke packaged runtime assets - name: Smoke packaged runtime assets
shell: bash shell: bash
run: xvfb-run -a bun run test:package "release/linux-unpacked/resources" run: xvfb-run -a bun run test:package "dist/release/linux-unpacked/resources"
- name: Upload AppImage artifact - name: Upload AppImage artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: appimage name: appimage
path: | path: |
release/*.AppImage dist/release/*.AppImage
release/latest*.yml dist/release/latest*.yml
release/*.blockmap dist/release/*.blockmap
if-no-files-found: error if-no-files-found: error
build-macos: build-macos:
@@ -159,17 +159,17 @@ jobs:
- name: Smoke packaged runtime assets - name: Smoke packaged runtime assets
shell: bash shell: bash
run: bun run test:package "release/mac-arm64/SubMiner.app/Contents/Resources" run: bun run test:package "dist/release/mac-arm64/SubMiner.app/Contents/Resources"
- name: Upload macOS artifacts - name: Upload macOS artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: macos name: macos
path: | path: |
release/*.dmg dist/release/*.dmg
release/*.zip dist/release/*.zip
release/latest*.yml dist/release/latest*.yml
release/*.blockmap dist/release/*.blockmap
if-no-files-found: error if-no-files-found: error
build-windows: build-windows:
@@ -224,15 +224,15 @@ jobs:
- name: Smoke packaged runtime assets - name: Smoke packaged runtime assets
shell: bash shell: bash
run: bun run test:package "release/win-unpacked/resources" run: bun run test:package "dist/release/win-unpacked/resources"
- name: Upload Windows artifacts - name: Upload Windows artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: windows name: windows
path: | path: |
release/*.exe dist/release/*.exe
release/*.zip dist/release/*.zip
release/latest*.yml dist/release/latest*.yml
release/*.blockmap dist/release/*.blockmap
if-no-files-found: error if-no-files-found: error
+25 -23
View File
@@ -44,19 +44,19 @@ jobs:
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: appimage name: appimage
path: release path: dist/release
- name: Download macOS artifacts - name: Download macOS artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: macos name: macos
path: release path: dist/release
- name: Download Windows artifacts - name: Download Windows artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: windows name: windows
path: release path: dist/release
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
@@ -90,13 +90,15 @@ jobs:
- name: Package optional assets bundle - name: Package optional assets bundle
run: | run: |
tar -czf "release/subminer-assets.tar.gz" \ # Keep the archive layout stable for the AUR PKGBUILD.
tar -czf "dist/release/subminer-assets.tar.gz" \
--transform 's,^packaging/bun/licenses,resources/bun/licenses,' \
config.example.jsonc \ config.example.jsonc \
plugin/subminer \ plugin/subminer \
plugin/subminer.conf \ plugin/subminer.conf \
assets/themes/subminer.rasi \ assets/themes/subminer.rasi \
assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer \ assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer \
resources/bun/licenses packaging/bun/licenses
- name: Package Bun corresponding source - name: Package Bun corresponding source
run: bun scripts/package-bun-source.mjs run: bun scripts/package-bun-source.mjs
@@ -104,16 +106,16 @@ jobs:
- name: Generate checksums - name: Generate checksums
run: | run: |
shopt -s nullglob shopt -s nullglob
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd) files=(dist/release/*.AppImage dist/release/*.dmg dist/release/*.exe dist/release/*.zip dist/release/*.tar.gz dist/release/latest*.yml dist/release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
if [ "${#files[@]}" -eq 0 ]; then if [ "${#files[@]}" -eq 0 ]; then
echo "No release artifacts found for checksum generation." echo "No release artifacts found for checksum generation."
exit 1 exit 1
fi fi
: > release/SHA256SUMS.txt : > dist/release/SHA256SUMS.txt
for file in "${files[@]}"; do for file in "${files[@]}"; do
printf '%s %s\n' \ printf '%s %s\n' \
"$(sha256sum "$file" | awk '{print $1}')" \ "$(sha256sum "$file" | awk '{print $1}')" \
"${file##*/}" >> release/SHA256SUMS.txt "${file##*/}" >> dist/release/SHA256SUMS.txt
done done
- name: Get version from tag - name: Get version from tag
@@ -124,12 +126,12 @@ jobs:
env: env:
RELEASE_VERSION: ${{ steps.version.outputs.VERSION }} RELEASE_VERSION: ${{ steps.version.outputs.VERSION }}
run: | run: |
if [ ! -s release/prerelease-notes.md ]; then if [ ! -s packaging/release-notes/prerelease-notes.md ]; then
echo "::error::release/prerelease-notes.md is missing or empty. Run 'bun run changelog:prerelease-notes --version <version>' locally and commit the file before tagging." echo "::error::packaging/release-notes/prerelease-notes.md is missing or empty. Run 'bun run changelog:prerelease-notes --version <version>' locally and commit the file before tagging."
exit 1 exit 1
fi fi
if ! bun run changelog:check-prerelease-notes --version "$RELEASE_VERSION"; then if ! bun run changelog:check-prerelease-notes --version "$RELEASE_VERSION"; then
echo "::error::release/prerelease-notes.md was not generated for $RELEASE_VERSION. Rerun 'bun run changelog:prerelease-notes --version $RELEASE_VERSION' locally, commit, and retag." echo "::error::packaging/release-notes/prerelease-notes.md was not generated for $RELEASE_VERSION. Rerun 'bun run changelog:prerelease-notes --version $RELEASE_VERSION' locally, commit, and retag."
exit 1 exit 1
fi fi
@@ -142,15 +144,15 @@ jobs:
shopt -s nullglob shopt -s nullglob
artifacts=( artifacts=(
release/*.AppImage dist/release/*.AppImage
release/*.dmg dist/release/*.dmg
release/*.exe dist/release/*.exe
release/*.zip dist/release/*.zip
release/*.tar.gz dist/release/*.tar.gz
release/*.tar.gz.sha256 dist/release/*.tar.gz.sha256
release/latest*.yml dist/release/latest*.yml
release/*.blockmap dist/release/*.blockmap
release/SHA256SUMS.txt dist/release/SHA256SUMS.txt
dist/launcher/subminer dist/launcher/subminer
dist/launcher/subminer.cmd dist/launcher/subminer.cmd
) )
@@ -165,14 +167,14 @@ jobs:
--draft \ --draft \
--prerelease \ --prerelease \
--title "$RELEASE_VERSION" \ --title "$RELEASE_VERSION" \
--notes-file release/prerelease-notes.md --notes-file packaging/release-notes/prerelease-notes.md
else else
gh release create "$RELEASE_VERSION" \ gh release create "$RELEASE_VERSION" \
--draft \ --draft \
--latest=false \ --latest=false \
--prerelease \ --prerelease \
--title "$RELEASE_VERSION" \ --title "$RELEASE_VERSION" \
--notes-file release/prerelease-notes.md --notes-file packaging/release-notes/prerelease-notes.md
fi fi
for asset in "${artifacts[@]}"; do for asset in "${artifacts[@]}"; do
@@ -183,4 +185,4 @@ jobs:
--draft=false \ --draft=false \
--prerelease \ --prerelease \
--title "$RELEASE_VERSION" \ --title "$RELEASE_VERSION" \
--notes-file release/prerelease-notes.md --notes-file packaging/release-notes/prerelease-notes.md
+21 -19
View File
@@ -45,19 +45,19 @@ jobs:
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: appimage name: appimage
path: release path: dist/release
- name: Download macOS artifacts - name: Download macOS artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: macos name: macos
path: release path: dist/release
- name: Download Windows artifacts - name: Download Windows artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: windows name: windows
path: release path: dist/release
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
@@ -91,13 +91,15 @@ jobs:
- name: Package optional assets bundle - name: Package optional assets bundle
run: | run: |
tar -czf "release/subminer-assets.tar.gz" \ # Keep the archive layout stable for the AUR PKGBUILD.
tar -czf "dist/release/subminer-assets.tar.gz" \
--transform 's,^packaging/bun/licenses,resources/bun/licenses,' \
config.example.jsonc \ config.example.jsonc \
plugin/subminer \ plugin/subminer \
plugin/subminer.conf \ plugin/subminer.conf \
assets/themes/subminer.rasi \ assets/themes/subminer.rasi \
assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer \ assets/thumbnailers/subminer-ffmpegthumbnailer.thumbnailer \
resources/bun/licenses packaging/bun/licenses
- name: Package Bun corresponding source - name: Package Bun corresponding source
run: bun scripts/package-bun-source.mjs run: bun scripts/package-bun-source.mjs
@@ -105,16 +107,16 @@ jobs:
- name: Generate checksums - name: Generate checksums
run: | run: |
shopt -s nullglob shopt -s nullglob
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd) files=(dist/release/*.AppImage dist/release/*.dmg dist/release/*.exe dist/release/*.zip dist/release/*.tar.gz dist/release/latest*.yml dist/release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
if [ "${#files[@]}" -eq 0 ]; then if [ "${#files[@]}" -eq 0 ]; then
echo "No release artifacts found for checksum generation." echo "No release artifacts found for checksum generation."
exit 1 exit 1
fi fi
: > release/SHA256SUMS.txt : > dist/release/SHA256SUMS.txt
for file in "${files[@]}"; do for file in "${files[@]}"; do
printf '%s %s\n' \ printf '%s %s\n' \
"$(sha256sum "$file" | awk '{print $1}')" \ "$(sha256sum "$file" | awk '{print $1}')" \
"${file##*/}" >> release/SHA256SUMS.txt "${file##*/}" >> dist/release/SHA256SUMS.txt
done done
- name: Get version from tag - name: Get version from tag
@@ -152,24 +154,24 @@ jobs:
gh release edit "$RELEASE_VERSION" \ gh release edit "$RELEASE_VERSION" \
--draft=false \ --draft=false \
--title "$RELEASE_VERSION" \ --title "$RELEASE_VERSION" \
--notes-file release/release-notes.md --notes-file packaging/release-notes/release-notes.md
else else
gh release create "$RELEASE_VERSION" \ gh release create "$RELEASE_VERSION" \
--title "$RELEASE_VERSION" \ --title "$RELEASE_VERSION" \
--notes-file release/release-notes.md --notes-file packaging/release-notes/release-notes.md
fi fi
shopt -s nullglob shopt -s nullglob
artifacts=( artifacts=(
release/*.AppImage dist/release/*.AppImage
release/*.dmg dist/release/*.dmg
release/*.exe dist/release/*.exe
release/*.zip dist/release/*.zip
release/*.tar.gz dist/release/*.tar.gz
release/*.tar.gz.sha256 dist/release/*.tar.gz.sha256
release/latest*.yml dist/release/latest*.yml
release/*.blockmap dist/release/*.blockmap
release/SHA256SUMS.txt dist/release/SHA256SUMS.txt
dist/launcher/subminer dist/launcher/subminer
dist/launcher/subminer.cmd dist/launcher/subminer.cmd
) )
+1 -7
View File
@@ -8,11 +8,6 @@ node_modules/
# Electron build output # Electron build output
out/ out/
dist/ dist/
release/*
!release/
!release/release-notes.md
!release/prerelease-notes.md
build/yomitan/
coverage/ coverage/
# Launcher build artifact (produced by make build-launcher) # Launcher build artifact (produced by make build-launcher)
@@ -20,8 +15,6 @@ coverage/
/main-entry.js /main-entry.js
/main-entry.js.map /main-entry.js.map
/build/hachidori/
# Logs # Logs
*.log *.log
npm-debug.log* npm-debug.log*
@@ -43,6 +36,7 @@ environment.toml
.vitepress/dist/ .vitepress/dist/
docs/.vitepress/cache/ docs/.vitepress/cache/
docs/.vitepress/dist/ docs/.vitepress/dist/
# Local test media and scripts
tests/* tests/*
.worktrees/ .worktrees/
.tmp/ .tmp/
+3 -1
View File
@@ -1,7 +1,9 @@
node_modules node_modules
dist dist
release
coverage coverage
vendor vendor
*.log *.log
src/core/services/tokenizer/__fixtures__/golden/*.json src/core/services/tokenizer/__fixtures__/golden/*.json
# Reviewed release text and vendored license texts; never reformat
packaging/release-notes
packaging/bun/licenses
-5
View File
@@ -1,5 +0,0 @@
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
}
+8 -17
View File
@@ -21,11 +21,10 @@ MACOS_DATA_DIR ?= $(HOME)/Library/Application Support/SubMiner
MACOS_APP_DIR ?= $(HOME)/Applications MACOS_APP_DIR ?= $(HOME)/Applications
MACOS_APP_DEST ?= $(MACOS_APP_DIR)/SubMiner.app MACOS_APP_DEST ?= $(MACOS_APP_DIR)/SubMiner.app
# If building from source, the AppImage will typically land in release/. # If building from source, the AppImage will typically land in dist/release/.
APPIMAGE_SRC = $(firstword $(wildcard release/SubMiner-*.AppImage)) APPIMAGE_SRC = $(firstword $(wildcard dist/release/SubMiner-*.AppImage))
MACOS_APP_SRC = $(firstword $(wildcard release/*.app release/*/*.app)) MACOS_APP_SRC = $(firstword $(wildcard dist/release/*.app dist/release/*/*.app))
MACOS_ZIP_SRC = $(firstword $(wildcard release/SubMiner-*.zip)) MACOS_ZIP_SRC = $(firstword $(wildcard dist/release/SubMiner-*.zip))
PRERELEASE_NOTES := release/prerelease-notes.md
UNAME_S := $(shell uname -s 2>/dev/null || echo Unknown) UNAME_S := $(shell uname -s 2>/dev/null || echo Unknown)
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
@@ -57,7 +56,7 @@ help:
" build-linux Build Linux AppImage" \ " build-linux Build Linux AppImage" \
" build-macos Build macOS DMG/ZIP (signed if configured)" \ " build-macos Build macOS DMG/ZIP (signed if configured)" \
" build-macos-unsigned Build macOS DMG/ZIP without signing/notarization" \ " build-macos-unsigned Build macOS DMG/ZIP without signing/notarization" \
" clean Remove build artifacts (dist/, release/, AppImage, binary)" \ " clean Remove build artifacts (dist/, AppImage, binary)" \
" dev-start Build and launch local Electron app" \ " dev-start Build and launch local Electron app" \
" dev-start-macos Build and launch local Electron app with macOS tracker backend" \ " dev-start-macos Build and launch local Electron app with macOS tracker backend" \
" dev-watch Start fast watch loop (tsc + renderer + Electron dev app)" \ " dev-watch Start fast watch loop (tsc + renderer + Electron dev app)" \
@@ -165,15 +164,7 @@ build-launcher:
clean: clean:
@printf '%s\n' "[INFO] Removing build artifacts" @printf '%s\n' "[INFO] Removing build artifacts"
@if [ -f "$(PRERELEASE_NOTES)" ]; then \ @rm -rf dist
PRERELEASE_NOTES_BACKUP="$$(mktemp -t subminer-prerelease-notes.XXXXXX)" && \
cp "$(PRERELEASE_NOTES)" "$$PRERELEASE_NOTES_BACKUP" && \
rm -rf dist release && \
install -d release && \
mv "$$PRERELEASE_NOTES_BACKUP" "$(PRERELEASE_NOTES)"; \
else \
rm -rf dist release; \
fi
@rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage" @rm -f "$(BINDIR)/subminer" "$(BINDIR)/SubMiner.AppImage"
generate-config: ensure-bun generate-config: ensure-bun
@@ -229,7 +220,7 @@ install-linux: build-launcher
@if [ -n "$(APPIMAGE_SRC)" ]; then \ @if [ -n "$(APPIMAGE_SRC)" ]; then \
install -m 0755 "$(APPIMAGE_SRC)" "$(BINDIR)/SubMiner.AppImage"; \ install -m 0755 "$(APPIMAGE_SRC)" "$(BINDIR)/SubMiner.AppImage"; \
else \ else \
printf '%s\n' "[WARN] No release/SubMiner-*.AppImage found; skipping AppImage install"; \ printf '%s\n' "[WARN] No dist/release/SubMiner-*.AppImage found; skipping AppImage install"; \
printf '%s\n' " Build one with: make build"; \ printf '%s\n' " Build one with: make build"; \
fi fi
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)" " $(LINUX_DATA_DIR)/thumbnailers/$(THUMBNAILER_FILE)" @printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(LINUX_DATA_DIR)/themes/$(THEME_FILE)" " $(LINUX_DATA_DIR)/thumbnailers/$(THUMBNAILER_FILE)"
@@ -252,7 +243,7 @@ install-macos: build-launcher
ditto -x -k "$(MACOS_ZIP_SRC)" "$(MACOS_APP_DIR)"; \ ditto -x -k "$(MACOS_ZIP_SRC)" "$(MACOS_APP_DIR)"; \
printf '%s\n' "[INFO] Installed app bundle from $(MACOS_ZIP_SRC)"; \ printf '%s\n' "[INFO] Installed app bundle from $(MACOS_ZIP_SRC)"; \
else \ else \
printf '%s\n' "[WARN] No macOS app bundle or zip found in release/; skipping app install"; \ printf '%s\n' "[WARN] No macOS app bundle or zip found in dist/release/; skipping app install"; \
printf '%s\n' " Build one with: make build"; \ printf '%s\n' " Build one with: make build"; \
fi fi
@printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)" @printf '%s\n' "Installed to:" " $(BINDIR)/subminer" " $(MACOS_DATA_DIR)/themes/$(THEME_FILE)" " $(MACOS_APP_DEST)"
+2 -2
View File
@@ -44,11 +44,11 @@ How fragments turn into a release:
- The polish step treats pending fragments as the final release outcome, not prerelease history. If a feature is added and then renamed or fixed before the stable cut, ship the final feature bullet instead of separate prerelease-only breaking/fix entries. - The polish step treats pending fragments as the final release outcome, not prerelease history. If a feature is added and then renamed or fixed before the stable cut, ship the final feature bullet instead of separate prerelease-only breaking/fix entries.
- `CHANGELOG.md`, GitHub release notes, and prerelease notes all use one item per feature or area, inline by default: `- **Name**: one or two short sentences.` Items with several separate changes may use a few short nested bullets instead of a run-on sentence. Items say what changed and how to reach it; implementation details, edge cases, and step-by-step usage belong in the docs, not the release body. - `CHANGELOG.md`, GitHub release notes, and prerelease notes all use one item per feature or area, inline by default: `- **Name**: one or two short sentences.` Items with several separate changes may use a few short nested bullets instead of a run-on sentence. Items say what changed and how to reach it; implementation details, edge cases, and step-by-step usage belong in the docs, not the release body.
- `internal` fragments stay in `CHANGELOG.md` (inside a collapsed `<details>` block) but are dropped from the GitHub release notes entirely. - `internal` fragments stay in `CHANGELOG.md` (inside a collapsed `<details>` block) but are dropped from the GitHub release notes entirely.
- The polished `CHANGELOG.md` and `release/release-notes.md` are committed and reviewed before tagging — edit the Markdown by hand if Claude misses something. - The polished `CHANGELOG.md` and `packaging/release-notes/release-notes.md` are committed and reviewed before tagging — edit the Markdown by hand if Claude misses something.
Prerelease notes: Prerelease notes:
- prerelease tags like `v0.11.3-beta.1` and `v0.11.3-rc.1` reuse the current pending fragments to generate `release/prerelease-notes.md` - prerelease tags like `v0.11.3-beta.1` and `v0.11.3-rc.1` reuse the current pending fragments to generate `packaging/release-notes/prerelease-notes.md`
- from the second prerelease of a base version onward, the notes also open with a `## Changes since <previous tag>` section generated from the fragment diff against the previous beta/RC tag; keep fragment edits meaningful. Editorial-only rewording is filtered out of that section, while genuinely changed behavior and deleted fragments (reverted changes) are reported - from the second prerelease of a base version onward, the notes also open with a `## Changes since <previous tag>` section generated from the fragment diff against the previous beta/RC tag; keep fragment edits meaningful. Editorial-only rewording is filtered out of that section, while genuinely changed behavior and deleted fragments (reverted changes) are reported
- existing prerelease notes are a reviewed baseline; later prerelease runs should replace stale beta/RC wording with the current outcome instead of appending fix churn - existing prerelease notes are a reviewed baseline; later prerelease runs should replace stale beta/RC wording with the current outcome instead of appending fix churn
- prerelease note generation does not consume fragments and does not update `CHANGELOG.md` or `docs-site/changelog.md` - prerelease note generation does not consume fragments and does not update `CHANGELOG.md` or `docs-site/changelog.md`
+5
View File
@@ -0,0 +1,5 @@
type: internal
area: build
- Moved packaging inputs (electron-builder resources, Bun runtime manifests and licenses, committed release notes) under `packaging/`.
- Build output now lands in `dist/`: packaged apps in `dist/release/`, staged Yomitan and Hachidori in `dist/yomitan/` and `dist/hachidori/`.
+1 -1
View File
@@ -226,7 +226,7 @@ make deps
make build-macos make build-macos
``` ```
The `.dmg` and `.zip` land in `release/`. For an unsigned local build, run `bun run build:mac:unsigned`. The `.dmg` and `.zip` land in `dist/release/`. For an unsigned local build, run `bun run build:mac:unsigned`.
</details> </details>
+1 -1
View File
@@ -86,7 +86,7 @@ SubMiner detected that a previously non-empty Yomitan profile suddenly appears e
## "Yomitan extension not found in any search path" ## "Yomitan extension not found in any search path"
The bundled Yomitan is missing. Re-download the AppImage, or place an unpacked Yomitan extension in `~/.config/SubMiner/yomitan`. Source builds must run `bun run build` first to produce `build/yomitan`. The bundled Yomitan is missing. Re-download the AppImage, or place an unpacked Yomitan extension in `~/.config/SubMiner/yomitan`. Source builds must run `bun run build` first to produce `dist/yomitan`.
## "MeCab not found on system" ## "MeCab not found on system"
+10 -10
View File
@@ -54,10 +54,10 @@ files before signing and generating updater hashes, never from a signed app.
4. Bump `package.json` to the release version. 4. Bump `package.json` to the release version.
5. Build release metadata before tagging (this calls `claude -p` locally): 5. Build release metadata before tagging (this calls `claude -p` locally):
`bun run changelog:build --version <version> --date <yyyy-mm-dd>` `bun run changelog:build --version <version> --date <yyyy-mm-dd>`
- The polished `CHANGELOG.md` and `release/release-notes.md` are committed - The polished `CHANGELOG.md` and `packaging/release-notes/release-notes.md` are committed
before tagging. Release CI no longer auto-builds the changelog; it fails before tagging. Release CI no longer auto-builds the changelog; it fails
fast if `changes/*.md` fragments are still present on a tag-based run. fast if `changes/*.md` fragments are still present on a tag-based run.
6. Review `CHANGELOG.md` and `release/release-notes.md`. Edit by hand if Claude 6. Review `CHANGELOG.md` and `packaging/release-notes/release-notes.md`. Edit by hand if Claude
missed something — the committed Markdown is what ships. missed something — the committed Markdown is what ships.
7. Run release gate locally: 7. Run release gate locally:
`bun run changelog:check --version <version>` `bun run changelog:check --version <version>`
@@ -73,9 +73,9 @@ files before signing and generating updater hashes, never from a signed app.
`bun-v<version>-source.tar.gz` plus its `.sha256` file. The script `bun-v<version>-source.tar.gz` plus its `.sha256` file. The script
fails if Bun's CMake dependency pins, WebKit pin, source checksums, patch fails if Bun's CMake dependency pins, WebKit pin, source checksums, patch
inputs, or collected license files differ from inputs, or collected license files differ from
`build/bun-source-manifest.json`. `packaging/bun/bun-source-manifest.json`.
When validating auto-update metadata, also run the relevant platform package When validating auto-update metadata, also run the relevant platform package
build and confirm `release/` contains the generated updater metadata build and confirm `dist/release/` contains the generated updater metadata
(`latest*.yml`) and blockmaps (`*.blockmap`). (`latest*.yml`) and blockmaps (`*.blockmap`).
8. If `docs-site/` changed, also run: 8. If `docs-site/` changed, also run:
`bun run docs:test` `bun run docs:test`
@@ -103,9 +103,9 @@ files before signing and generating updater hashes, never from a signed app.
source archive. A missing source repository or license file fails the source archive. A missing source repository or license file fails the
release instead of publishing only the executable. release instead of publishing only the executable.
When validating packaged updater output, confirm the platform build writes When validating packaged updater output, confirm the platform build writes
`latest*.yml` and `*.blockmap` files under `release/`. `latest*.yml` and `*.blockmap` files under `dist/release/`.
5. Commit the prerelease prep (package.json version bump + the generated 5. Commit the prerelease prep (package.json version bump + the generated
`release/prerelease-notes.md`). CI does not regenerate notes — it uses the `packaging/release-notes/prerelease-notes.md`). CI does not regenerate notes — it uses the
committed file — so review it before committing. Rerun committed file — so review it before committing. Rerun
`bun run changelog:prerelease-notes --version <version>` for every later `bun run changelog:prerelease-notes --version <version>` for every later
beta/RC, even if no fragments changed: the notes carry a hidden beta/RC, even if no fragments changed: the notes carry a hidden
@@ -128,14 +128,14 @@ Notes:
- Supported prerelease channels are `beta` and `rc`, with versions like `0.11.3-beta.1` and `0.11.3-rc.1`. - Supported prerelease channels are `beta` and `rc`, with versions like `0.11.3-beta.1` and `0.11.3-rc.1`.
- Pass `--date` explicitly when you want the release stamped with the local cut date; otherwise the generator uses the current ISO date, which can roll over to the next UTC day late at night. - Pass `--date` explicitly when you want the release stamped with the local cut date; otherwise the generator uses the current ISO date, which can roll over to the next UTC day late at night.
- `changelog:check` now rejects tag/package version mismatches. - `changelog:check` now rejects tag/package version mismatches.
- `changelog:prerelease-notes` also rejects tag/package version mismatches and writes `release/prerelease-notes.md` without mutating tracked changelog files. When that file already exists, the generator includes it in the Claude prompt so later beta/RC notes reuse the reviewed text instead of starting over. - `changelog:prerelease-notes` also rejects tag/package version mismatches and writes `packaging/release-notes/prerelease-notes.md` without mutating tracked changelog files. When that file already exists, the generator includes it in the Claude prompt so later beta/RC notes reuse the reviewed text instead of starting over.
- From the second prerelease of a base version onward, the notes open with a `## Changes since <previous tag>` section above the cumulative `## Highlights`. The generator locates the newest preceding beta/RC tag for the same base version (semver order: all betas before all RCs), diffs `changes/*.md` between that tag and the working tree, and asks Claude to describe only the behavioral beta-to-beta differences — added fragments as new changes, modified fragments by their before/after difference (editorial-only edits are dropped), deleted fragments as removed/reverted changes. If no fragments changed (for example a packaging-only rebuild), the section states that explicitly without a Claude call. The delta section carries no separate contributor attribution; `## What's Changed` stays cumulative like `## Highlights`. - From the second prerelease of a base version onward, the notes open with a `## Changes since <previous tag>` section above the cumulative `## Highlights`. The generator locates the newest preceding beta/RC tag for the same base version (semver order: all betas before all RCs), diffs `changes/*.md` between that tag and the working tree, and asks Claude to describe only the behavioral beta-to-beta differences — added fragments as new changes, modified fragments by their before/after difference (editorial-only edits are dropped), deleted fragments as removed/reverted changes. If no fragments changed (for example a packaging-only rebuild), the section states that explicitly without a Claude call. The delta section carries no separate contributor attribution; `## What's Changed` stays cumulative like `## Highlights`.
- `changelog:check-prerelease-notes --version <version>` verifies the committed notes' `prerelease-version` marker matches the version being tagged; the prerelease workflow runs it and fails the release on stale notes. - `changelog:check-prerelease-notes --version <version>` verifies the committed notes' `prerelease-version` marker matches the version being tagged; the prerelease workflow runs it and fails the release on stale notes.
- `changelog:build` generates `CHANGELOG.md` + `release/release-notes.md` (both polished by `claude -p`) and removes the released `changes/*.md` fragments. The CHANGELOG keeps internal notes inside a `<details><summary>Internal changes</summary>` collapse; the release notes drop them entirely. - `changelog:build` generates `CHANGELOG.md` + `packaging/release-notes/release-notes.md` (both polished by `claude -p`) and removes the released `changes/*.md` fragments. The CHANGELOG keeps internal notes inside a `<details><summary>Internal changes</summary>` collapse; the release notes drop them entirely.
- `release/release-notes.md` (and `release/prerelease-notes.md`) include GitHub-style attribution after `## Highlights`: a `## What's Changed` list crediting each released fragment as `by @<author> in #<pr>`, plus a `## New Contributors` section for first-time authors. Attribution is resolved per fragment via `git log` (the commit that added the fragment) + `gh api .../commits/<sha>/pulls`, with one `gh` search per author for the first-contribution check. It needs `gh` installed and authenticated; if `gh` is unavailable or a lookup fails, the generator warns and emits notes without the attribution sections rather than failing. The CHANGELOG itself stays attribution-free. - `packaging/release-notes/release-notes.md` (and `packaging/release-notes/prerelease-notes.md`) include GitHub-style attribution after `## Highlights`: a `## What's Changed` list crediting each released fragment as `by @<author> in #<pr>`, plus a `## New Contributors` section for first-time authors. Attribution is resolved per fragment via `git log` (the commit that added the fragment) + `gh api .../commits/<sha>/pulls`, with one `gh` search per author for the first-contribution check. It needs `gh` installed and authenticated; if `gh` is unavailable or a lookup fails, the generator warns and emits notes without the attribution sections rather than failing. The CHANGELOG itself stays attribution-free.
- The release workflow no longer auto-runs `changelog:build`. If pending `changes/*.md` fragments are present on a tag-based run, CI exits with a clear `::error::` pointing at the local fix. Run `bun run changelog:build --version <version>` locally, commit the polished output, then tag. - The release workflow no longer auto-runs `changelog:build`. If pending `changes/*.md` fragments are present on a tag-based run, CI exits with a clear `::error::` pointing at the local fix. Run `bun run changelog:build --version <version>` locally, commit the polished output, then tag.
- Do not tag while `changes/*.md` fragments still exist. - Do not tag while `changes/*.md` fragments still exist.
- Prerelease tags intentionally keep `changes/*.md` fragments in place so multiple prereleases can reuse the same cumulative pending notes until the final stable cut. `make clean` preserves `release/prerelease-notes.md` while deleting generated build artifacts. - Prerelease tags intentionally keep `changes/*.md` fragments in place so multiple prereleases can reuse the same cumulative pending notes until the final stable cut.
- If you need to repair a published release body (for example, a prior version’s section was omitted), regenerate notes from `CHANGELOG.md` and re-edit the release with `gh release edit --notes-file`. - If you need to repair a published release body (for example, a prior version’s section was omitted), regenerate notes from `CHANGELOG.md` and re-edit the release with `gh release edit --notes-file`.
- Prerelease tags are handled by `.github/workflows/prerelease.yml`, which always publishes a GitHub prerelease with all current release platforms and never runs the AUR sync job. - Prerelease tags are handled by `.github/workflows/prerelease.yml`, which always publishes a GitHub prerelease with all current release platforms and never runs the AUR sync job.
- CI, stable release, and prerelease workflows call the shared `.github/workflows/quality-gate.yml`; update that reusable workflow when changing common test coverage. Its environment suite includes the Lua mpv plugin tests. - CI, stable release, and prerelease workflows call the shared `.github/workflows/quality-gate.yml`; update that reusable workflow when changing common test coverage. Its environment suite includes the Lua mpv plugin tests.
+1 -1
View File
@@ -62,7 +62,7 @@ bun run docs:build
then `bun run test:e2e` (see [Headless End-to-End Harness](#headless-end-to-end-harness)) then `bun run test:e2e` (see [Headless End-to-End Harness](#headless-end-to-end-harness))
- Build/release scripts (`scripts/**`): `bun run test:scripts` - Build/release scripts (`scripts/**`): `bun run test:scripts`
- Packaging: build the platform package, then run `bun run test:package <resources-directory>`. - Packaging: build the platform package, then run `bun run test:package <resources-directory>`.
On headless Linux: `xvfb-run -a bun run test:package release/linux-unpacked/resources`. On headless Linux: `xvfb-run -a bun run test:package dist/release/linux-unpacked/resources`.
Content checks run inside the electron-builder afterPack hook. See the Content checks run inside the electron-builder afterPack hook. See the
[release guide](../RELEASING.md#package-contents-checks) for the [release guide](../RELEASING.md#package-contents-checks) for the
installed-app verification checklist. installed-app verification checklist.
+13 -6
View File
@@ -146,7 +146,8 @@
"afterPack": "scripts/electron-builder-after-pack.cjs", "afterPack": "scripts/electron-builder-after-pack.cjs",
"icon": "assets/SubMiner-square.png", "icon": "assets/SubMiner-square.png",
"directories": { "directories": {
"output": "release" "buildResources": "packaging/electron-builder",
"output": "dist/release"
}, },
"publish": [ "publish": [
{ {
@@ -178,8 +179,8 @@
"category": "public.app-category.video", "category": "public.app-category.video",
"icon": "assets/SubMiner-square.png", "icon": "assets/SubMiner-square.png",
"hardenedRuntime": true, "hardenedRuntime": true,
"entitlements": "build/entitlements.mac.plist", "entitlements": "packaging/electron-builder/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist", "entitlementsInherit": "packaging/electron-builder/entitlements.mac.plist",
"extraResources": [ "extraResources": [
{ {
"from": "dist/scripts/get-mpv-window-macos", "from": "dist/scripts/get-mpv-window-macos",
@@ -211,7 +212,7 @@
"oneClick": false, "oneClick": false,
"perMachine": false, "perMachine": false,
"allowToChangeInstallationDirectory": true, "allowToChangeInstallationDirectory": true,
"include": "build/installer.nsh" "include": "packaging/electron-builder/installer.nsh"
}, },
"files": [ "files": [
"dist/**/*", "dist/**/*",
@@ -225,17 +226,18 @@
"!**/{test,tests,__tests__,fixture,fixtures,__fixtures__}{,/**/*}", "!**/{test,tests,__tests__,fixture,fixtures,__fixtures__}{,/**/*}",
"!dist/launcher{,/**/*}", "!dist/launcher{,/**/*}",
"!dist/scripts{,/**/*}", "!dist/scripts{,/**/*}",
"!dist/{yomitan,hachidori,release}{,/**/*}",
"!dist/{renderer,settings,syncui}/fonts{,/**/*}", "!dist/{renderer,settings,syncui}/fonts{,/**/*}",
"!node_modules/koffi/{src,vendor,doc}{,/**/*}", "!node_modules/koffi/{src,vendor,doc}{,/**/*}",
"!node_modules/@libsql/linux-x64-musl{,/**/*}" "!node_modules/@libsql/linux-x64-musl{,/**/*}"
], ],
"extraResources": [ "extraResources": [
{ {
"from": "build/yomitan", "from": "dist/yomitan",
"to": "yomitan" "to": "yomitan"
}, },
{ {
"from": "build/hachidori", "from": "dist/hachidori",
"to": "hachidori" "to": "hachidori"
}, },
{ {
@@ -277,6 +279,11 @@
} }
] ]
}, },
"prettier": {
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
},
"patchedDependencies": { "patchedDependencies": {
"@discordjs/rest@2.6.1": "patches/@discordjs%2Frest@2.6.1.patch" "@discordjs/rest@2.6.1": "patches/@discordjs%2Frest@2.6.1.patch"
} }
@@ -26,6 +26,6 @@
} }
}, },
"licenseInventoryStatus": "source-and-notices-pinned-to-binary-revision", "licenseInventoryStatus": "source-and-notices-pinned-to-binary-revision",
"sourceManifest": "build/bun-source-manifest.json", "sourceManifest": "packaging/bun/bun-source-manifest.json",
"correspondingSourceAsset": "bun-v1.3.5-source.tar.gz" "correspondingSourceAsset": "bun-v1.3.5-source.tar.gz"
} }
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -10,7 +10,7 @@ The SubMiner GitHub release containing this application also contains `bun-v1.3.
- every external source repository registered by that Bun revision's CMake build, at its exact commit - every external source repository registered by that Bun revision's CMake build, at its exact commit
- Bun's dependency patches, build scripts, lockfiles, collected third-party license files, and instructions for rebuilding Bun against a modified JavaScriptCore - Bun's dependency patches, build scripts, lockfiles, collected third-party license files, and instructions for rebuilding Bun against a modified JavaScriptCore
The machine-readable inventory lives at `SOURCE-INVENTORY.json` inside the source archive and at `build/bun-source-manifest.json` in SubMiner's source repository. The machine-readable inventory lives at `SOURCE-INVENTORY.json` inside the source archive and at `packaging/bun/bun-source-manifest.json` in SubMiner's source repository.
The archive vendors the source repositories linked through Bun's CMake build. It preserves Bun's package-manager lockfiles and lol-html's `Cargo.lock`, but it does not vendor npm packages, crates.io packages, compilers, SDKs, or other build tools. Rebuilding needs network access for those package-manager and toolchain inputs. The archive's rebuild README records the known tool versions and the remaining unpinned Rust nightly input. The archive vendors the source repositories linked through Bun's CMake build. It preserves Bun's package-manager lockfiles and lol-html's `Cargo.lock`, but it does not vendor npm packages, crates.io packages, compilers, SDKs, or other build tools. Rebuilding needs network access for those package-manager and toolchain inputs. The archive's rebuild README records the known tool versions and the remaining unpinned Rust nightly input.
@@ -1,6 +1,6 @@
# Bun 1.3.5 third-party notices # Bun 1.3.5 third-party notices
This file collects the license texts shipped by the external source repositories used by Bun 1.3.5 at the revisions in build/bun-source-manifest.json. The corresponding-source release asset also preserves license notices embedded in individual Bun and WebKit source files. Bun’s own LICENSE.md lists additional embedded libraries and JavaScript polyfills whose source and per-file notices are included in that asset. This file collects the license texts shipped by the external source repositories used by Bun 1.3.5 at the revisions in packaging/bun/bun-source-manifest.json. The corresponding-source release asset also preserves license notices embedded in individual Bun and WebKit source files. Bun’s own LICENSE.md lists additional embedded libraries and JavaScript polyfills whose source and per-file notices are included in that asset.
## BoringSSL ## BoringSSL
File renamed without changes.
File renamed without changes.
File renamed without changes.
+19 -13
View File
@@ -203,7 +203,7 @@ test('writeChangelogArtifacts ignores README, groups fragments by type, writes r
assert.match(changelog, /### Fixed\n- Polished: fixed entry\./); assert.match(changelog, /### Fixed\n- Polished: fixed entry\./);
assert.match(changelog, /## v0\.4\.0 \(2026-03-01\)\n- Existing fix\n$/); assert.match(changelog, /## v0\.4\.0 \(2026-03-01\)\n- Existing fix\n$/);
const releaseNotes = project.read('release/release-notes.md'); const releaseNotes = project.read('packaging/release-notes/release-notes.md');
assert.match(releaseNotes, /## Highlights\n### Added\n- Polished: added entry\./); assert.match(releaseNotes, /## Highlights\n### Added\n- Polished: added entry\./);
assert.match(releaseNotes, /### Fixed\n- Polished: fixed entry\./); assert.match(releaseNotes, /### Fixed\n- Polished: fixed entry\./);
assert.match(releaseNotes, /## Installation\n\nSee the README and docs\/installation guide/); assert.match(releaseNotes, /## Installation\n\nSee the README and docs\/installation guide/);
@@ -240,7 +240,7 @@ test('writeChangelogArtifacts skips changelog prepend when release section alrea
assert.equal(project.exists('changes/001.md'), false); assert.equal(project.exists('changes/001.md'), false);
assert.equal(project.read('CHANGELOG.md'), existingChangelog); assert.equal(project.read('CHANGELOG.md'), existingChangelog);
assert.match( assert.match(
project.read('release/release-notes.md'), project.read('packaging/release-notes/release-notes.md'),
/## Highlights\n### Added\n- Existing release bullet\./, /## Highlights\n### Added\n- Existing release bullet\./,
); );
}, },
@@ -268,7 +268,7 @@ test('writeStableReleaseArtifacts reuses the requested version and date for chan
}); });
assert.deepEqual(result.outputPaths, [project.at('CHANGELOG.md')]); assert.deepEqual(result.outputPaths, [project.at('CHANGELOG.md')]);
assert.equal(result.releaseNotesPath, project.at('release/release-notes.md')); assert.equal(result.releaseNotesPath, project.at('packaging/release-notes/release-notes.md'));
assert.equal(result.docsChangelogPath, project.at('docs-site/changelog.md')); assert.equal(result.docsChangelogPath, project.at('docs-site/changelog.md'));
assert.match(project.read('CHANGELOG.md'), /## v0\.4\.1 \(2026-03-07\)/); assert.match(project.read('CHANGELOG.md'), /## v0\.4\.1 \(2026-03-07\)/);
@@ -471,7 +471,7 @@ test('writePrereleaseNotesForVersion writes cumulative beta notes without mutati
deps: { runClaude: stub.runClaude, listPrereleaseTags: () => [] }, deps: { runClaude: stub.runClaude, listPrereleaseTags: () => [] },
}); });
assert.equal(outputPath, project.at('release/prerelease-notes.md')); assert.equal(outputPath, project.at('packaging/release-notes/prerelease-notes.md'));
assert.equal( assert.equal(
project.read('CHANGELOG.md'), project.read('CHANGELOG.md'),
existingChangelog, existingChangelog,
@@ -527,7 +527,7 @@ test('writePrereleaseNotesForVersion reuses existing prerelease notes when addin
'prerelease-reuse-existing-notes', 'prerelease-reuse-existing-notes',
{ {
'package.json': packageJson('0.11.3-beta.2'), 'package.json': packageJson('0.11.3-beta.2'),
'release/prerelease-notes.md': existingNotes, 'packaging/release-notes/prerelease-notes.md': existingNotes,
'changes/001.md': fragment('fixed', 'launcher', '- Fixed launcher prerelease packaging.'), 'changes/001.md': fragment('fixed', 'launcher', '- Fixed launcher prerelease packaging.'),
}, },
(project) => { (project) => {
@@ -574,7 +574,7 @@ test('writePrereleaseNotesForVersion ignores unmarked prerelease notes from an o
'prerelease-ignore-unmarked-old-notes', 'prerelease-ignore-unmarked-old-notes',
{ {
'package.json': packageJson('0.17.0-beta.1'), 'package.json': packageJson('0.17.0-beta.1'),
'release/prerelease-notes.md': existingNotes, 'packaging/release-notes/prerelease-notes.md': existingNotes,
'changes/001.md': fragment( 'changes/001.md': fragment(
'changed', 'changed',
'overlay', 'overlay',
@@ -785,7 +785,7 @@ test('writeChangelogArtifacts filters internal fragments from the release-notes
'release-notes mode drops internal fragments', 'release-notes mode drops internal fragments',
); );
const releaseNotes = project.read('release/release-notes.md'); const releaseNotes = project.read('packaging/release-notes/release-notes.md');
assert.doesNotMatch(releaseNotes, /<details>/); assert.doesNotMatch(releaseNotes, /<details>/);
assert.doesNotMatch(releaseNotes, /### Internal/); assert.doesNotMatch(releaseNotes, /### Internal/);
assert.match( assert.match(
@@ -841,7 +841,7 @@ test('writeChangelogArtifacts appends contributor attribution and a new-contribu
project.at('changes/002.md'), project.at('changes/002.md'),
]); ]);
const releaseNotes = project.read('release/release-notes.md'); const releaseNotes = project.read('packaging/release-notes/release-notes.md');
assert.match(releaseNotes, /## What's Changed\n\n/); assert.match(releaseNotes, /## What's Changed\n\n/);
assert.match(releaseNotes, /- feat\(overlay\): add a feature by @ksyasuda in #110\n/); assert.match(releaseNotes, /- feat\(overlay\): add a feature by @ksyasuda in #110\n/);
assert.match(releaseNotes, /- fix\(jellyfin\): restart remote session by @bee-san in #112\n/); assert.match(releaseNotes, /- fix\(jellyfin\): restart remote session by @bee-san in #112\n/);
@@ -904,7 +904,10 @@ test('writeChangelogArtifacts skips contributor attribution in GitHub Actions wi
}); });
assert.deepEqual(warnings, []); assert.deepEqual(warnings, []);
assert.doesNotMatch(project.read('release/release-notes.md'), /## What's Changed/); assert.doesNotMatch(
project.read('packaging/release-notes/release-notes.md'),
/## What's Changed/,
);
} finally { } finally {
console.warn = originalWarn; console.warn = originalWarn;
for (const key of envKeys) { for (const key of envKeys) {
@@ -990,7 +993,10 @@ test('writeReleaseNotesForVersion preserves committed contributor attribution be
await withProject( await withProject(
'release-notes-preserve-attribution', 'release-notes-preserve-attribution',
{ 'CHANGELOG.md': existingChangelog, 'release/release-notes.md': committedReleaseNotes }, {
'CHANGELOG.md': existingChangelog,
'packaging/release-notes/release-notes.md': committedReleaseNotes,
},
(project) => { (project) => {
const outputPath = writeReleaseNotesForVersion({ const outputPath = writeReleaseNotesForVersion({
cwd: project.projectRoot, cwd: project.projectRoot,
@@ -1055,7 +1061,7 @@ test('writeChangelogArtifacts strips <details> blocks from release notes when re
'no Claude calls should fire when the section already exists', 'no Claude calls should fire when the section already exists',
); );
const releaseNotes = project.read('release/release-notes.md'); const releaseNotes = project.read('packaging/release-notes/release-notes.md');
assert.match(releaseNotes, /## Highlights\n### Added\n- Polished: previously committed\./); assert.match(releaseNotes, /## Highlights\n### Added\n- Polished: previously committed\./);
assert.doesNotMatch(releaseNotes, /<details>/); assert.doesNotMatch(releaseNotes, /<details>/);
assert.doesNotMatch(releaseNotes, /### Internal/); assert.doesNotMatch(releaseNotes, /### Internal/);
@@ -1237,7 +1243,7 @@ test('writePrereleaseNotesForVersion strips the stale delta section from the reu
'prerelease-reuse-strips-delta', 'prerelease-reuse-strips-delta',
{ {
'package.json': packageJson('0.12.0-beta.3'), 'package.json': packageJson('0.12.0-beta.3'),
'release/prerelease-notes.md': existingNotes, 'packaging/release-notes/prerelease-notes.md': existingNotes,
'changes/001.md': fragment('added', 'overlay', '- Added overlay coverage.'), 'changes/001.md': fragment('added', 'overlay', '- Added overlay coverage.'),
}, },
(project) => { (project) => {
@@ -1271,7 +1277,7 @@ test('verifyPrereleaseNotesMatchVersion accepts matching notes and rejects stale
(project) => { (project) => {
const verify = (version = '0.12.0-beta.2') => const verify = (version = '0.12.0-beta.2') =>
verifyPrereleaseNotesMatchVersion({ cwd: project.projectRoot, version }); verifyPrereleaseNotesMatchVersion({ cwd: project.projectRoot, version });
const notesPath = 'release/prerelease-notes.md'; const notesPath = 'packaging/release-notes/prerelease-notes.md';
assert.throws(() => verify(), /Missing .*prerelease-notes\.md/); assert.throws(() => verify(), /Missing .*prerelease-notes\.md/);
+4 -4
View File
@@ -68,8 +68,8 @@ type PullRequestChangelogOptions = {
changedLabels?: string[]; changedLabels?: string[];
}; };
const RELEASE_NOTES_PATH = path.join('release', 'release-notes.md'); const RELEASE_NOTES_PATH = path.join('packaging', 'release-notes', 'release-notes.md');
const PRERELEASE_NOTES_PATH = path.join('release', 'prerelease-notes.md'); const PRERELEASE_NOTES_PATH = path.join('packaging', 'release-notes', 'prerelease-notes.md');
const CHANGELOG_HEADER = '# Changelog'; const CHANGELOG_HEADER = '# Changelog';
const CHANGE_TYPES: FragmentType[] = ['added', 'changed', 'fixed', 'docs', 'internal']; const CHANGE_TYPES: FragmentType[] = ['added', 'changed', 'fixed', 'docs', 'internal'];
const SKIP_CHANGELOG_LABEL = 'skip-changelog'; const SKIP_CHANGELOG_LABEL = 'skip-changelog';
@@ -1144,7 +1144,7 @@ function isFragmentPath(candidate: string): boolean {
function isIgnoredPullRequestPath(candidate: string): boolean { function isIgnoredPullRequestPath(candidate: string): boolean {
return ( return (
candidate === 'CHANGELOG.md' || candidate === 'CHANGELOG.md' ||
candidate === 'release/release-notes.md' || candidate === 'packaging/release-notes/release-notes.md' ||
candidate === 'AGENTS.md' || candidate === 'AGENTS.md' ||
candidate === 'README.md' || candidate === 'README.md' ||
candidate.startsWith('changes/') || candidate.startsWith('changes/') ||
@@ -1405,7 +1405,7 @@ export function verifyPrereleaseNotesMatchVersion(options?: ChangelogOptions): v
const markerVersion = extractPrereleaseVersionMarker(readFileSync(prereleaseNotesPath, 'utf8')); const markerVersion = extractPrereleaseVersionMarker(readFileSync(prereleaseNotesPath, 'utf8'));
if (markerVersion !== version) { if (markerVersion !== version) {
throw new Error( throw new Error(
`release/prerelease-notes.md was generated for ${markerVersion ?? 'an unknown version (missing or legacy prerelease-version marker)'} but this release is ${version}. Rerun 'bun run changelog:prerelease-notes --version ${version}' and commit the result.`, `packaging/release-notes/prerelease-notes.md was generated for ${markerVersion ?? 'an unknown version (missing or legacy prerelease-version marker)'} but this release is ${version}. Rerun 'bun run changelog:prerelease-notes --version ${version}' and commit the result.`,
); );
} }
} }
+1 -1
View File
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const source = path.join(repoRoot, 'vendor', 'hachidori'); const source = path.join(repoRoot, 'vendor', 'hachidori');
const output = path.join(repoRoot, 'build', 'hachidori'); const output = path.join(repoRoot, 'dist', 'hachidori');
const provenance = JSON.parse(fs.readFileSync(path.join(source, 'SOURCE.json'), 'utf8')); const provenance = JSON.parse(fs.readFileSync(path.join(source, 'SOURCE.json'), 'utf8'));
// Upstream commits the engine binaries. Verify the pinned bytes before staging // Upstream commits the engine binaries. Verify the pinned bytes before staging
+3 -3
View File
@@ -19,7 +19,7 @@ test('Hachidori staging configures Electron without changing the fork source', a
files.map((file) => readFileSync(source(file), 'utf8')), files.map((file) => readFileSync(source(file), 'utf8')),
before, before,
); );
const staged = await import(new URL('../build/hachidori/overlay-mode.js', import.meta.url).href); const staged = await import(new URL('../dist/hachidori/overlay-mode.js', import.meta.url).href);
assert.equal(staged.OVERLAY_MODE, true); assert.equal(staged.OVERLAY_MODE, true);
assert.equal(staged.HOST_CAPABILITIES.customJavaScript, false); assert.equal(staged.HOST_CAPABILITIES.customJavaScript, false);
const original = await import(source('overlay-mode.js').href); const original = await import(source('overlay-mode.js').href);
@@ -28,13 +28,13 @@ test('Hachidori staging configures Electron without changing the fork source', a
// SubMiner mines media from mpv, not the overlay viewport, so the lookup // SubMiner mines media from mpv, not the overlay viewport, so the lookup
// highlight stays on for a first install. // highlight stays on for a first install.
const stagedSetup = await import( const stagedSetup = await import(
new URL('../build/hachidori/setup-state.js', import.meta.url).href new URL('../dist/hachidori/setup-state.js', import.meta.url).href
); );
assert.equal(stagedSetup.OVERLAY_MODE_OPTIONS.sourceHighlightEnabled, true); assert.equal(stagedSetup.OVERLAY_MODE_OPTIONS.sourceHighlightEnabled, true);
const originalSetup = await import(source('setup-state.js').href); const originalSetup = await import(source('setup-state.js').href);
assert.equal(originalSetup.OVERLAY_MODE_OPTIONS.sourceHighlightEnabled, false); assert.equal(originalSetup.OVERLAY_MODE_OPTIONS.sourceHighlightEnabled, false);
const manifest = JSON.parse( const manifest = JSON.parse(
readFileSync(new URL('../build/hachidori/manifest.json', import.meta.url), 'utf8'), readFileSync(new URL('../dist/hachidori/manifest.json', import.meta.url), 'utf8'),
); );
const originalManifest = JSON.parse(readFileSync(source('manifest.json'), 'utf8')); const originalManifest = JSON.parse(readFileSync(source('manifest.json'), 'utf8'));
assert.deepEqual(manifest, { assert.deepEqual(manifest, {
+1 -1
View File
@@ -10,7 +10,7 @@ const repoRoot = path.resolve(dirname, '..');
const submoduleDir = path.join(repoRoot, 'vendor', 'subminer-yomitan'); const submoduleDir = path.join(repoRoot, 'vendor', 'subminer-yomitan');
const submodulePackagePath = path.join(submoduleDir, 'package.json'); const submodulePackagePath = path.join(submoduleDir, 'package.json');
const submodulePackageLockPath = path.join(submoduleDir, 'package-lock.json'); const submodulePackageLockPath = path.join(submoduleDir, 'package-lock.json');
const buildOutputDir = path.join(repoRoot, 'build', 'yomitan'); const buildOutputDir = path.join(repoRoot, 'dist', 'yomitan');
const stampPath = path.join(buildOutputDir, '.subminer-build.json'); const stampPath = path.join(buildOutputDir, '.subminer-build.json');
const zipPath = path.join(submoduleDir, 'builds', 'yomitan-chrome.zip'); const zipPath = path.join(submoduleDir, 'builds', 'yomitan-chrome.zip');
const bunCommand = process.versions.bun ? process.execPath : 'bun'; const bunCommand = process.versions.bun ? process.execPath : 'bun';
+1 -1
View File
@@ -48,7 +48,7 @@ app
.then(async () => { .then(async () => {
const targetSession = session.fromPartition('persist:hachidori-check'); const targetSession = session.fromPartition('persist:hachidori-check');
const extension = await targetSession.extensions.loadExtension( const extension = await targetSession.extensions.loadExtension(
path.join(root, 'build/hachidori'), path.join(root, 'dist/hachidori'),
{ allowFileAccess: true }, { allowFileAccess: true },
); );
// Keep a host window alive while the dictionary importer opens/closes its // Keep a host window alive while the dictionary importer opens/closes its
+4 -1
View File
@@ -18,7 +18,7 @@ test('platform packaging preserves the runtime allowlist after builder normalize
'/tmp/subminer-filter-output', '/tmp/subminer-filter-output',
{ {
defaultSrc: root, defaultSrc: root,
globalOutDir: path.join(root, 'release'), globalOutDir: path.join(root, 'dist', 'release'),
customBuildOptions: { files: config.build[platform].files }, customBuildOptions: { files: config.build[platform].files },
macroExpander: (value) => value.replaceAll('${arch}', 'x64'), macroExpander: (value) => value.replaceAll('${arch}', 'x64'),
}, },
@@ -51,6 +51,9 @@ test('platform packaging preserves the runtime allowlist after builder normalize
'stats/dist/nested/fixtures/data.json', 'stats/dist/nested/fixtures/data.json',
'vendor/texthooker-ui/docs/nested/component.tsx', 'vendor/texthooker-ui/docs/nested/component.tsx',
'dist/launcher/subminer', 'dist/launcher/subminer',
'dist/yomitan/manifest.json',
'dist/hachidori/manifest.json',
'dist/release/SubMiner-1.0.0.AppImage',
'dist/settings/fonts/MPLUS1[wght].ttf', 'dist/settings/fonts/MPLUS1[wght].ttf',
'vendor/subminer-yomitan/ext/manifest.json', 'vendor/subminer-yomitan/ext/manifest.json',
]) { ]) {
+9 -3
View File
@@ -10,14 +10,20 @@ import { Readable } from 'node:stream';
const scriptDir = path.dirname(fileURLToPath(import.meta.url)); const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, '..'); const repoRoot = path.resolve(scriptDir, '..');
export const DEFAULT_MANIFEST_PATH = path.join(repoRoot, 'build', 'bun-source-manifest.json'); export const DEFAULT_MANIFEST_PATH = path.join(
repoRoot,
'packaging',
'bun',
'bun-source-manifest.json',
);
export const DEFAULT_RUNTIME_MANIFEST_PATH = path.join( export const DEFAULT_RUNTIME_MANIFEST_PATH = path.join(
repoRoot, repoRoot,
'build', 'packaging',
'bun',
'bun-runtime-manifest.json', 'bun-runtime-manifest.json',
); );
export const DEFAULT_PACKAGE_JSON_PATH = path.join(repoRoot, 'package.json'); export const DEFAULT_PACKAGE_JSON_PATH = path.join(repoRoot, 'package.json');
export const DEFAULT_OUTPUT_DIR = path.join(repoRoot, 'release'); export const DEFAULT_OUTPUT_DIR = path.join(repoRoot, 'dist', 'release');
export const DEFAULT_CACHE_DIR = path.join(repoRoot, '.tmp', 'bun-corresponding-source'); export const DEFAULT_CACHE_DIR = path.join(repoRoot, '.tmp', 'bun-corresponding-source');
function isRecord(value) { function isRecord(value) {
+21 -2
View File
@@ -4,6 +4,9 @@ import fs from 'node:fs/promises';
import os from 'node:os'; import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
import { import {
DEFAULT_MANIFEST_PATH,
DEFAULT_PACKAGE_JSON_PATH,
DEFAULT_RUNTIME_MANIFEST_PATH,
downloadArchive, downloadArchive,
parseRegisteredRepositories, parseRegisteredRepositories,
parseSourceManifest, parseSourceManifest,
@@ -67,10 +70,26 @@ describe('Bun corresponding-source manifest', () => {
}); });
}); });
test('default manifest paths resolve to aligned checked-in manifests', async () => {
const [manifestText, runtimeManifestText, packageJsonText] = await Promise.all([
fs.readFile(DEFAULT_MANIFEST_PATH, 'utf8'),
fs.readFile(DEFAULT_RUNTIME_MANIFEST_PATH, 'utf8'),
fs.readFile(DEFAULT_PACKAGE_JSON_PATH, 'utf8'),
]);
expect(() =>
validateRuntimeAlignment(
parseSourceManifest(JSON.parse(manifestText)),
JSON.parse(packageJsonText),
JSON.parse(runtimeManifestText),
),
).not.toThrow();
});
test('rejects stale runtime or package-manager pins', async () => { test('rejects stale runtime or package-manager pins', async () => {
const manifest = parseSourceManifest( const manifest = parseSourceManifest(
JSON.parse( JSON.parse(
await fs.readFile(path.join(projectRoot, 'build/bun-source-manifest.json'), 'utf8'), await fs.readFile(path.join(projectRoot, 'packaging/bun/bun-source-manifest.json'), 'utf8'),
), ),
); );
const runtimeManifest = { const runtimeManifest = {
@@ -100,7 +119,7 @@ describe('Bun corresponding-source manifest', () => {
test('rejects drift in CMake dependency and WebKit pins', async () => { test('rejects drift in CMake dependency and WebKit pins', async () => {
const manifest = parseSourceManifest( const manifest = parseSourceManifest(
JSON.parse( JSON.parse(
await fs.readFile(path.join(projectRoot, 'build/bun-source-manifest.json'), 'utf8'), await fs.readFile(path.join(projectRoot, 'packaging/bun/bun-source-manifest.json'), 'utf8'),
), ),
); );
const registrations = manifest.sources const registrations = manifest.sources
+1 -1
View File
@@ -6,7 +6,7 @@ cat <<'EOF'
patch-yomitan.sh is retired. patch-yomitan.sh is retired.
SubMiner now uses the forked source submodule at vendor/subminer-yomitan and builds the SubMiner now uses the forked source submodule at vendor/subminer-yomitan and builds the
Chromium extension artifact into build/yomitan. Chromium extension artifact into dist/yomitan.
Use: Use:
git submodule update --init --recursive git submodule update --init --recursive
+2 -2
View File
@@ -9,9 +9,9 @@ paths=(
"tsconfig.json" "tsconfig.json"
"tsconfig.renderer.json" "tsconfig.renderer.json"
"tsconfig.typecheck.json" "tsconfig.typecheck.json"
".prettierrc.json"
".github" ".github"
"build" "packaging/bun/bun-runtime-manifest.json"
"packaging/bun/bun-source-manifest.json"
"e2e" "e2e"
"launcher" "launcher"
"scripts" "scripts"
+7 -2
View File
@@ -14,9 +14,14 @@ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, '..'); const repoRoot = path.resolve(scriptDir, '..');
export const DEFAULT_PACKAGE_JSON_PATH = path.join(repoRoot, 'package.json'); export const DEFAULT_PACKAGE_JSON_PATH = path.join(repoRoot, 'package.json');
export const DEFAULT_MANIFEST_PATH = path.join(repoRoot, 'build', 'bun-runtime-manifest.json'); export const DEFAULT_MANIFEST_PATH = path.join(
repoRoot,
'packaging',
'bun',
'bun-runtime-manifest.json',
);
export const DEFAULT_CACHE_DIR = path.join(repoRoot, '.tmp', 'bun-runtime'); export const DEFAULT_CACHE_DIR = path.join(repoRoot, '.tmp', 'bun-runtime');
export const DEFAULT_LICENSES_SOURCE_DIR = path.join(repoRoot, 'resources', 'bun', 'licenses'); export const DEFAULT_LICENSES_SOURCE_DIR = path.join(repoRoot, 'packaging', 'bun', 'licenses');
export const STAGED_METADATA_FILE = 'metadata.json'; export const STAGED_METADATA_FILE = 'metadata.json';
export const REQUIRED_LICENSE_FILES = [ export const REQUIRED_LICENSE_FILES = [
'Bun-LICENSE.md', 'Bun-LICENSE.md',
+2 -2
View File
@@ -35,7 +35,7 @@ function runtimeConfig() {
'win32-x64': { file: 'bun-windows-x64-baseline.zip', sha256: '5'.repeat(64) }, 'win32-x64': { file: 'bun-windows-x64-baseline.zip', sha256: '5'.repeat(64) },
}, },
licenseInventoryStatus: 'complete-for-bun-1.3.5-declared-linked-libraries', licenseInventoryStatus: 'complete-for-bun-1.3.5-declared-linked-libraries',
sourceManifest: 'build/bun-source-manifest.json', sourceManifest: 'packaging/bun/bun-source-manifest.json',
correspondingSourceAsset: 'bun-v1.3.5-source.tar.gz', correspondingSourceAsset: 'bun-v1.3.5-source.tar.gz',
}; };
} }
@@ -96,7 +96,7 @@ test('manifest version must match the exact packageManager Bun pin', () => {
releaseTagCommit: 'fa5a5bbe556a4bda5bde77b4013aa6c3bb4ec9ab', releaseTagCommit: 'fa5a5bbe556a4bda5bde77b4013aa6c3bb4ec9ab',
artifacts: {}, artifacts: {},
licenseInventoryStatus: 'partial', licenseInventoryStatus: 'partial',
sourceManifest: 'build/bun-source-manifest.json', sourceManifest: 'packaging/bun/bun-source-manifest.json',
correspondingSourceAsset: 'bun-v1.3.5-source.tar.gz', correspondingSourceAsset: 'bun-v1.3.5-source.tar.gz',
}, },
'1.3.5', '1.3.5',
@@ -11,9 +11,9 @@ test('Hachidori resolves development and packaged artifacts without falling back
assert.equal( assert.equal(
resolveHachidoriExtensionPath({ resolveHachidoriExtensionPath({
...options, ...options,
exists: (p) => p === '/app/build/hachidori/manifest.json', exists: (p) => p === '/app/dist/hachidori/manifest.json',
}), }),
'/app/build/hachidori', '/app/dist/hachidori',
); );
assert.equal( assert.equal(
resolveHachidoriExtensionPath({ resolveHachidoriExtensionPath({
+1 -1
View File
@@ -15,7 +15,7 @@ export function resolveHachidoriExtensionPath(options: {
exists?: (candidate: string) => boolean; exists?: (candidate: string) => boolean;
}): string { }): string {
const candidates = [ const candidates = [
path.resolve(options.moduleDir, '../../../build/hachidori'), path.resolve(options.moduleDir, '../../../dist/hachidori'),
path.join(options.resourcesPath, 'hachidori'), path.join(options.resourcesPath, 'hachidori'),
]; ];
const found = candidates.find((candidate) => const found = candidates.find((candidate) =>
@@ -20,7 +20,7 @@ test('getYomitanExtensionSearchPaths prioritizes generated build output before p
}); });
assert.deepEqual(searchPaths, [ assert.deepEqual(searchPaths, [
path.join(repoRoot, 'build', 'yomitan'), path.join(repoRoot, 'dist', 'yomitan'),
path.join(resourcesPath, 'yomitan'), path.join(resourcesPath, 'yomitan'),
'/usr/share/SubMiner/yomitan', '/usr/share/SubMiner/yomitan',
path.join(userDataPath, 'yomitan'), path.join(userDataPath, 'yomitan'),
@@ -29,19 +29,19 @@ test('getYomitanExtensionSearchPaths prioritizes generated build output before p
test('resolveExistingYomitanExtensionPath returns first manifest-backed candidate', () => { test('resolveExistingYomitanExtensionPath returns first manifest-backed candidate', () => {
const existing = new Set<string>([ const existing = new Set<string>([
path.join('/repo', 'build', 'yomitan', 'manifest.json'), path.join('/repo', 'dist', 'yomitan', 'manifest.json'),
path.join('/repo', 'vendor', 'subminer-yomitan', 'ext', 'manifest.json'), path.join('/repo', 'vendor', 'subminer-yomitan', 'ext', 'manifest.json'),
]); ]);
const resolved = resolveExistingYomitanExtensionPath( const resolved = resolveExistingYomitanExtensionPath(
[ [
path.join('/repo', 'build', 'yomitan'), path.join('/repo', 'dist', 'yomitan'),
path.join('/repo', 'vendor', 'subminer-yomitan', 'ext'), path.join('/repo', 'vendor', 'subminer-yomitan', 'ext'),
], ],
(candidate) => existing.has(candidate), (candidate) => existing.has(candidate),
); );
assert.equal(resolved, path.join('/repo', 'build', 'yomitan')); assert.equal(resolved, path.join('/repo', 'dist', 'yomitan'));
}); });
test('resolveExistingYomitanExtensionPath ignores source tree without built manifest', () => { test('resolveExistingYomitanExtensionPath ignores source tree without built manifest', () => {
+2 -4
View File
@@ -22,12 +22,10 @@ export function getYomitanExtensionSearchPaths(
const searchPaths: string[] = []; const searchPaths: string[] = [];
pushUnique(searchPaths, options.explicitPath ? path.resolve(options.explicitPath) : null); pushUnique(searchPaths, options.explicitPath ? path.resolve(options.explicitPath) : null);
pushUnique(searchPaths, options.cwd ? path.resolve(options.cwd, 'build', 'yomitan') : null); pushUnique(searchPaths, options.cwd ? path.resolve(options.cwd, 'dist', 'yomitan') : null);
pushUnique( pushUnique(
searchPaths, searchPaths,
options.moduleDir options.moduleDir ? path.resolve(options.moduleDir, '..', '..', '..', 'dist', 'yomitan') : null,
? path.resolve(options.moduleDir, '..', '..', '..', 'build', 'yomitan')
: null,
); );
pushUnique( pushUnique(
searchPaths, searchPaths,