From d786bbf78393fa1dbb9f23d4de9e4774dc6f1169 Mon Sep 17 00:00:00 2001 From: qiyinxi Date: Sat, 7 Nov 2026 22:04:20 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=E5=8F=91=E7=89=88=20workflow=20=E5=92=8C?= =?UTF-8?q?=20v0.1.0=20=E7=9A=84=E8=AF=B4=E6=98=8E=E3=80=81=E5=93=88?= =?UTF-8?q?=E5=B8=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - release.yml:照 ModAVDManager 的做法。打 vX.Y.Z tag 触发,只在 ModAVD/ModAVDExtras 上跑; 核对 tag 在 main 上、和 CMakeLists.txt 的版本一致;在 windows-2025 上照 ci.yml 编、跑测试, 两个 DLL 逐个和 release/<版本>.SHA256SUMS.txt 核对;草稿 job 才有 contents: write, 下载回来再核一遍,同一个 tag 已有 Release(含草稿)就停。 - ci.yml:Windows 最后一步按 SHA256SUMS 的格式打印两个 DLL 的哈希,取哈希时不用再猜哪行是哪个。 - release/0.1.0.SHA256SUMS.txt:取自 main(566f823)上 CI 编出的值,PR #4 的 CI 编出的也相同。 Refs ModAVD/ModAVDExtras#1 --- .github/workflows/ci.yml | 3 +- .github/workflows/release.yml | 119 ++++++++++++++++++++++++++++++++++ release/0.1.0.SHA256SUMS.txt | 2 + release/0.1.0.md | 48 ++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100644 release/0.1.0.SHA256SUMS.txt create mode 100644 release/0.1.0.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 885fafb..40635be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,8 @@ jobs: if ($LASTEXITCODE -ne 0) { throw "build failed" } ctest --test-dir build -C Release --output-on-failure if ($LASTEXITCODE -ne 0) { throw "tests failed" } - Get-FileHash build\Release\*.dll -Algorithm SHA256 | Format-Table -AutoSize + # In the format of release/.SHA256SUMS.txt. + Get-ChildItem build\Release\*.dll | Sort-Object Name | ForEach-Object { '{0} {1}' -f (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(), $_.Name } posix: strategy: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dc8a983 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,119 @@ +name: release + +on: + push: + tags: ["v*"] + +permissions: {} + +concurrency: + group: release-${{ github.ref_name }} + cancel-in-progress: false + +jobs: + build: + if: github.repository == 'ModAVD/ModAVDExtras' + runs-on: windows-2025 + timeout-minutes: 20 + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + fetch-depth: 0 + - name: verify tag, ancestry and version + shell: pwsh + env: + TAG: ${{ github.ref_name }} + SHA: ${{ github.sha }} + run: | + if ($env:TAG -notmatch '^v\d+\.\d+\.\d+$') { throw "invalid tag: $env:TAG" } + git merge-base --is-ancestor $env:SHA origin/main + if ($LASTEXITCODE -ne 0) { throw 'tag is not on origin/main' } + $version = $env:TAG.Substring(1) + $want = (Select-String -LiteralPath CMakeLists.txt -Pattern 'project\(ModAVDExtras VERSION (\d+\.\d+\.\d+)').Matches.Groups[1].Value + if ($want -ne $version) { throw "CMakeLists.txt version=$want, tag=$version" } + - name: build, test and verify + shell: pwsh + env: + TAG: ${{ github.ref_name }} + run: | + # The same build as ci.yml's windows job. + cmake -S . -B build -A x64 + if ($LASTEXITCODE -ne 0) { throw 'configure failed' } + cmake --build build --config Release + if ($LASTEXITCODE -ne 0) { throw 'build failed' } + ctest --test-dir build -C Release --output-on-failure + if ($LASTEXITCODE -ne 0) { throw 'tests failed' } + $version = $env:TAG.Substring(1) + $names = @('external_renderer_ipc.dll', 'modavd_extras.dll') + $sums = @{} + foreach ($line in Get-Content "release/$version.SHA256SUMS.txt") { + if ($line -notmatch '^([0-9a-f]{64}) (.+)$') { throw "bad line in SHA256SUMS: $line" } + $sums[$Matches[2]] = $Matches[1] + } + if ((($sums.Keys | Sort-Object) -join ',') -cne ($names -join ',')) { throw "SHA256SUMS must list exactly: $($names -join ', ')" } + New-Item -ItemType Directory -Force artifacts | Out-Null + $bad = $false + foreach ($name in $names) { + $got = (Get-FileHash "build/Release/$name" -Algorithm SHA256).Hash.ToLowerInvariant() + "$got $name" + if ($got -ne $sums[$name]) { Write-Output "::error::$name hash mismatch: built $got, release/$version.SHA256SUMS.txt has $($sums[$name])"; $bad = $true } + Copy-Item "build/Release/$name" artifacts + } + if ($bad) { throw 'hash mismatch' } + Copy-Item "release/$version.SHA256SUMS.txt" artifacts/SHA256SUMS.txt + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: release-assets + path: artifacts + if-no-files-found: error + + draft: + needs: build + runs-on: windows-2025 + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + fetch-depth: 1 + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: release-assets + path: artifacts + - name: verify artifact + shell: pwsh + env: + TAG: ${{ github.ref_name }} + run: | + # Checked against the tag's own list, not only the copy that travelled with the artifact. + $version = $env:TAG.Substring(1) + $sums = Get-Content -Raw "release/$version.SHA256SUMS.txt" + if ((Get-Content -Raw artifacts/SHA256SUMS.txt) -cne $sums) { throw 'artifact SHA256SUMS.txt differs from the tag' } + $names = @('external_renderer_ipc.dll', 'modavd_extras.dll') + $have = (Get-ChildItem artifacts -File | ForEach-Object Name | Sort-Object) -join ',' + $expect = (@($names + 'SHA256SUMS.txt') | Sort-Object) -join ',' + if ($have -cne $expect) { throw "unexpected artifact files: $have" } + foreach ($name in $names) { + $want = [regex]::Match($sums, "(?m)^([0-9a-f]{64}) $([regex]::Escape($name))\r?$").Groups[1].Value + $got = (Get-FileHash "artifacts/$name" -Algorithm SHA256).Hash.ToLowerInvariant() + if (-not $want -or $got -ne $want) { throw "downloaded $name hash mismatch" } + } + - name: create draft + shell: pwsh + env: + TAG: ${{ github.ref_name }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + run: | + $version = $env:TAG.Substring(1) + # Drafts are not bound to their tag, so one tag can collect several: list every release, drafts included. + $tags = gh api "repos/$env:GH_REPO/releases?per_page=100" --paginate --jq '.[].tag_name' + if ($LASTEXITCODE -ne 0) { throw 'cannot list releases' } + if ($tags -contains $env:TAG) { throw "a release for $env:TAG already exists (draft or published)" } + gh release create $env:TAG --draft --verify-tag --title "ModAVDExtras v$version" --notes-file "release/$version.md" artifacts/modavd_extras.dll artifacts/external_renderer_ipc.dll artifacts/SHA256SUMS.txt + if ($LASTEXITCODE -ne 0) { throw 'gh release create failed' } diff --git a/release/0.1.0.SHA256SUMS.txt b/release/0.1.0.SHA256SUMS.txt new file mode 100644 index 0000000..485a965 --- /dev/null +++ b/release/0.1.0.SHA256SUMS.txt @@ -0,0 +1,2 @@ +3e55150f1cc0c702e0b9b41dd9f248e99363aaa7c07d881660143f0cd36b4c45 external_renderer_ipc.dll +84719e698c4679185eea9b194dfd33bbecadd05fb4d9bfaf9bba0398aa65f8e3 modavd_extras.dll diff --git a/release/0.1.0.md b/release/0.1.0.md new file mode 100644 index 0000000..357f561 --- /dev/null +++ b/release/0.1.0.md @@ -0,0 +1,48 @@ +# ModAVDExtras v0.1.0 + +- `modavd_extras.dll`:`84719e698c4679185eea9b194dfd33bbecadd05fb4d9bfaf9bba0398aa65f8e3` +- `external_renderer_ipc.dll`:`3e55150f1cc0c702e0b9b41dd9f248e99363aaa7c07d881660143f0cd36b4c45` + +第一个版本,只有截图,附件是 Windows x64 的两个 DLL。Linux、macOS 能从源码编,这一版不出二进制。 + +## 两个 DLL + +- `modavd_extras.dll`:ModAVD 自己的截图库,C 接口,见 `include/modavd_extras.h`(接口版本 1)。 + - 从 ModAVD 实例的截图共享内存直接取画面,不经 adb。按 adb 地址(`127.0.0.1:`、`emulator-<控制台端口>`)或控制台端口找到实例。 + - 交出 BGR、RGBA 或 BGRA,正向或倒向。新帧写到一半时重拷;共享内存没开时经模拟器控制台打开。 + - 出错返回错误码,不自己退回 adb,由调用方决定换什么办法。 + - 点击、按键、文字的函数已经声明,这一版返回 `MODAVD_E_UNSUPPORTED`。 +- `external_renderer_ipc.dll`:兼容外壳,只在 Windows 上用。 + - 导出和 MuMu 同名 DLL 一样的四个截图函数(`nemu_connect`、`nemu_disconnect`、`nemu_capture_display`、`nemu_get_display_id`),让还不认识 ModAVD 的 MAA 走「MuMu 截图增强」那条路。不导出输入函数,MAA 照旧用 MaaTouch 点击。 + - 用来替换 AUTO-MAS 里原来的伪装 DLL(`res/avdshim`):导出、实例编号的换算、返回值都一样,截出的图逐字节相同。日志有三处不同:经控制台开截图时不再记 `console <端口> webrtc start -> OK`;连接失败时多一行 `port=<端口>: shared memory unavailable`,外壳自己那行末尾多出错原因;控制台没人应答时直接返回 0,不再多等约 2 秒。 +- 两个 DLL 都静态链接 C 运行库,只依赖 `KERNEL32`、`WS2_32`。 + +## 速度 + +截一帧的耗时,p50,单位 ms(Windows,7950X3D,测试实例停在桌面,三种实现轮流各截 500 次 × 3 轮): + +| | 720p | 1080p | +|---|---|---| +| 原来的伪装 DLL(RGBA,倒向) | 0.28–0.30 | 0.68–0.70 | +| `external_renderer_ipc.dll`(同上) | 0.11–0.12 | 0.28–0.31 | +| `modavd_extras.dll`,BGR 正向 | 0.06–0.07 | 0.13 | + +## 许可证与源码 + +- LGPL-3.0-or-later。正文是 tag 下的 [LICENSE](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.0/LICENSE)(LGPL-3.0)和 [COPYING](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.0/COPYING)(GPL-3.0);转发这两个 DLL 时请一起带上。 +- 源码:。 + +## 下载与核对 + +两个 DLL 由本仓库的 release workflow 在 GitHub 的 `windows-2025` 上编出(`cmake -A x64`,`--config Release`),和 tag 下的 `release/0.1.0.SHA256SUMS.txt` 核对过才上传。请用附件 `SHA256SUMS.txt` 核对,例如在 PowerShell 里运行 `Get-FileHash .\modavd_extras.dll -Algorithm SHA256`。编译带 `/Brepro`,用同一版本的 MSVC 编能得到相同的字节。 + +## English + +The first release: screen capture only. Attached are the two Windows x64 DLLs; Linux and macOS build from source, with no binaries in this release. + +- `modavd_extras.dll` is ModAVD's own capture library with a C interface (`include/modavd_extras.h`, API version 1). It reads frames straight from a ModAVD instance's frame-sharing memory, without adb, finding the instance from an adb address (`127.0.0.1:`, `emulator-`) or a console port. It returns BGR, RGBA or BGRA, top-down or bottom-up; copies again when a new frame lands mid-copy; turns frame sharing on through the emulator console when it is off; and returns an error code instead of falling back to adb. The input functions are declared and return `MODAVD_E_UNSUPPORTED` in this release. +- `external_renderer_ipc.dll` is a Windows-only compatibility shell. It exports the four capture functions of MuMu's DLL of the same name, so MAA versions that do not know ModAVD can capture through the "MuMu screenshot enhancement" path; touch stays on MaaTouch. It replaces the impersonation DLL in AUTO-MAS (`res/avdshim`) with the same exports, index mapping and return values, and byte-identical frames. Its log differs in three places: no `console webrtc start -> OK` line; on a failed connect an extra `port=: shared memory unavailable` line and the reason at the end of the shell's own line; and no extra wait of about 2 s when the console does not answer. +- Both DLLs link the C runtime statically and depend only on `KERNEL32` and `WS2_32`. +- Capture time, p50 in ms (720p / 1080p): the old impersonation DLL 0.28–0.30 / 0.68–0.70; `external_renderer_ipc.dll` 0.11–0.12 / 0.28–0.31; `modavd_extras.dll` BGR top-down 0.06–0.07 / 0.13. +- License: LGPL-3.0-or-later; the texts are [LICENSE](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.0/LICENSE) (LGPL-3.0) and [COPYING](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.0/COPYING) (GPL-3.0) at the tag. Ship them with the DLLs. Source: . +- The DLLs are built by this repository's release workflow on GitHub's `windows-2025` (`cmake -A x64`, `--config Release`) and checked against `release/0.1.0.SHA256SUMS.txt` at the tag before upload. Check them with the attached `SHA256SUMS.txt`. The build uses `/Brepro`; the same MSVC version gives the same bytes. From be07ce12fada3d64b635b07bfb85e099306902e3 Mon Sep 17 00:00:00 2001 From: qiyinxi Date: Sat, 7 Nov 2026 22:14:16 +0100 Subject: [PATCH 2/2] =?UTF-8?q?release.yml=EF=BC=9A=E6=8C=89=E5=8F=A6?= =?UTF-8?q?=E5=AE=A1=E6=84=8F=E8=A7=81=E6=94=B6=E7=B4=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 建草稿前核对 tag 仍指向 build 编的那个提交;--verify-tag 只看 tag 在不在。删 tag 的推送不跑。 - ancestry 用 refs/remotes/origin/main,叫 origin/main 的 tag 不会顶替远程分支。 - SHA256SUMS 必须和按 ci.yml 格式拼出的两行逐字节相同,不再逐行解析。 - tag 和 CMakeLists.txt 的版本按大小写、只认 ASCII 数字、只许一行 project(...) 来比。 - 去掉 --title,标题照 ModAVDManager 用 tag 名;说明里可复现的条件写全(同样的命令、MSVC 和 Windows SDK 版本)。 Refs ModAVD/ModAVDExtras#1 --- .github/workflows/release.yml | 40 +++++++++++++++++------------------ release/0.1.0.md | 4 ++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc8a983..9cae70e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ concurrency: jobs: build: - if: github.repository == 'ModAVD/ModAVDExtras' + if: github.repository == 'ModAVD/ModAVDExtras' && !github.event.deleted runs-on: windows-2025 timeout-minutes: 20 permissions: @@ -28,12 +28,15 @@ jobs: TAG: ${{ github.ref_name }} SHA: ${{ github.sha }} run: | - if ($env:TAG -notmatch '^v\d+\.\d+\.\d+$') { throw "invalid tag: $env:TAG" } - git merge-base --is-ancestor $env:SHA origin/main + if ($env:TAG -cnotmatch '^v[0-9]+\.[0-9]+\.[0-9]+$') { throw "invalid tag: $env:TAG" } + # Full ref name: a tag called origin/main would win over the remote branch. + git merge-base --is-ancestor $env:SHA refs/remotes/origin/main if ($LASTEXITCODE -ne 0) { throw 'tag is not on origin/main' } $version = $env:TAG.Substring(1) - $want = (Select-String -LiteralPath CMakeLists.txt -Pattern 'project\(ModAVDExtras VERSION (\d+\.\d+\.\d+)').Matches.Groups[1].Value - if ($want -ne $version) { throw "CMakeLists.txt version=$want, tag=$version" } + $found = @(Select-String -LiteralPath CMakeLists.txt -CaseSensitive -Pattern '^project\(ModAVDExtras VERSION ([0-9]+\.[0-9]+\.[0-9]+)') + if ($found.Count -ne 1) { throw "expected one project(ModAVDExtras VERSION ...) line in CMakeLists.txt, found $($found.Count)" } + $want = $found[0].Matches[0].Groups[1].Value + if ($want -cne $version) { throw "CMakeLists.txt version=$want, tag=$version" } - name: build, test and verify shell: pwsh env: @@ -47,22 +50,13 @@ jobs: ctest --test-dir build -C Release --output-on-failure if ($LASTEXITCODE -ne 0) { throw 'tests failed' } $version = $env:TAG.Substring(1) + # The list must be exactly these lines, in this order: what ci.yml prints for the same build. $names = @('external_renderer_ipc.dll', 'modavd_extras.dll') - $sums = @{} - foreach ($line in Get-Content "release/$version.SHA256SUMS.txt") { - if ($line -notmatch '^([0-9a-f]{64}) (.+)$') { throw "bad line in SHA256SUMS: $line" } - $sums[$Matches[2]] = $Matches[1] - } - if ((($sums.Keys | Sort-Object) -join ',') -cne ($names -join ',')) { throw "SHA256SUMS must list exactly: $($names -join ', ')" } + $lines = foreach ($name in $names) { '{0} {1}' -f (Get-FileHash "build/Release/$name" -Algorithm SHA256).Hash.ToLowerInvariant(), $name } + $lines + if ((Get-Content -Raw "release/$version.SHA256SUMS.txt") -cne (($lines -join "`n") + "`n")) { throw "release/$version.SHA256SUMS.txt must be exactly the lines above" } New-Item -ItemType Directory -Force artifacts | Out-Null - $bad = $false - foreach ($name in $names) { - $got = (Get-FileHash "build/Release/$name" -Algorithm SHA256).Hash.ToLowerInvariant() - "$got $name" - if ($got -ne $sums[$name]) { Write-Output "::error::$name hash mismatch: built $got, release/$version.SHA256SUMS.txt has $($sums[$name])"; $bad = $true } - Copy-Item "build/Release/$name" artifacts - } - if ($bad) { throw 'hash mismatch' } + foreach ($name in $names) { Copy-Item "build/Release/$name" artifacts } Copy-Item "release/$version.SHA256SUMS.txt" artifacts/SHA256SUMS.txt - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: @@ -115,5 +109,11 @@ jobs: $tags = gh api "repos/$env:GH_REPO/releases?per_page=100" --paginate --jq '.[].tag_name' if ($LASTEXITCODE -ne 0) { throw 'cannot list releases' } if ($tags -contains $env:TAG) { throw "a release for $env:TAG already exists (draft or published)" } - gh release create $env:TAG --draft --verify-tag --title "ModAVDExtras v$version" --notes-file "release/$version.md" artifacts/modavd_extras.dll artifacts/external_renderer_ipc.dll artifacts/SHA256SUMS.txt + # --verify-tag only checks that the tag exists: make sure it still points at the commit the build job built. + $head = git rev-parse HEAD + if ($LASTEXITCODE -ne 0) { throw 'git rev-parse failed' } + $tagCommit = gh api "repos/$env:GH_REPO/commits/tags/$env:TAG" --jq .sha + if ($LASTEXITCODE -ne 0) { throw "cannot resolve $env:TAG" } + if ($tagCommit -ne $head) { throw "$env:TAG now points at $tagCommit; this run built $head" } + gh release create $env:TAG --draft --verify-tag --notes-file "release/$version.md" artifacts/modavd_extras.dll artifacts/external_renderer_ipc.dll artifacts/SHA256SUMS.txt if ($LASTEXITCODE -ne 0) { throw 'gh release create failed' } diff --git a/release/0.1.0.md b/release/0.1.0.md index 357f561..9e567ff 100644 --- a/release/0.1.0.md +++ b/release/0.1.0.md @@ -34,7 +34,7 @@ ## 下载与核对 -两个 DLL 由本仓库的 release workflow 在 GitHub 的 `windows-2025` 上编出(`cmake -A x64`,`--config Release`),和 tag 下的 `release/0.1.0.SHA256SUMS.txt` 核对过才上传。请用附件 `SHA256SUMS.txt` 核对,例如在 PowerShell 里运行 `Get-FileHash .\modavd_extras.dll -Algorithm SHA256`。编译带 `/Brepro`,用同一版本的 MSVC 编能得到相同的字节。 +两个 DLL 由本仓库的 release workflow 在 GitHub 的 `windows-2025` 上编出(`cmake -A x64`,`--config Release`),和 tag 下的 `release/0.1.0.SHA256SUMS.txt` 核对过才上传。请用附件 `SHA256SUMS.txt` 核对,例如在 PowerShell 里运行 `Get-FileHash .\modavd_extras.dll -Algorithm SHA256`。编译带 `/Brepro`:用同样的命令、同一版本的 MSVC 和 Windows SDK 编,能得到相同的字节;这两个版本在 workflow 日志里 CMake 配置那一步能看到。 ## English @@ -45,4 +45,4 @@ The first release: screen capture only. Attached are the two Windows x64 DLLs; L - Both DLLs link the C runtime statically and depend only on `KERNEL32` and `WS2_32`. - Capture time, p50 in ms (720p / 1080p): the old impersonation DLL 0.28–0.30 / 0.68–0.70; `external_renderer_ipc.dll` 0.11–0.12 / 0.28–0.31; `modavd_extras.dll` BGR top-down 0.06–0.07 / 0.13. - License: LGPL-3.0-or-later; the texts are [LICENSE](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.0/LICENSE) (LGPL-3.0) and [COPYING](https://github.com/ModAVD/ModAVDExtras/blob/v0.1.0/COPYING) (GPL-3.0) at the tag. Ship them with the DLLs. Source: . -- The DLLs are built by this repository's release workflow on GitHub's `windows-2025` (`cmake -A x64`, `--config Release`) and checked against `release/0.1.0.SHA256SUMS.txt` at the tag before upload. Check them with the attached `SHA256SUMS.txt`. The build uses `/Brepro`; the same MSVC version gives the same bytes. +- The DLLs are built by this repository's release workflow on GitHub's `windows-2025` (`cmake -A x64`, `--config Release`) and checked against `release/0.1.0.SHA256SUMS.txt` at the tag before upload. Check them with the attached `SHA256SUMS.txt`. The build uses `/Brepro`: the same commands with the same MSVC and Windows SDK versions give the same bytes; both versions show in the CMake configure step of the workflow log.