diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 59f226a..ac3a5e2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,10 +1,6 @@ name: build on: - push: - branches: ["main"] - - pull_request: workflow_call: secrets: BUILDBUDDY_API_KEY: @@ -29,8 +25,11 @@ jobs: env: BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} run: | - bazel build --config release --config remote \ + bazel build \ + --config release \ + --config remote \ --remote_header=x-buildbuddy-api-key=$BUILDBUDDY_API_KEY \ + --@libarchive//:use_mbedtls=true \ //:for_all_platforms mv bazel-out/linux_amd64_musl-opt/bin/pkgutil pkgutil_linux_amd64 mv bazel-out/linux_arm64_musl-opt/bin/pkgutil pkgutil_linux_arm64 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e78751a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,66 @@ +name: pkgutil-tests + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + test: + name: Test (${{ matrix.name }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - name: macos-arm64 + runner: macos-latest + - name: linux-amd64 + runner: ubuntu-latest + # - name: linux-arm64 + # runner: ubuntu-24.04-arm64 + # rules_zig doesn't work there :( + - name: windows-amd64 + runner: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Bazelisk + uses: bazel-contrib/setup-bazel@0.18.0 + with: + # Avoid downloading Bazel every time. + bazelisk-cache: true + # Store build cache per workflow. + disk-cache: ${{ github.workflow }} + # Share repository cache between workflows. + repository-cache: true + + - name: bazel build //:for_all_platforms + env: + BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} + shell: bash + run: | + bazel build \ + --config release \ + --config remote \ + --remote_header=x-buildbuddy-api-key=$BUILDBUDDY_API_KEY \ + --@libarchive//:use_mbedtls=true \ + //:for_all_platforms + + - name: bazel test //... + if: matrix.name != 'windows-amd64' + env: + BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} + shell: bash + run: | + bazel test --test_output=errors \ + --config release \ + --config remote \ + --remote_header=x-buildbuddy-api-key=$BUILDBUDDY_API_KEY \ + --@libarchive//:use_mbedtls=true \ + //... diff --git a/BUILD.bazel b/BUILD.bazel index 693e034..5cf71f1 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -34,7 +34,7 @@ cc_binary( platform_transition_binary( name = "for_" + platform.split(":")[1], binary = ":pkgutil", - tags = ["no-remote"] if platform == "@toolchains_llvm_bootstrapped//platforms:macos_arm64" else [], + tags = ["manual"] + ["no-remote"] if platform == "@toolchains_llvm_bootstrapped//platforms:macos_arm64" else [], target_platform = platform, ) for platform in PLATFORMS @@ -46,4 +46,5 @@ filegroup( ":for_" + platform.split(":")[1] for platform in PLATFORMS ], + tags = ["manual"], ) diff --git a/MODULE.bazel b/MODULE.bazel index 229df11..c64b21e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -11,6 +11,10 @@ bazel_dep(name = "libarchive", version = "3.8.1") bazel_dep(name = "toolchains_llvm_bootstrapped", version = "0.4.1") bazel_dep(name = "rules_cc", version = "0.2.14") +bazel_dep(name = "bazel_skylib", version = "1.9.0", dev_dependency = True) +bazel_dep(name = "rules_shell", version = "0.5.0", dev_dependency = True) +bazel_dep(name = "rules_zig", version = "0.12.3", dev_dependency = True) + register_toolchains( "@toolchains_llvm_bootstrapped//toolchain:all", ) @@ -45,3 +49,33 @@ single_version_override( module_name = "mbedtls", version = "3.6.0.bcr.1", ) + +single_version_override( + module_name = "zlib", + version = "1.3.1.bcr.8", +) + +## TESTS + +http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") + +http_file( + name = "component_pkg", + urls = ["https://swcdn.apple.com/content/downloads/52/01/082-41241-A_0747ZN8FHV/dectd075r63pppkkzsb75qk61s0lfee22j/CLTools_macOSNMOS_SDK.pkg"], + downloaded_file_path = "component.pkg", + sha256 = "ba3453d62b3d2babf67f3a4a44e8073d6555c85f114856f4390a1f53bd76e24a", +) + +http_file( + name = "product_pkg", + urls = ["https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg"], + downloaded_file_path = "product.pkg", + sha256 = "50b709f72cb5ed87d5882901923face981dd657569717761832c36db3bf08238", +) + +archive_override( + module_name = "toolchains_llvm_bootstrapped", + urls = ["https://github.com/cerisier/toolchains_llvm_bootstrapped/archive/9799c1d0d976409451bbe5275e8d3d607e442956.tar.gz"], + strip_prefix = "toolchains_llvm_bootstrapped-9799c1d0d976409451bbe5275e8d3d607e442956", + integrity = "sha256-SJuGXePryLXuDemHsvfajLFMHT+vonxstVddkJlhbrg=", +) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index e62b2b8..81efe96 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -22,7 +22,8 @@ "https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca", - "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/source.json": "ffab9254c65ba945f8369297ad97ca0dec213d3adc6e07877e23a48624a8b456", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.21.2/MODULE.bazel": "276347663a25b0d5bd6cad869252bea3e160c4d980e764b15f3bae7f80b30624", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.21.2/source.json": "f42051fa42629f0e59b7ac2adf0a55749144b11f1efcd8c697f0ee247181e526", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", @@ -60,7 +61,8 @@ "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", - "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0", "https://bcr.bazel.build/modules/buildozer/8.2.1/MODULE.bazel": "61e9433c574c2bd9519cad7fa66b9c1d2b8e8d5f3ae5d6528a2c2d26e68d874d", "https://bcr.bazel.build/modules/buildozer/8.2.1/source.json": "7c33f6a26ee0216f85544b4bca5e9044579e0219b6898dd653f5fb449cf2e484", "https://bcr.bazel.build/modules/bzip2/1.0.8.bcr.3/MODULE.bazel": "29ecf4babfd3c762be00d7573c288c083672ab60e79c833ff7f49ee662e54471", @@ -147,11 +149,14 @@ "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", @@ -162,9 +167,12 @@ "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd", "https://bcr.bazel.build/modules/rules_jvm_external/6.7/source.json": "5426f412d0a7fc6b611643376c7e4a82dec991491b9ce5cb1cfdd25fe2e92be4", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", @@ -195,6 +203,7 @@ "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592", + "https://bcr.bazel.build/modules/rules_shell/0.5.0/MODULE.bazel": "8c8447370594d45539f66858b602b0bb2cb2d3401a4ebb9ad25830c59c0f366d", "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c", "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca", @@ -202,20 +211,23 @@ "https://bcr.bazel.build/modules/rules_swift/2.4.0/MODULE.bazel": "1639617eb1ede28d774d967a738b4a68b0accb40650beadb57c21846beab5efd", "https://bcr.bazel.build/modules/rules_swift/3.1.2/MODULE.bazel": "72c8f5cf9d26427cee6c76c8e3853eb46ce6b0412a081b2b6db6e8ad56267400", "https://bcr.bazel.build/modules/rules_swift/3.1.2/source.json": "e85761f3098a6faf40b8187695e3de6d97944e98abd0d8ce579cb2daf6319a66", + "https://bcr.bazel.build/modules/rules_zig/0.12.3/MODULE.bazel": "d2917ed1d6d0510553101be587bd2d2b236a63ac20612c8f26a7fcfb2fb5c47f", + "https://bcr.bazel.build/modules/rules_zig/0.12.3/source.json": "9b599ada6d04fbe0144b8e4092bdc5540d49b8c62a3a13988d6ab1f03ba69ae9", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd", "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f", "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b", "https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468", + "https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351", "https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c", "https://bcr.bazel.build/modules/tar.bzl/0.6.0/source.json": "4a620381df075a16cb3a7ed57bd1d05f7480222394c64a20fa51bdb636fda658", - "https://bcr.bazel.build/modules/toolchains_llvm_bootstrapped/0.4.1/MODULE.bazel": "7f52827739e887a7782f68a0124357e84548b38ba4e2aaf26d055a8a26d76a53", - "https://bcr.bazel.build/modules/toolchains_llvm_bootstrapped/0.4.1/source.json": "7ed948f7085d5f6850409df9bca33240b432ce32068e5d0e94f403f0331da4e5", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/MODULE.bazel": "b573395fe63aef4299ba095173e2f62ccfee5ad9bbf7acaa95dba73af9fc2b38", "https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/source.json": "3f3fbaeafecaf629877ad152a2c9def21f8d330d91aa94c5dc75bbb98c10b8b8", @@ -509,6 +521,130 @@ } } } + }, + "@@rules_zig+//zig:extensions.bzl%zig": { + "general": { + "bzlTransitiveDigest": "0NkKAkEkkyfLJa7aiV5uv5YbQUCgvN/mFwybs5Vg/Dc=", + "usagesDigest": "G2bWsnXoUXPlqcJyD4ShMah52kUPWKIQdDx2aRF75P4=", + "recordedInputs": [ + "REPO_MAPPING:rules_zig+,bazel_skylib bazel_skylib+", + "REPO_MAPPING:rules_zig+,bazel_tools bazel_tools", + "FILE:@@rules_zig+//zig/private/versions.json f1c2df3a27b39eac2e69bcb8b37c455d3136ad418c35b91e22f4020dcd5d179b" + ], + "generatedRepoSpecs": { + "zig_0.15.2_aarch64-linux": { + "repoRuleId": "@@rules_zig+//zig/private/repo:zig_repository.bzl%zig_repository", + "attributes": { + "url": "https://ziglang.org/download/0.15.2/zig-aarch64-linux-0.15.2.tar.xz", + "mirrors": [], + "sha256": "958ed7d1e00d0ea76590d27666efbf7a932281b3d7ba0c6b01b0ff26498f667f", + "zig_version": "0.15.2", + "platform": "aarch64-linux" + } + }, + "zig_0.15.2_aarch64-macos": { + "repoRuleId": "@@rules_zig+//zig/private/repo:zig_repository.bzl%zig_repository", + "attributes": { + "url": "https://ziglang.org/download/0.15.2/zig-aarch64-macos-0.15.2.tar.xz", + "mirrors": [], + "sha256": "3cc2bab367e185cdfb27501c4b30b1b0653c28d9f73df8dc91488e66ece5fa6b", + "zig_version": "0.15.2", + "platform": "aarch64-macos" + } + }, + "zig_0.15.2_aarch64-windows": { + "repoRuleId": "@@rules_zig+//zig/private/repo:zig_repository.bzl%zig_repository", + "attributes": { + "url": "https://ziglang.org/download/0.15.2/zig-aarch64-windows-0.15.2.zip", + "mirrors": [], + "sha256": "b926465f8872bf983422257cd9ec248bb2b270996fbe8d57872cca13b56fc370", + "zig_version": "0.15.2", + "platform": "aarch64-windows" + } + }, + "zig_0.15.2_x86_64-linux": { + "repoRuleId": "@@rules_zig+//zig/private/repo:zig_repository.bzl%zig_repository", + "attributes": { + "url": "https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz", + "mirrors": [], + "sha256": "02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239", + "zig_version": "0.15.2", + "platform": "x86_64-linux" + } + }, + "zig_0.15.2_x86_64-macos": { + "repoRuleId": "@@rules_zig+//zig/private/repo:zig_repository.bzl%zig_repository", + "attributes": { + "url": "https://ziglang.org/download/0.15.2/zig-x86_64-macos-0.15.2.tar.xz", + "mirrors": [], + "sha256": "375b6909fc1495d16fc2c7db9538f707456bfc3373b14ee83fdd3e22b3d43f7f", + "zig_version": "0.15.2", + "platform": "x86_64-macos" + } + }, + "zig_0.15.2_x86_64-windows": { + "repoRuleId": "@@rules_zig+//zig/private/repo:zig_repository.bzl%zig_repository", + "attributes": { + "url": "https://ziglang.org/download/0.15.2/zig-x86_64-windows-0.15.2.zip", + "mirrors": [], + "sha256": "3a0ed1e8799a2f8ce2a6e6290a9ff22e6906f8227865911fb7ddedc3cc14cb0c", + "zig_version": "0.15.2", + "platform": "x86_64-windows" + } + }, + "zig_toolchains": { + "repoRuleId": "@@rules_zig+//zig/private/repo:toolchains_repo.bzl%toolchains_repo", + "attributes": { + "names": [ + "zig_0.15.2_aarch64-linux", + "zig_0.15.2_aarch64-macos", + "zig_0.15.2_aarch64-windows", + "zig_0.15.2_x86_64-linux", + "zig_0.15.2_x86_64-macos", + "zig_0.15.2_x86_64-windows" + ], + "labels": [ + "@zig_0.15.2_aarch64-linux//:zig_toolchain", + "@zig_0.15.2_aarch64-macos//:zig_toolchain", + "@zig_0.15.2_aarch64-windows//:zig_toolchain", + "@zig_0.15.2_x86_64-linux//:zig_toolchain", + "@zig_0.15.2_x86_64-macos//:zig_toolchain", + "@zig_0.15.2_x86_64-windows//:zig_toolchain" + ], + "zig_versions": [ + "0.15.2", + "0.15.2", + "0.15.2", + "0.15.2", + "0.15.2", + "0.15.2" + ], + "exec_lengths": [ + 2, + 2, + 2, + 2, + 2, + 2 + ], + "exec_constraints": [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + "@platforms//os:macos", + "@platforms//cpu:aarch64", + "@platforms//os:windows", + "@platforms//cpu:aarch64", + "@platforms//os:linux", + "@platforms//cpu:x86_64", + "@platforms//os:macos", + "@platforms//cpu:x86_64", + "@platforms//os:windows", + "@platforms//cpu:x86_64" + ] + } + } + } + } } }, "facts": {} diff --git a/pkgutil.c b/pkgutil.c index bd63bd4..74026c3 100644 --- a/pkgutil.c +++ b/pkgutil.c @@ -23,17 +23,29 @@ static const char *const nested_archive_names[] = {"Payload", "Scripts", NULL}; static const int disk_flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | - ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS | + ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; +enum { + opt_include = 256, + opt_exclude, + opt_strip_components, +}; + static const struct option { const char *name; int required; int equivalent; -} pkg_longopts[] = {{"expand", 0, 'X'}, {"expand-full", 0, 'E'}, - {"force", 0, 'f'}, {"help", 0, 'h'}, - {"verbose", 0, 'v'}, {NULL, 0, 0}}; +} pkg_longopts[] = {{"expand", 0, 'X'}, + {"expand-full", 0, 'E'}, + {"force", 0, 'f'}, + {"help", 0, 'h'}, + {"include", 1, opt_include}, + {"exclude", 1, opt_exclude}, + {"strip-components", 1, opt_strip_components}, + {"verbose", 0, 'v'}, + {NULL, 0, 0}}; static void fail_archive(struct archive *a, const char *ctx) { fprintf(stderr, "%s: %s\n", ctx, @@ -54,12 +66,21 @@ static void usage(FILE *out) { " --verbose, -v Show contextual information and " "format for easy reading\n" " --force, -f Perform all operations without asking " - "for confirmation\n\n" + "for confirmation\n" + " --include PATTERN Only include paths matching PATTERN\n" + " --exclude PATTERN Exclude paths matching PATTERN\n" + " --strip-components N Strip N leading path components\n" "File Commands:\n" " --expand PKG DIR Write flat package entries to DIR\n" " --expand-full PKG DIR Fully expand package contents to DIR\n"); } +static char *strip_components_path(const char *path, int strip); +static int apply_strip_components(struct archive_entry *e, int strip); +static int match_excluded_with_prefix(struct archive *match, + struct archive_entry *e, + const char *prefix); + static int pkg_getopt(int *argc, char ***argv, const char **arg) { enum { state_start = 0, state_next_word, state_short, state_long }; static int state = state_start; @@ -239,12 +260,14 @@ static int astream_close_cb(struct archive *a, void *client_data) { } static void extract_nested_archive_from_stream(struct astream *in, - const char *outdir, int force) { + const char *outdir, int flags, + struct archive *match, + int strip_components, + const char *prefix) { struct archive *a = archive_read_new(); struct archive *disk = archive_write_disk_new(); struct archive_entry *e; int r; - int flags; char *cwd = NULL; if (a == NULL || disk == NULL) { @@ -259,11 +282,6 @@ static void extract_nested_archive_from_stream(struct astream *in, fail_archive(a, "open nested archive"); } - flags = disk_flags; - if (force) { - flags |= ARCHIVE_EXTRACT_UNLINK; - } - archive_write_disk_set_options(disk, flags); archive_write_disk_set_standard_lookup(disk); @@ -284,6 +302,19 @@ static void extract_nested_archive_from_stream(struct astream *in, fail_archive(a, "read nested header"); } + if (match != NULL) { + r = match_excluded_with_prefix(match, e, prefix); + if (r != 0) { + archive_read_data_skip(a); + continue; + } + } + + if (apply_strip_components(e, strip_components)) { + archive_read_data_skip(a); + continue; + } + r = archive_read_extract2(a, e, disk); if (r != ARCHIVE_OK) { fail_archive(a, "extract nested entry"); @@ -313,6 +344,117 @@ static int should_be_treated_as_nested_archive(const char *path) { return (0); } +static char *strip_components_path(const char *path, int strip) { + const char *p = path; + int remaining = strip; + + if (p == NULL) { + return (NULL); + } + if (strip <= 0) { + char *dup = strdup(p); + if (dup == NULL) { + fail_errno("strdup"); + } + return (dup); + } + + while (remaining > 0) { + switch (*p++) { + case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': +#endif + remaining--; + break; + case '\0': + return (NULL); + } + } + + for (;;) { + switch (*p) { + case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': +#endif + ++p; + break; + case '\0': + return (NULL); + default: { + char *out = strdup(p); + if (out == NULL) { + fail_errno("strdup"); + } + return (out); + } + } + } +} + +static int apply_strip_components(struct archive_entry *e, int strip) { + if (strip <= 0) { + return (0); + } + + const char *name = archive_entry_pathname(e); + char *stripped = strip_components_path(name, strip); + if (stripped == NULL) { + return (1); + } + archive_entry_set_pathname(e, stripped); + free(stripped); + + const char *hardlink = archive_entry_hardlink(e); + if (hardlink != NULL) { + stripped = strip_components_path(hardlink, strip); + if (stripped == NULL) { + return (1); + } + archive_entry_set_hardlink(e, stripped); + free(stripped); + } + return (0); +} + +static int match_excluded_with_prefix(struct archive *match, + struct archive_entry *e, + const char *prefix) { + if (match == NULL) { + return (0); + } + if (prefix == NULL || prefix[0] == '\0') { + return archive_match_excluded(match, e); + } + const char *orig = archive_entry_pathname(e); + if (orig == NULL) { + return archive_match_excluded(match, e); + } + char *orig_copy = strdup(orig); + if (orig_copy == NULL) { + fail_errno("strdup"); + } + size_t plen = strlen(prefix); + size_t olen = strlen(orig_copy); + size_t total = plen + 1 + olen + 1; + char *buf = malloc(total); + if (buf == NULL) { + free(orig_copy); + fail_errno("malloc"); + } + memcpy(buf, prefix, plen); + buf[plen] = '/'; + memcpy(buf + plen + 1, orig_copy, olen + 1); + + archive_entry_set_pathname(e, buf); + int r = archive_match_excluded(match, e); + archive_entry_set_pathname(e, orig_copy); + free(buf); + free(orig_copy); + return (r); +} + static int contains_dotdot_segment(const char *path) { const char *p = path; while (*p != '\0') { @@ -402,6 +544,7 @@ int main(int argc, char **argv) { const char *xar_path = NULL; const char *outdir = NULL; struct archive *xar; + struct archive *match = NULL; struct archive *disk; struct archive_entry *e; int r; @@ -410,6 +553,7 @@ int main(int argc, char **argv) { int force = 0; int do_expand = 0; int do_expand_full = 0; + int strip_components = 0; int flags; while ((opt = pkg_getopt(&argc, &argv, &arg)) != -1) { @@ -428,6 +572,37 @@ int main(int argc, char **argv) { case 'E': do_expand_full = 1; break; + case opt_include: + if (match == NULL) { + match = archive_match_new(); + if (match == NULL) { + fail_errno("archive_match_new"); + } + } + r = archive_match_include_pattern(match, arg); + if (r != ARCHIVE_OK) { + fail_archive(match, "include pattern"); + } + break; + case opt_exclude: + if (match == NULL) { + match = archive_match_new(); + if (match == NULL) { + fail_errno("archive_match_new"); + } + } + r = archive_match_exclude_pattern(match, arg); + if (r != ARCHIVE_OK) { + fail_archive(match, "exclude pattern"); + } + break; + case opt_strip_components: + strip_components = atoi(arg); + if (strip_components < 0) { + fprintf(stderr, "invalid strip-components: %s\n", arg); + return (2); + } + break; default: usage(stderr); return (2); @@ -454,6 +629,10 @@ int main(int argc, char **argv) { fail_errno("archive_read_new"); } + if (match != NULL) { + archive_match_set_inclusion_recursion(match, 1); + } + disk = archive_write_disk_new(); if (disk == NULL) { fail_errno("archive_write_disk_new"); @@ -463,6 +642,14 @@ int main(int argc, char **argv) { if (force) { flags |= ARCHIVE_EXTRACT_UNLINK; } + // Force no-same-owner behavior + flags &= ~ARCHIVE_EXTRACT_OWNER; + + flags &= ~(ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | + ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); +#ifdef ARCHIVE_EXTRACT_MAC_METADATA + flags &= ~ARCHIVE_EXTRACT_MAC_METADATA; +#endif archive_write_disk_set_options(disk, flags); archive_write_disk_set_standard_lookup(disk); @@ -485,7 +672,26 @@ int main(int argc, char **argv) { while ((r = archive_read_next_header(xar, &e)) == ARCHIVE_OK) { const char *p = archive_entry_pathname(e); char *rel = normalize_rel_path(p); + archive_entry_set_pathname(e, rel); + if (match != NULL) { + r = archive_match_excluded(match, e); + if (r != 0) { + archive_read_data_skip(xar); + free(rel); + continue; + } + } int is_nested = should_be_treated_as_nested_archive(rel); + if (apply_strip_components(e, strip_components)) { + archive_read_data_skip(xar); + free(rel); + continue; + } + free(rel); + rel = strdup(archive_entry_pathname(e)); + if (rel == NULL) { + fail_errno("strdup"); + } if (do_expand_full && is_nested) { mkdirs_for_path(rel); @@ -500,11 +706,11 @@ int main(int argc, char **argv) { .eof = 0, }; - extract_nested_archive_from_stream(&in, rel, force); + extract_nested_archive_from_stream(&in, rel, flags, match, + strip_components, rel); } free(rel); } else { - archive_entry_set_pathname(e, rel); r = archive_read_extract2(xar, e, disk); if (r != ARCHIVE_OK) { free(rel); @@ -516,5 +722,8 @@ int main(int argc, char **argv) { archive_write_free(disk); archive_read_free(xar); + if (match != NULL) { + archive_match_free(match); + } return (0); } diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel new file mode 100644 index 0000000..6902fbe --- /dev/null +++ b/tests/BUILD.bazel @@ -0,0 +1,192 @@ +load("@bazel_lib//lib:run_binary.bzl", "run_binary") +load("@rules_zig//zig:defs.bzl", "zig_binary") +load("@bazel_skylib//rules:native_binary.bzl", "native_test") +load(":exec_test.bzl", "exec_test") + +zig_binary( + name = "test", + main = "test.zig", + testonly = True, + tags = ["manual"], +) + +run_binary( + name = "pkgutil_component_expand_action", + tool = "//:pkgutil", + srcs = [ + "@component_pkg//file", + ], + args = [ + "--expand", + "$(location @component_pkg//file)", + "$@", + ], + out_dirs = ["pkgutil-component-expand"], + testonly = True, + tags = ["manual"], +) + +run_binary( + name = "pkgutil_component_expand_full_action", + tool = "//:pkgutil", + srcs = [ + "@component_pkg//file", + ], + args = [ + "--exclude", + "Payload/Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/Headers/ruby/ruby", + "--expand-full", + "$(location @component_pkg//file)", + "$@", + ], + out_dirs = ["pkgutil-component-expand-full"], + testonly = True, + tags = ["manual"], +) + +run_binary( + name = "pkgutil_product_expand_action", + tool = "//:pkgutil", + srcs = [ + "@product_pkg//file", + ], + args = [ + "--expand", + "$(location @product_pkg//file)", + "$@", + ], + out_dirs = ["pkgutil-product-expand"], + testonly = True, + tags = ["manual"], +) + +run_binary( + name = "pkgutil_product_expand_full_action", + tool = "//:pkgutil", + srcs = [ + "@product_pkg//file", + ], + args = [ + "--exclude", + "Python_Command_Line_Tools.pkg/Payload/py*", + "--exclude", + "Python_Command_Line_Tools.pkg/Payload/idle*", + "--exclude", + # run_binary doesn't like spaces in args + "Python_Applications.pkg/Payload/Python*", + "--exclude", + "Python_Framework.pkg/Payload/Versions/Current", + "--exclude", + "Python_Framework.pkg/Payload/Versions/3.14/Frameworks/Tcl.framework/PrivateHeaders", + "--exclude", + "Python_Framework.pkg/Payload/Versions/3.14/Frameworks/Tk.framework/PrivateHeaders", + "--exclude", + "Python_Framework.pkg/Payload/Headers", + "--exclude", + "Python_Framework.pkg/Payload/Python", + "--exclude", + "Python_Framework.pkg/Payload/Resources", + "--expand-full", + "$(location @product_pkg//file)", + "$@", + ], + out_dirs = ["pkgutil-product-expand-full"], + testonly = True, + tags = ["manual"], +) + +exec_test( + native_test, + name = "pkgutil_component_expand_test", + src = ":test", + args = [ + "-e", + "$(location :pkgutil_component_expand_action)/Bom", + "$(location :pkgutil_component_expand_action)/Payload", + "$(location :pkgutil_component_expand_action)/PackageInfo", + ], + data = [ + ":pkgutil_component_expand_action", + ], +) + +exec_test( + native_test, + name = "pkgutil_component_expand_full_test", + src = ":test", + args = [ + "-ne", + "$(location :pkgutil_component_expand_full_action)/Payload/Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/Headers/ruby/ruby", + ], + data = [ + ":pkgutil_component_expand_full_action", + ], +) + +exec_test( + native_test, + name = "pkgutil_component_expand_full_exists_test", + src = ":test", + args = [ + "-e", + "$(location :pkgutil_component_expand_full_action)/Payload/Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/System/Cryptexes/OS/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Headers/JavaScriptCore.h", + ], + data = [ + ":pkgutil_component_expand_full_action", + ], +) + +exec_test( + native_test, + name = "pkgutil_product_expand_test", + src = ":test", + args = [ + "-e", + "$(location :pkgutil_product_expand_action)/Python_Install_Pip.pkg/Scripts", + "$(location :pkgutil_product_expand_action)/Python_Documentation.pkg/Payload", + "$(location :pkgutil_product_expand_action)/Python_Documentation.pkg/Scripts", + "$(location :pkgutil_product_expand_action)/PythonT_Framework.pkg/Payload", + "$(location :pkgutil_product_expand_action)/PythonT_Framework.pkg/Scripts", + "$(location :pkgutil_product_expand_action)/Python_Applications.pkg/Payload", + "$(location :pkgutil_product_expand_action)/Python_Command_Line_Tools.pkg/Payload", + "$(location :pkgutil_product_expand_action)/Python_Shell_Profile_Updater.pkg/Scripts", + "$(location :pkgutil_product_expand_action)/Python_Framework.pkg/Payload", + ], + data = [ + ":pkgutil_product_expand_action", + ], +) + +exec_test( + native_test, + name = "pkgutil_product_expand_full_test", + src = ":test", + args = [ + "-e", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Versions/3.14/share/doc/python3.14/examples/Tools/msi/dev/dev_d.wxs", + ], + data = [ + ":pkgutil_product_expand_full_action", + ], +) + +exec_test( + native_test, + name = "pkgutil_product_expand_full_missing_test", + src = ":test", + args = [ + "-ne", + "$(location :pkgutil_product_expand_full_action)/Python_Command_Line_Tools.pkg/Payload/python3.14", + "$(location :pkgutil_product_expand_full_action)/Python_Command_Line_Tools.pkg/Payload/idle3.14", + "$(location :pkgutil_product_expand_full_action)/Python_Applications.pkg/Payload/Python 3.14/", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Versions/Current", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Versions/3.14/Frameworks/Tcl.framework/PrivateHeaders", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Versions/3.14/Frameworks/Tk.framework/PrivateHeaders", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Headers", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Python", + "$(location :pkgutil_product_expand_full_action)/Python_Framework.pkg/Payload/Resources", + ], + data = [ + ":pkgutil_product_expand_full_action", + ], +) diff --git a/tests/exec_test.bzl b/tests/exec_test.bzl new file mode 100644 index 0000000..00c3364 --- /dev/null +++ b/tests/exec_test.bzl @@ -0,0 +1,68 @@ +def exec_test(rule, name, tags=[], args=[], env={}, data = [], tools = [], **kwargs): + rule( + name = name + "_", + tags = tags + (["manual"] if "manual" not in tags else []), + data = data, + **kwargs + ) + + _exec_test( + name = name, + inner = name + "_", + tags = tags, + args = args, + env = env, + data = data, + tools = tools, + ) + +def _exec_test_impl(ctx): + inner = ctx.attr.inner[DefaultInfo] + out = ctx.outputs.executable + + ctx.actions.symlink( + target_file = inner.files_to_run.executable, + output = out, + ) + + runfiles = ctx.runfiles(ctx.files.data + ctx.files.tools) + + data = ctx.attr.data + ctx.attr.tools + + return [ + DefaultInfo( + files = depset([out]), + executable = out, + runfiles = runfiles.merge(inner.default_runfiles), + ), + RunEnvironmentInfo( + environment = { + k: ctx.expand_location(v, data) + for k, v in ctx.attr.env.items() + }, + ), + ] + +_exec_test = rule( + implementation = _exec_test_impl, + attrs = { + "inner": attr.label( + executable = True, + cfg = "exec", + mandatory = True, + ), + "data": attr.label_list( + doc = "The service manager will merge these variables into the environment when spawning the underlying binary.", + allow_files = True, + ), + "tools": attr.label_list( + doc = "The service manager will merge these variables into the environment when spawning the underlying binary.", + cfg = "exec", + allow_files = True, + ), + "env": attr.string_dict( + doc = "The service manager will merge these variables into the environment when spawning the underlying binary.", + ), + }, + test = True, +) diff --git a/tests/test.zig b/tests/test.zig new file mode 100644 index 0000000..182addd --- /dev/null +++ b/tests/test.zig @@ -0,0 +1,65 @@ +const std = @import("std"); + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + const args = try std.process.argsAlloc(allocator); + defer std.process.argsFree(allocator, args); + + if (args.len < 3) { + return usage(); + } + + const mode = args[1]; + const expect_exists = if (std.mem.eql(u8, mode, "-e")) + true + else if (std.mem.eql(u8, mode, "-ne")) + false + else { + return usage(); + }; + + var ok = true; + + var i: usize = 2; + while (i < args.len) : (i += 1) { + const path = args[i]; + const exists = pathExists(path); + + if (expect_exists and !exists) { + ok = false; + eprint("missing: {s}\n", .{path}); + } else if (!expect_exists and exists) { + ok = false; + eprint("unexpected: {s}\n", .{path}); + } + } + + if (!ok) { + std.process.exit(1); + } +} + +fn pathExists(path: []const u8) bool { + if (std.fs.cwd().access(path, .{})) |_| { + return true; + } else |err| { + if (err == error.FileNotFound) { + return false; + } + + eprint("error accessing {s}: {s}\n", .{ path, @errorName(err) }); + return false; + } +} + +fn usage() void { + eprint("usage: test.zig (-e|-ne) [path...]\n", .{}); + std.process.exit(2); +} + +fn eprint(comptime fmt: []const u8, args: anytype) void { + std.debug.print(fmt, args); +}