From 258f6f7552f9e0a0cad924bddfc11bfea43df715 Mon Sep 17 00:00:00 2001 From: zhiiw Date: Sat, 22 Aug 2026 11:26:33 +0800 Subject: [PATCH 1/8] refactor(release): remove bundled Git distribution --- .github/RELEASE_CHECKLIST.md | 22 +- .github/workflows/release-windows-check.yml | 2 - .github/workflows/release.yml | 63 +-- apps/desktop/electron-builder.config.mjs | 24 - .../resources/licenses/git/LICENSE.txt | 360 -------------- .../desktop/resources/licenses/git/NOTICE.txt | 18 - .../resources/licenses/git/SOURCE_OFFER.txt | 21 - .../licenses/npm/THIRD_PARTY_NOTICES.txt | 450 ------------------ docs/architecture/bundled-git-runtime-v1.md | 204 -------- ...-workspace-execution-admission-v1.zh-CN.md | 3 +- ...me-workspace-version-authority-v1.zh-CN.md | 9 +- package-lock.json | 178 ------- package.json | 6 +- packages/cli/THIRD_PARTY_NOTICES.txt | 450 ------------------ .../src/__tests__/bundled-git-runtime.test.ts | 147 ------ .../execution-composition-factory.test.ts | 27 ++ .../src/server/bundled-git-runtime.ts | 194 -------- .../src/server/execution-candidate.ts | 2 - .../server/execution-composition-factory.ts | 12 +- .../src/server/execution-service.ts | 1 - .../bundled-git-workspace-smoke.test.ts | 166 ------- .../dugite-native-environment.test.ts | 53 --- .../storage/src/dugite-native-environment.ts | 35 -- packages/storage/src/git-workspace-service.ts | 54 +-- scripts/ci-test-plan.mjs | 3 - scripts/package-macos-arm64.mjs | 1 - scripts/package-windows-x64.mjs | 1 - scripts/prepare-bundled-git-source.mjs | 180 ------- scripts/prepare-bundled-git.mjs | 122 ----- scripts/product-release-identity.mjs | 4 - scripts/product-release.test.mjs | 34 +- scripts/verify-packaged-app.mjs | 9 +- scripts/verify-packaged-app.test.mjs | 26 +- 33 files changed, 108 insertions(+), 2773 deletions(-) delete mode 100644 apps/desktop/resources/licenses/git/LICENSE.txt delete mode 100644 apps/desktop/resources/licenses/git/NOTICE.txt delete mode 100644 apps/desktop/resources/licenses/git/SOURCE_OFFER.txt delete mode 100644 docs/architecture/bundled-git-runtime-v1.md delete mode 100644 packages/runtime-host/src/__tests__/bundled-git-runtime.test.ts create mode 100644 packages/runtime-host/src/__tests__/execution-composition-factory.test.ts delete mode 100644 packages/runtime-host/src/server/bundled-git-runtime.ts delete mode 100644 packages/storage/src/__tests__/bundled-git-workspace-smoke.test.ts delete mode 100644 packages/storage/src/__tests__/dugite-native-environment.test.ts delete mode 100644 packages/storage/src/dugite-native-environment.ts delete mode 100644 scripts/prepare-bundled-git-source.mjs delete mode 100644 scripts/prepare-bundled-git.mjs diff --git a/.github/RELEASE_CHECKLIST.md b/.github/RELEASE_CHECKLIST.md index 426a6e6f43..a4cdad71f4 100644 --- a/.github/RELEASE_CHECKLIST.md +++ b/.github/RELEASE_CHECKLIST.md @@ -1,19 +1,21 @@ # Product release checklist -The `Release` workflow is Maka's convenience-artifact release entry point. Desktop, CLI/TUI, and -bundled Git source materials are built from the exact IPMC-approved ASF source candidate commit. -They share that source commit, the root product version, one convenience tag, one GitHub Release, -one Draft decision, and one release gate. The workflow creates no Draft until every required -artifact job succeeds. +The `Release` workflow is Maka's convenience-artifact release entry point. Desktop and CLI/TUI are +built from the exact IPMC-approved ASF source candidate commit. They share that source commit, the +root product version, one convenience tag, one GitHub Release, one Draft decision, and one release +gate. The workflow creates no Draft until every required artifact job succeeds. Phase 1 requires: - signed and notarized Apple Silicon macOS Desktop artifacts; - the unsigned Windows x64 Desktop installer and ZIP; - the signed, notarized, relocatable Apple Silicon CLI/TUI ZIP; -- bundled Git source materials; - checksums generated after each artifact reaches its final form. +The ASF Desktop artifacts must not contain a Git runtime, a bundled-Git manifest, or Git/Dugite +redistribution notices. Managed-workspace execution remains unavailable until a separately reviewed, +ASF-compatible verified runtime is connected before admission/T1. + The first product release also requires the exact `maka-agent@` npm package. The product tag and Draft must exist before npm staging, but the Draft must remain unpublished until npm is public, Finalize has verified it, and Desktop acceptance has exercised remote Runtime Host setup. @@ -55,13 +57,13 @@ These controls close the check-to-upload and check-to-stage windows. Keep the Re version, and the CLI manifest exposes only the `maka` command. 4. Dispatch `Release` from the exact approved candidate tag and supply the same tag as `source_reference_tag`. A rerun must use that same tag; never select current `main` instead. -5. Confirm `release-identity`, both Desktop matrix entries, `cli-macos-arm64`, `source`, and +5. Confirm `release-identity`, both Desktop matrix entries, `cli-macos-arm64`, and `publish` pass. A skipped or failed required job must prevent Draft creation. 6. Confirm one Draft named `v` targets the approved source SHA, identifies the ASF source reference in its notes, is marked as a GitHub prerelease exactly when the product version is a prerelease, is not marked Latest while it remains a Draft, and contains exactly the manifest reported by `node scripts/product-release-artifacts.mjs list`. The manifest covers both Desktop - platforms and update metadata, the standalone CLI/TUI, bundled source, and their required checksums. + platforms and update metadata, the standalone CLI/TUI, and their required checksums. 7. Inspect the CLI ZIP. It must contain `bin/maka`, `RELEASE.json`, `DISCLAIMER-WIP`, `LICENSE`, `NOTICE`, `THIRD_PARTY_NOTICES.txt`, the pinned Node license, and no `bin/maka-agent`. 8. Confirm `RELEASE.json` records the Draft's product version and source SHA, Apple Team ID @@ -71,8 +73,8 @@ These controls close the check-to-upload and check-to-stage windows. Keep the Re entitlements must retain the required hardened-runtime capabilities and omit `com.apple.security.get-task-allow`, as required by Apple's [notarization guidance](https://developer.apple.com/documentation/security/resolving-common-notarization-issues). -9. Extract the bundled Git source-materials archive. Confirm `SOURCE_MANIFEST.json`, `README.txt`, - all manifest archives, and the expected Dugite native release are present. +9. Inspect both Desktop resource trees and confirm `git/`, `bundled-git.json`, `licenses/git/`, and + `licenses/dugite/` are absent. If the publish job created the product tag or Draft but failed before every asset was uploaded, rerun `Release` from the same approved ASF source candidate tag with the same diff --git a/.github/workflows/release-windows-check.yml b/.github/workflows/release-windows-check.yml index 774ace8d3d..f639d13df5 100644 --- a/.github/workflows/release-windows-check.yml +++ b/.github/workflows/release-windows-check.yml @@ -17,7 +17,6 @@ on: - 'apps/desktop/package.json' - 'scripts/package-windows-x64.mjs' - 'scripts/verify-windows-sandbox-e2e.mjs' - - 'scripts/prepare-bundled-git.mjs' - 'scripts/verify-windows-x64.mjs' - 'scripts/verify-windows-sandbox-e2e.mjs' - 'scripts/verify-windows-installer-lifecycle.mjs' @@ -38,7 +37,6 @@ on: - '.gitattributes' - 'package.json' - 'package-lock.json' - - 'apps/desktop/resources/licenses/git/NOTICE.txt' - 'apps/desktop/resources/licenses/cargo/THIRD_PARTY_NOTICES.txt' - '.github/workflows/release-windows-check.yml' workflow_dispatch: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5b2ede9cf..ecc1047c0b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,6 @@ jobs: dmg: ${{ steps.identity.outputs.dmg }} exe: ${{ steps.identity.outputs.exe }} cli_archive: ${{ steps.identity.outputs.cli_archive }} - source_archive: ${{ steps.identity.outputs.source_archive }} node_version: ${{ steps.identity.outputs.node_version }} npm_version: ${{ steps.identity.outputs.npm_version }} node_archive: ${{ steps.identity.outputs.node_archive }} @@ -321,66 +320,10 @@ jobs: if: always() run: rm -f "${{ runner.temp }}/AuthKey_Maka.p8" - source: - needs: release-identity - runs-on: ubuntu-latest - timeout-minutes: 20 - defaults: - run: - shell: bash - - steps: - - name: Check out the dispatched commit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ needs.release-identity.outputs.source_commit }} - persist-credentials: false - - - name: Set up the pinned release Node.js - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ needs.release-identity.outputs.node_version }} - cache: npm - - - name: Select and verify the pinned npm release toolchain - run: | - npm install --global --no-audit --no-fund "npm@${{ needs.release-identity.outputs.npm_version }}" - test "$(npm --version)" = "${{ needs.release-identity.outputs.npm_version }}" - - - name: Install pinned dependency metadata - run: npm ci --ignore-scripts - - - name: Materialize bundled Git source materials - run: npm run prepare:bundled-git-source - - - name: Archive and hash source materials - env: - SOURCE_ARCHIVE: ${{ needs.release-identity.outputs.source_archive }} - run: | - archive_dir="apps/desktop/release-sources" - archive="${archive_dir}/${SOURCE_ARCHIVE}" - tar -C apps/desktop/release-sources/bundled-git -czf "$archive" . - (cd "$archive_dir" && sha256sum "$SOURCE_ARCHIVE" > "${SOURCE_ARCHIVE}.sha256") - - - name: Stage the exact product artifact group - run: >- - node scripts/product-release-artifacts.mjs stage - "source" - apps/desktop/release-sources - "${{ runner.temp }}/release-assets" - - - name: Upload verified source assets - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: release-source - path: ${{ runner.temp }}/release-assets - if-no-files-found: error - retention-days: 7 - publish: # One draft release carries both platforms, so it is created once, after # every platform has been packaged and verified. - needs: [release-identity, desktop, cli-macos-arm64, source] + needs: [release-identity, desktop, cli-macos-arm64] runs-on: ubuntu-latest timeout-minutes: 15 permissions: @@ -447,9 +390,7 @@ jobs: The Windows build is unsigned: SmartScreen warns on first launch, and the download has to be checked against its .sha256 file. - Bundled Git source materials and their checksum are attached to this draft. The packaged applications also carry the GPLv2 license and written source offer. - - Known limitation: Computer Use is not included in this release." + Known limitations: Computer Use and managed-workspace execution are not included in this release. The ASF Desktop artifacts do not distribute a Git runtime." classification=(--prerelease=false --latest=false) if [[ "$IS_PRERELEASE" == "true" ]]; then diff --git a/apps/desktop/electron-builder.config.mjs b/apps/desktop/electron-builder.config.mjs index 03436ca5ff..a22fcfbd1e 100644 --- a/apps/desktop/electron-builder.config.mjs +++ b/apps/desktop/electron-builder.config.mjs @@ -47,14 +47,6 @@ export default { 'dist/renderer/computer-use-overlay/**', ], extraResources: [ - { - from: '../../node_modules/dugite/git', - to: 'git', - }, - { - from: 'bundled-git.json', - to: 'bundled-git.json', - }, { from: 'bundled-tools.json', to: 'bundled-tools.json', @@ -93,22 +85,6 @@ export default { from: '../../LICENSE', to: 'licenses/maka/LICENSE', }, - { - from: '../../node_modules/dugite/LICENSE', - to: 'licenses/dugite/LICENSE', - }, - { - from: 'resources/licenses/git/NOTICE.txt', - to: 'licenses/git/NOTICE.txt', - }, - { - from: 'resources/licenses/git/LICENSE.txt', - to: 'licenses/git/LICENSE.txt', - }, - { - from: 'resources/licenses/git/SOURCE_OFFER.txt', - to: 'licenses/git/SOURCE_OFFER.txt', - }, { from: '../../NOTICE', to: 'licenses/maka/NOTICE', diff --git a/apps/desktop/resources/licenses/git/LICENSE.txt b/apps/desktop/resources/licenses/git/LICENSE.txt deleted file mode 100644 index 536e55524d..0000000000 --- a/apps/desktop/resources/licenses/git/LICENSE.txt +++ /dev/null @@ -1,360 +0,0 @@ - - Note that the only valid version of the GPL as far as this project - is concerned is _this_ particular version of the license (ie v2, not - v2.2 or v3.x or whatever), unless explicitly otherwise stated. - - HOWEVER, in order to allow a migration to GPLv3 if that seems like - a good idea, I also ask that people involved with the project make - their preferences known. In particular, if you trust me to make that - decision, you might note so in your copyright message, ie something - like - - This file is licensed under the GPL v2, or a later version - at the discretion of Linus. - - might avoid issues. But we can also just decide to synchronize and - contact all copyright holders on record if/when the occasion arises. - - Linus Torvalds - ----------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/apps/desktop/resources/licenses/git/NOTICE.txt b/apps/desktop/resources/licenses/git/NOTICE.txt deleted file mode 100644 index c5f35874f9..0000000000 --- a/apps/desktop/resources/licenses/git/NOTICE.txt +++ /dev/null @@ -1,18 +0,0 @@ -Maka bundled Git runtime -======================== - -Maka packages the cross-platform Git distribution maintained by GitHub Desktop's -dugite-native project. The pinned release is declared by dugite 3.2.2 in -node_modules/dugite/script/embedded-git.json and is verified by SHA-256 before -extraction. - -Git is licensed under the GNU General Public License version 2. Maka packages a -stable copy of that license at resources/licenses/git/LICENSE.txt because the -upstream platform archives do not expose one uniform internal path. Component -notices that are present in the upstream distribution remain inside resources/git. -Each Maka GitHub Release carries a source-materials archive for the pinned -upstream revisions. The written offer and request procedure for complete -corresponding source are packaged beside this notice in SOURCE_OFFER.txt. - -The dugite JavaScript wrapper is licensed under the MIT License; its license is -packaged at resources/licenses/dugite/LICENSE. diff --git a/apps/desktop/resources/licenses/git/SOURCE_OFFER.txt b/apps/desktop/resources/licenses/git/SOURCE_OFFER.txt deleted file mode 100644 index 2ff7975cb3..0000000000 --- a/apps/desktop/resources/licenses/git/SOURCE_OFFER.txt +++ /dev/null @@ -1,21 +0,0 @@ -Written offer for bundled Git source code -========================================= - -Maka distributes separate Git command-line programs under the GNU General -Public License version 2. For at least three years after a Maka release is -distributed, Maka offers any third party the complete corresponding -machine-readable source code for those GPL-covered programs, including the -scripts used to control their compilation and installation, for no more than -the physical cost of providing that source. - -Each Maka GitHub Release includes an immediately downloadable source-materials -archive and manifest for the pinned upstream revisions. To request the complete -corresponding source for a particular Maka version, open a licensing request at: - -https://github.com/maka-agent/maka-agent/issues/new?labels=licensing - -Include the Maka version, operating system, CPU architecture, and a reference -to this written offer. This offer applies to everyone who receives it. - -Maka's own source code remains licensed under Apache-2.0. The bundled Git -programs and their source remain under their respective upstream licenses. diff --git a/apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt b/apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt index a5adfeec03..277efb15d2 100644 --- a/apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt +++ b/apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt @@ -4780,246 +4780,6 @@ Apache License ================================================================================ -Package: bare-path@3.1.1 -Declared license: Apache-2.0 -Selected license: Apache-2.0 -Repository: git+https://github.com/holepunchto/bare-path.git - ---- LICENSE --- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ---- NOTICE --- -Copyright 2023 Holepunch Inc - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---- - -Copyright Joyent, Inc. and other Node contributors. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to permit -persons to whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -================================================================================ - Package: bare-semver@1.1.0 Declared license: Apache-2.0 Selected license: Apache-2.0 @@ -5230,216 +4990,6 @@ Apache License ================================================================================ -Package: bare-url@2.4.7 -Declared license: Apache-2.0 -Selected license: Apache-2.0 -Repository: git+https://github.com/holepunchto/bare-url.git - ---- LICENSE --- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -================================================================================ - Package: body-parser@2.3.0 Declared license: MIT Selected license: MIT diff --git a/docs/architecture/bundled-git-runtime-v1.md b/docs/architecture/bundled-git-runtime-v1.md deleted file mode 100644 index b80b03afac..0000000000 --- a/docs/architecture/bundled-git-runtime-v1.md +++ /dev/null @@ -1,204 +0,0 @@ -# Bundled Git Runtime v1 - -Status: implementation slice for managed-workspace execution. This is a publication capability that -sits between M1.2 runtime-host composition and broad managed-workspace enablement; it is not M1.3 -dependency/secret provisioning. - -## 1. Invariant and owner - -The Runtime Host may enable the managed-workspace owner only with the Maka-packaged Git toolchain for -the current platform and architecture. It must never discover or fall back to a Git executable through -`PATH`, a shell profile, a package manager, or the source workspace. - -Ownership is split deliberately: - -- `dugite@3.2.2` owns the upstream `dugite-native` release URL and archive SHA-256; -- `scripts/prepare-bundled-git.mjs` owns build-time version, executable, license, and digest validation; -- `bundled-git.json` binds one packaged artifact to platform, architecture, Git version, archive digest, - executable path, and executable digest; -- Runtime Host resolves the manifest and refuses missing, malformed, mismatched, escaped, symlinked, or - digest-mismatched artifacts; -- storage re-hashes the exact executable before every Git process and constructs the `dugite-native` - helper environment without inheriting a system-Git `PATH`. - -Two digests intentionally serve different purposes: - -- `executableSha256` is rechecked immediately before every Git invocation. It proves which entry binary - storage is about to execute; -- `runtimeIdentitySha256` is a canonical digest of the declared distribution identity: manifest - protocol, provider, Git version, platform, architecture, executable location and digest, and the - upstream archive digest. Managed repository, epoch, and worktree-binding artifacts persist this value - as `gitRuntimeSha256`, so changing any of that provenance creates an explicit epoch incompatibility - instead of silently reinterpreting an existing workspace. - -The manifest is evidence about one packaged distribution, not a mutable preference and not a system Git -probe result. - -## 2. Supply chain - -The dependency is exact-pinned as `dugite@3.2.2`. Its `embedded-git.json` pins -`desktop/dugite-native@v2.53.0-3` and provides a SHA-256 for every supported archive. Dugite verifies that -archive before extraction. Maka then executes the extracted binary with `--version`, hashes that exact -binary, and emits the platform manifest used by packaging and runtime admission. - -The archive checksum is not an independent trust root. `npm ci` first authenticates the exact dugite -package bytes against `package-lock.json` integrity; that pinned package supplies `embedded-git.json`, -which authenticates the native archive; Maka then regenerates `bundled-git.json` inside each platform's -release job immediately before packaging. Review and branch protection own lockfile changes. The signed -and notarized macOS application closes the artifact-publication chain. The current Windows release is -explicitly unsigned, so its published SHA-256 and GitHub release transport do not provide OS-level -publisher identity; that remains a release security limitation rather than something the runtime -manifest can repair. - -The packaged runtime includes the complete `dugite-native` directory rather than copying only `git`. -Git subprograms, templates, MinGit libraries, certificates, and platform support files remain relative to -the same root. Runtime environment variables (`GIT_EXEC_PATH`, templates, Linux `PREFIX`/CA bundle, and -Windows MinGit paths) are derived only from that declared root. - -Every managed Git process receives an isolated environment: an owner-specific `HOME` and -`XDG_CONFIG_HOME`, `GIT_CONFIG_NOSYSTEM=1`, non-interactive credential settings, a fixed hooks directory, -and a `PATH` containing only the declared runtime directories. Runtime Host does not merge the user's -shell environment into this map. On Windows, the MinGit DLL/helper directories are included explicitly; -the current working directory is the Maka-owned home rather than the source checkout or application -directory. - -The execution profile is also injected at command priority and persisted on Maka-owned repositories: -`core.hooksPath` points at the owned empty hooks directory, credential helpers and interactive prompts -are disabled, `core.sshCommand` is empty, and `protocol.allow=never` prevents transport activation in -the current local-only workspace protocol. Operation-scoped environment values are merged before the -hermetic profile, so they cannot override its HOME, PATH, config, or prompt fences. Source repository -configuration is never copied into the independent managed repository; unsupported indirection such as -includes, alternates, replace refs, partial-clone state, and executable fsmonitor configuration is -rejected before baseline import. - -Production never resolves Git through `node_modules`. Dugite is the build-time supplier; Electron copies -the complete extracted distribution to `resources/git`, and Runtime Host resolves only that signed -application resource root. Storage receives a narrow verified-runtime input and has no dependency on the -dugite npm package or its manifest format. - -`VerifiedGitRuntime` is currently the Maka-owned adapter boundary between managed-workspace operations -and the distribution. A broader public `MakaGitRuntime` interface is intentionally deferred until a -second production implementation exists; introducing a swappable system-Git or libgit2 abstraction now -would create an unsupported execution mode rather than strengthen the present invariant. - -## 3. Ordering and atomic boundary - -Publication ordering is: - -1. install the exact npm lockfile; -2. let dugite download and SHA-256 verify the platform archive; -3. run `prepare:bundled-git` and verify the executable version and digest; -4. package the complete Git directory, manifest, and notices in the signed application; -5. verify their presence in the packaged application; -6. at Runtime Host startup, resolve and verify the manifest before composing a managed-workspace owner; -7. immediately before each Git invocation, storage re-verifies the executable digest. - -There is no cross-filesystem transaction spanning npm download and application packaging. A partially -prepared or mixed artifact is therefore not repaired in place: no `distributionReady: true` manifest is -accepted unless all build checks have completed, and runtime fails closed on any mismatch. - -The v1 identity binds the SHA-256-verified upstream archive rather than hashing every extracted file at -startup. This avoids an important signing ambiguity: macOS code signing may legitimately rewrite Mach-O -bytes after the prepare step, so a pre-sign tree digest could make the signed application reject itself. -A future post-sign runtime-tree/Merkle manifest must be generated and verified inside the release-signing -pipeline, not added as an ordinary prepare-time hash. Until then, Maka does not claim per-file integrity -for every helper in the extracted tree. - -That limitation is deliberate but security-relevant: v1 detects entry-binary replacement before every -Git process, while helper integrity relies on the authenticated upstream archive plus the packaged -application boundary. Closing it requires a dedicated publication slice that observes the final signed -helper bytes at the correct signing phase, binds that inventory into the outer application signature, -and verifies the inventory at managed-operation admission. Hashing the pre-sign extraction here would -be incorrect. The remaining verify-to-spawn window cannot be eliminated portably by a path-based handle; -the current threat model relies on packaged resources not being writable by the managed workspace and -on platform publisher authenticity. macOS supplies that authenticity today; Windows Authenticode is a -separate release-security prerequisite and is not synthesized by `bundled-git.json`. - -## 4. Failure states and rollback - -Stable runtime failure classes are: - -- `bundled_git_unavailable`: manifest or executable is absent/unreadable; -- `bundled_git_manifest_invalid`: schema, path, or distribution metadata is invalid; -- `bundled_git_platform_mismatch`: artifact targets a different OS or architecture; -- `bundled_git_integrity_mismatch`: packaged executable differs from the build manifest. - -These failures disable managed-workspace composition. They do not change attached mode and never cause a -system Git fallback. Rollback is release-level: ship the prior application bundle or disable the managed -workspace feature. Runtime must not rewrite the signed resources or silently regenerate the manifest. - -## 5. Platform matrix - -| Platform | Bundled executable | Helper environment | Current promise | -|---|---|---|---| -| Windows x64/arm64/ia32 | `git/cmd/git.exe` | MinGit `mingw*`/`clangarm64` bin and `libexec/git-core` | Supported by the pinned dugite-native archive; managed filesystem execution remains separately gated by the Windows sandbox capability | -| macOS x64/arm64 | `git/bin/git` | bundle `libexec/git-core` and templates | Supported; release must remain code-signed/notarized as one app bundle | -| Linux x64/arm64/arm/ia32 | `git/bin/git` | bundle `libexec`, templates, `PREFIX`, and CA file | Supported where the corresponding pinned archive and filesystem sandbox backend are available | - -Power-loss durability is not created by bundling Git. Repository/worktree durability remains owned by the -managed-workspace artifact protocol and its crash tests. - -## 6. Acceptance tests - -- strict manifest happy path; -- platform/architecture mismatch; -- executable tampering; -- source-repository config and environment poisoning cannot override the managed execution profile; -- missing manifest with a system Git present (must still fail); -- path escape and symlink rejection; -- build preparation from the exact dugite platform record; -- Git version mismatch and unsupported platform rejection; -- packaged app contains the Git runtime, manifest, and license notices; -- production-shaped managed workspace open using the packaged runtime (required before broad enablement). - -The production-shaped smoke exercises the real commands required by the current owner, including -repository creation, ref updates, tree/index operations, and worktree lifecycle. This is the capability -gate. Startup `--help` probes are deliberately not used: they are weaker than executing the actual -workflow, can invoke pagers or platform-specific help behavior, and would duplicate checks before every -application launch. Individual Git failures remain fail-closed at the operation boundary. - -The last item is deliberately a release gate rather than evidence that managed execution is already the -default. Desktop/CLI activation remains a later, explicit product decision. - -The future M2 `WorkspaceVersionAccepted` fact must carry the same Git runtime identity alongside its -parent version, accepted commit/tree, materialization profile, and policy identity. Repository/epoch -binding protects the current managed artifact; version-fact binding is separately required to make an -accepted mutation historically interpretable. It belongs to M2 and must not be approximated in this -publication PR before that fact has a production writer and consumer. - -## 7. Licensing and size policy - -The full `dugite-native` directory is packaged, including whatever component license directories its -platform archive supplies. Because those archives do not expose Git's license at one uniform path, Maka -also tracks the Git GPLv2 text and packages it consistently as `resources/licenses/git/LICENSE.txt` -alongside the Git/dugite notices. Prepare and final-package verification both require that stable asset. -A generated component inventory is useful release hardening, but it is not represented as a -runtime-authority guarantee in v1 and should be implemented as a dedicated supply-chain slice. - -The first release keeps the full upstream distribution. Pruning shells, Perl, GUI programs, docs, or -helpers is deferred until Maka has an exact command/helper allowlist and the pruned artifact passes the -same production-shaped release smoke on Windows, macOS, and Linux. Package size alone is not sufficient -evidence that a helper is safe to remove. - -An application upgrade currently replaces the packaged runtime. Existing managed artifacts whose -recorded runtime identity differs therefore fail closed and require an explicit future migration, -rebaseline, or compatible-runtime retention policy. V1 does not silently migrate them and does not ship -multiple runtime generations. A versioned `resources/git-runtime/` layout is a possible future -carrier, but adopting it requires an owner and retention/patching policy rather than a directory rename. - -### Source distribution and license boundary - -Bundled Git remains a separate command-line program; Maka invokes it through a process boundary, so -Maka's own Apache-2.0 license is unchanged. Binary redistribution still creates GPLv2 obligations for -the Git program itself. The release pipeline therefore owns two independent artifacts: - -1. `prepare:bundled-git-source` materializes commit-addressed source archives and a checksum manifest - for the upstream revisions used by Dugite native; -2. every packaged application carries `licenses/git/SOURCE_OFFER.txt`, which promises complete - corresponding source on request for at least three years. - -The source-materials archive is a convenience artifact, not an unsupported claim that a short list of -upstream tarballs is necessarily the complete corresponding source for every platform toolchain. The -written offer is the fail-safe for build scripts and indirect platform dependencies. A release is not -publishable unless the binary artifacts, source-materials archive, source-offer text, and their hashes -are all present. The release owner must retain the ability to honor requests for the full offer period. diff --git a/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md b/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md index 40077b9371..2835bd2358 100644 --- a/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md +++ b/docs/architecture/runtime-managed-workspace-execution-admission-v1.zh-CN.md @@ -155,4 +155,5 @@ JavaScript finally,因此本切片的安全保证来自“只读 operation all 与 successor workspace version。 M1.2 提供真实 runtime-host composition seam,但本切片不修改 Desktop/CLI 默认配置,因此不默认开启 managed -execution,也不改变 attached mode。bundled Git 的发行与 launcher 参数接线仍是独立发布能力,不得回退系统 Git。 +execution,也不改变 attached mode。ASF Desktop 已停止分发 bundled Git;在兼容许可证的 verified runtime +接入前,managed execution 必须在 admission/T1 前保持不可用,且不得回退系统 Git。 diff --git a/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md b/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md index 63c9399c46..9779d570b3 100644 --- a/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md +++ b/docs/architecture/runtime-workspace-version-authority-v1.zh-CN.md @@ -292,7 +292,7 @@ SQLite read transaction/snapshot;否则并发 writer 可能让读者拼接两 以下能力不属于 M0 baseline authority: -- bundled Git 探测、source eligibility 与 internal bare repository; +- verified Git runtime 探测、source eligibility 与 internal bare repository; - managed worktree 创建、owner lifecycle、quarantine 与 repair; - `.maka-workspace.json` 在 managed worktree 的 identity/exclude 策略; - ignored dependency/cache 路径的挂载或 scratch policy; @@ -309,9 +309,10 @@ SQLite read transaction/snapshot;否则并发 writer 可能让读者拼接两 ### Slice 2:Managed Workspace Owner(已完成) -只证明:Maka 能用 bundled Git 创建并独占一个 private internal repository/worktree lifecycle;外部 drift -被检测后 quarantine。需要先拍板 ignored dependencies/scratch、identity marker、fixed Git config、 -symlink/LFS/submodule/case/filemode 平台政策。 +只证明:Maka 能用一个显式注入且经过校验的 Git runtime 创建并独占 private internal +repository/worktree lifecycle;外部 drift 被检测后 quarantine。ASF Desktop 不再提供该 runtime,后续实现将 +验证 Apache-2.0/MIT 的 gitoxide backend。需要先拍板 ignored dependencies/scratch、identity marker、fixed +Git config、symlink/LFS/submodule/case/filemode 平台政策。 ### Slice 3:Baseline Open Bundle(实现中) diff --git a/package-lock.json b/package-lock.json index eabeb432e3..2c5c16c1b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,6 @@ "@biomejs/biome": "2.5.6", "@electron/asar": "3.4.1", "@types/node": "^26.1.2", - "dugite": "3.2.2", "knip": "^6.26.0", "patch-package": "8.0.1", "typescript": "^7.0.2", @@ -5360,21 +5359,6 @@ "proxy-from-env": "^2.1.0" } }, - "node_modules/b4a": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", - "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -5403,46 +5387,6 @@ } } }, - "node_modules/bare-events": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", - "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, - "node_modules/bare-fs": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.0.tgz", - "integrity": "sha512-fM+MhCvdQhZ7NV6S95a07gPSqjIYKn6mFaXfx266wN3ajZGl/+1AzH+ubkXQ0fFZvOe2nk9VHkzdYkQE5zMV3Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.28.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, "node_modules/bare-module-resolve": { "version": "1.12.4", "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.4.tgz", @@ -5460,57 +5404,12 @@ } } }, - "node_modules/bare-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", - "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", - "devOptional": true, - "license": "Apache-2.0" - }, "node_modules/bare-semver": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.1.0.tgz", "integrity": "sha512-1Hw5qJ7hXdVt3uPUqjeFTuxyvBUJauvz5A1I2jk8gzjZMHp04n//6nV9MDbG9CMw78JHY2lGV0w6s//LrASm2w==", "license": "Apache-2.0" }, - "node_modules/bare-stream": { - "version": "2.13.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", - "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.8.1", - "streamx": "^2.25.0", - "teex": "^1.0.1" - }, - "peerDependencies": { - "bare-abort-controller": "*", - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - }, - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.7.tgz", - "integrity": "sha512-o8CRCiJtib+ycO3mE4A5UChtGX4dDP2XxsWVu9P+Zc3H8tcmKwNVEDoDTXmwN+uuMhfKeT7/i7Y26xS8W7ohoA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "bare-path": "^3.0.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -7201,21 +7100,6 @@ "url": "https://dotenvx.com" } }, - "node_modules/dugite": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/dugite/-/dugite-3.2.2.tgz", - "integrity": "sha512-pGTVaxea0WqauGXF5A3GmpmPOim6oTnfYM6dS6s8nHyJxf4pRTjwtFHkqLkT5de87uUPhTI+LtlmcJQ2WkqeGA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "progress": "^2.0.3", - "tar-stream": "^3.1.7" - }, - "engines": { - "node": ">= 20" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -7775,16 +7659,6 @@ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.7.0" - } - }, "node_modules/eventsource": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", @@ -7883,13 +7757,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-uri": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", @@ -12696,18 +12563,6 @@ "node": ">=10" } }, - "node_modules/streamx": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", - "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", - "dev": true, - "license": "MIT", - "dependencies": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -12849,19 +12704,6 @@ "node": ">=18" } }, - "node_modules/tar-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", - "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "bare-fs": "^4.5.5", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -12872,16 +12714,6 @@ "node": ">=18" } }, - "node_modules/teex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", - "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "streamx": "^2.12.5" - } - }, "node_modules/temp": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", @@ -12908,16 +12740,6 @@ "fs-extra": "^10.0.0" } }, - "node_modules/text-decoder": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", - "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, "node_modules/tiny-async-pool": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", diff --git a/package.json b/package.json index c245429fbf..58f4c686c3 100644 --- a/package.json +++ b/package.json @@ -79,9 +79,7 @@ "windows:inventory": "node --test scripts/windows-test-inventory.test.mjs && node scripts/windows-test-inventory.mjs --check", "windows:inventory:write": "node scripts/windows-test-inventory.mjs --write", "smoke:windows": "npm run build && npm run smoke:windows:dist", - "smoke:windows:dist": "node scripts/windows-smoke.mjs", - "prepare:bundled-git": "node scripts/prepare-bundled-git.mjs", - "prepare:bundled-git-source": "node scripts/prepare-bundled-git-source.mjs" + "smoke:windows:dist": "node scripts/windows-smoke.mjs" }, "devDependencies": { "@ai-sdk/provider-utils": "5.0.27", @@ -90,7 +88,6 @@ "@astryxdesign/core": "0.4.0", "@biomejs/biome": "2.5.6", "@types/node": "^26.1.2", - "dugite": "3.2.2", "knip": "^6.26.0", "patch-package": "8.0.1", "typescript": "^7.0.2", @@ -99,7 +96,6 @@ "allowScripts": { "esbuild@0.27.7": true, "@jackwener/opencli@1.8.4": true, - "dugite@3.2.2": true, "node-pty@1.2.0-beta.15": true }, "overrides": { diff --git a/packages/cli/THIRD_PARTY_NOTICES.txt b/packages/cli/THIRD_PARTY_NOTICES.txt index 18f5c80568..a7c819554e 100644 --- a/packages/cli/THIRD_PARTY_NOTICES.txt +++ b/packages/cli/THIRD_PARTY_NOTICES.txt @@ -2579,246 +2579,6 @@ Apache License ================================================================================ -Package: bare-path@3.1.1 -Declared license: Apache-2.0 -Selected license: Apache-2.0 -Repository: git+https://github.com/holepunchto/bare-path.git - ---- LICENSE --- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ---- NOTICE --- -Copyright 2023 Holepunch Inc - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---- - -Copyright Joyent, Inc. and other Node contributors. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to permit -persons to whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -================================================================================ - Package: bare-semver@1.1.0 Declared license: Apache-2.0 Selected license: Apache-2.0 @@ -3029,216 +2789,6 @@ Apache License ================================================================================ -Package: bare-url@2.4.7 -Declared license: Apache-2.0 -Selected license: Apache-2.0 -Repository: git+https://github.com/holepunchto/bare-url.git - ---- LICENSE --- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -================================================================================ - Package: body-parser@2.3.0 Declared license: MIT Selected license: MIT diff --git a/packages/runtime-host/src/__tests__/bundled-git-runtime.test.ts b/packages/runtime-host/src/__tests__/bundled-git-runtime.test.ts deleted file mode 100644 index e85c540162..0000000000 --- a/packages/runtime-host/src/__tests__/bundled-git-runtime.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -import assert from 'node:assert/strict'; -import { mkdtemp, mkdir, realpath, rm, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; -import { tmpdir } from 'node:os'; -import test from 'node:test'; -import { resolveBundledGitRuntime } from '../server/bundled-git-runtime.js'; - -test('resolves the packaged Git executable from a strict platform manifest', async () => { - const fixture = await createFixture(); - try { - const canonicalRoot = await realpath(fixture.root); - assert.deepEqual( - await resolveBundledGitRuntime({ - resourcesRoot: fixture.root, - platform: 'win32', - arch: 'x64', - }), - { - executablePath: await realpath(fixture.executablePath), - expectedSha256: 'sha256:f391158ea86e1e56b2b0c13583821c2b752c2abccd91496d91e025d54ac616e5', - runtimeIdentitySha256: - 'sha256:926a45a8cd95c8ae25683905dc9171c54a9e31ffe362875c8f8b7f6e67ed522f', - distribution: { - kind: 'dugite_native_v1', - rootPath: join(canonicalRoot, 'git'), - }, - }, - ); - } finally { - await fixture.remove(); - } -}); - -test('fails closed when the bundled Git platform does not match the host', async () => { - const fixture = await createFixture(); - try { - await assert.rejects( - resolveBundledGitRuntime({ - resourcesRoot: fixture.root, - platform: 'darwin', - arch: 'arm64', - }), - { code: 'bundled_git_platform_mismatch' }, - ); - } finally { - await fixture.remove(); - } -}); - -test('binds runtime identity to the complete source archive provenance', async () => { - const first = await createFixture(); - const second = await createFixture({ - sourceArchiveSha256: `sha256:${'2'.repeat(64)}`, - }); - try { - const firstRuntime = await resolveBundledGitRuntime({ - resourcesRoot: first.root, - platform: 'win32', - arch: 'x64', - }); - const secondRuntime = await resolveBundledGitRuntime({ - resourcesRoot: second.root, - platform: 'win32', - arch: 'x64', - }); - assert.equal(firstRuntime.expectedSha256, secondRuntime.expectedSha256); - assert.notEqual(firstRuntime.runtimeIdentitySha256, secondRuntime.runtimeIdentitySha256); - } finally { - await Promise.all([first.remove(), second.remove()]); - } -}); - -test('fails closed when the packaged Git executable does not match its manifest', async () => { - const fixture = await createFixture(); - try { - await writeFile(fixture.executablePath, 'tampered bundled git\n'); - await assert.rejects( - resolveBundledGitRuntime({ - resourcesRoot: fixture.root, - platform: 'win32', - arch: 'x64', - }), - { code: 'bundled_git_integrity_mismatch' }, - ); - } finally { - await fixture.remove(); - } -}); - -test('does not fall back to PATH when the bundled Git manifest is missing', async () => { - const root = await mkdtemp(join(tmpdir(), 'maka-bundled-git-missing-')); - try { - await assert.rejects( - resolveBundledGitRuntime({ resourcesRoot: root, platform: 'win32', arch: 'x64' }), - { code: 'bundled_git_unavailable' }, - ); - } finally { - await rm(root, { recursive: true, force: true }); - } -}); - -test('rejects manifest fields that could redirect execution outside resources', async () => { - const fixture = await createFixture({ executableRelativePath: '../system/git.exe' }); - try { - await assert.rejects( - resolveBundledGitRuntime({ - resourcesRoot: fixture.root, - platform: 'win32', - arch: 'x64', - }), - { code: 'bundled_git_manifest_invalid' }, - ); - } finally { - await fixture.remove(); - } -}); - -const executableSha256 = 'sha256:f391158ea86e1e56b2b0c13583821c2b752c2abccd91496d91e025d54ac616e5'; - -async function createFixture(overrides: Record = {}) { - const root = await mkdtemp(join(tmpdir(), 'maka-bundled-git-')); - const executablePath = join(root, 'git', 'cmd', 'git.exe'); - await mkdir(join(root, 'git', 'cmd'), { recursive: true }); - await writeFile(executablePath, 'fake bundled git\n'); - await writeFile( - join(root, 'bundled-git.json'), - `${JSON.stringify({ - schemaVersion: 1, - protocol: 'maka_bundled_git_runtime_v1', - provider: 'desktop/dugite-native', - gitVersion: '2.53.0', - platform: 'win32', - arch: 'x64', - executableRelativePath: 'git/cmd/git.exe', - executableSha256, - sourceArchiveSha256: - 'sha256:f843a87a693bfdabed83b8492bca59db6f64d1168c74d23e2c8dfb7388a97142', - distributionReady: true, - ...overrides, - })}\n`, - ); - return { - root, - executablePath, - remove: () => rm(root, { recursive: true, force: true }), - }; -} diff --git a/packages/runtime-host/src/__tests__/execution-composition-factory.test.ts b/packages/runtime-host/src/__tests__/execution-composition-factory.test.ts new file mode 100644 index 0000000000..672890a3f6 --- /dev/null +++ b/packages/runtime-host/src/__tests__/execution-composition-factory.test.ts @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { + createExecutionRuntimeHostCompositionSource, + type ExecutionRuntimeHostCompositionSourceOptions, +} from '../server/execution-composition-factory.js'; +import type { ExecutionRuntimeHostComposition } from '../server/execution-composition.js'; +import type { RuntimeHostCompositionContext } from '../server/host-kernel.js'; + +test('an execution Host starts without a managed Git runtime', async () => { + const expected = {} as ExecutionRuntimeHostComposition; + let observed: ExecutionRuntimeHostCompositionSourceOptions | undefined; + const source = await createExecutionRuntimeHostCompositionSource( + {}, + { + createComposition: async (_context, options) => { + observed = options; + return expected; + }, + }, + ); + + const actual = await source.create({} as RuntimeHostCompositionContext); + + assert.equal(actual, expected); + assert.deepEqual(observed, {}); +}); diff --git a/packages/runtime-host/src/server/bundled-git-runtime.ts b/packages/runtime-host/src/server/bundled-git-runtime.ts deleted file mode 100644 index f67914153c..0000000000 --- a/packages/runtime-host/src/server/bundled-git-runtime.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { createHash } from 'node:crypto'; -import { lstat, open, readFile, realpath } from 'node:fs/promises'; -import { isAbsolute, join, normalize, relative } from 'node:path'; -import type { VerifiedGitRuntimeInput } from '@maka/storage/managed-workspace-owner'; - -const MANIFEST_KEYS = [ - 'arch', - 'distributionReady', - 'executableRelativePath', - 'executableSha256', - 'gitVersion', - 'platform', - 'protocol', - 'provider', - 'schemaVersion', - 'sourceArchiveSha256', -] as const; -const SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/u; -const VERSION_PATTERN = /^[1-9][0-9]*\.[0-9]+\.[0-9]+(?:[-.][A-Za-z0-9]+)*$/u; - -export type BundledGitRuntimeErrorCode = - | 'bundled_git_unavailable' - | 'bundled_git_manifest_invalid' - | 'bundled_git_platform_mismatch' - | 'bundled_git_integrity_mismatch'; - -export class BundledGitRuntimeError extends Error { - constructor( - readonly code: BundledGitRuntimeErrorCode, - message: string, - options?: ErrorOptions, - ) { - super(message, options); - this.name = 'BundledGitRuntimeError'; - } -} - -export interface ResolveBundledGitRuntimeInput { - readonly resourcesRoot: string; - readonly platform?: NodeJS.Platform; - readonly arch?: string; - readonly manifestPath?: string; -} - -export async function resolveBundledGitRuntime( - input: ResolveBundledGitRuntimeInput, -): Promise { - const platform = input.platform ?? process.platform; - const arch = input.arch ?? process.arch; - try { - const resourcesRoot = normalize(await realpath(input.resourcesRoot)); - const manifestPath = normalize( - await realpath(input.manifestPath ?? join(resourcesRoot, 'bundled-git.json')), - ); - assertWithinRoot(resourcesRoot, manifestPath, 'Bundled Git manifest'); - const manifestInfo = await lstat(manifestPath); - if (!manifestInfo.isFile() || manifestInfo.isSymbolicLink()) { - throw invalidManifest('Bundled Git manifest must be a regular non-symlink file'); - } - const manifest = decodeManifest(JSON.parse(await readFile(manifestPath, 'utf8'))); - if (manifest.platform !== platform || manifest.arch !== arch) { - throw new BundledGitRuntimeError( - 'bundled_git_platform_mismatch', - `Bundled Git targets ${manifest.platform}-${manifest.arch}, not ${platform}-${arch}`, - ); - } - const executablePath = normalize( - join(resourcesRoot, ...manifest.executableRelativePath.split('/')), - ); - assertWithinRoot(resourcesRoot, executablePath, 'Bundled Git executable'); - const executableInfo = await lstat(executablePath); - if (!executableInfo.isFile() || executableInfo.isSymbolicLink()) { - throw new BundledGitRuntimeError( - 'bundled_git_unavailable', - 'Bundled Git executable must be a regular non-symlink file', - ); - } - const canonicalExecutable = normalize(await realpath(executablePath)); - assertWithinRoot(resourcesRoot, canonicalExecutable, 'Bundled Git executable'); - const actualSha256 = await sha256File(canonicalExecutable); - if (actualSha256 !== manifest.executableSha256) { - throw new BundledGitRuntimeError( - 'bundled_git_integrity_mismatch', - `Bundled Git executable digest mismatch: ${canonicalExecutable}`, - ); - } - return { - executablePath: canonicalExecutable, - expectedSha256: manifest.executableSha256, - runtimeIdentitySha256: bundledGitRuntimeIdentity(manifest), - distribution: { - kind: 'dugite_native_v1', - rootPath: normalize(await realpath(join(resourcesRoot, 'git'))), - }, - }; - } catch (error) { - if (error instanceof BundledGitRuntimeError) throw error; - throw new BundledGitRuntimeError( - 'bundled_git_unavailable', - 'Bundled Git runtime is unavailable', - { cause: error }, - ); - } -} - -function bundledGitRuntimeIdentity(manifest: BundledGitManifestV1): `sha256:${string}` { - const identity = JSON.stringify({ - protocol: 'maka_git_runtime_identity_v1', - manifestProtocol: manifest.protocol, - provider: manifest.provider, - gitVersion: manifest.gitVersion, - platform: manifest.platform, - arch: manifest.arch, - executableRelativePath: manifest.executableRelativePath, - executableSha256: manifest.executableSha256, - sourceArchiveSha256: manifest.sourceArchiveSha256, - }); - return `sha256:${createHash('sha256').update(identity).digest('hex')}`; -} - -interface BundledGitManifestV1 { - readonly schemaVersion: 1; - readonly protocol: 'maka_bundled_git_runtime_v1'; - readonly provider: 'desktop/dugite-native'; - readonly gitVersion: string; - readonly platform: NodeJS.Platform; - readonly arch: string; - readonly executableRelativePath: string; - readonly executableSha256: `sha256:${string}`; - readonly sourceArchiveSha256: `sha256:${string}`; - readonly distributionReady: true; -} - -function decodeManifest(input: unknown): BundledGitManifestV1 { - if (!input || typeof input !== 'object' || Array.isArray(input)) { - throw invalidManifest('Bundled Git manifest must be an object'); - } - const value = input as Record; - if ( - Object.keys(value).sort().join('\0') !== [...MANIFEST_KEYS].sort().join('\0') || - value.schemaVersion !== 1 || - value.protocol !== 'maka_bundled_git_runtime_v1' || - value.provider !== 'desktop/dugite-native' || - typeof value.gitVersion !== 'string' || - !VERSION_PATTERN.test(value.gitVersion) || - (value.platform !== 'win32' && value.platform !== 'darwin' && value.platform !== 'linux') || - typeof value.arch !== 'string' || - !/^[a-z0-9_]+$/u.test(value.arch) || - typeof value.executableRelativePath !== 'string' || - !isSafeRelativePath(value.executableRelativePath) || - typeof value.executableSha256 !== 'string' || - !SHA256_PATTERN.test(value.executableSha256) || - typeof value.sourceArchiveSha256 !== 'string' || - !SHA256_PATTERN.test(value.sourceArchiveSha256) || - value.distributionReady !== true - ) { - throw invalidManifest('Bundled Git manifest is invalid'); - } - return value as unknown as BundledGitManifestV1; -} - -function isSafeRelativePath(value: string): boolean { - if (!value || isAbsolute(value) || value.includes('\\')) return false; - const segments = value.split('/'); - return segments.every((segment) => segment.length > 0 && segment !== '.' && segment !== '..'); -} - -function assertWithinRoot(root: string, target: string, label: string): void { - const rel = relative(root, target); - if (rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))) return; - throw invalidManifest(`${label} escapes the packaged resources root`); -} - -function invalidManifest(message: string): BundledGitRuntimeError { - return new BundledGitRuntimeError('bundled_git_manifest_invalid', message); -} - -async function sha256File(path: string): Promise<`sha256:${string}`> { - const hash = createHash('sha256'); - const file = await open(path, 'r'); - try { - const buffer = Buffer.allocUnsafe(64 * 1024); - let position = 0; - while (true) { - const { bytesRead } = await file.read(buffer, 0, buffer.length, position); - if (bytesRead === 0) break; - hash.update(buffer.subarray(0, bytesRead)); - position += bytesRead; - } - return `sha256:${hash.digest('hex')}`; - } finally { - await file.close(); - } -} diff --git a/packages/runtime-host/src/server/execution-candidate.ts b/packages/runtime-host/src/server/execution-candidate.ts index 0aa666408a..c863021d5f 100644 --- a/packages/runtime-host/src/server/execution-candidate.ts +++ b/packages/runtime-host/src/server/execution-candidate.ts @@ -14,8 +14,6 @@ export type ExecutionRuntimeHostCandidateResult = InteractiveRuntimeHostCandidat export interface ExecutionRuntimeHostCandidateOptions extends InteractiveRuntimeHostCandidateOptions { readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; - /** Packaged resource root containing bundled-git.json and the Git toolchain. */ - readonly bundledGitResourcesRoot?: string; } export type ExecutionRuntimeHostCandidateDependencies = ExecutionRuntimeHostCompositionDependencies; diff --git a/packages/runtime-host/src/server/execution-composition-factory.ts b/packages/runtime-host/src/server/execution-composition-factory.ts index a9a2d76a3f..a0ccc4cd5e 100644 --- a/packages/runtime-host/src/server/execution-composition-factory.ts +++ b/packages/runtime-host/src/server/execution-composition-factory.ts @@ -1,5 +1,4 @@ import type { VerifiedGitRuntimeInput } from '@maka/storage/managed-workspace-owner'; -import { resolveBundledGitRuntime } from './bundled-git-runtime.js'; import type { PublishedProjectDirectoryRoot } from './project-directory-authority.js'; import { createExecutionRuntimeHostComposition, @@ -13,7 +12,6 @@ import { export interface ExecutionRuntimeHostCompositionSourceOptions { readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; - readonly bundledGitResourcesRoot?: string; readonly projectDirectoryRoots?: readonly PublishedProjectDirectoryRoot[]; } @@ -28,14 +26,10 @@ export async function createExecutionRuntimeHostCompositionSource( options: ExecutionRuntimeHostCompositionSourceOptions, dependencies: ExecutionRuntimeHostCompositionDependencies = {}, ): Promise { - if (options.managedWorkspaceGitRuntime && options.bundledGitResourcesRoot) { - throw new Error('Managed workspace Git runtime must have exactly one authority'); - } - const managedWorkspaceGitRuntime = options.bundledGitResourcesRoot - ? await resolveBundledGitRuntime({ resourcesRoot: options.bundledGitResourcesRoot }) - : options.managedWorkspaceGitRuntime; const compositionOptions = { - ...(managedWorkspaceGitRuntime ? { managedWorkspaceGitRuntime } : {}), + ...(options.managedWorkspaceGitRuntime + ? { managedWorkspaceGitRuntime: options.managedWorkspaceGitRuntime } + : {}), ...(options.projectDirectoryRoots ? { projectDirectoryRoots: options.projectDirectoryRoots } : {}), diff --git a/packages/runtime-host/src/server/execution-service.ts b/packages/runtime-host/src/server/execution-service.ts index ad39cc17b3..4377eef208 100644 --- a/packages/runtime-host/src/server/execution-service.ts +++ b/packages/runtime-host/src/server/execution-service.ts @@ -13,7 +13,6 @@ import type { PublishedProjectDirectoryRoot } from './project-directory-authorit export interface ExecutionRuntimeHostServiceOptions { readonly rootPath: string; readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; - readonly bundledGitResourcesRoot?: string; readonly projectDirectoryRoots?: readonly PublishedProjectDirectoryRoot[]; readonly handshakeTimeoutMs?: number; readonly shutdownGraceMs?: number; diff --git a/packages/storage/src/__tests__/bundled-git-workspace-smoke.test.ts b/packages/storage/src/__tests__/bundled-git-workspace-smoke.test.ts deleted file mode 100644 index b4e53f24af..0000000000 --- a/packages/storage/src/__tests__/bundled-git-workspace-smoke.test.ts +++ /dev/null @@ -1,166 +0,0 @@ -import assert from 'node:assert/strict'; -import { execFile } from 'node:child_process'; -import { createHash } from 'node:crypto'; -import { createReadStream } from 'node:fs'; -import { - chmod, - copyFile, - mkdir, - mkdtemp, - readFile, - readdir, - rm, - stat, - symlink, - writeFile, -} from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { dirname, join, resolve } from 'node:path'; -import { promisify } from 'node:util'; -import test from 'node:test'; -import { bundledGitEnvironment } from '../dugite-native-environment.js'; -import { createGitWorkspaceService } from '../git-workspace-service.js'; - -const execFileAsync = promisify(execFile); - -test('uses the installed runtime and rejects entry-binary replacement before the next operation', async () => { - const repoRoot = resolve(import.meta.dirname, '..', '..', '..', '..'); - const installedGitRoot = join(repoRoot, 'node_modules', 'dugite', 'git'); - const installedExecutablePath = - process.platform === 'win32' - ? join(installedGitRoot, 'cmd', 'git.exe') - : join(installedGitRoot, 'bin', 'git'); - const gitRoot = await mkdtemp(join(tmpdir(), 'maka-bundled-git-runtime-')); - const executablePath = await materializeIsolatedRuntime( - installedGitRoot, - gitRoot, - process.platform === 'win32' ? 'cmd/git.exe' : 'bin/git', - ); - const sourceRoot = await mkdtemp(join(tmpdir(), 'maka-bundled-git-source-')); - const storageRoot = await mkdtemp(join(tmpdir(), 'maka-bundled-git-storage-')); - const homePath = await mkdtemp(join(tmpdir(), 'maka-bundled-git-home-')); - const env = { - HOME: homePath, - XDG_CONFIG_HOME: join(homePath, 'xdg'), - GIT_CONFIG_NOSYSTEM: '1', - GIT_TERMINAL_PROMPT: '0', - ...bundledGitEnvironment({ - platform: process.platform, - arch: process.arch, - rootPath: installedGitRoot, - executablePath: installedExecutablePath, - }), - }; - try { - await runGit(installedExecutablePath, env, ['-C', sourceRoot, 'init', '--quiet']); - await runGit(installedExecutablePath, env, [ - '-C', - sourceRoot, - 'config', - 'user.name', - 'Maka Test', - ]); - await runGit(installedExecutablePath, env, [ - '-C', - sourceRoot, - 'config', - 'user.email', - 'test@maka.invalid', - ]); - await writeFile(join(sourceRoot, 'README.md'), 'bundled Git runtime\n'); - await runGit(installedExecutablePath, env, ['-C', sourceRoot, 'add', 'README.md']); - await runGit(installedExecutablePath, env, [ - '-C', - sourceRoot, - 'commit', - '--quiet', - '-m', - 'baseline', - ]); - - const service = createGitWorkspaceService({ - storageRoot, - gitRuntime: { - executablePath, - expectedSha256: await sha256File(executablePath), - runtimeIdentitySha256: `sha256:${'1'.repeat(64)}`, - distribution: { kind: 'dugite_native_v1', rootPath: gitRoot }, - }, - }); - const binding = await service.createManagedWorkspaceFromSource({ - repositoryId: 'repository_11111111111111111111111111111111', - workspaceId: 'workspace_22222222222222222222222222222222', - workspaceEpochId: 'epoch_33333333333333333333333333333333', - workspaceInstanceId: 'instance_44444444444444444444444444444444', - sourceRoot, - }); - - assert.equal( - await readFile(join(binding.worktreePath, 'README.md'), 'utf8'), - 'bundled Git runtime\n', - ); - assert.equal(binding.gitRuntimeSha256, `sha256:${'1'.repeat(64)}`); - - await writeFile(executablePath, 'tampered after successful workspace creation\n'); - await assert.rejects( - service.inspectManagedWorkspace(binding), - (error: unknown) => - error instanceof Error && - 'code' in error && - error.code === 'git_runtime_integrity_mismatch', - ); - } finally { - await Promise.all( - [sourceRoot, storageRoot, homePath, gitRoot].map((path) => - rm(path, { recursive: true, force: true }), - ), - ); - } -}); - -async function materializeIsolatedRuntime( - sourceRoot: string, - targetRoot: string, - executableRelativePath: string, -): Promise { - const executablePath = join(targetRoot, ...executableRelativePath.split('/')); - const executableDirectory = executableRelativePath.split('/')[0] ?? ''; - await mkdir(dirname(executablePath), { recursive: true }); - await copyFile(join(sourceRoot, ...executableRelativePath.split('/')), executablePath); - if (process.platform !== 'win32') { - await chmod( - executablePath, - (await stat(join(sourceRoot, ...executableRelativePath.split('/')))).mode & 0o7777, - ); - } - for (const entry of await readdir(sourceRoot, { withFileTypes: true })) { - if (entry.name === executableDirectory) continue; - const source = join(sourceRoot, entry.name); - const target = join(targetRoot, entry.name); - if (entry.isDirectory()) { - await symlink(source, target, process.platform === 'win32' ? 'junction' : 'dir'); - } else if (entry.isFile()) { - await copyFile(source, target); - } - } - return executablePath; -} - -async function runGit( - executablePath: string, - env: NodeJS.ProcessEnv, - args: readonly string[], -): Promise { - await execFileAsync(executablePath, args, { - env, - encoding: 'utf8', - timeout: 30_000, - windowsHide: true, - }); -} - -async function sha256File(path: string): Promise<`sha256:${string}`> { - const hash = createHash('sha256'); - for await (const chunk of createReadStream(path)) hash.update(chunk); - return `sha256:${hash.digest('hex')}`; -} diff --git a/packages/storage/src/__tests__/dugite-native-environment.test.ts b/packages/storage/src/__tests__/dugite-native-environment.test.ts deleted file mode 100644 index 1ef22398fa..0000000000 --- a/packages/storage/src/__tests__/dugite-native-environment.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { bundledGitEnvironment } from '../dugite-native-environment.js'; - -test('constructs a Windows MinGit environment without a system PATH', () => { - assert.deepEqual( - bundledGitEnvironment({ - platform: 'win32', - arch: 'arm64', - rootPath: 'C:\\Maka\\resources\\git', - executablePath: 'C:\\Maka\\resources\\git\\cmd\\git.exe', - }), - { - PATH: [ - 'C:\\Maka\\resources\\git\\cmd', - 'C:\\Maka\\resources\\git\\clangarm64\\bin', - 'C:\\Maka\\resources\\git\\clangarm64\\usr\\bin', - ].join(';'), - GIT_EXEC_PATH: 'C:\\Maka\\resources\\git\\clangarm64\\libexec\\git-core', - }, - ); -}); - -test('constructs relocatable macOS and Linux Git environments', () => { - assert.deepEqual( - bundledGitEnvironment({ - platform: 'darwin', - arch: 'arm64', - rootPath: '/Applications/Maka.app/Contents/Resources/git', - executablePath: '/Applications/Maka.app/Contents/Resources/git/bin/git', - }), - { - PATH: '/Applications/Maka.app/Contents/Resources/git/bin', - GIT_EXEC_PATH: '/Applications/Maka.app/Contents/Resources/git/libexec/git-core', - GIT_TEMPLATE_DIR: '/Applications/Maka.app/Contents/Resources/git/share/git-core/templates', - }, - ); - assert.deepEqual( - bundledGitEnvironment({ - platform: 'linux', - arch: 'x64', - rootPath: '/opt/maka/resources/git', - executablePath: '/opt/maka/resources/git/bin/git', - }), - { - PATH: '/opt/maka/resources/git/bin', - GIT_EXEC_PATH: '/opt/maka/resources/git/libexec/git-core', - GIT_TEMPLATE_DIR: '/opt/maka/resources/git/share/git-core/templates', - PREFIX: '/opt/maka/resources/git', - GIT_SSL_CAINFO: '/opt/maka/resources/git/ssl/cacert.pem', - }, - ); -}); diff --git a/packages/storage/src/dugite-native-environment.ts b/packages/storage/src/dugite-native-environment.ts deleted file mode 100644 index 287cd0d73c..0000000000 --- a/packages/storage/src/dugite-native-environment.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { posix, win32 } from 'node:path'; - -export interface BundledGitEnvironmentInput { - readonly platform: NodeJS.Platform; - readonly arch: string; - readonly rootPath: string; - readonly executablePath: string; -} - -export function bundledGitEnvironment(input: BundledGitEnvironmentInput): NodeJS.ProcessEnv { - const path = input.platform === 'win32' ? win32 : posix; - const env: NodeJS.ProcessEnv = { - PATH: path.dirname(input.executablePath), - }; - if (input.platform === 'win32') { - const platformRoot = path.join( - input.rootPath, - input.arch === 'x64' ? 'mingw64' : input.arch === 'arm64' ? 'clangarm64' : 'mingw32', - ); - env.PATH = [ - path.dirname(input.executablePath), - path.join(platformRoot, 'bin'), - path.join(platformRoot, 'usr', 'bin'), - ].join(';'); - env.GIT_EXEC_PATH = path.join(platformRoot, 'libexec', 'git-core'); - return env; - } - env.GIT_EXEC_PATH = path.join(input.rootPath, 'libexec', 'git-core'); - env.GIT_TEMPLATE_DIR = path.join(input.rootPath, 'share', 'git-core', 'templates'); - if (input.platform === 'linux') { - env.PREFIX = input.rootPath; - env.GIT_SSL_CAINFO = path.join(input.rootPath, 'ssl', 'cacert.pem'); - } - return env; -} diff --git a/packages/storage/src/git-workspace-service.ts b/packages/storage/src/git-workspace-service.ts index a1d07e2e6c..6aba50ae87 100644 --- a/packages/storage/src/git-workspace-service.ts +++ b/packages/storage/src/git-workspace-service.ts @@ -14,7 +14,6 @@ import { import { dirname, isAbsolute, join, normalize, relative, resolve } from 'node:path'; import { promisify } from 'node:util'; import { withArtifactWriterLock } from './artifact-writer-lock.js'; -import { bundledGitEnvironment } from './dugite-native-environment.js'; import { registerManagedBaselineReceiptAuthorityInternal } from './managed-baseline-receipt-authority-internal.js'; const execFileAsync = promisify(execFile); @@ -141,27 +140,11 @@ export class GitWorkspaceServiceError extends Error { } } -interface GitRuntimeExecutableIdentity { +export interface VerifiedGitRuntimeInput { readonly executablePath: string; readonly expectedSha256: `sha256:${string}`; } -export type VerifiedGitRuntimeInput = GitRuntimeExecutableIdentity & - ( - | { - readonly distribution?: undefined; - readonly runtimeIdentitySha256?: undefined; - } - | { - readonly distribution: { - readonly kind: 'dugite_native_v1'; - readonly rootPath: string; - }; - /** Stable identity of the complete declared distribution. */ - readonly runtimeIdentitySha256: `sha256:${string}`; - } - ); - export interface CreateGitWorkspaceServiceInput { readonly storageRoot: string; readonly gitRuntime: VerifiedGitRuntimeInput; @@ -1610,32 +1593,12 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { class VerifiedGitRuntime { constructor(private readonly input: VerifiedGitRuntimeInput) { - if ( - !isAbsolute(input.executablePath) || - !SHA256_PATTERN.test(input.expectedSha256) || - (input.distribution !== undefined && !SHA256_PATTERN.test(input.runtimeIdentitySha256 ?? '')) - ) { + if (!isAbsolute(input.executablePath) || !SHA256_PATTERN.test(input.expectedSha256)) { throw new GitWorkspaceServiceError( 'git_runtime_unavailable', 'Managed workspace requires an absolute Git executable and SHA-256 digest', ); } - if (input.distribution) { - const rootPath = input.distribution.rootPath; - const executableRelativePath = relative(rootPath, input.executablePath); - if ( - !isAbsolute(rootPath) || - executableRelativePath === '' || - executableRelativePath === '..' || - executableRelativePath.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) || - isAbsolute(executableRelativePath) - ) { - throw new GitWorkspaceServiceError( - 'git_runtime_unavailable', - 'Bundled Git executable must belong to its declared distribution root', - ); - } - } } verify(): Promise<{ executablePath: string; digest: `sha256:${string}` }> { @@ -1810,7 +1773,7 @@ class VerifiedGitRuntime { } return { executablePath, - digest: this.input.runtimeIdentitySha256 ?? executableDigest, + digest: executableDigest, }; } catch (error) { if (error instanceof GitWorkspaceServiceError) throw error; @@ -2839,17 +2802,6 @@ function isolatedGitEnvironment( LC_ALL: 'C', PATH: dirname(executablePath), }; - if (input.distribution?.kind === 'dugite_native_v1') { - Object.assign( - env, - bundledGitEnvironment({ - platform: process.platform, - arch: process.arch, - rootPath: input.distribution.rootPath, - executablePath, - }), - ); - } for (const name of ['SystemRoot', 'WINDIR', 'COMSPEC', 'TMP', 'TEMP', 'TMPDIR']) { if (process.env[name]) env[name] = process.env[name]; } diff --git a/scripts/ci-test-plan.mjs b/scripts/ci-test-plan.mjs index 20395ec28d..b5e248a5da 100644 --- a/scripts/ci-test-plan.mjs +++ b/scripts/ci-test-plan.mjs @@ -19,7 +19,6 @@ const FULL_SUITE_FILES = new Set([ const RELEASE_CONTRACT_FILES = new Set([ 'apps/desktop/build/entitlements.mac.inherit.plist', 'apps/desktop/build/entitlements.mac.plist', - 'apps/desktop/bundled-git.json', 'apps/desktop/bundled-tools.json', 'apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt', 'apps/desktop/electron-builder.config.mjs', @@ -32,8 +31,6 @@ const RELEASE_CONTRACT_FILES = new Set([ 'scripts/package-macos-arm64-cli.mjs', 'scripts/package-windows-autoupdate-next.mjs', 'scripts/package-windows-x64.mjs', - 'scripts/prepare-bundled-git-source.mjs', - 'scripts/prepare-bundled-git.mjs', 'scripts/prepare-windows-upgrade-baseline.mjs', 'scripts/generate-third-party-notices.test.mjs', 'scripts/prepare-windows-upgrade-baseline.test.mjs', diff --git a/scripts/package-macos-arm64.mjs b/scripts/package-macos-arm64.mjs index 5f08206447..beb519121b 100644 --- a/scripts/package-macos-arm64.mjs +++ b/scripts/package-macos-arm64.mjs @@ -72,7 +72,6 @@ export async function packageMacosArm64({ await run('npm', ['run', 'clean']); await run('npm', ['run', 'build']); - await run('npm', ['run', 'prepare:bundled-git']); await run('npm', ['run', 'check:release']); await remove(releaseDirectory, { recursive: true, force: true }); await run('npm', ['--workspace', '@maka/desktop', 'run', 'package:macos-arm64']); diff --git a/scripts/package-windows-x64.mjs b/scripts/package-windows-x64.mjs index 3856d354c0..c98f2907b1 100644 --- a/scripts/package-windows-x64.mjs +++ b/scripts/package-windows-x64.mjs @@ -88,7 +88,6 @@ export async function packageWindowsX64({ await run('npm', ['run', 'check:windows-cargo-notices']); await mkdir(sandboxResourceDirectory, { recursive: true }); await copyFile(sandboxBinaryPath, sandboxResourcePath); - await run('npm', ['run', 'prepare:bundled-git']); await run('npm', ['run', 'check:release']); await remove(releaseDirectory, { recursive: true, force: true }); await run('npm', ['--workspace', '@maka/desktop', 'run', 'package:windows-x64']); diff --git a/scripts/prepare-bundled-git-source.mjs b/scripts/prepare-bundled-git-source.mjs deleted file mode 100644 index a480999ecf..0000000000 --- a/scripts/prepare-bundled-git-source.mjs +++ /dev/null @@ -1,180 +0,0 @@ -import { createHash } from 'node:crypto'; -import { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; -import { dirname, isAbsolute, join, parse, resolve } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; - -const DUGITE_VERSION = '3.2.2'; -const DUGITE_NATIVE_RELEASE = 'v2.53.0-3'; -const DUGITE_NATIVE_COMMIT = 'f49d0098409aa243de8b9162127025ab0bb07a88'; -const DUGITE_NATIVE_BUILD = DUGITE_NATIVE_COMMIT.slice(0, 7); - -export const BUNDLED_GIT_SOURCE_COMPONENTS = Object.freeze([ - component('dugite-native', 'desktop/dugite-native', DUGITE_NATIVE_COMMIT, 'GPL-2.0-only'), - component('git', 'git/git', '67ad42147a7acc2af6074753ebd03d904476118f', 'GPL-2.0-only'), - component( - 'git-for-windows', - 'git-for-windows/git', - 'f8165afd89b0c190677a093f20894f5fce12f97a', - 'GPL-2.0-only', - ), - component('git-lfs', 'git-lfs/git-lfs', 'b84b33847fe6458f36ef521534dc0eac953cb379', 'MIT'), - component( - 'git-credential-manager', - 'git-ecosystem/git-credential-manager', - '5fa7116896c82164996a609accd1c5ad90fe730a', - 'MIT', - ), - component( - 'sha1collisiondetection', - 'cr-marcstevens/sha1collisiondetection', - '855827c583bc30645ba427885caa40c5b81764d2', - 'MIT', - ), -]); - -export async function prepareBundledGitSourceMaterials({ - dugiteRoot, - outputDirectory, - fetchImpl = globalThis.fetch, -}) { - if (typeof fetchImpl !== 'function') { - throw new Error('A Fetch implementation is required to prepare bundled Git source materials.'); - } - const packageJson = JSON.parse(await readFile(join(dugiteRoot, 'package.json'), 'utf8')); - if (packageJson.version !== DUGITE_VERSION) { - throw new Error(`Expected dugite ${DUGITE_VERSION}, found ${String(packageJson.version)}.`); - } - const embeddedGit = JSON.parse( - await readFile(join(dugiteRoot, 'script', 'embedded-git.json'), 'utf8'), - ); - assertRuntimeProvenance(embeddedGit); - - const finalDirectory = resolve(outputDirectory); - assertSafeOutputDirectory(finalDirectory); - await assertReplaceableOutputDirectory(finalDirectory); - const stagingDirectory = `${finalDirectory}.staging-${process.pid}`; - await rm(stagingDirectory, { recursive: true, force: true }); - await mkdir(stagingDirectory, { recursive: true }); - - try { - const materialized = []; - for (const source of BUNDLED_GIT_SOURCE_COMPONENTS) { - const response = await fetchImpl(source.sourceUrl); - if (!response?.ok) { - throw new Error( - `Unable to download ${source.name} source (${response?.status ?? 'unknown status'}).`, - ); - } - const content = Buffer.from(await response.arrayBuffer()); - await writeFile(join(stagingDirectory, source.archiveFile), content); - materialized.push({ - ...source, - sha256: `sha256:${createHash('sha256').update(content).digest('hex')}`, - bytes: content.byteLength, - }); - } - - const manifest = { - protocol: 'maka_bundled_git_source_materials_v1', - dugiteVersion: DUGITE_VERSION, - dugiteNativeRelease: DUGITE_NATIVE_RELEASE, - dugiteNativeCommit: DUGITE_NATIVE_COMMIT, - components: materialized, - }; - await writeFile( - join(stagingDirectory, 'SOURCE_MANIFEST.json'), - `${JSON.stringify(manifest, null, 2)}\n`, - ); - await writeFile(join(stagingDirectory, 'README.txt'), sourceReadme()); - await rm(finalDirectory, { recursive: true, force: true }); - await rename(stagingDirectory, finalDirectory); - return manifest; - } catch (error) { - await rm(stagingDirectory, { recursive: true, force: true }); - throw error; - } -} - -function component(name, repository, commit, license) { - return Object.freeze({ - name, - repository: `https://github.com/${repository}`, - commit, - license, - sourceUrl: `https://codeload.github.com/${repository}/tar.gz/${commit}`, - archiveFile: `${name}-${commit}.tar.gz`, - }); -} - -function assertRuntimeProvenance(embeddedGit) { - const records = Object.values(embeddedGit); - if (records.length === 0) { - throw new Error('dugite embedded-git.json contains no runtime archives.'); - } - const expectedReleaseSegment = `/releases/download/${DUGITE_NATIVE_RELEASE}/`; - for (const record of records) { - if ( - typeof record?.name !== 'string' || - typeof record?.url !== 'string' || - !record.name.includes(`-${DUGITE_NATIVE_BUILD}-`) || - !record.url.includes(expectedReleaseSegment) - ) { - throw new Error('Bundled Git runtime provenance does not match source-material pins.'); - } - } -} - -function assertSafeOutputDirectory(outputDirectory) { - if (!isAbsolute(outputDirectory) || outputDirectory === parse(outputDirectory).root) { - throw new Error(`Unsafe bundled Git source output directory: ${outputDirectory}`); - } - if (dirname(outputDirectory) === outputDirectory) { - throw new Error(`Unsafe bundled Git source output directory: ${outputDirectory}`); - } -} - -async function assertReplaceableOutputDirectory(outputDirectory) { - let info; - try { - info = await lstat(outputDirectory); - } catch (error) { - if (error?.code === 'ENOENT') return; - throw error; - } - try { - if (!info.isDirectory() || info.isSymbolicLink()) throw new Error('not an owned directory'); - const manifest = JSON.parse( - await readFile(join(outputDirectory, 'SOURCE_MANIFEST.json'), 'utf8'), - ); - if (manifest.protocol !== 'maka_bundled_git_source_materials_v1') { - throw new Error('unexpected source-material protocol'); - } - } catch (error) { - throw new Error( - `Bundled Git source output is not owned by the bundled Git source-material protocol: ${outputDirectory}`, - { cause: error }, - ); - } -} - -function sourceReadme() { - return `Maka bundled Git source materials\n=================================\n\nThese archives are pinned, machine-readable source materials for the Git runtime\nshipped with Maka. Their component licenses are recorded in SOURCE_MANIFEST.json.\nBundling separate Git command-line programs does not change Maka's Apache-2.0 license.\n\nThese convenience archives do not replace Maka's written GPL source offer. See the\nSOURCE_OFFER.txt distributed with the application for the request procedure and the\ncomplete-corresponding-source commitment.\n`; -} - -async function main() { - const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); - const outputDirectory = resolve( - process.argv[2] ?? join(repositoryRoot, 'apps', 'desktop', 'release-sources', 'bundled-git'), - ); - const manifest = await prepareBundledGitSourceMaterials({ - dugiteRoot: join(repositoryRoot, 'node_modules', 'dugite'), - outputDirectory, - }); - console.log( - `[bundled-git-source] wrote ${manifest.components.length} source archives to ${outputDirectory}`, - ); -} - -if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) { - await main(); -} diff --git a/scripts/prepare-bundled-git.mjs b/scripts/prepare-bundled-git.mjs deleted file mode 100644 index 0ed9660290..0000000000 --- a/scripts/prepare-bundled-git.mjs +++ /dev/null @@ -1,122 +0,0 @@ -import { execFile } from 'node:child_process'; -import { createHash } from 'node:crypto'; -import { createReadStream } from 'node:fs'; -import { lstat, mkdir, readFile, writeFile } from 'node:fs/promises'; -import { dirname, join, relative, resolve, sep } from 'node:path'; -import { promisify } from 'node:util'; -import { fileURLToPath, pathToFileURL } from 'node:url'; - -const execFileAsync = promisify(execFile); -const repoRoot = dirname(dirname(fileURLToPath(import.meta.url))); -const expectedDugiteVersion = '3.2.2'; -const sha256Pattern = /^[a-f0-9]{64}$/u; - -export async function prepareBundledGit({ - dugiteRoot = join(repoRoot, 'node_modules', 'dugite'), - gitLicensePath = join(repoRoot, 'apps', 'desktop', 'resources', 'licenses', 'git', 'LICENSE.txt'), - outputPath = join(repoRoot, 'apps', 'desktop', 'bundled-git.json'), - platform = process.platform, - arch = process.arch, - runGit = runGitVersion, -} = {}) { - const packageManifest = JSON.parse(await readFile(join(dugiteRoot, 'package.json'), 'utf8')); - if (packageManifest.name !== 'dugite' || packageManifest.version !== expectedDugiteVersion) { - throw new Error(`Bundled Git preparation requires dugite ${expectedDugiteVersion}.`); - } - await requireRegularFile(join(dugiteRoot, 'LICENSE'), 'dugite license'); - await requireRegularFile(gitLicensePath, 'packaged Git license'); - - const embeddedGit = JSON.parse( - await readFile(join(dugiteRoot, 'script', 'embedded-git.json'), 'utf8'), - ); - const archive = embeddedGit[`${platform}-${arch}`]; - if ( - !archive || - typeof archive !== 'object' || - typeof archive.name !== 'string' || - typeof archive.url !== 'string' || - typeof archive.checksum !== 'string' || - !sha256Pattern.test(archive.checksum) - ) { - throw new Error( - `dugite ${expectedDugiteVersion} does not provide a pinned Git archive for ${platform}-${arch}.`, - ); - } - const gitVersion = parseArchiveGitVersion(archive.name); - const executableRelativePath = platform === 'win32' ? 'git/cmd/git.exe' : 'git/bin/git'; - const executablePath = resolve(dugiteRoot, ...executableRelativePath.split('/')); - assertWithin(dugiteRoot, executablePath); - await requireRegularFile(executablePath, 'bundled Git executable'); - - const reportedVersion = await runGit(executablePath); - if ( - !new RegExp(`^git version ${escapeRegExp(gitVersion)}(?:[.\\s]|$)`, 'u').test( - reportedVersion.trim(), - ) - ) { - throw new Error( - `Bundled Git executable reported ${JSON.stringify(reportedVersion.trim())}; expected Git ${gitVersion}.`, - ); - } - - const manifest = { - schemaVersion: 1, - protocol: 'maka_bundled_git_runtime_v1', - provider: 'desktop/dugite-native', - gitVersion, - platform, - arch, - executableRelativePath, - executableSha256: await sha256File(executablePath), - sourceArchiveSha256: `sha256:${archive.checksum}`, - distributionReady: true, - }; - await mkdir(dirname(outputPath), { recursive: true }); - await writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\n`); - return manifest; -} - -async function runGitVersion(executablePath) { - const { stdout } = await execFileAsync(executablePath, ['--version'], { - encoding: 'utf8', - timeout: 15_000, - windowsHide: true, - }); - return stdout; -} - -function parseArchiveGitVersion(name) { - const match = /^dugite-native-v(\d+\.\d+\.\d+)-/u.exec(name); - if (!match) throw new Error(`dugite embedded Git archive name is invalid: ${name}`); - return match[1]; -} - -async function requireRegularFile(path, label) { - const info = await lstat(path); - if (!info.isFile() || info.isSymbolicLink()) { - throw new Error(`${label} must be a regular non-symlink file: ${path}`); - } -} - -function assertWithin(root, target) { - const rel = relative(resolve(root), resolve(target)); - if (rel === '' || (!rel.startsWith(`..${sep}`) && rel !== '..')) return; - throw new Error(`Bundled Git executable escapes dugite root: ${target}`); -} - -async function sha256File(path) { - const hash = createHash('sha256'); - for await (const chunk of createReadStream(path)) hash.update(chunk); - return `sha256:${hash.digest('hex')}`; -} - -function escapeRegExp(value) { - return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); -} - -if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - const manifest = await prepareBundledGit(); - console.log( - `Prepared bundled Git ${manifest.gitVersion} for ${manifest.platform}-${manifest.arch}.`, - ); -} diff --git a/scripts/product-release-identity.mjs b/scripts/product-release-identity.mjs index 98942ddabb..f3081476f4 100644 --- a/scripts/product-release-identity.mjs +++ b/scripts/product-release-identity.mjs @@ -102,7 +102,6 @@ export function resolveProductReleaseIdentity({ const exe = `Maka-${version}-win-x64.exe`; const windowsZip = `Maka-${version}-win-x64.zip`; const cliArchive = `Maka-${version}-cli-mac-arm64.zip`; - const sourceArchive = `Maka-${version}-bundled-git-source.tar.gz`; const artifacts = { 'desktop-macos': [dmg, `${dmg}.sha256`, macZip, `${macZip}.blockmap`, 'latest-mac.yml'], 'desktop-windows': [ @@ -114,7 +113,6 @@ export function resolveProductReleaseIdentity({ 'latest.yml', ], 'cli-macos-arm64': [cliArchive, `${cliArchive}.sha256`], - source: [sourceArchive, `${sourceArchive}.sha256`], }; return { @@ -126,7 +124,6 @@ export function resolveProductReleaseIdentity({ dmg, exe, cliArchive, - sourceArchive, artifacts, }; } @@ -172,7 +169,6 @@ function githubOutputEntries(identity) { dmg: identity.dmg, exe: identity.exe, cli_archive: identity.cliArchive, - source_archive: identity.sourceArchive, node_version: identity.nodeVersion, node_archive: identity.nodeArchive, node_archive_sha256: identity.nodeArchiveSha256, diff --git a/scripts/product-release.test.mjs b/scripts/product-release.test.mjs index 95e01023f8..1c94c8d66f 100644 --- a/scripts/product-release.test.mjs +++ b/scripts/product-release.test.mjs @@ -76,7 +76,7 @@ test('one root version defines every product artifact from one source commit', ( assert.equal(identity.dmg, 'Maka-1.2.3-mac-arm64.dmg'); assert.equal(identity.exe, 'Maka-1.2.3-win-x64.exe'); assert.equal(identity.cliArchive, 'Maka-1.2.3-cli-mac-arm64.zip'); - assert.equal(identity.sourceArchive, 'Maka-1.2.3-bundled-git-source.tar.gz'); + assert.equal(Object.hasOwn(identity, 'sourceArchive'), false); assert.deepEqual(identity.artifacts, { 'desktop-macos': [ 'Maka-1.2.3-mac-arm64.dmg', @@ -94,7 +94,6 @@ test('one root version defines every product artifact from one source commit', ( 'latest.yml', ], 'cli-macos-arm64': ['Maka-1.2.3-cli-mac-arm64.zip', 'Maka-1.2.3-cli-mac-arm64.zip.sha256'], - source: ['Maka-1.2.3-bundled-git-source.tar.gz', 'Maka-1.2.3-bundled-git-source.tar.gz.sha256'], }); }); @@ -156,6 +155,26 @@ test('Desktop packaging derives the Runtime Host setup package from product mani ]); }); +test('Desktop packaging does not distribute the retired bundled Git runtime', () => { + const resources = desktopBuilderConfig.extraResources.map(({ from, to }) => ({ from, to })); + assert.equal( + resources.some(({ from }) => from.includes('dugite')), + false, + ); + assert.equal( + resources.some(({ to }) => to === 'git' || to.startsWith('licenses/git')), + false, + ); + assert.equal( + resources.some(({ to }) => to === 'bundled-git.json'), + false, + ); + assert.equal( + resources.some(({ to }) => to.startsWith('licenses/dugite')), + false, + ); +}); + test('the packaged-app probe rejects a mismatched Runtime Host setup package', async () => { const fixture = await mkdtemp(join(tmpdir(), 'maka-packaged-manifest-')); try { @@ -534,12 +553,7 @@ test('one product workflow gates one draft release on every required artifact', assert.equal(workflow.permissions.contents, 'read'); assert.equal(jobs.publish.permissions.contents, 'write'); - assert.deepEqual(jobs.publish.needs, [ - 'release-identity', - 'desktop', - 'cli-macos-arm64', - 'source', - ]); + assert.deepEqual(jobs.publish.needs, ['release-identity', 'desktop', 'cli-macos-arm64']); assert.equal(jobs.publish.if, undefined); assert.equal(Object.hasOwn(jobs, 'npm'), false); assert.equal(workflow.on.workflow_dispatch.inputs.source_reference_tag.required, true); @@ -558,7 +572,8 @@ test('one product workflow gates one draft release on every required artifact', assert.match(liveSourceAuthority, /git fetch --force --no-tags origin/u); assert.match(liveSourceAuthority, /git rev-parse.*\^\{commit\}/u); assert.match(liveSourceAuthority, /git merge-base --is-ancestor/u); - for (const name of ['desktop', 'cli-macos-arm64', 'source', 'publish']) { + assert.equal(Object.hasOwn(jobs, 'source'), false); + for (const name of ['desktop', 'cli-macos-arm64', 'publish']) { const checkout = jobs[name].steps.find((step) => String(step.uses).startsWith('actions/checkout@'), ); @@ -575,7 +590,6 @@ test('one product workflow gates one draft release on every required artifact', for (const [jobName, group] of [ ['desktop', 'desktop-${{ matrix.platform }}'], ['cli-macos-arm64', 'cli-macos-arm64'], - ['source', 'source'], ]) { const stage = jobs[jobName].steps.find( (step) => step.name === 'Stage the exact product artifact group', diff --git a/scripts/verify-packaged-app.mjs b/scripts/verify-packaged-app.mjs index ab4446efb7..2f06bdf066 100644 --- a/scripts/verify-packaged-app.mjs +++ b/scripts/verify-packaged-app.mjs @@ -714,15 +714,10 @@ export async function assertPackagedResources( const required = [ 'app.asar', 'bundled-tools.json', - 'bundled-git.json', - join('licenses', 'git', 'LICENSE.txt'), - join('licenses', 'git', 'SOURCE_OFFER.txt'), join('workers', 'filesystem-worker.js'), join('licenses', 'maka', 'LICENSE'), join('licenses', 'maka', 'NOTICE'), ...(requireDisclaimer ? [join('licenses', 'maka', 'DISCLAIMER-WIP')] : []), - join('licenses', 'dugite', 'LICENSE'), - join('licenses', 'git', 'NOTICE.txt'), join('licenses', 'electron', 'LICENSE'), join('licenses', 'electron', 'LICENSES.chromium.html'), join('licenses', 'npm', 'THIRD_PARTY_NOTICES.txt'), @@ -746,6 +741,10 @@ export async function assertPackagedResources( await requirePath(join(resourcesPath, path)); } const forbidden = [ + 'git', + 'bundled-git.json', + join('licenses', 'dugite'), + join('licenses', 'git'), join('tools', 'officecli'), join('licenses', 'officecli'), // cua-driver is gone from this repository, and these two forbids stay for the diff --git a/scripts/verify-packaged-app.test.mjs b/scripts/verify-packaged-app.test.mjs index 637b1c31ab..f475d9e798 100644 --- a/scripts/verify-packaged-app.test.mjs +++ b/scripts/verify-packaged-app.test.mjs @@ -4,7 +4,31 @@ import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { after, describe, test } from 'node:test'; import { createPackage } from '@electron/asar'; -import { asarLookupPath, assertPackagedDependencyClosure } from './verify-packaged-app.mjs'; +import { + asarLookupPath, + assertPackagedDependencyClosure, + assertPackagedResources, +} from './verify-packaged-app.mjs'; + +test('packaged resources forbid the retired bundled Git distribution', async () => { + const required = []; + const forbidden = []; + await assertPackagedResources('resources', { + requirePath: async (path) => required.push(path), + forbidPath: async (path) => forbidden.push(path), + requireWindowsSandbox: false, + }); + + for (const path of [ + join('resources', 'git'), + join('resources', 'bundled-git.json'), + join('resources', 'licenses', 'dugite'), + join('resources', 'licenses', 'git'), + ]) { + assert.equal(required.includes(path), false); + assert.equal(forbidden.includes(path), true); + } +}); describe('asarLookupPath', () => { // The archive stores `/`-joined paths, but `@electron/asar` resolves a lookup From b6518a3616e2f92e2210b7b5825d69a032098a03 Mon Sep 17 00:00:00 2001 From: zhiiw Date: Sat, 22 Aug 2026 17:18:07 +0800 Subject: [PATCH 2/8] fix(release): align Git absence and persisted identity --- .../__tests__/git-workspace-service.test.ts | 22 +++++ packages/storage/src/git-workspace-service.ts | 90 +++++++++---------- .../storage/src/managed-workspace-owner.ts | 2 +- scripts/product-release.test.mjs | 7 ++ scripts/verify-macos-arm64-dmg.mjs | 1 - scripts/verify-windows-x64.mjs | 1 - 6 files changed, 75 insertions(+), 48 deletions(-) diff --git a/packages/storage/src/__tests__/git-workspace-service.test.ts b/packages/storage/src/__tests__/git-workspace-service.test.ts index 1a6c8f7091..cc0e5bc927 100644 --- a/packages/storage/src/__tests__/git-workspace-service.test.ts +++ b/packages/storage/src/__tests__/git-workspace-service.test.ts @@ -696,6 +696,28 @@ describe('Git workspace service', () => { isWorkspaceError('managed_workspace_identity_conflict'), ); }); + + test('rejects legacy distribution-identity bindings after the executable identity epoch', async () => { + const root = await temporaryRoot(); + const sourceRoot = await createEligibleSource(join(root, 'source')); + const service = await serviceAt(join(root, 'storage')); + const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); + const bindingPath = join(dirname(binding.worktreePath), 'binding.json'); + const current = JSON.parse(await readFile(bindingPath, 'utf8')); + const legacy = { + ...current, + schemaVersion: 1, + protocol: 'git_managed_workspace_v1', + gitRuntimeSha256: current.gitExecutableSha256 ?? current.gitRuntimeSha256, + }; + delete legacy.gitExecutableSha256; + await writeFile(bindingPath, `${JSON.stringify(legacy)}\n`, 'utf8'); + + await assert.rejects( + service.inspectManagedWorkspace(binding), + isWorkspaceError('managed_workspace_identity_conflict'), + ); + }); }); async function serviceAt(storageRoot: string): Promise { diff --git a/packages/storage/src/git-workspace-service.ts b/packages/storage/src/git-workspace-service.ts index 6aba50ae87..bc14b3abfb 100644 --- a/packages/storage/src/git-workspace-service.ts +++ b/packages/storage/src/git-workspace-service.ts @@ -19,9 +19,9 @@ import { registerManagedBaselineReceiptAuthorityInternal } from './managed-basel const execFileAsync = promisify(execFile); const GIT_TIMEOUT_MS = 2 * 60 * 1_000; const GIT_MAX_BUFFER_BYTES = 16 * 1024 * 1024; -const BINDING_SCHEMA_VERSION = 1; -const REPOSITORY_SCHEMA_VERSION = 1; -const EPOCH_ARTIFACT_SCHEMA_VERSION = 1; +const BINDING_SCHEMA_VERSION = 2; +const REPOSITORY_SCHEMA_VERSION = 2; +const EPOCH_ARTIFACT_SCHEMA_VERSION = 2; const QUARANTINE_INTENT_SCHEMA_VERSION = 1; const BASELINE_RECEIPT_SCHEMA_VERSION = 1; const IDENTIFIER_PATTERN = /^(repository|workspace|epoch|instance)_[a-f0-9]{32}$/u; @@ -44,7 +44,7 @@ const BINDING_KEYS = [ 'baselineCommitOid', 'baselineTreeOid', 'headRef', - 'gitRuntimeSha256', + 'gitExecutableSha256', 'objectFormat', 'materializationProfileDigest', 'materializationSemantics', @@ -55,7 +55,7 @@ const REPOSITORY_KEYS = [ 'repositoryId', 'repositoryPath', 'hooksPath', - 'gitRuntimeSha256', + 'gitExecutableSha256', 'objectFormat', 'repositoryCapabilityDigest', ] as const; @@ -73,7 +73,7 @@ const EPOCH_ARTIFACT_KEYS = [ 'baselineTreeOid', 'baselineRef', 'headRef', - 'gitRuntimeSha256', + 'gitExecutableSha256', 'objectFormat', 'materializationProfileDigest', 'materializationSemantics', @@ -181,8 +181,8 @@ export interface CreateManagedWorkspaceFromSourceInput extends ManagedWorkspaceI } export interface ManagedWorkspaceBinding { - readonly schemaVersion: 1; - readonly protocol: 'git_managed_workspace_v1'; + readonly schemaVersion: 2; + readonly protocol: 'git_managed_workspace_v2'; readonly repositoryId: string; readonly workspaceId: string; readonly workspaceEpochId: string; @@ -197,7 +197,7 @@ export interface ManagedWorkspaceBinding { readonly baselineCommitOid: string; readonly baselineTreeOid: string; readonly headRef: string; - readonly gitRuntimeSha256: `sha256:${string}`; + readonly gitExecutableSha256: `sha256:${string}`; readonly objectFormat: 'sha1' | 'sha256'; readonly materializationProfileDigest: `sha256:${string}`; readonly materializationSemantics: typeof MATERIALIZATION_SEMANTICS; @@ -278,19 +278,19 @@ interface BaselineTreeSummary { } interface ManagedRepositoryRecord { - readonly schemaVersion: 1; - readonly protocol: 'maka_managed_git_repository_v1'; + readonly schemaVersion: 2; + readonly protocol: 'maka_managed_git_repository_v2'; readonly repositoryId: string; readonly repositoryPath: string; readonly hooksPath: string; - readonly gitRuntimeSha256: `sha256:${string}`; + readonly gitExecutableSha256: `sha256:${string}`; readonly objectFormat: 'sha1' | 'sha256'; readonly repositoryCapabilityDigest: `sha256:${string}`; } interface ManagedWorkspaceEpochArtifact { - readonly schemaVersion: 1; - readonly protocol: 'maka_managed_workspace_epoch_artifact_v1'; + readonly schemaVersion: 2; + readonly protocol: 'maka_managed_workspace_epoch_artifact_v2'; readonly repositoryId: string; readonly workspaceId: string; readonly workspaceEpochId: string; @@ -302,7 +302,7 @@ interface ManagedWorkspaceEpochArtifact { readonly baselineTreeOid: string; readonly baselineRef: string; readonly headRef: string; - readonly gitRuntimeSha256: `sha256:${string}`; + readonly gitExecutableSha256: `sha256:${string}`; readonly objectFormat: 'sha1' | 'sha256'; readonly materializationProfileDigest: `sha256:${string}`; readonly materializationSemantics: typeof MATERIALIZATION_SEMANTICS; @@ -435,7 +435,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { const binding: ManagedWorkspaceBinding = { schemaVersion: BINDING_SCHEMA_VERSION, - protocol: 'git_managed_workspace_v1', + protocol: 'git_managed_workspace_v2', repositoryId: input.repositoryId, workspaceId: input.workspaceId, workspaceEpochId: input.workspaceEpochId, @@ -450,7 +450,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { baselineCommitOid: epoch.baselineCommitOid, baselineTreeOid: epoch.baselineTreeOid, headRef: epoch.headRef, - gitRuntimeSha256: runtime.digest, + gitExecutableSha256: runtime.digest, objectFormat: repository.objectFormat, materializationProfileDigest: epoch.materializationProfileDigest, materializationSemantics: MATERIALIZATION_SEMANTICS, @@ -861,11 +861,11 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { const record: ManagedRepositoryRecord = { schemaVersion: REPOSITORY_SCHEMA_VERSION, - protocol: 'maka_managed_git_repository_v1', + protocol: 'maka_managed_git_repository_v2', repositoryId: input.repositoryId, repositoryPath: normalize(layout.repositoryPath), hooksPath: normalize(layout.hooksPath), - gitRuntimeSha256: runtimeDigest, + gitExecutableSha256: runtimeDigest, objectFormat: source.objectFormat, repositoryCapabilityDigest: repositoryCapabilityDigest(runtimeDigest, source.objectFormat), }; @@ -1021,7 +1021,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { const artifact: ManagedWorkspaceEpochArtifact = { schemaVersion: EPOCH_ARTIFACT_SCHEMA_VERSION, - protocol: 'maka_managed_workspace_epoch_artifact_v1', + protocol: 'maka_managed_workspace_epoch_artifact_v2', repositoryId: input.repositoryId, workspaceId: input.workspaceId, workspaceEpochId: input.workspaceEpochId, @@ -1033,10 +1033,10 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { baselineTreeOid: importedTree, baselineRef, headRef: managedHeadRef(input.workspaceId, input.workspaceEpochId), - gitRuntimeSha256: repository.gitRuntimeSha256, + gitExecutableSha256: repository.gitExecutableSha256, objectFormat: repository.objectFormat, materializationProfileDigest: materializationProfileDigest( - repository.gitRuntimeSha256, + repository.gitExecutableSha256, repository.objectFormat, ), materializationSemantics: MATERIALIZATION_SEMANTICS, @@ -1104,10 +1104,10 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { ): Promise { if ( epoch.repositoryId !== repository.repositoryId || - epoch.gitRuntimeSha256 !== repository.gitRuntimeSha256 || + epoch.gitExecutableSha256 !== repository.gitExecutableSha256 || epoch.objectFormat !== repository.objectFormat || epoch.materializationProfileDigest !== - materializationProfileDigest(repository.gitRuntimeSha256, repository.objectFormat) || + materializationProfileDigest(repository.gitExecutableSha256, repository.objectFormat) || epoch.baselineRef !== managedBaselineRef(epoch.workspaceEpochId) || epoch.headRef !== managedHeadRef(epoch.workspaceId, epoch.workspaceEpochId) ) { @@ -1274,7 +1274,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { if ( objectFormat !== record.objectFormat || record.repositoryCapabilityDigest !== - repositoryCapabilityDigest(record.gitRuntimeSha256, record.objectFormat) + repositoryCapabilityDigest(record.gitExecutableSha256, record.objectFormat) ) { throw new GitWorkspaceServiceError( 'managed_workspace_identity_conflict', @@ -1879,7 +1879,7 @@ function assertBindingIdentity( binding.workspaceId !== input.workspaceId || binding.workspaceEpochId !== input.workspaceEpochId || binding.workspaceInstanceId !== input.workspaceInstanceId || - binding.gitRuntimeSha256 !== runtimeDigest || + binding.gitExecutableSha256 !== runtimeDigest || binding.materializationProfileDigest !== materializationProfileDigest(runtimeDigest, binding.objectFormat) || binding.headRef !== managedHeadRef(input.workspaceId, input.workspaceEpochId) @@ -1899,10 +1899,10 @@ function assertBindingRepository( binding.repositoryId !== repository.repositoryId || !samePath(binding.repositoryPath, repository.repositoryPath) || !samePath(binding.hooksPath, repository.hooksPath) || - binding.gitRuntimeSha256 !== repository.gitRuntimeSha256 || + binding.gitExecutableSha256 !== repository.gitExecutableSha256 || binding.objectFormat !== repository.objectFormat || repository.repositoryCapabilityDigest !== - repositoryCapabilityDigest(repository.gitRuntimeSha256, repository.objectFormat) + repositoryCapabilityDigest(repository.gitExecutableSha256, repository.objectFormat) ) { throw new GitWorkspaceServiceError( 'managed_workspace_identity_conflict', @@ -1957,7 +1957,7 @@ function assertRepositoryMatches( ): void { if ( record.repositoryId !== input.repositoryId || - record.gitRuntimeSha256 !== runtimeDigest || + record.gitExecutableSha256 !== runtimeDigest || record.objectFormat !== objectFormat || record.repositoryCapabilityDigest !== repositoryCapabilityDigest(runtimeDigest, objectFormat) || !samePath(record.repositoryPath, layout.repositoryPath) || @@ -2013,7 +2013,7 @@ function assertEpochArtifactMatches( artifact.sourceHeadCommitOid !== source.headCommitOid || artifact.sourceTreeOid !== source.treeOid || artifact.baselineTreeOid !== source.treeOid || - artifact.gitRuntimeSha256 !== repository.gitRuntimeSha256 || + artifact.gitExecutableSha256 !== repository.gitExecutableSha256 || artifact.objectFormat !== repository.objectFormat ) { throw new GitWorkspaceServiceError( @@ -2107,7 +2107,7 @@ function isBinding(value: unknown): value is ManagedWorkspaceBinding { return ( hasExactKeys(value, BINDING_KEYS) && value.schemaVersion === BINDING_SCHEMA_VERSION && - value.protocol === 'git_managed_workspace_v1' && + value.protocol === 'git_managed_workspace_v2' && typeof value.repositoryId === 'string' && IDENTIFIER_PATTERN.test(value.repositoryId) && typeof value.workspaceId === 'string' && @@ -2136,8 +2136,8 @@ function isBinding(value: unknown): value is ManagedWorkspaceBinding { OID_PATTERN.test(value.baselineTreeOid) && typeof value.headRef === 'string' && value.headRef === managedHeadRef(value.workspaceId, value.workspaceEpochId) && - typeof value.gitRuntimeSha256 === 'string' && - SHA256_PATTERN.test(value.gitRuntimeSha256) && + typeof value.gitExecutableSha256 === 'string' && + SHA256_PATTERN.test(value.gitExecutableSha256) && (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && typeof value.materializationProfileDigest === 'string' && SHA256_PATTERN.test(value.materializationProfileDigest) && @@ -2247,15 +2247,15 @@ function isRepositoryRecord(value: unknown): value is ManagedRepositoryRecord { return ( hasExactKeys(value, REPOSITORY_KEYS) && value.schemaVersion === REPOSITORY_SCHEMA_VERSION && - value.protocol === 'maka_managed_git_repository_v1' && + value.protocol === 'maka_managed_git_repository_v2' && typeof value.repositoryId === 'string' && IDENTIFIER_PATTERN.test(value.repositoryId) && typeof value.repositoryPath === 'string' && isAbsolute(value.repositoryPath) && typeof value.hooksPath === 'string' && isAbsolute(value.hooksPath) && - typeof value.gitRuntimeSha256 === 'string' && - SHA256_PATTERN.test(value.gitRuntimeSha256) && + typeof value.gitExecutableSha256 === 'string' && + SHA256_PATTERN.test(value.gitExecutableSha256) && (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && typeof value.repositoryCapabilityDigest === 'string' && SHA256_PATTERN.test(value.repositoryCapabilityDigest) @@ -2267,7 +2267,7 @@ function isEpochArtifact(value: unknown): value is ManagedWorkspaceEpochArtifact return ( hasExactKeys(value, EPOCH_ARTIFACT_KEYS) && value.schemaVersion === EPOCH_ARTIFACT_SCHEMA_VERSION && - value.protocol === 'maka_managed_workspace_epoch_artifact_v1' && + value.protocol === 'maka_managed_workspace_epoch_artifact_v2' && typeof value.repositoryId === 'string' && IDENTIFIER_PATTERN.test(value.repositoryId) && typeof value.workspaceId === 'string' && @@ -2288,8 +2288,8 @@ function isEpochArtifact(value: unknown): value is ManagedWorkspaceEpochArtifact OID_PATTERN.test(value.baselineTreeOid) && value.baselineRef === managedBaselineRef(value.workspaceEpochId) && value.headRef === managedHeadRef(value.workspaceId, value.workspaceEpochId) && - typeof value.gitRuntimeSha256 === 'string' && - SHA256_PATTERN.test(value.gitRuntimeSha256) && + typeof value.gitExecutableSha256 === 'string' && + SHA256_PATTERN.test(value.gitExecutableSha256) && (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && typeof value.materializationProfileDigest === 'string' && SHA256_PATTERN.test(value.materializationProfileDigest) && @@ -2636,12 +2636,12 @@ function findWorktreeRegistration( } function repositoryCapabilityDigest( - gitRuntimeSha256: `sha256:${string}`, + gitExecutableSha256: `sha256:${string}`, objectFormat: string, ): `sha256:${string}` { const capability = JSON.stringify({ - protocol: 'maka_managed_git_repository_capability_v1', - gitRuntimeSha256, + protocol: 'maka_managed_git_repository_capability_v2', + gitExecutableSha256, objectFormat, hooks: 'disabled_v1', credentials: 'disabled_v1', @@ -2652,12 +2652,12 @@ function repositoryCapabilityDigest( } function materializationProfileDigest( - gitRuntimeSha256: `sha256:${string}`, + gitExecutableSha256: `sha256:${string}`, objectFormat: string, ): `sha256:${string}` { const profile = JSON.stringify({ - protocol: 'git_materialization_profile_v1', - gitRuntimeSha256, + protocol: 'git_materialization_profile_v2', + gitExecutableSha256, objectFormat, platform: process.platform, autocrlf: false, diff --git a/packages/storage/src/managed-workspace-owner.ts b/packages/storage/src/managed-workspace-owner.ts index 99765efcf3..046d13762e 100644 --- a/packages/storage/src/managed-workspace-owner.ts +++ b/packages/storage/src/managed-workspace-owner.ts @@ -557,7 +557,7 @@ function sameManagedWorkspaceBinding( left.baselineCommitOid === right.baselineCommitOid && left.baselineTreeOid === right.baselineTreeOid && left.headRef === right.headRef && - left.gitRuntimeSha256 === right.gitRuntimeSha256 && + left.gitExecutableSha256 === right.gitExecutableSha256 && left.objectFormat === right.objectFormat && left.materializationProfileDigest === right.materializationProfileDigest && left.materializationSemantics === right.materializationSemantics diff --git a/scripts/product-release.test.mjs b/scripts/product-release.test.mjs index 1c94c8d66f..5e94ddeb5c 100644 --- a/scripts/product-release.test.mjs +++ b/scripts/product-release.test.mjs @@ -175,6 +175,13 @@ test('Desktop packaging does not distribute the retired bundled Git runtime', () ); }); +test('platform package verifiers do not reintroduce retired Git presence checks', async () => { + for (const verifier of ['verify-windows-x64.mjs', 'verify-macos-arm64-dmg.mjs']) { + const source = await readFile(join(repoRoot, 'scripts', verifier), 'utf8'); + assert.doesNotMatch(source, /requirePath\(join\(resources, ['"]git['"]/u); + } +}); + test('the packaged-app probe rejects a mismatched Runtime Host setup package', async () => { const fixture = await mkdtemp(join(tmpdir(), 'maka-packaged-manifest-')); try { diff --git a/scripts/verify-macos-arm64-dmg.mjs b/scripts/verify-macos-arm64-dmg.mjs index bb1f0df160..c9635e13d8 100644 --- a/scripts/verify-macos-arm64-dmg.mjs +++ b/scripts/verify-macos-arm64-dmg.mjs @@ -126,7 +126,6 @@ export async function verifyPackagedMacApp( await requirePath(executable); await assertPackagedResources(resources, { requirePath, forbidPath }); await assertPackagedDependencyClosure(resources); - await requirePath(join(resources, 'git', 'bin', 'git')); const executableArchitectures = await run('lipo', ['-archs', executable]); assertSingleArchitecture(executableArchitectures.stdout, 'Maka executable'); diff --git a/scripts/verify-windows-x64.mjs b/scripts/verify-windows-x64.mjs index 8bf7a0f280..9829594e58 100644 --- a/scripts/verify-windows-x64.mjs +++ b/scripts/verify-windows-x64.mjs @@ -111,7 +111,6 @@ export async function verifyPackagedWindowsApp( requireDisclaimer: requiresCurrentContract, }); if (requiresCurrentContract) await assertPackagedDependencyClosure(resources); - await requirePath(join(resources, 'git', 'cmd', 'git.exe')); step('reading the executable architecture'); const machine = await readMachine(executable); From d1d7bc5175a2d9a629cf2f4bd748cc40cee96644 Mon Sep 17 00:00:00 2001 From: zhiiw Date: Sat, 22 Aug 2026 17:23:43 +0800 Subject: [PATCH 3/8] test(runtime-host): prove PATH Git is never discovered --- .../__tests__/execution-composition.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/runtime-host/src/__tests__/execution-composition.test.ts b/packages/runtime-host/src/__tests__/execution-composition.test.ts index 6e474e8c61..b3440fdd92 100644 --- a/packages/runtime-host/src/__tests__/execution-composition.test.ts +++ b/packages/runtime-host/src/__tests__/execution-composition.test.ts @@ -1,5 +1,6 @@ import assert from 'node:assert/strict'; import { parseNoRealConnectionError } from '@maka/core/connection-error-copy'; +import { execFile } from 'node:child_process'; import { createRequire } from 'node:module'; import { mkdir, mkdtemp, rm, stat, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; @@ -35,6 +36,10 @@ import { createExecutionRuntimeHostComposition, runtimeHostFilesystemWorkerRuntime, } from '../server/execution-composition.js'; +import { + createManagedWorkspaceExecutionProfile, + RuntimeHostWorkspaceExecutionError, +} from '../server/workspace-execution-composition.js'; const require = createRequire(import.meta.url); @@ -43,6 +48,34 @@ test('filesystem worker follows the candidate executable runtime', () => { assert.equal(runtimeHostFilesystemWorkerRuntime({}), 'node'); }); +test('production composition never discovers PATH Git when no runtime is admitted', async () => { + await new Promise((resolve, reject) => { + execFile('git', ['--version'], (error) => (error ? reject(error) : resolve())); + }); + + await withCompositionRoot(async ({ owner }) => { + const composition = await createExecutionRuntimeHostComposition(compositionContext(owner)); + try { + assert.equal(composition.workspaceExecution.state, 'ready'); + const managed = createManagedWorkspaceExecutionProfile( + Object.freeze({ kind: 'managed_workspace_execution_handle_v1' }), + ); + await assert.rejects( + () => + composition.workspaceExecution.executeReadOnly(managed, { + kind: 'read', + path: 'README.md', + }), + (error) => + error instanceof RuntimeHostWorkspaceExecutionError && + error.code === 'managed_workspace_profile_unavailable', + ); + } finally { + await composition.close(); + } + }); +}); + test('production composition owns the long-term memory database lifecycle', async () => { await withCompositionRoot(async ({ root, owner }) => { const databasePath = join(root, LONG_TERM_MEMORY_DATABASE_NAME); From 0562b2ce3efb78f38e06e459a89b72822459c013 Mon Sep 17 00:00:00 2001 From: zhiiw Date: Sat, 22 Aug 2026 17:25:54 +0800 Subject: [PATCH 4/8] refactor(runtime-host): remove legacy Git CLI injection --- .../src/server/execution-candidate.ts | 8 ++----- .../server/execution-composition-factory.ts | 5 ---- .../src/server/execution-composition.ts | 24 +------------------ .../src/server/execution-service.ts | 2 -- 4 files changed, 3 insertions(+), 36 deletions(-) diff --git a/packages/runtime-host/src/server/execution-candidate.ts b/packages/runtime-host/src/server/execution-candidate.ts index c863021d5f..b1ca6cd2fd 100644 --- a/packages/runtime-host/src/server/execution-candidate.ts +++ b/packages/runtime-host/src/server/execution-candidate.ts @@ -1,4 +1,3 @@ -import type { VerifiedGitRuntimeInput } from '@maka/storage/managed-workspace-owner'; import { startInteractiveRuntimeHostCandidate, type InteractiveRuntimeHostCandidateOptions, @@ -11,10 +10,7 @@ import { export type ExecutionRuntimeHostCandidateResult = InteractiveRuntimeHostCandidateResult; -export interface ExecutionRuntimeHostCandidateOptions - extends InteractiveRuntimeHostCandidateOptions { - readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; -} +export type ExecutionRuntimeHostCandidateOptions = InteractiveRuntimeHostCandidateOptions; export type ExecutionRuntimeHostCandidateDependencies = ExecutionRuntimeHostCompositionDependencies; @@ -22,6 +18,6 @@ export async function startExecutionRuntimeHostCandidate( options: ExecutionRuntimeHostCandidateOptions, dependencies: ExecutionRuntimeHostCandidateDependencies = {}, ): Promise { - const composition = await createExecutionRuntimeHostCompositionSource(options, dependencies); + const composition = await createExecutionRuntimeHostCompositionSource({}, dependencies); return startInteractiveRuntimeHostCandidate(options, composition); } diff --git a/packages/runtime-host/src/server/execution-composition-factory.ts b/packages/runtime-host/src/server/execution-composition-factory.ts index a0ccc4cd5e..be901ee285 100644 --- a/packages/runtime-host/src/server/execution-composition-factory.ts +++ b/packages/runtime-host/src/server/execution-composition-factory.ts @@ -1,4 +1,3 @@ -import type { VerifiedGitRuntimeInput } from '@maka/storage/managed-workspace-owner'; import type { PublishedProjectDirectoryRoot } from './project-directory-authority.js'; import { createExecutionRuntimeHostComposition, @@ -11,7 +10,6 @@ import { } from './host-composition.js'; export interface ExecutionRuntimeHostCompositionSourceOptions { - readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; readonly projectDirectoryRoots?: readonly PublishedProjectDirectoryRoot[]; } @@ -27,9 +25,6 @@ export async function createExecutionRuntimeHostCompositionSource( dependencies: ExecutionRuntimeHostCompositionDependencies = {}, ): Promise { const compositionOptions = { - ...(options.managedWorkspaceGitRuntime - ? { managedWorkspaceGitRuntime: options.managedWorkspaceGitRuntime } - : {}), ...(options.projectDirectoryRoots ? { projectDirectoryRoots: options.projectDirectoryRoots } : {}), diff --git a/packages/runtime-host/src/server/execution-composition.ts b/packages/runtime-host/src/server/execution-composition.ts index 1bb701a64b..85fab99da3 100644 --- a/packages/runtime-host/src/server/execution-composition.ts +++ b/packages/runtime-host/src/server/execution-composition.ts @@ -59,12 +59,7 @@ import { createGitWorktreeChildExecutor } from '@maka/storage/git-worktree-child import { runWithStorageRootLease } from '@maka/storage/root-authority'; import { openStorageWriterComposition } from '@maka/storage'; import { resolveWorkspaceIdentity } from '@maka/storage/workspace-identity'; -import { - openManagedWorkspaceOwner, - type ManagedWorkspaceFilesystemWorker, - type ManagedWorkspaceOwner, - type VerifiedGitRuntimeInput, -} from '@maka/storage/managed-workspace-owner'; +import { type ManagedWorkspaceFilesystemWorker } from '@maka/storage/managed-workspace-owner'; import { CanonicalSessionProjectionReader } from './canonical-session-projection.js'; import { bindHostChildAgentBackend, @@ -168,7 +163,6 @@ export interface ExecutionRuntimeHostComposition extends RuntimeHostComposition } export interface CreateExecutionRuntimeHostCompositionOptions { - readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; readonly bootstrapRuntimePolicy?: boolean; readonly skillHomeDirectory?: string; readonly projectDirectoryRoots?: readonly PublishedProjectDirectoryRoot[]; @@ -215,7 +209,6 @@ export async function createExecutionRuntimeHostComposition( let unsubscribeTaskLedger: (() => void) | undefined; let unsubscribeTranscriptChanges: (() => void) | undefined; let unsubscribeUsageChanges: (() => void) | undefined; - let managedWorkspaceOwner: ManagedWorkspaceOwner | undefined; let workspaceExecution: RuntimeHostWorkspaceExecutionComposition | undefined; let goalExecutions: HostGoalExecutionCoordinator | undefined; try { @@ -299,22 +292,8 @@ export async function createExecutionRuntimeHostComposition( const managedFilesystemWorker = filesystemWorker ? adaptManagedWorkspaceFilesystemWorker(filesystemWorker) : undefined; - if (options.managedWorkspaceGitRuntime) { - if (!managedFilesystemWorker) { - throw new RuntimeHostWorkspaceExecutionError( - 'filesystem_worker_unavailable', - 'Managed workspace execution requires the sandboxed filesystem worker', - ); - } - managedWorkspaceOwner = await openManagedWorkspaceOwner({ - rootOwner: context.owner, - gitRuntime: options.managedWorkspaceGitRuntime, - filesystemWorker: managedFilesystemWorker, - }); - } workspaceExecution = createRuntimeHostWorkspaceExecutionComposition({ ...(managedFilesystemWorker ? { filesystemWorker: managedFilesystemWorker } : {}), - ...(managedWorkspaceOwner ? { managedOwner: managedWorkspaceOwner } : {}), }); const taskLedger = new HostTaskLedgerCoordinator( taskLedgerStore, @@ -1595,7 +1574,6 @@ export async function createExecutionRuntimeHostComposition( goalExecutions?.beginDrain(); try { await workspaceExecution?.close(); - if (!workspaceExecution) await managedWorkspaceOwner?.close(); } catch (closeError) { errors.push(closeError); } diff --git a/packages/runtime-host/src/server/execution-service.ts b/packages/runtime-host/src/server/execution-service.ts index 4377eef208..2c728ca4ad 100644 --- a/packages/runtime-host/src/server/execution-service.ts +++ b/packages/runtime-host/src/server/execution-service.ts @@ -1,5 +1,4 @@ import { resolveStorageRoot, tryAcquireStateRootOwner } from '@maka/storage/root-authority'; -import type { VerifiedGitRuntimeInput } from '@maka/storage/managed-workspace-owner'; import { createExecutionRuntimeHostCompositionSource, type ExecutionRuntimeHostCompositionDependencies, @@ -12,7 +11,6 @@ import type { PublishedProjectDirectoryRoot } from './project-directory-authorit export interface ExecutionRuntimeHostServiceOptions { readonly rootPath: string; - readonly managedWorkspaceGitRuntime?: VerifiedGitRuntimeInput; readonly projectDirectoryRoots?: readonly PublishedProjectDirectoryRoot[]; readonly handshakeTimeoutMs?: number; readonly shutdownGraceMs?: number; From fd6421af276c7e5c9cfedcf8bf85fc9cb8c4e6ed Mon Sep 17 00:00:00 2001 From: zhiiw Date: Sat, 22 Aug 2026 17:47:00 +0800 Subject: [PATCH 5/8] refactor(storage): keep legacy Git adapter schema unchanged --- .../__tests__/git-workspace-service.test.ts | 22 ----- packages/storage/src/git-workspace-service.ts | 90 +++++++++---------- .../storage/src/managed-workspace-owner.ts | 2 +- 3 files changed, 46 insertions(+), 68 deletions(-) diff --git a/packages/storage/src/__tests__/git-workspace-service.test.ts b/packages/storage/src/__tests__/git-workspace-service.test.ts index cc0e5bc927..1a6c8f7091 100644 --- a/packages/storage/src/__tests__/git-workspace-service.test.ts +++ b/packages/storage/src/__tests__/git-workspace-service.test.ts @@ -696,28 +696,6 @@ describe('Git workspace service', () => { isWorkspaceError('managed_workspace_identity_conflict'), ); }); - - test('rejects legacy distribution-identity bindings after the executable identity epoch', async () => { - const root = await temporaryRoot(); - const sourceRoot = await createEligibleSource(join(root, 'source')); - const service = await serviceAt(join(root, 'storage')); - const binding = await service.createManagedWorkspaceFromSource(openRequest(sourceRoot)); - const bindingPath = join(dirname(binding.worktreePath), 'binding.json'); - const current = JSON.parse(await readFile(bindingPath, 'utf8')); - const legacy = { - ...current, - schemaVersion: 1, - protocol: 'git_managed_workspace_v1', - gitRuntimeSha256: current.gitExecutableSha256 ?? current.gitRuntimeSha256, - }; - delete legacy.gitExecutableSha256; - await writeFile(bindingPath, `${JSON.stringify(legacy)}\n`, 'utf8'); - - await assert.rejects( - service.inspectManagedWorkspace(binding), - isWorkspaceError('managed_workspace_identity_conflict'), - ); - }); }); async function serviceAt(storageRoot: string): Promise { diff --git a/packages/storage/src/git-workspace-service.ts b/packages/storage/src/git-workspace-service.ts index bc14b3abfb..6aba50ae87 100644 --- a/packages/storage/src/git-workspace-service.ts +++ b/packages/storage/src/git-workspace-service.ts @@ -19,9 +19,9 @@ import { registerManagedBaselineReceiptAuthorityInternal } from './managed-basel const execFileAsync = promisify(execFile); const GIT_TIMEOUT_MS = 2 * 60 * 1_000; const GIT_MAX_BUFFER_BYTES = 16 * 1024 * 1024; -const BINDING_SCHEMA_VERSION = 2; -const REPOSITORY_SCHEMA_VERSION = 2; -const EPOCH_ARTIFACT_SCHEMA_VERSION = 2; +const BINDING_SCHEMA_VERSION = 1; +const REPOSITORY_SCHEMA_VERSION = 1; +const EPOCH_ARTIFACT_SCHEMA_VERSION = 1; const QUARANTINE_INTENT_SCHEMA_VERSION = 1; const BASELINE_RECEIPT_SCHEMA_VERSION = 1; const IDENTIFIER_PATTERN = /^(repository|workspace|epoch|instance)_[a-f0-9]{32}$/u; @@ -44,7 +44,7 @@ const BINDING_KEYS = [ 'baselineCommitOid', 'baselineTreeOid', 'headRef', - 'gitExecutableSha256', + 'gitRuntimeSha256', 'objectFormat', 'materializationProfileDigest', 'materializationSemantics', @@ -55,7 +55,7 @@ const REPOSITORY_KEYS = [ 'repositoryId', 'repositoryPath', 'hooksPath', - 'gitExecutableSha256', + 'gitRuntimeSha256', 'objectFormat', 'repositoryCapabilityDigest', ] as const; @@ -73,7 +73,7 @@ const EPOCH_ARTIFACT_KEYS = [ 'baselineTreeOid', 'baselineRef', 'headRef', - 'gitExecutableSha256', + 'gitRuntimeSha256', 'objectFormat', 'materializationProfileDigest', 'materializationSemantics', @@ -181,8 +181,8 @@ export interface CreateManagedWorkspaceFromSourceInput extends ManagedWorkspaceI } export interface ManagedWorkspaceBinding { - readonly schemaVersion: 2; - readonly protocol: 'git_managed_workspace_v2'; + readonly schemaVersion: 1; + readonly protocol: 'git_managed_workspace_v1'; readonly repositoryId: string; readonly workspaceId: string; readonly workspaceEpochId: string; @@ -197,7 +197,7 @@ export interface ManagedWorkspaceBinding { readonly baselineCommitOid: string; readonly baselineTreeOid: string; readonly headRef: string; - readonly gitExecutableSha256: `sha256:${string}`; + readonly gitRuntimeSha256: `sha256:${string}`; readonly objectFormat: 'sha1' | 'sha256'; readonly materializationProfileDigest: `sha256:${string}`; readonly materializationSemantics: typeof MATERIALIZATION_SEMANTICS; @@ -278,19 +278,19 @@ interface BaselineTreeSummary { } interface ManagedRepositoryRecord { - readonly schemaVersion: 2; - readonly protocol: 'maka_managed_git_repository_v2'; + readonly schemaVersion: 1; + readonly protocol: 'maka_managed_git_repository_v1'; readonly repositoryId: string; readonly repositoryPath: string; readonly hooksPath: string; - readonly gitExecutableSha256: `sha256:${string}`; + readonly gitRuntimeSha256: `sha256:${string}`; readonly objectFormat: 'sha1' | 'sha256'; readonly repositoryCapabilityDigest: `sha256:${string}`; } interface ManagedWorkspaceEpochArtifact { - readonly schemaVersion: 2; - readonly protocol: 'maka_managed_workspace_epoch_artifact_v2'; + readonly schemaVersion: 1; + readonly protocol: 'maka_managed_workspace_epoch_artifact_v1'; readonly repositoryId: string; readonly workspaceId: string; readonly workspaceEpochId: string; @@ -302,7 +302,7 @@ interface ManagedWorkspaceEpochArtifact { readonly baselineTreeOid: string; readonly baselineRef: string; readonly headRef: string; - readonly gitExecutableSha256: `sha256:${string}`; + readonly gitRuntimeSha256: `sha256:${string}`; readonly objectFormat: 'sha1' | 'sha256'; readonly materializationProfileDigest: `sha256:${string}`; readonly materializationSemantics: typeof MATERIALIZATION_SEMANTICS; @@ -435,7 +435,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { const binding: ManagedWorkspaceBinding = { schemaVersion: BINDING_SCHEMA_VERSION, - protocol: 'git_managed_workspace_v2', + protocol: 'git_managed_workspace_v1', repositoryId: input.repositoryId, workspaceId: input.workspaceId, workspaceEpochId: input.workspaceEpochId, @@ -450,7 +450,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { baselineCommitOid: epoch.baselineCommitOid, baselineTreeOid: epoch.baselineTreeOid, headRef: epoch.headRef, - gitExecutableSha256: runtime.digest, + gitRuntimeSha256: runtime.digest, objectFormat: repository.objectFormat, materializationProfileDigest: epoch.materializationProfileDigest, materializationSemantics: MATERIALIZATION_SEMANTICS, @@ -861,11 +861,11 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { const record: ManagedRepositoryRecord = { schemaVersion: REPOSITORY_SCHEMA_VERSION, - protocol: 'maka_managed_git_repository_v2', + protocol: 'maka_managed_git_repository_v1', repositoryId: input.repositoryId, repositoryPath: normalize(layout.repositoryPath), hooksPath: normalize(layout.hooksPath), - gitExecutableSha256: runtimeDigest, + gitRuntimeSha256: runtimeDigest, objectFormat: source.objectFormat, repositoryCapabilityDigest: repositoryCapabilityDigest(runtimeDigest, source.objectFormat), }; @@ -1021,7 +1021,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { const artifact: ManagedWorkspaceEpochArtifact = { schemaVersion: EPOCH_ARTIFACT_SCHEMA_VERSION, - protocol: 'maka_managed_workspace_epoch_artifact_v2', + protocol: 'maka_managed_workspace_epoch_artifact_v1', repositoryId: input.repositoryId, workspaceId: input.workspaceId, workspaceEpochId: input.workspaceEpochId, @@ -1033,10 +1033,10 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { baselineTreeOid: importedTree, baselineRef, headRef: managedHeadRef(input.workspaceId, input.workspaceEpochId), - gitExecutableSha256: repository.gitExecutableSha256, + gitRuntimeSha256: repository.gitRuntimeSha256, objectFormat: repository.objectFormat, materializationProfileDigest: materializationProfileDigest( - repository.gitExecutableSha256, + repository.gitRuntimeSha256, repository.objectFormat, ), materializationSemantics: MATERIALIZATION_SEMANTICS, @@ -1104,10 +1104,10 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { ): Promise { if ( epoch.repositoryId !== repository.repositoryId || - epoch.gitExecutableSha256 !== repository.gitExecutableSha256 || + epoch.gitRuntimeSha256 !== repository.gitRuntimeSha256 || epoch.objectFormat !== repository.objectFormat || epoch.materializationProfileDigest !== - materializationProfileDigest(repository.gitExecutableSha256, repository.objectFormat) || + materializationProfileDigest(repository.gitRuntimeSha256, repository.objectFormat) || epoch.baselineRef !== managedBaselineRef(epoch.workspaceEpochId) || epoch.headRef !== managedHeadRef(epoch.workspaceId, epoch.workspaceEpochId) ) { @@ -1274,7 +1274,7 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { if ( objectFormat !== record.objectFormat || record.repositoryCapabilityDigest !== - repositoryCapabilityDigest(record.gitExecutableSha256, record.objectFormat) + repositoryCapabilityDigest(record.gitRuntimeSha256, record.objectFormat) ) { throw new GitWorkspaceServiceError( 'managed_workspace_identity_conflict', @@ -1879,7 +1879,7 @@ function assertBindingIdentity( binding.workspaceId !== input.workspaceId || binding.workspaceEpochId !== input.workspaceEpochId || binding.workspaceInstanceId !== input.workspaceInstanceId || - binding.gitExecutableSha256 !== runtimeDigest || + binding.gitRuntimeSha256 !== runtimeDigest || binding.materializationProfileDigest !== materializationProfileDigest(runtimeDigest, binding.objectFormat) || binding.headRef !== managedHeadRef(input.workspaceId, input.workspaceEpochId) @@ -1899,10 +1899,10 @@ function assertBindingRepository( binding.repositoryId !== repository.repositoryId || !samePath(binding.repositoryPath, repository.repositoryPath) || !samePath(binding.hooksPath, repository.hooksPath) || - binding.gitExecutableSha256 !== repository.gitExecutableSha256 || + binding.gitRuntimeSha256 !== repository.gitRuntimeSha256 || binding.objectFormat !== repository.objectFormat || repository.repositoryCapabilityDigest !== - repositoryCapabilityDigest(repository.gitExecutableSha256, repository.objectFormat) + repositoryCapabilityDigest(repository.gitRuntimeSha256, repository.objectFormat) ) { throw new GitWorkspaceServiceError( 'managed_workspace_identity_conflict', @@ -1957,7 +1957,7 @@ function assertRepositoryMatches( ): void { if ( record.repositoryId !== input.repositoryId || - record.gitExecutableSha256 !== runtimeDigest || + record.gitRuntimeSha256 !== runtimeDigest || record.objectFormat !== objectFormat || record.repositoryCapabilityDigest !== repositoryCapabilityDigest(runtimeDigest, objectFormat) || !samePath(record.repositoryPath, layout.repositoryPath) || @@ -2013,7 +2013,7 @@ function assertEpochArtifactMatches( artifact.sourceHeadCommitOid !== source.headCommitOid || artifact.sourceTreeOid !== source.treeOid || artifact.baselineTreeOid !== source.treeOid || - artifact.gitExecutableSha256 !== repository.gitExecutableSha256 || + artifact.gitRuntimeSha256 !== repository.gitRuntimeSha256 || artifact.objectFormat !== repository.objectFormat ) { throw new GitWorkspaceServiceError( @@ -2107,7 +2107,7 @@ function isBinding(value: unknown): value is ManagedWorkspaceBinding { return ( hasExactKeys(value, BINDING_KEYS) && value.schemaVersion === BINDING_SCHEMA_VERSION && - value.protocol === 'git_managed_workspace_v2' && + value.protocol === 'git_managed_workspace_v1' && typeof value.repositoryId === 'string' && IDENTIFIER_PATTERN.test(value.repositoryId) && typeof value.workspaceId === 'string' && @@ -2136,8 +2136,8 @@ function isBinding(value: unknown): value is ManagedWorkspaceBinding { OID_PATTERN.test(value.baselineTreeOid) && typeof value.headRef === 'string' && value.headRef === managedHeadRef(value.workspaceId, value.workspaceEpochId) && - typeof value.gitExecutableSha256 === 'string' && - SHA256_PATTERN.test(value.gitExecutableSha256) && + typeof value.gitRuntimeSha256 === 'string' && + SHA256_PATTERN.test(value.gitRuntimeSha256) && (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && typeof value.materializationProfileDigest === 'string' && SHA256_PATTERN.test(value.materializationProfileDigest) && @@ -2247,15 +2247,15 @@ function isRepositoryRecord(value: unknown): value is ManagedRepositoryRecord { return ( hasExactKeys(value, REPOSITORY_KEYS) && value.schemaVersion === REPOSITORY_SCHEMA_VERSION && - value.protocol === 'maka_managed_git_repository_v2' && + value.protocol === 'maka_managed_git_repository_v1' && typeof value.repositoryId === 'string' && IDENTIFIER_PATTERN.test(value.repositoryId) && typeof value.repositoryPath === 'string' && isAbsolute(value.repositoryPath) && typeof value.hooksPath === 'string' && isAbsolute(value.hooksPath) && - typeof value.gitExecutableSha256 === 'string' && - SHA256_PATTERN.test(value.gitExecutableSha256) && + typeof value.gitRuntimeSha256 === 'string' && + SHA256_PATTERN.test(value.gitRuntimeSha256) && (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && typeof value.repositoryCapabilityDigest === 'string' && SHA256_PATTERN.test(value.repositoryCapabilityDigest) @@ -2267,7 +2267,7 @@ function isEpochArtifact(value: unknown): value is ManagedWorkspaceEpochArtifact return ( hasExactKeys(value, EPOCH_ARTIFACT_KEYS) && value.schemaVersion === EPOCH_ARTIFACT_SCHEMA_VERSION && - value.protocol === 'maka_managed_workspace_epoch_artifact_v2' && + value.protocol === 'maka_managed_workspace_epoch_artifact_v1' && typeof value.repositoryId === 'string' && IDENTIFIER_PATTERN.test(value.repositoryId) && typeof value.workspaceId === 'string' && @@ -2288,8 +2288,8 @@ function isEpochArtifact(value: unknown): value is ManagedWorkspaceEpochArtifact OID_PATTERN.test(value.baselineTreeOid) && value.baselineRef === managedBaselineRef(value.workspaceEpochId) && value.headRef === managedHeadRef(value.workspaceId, value.workspaceEpochId) && - typeof value.gitExecutableSha256 === 'string' && - SHA256_PATTERN.test(value.gitExecutableSha256) && + typeof value.gitRuntimeSha256 === 'string' && + SHA256_PATTERN.test(value.gitRuntimeSha256) && (value.objectFormat === 'sha1' || value.objectFormat === 'sha256') && typeof value.materializationProfileDigest === 'string' && SHA256_PATTERN.test(value.materializationProfileDigest) && @@ -2636,12 +2636,12 @@ function findWorktreeRegistration( } function repositoryCapabilityDigest( - gitExecutableSha256: `sha256:${string}`, + gitRuntimeSha256: `sha256:${string}`, objectFormat: string, ): `sha256:${string}` { const capability = JSON.stringify({ - protocol: 'maka_managed_git_repository_capability_v2', - gitExecutableSha256, + protocol: 'maka_managed_git_repository_capability_v1', + gitRuntimeSha256, objectFormat, hooks: 'disabled_v1', credentials: 'disabled_v1', @@ -2652,12 +2652,12 @@ function repositoryCapabilityDigest( } function materializationProfileDigest( - gitExecutableSha256: `sha256:${string}`, + gitRuntimeSha256: `sha256:${string}`, objectFormat: string, ): `sha256:${string}` { const profile = JSON.stringify({ - protocol: 'git_materialization_profile_v2', - gitExecutableSha256, + protocol: 'git_materialization_profile_v1', + gitRuntimeSha256, objectFormat, platform: process.platform, autocrlf: false, diff --git a/packages/storage/src/managed-workspace-owner.ts b/packages/storage/src/managed-workspace-owner.ts index 046d13762e..99765efcf3 100644 --- a/packages/storage/src/managed-workspace-owner.ts +++ b/packages/storage/src/managed-workspace-owner.ts @@ -557,7 +557,7 @@ function sameManagedWorkspaceBinding( left.baselineCommitOid === right.baselineCommitOid && left.baselineTreeOid === right.baselineTreeOid && left.headRef === right.headRef && - left.gitExecutableSha256 === right.gitExecutableSha256 && + left.gitRuntimeSha256 === right.gitRuntimeSha256 && left.objectFormat === right.objectFormat && left.materializationProfileDigest === right.materializationProfileDigest && left.materializationSemantics === right.materializationSemantics From fce583b1facab6635fc5ee3e8778ff61c8899dc8 Mon Sep 17 00:00:00 2001 From: zhiiw Date: Sat, 22 Aug 2026 17:48:27 +0800 Subject: [PATCH 6/8] refactor(storage): leave dormant Git adapter unchanged --- .../dugite-native-environment.test.ts | 53 ++++++++++++++++++ .../storage/src/dugite-native-environment.ts | 35 ++++++++++++ packages/storage/src/git-workspace-service.ts | 54 +++++++++++++++++-- 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 packages/storage/src/__tests__/dugite-native-environment.test.ts create mode 100644 packages/storage/src/dugite-native-environment.ts diff --git a/packages/storage/src/__tests__/dugite-native-environment.test.ts b/packages/storage/src/__tests__/dugite-native-environment.test.ts new file mode 100644 index 0000000000..1ef22398fa --- /dev/null +++ b/packages/storage/src/__tests__/dugite-native-environment.test.ts @@ -0,0 +1,53 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { bundledGitEnvironment } from '../dugite-native-environment.js'; + +test('constructs a Windows MinGit environment without a system PATH', () => { + assert.deepEqual( + bundledGitEnvironment({ + platform: 'win32', + arch: 'arm64', + rootPath: 'C:\\Maka\\resources\\git', + executablePath: 'C:\\Maka\\resources\\git\\cmd\\git.exe', + }), + { + PATH: [ + 'C:\\Maka\\resources\\git\\cmd', + 'C:\\Maka\\resources\\git\\clangarm64\\bin', + 'C:\\Maka\\resources\\git\\clangarm64\\usr\\bin', + ].join(';'), + GIT_EXEC_PATH: 'C:\\Maka\\resources\\git\\clangarm64\\libexec\\git-core', + }, + ); +}); + +test('constructs relocatable macOS and Linux Git environments', () => { + assert.deepEqual( + bundledGitEnvironment({ + platform: 'darwin', + arch: 'arm64', + rootPath: '/Applications/Maka.app/Contents/Resources/git', + executablePath: '/Applications/Maka.app/Contents/Resources/git/bin/git', + }), + { + PATH: '/Applications/Maka.app/Contents/Resources/git/bin', + GIT_EXEC_PATH: '/Applications/Maka.app/Contents/Resources/git/libexec/git-core', + GIT_TEMPLATE_DIR: '/Applications/Maka.app/Contents/Resources/git/share/git-core/templates', + }, + ); + assert.deepEqual( + bundledGitEnvironment({ + platform: 'linux', + arch: 'x64', + rootPath: '/opt/maka/resources/git', + executablePath: '/opt/maka/resources/git/bin/git', + }), + { + PATH: '/opt/maka/resources/git/bin', + GIT_EXEC_PATH: '/opt/maka/resources/git/libexec/git-core', + GIT_TEMPLATE_DIR: '/opt/maka/resources/git/share/git-core/templates', + PREFIX: '/opt/maka/resources/git', + GIT_SSL_CAINFO: '/opt/maka/resources/git/ssl/cacert.pem', + }, + ); +}); diff --git a/packages/storage/src/dugite-native-environment.ts b/packages/storage/src/dugite-native-environment.ts new file mode 100644 index 0000000000..287cd0d73c --- /dev/null +++ b/packages/storage/src/dugite-native-environment.ts @@ -0,0 +1,35 @@ +import { posix, win32 } from 'node:path'; + +export interface BundledGitEnvironmentInput { + readonly platform: NodeJS.Platform; + readonly arch: string; + readonly rootPath: string; + readonly executablePath: string; +} + +export function bundledGitEnvironment(input: BundledGitEnvironmentInput): NodeJS.ProcessEnv { + const path = input.platform === 'win32' ? win32 : posix; + const env: NodeJS.ProcessEnv = { + PATH: path.dirname(input.executablePath), + }; + if (input.platform === 'win32') { + const platformRoot = path.join( + input.rootPath, + input.arch === 'x64' ? 'mingw64' : input.arch === 'arm64' ? 'clangarm64' : 'mingw32', + ); + env.PATH = [ + path.dirname(input.executablePath), + path.join(platformRoot, 'bin'), + path.join(platformRoot, 'usr', 'bin'), + ].join(';'); + env.GIT_EXEC_PATH = path.join(platformRoot, 'libexec', 'git-core'); + return env; + } + env.GIT_EXEC_PATH = path.join(input.rootPath, 'libexec', 'git-core'); + env.GIT_TEMPLATE_DIR = path.join(input.rootPath, 'share', 'git-core', 'templates'); + if (input.platform === 'linux') { + env.PREFIX = input.rootPath; + env.GIT_SSL_CAINFO = path.join(input.rootPath, 'ssl', 'cacert.pem'); + } + return env; +} diff --git a/packages/storage/src/git-workspace-service.ts b/packages/storage/src/git-workspace-service.ts index 6aba50ae87..a1d07e2e6c 100644 --- a/packages/storage/src/git-workspace-service.ts +++ b/packages/storage/src/git-workspace-service.ts @@ -14,6 +14,7 @@ import { import { dirname, isAbsolute, join, normalize, relative, resolve } from 'node:path'; import { promisify } from 'node:util'; import { withArtifactWriterLock } from './artifact-writer-lock.js'; +import { bundledGitEnvironment } from './dugite-native-environment.js'; import { registerManagedBaselineReceiptAuthorityInternal } from './managed-baseline-receipt-authority-internal.js'; const execFileAsync = promisify(execFile); @@ -140,11 +141,27 @@ export class GitWorkspaceServiceError extends Error { } } -export interface VerifiedGitRuntimeInput { +interface GitRuntimeExecutableIdentity { readonly executablePath: string; readonly expectedSha256: `sha256:${string}`; } +export type VerifiedGitRuntimeInput = GitRuntimeExecutableIdentity & + ( + | { + readonly distribution?: undefined; + readonly runtimeIdentitySha256?: undefined; + } + | { + readonly distribution: { + readonly kind: 'dugite_native_v1'; + readonly rootPath: string; + }; + /** Stable identity of the complete declared distribution. */ + readonly runtimeIdentitySha256: `sha256:${string}`; + } + ); + export interface CreateGitWorkspaceServiceInput { readonly storageRoot: string; readonly gitRuntime: VerifiedGitRuntimeInput; @@ -1593,12 +1610,32 @@ class GitWorkspaceServiceImpl implements GitWorkspaceService { class VerifiedGitRuntime { constructor(private readonly input: VerifiedGitRuntimeInput) { - if (!isAbsolute(input.executablePath) || !SHA256_PATTERN.test(input.expectedSha256)) { + if ( + !isAbsolute(input.executablePath) || + !SHA256_PATTERN.test(input.expectedSha256) || + (input.distribution !== undefined && !SHA256_PATTERN.test(input.runtimeIdentitySha256 ?? '')) + ) { throw new GitWorkspaceServiceError( 'git_runtime_unavailable', 'Managed workspace requires an absolute Git executable and SHA-256 digest', ); } + if (input.distribution) { + const rootPath = input.distribution.rootPath; + const executableRelativePath = relative(rootPath, input.executablePath); + if ( + !isAbsolute(rootPath) || + executableRelativePath === '' || + executableRelativePath === '..' || + executableRelativePath.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) || + isAbsolute(executableRelativePath) + ) { + throw new GitWorkspaceServiceError( + 'git_runtime_unavailable', + 'Bundled Git executable must belong to its declared distribution root', + ); + } + } } verify(): Promise<{ executablePath: string; digest: `sha256:${string}` }> { @@ -1773,7 +1810,7 @@ class VerifiedGitRuntime { } return { executablePath, - digest: executableDigest, + digest: this.input.runtimeIdentitySha256 ?? executableDigest, }; } catch (error) { if (error instanceof GitWorkspaceServiceError) throw error; @@ -2802,6 +2839,17 @@ function isolatedGitEnvironment( LC_ALL: 'C', PATH: dirname(executablePath), }; + if (input.distribution?.kind === 'dugite_native_v1') { + Object.assign( + env, + bundledGitEnvironment({ + platform: process.platform, + arch: process.arch, + rootPath: input.distribution.rootPath, + executablePath, + }), + ); + } for (const name of ['SystemRoot', 'WINDIR', 'COMSPEC', 'TMP', 'TEMP', 'TMPDIR']) { if (process.env[name]) env[name] = process.env[name]; } From 0fd800aa267584457ff5ead9169841727437f711 Mon Sep 17 00:00:00 2001 From: jackwener Date: Sat, 22 Aug 2026 19:41:37 +0800 Subject: [PATCH 7/8] fix(cli): declare transcript mid-turn behavior --- packages/cli/src/pi-tui-runner.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/pi-tui-runner.ts b/packages/cli/src/pi-tui-runner.ts index 516c309d44..bf0f925faf 100644 --- a/packages/cli/src/pi-tui-runner.ts +++ b/packages/cli/src/pi-tui-runner.ts @@ -2876,6 +2876,8 @@ export async function runMakaPiTui(input: MakaPiTuiInput): Promise { }, transcript: { description: primaryGuidance.commands.transcript, + // Read-only overlay; it is explicitly available while a turn is running. + midTurn: 'local', run: (parts: string[]) => { if (parts.length !== 1) { state.entries.push({ From 3e3fea31e8540c0863989b3490afbcb5ddef0436 Mon Sep 17 00:00:00 2001 From: jackwener Date: Sat, 22 Aug 2026 19:52:19 +0800 Subject: [PATCH 8/8] fix(runtime): pass identity in packaged Windows smoke --- scripts/verify-windows-sandbox-e2e.mjs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/verify-windows-sandbox-e2e.mjs b/scripts/verify-windows-sandbox-e2e.mjs index 853aa00966..73c3840354 100644 --- a/scripts/verify-windows-sandbox-e2e.mjs +++ b/scripts/verify-windows-sandbox-e2e.mjs @@ -1,5 +1,5 @@ import { existsSync } from 'node:fs'; -import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises'; +import { mkdir, mkdtemp, readFile, realpath, rm, stat, writeFile } from 'node:fs/promises'; import { homedir, tmpdir } from 'node:os'; import { basename, dirname, join, resolve } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; @@ -88,13 +88,18 @@ export async function verifyWindowsSandboxWorkerE2E(appDirectoryPath) { platform: 'win32', getLaunchSpec, }); - const execute = (operation) => client.execute({ operation, cwd: workspace, mode: 'ask' }); + const execute = (operation, expectedIdentity) => + client.execute({ operation, cwd: workspace, mode: 'ask', expectedIdentity }); // Exact writes stay exact in the preview: the target is pre-seeded so the // grant covers only this file object, never its parent directory. const insidePath = join(workspace, 'inside.txt'); await writeFile(insidePath, 'seeded'); - await execute({ kind: 'write', path: insidePath, content: 'packaged-relay-ok' }); + const insideMetadata = await stat(insidePath, { bigint: true }); + await execute( + { kind: 'write', path: insidePath, content: 'packaged-relay-ok' }, + { dev: String(insideMetadata.dev), ino: String(insideMetadata.ino) }, + ); assertCondition( (await readFile(insidePath, 'utf8')) === 'packaged-relay-ok', 'Sandboxed write did not land in the workspace.',