├── .github ├── CODEOWNERS ├── actions │ └── generate-action-code │ │ └── action.yml ├── codeql │ └── config.yml ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── deploy.yml │ ├── enable-auto-merge.yml │ ├── tag-update.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── __test__ └── TestProject │ ├── .gitignore │ ├── Package.swift │ ├── Sources │ ├── TestProject1 │ │ └── MainObject.swift │ └── TestProject2 │ │ └── SomeObject.swift │ └── Tests │ ├── TestProject1Tests │ └── MainObjectTests.swift │ └── TestProject2Tests │ └── SomeObjectTests.swift ├── action.yml ├── dist ├── index.js └── license.txt ├── package-lock.json ├── package.json ├── src └── main.ts └── tsconfig.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global 2 | * @ffried 3 | 4 | # Workflow & Deployment related files 5 | .github/* @ffried 6 | action.yml @ffried 7 | 8 | # Project & Source files 9 | /__test__ @ffried 10 | /*.json @ffried 11 | *.ts @ffried 12 | -------------------------------------------------------------------------------- /.github/actions/generate-action-code/action.yml: -------------------------------------------------------------------------------- 1 | name: Generate Action Code 2 | description: Generates the action code 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - uses: actions/setup-node@v4 8 | with: 9 | node-version: 20 10 | check-latest: true 11 | cache: 'npm' 12 | - name: Generate action code 13 | shell: bash 14 | run: | 15 | npm clean-install 16 | npm run build 17 | npm run pack 18 | -------------------------------------------------------------------------------- /.github/codeql/config.yml: -------------------------------------------------------------------------------- 1 | paths-ignore: 2 | - node_modules 3 | - __test__ 4 | - dist 5 | - lib 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | open-pull-requests-limit: 10 7 | schedule: 8 | interval: "daily" 9 | time: "07:00" 10 | timezone: "Europe/Berlin" 11 | assignees: 12 | - ffried 13 | reviewers: 14 | - ffried 15 | - package-ecosystem: "github-actions" 16 | directory: ".github/actions/generate-action-code" 17 | open-pull-requests-limit: 10 18 | schedule: 19 | interval: "daily" 20 | time: "07:00" 21 | timezone: "Europe/Berlin" 22 | assignees: 23 | - ffried 24 | reviewers: 25 | - ffried 26 | - package-ecosystem: "npm" 27 | directory: "/" 28 | open-pull-requests-limit: 10 29 | schedule: 30 | interval: "daily" 31 | time: "07:00" 32 | timezone: "Europe/Berlin" 33 | assignees: 34 | - ffried 35 | reviewers: 36 | - ffried 37 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: [ 'dist/*.js' ] 7 | pull_request: 8 | # The branches below must be a subset of the branches above 9 | branches: [ main ] 10 | schedule: 11 | - cron: '15 11 * * 1' 12 | 13 | jobs: 14 | analyze: 15 | name: Analyze 16 | runs-on: ubuntu-latest 17 | permissions: 18 | actions: read 19 | contents: read 20 | security-events: write 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | language: [ 'javascript' ] 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v3 33 | with: 34 | config-file: ./.github/codeql/config.yml 35 | languages: ${{ matrix.language }} 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v3 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v3 55 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Action Code 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: [ 'dist/*.js' ] 7 | 8 | jobs: 9 | deploy-action-code: 10 | name: Deploy Action Code 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | token: ${{ secrets.BOT_TOKEN }} 16 | - uses: ./.github/actions/generate-action-code 17 | - name: Configure repository 18 | env: 19 | BOT_USERNAME: ${{ secrets.BOT_USERNAME }} 20 | run: | 21 | git config user.name "${BOT_USERNAME}" 22 | git config user.email "${BOT_USERNAME}@users.noreply.github.com" 23 | - name: Commit changes 24 | run: | 25 | if ! git diff --exit-code --quiet; then 26 | git add . 27 | git commit -m '[AUTO] Update generated code' 28 | git push 29 | fi 30 | -------------------------------------------------------------------------------- /.github/workflows/enable-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto-merge for Dependabot PRs 2 | 3 | on: pull_request 4 | 5 | permissions: 6 | contents: write 7 | pull-requests: write 8 | 9 | jobs: 10 | enable-auto-merge: 11 | runs-on: ubuntu-latest 12 | if: ${{ github.actor == 'dependabot[bot]' }} 13 | steps: 14 | - name: Enable auto-merge 15 | run: gh pr merge --auto --rebase "${PR_URL}" 16 | env: 17 | PR_URL: ${{ github.event.pull_request.html_url }} 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/tag-update.yml: -------------------------------------------------------------------------------- 1 | name: Update Release Tags 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | update-tags: 9 | name: Update Running Releases 10 | runs-on: ubuntu-latest 11 | if: ${{ github.event.release.prerelease == false }} 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: sersoft-gmbh/running-release-tags-action@v3 15 | with: 16 | update-full-release: true 17 | github-token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: 7 | - 'dist/*.js' 8 | - '.github/workflows/tests.yml' 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | test-defaults: 14 | name: Test Default Inputs 15 | strategy: 16 | matrix: 17 | os: [ macos-14, ubuntu-latest ] 18 | runs-on: ${{ matrix.os }} 19 | container: ${{ matrix.os == 'ubuntu-latest' && 'swift:5.9' || '' }} 20 | steps: 21 | - uses: maxim-lobanov/setup-xcode@v1 22 | if: ${{ runner.os == 'macOS' }} 23 | with: 24 | xcode-version: ^15.0 25 | - uses: actions/checkout@v4 26 | - name: Generate action code 27 | if: ${{ github.event_name == 'pull_request' }} 28 | uses: ./.github/actions/generate-action-code 29 | - uses: sersoft-gmbh/xcodebuild-action@v3 30 | if: ${{ runner.os == 'macOS' }} 31 | with: 32 | spm-package: __test__/TestProject 33 | scheme: TestProject-Package 34 | destination: platform=macOS 35 | action: test 36 | enable-code-coverage: true 37 | parallel-testing-enabled: true 38 | - run: swift test --parallel --enable-code-coverage 39 | working-directory: __test__/TestProject 40 | - run: ln -s __test__/TestProject/.build .build 41 | - uses: sersoft-gmbh/swift-coverage-action@main 42 | if: ${{ github.event_name == 'push' }} 43 | - uses: './' 44 | if: ${{ github.event_name == 'pull_request' }} 45 | - name: Check for produced coverage files 46 | run: | # check that the directory exists and contains files 47 | test -d '.swiftcov' && \ 48 | test "$(find '.swiftcov' -name '*.lcov')" != '' 49 | 50 | test-customized: 51 | name: Test Customized Inputs 52 | strategy: 53 | matrix: 54 | os: [ macos-14, ubuntu-latest ] 55 | cov-format: [ 'txt', 'lcov' ] 56 | env: 57 | INPUT_DERIVED_DATA: .derived-data 58 | INPUT_SEARCH_PATHS: | 59 | __test__/TestProject/.build 60 | .derived-data 61 | INPUT_TARGET_NAME_FILTER: TestProject(1|Package).* 62 | INPUT_OUTPUT_FOLDER: .swiftcov-test 63 | runs-on: ${{ matrix.os }} 64 | container: ${{ matrix.os == 'ubuntu-latest' && 'swift:5.9' || '' }} 65 | steps: 66 | - uses: maxim-lobanov/setup-xcode@v1 67 | if: ${{ runner.os == 'macOS' }} 68 | with: 69 | xcode-version: ^15.0 70 | - uses: actions/checkout@v4 71 | - name: Generate action code 72 | if: ${{ github.event_name == 'pull_request' }} 73 | uses: ./.github/actions/generate-action-code 74 | - uses: sersoft-gmbh/xcodebuild-action@v3 75 | if: ${{ runner.os == 'macOS' }} 76 | with: 77 | spm-package: __test__/TestProject 78 | scheme: TestProject-Package 79 | destination: platform=macOS 80 | action: test 81 | enable-code-coverage: true 82 | parallel-testing-enabled: true 83 | derived-data-path: ${{ env.INPUT_DERIVED_DATA }} 84 | - run: swift test --parallel --enable-code-coverage 85 | working-directory: __test__/TestProject 86 | - uses: sersoft-gmbh/swift-coverage-action@main 87 | if: ${{ github.event_name == 'push' }} 88 | with: 89 | format: ${{ matrix.cov-format }} 90 | search-paths: ${{ env.INPUT_SEARCH_PATHS }} 91 | output: ${{ env.INPUT_OUTPUT_FOLDER }} 92 | target-name-filter: ${{ env.INPUT_TARGET_NAME_FILTER }} 93 | - uses: './' 94 | if: ${{ github.event_name == 'pull_request' }} 95 | with: 96 | format: ${{ matrix.cov-format }} 97 | search-paths: ${{ env.INPUT_SEARCH_PATHS }} 98 | output: ${{ env.INPUT_OUTPUT_FOLDER }} 99 | target-name-filter: ${{ env.INPUT_TARGET_NAME_FILTER }} 100 | - name: Check for produced coverage files 101 | env: 102 | RUNNER_OS: ${{ runner.os }} 103 | COV_FORMAT: ${{ matrix.cov-format }} 104 | run: | # check that the directory exists and contains files 105 | if [ ! -d "${INPUT_OUTPUT_FOLDER}" ]; then 106 | echo '::error::Output folder does not exist!' 107 | exit 1 108 | fi 109 | if [ "$(find "${INPUT_OUTPUT_FOLDER}" -name "TestProject2*.${COV_FORMAT}")" != '' ]; then 110 | echo '::error::Coverage files for incorrect project found!' 111 | exit 1 112 | fi 113 | if [ "${RUNNER_OS}" = 'macOS' ]; then 114 | if [ "$(find "${INPUT_OUTPUT_FOLDER}" -name "TestProject1*.${COV_FORMAT}")" = '' ]; then 115 | echo '::error::No coverage files found!' 116 | exit 1 117 | fi 118 | elif [ "${RUNNER_OS}" = 'Linux' ]; then 119 | if [ "$(find "${INPUT_OUTPUT_FOLDER}" -name "TestProjectPackage*.${COV_FORMAT}")" = '' ]; then 120 | echo '::error::No coverage files found!' 121 | exit 1 122 | fi 123 | else 124 | echo '::error::Unsupported OS!' 125 | exit 1 126 | fi 127 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .idea/ 4 | 5 | node_modules/ 6 | 7 | # Lib is just temporary - so we ignore it 8 | lib/ 9 | 10 | # Default and test output folder of coverage files generated by this action. 11 | .swiftcov/ 12 | .swiftcov-test/ 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Coverage Conversion 2 | 3 | [![Tests](https://github.com/sersoft-gmbh/swift-coverage-action/actions/workflows/tests.yml/badge.svg)](https://github.com/sersoft-gmbh/swift-coverage-action/actions/workflows/tests.yml) 4 | 5 | This action converts code coverage files from `swift test` or `xcodebuild` runs for processing with e.g. codecov. 6 | Note that this action does not run any test. Use `swift test`, `xcodebuild` or [xcodebuild-action](https://github.com/sersoft-gmbh/xcodebuild-action) for that. 7 | 8 | ## Inputs 9 | 10 | ### `search-paths` 11 | 12 | A list of search paths (one per line) that should be used for searching coverage data.
13 | Default: 14 | ``` 15 | ./.build 16 | $HOME/Library/Developer/Xcode/DerivedData 17 | ``` 18 | 19 | ### `output` 20 | 21 | The path to the output folder. Note that this folder will be deleted / overwritten by this action. 22 | You should probably put it in `.gitignore`.
23 | Default: `./.swiftcov` 24 | 25 | ### `format` 26 | 27 | The format to write the coverage files in. Can be 'lcov' or 'txt'.
28 | Default: `lcov` 29 | 30 | ### `target-name-filter` 31 | 32 | A regular expression that is used to filter coverage files by their target names. 33 | 34 | ### `ignore-conversion-failures` 35 | 36 | If `true`, conversion failures are ignored. If `fail-on-empty-output` is also set to `true`, the action might still fail if all conversions fail.
37 | Default: `'false'` 38 | 39 | ### `fail-on-empty-output` 40 | 41 | If `true`, the action fails if no coverage files were found (output is still set to an empty array).
42 | Default: `'false'` 43 | 44 | ## Outputs 45 | 46 | ### `files` 47 | 48 | The JSON encoded array of (absolute) file paths that were written. They are all contained inside the directory specified in the `output` input. 49 | 50 | ## Example Usage 51 | 52 | Use the following snippet after running tests with Swift or Xcode to convert those coverage files: 53 | ```yaml 54 | uses: sersoft-gmbh/swift-coverage-action@v4 55 | ``` 56 | 57 | ### Codecov Action (v2 or later) 58 | 59 | To use this action together with [codecov/codecov-action](https://github.com/codecov/codecov-action), you need to convert the output to a comma separated string: 60 | ```yaml 61 | - uses: sersoft-gmbh/swift-coverage-action@v4 62 | id: coverage-files 63 | - uses: codecov/codecov-action@v3 64 | with: 65 | token: ${{ secrets.CODECOV_TOKEN }} 66 | files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} 67 | ``` 68 | -------------------------------------------------------------------------------- /__test__/TestProject/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /.swiftpm 4 | /Packages 5 | /*.xcodeproj 6 | xcuserdata/ 7 | -------------------------------------------------------------------------------- /__test__/TestProject/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.6 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "TestProject", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "TestProject1", 12 | targets: ["TestProject1"]), 13 | .library( 14 | name: "TestProject2", 15 | targets: ["TestProject2"]), 16 | .library( 17 | name: "TestProjectCombined", 18 | targets: ["TestProject1", "TestProject2"]), 19 | ], 20 | targets: [ 21 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 22 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 23 | .target(name: "TestProject1"), 24 | .target(name: "TestProject2"), 25 | .testTarget( 26 | name: "TestProject1Tests", 27 | dependencies: ["TestProject1"]), 28 | .testTarget( 29 | name: "TestProject2Tests", 30 | dependencies: ["TestProject2"]), 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /__test__/TestProject/Sources/TestProject1/MainObject.swift: -------------------------------------------------------------------------------- 1 | /// This is an exemplary enumeration. 2 | public enum SomeEnum: Equatable { 3 | /// The A case 4 | case a 5 | /// The B case 6 | case b 7 | /// The C case 8 | case c 9 | 10 | /// Returns whether self is equal to ``b``. 11 | public var isB: Bool { self == .b } 12 | } 13 | 14 | /// This is an exemplary final class. 15 | public final class SomeClass { 16 | /// Simply creates a new instance of this class. 17 | public init() {} 18 | 19 | /// This prints 'Hello' into the given stream. 20 | public func sayHello(to stream: inout S) { 21 | print("Hello", to: &stream) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /__test__/TestProject/Sources/TestProject2/SomeObject.swift: -------------------------------------------------------------------------------- 1 | /// This is an exemplary struct. 2 | public struct SomeObject: Hashable { 3 | /// The name of this struct. 4 | public let name: String 5 | 6 | /// Creates a new instance of this struct. 7 | /// - Parameter name: The name to use for the new instance. 8 | public init(name: String) { 9 | self.name = name 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /__test__/TestProject/Tests/TestProject1Tests/MainObjectTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import TestProject1 3 | 4 | final class MainObjectTests: XCTestCase { 5 | private struct Stream: TextOutputStream { 6 | private var _string = "" 7 | 8 | mutating func write(_ string: String) { 9 | _string += string 10 | } 11 | 12 | mutating func finalize() -> String { 13 | defer { _string = "" } 14 | return _string 15 | } 16 | } 17 | 18 | func testSomeEnum() throws { 19 | XCTAssertFalse(SomeEnum.a.isB) 20 | XCTAssertTrue(SomeEnum.b.isB) 21 | } 22 | 23 | func testSomeClass() throws { 24 | let cls = SomeClass() 25 | var stream = Stream() 26 | cls.sayHello(to: &stream) 27 | XCTAssertEqual(stream.finalize(), "Hello\n") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /__test__/TestProject/Tests/TestProject2Tests/SomeObjectTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import TestProject2 3 | 4 | final class SomeObjectTests: XCTestCase { 5 | func testSomeObject() throws { 6 | XCTAssertEqual(SomeObject(name: "Test"), SomeObject(name: "Test")) 7 | XCTAssertNotEqual(SomeObject(name: "Test1"), SomeObject(name: "Test2")) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: Swift Coverage Conversion 2 | description: Converts Swift / Xcode code coverage files for processing with e.g. codecov. 3 | author: ser.soft GmbH 4 | inputs: 5 | search-paths: 6 | description: A list of search paths (one per line) that should be used for searching coverage data. 7 | required: true 8 | default: | 9 | ./.build 10 | $HOME/Library/Developer/Xcode/DerivedData 11 | output: 12 | description: The path to the output folder to put the converted coverage files in. 13 | required: true 14 | default: './.swiftcov' 15 | format: 16 | description: The format to write the coverage files in. Can be 'lcov' or 'txt'. 17 | required: true 18 | default: 'lcov' 19 | target-name-filter: 20 | description: A regular expression that is used to filter coverage files by their target names. 21 | required: false 22 | ignore-conversion-failures: 23 | description: If `true`, conversion failures are ignored. If `fail-on-empty-output` is also set to `true`, the action might still fail if all conversions fail. 24 | required: true 25 | default: 'false' 26 | fail-on-empty-output: 27 | description: If `true`, the action fails if no coverage files were found (output is still set to an empty array). 28 | required: true 29 | default: 'false' 30 | outputs: 31 | files: 32 | description: The JSON-encoded array of (absolute) file paths that were converted. 33 | runs: 34 | using: node20 35 | main: dist/index.js 36 | branding: 37 | color: gray-dark 38 | icon: refresh-cw 39 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | /******/ (() => { // webpackBootstrap 2 | /******/ var __webpack_modules__ = ({ 3 | 4 | /***/ 915: 5 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 6 | 7 | "use strict"; 8 | 9 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | var desc = Object.getOwnPropertyDescriptor(m, k); 12 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 13 | desc = { enumerable: true, get: function() { return m[k]; } }; 14 | } 15 | Object.defineProperty(o, k2, desc); 16 | }) : (function(o, m, k, k2) { 17 | if (k2 === undefined) k2 = k; 18 | o[k2] = m[k]; 19 | })); 20 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 21 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 22 | }) : function(o, v) { 23 | o["default"] = v; 24 | }); 25 | var __importStar = (this && this.__importStar) || (function () { 26 | var ownKeys = function(o) { 27 | ownKeys = Object.getOwnPropertyNames || function (o) { 28 | var ar = []; 29 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 30 | return ar; 31 | }; 32 | return ownKeys(o); 33 | }; 34 | return function (mod) { 35 | if (mod && mod.__esModule) return mod; 36 | var result = {}; 37 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 38 | __setModuleDefault(result, mod); 39 | return result; 40 | }; 41 | })(); 42 | Object.defineProperty(exports, "__esModule", ({ value: true })); 43 | const core = __importStar(__nccwpck_require__(484)); 44 | const exec_1 = __nccwpck_require__(236); 45 | const io = __importStar(__nccwpck_require__(994)); 46 | const fs_1 = __nccwpck_require__(896); 47 | const path = __importStar(__nccwpck_require__(928)); 48 | const os = __importStar(__nccwpck_require__(857)); 49 | async function runCmd(cmd, ...args) { 50 | const output = await (0, exec_1.getExecOutput)(cmd, args.length <= 0 ? undefined : args, { silent: !core.isDebug() }); 51 | if (output.stderr.length > 0) 52 | core.warning(`Command execution wrote lines to stderr:\n${output.stderr}`); 53 | return output.stdout; 54 | } 55 | var CovFormat; 56 | (function (CovFormat) { 57 | CovFormat["txt"] = "txt"; 58 | CovFormat["lcov"] = "lcov"; 59 | })(CovFormat || (CovFormat = {})); 60 | // Taken and adjusted from https://stackoverflow.com/a/65415138/1388842 61 | async function* walk(dir, onlyFiles = true) { 62 | const entries = await fs_1.promises.readdir(dir, { withFileTypes: true }); 63 | for (const entry of entries) { 64 | const res = path.resolve(dir, entry.name); 65 | if (entry.isDirectory()) { 66 | let skipDesc = false; 67 | if (!onlyFiles) 68 | yield { path: res, isDirectory: true, skipDescendants: () => skipDesc = true }; 69 | if (!skipDesc) 70 | yield* walk(res, onlyFiles); 71 | } 72 | else { 73 | yield { path: res, isDirectory: false, skipDescendants: () => { } }; 74 | } 75 | } 76 | } 77 | async function directoryExists(path) { 78 | if (!(0, fs_1.existsSync)(path)) 79 | return false; 80 | const stat = await fs_1.promises.stat(path); 81 | return stat.isDirectory(); 82 | } 83 | async function fileExists(path) { 84 | if (!(0, fs_1.existsSync)(path)) 85 | return false; 86 | const stat = await fs_1.promises.stat(path); 87 | return stat.isFile(); 88 | } 89 | async function main() { 90 | switch (process.platform) { 91 | case 'darwin': break; 92 | case 'linux': break; 93 | default: throw new Error('This action only supports macOS and Linux!'); 94 | } 95 | core.startGroup('Validating input'); 96 | const searchPaths = core.getMultilineInput('search-paths', { required: true }) 97 | .map(p => path.resolve(p.replace(/(~|\$HOME|\${HOME})/g, os.homedir))); 98 | const outputFolder = path.resolve(core.getInput('output', { required: true }) 99 | .replace(/(~|\$HOME|\${HOME})/g, os.homedir)); 100 | const _format = core.getInput('format', { required: true }); 101 | const format = CovFormat[_format]; 102 | if (!format) 103 | throw new Error(`Invalid format: ${_format}`); 104 | const _targetNameFilter = core.getInput('target-name-filter'); 105 | const targetNameFilter = _targetNameFilter ? new RegExp(_targetNameFilter) : null; 106 | const ignoreConversionFailures = core.getBooleanInput('ignore-conversion-failures'); 107 | const failOnEmptyOutput = core.getBooleanInput('fail-on-empty-output'); 108 | core.endGroup(); 109 | await core.group('Setting up paths', async () => { 110 | await io.rmRF(outputFolder); 111 | await io.mkdirP(outputFolder); 112 | }); 113 | const profDataFiles = await core.group('Finding coverage files', async () => { 114 | let profDataFiles = []; 115 | for (const searchPath of searchPaths) { 116 | if (!await directoryExists(searchPath)) { 117 | core.info(`Skipping non-existent search path ${searchPath}...`); 118 | continue; 119 | } 120 | for await (const entry of walk(searchPath, true)) { 121 | if (/.*\.profdata$/.test(entry.path)) { 122 | profDataFiles.push(entry.path); 123 | core.debug(`Found profdata file: ${entry.path}`); 124 | } 125 | } 126 | } 127 | core.info(`Found ${profDataFiles.length} profiling data file(s):\n${profDataFiles.join('\n')}`); 128 | return profDataFiles; 129 | }); 130 | let convertedFiles = []; 131 | if (profDataFiles.length > 0) { 132 | convertedFiles = await core.group('Converting files', async () => { 133 | let outFiles = []; 134 | let conversionFailures = []; 135 | let processedTargets = new Set(); 136 | for (const profDataFile of profDataFiles) { 137 | const profDataDir = path.dirname(profDataFile); 138 | const xcodeRegex = /(Build).*/; 139 | let buildDir; 140 | let isXcode; 141 | if (xcodeRegex.test(profDataDir)) { 142 | buildDir = profDataDir.replace(xcodeRegex, '$1'); 143 | isXcode = true; 144 | } 145 | else { // SPM 146 | buildDir = path.dirname(profDataDir); 147 | isXcode = false; 148 | } 149 | core.debug(`Checking contents of build dir ${buildDir} of prof data file ${profDataFile}`); 150 | for await (const entry of walk(buildDir, false)) { 151 | const typesRegex = /.*\.(app|framework|xctest)$/; 152 | if (!typesRegex.test(entry.path)) 153 | continue; 154 | entry.skipDescendants(); // Don't process any further files inside this container. 155 | if (isXcode && !/\/Build[^/]*\/Products\//.test(entry.path)) { 156 | core.info(`Skipping ${entry.path} because it is not in a Xcode build products directory...`); 157 | continue; 158 | } 159 | const type = entry.path.replace(typesRegex, '$1'); 160 | core.debug(`Found match of type ${type}: ${entry.path}`); 161 | const proj = entry.path 162 | .replace(/.*\//, '') 163 | .replace(`.${type}`, ''); 164 | core.debug(`Project name: ${proj}`); 165 | if (processedTargets.has(`${proj}.${type}`)) { 166 | core.info(`Skipping ${proj} with type ${type} because it has already been converted...`); 167 | continue; 168 | } 169 | if (targetNameFilter && !targetNameFilter.test(proj)) { 170 | core.info(`Skipping ${proj} due to target name filter...`); 171 | continue; 172 | } 173 | let dest; 174 | let cmd; 175 | let args; 176 | if (process.platform === 'darwin') { 177 | dest = path.join(entry.path, proj); 178 | if (!await fileExists(dest)) { 179 | const macOSPath = path.join(entry.path, 'Contents', 'MacOS'); 180 | dest = path.join(macOSPath, proj); 181 | if (!await fileExists(dest)) { 182 | // Try again with whitespaces removed. 183 | dest = path.join(macOSPath, proj.replace(' ', '')); 184 | } 185 | if (!await fileExists(dest)) { 186 | core.warning(`Couldn't find a suitable target file in ${entry.path}. Using the path itself...`); 187 | dest = entry.path; 188 | } 189 | } 190 | cmd = 'xcrun'; 191 | args = ['llvm-cov']; 192 | } 193 | else { 194 | dest = entry.path; 195 | cmd = 'llvm-cov'; 196 | args = []; 197 | } 198 | let fileEnding; 199 | switch (format) { 200 | case CovFormat.txt: 201 | args.push('show'); 202 | fileEnding = 'txt'; 203 | break; 204 | case CovFormat.lcov: 205 | args.push('export', '-format=lcov'); 206 | fileEnding = 'lcov'; 207 | break; 208 | } 209 | args.push('-instr-profile', profDataFile, dest); 210 | let converted; 211 | try { 212 | converted = await runCmd(cmd, ...args); 213 | } 214 | catch (error) { 215 | const msg = `Failed to convert ${dest}: ${error}`; 216 | if (error instanceof Error) 217 | conversionFailures.push(error); 218 | else 219 | conversionFailures.push(new Error(msg)); 220 | if (ignoreConversionFailures) 221 | core.info(msg); 222 | else 223 | core.error(msg); 224 | continue; 225 | } 226 | const projFileName = proj.replace(/\s/g, ''); 227 | const outFile = path.join(outputFolder, `${projFileName}.${type}.coverage.${fileEnding}`); 228 | core.debug(`Writing coverage report to ${outFile}`); 229 | await fs_1.promises.writeFile(outFile, converted); 230 | outFiles.push(outFile); 231 | processedTargets.add(`${proj}.${type}`); 232 | } 233 | } 234 | if (conversionFailures.length > 0) { 235 | if (ignoreConversionFailures) 236 | core.info(`Failed to convert ${conversionFailures.length} file(s)...`); 237 | else 238 | throw new Error('Conversion failures:\n' + conversionFailures.map(e => e.toString()).join('\n')); 239 | } 240 | core.info(`Processed ${outFiles.length} file(s):\n${outFiles.join('\n')}`); 241 | return outFiles; 242 | }); 243 | } 244 | core.setOutput('files', JSON.stringify(convertedFiles)); 245 | if (convertedFiles.length <= 0 && failOnEmptyOutput) 246 | throw new Error('No coverage files found (or none succeeded to convert)!'); 247 | } 248 | try { 249 | main().catch(error => core.setFailed(error.message)); 250 | } 251 | catch (error) { 252 | core.setFailed(error.message); 253 | } 254 | 255 | 256 | /***/ }), 257 | 258 | /***/ 914: 259 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 260 | 261 | "use strict"; 262 | 263 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 264 | if (k2 === undefined) k2 = k; 265 | var desc = Object.getOwnPropertyDescriptor(m, k); 266 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 267 | desc = { enumerable: true, get: function() { return m[k]; } }; 268 | } 269 | Object.defineProperty(o, k2, desc); 270 | }) : (function(o, m, k, k2) { 271 | if (k2 === undefined) k2 = k; 272 | o[k2] = m[k]; 273 | })); 274 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 275 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 276 | }) : function(o, v) { 277 | o["default"] = v; 278 | }); 279 | var __importStar = (this && this.__importStar) || function (mod) { 280 | if (mod && mod.__esModule) return mod; 281 | var result = {}; 282 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 283 | __setModuleDefault(result, mod); 284 | return result; 285 | }; 286 | Object.defineProperty(exports, "__esModule", ({ value: true })); 287 | exports.issue = exports.issueCommand = void 0; 288 | const os = __importStar(__nccwpck_require__(857)); 289 | const utils_1 = __nccwpck_require__(302); 290 | /** 291 | * Commands 292 | * 293 | * Command Format: 294 | * ::name key=value,key=value::message 295 | * 296 | * Examples: 297 | * ::warning::This is the message 298 | * ::set-env name=MY_VAR::some value 299 | */ 300 | function issueCommand(command, properties, message) { 301 | const cmd = new Command(command, properties, message); 302 | process.stdout.write(cmd.toString() + os.EOL); 303 | } 304 | exports.issueCommand = issueCommand; 305 | function issue(name, message = '') { 306 | issueCommand(name, {}, message); 307 | } 308 | exports.issue = issue; 309 | const CMD_STRING = '::'; 310 | class Command { 311 | constructor(command, properties, message) { 312 | if (!command) { 313 | command = 'missing.command'; 314 | } 315 | this.command = command; 316 | this.properties = properties; 317 | this.message = message; 318 | } 319 | toString() { 320 | let cmdStr = CMD_STRING + this.command; 321 | if (this.properties && Object.keys(this.properties).length > 0) { 322 | cmdStr += ' '; 323 | let first = true; 324 | for (const key in this.properties) { 325 | if (this.properties.hasOwnProperty(key)) { 326 | const val = this.properties[key]; 327 | if (val) { 328 | if (first) { 329 | first = false; 330 | } 331 | else { 332 | cmdStr += ','; 333 | } 334 | cmdStr += `${key}=${escapeProperty(val)}`; 335 | } 336 | } 337 | } 338 | } 339 | cmdStr += `${CMD_STRING}${escapeData(this.message)}`; 340 | return cmdStr; 341 | } 342 | } 343 | function escapeData(s) { 344 | return (0, utils_1.toCommandValue)(s) 345 | .replace(/%/g, '%25') 346 | .replace(/\r/g, '%0D') 347 | .replace(/\n/g, '%0A'); 348 | } 349 | function escapeProperty(s) { 350 | return (0, utils_1.toCommandValue)(s) 351 | .replace(/%/g, '%25') 352 | .replace(/\r/g, '%0D') 353 | .replace(/\n/g, '%0A') 354 | .replace(/:/g, '%3A') 355 | .replace(/,/g, '%2C'); 356 | } 357 | //# sourceMappingURL=command.js.map 358 | 359 | /***/ }), 360 | 361 | /***/ 484: 362 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 363 | 364 | "use strict"; 365 | 366 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 367 | if (k2 === undefined) k2 = k; 368 | var desc = Object.getOwnPropertyDescriptor(m, k); 369 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 370 | desc = { enumerable: true, get: function() { return m[k]; } }; 371 | } 372 | Object.defineProperty(o, k2, desc); 373 | }) : (function(o, m, k, k2) { 374 | if (k2 === undefined) k2 = k; 375 | o[k2] = m[k]; 376 | })); 377 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 378 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 379 | }) : function(o, v) { 380 | o["default"] = v; 381 | }); 382 | var __importStar = (this && this.__importStar) || function (mod) { 383 | if (mod && mod.__esModule) return mod; 384 | var result = {}; 385 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 386 | __setModuleDefault(result, mod); 387 | return result; 388 | }; 389 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 390 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 391 | return new (P || (P = Promise))(function (resolve, reject) { 392 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 393 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 394 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 395 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 396 | }); 397 | }; 398 | Object.defineProperty(exports, "__esModule", ({ value: true })); 399 | exports.platform = exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = exports.markdownSummary = exports.summary = exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; 400 | const command_1 = __nccwpck_require__(914); 401 | const file_command_1 = __nccwpck_require__(753); 402 | const utils_1 = __nccwpck_require__(302); 403 | const os = __importStar(__nccwpck_require__(857)); 404 | const path = __importStar(__nccwpck_require__(928)); 405 | const oidc_utils_1 = __nccwpck_require__(306); 406 | /** 407 | * The code to exit an action 408 | */ 409 | var ExitCode; 410 | (function (ExitCode) { 411 | /** 412 | * A code indicating that the action was successful 413 | */ 414 | ExitCode[ExitCode["Success"] = 0] = "Success"; 415 | /** 416 | * A code indicating that the action was a failure 417 | */ 418 | ExitCode[ExitCode["Failure"] = 1] = "Failure"; 419 | })(ExitCode || (exports.ExitCode = ExitCode = {})); 420 | //----------------------------------------------------------------------- 421 | // Variables 422 | //----------------------------------------------------------------------- 423 | /** 424 | * Sets env variable for this action and future actions in the job 425 | * @param name the name of the variable to set 426 | * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify 427 | */ 428 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 429 | function exportVariable(name, val) { 430 | const convertedVal = (0, utils_1.toCommandValue)(val); 431 | process.env[name] = convertedVal; 432 | const filePath = process.env['GITHUB_ENV'] || ''; 433 | if (filePath) { 434 | return (0, file_command_1.issueFileCommand)('ENV', (0, file_command_1.prepareKeyValueMessage)(name, val)); 435 | } 436 | (0, command_1.issueCommand)('set-env', { name }, convertedVal); 437 | } 438 | exports.exportVariable = exportVariable; 439 | /** 440 | * Registers a secret which will get masked from logs 441 | * @param secret value of the secret 442 | */ 443 | function setSecret(secret) { 444 | (0, command_1.issueCommand)('add-mask', {}, secret); 445 | } 446 | exports.setSecret = setSecret; 447 | /** 448 | * Prepends inputPath to the PATH (for this action and future actions) 449 | * @param inputPath 450 | */ 451 | function addPath(inputPath) { 452 | const filePath = process.env['GITHUB_PATH'] || ''; 453 | if (filePath) { 454 | (0, file_command_1.issueFileCommand)('PATH', inputPath); 455 | } 456 | else { 457 | (0, command_1.issueCommand)('add-path', {}, inputPath); 458 | } 459 | process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; 460 | } 461 | exports.addPath = addPath; 462 | /** 463 | * Gets the value of an input. 464 | * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. 465 | * Returns an empty string if the value is not defined. 466 | * 467 | * @param name name of the input to get 468 | * @param options optional. See InputOptions. 469 | * @returns string 470 | */ 471 | function getInput(name, options) { 472 | const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; 473 | if (options && options.required && !val) { 474 | throw new Error(`Input required and not supplied: ${name}`); 475 | } 476 | if (options && options.trimWhitespace === false) { 477 | return val; 478 | } 479 | return val.trim(); 480 | } 481 | exports.getInput = getInput; 482 | /** 483 | * Gets the values of an multiline input. Each value is also trimmed. 484 | * 485 | * @param name name of the input to get 486 | * @param options optional. See InputOptions. 487 | * @returns string[] 488 | * 489 | */ 490 | function getMultilineInput(name, options) { 491 | const inputs = getInput(name, options) 492 | .split('\n') 493 | .filter(x => x !== ''); 494 | if (options && options.trimWhitespace === false) { 495 | return inputs; 496 | } 497 | return inputs.map(input => input.trim()); 498 | } 499 | exports.getMultilineInput = getMultilineInput; 500 | /** 501 | * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. 502 | * Support boolean input list: `true | True | TRUE | false | False | FALSE` . 503 | * The return value is also in boolean type. 504 | * ref: https://yaml.org/spec/1.2/spec.html#id2804923 505 | * 506 | * @param name name of the input to get 507 | * @param options optional. See InputOptions. 508 | * @returns boolean 509 | */ 510 | function getBooleanInput(name, options) { 511 | const trueValue = ['true', 'True', 'TRUE']; 512 | const falseValue = ['false', 'False', 'FALSE']; 513 | const val = getInput(name, options); 514 | if (trueValue.includes(val)) 515 | return true; 516 | if (falseValue.includes(val)) 517 | return false; 518 | throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + 519 | `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); 520 | } 521 | exports.getBooleanInput = getBooleanInput; 522 | /** 523 | * Sets the value of an output. 524 | * 525 | * @param name name of the output to set 526 | * @param value value to store. Non-string values will be converted to a string via JSON.stringify 527 | */ 528 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 529 | function setOutput(name, value) { 530 | const filePath = process.env['GITHUB_OUTPUT'] || ''; 531 | if (filePath) { 532 | return (0, file_command_1.issueFileCommand)('OUTPUT', (0, file_command_1.prepareKeyValueMessage)(name, value)); 533 | } 534 | process.stdout.write(os.EOL); 535 | (0, command_1.issueCommand)('set-output', { name }, (0, utils_1.toCommandValue)(value)); 536 | } 537 | exports.setOutput = setOutput; 538 | /** 539 | * Enables or disables the echoing of commands into stdout for the rest of the step. 540 | * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. 541 | * 542 | */ 543 | function setCommandEcho(enabled) { 544 | (0, command_1.issue)('echo', enabled ? 'on' : 'off'); 545 | } 546 | exports.setCommandEcho = setCommandEcho; 547 | //----------------------------------------------------------------------- 548 | // Results 549 | //----------------------------------------------------------------------- 550 | /** 551 | * Sets the action status to failed. 552 | * When the action exits it will be with an exit code of 1 553 | * @param message add error issue message 554 | */ 555 | function setFailed(message) { 556 | process.exitCode = ExitCode.Failure; 557 | error(message); 558 | } 559 | exports.setFailed = setFailed; 560 | //----------------------------------------------------------------------- 561 | // Logging Commands 562 | //----------------------------------------------------------------------- 563 | /** 564 | * Gets whether Actions Step Debug is on or not 565 | */ 566 | function isDebug() { 567 | return process.env['RUNNER_DEBUG'] === '1'; 568 | } 569 | exports.isDebug = isDebug; 570 | /** 571 | * Writes debug message to user log 572 | * @param message debug message 573 | */ 574 | function debug(message) { 575 | (0, command_1.issueCommand)('debug', {}, message); 576 | } 577 | exports.debug = debug; 578 | /** 579 | * Adds an error issue 580 | * @param message error issue message. Errors will be converted to string via toString() 581 | * @param properties optional properties to add to the annotation. 582 | */ 583 | function error(message, properties = {}) { 584 | (0, command_1.issueCommand)('error', (0, utils_1.toCommandProperties)(properties), message instanceof Error ? message.toString() : message); 585 | } 586 | exports.error = error; 587 | /** 588 | * Adds a warning issue 589 | * @param message warning issue message. Errors will be converted to string via toString() 590 | * @param properties optional properties to add to the annotation. 591 | */ 592 | function warning(message, properties = {}) { 593 | (0, command_1.issueCommand)('warning', (0, utils_1.toCommandProperties)(properties), message instanceof Error ? message.toString() : message); 594 | } 595 | exports.warning = warning; 596 | /** 597 | * Adds a notice issue 598 | * @param message notice issue message. Errors will be converted to string via toString() 599 | * @param properties optional properties to add to the annotation. 600 | */ 601 | function notice(message, properties = {}) { 602 | (0, command_1.issueCommand)('notice', (0, utils_1.toCommandProperties)(properties), message instanceof Error ? message.toString() : message); 603 | } 604 | exports.notice = notice; 605 | /** 606 | * Writes info to log with console.log. 607 | * @param message info message 608 | */ 609 | function info(message) { 610 | process.stdout.write(message + os.EOL); 611 | } 612 | exports.info = info; 613 | /** 614 | * Begin an output group. 615 | * 616 | * Output until the next `groupEnd` will be foldable in this group 617 | * 618 | * @param name The name of the output group 619 | */ 620 | function startGroup(name) { 621 | (0, command_1.issue)('group', name); 622 | } 623 | exports.startGroup = startGroup; 624 | /** 625 | * End an output group. 626 | */ 627 | function endGroup() { 628 | (0, command_1.issue)('endgroup'); 629 | } 630 | exports.endGroup = endGroup; 631 | /** 632 | * Wrap an asynchronous function call in a group. 633 | * 634 | * Returns the same type as the function itself. 635 | * 636 | * @param name The name of the group 637 | * @param fn The function to wrap in the group 638 | */ 639 | function group(name, fn) { 640 | return __awaiter(this, void 0, void 0, function* () { 641 | startGroup(name); 642 | let result; 643 | try { 644 | result = yield fn(); 645 | } 646 | finally { 647 | endGroup(); 648 | } 649 | return result; 650 | }); 651 | } 652 | exports.group = group; 653 | //----------------------------------------------------------------------- 654 | // Wrapper action state 655 | //----------------------------------------------------------------------- 656 | /** 657 | * Saves state for current action, the state can only be retrieved by this action's post job execution. 658 | * 659 | * @param name name of the state to store 660 | * @param value value to store. Non-string values will be converted to a string via JSON.stringify 661 | */ 662 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 663 | function saveState(name, value) { 664 | const filePath = process.env['GITHUB_STATE'] || ''; 665 | if (filePath) { 666 | return (0, file_command_1.issueFileCommand)('STATE', (0, file_command_1.prepareKeyValueMessage)(name, value)); 667 | } 668 | (0, command_1.issueCommand)('save-state', { name }, (0, utils_1.toCommandValue)(value)); 669 | } 670 | exports.saveState = saveState; 671 | /** 672 | * Gets the value of an state set by this action's main execution. 673 | * 674 | * @param name name of the state to get 675 | * @returns string 676 | */ 677 | function getState(name) { 678 | return process.env[`STATE_${name}`] || ''; 679 | } 680 | exports.getState = getState; 681 | function getIDToken(aud) { 682 | return __awaiter(this, void 0, void 0, function* () { 683 | return yield oidc_utils_1.OidcClient.getIDToken(aud); 684 | }); 685 | } 686 | exports.getIDToken = getIDToken; 687 | /** 688 | * Summary exports 689 | */ 690 | var summary_1 = __nccwpck_require__(847); 691 | Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); 692 | /** 693 | * @deprecated use core.summary 694 | */ 695 | var summary_2 = __nccwpck_require__(847); 696 | Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); 697 | /** 698 | * Path exports 699 | */ 700 | var path_utils_1 = __nccwpck_require__(976); 701 | Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); 702 | Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); 703 | Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); 704 | /** 705 | * Platform utilities exports 706 | */ 707 | exports.platform = __importStar(__nccwpck_require__(968)); 708 | //# sourceMappingURL=core.js.map 709 | 710 | /***/ }), 711 | 712 | /***/ 753: 713 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 714 | 715 | "use strict"; 716 | 717 | // For internal use, subject to change. 718 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 719 | if (k2 === undefined) k2 = k; 720 | var desc = Object.getOwnPropertyDescriptor(m, k); 721 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 722 | desc = { enumerable: true, get: function() { return m[k]; } }; 723 | } 724 | Object.defineProperty(o, k2, desc); 725 | }) : (function(o, m, k, k2) { 726 | if (k2 === undefined) k2 = k; 727 | o[k2] = m[k]; 728 | })); 729 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 730 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 731 | }) : function(o, v) { 732 | o["default"] = v; 733 | }); 734 | var __importStar = (this && this.__importStar) || function (mod) { 735 | if (mod && mod.__esModule) return mod; 736 | var result = {}; 737 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 738 | __setModuleDefault(result, mod); 739 | return result; 740 | }; 741 | Object.defineProperty(exports, "__esModule", ({ value: true })); 742 | exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; 743 | // We use any as a valid input type 744 | /* eslint-disable @typescript-eslint/no-explicit-any */ 745 | const crypto = __importStar(__nccwpck_require__(982)); 746 | const fs = __importStar(__nccwpck_require__(896)); 747 | const os = __importStar(__nccwpck_require__(857)); 748 | const utils_1 = __nccwpck_require__(302); 749 | function issueFileCommand(command, message) { 750 | const filePath = process.env[`GITHUB_${command}`]; 751 | if (!filePath) { 752 | throw new Error(`Unable to find environment variable for file command ${command}`); 753 | } 754 | if (!fs.existsSync(filePath)) { 755 | throw new Error(`Missing file at path: ${filePath}`); 756 | } 757 | fs.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os.EOL}`, { 758 | encoding: 'utf8' 759 | }); 760 | } 761 | exports.issueFileCommand = issueFileCommand; 762 | function prepareKeyValueMessage(key, value) { 763 | const delimiter = `ghadelimiter_${crypto.randomUUID()}`; 764 | const convertedValue = (0, utils_1.toCommandValue)(value); 765 | // These should realistically never happen, but just in case someone finds a 766 | // way to exploit uuid generation let's not allow keys or values that contain 767 | // the delimiter. 768 | if (key.includes(delimiter)) { 769 | throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); 770 | } 771 | if (convertedValue.includes(delimiter)) { 772 | throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); 773 | } 774 | return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; 775 | } 776 | exports.prepareKeyValueMessage = prepareKeyValueMessage; 777 | //# sourceMappingURL=file-command.js.map 778 | 779 | /***/ }), 780 | 781 | /***/ 306: 782 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 783 | 784 | "use strict"; 785 | 786 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 787 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 788 | return new (P || (P = Promise))(function (resolve, reject) { 789 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 790 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 791 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 792 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 793 | }); 794 | }; 795 | Object.defineProperty(exports, "__esModule", ({ value: true })); 796 | exports.OidcClient = void 0; 797 | const http_client_1 = __nccwpck_require__(844); 798 | const auth_1 = __nccwpck_require__(552); 799 | const core_1 = __nccwpck_require__(484); 800 | class OidcClient { 801 | static createHttpClient(allowRetry = true, maxRetry = 10) { 802 | const requestOptions = { 803 | allowRetries: allowRetry, 804 | maxRetries: maxRetry 805 | }; 806 | return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); 807 | } 808 | static getRequestToken() { 809 | const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; 810 | if (!token) { 811 | throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); 812 | } 813 | return token; 814 | } 815 | static getIDTokenUrl() { 816 | const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; 817 | if (!runtimeUrl) { 818 | throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); 819 | } 820 | return runtimeUrl; 821 | } 822 | static getCall(id_token_url) { 823 | var _a; 824 | return __awaiter(this, void 0, void 0, function* () { 825 | const httpclient = OidcClient.createHttpClient(); 826 | const res = yield httpclient 827 | .getJson(id_token_url) 828 | .catch(error => { 829 | throw new Error(`Failed to get ID Token. \n 830 | Error Code : ${error.statusCode}\n 831 | Error Message: ${error.message}`); 832 | }); 833 | const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; 834 | if (!id_token) { 835 | throw new Error('Response json body do not have ID Token field'); 836 | } 837 | return id_token; 838 | }); 839 | } 840 | static getIDToken(audience) { 841 | return __awaiter(this, void 0, void 0, function* () { 842 | try { 843 | // New ID Token is requested from action service 844 | let id_token_url = OidcClient.getIDTokenUrl(); 845 | if (audience) { 846 | const encodedAudience = encodeURIComponent(audience); 847 | id_token_url = `${id_token_url}&audience=${encodedAudience}`; 848 | } 849 | (0, core_1.debug)(`ID token url is ${id_token_url}`); 850 | const id_token = yield OidcClient.getCall(id_token_url); 851 | (0, core_1.setSecret)(id_token); 852 | return id_token; 853 | } 854 | catch (error) { 855 | throw new Error(`Error message: ${error.message}`); 856 | } 857 | }); 858 | } 859 | } 860 | exports.OidcClient = OidcClient; 861 | //# sourceMappingURL=oidc-utils.js.map 862 | 863 | /***/ }), 864 | 865 | /***/ 976: 866 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 867 | 868 | "use strict"; 869 | 870 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 871 | if (k2 === undefined) k2 = k; 872 | var desc = Object.getOwnPropertyDescriptor(m, k); 873 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 874 | desc = { enumerable: true, get: function() { return m[k]; } }; 875 | } 876 | Object.defineProperty(o, k2, desc); 877 | }) : (function(o, m, k, k2) { 878 | if (k2 === undefined) k2 = k; 879 | o[k2] = m[k]; 880 | })); 881 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 882 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 883 | }) : function(o, v) { 884 | o["default"] = v; 885 | }); 886 | var __importStar = (this && this.__importStar) || function (mod) { 887 | if (mod && mod.__esModule) return mod; 888 | var result = {}; 889 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 890 | __setModuleDefault(result, mod); 891 | return result; 892 | }; 893 | Object.defineProperty(exports, "__esModule", ({ value: true })); 894 | exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; 895 | const path = __importStar(__nccwpck_require__(928)); 896 | /** 897 | * toPosixPath converts the given path to the posix form. On Windows, \\ will be 898 | * replaced with /. 899 | * 900 | * @param pth. Path to transform. 901 | * @return string Posix path. 902 | */ 903 | function toPosixPath(pth) { 904 | return pth.replace(/[\\]/g, '/'); 905 | } 906 | exports.toPosixPath = toPosixPath; 907 | /** 908 | * toWin32Path converts the given path to the win32 form. On Linux, / will be 909 | * replaced with \\. 910 | * 911 | * @param pth. Path to transform. 912 | * @return string Win32 path. 913 | */ 914 | function toWin32Path(pth) { 915 | return pth.replace(/[/]/g, '\\'); 916 | } 917 | exports.toWin32Path = toWin32Path; 918 | /** 919 | * toPlatformPath converts the given path to a platform-specific path. It does 920 | * this by replacing instances of / and \ with the platform-specific path 921 | * separator. 922 | * 923 | * @param pth The path to platformize. 924 | * @return string The platform-specific path. 925 | */ 926 | function toPlatformPath(pth) { 927 | return pth.replace(/[/\\]/g, path.sep); 928 | } 929 | exports.toPlatformPath = toPlatformPath; 930 | //# sourceMappingURL=path-utils.js.map 931 | 932 | /***/ }), 933 | 934 | /***/ 968: 935 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 936 | 937 | "use strict"; 938 | 939 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 940 | if (k2 === undefined) k2 = k; 941 | var desc = Object.getOwnPropertyDescriptor(m, k); 942 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 943 | desc = { enumerable: true, get: function() { return m[k]; } }; 944 | } 945 | Object.defineProperty(o, k2, desc); 946 | }) : (function(o, m, k, k2) { 947 | if (k2 === undefined) k2 = k; 948 | o[k2] = m[k]; 949 | })); 950 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 951 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 952 | }) : function(o, v) { 953 | o["default"] = v; 954 | }); 955 | var __importStar = (this && this.__importStar) || function (mod) { 956 | if (mod && mod.__esModule) return mod; 957 | var result = {}; 958 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 959 | __setModuleDefault(result, mod); 960 | return result; 961 | }; 962 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 963 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 964 | return new (P || (P = Promise))(function (resolve, reject) { 965 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 966 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 967 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 968 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 969 | }); 970 | }; 971 | var __importDefault = (this && this.__importDefault) || function (mod) { 972 | return (mod && mod.__esModule) ? mod : { "default": mod }; 973 | }; 974 | Object.defineProperty(exports, "__esModule", ({ value: true })); 975 | exports.getDetails = exports.isLinux = exports.isMacOS = exports.isWindows = exports.arch = exports.platform = void 0; 976 | const os_1 = __importDefault(__nccwpck_require__(857)); 977 | const exec = __importStar(__nccwpck_require__(236)); 978 | const getWindowsInfo = () => __awaiter(void 0, void 0, void 0, function* () { 979 | const { stdout: version } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', undefined, { 980 | silent: true 981 | }); 982 | const { stdout: name } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', undefined, { 983 | silent: true 984 | }); 985 | return { 986 | name: name.trim(), 987 | version: version.trim() 988 | }; 989 | }); 990 | const getMacOsInfo = () => __awaiter(void 0, void 0, void 0, function* () { 991 | var _a, _b, _c, _d; 992 | const { stdout } = yield exec.getExecOutput('sw_vers', undefined, { 993 | silent: true 994 | }); 995 | const version = (_b = (_a = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ''; 996 | const name = (_d = (_c = stdout.match(/ProductName:\s*(.+)/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : ''; 997 | return { 998 | name, 999 | version 1000 | }; 1001 | }); 1002 | const getLinuxInfo = () => __awaiter(void 0, void 0, void 0, function* () { 1003 | const { stdout } = yield exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], { 1004 | silent: true 1005 | }); 1006 | const [name, version] = stdout.trim().split('\n'); 1007 | return { 1008 | name, 1009 | version 1010 | }; 1011 | }); 1012 | exports.platform = os_1.default.platform(); 1013 | exports.arch = os_1.default.arch(); 1014 | exports.isWindows = exports.platform === 'win32'; 1015 | exports.isMacOS = exports.platform === 'darwin'; 1016 | exports.isLinux = exports.platform === 'linux'; 1017 | function getDetails() { 1018 | return __awaiter(this, void 0, void 0, function* () { 1019 | return Object.assign(Object.assign({}, (yield (exports.isWindows 1020 | ? getWindowsInfo() 1021 | : exports.isMacOS 1022 | ? getMacOsInfo() 1023 | : getLinuxInfo()))), { platform: exports.platform, 1024 | arch: exports.arch, 1025 | isWindows: exports.isWindows, 1026 | isMacOS: exports.isMacOS, 1027 | isLinux: exports.isLinux }); 1028 | }); 1029 | } 1030 | exports.getDetails = getDetails; 1031 | //# sourceMappingURL=platform.js.map 1032 | 1033 | /***/ }), 1034 | 1035 | /***/ 847: 1036 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 1037 | 1038 | "use strict"; 1039 | 1040 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 1041 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 1042 | return new (P || (P = Promise))(function (resolve, reject) { 1043 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 1044 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 1045 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 1046 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 1047 | }); 1048 | }; 1049 | Object.defineProperty(exports, "__esModule", ({ value: true })); 1050 | exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; 1051 | const os_1 = __nccwpck_require__(857); 1052 | const fs_1 = __nccwpck_require__(896); 1053 | const { access, appendFile, writeFile } = fs_1.promises; 1054 | exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; 1055 | exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; 1056 | class Summary { 1057 | constructor() { 1058 | this._buffer = ''; 1059 | } 1060 | /** 1061 | * Finds the summary file path from the environment, rejects if env var is not found or file does not exist 1062 | * Also checks r/w permissions. 1063 | * 1064 | * @returns step summary file path 1065 | */ 1066 | filePath() { 1067 | return __awaiter(this, void 0, void 0, function* () { 1068 | if (this._filePath) { 1069 | return this._filePath; 1070 | } 1071 | const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; 1072 | if (!pathFromEnv) { 1073 | throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); 1074 | } 1075 | try { 1076 | yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); 1077 | } 1078 | catch (_a) { 1079 | throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); 1080 | } 1081 | this._filePath = pathFromEnv; 1082 | return this._filePath; 1083 | }); 1084 | } 1085 | /** 1086 | * Wraps content in an HTML tag, adding any HTML attributes 1087 | * 1088 | * @param {string} tag HTML tag to wrap 1089 | * @param {string | null} content content within the tag 1090 | * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add 1091 | * 1092 | * @returns {string} content wrapped in HTML element 1093 | */ 1094 | wrap(tag, content, attrs = {}) { 1095 | const htmlAttrs = Object.entries(attrs) 1096 | .map(([key, value]) => ` ${key}="${value}"`) 1097 | .join(''); 1098 | if (!content) { 1099 | return `<${tag}${htmlAttrs}>`; 1100 | } 1101 | return `<${tag}${htmlAttrs}>${content}`; 1102 | } 1103 | /** 1104 | * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. 1105 | * 1106 | * @param {SummaryWriteOptions} [options] (optional) options for write operation 1107 | * 1108 | * @returns {Promise} summary instance 1109 | */ 1110 | write(options) { 1111 | return __awaiter(this, void 0, void 0, function* () { 1112 | const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); 1113 | const filePath = yield this.filePath(); 1114 | const writeFunc = overwrite ? writeFile : appendFile; 1115 | yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); 1116 | return this.emptyBuffer(); 1117 | }); 1118 | } 1119 | /** 1120 | * Clears the summary buffer and wipes the summary file 1121 | * 1122 | * @returns {Summary} summary instance 1123 | */ 1124 | clear() { 1125 | return __awaiter(this, void 0, void 0, function* () { 1126 | return this.emptyBuffer().write({ overwrite: true }); 1127 | }); 1128 | } 1129 | /** 1130 | * Returns the current summary buffer as a string 1131 | * 1132 | * @returns {string} string of summary buffer 1133 | */ 1134 | stringify() { 1135 | return this._buffer; 1136 | } 1137 | /** 1138 | * If the summary buffer is empty 1139 | * 1140 | * @returns {boolen} true if the buffer is empty 1141 | */ 1142 | isEmptyBuffer() { 1143 | return this._buffer.length === 0; 1144 | } 1145 | /** 1146 | * Resets the summary buffer without writing to summary file 1147 | * 1148 | * @returns {Summary} summary instance 1149 | */ 1150 | emptyBuffer() { 1151 | this._buffer = ''; 1152 | return this; 1153 | } 1154 | /** 1155 | * Adds raw text to the summary buffer 1156 | * 1157 | * @param {string} text content to add 1158 | * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) 1159 | * 1160 | * @returns {Summary} summary instance 1161 | */ 1162 | addRaw(text, addEOL = false) { 1163 | this._buffer += text; 1164 | return addEOL ? this.addEOL() : this; 1165 | } 1166 | /** 1167 | * Adds the operating system-specific end-of-line marker to the buffer 1168 | * 1169 | * @returns {Summary} summary instance 1170 | */ 1171 | addEOL() { 1172 | return this.addRaw(os_1.EOL); 1173 | } 1174 | /** 1175 | * Adds an HTML codeblock to the summary buffer 1176 | * 1177 | * @param {string} code content to render within fenced code block 1178 | * @param {string} lang (optional) language to syntax highlight code 1179 | * 1180 | * @returns {Summary} summary instance 1181 | */ 1182 | addCodeBlock(code, lang) { 1183 | const attrs = Object.assign({}, (lang && { lang })); 1184 | const element = this.wrap('pre', this.wrap('code', code), attrs); 1185 | return this.addRaw(element).addEOL(); 1186 | } 1187 | /** 1188 | * Adds an HTML list to the summary buffer 1189 | * 1190 | * @param {string[]} items list of items to render 1191 | * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) 1192 | * 1193 | * @returns {Summary} summary instance 1194 | */ 1195 | addList(items, ordered = false) { 1196 | const tag = ordered ? 'ol' : 'ul'; 1197 | const listItems = items.map(item => this.wrap('li', item)).join(''); 1198 | const element = this.wrap(tag, listItems); 1199 | return this.addRaw(element).addEOL(); 1200 | } 1201 | /** 1202 | * Adds an HTML table to the summary buffer 1203 | * 1204 | * @param {SummaryTableCell[]} rows table rows 1205 | * 1206 | * @returns {Summary} summary instance 1207 | */ 1208 | addTable(rows) { 1209 | const tableBody = rows 1210 | .map(row => { 1211 | const cells = row 1212 | .map(cell => { 1213 | if (typeof cell === 'string') { 1214 | return this.wrap('td', cell); 1215 | } 1216 | const { header, data, colspan, rowspan } = cell; 1217 | const tag = header ? 'th' : 'td'; 1218 | const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); 1219 | return this.wrap(tag, data, attrs); 1220 | }) 1221 | .join(''); 1222 | return this.wrap('tr', cells); 1223 | }) 1224 | .join(''); 1225 | const element = this.wrap('table', tableBody); 1226 | return this.addRaw(element).addEOL(); 1227 | } 1228 | /** 1229 | * Adds a collapsable HTML details element to the summary buffer 1230 | * 1231 | * @param {string} label text for the closed state 1232 | * @param {string} content collapsable content 1233 | * 1234 | * @returns {Summary} summary instance 1235 | */ 1236 | addDetails(label, content) { 1237 | const element = this.wrap('details', this.wrap('summary', label) + content); 1238 | return this.addRaw(element).addEOL(); 1239 | } 1240 | /** 1241 | * Adds an HTML image tag to the summary buffer 1242 | * 1243 | * @param {string} src path to the image you to embed 1244 | * @param {string} alt text description of the image 1245 | * @param {SummaryImageOptions} options (optional) addition image attributes 1246 | * 1247 | * @returns {Summary} summary instance 1248 | */ 1249 | addImage(src, alt, options) { 1250 | const { width, height } = options || {}; 1251 | const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); 1252 | const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); 1253 | return this.addRaw(element).addEOL(); 1254 | } 1255 | /** 1256 | * Adds an HTML section heading element 1257 | * 1258 | * @param {string} text heading text 1259 | * @param {number | string} [level=1] (optional) the heading level, default: 1 1260 | * 1261 | * @returns {Summary} summary instance 1262 | */ 1263 | addHeading(text, level) { 1264 | const tag = `h${level}`; 1265 | const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) 1266 | ? tag 1267 | : 'h1'; 1268 | const element = this.wrap(allowedTag, text); 1269 | return this.addRaw(element).addEOL(); 1270 | } 1271 | /** 1272 | * Adds an HTML thematic break (
) to the summary buffer 1273 | * 1274 | * @returns {Summary} summary instance 1275 | */ 1276 | addSeparator() { 1277 | const element = this.wrap('hr', null); 1278 | return this.addRaw(element).addEOL(); 1279 | } 1280 | /** 1281 | * Adds an HTML line break (
) to the summary buffer 1282 | * 1283 | * @returns {Summary} summary instance 1284 | */ 1285 | addBreak() { 1286 | const element = this.wrap('br', null); 1287 | return this.addRaw(element).addEOL(); 1288 | } 1289 | /** 1290 | * Adds an HTML blockquote to the summary buffer 1291 | * 1292 | * @param {string} text quote text 1293 | * @param {string} cite (optional) citation url 1294 | * 1295 | * @returns {Summary} summary instance 1296 | */ 1297 | addQuote(text, cite) { 1298 | const attrs = Object.assign({}, (cite && { cite })); 1299 | const element = this.wrap('blockquote', text, attrs); 1300 | return this.addRaw(element).addEOL(); 1301 | } 1302 | /** 1303 | * Adds an HTML anchor tag to the summary buffer 1304 | * 1305 | * @param {string} text link text/content 1306 | * @param {string} href hyperlink 1307 | * 1308 | * @returns {Summary} summary instance 1309 | */ 1310 | addLink(text, href) { 1311 | const element = this.wrap('a', text, { href }); 1312 | return this.addRaw(element).addEOL(); 1313 | } 1314 | } 1315 | const _summary = new Summary(); 1316 | /** 1317 | * @deprecated use `core.summary` 1318 | */ 1319 | exports.markdownSummary = _summary; 1320 | exports.summary = _summary; 1321 | //# sourceMappingURL=summary.js.map 1322 | 1323 | /***/ }), 1324 | 1325 | /***/ 302: 1326 | /***/ ((__unused_webpack_module, exports) => { 1327 | 1328 | "use strict"; 1329 | 1330 | // We use any as a valid input type 1331 | /* eslint-disable @typescript-eslint/no-explicit-any */ 1332 | Object.defineProperty(exports, "__esModule", ({ value: true })); 1333 | exports.toCommandProperties = exports.toCommandValue = void 0; 1334 | /** 1335 | * Sanitizes an input into a string so it can be passed into issueCommand safely 1336 | * @param input input to sanitize into a string 1337 | */ 1338 | function toCommandValue(input) { 1339 | if (input === null || input === undefined) { 1340 | return ''; 1341 | } 1342 | else if (typeof input === 'string' || input instanceof String) { 1343 | return input; 1344 | } 1345 | return JSON.stringify(input); 1346 | } 1347 | exports.toCommandValue = toCommandValue; 1348 | /** 1349 | * 1350 | * @param annotationProperties 1351 | * @returns The command properties to send with the actual annotation command 1352 | * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 1353 | */ 1354 | function toCommandProperties(annotationProperties) { 1355 | if (!Object.keys(annotationProperties).length) { 1356 | return {}; 1357 | } 1358 | return { 1359 | title: annotationProperties.title, 1360 | file: annotationProperties.file, 1361 | line: annotationProperties.startLine, 1362 | endLine: annotationProperties.endLine, 1363 | col: annotationProperties.startColumn, 1364 | endColumn: annotationProperties.endColumn 1365 | }; 1366 | } 1367 | exports.toCommandProperties = toCommandProperties; 1368 | //# sourceMappingURL=utils.js.map 1369 | 1370 | /***/ }), 1371 | 1372 | /***/ 236: 1373 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 1374 | 1375 | "use strict"; 1376 | 1377 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 1378 | if (k2 === undefined) k2 = k; 1379 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 1380 | }) : (function(o, m, k, k2) { 1381 | if (k2 === undefined) k2 = k; 1382 | o[k2] = m[k]; 1383 | })); 1384 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 1385 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 1386 | }) : function(o, v) { 1387 | o["default"] = v; 1388 | }); 1389 | var __importStar = (this && this.__importStar) || function (mod) { 1390 | if (mod && mod.__esModule) return mod; 1391 | var result = {}; 1392 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 1393 | __setModuleDefault(result, mod); 1394 | return result; 1395 | }; 1396 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 1397 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 1398 | return new (P || (P = Promise))(function (resolve, reject) { 1399 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 1400 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 1401 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 1402 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 1403 | }); 1404 | }; 1405 | Object.defineProperty(exports, "__esModule", ({ value: true })); 1406 | exports.getExecOutput = exports.exec = void 0; 1407 | const string_decoder_1 = __nccwpck_require__(193); 1408 | const tr = __importStar(__nccwpck_require__(665)); 1409 | /** 1410 | * Exec a command. 1411 | * Output will be streamed to the live console. 1412 | * Returns promise with return code 1413 | * 1414 | * @param commandLine command to execute (can include additional args). Must be correctly escaped. 1415 | * @param args optional arguments for tool. Escaping is handled by the lib. 1416 | * @param options optional exec options. See ExecOptions 1417 | * @returns Promise exit code 1418 | */ 1419 | function exec(commandLine, args, options) { 1420 | return __awaiter(this, void 0, void 0, function* () { 1421 | const commandArgs = tr.argStringToArray(commandLine); 1422 | if (commandArgs.length === 0) { 1423 | throw new Error(`Parameter 'commandLine' cannot be null or empty.`); 1424 | } 1425 | // Path to tool to execute should be first arg 1426 | const toolPath = commandArgs[0]; 1427 | args = commandArgs.slice(1).concat(args || []); 1428 | const runner = new tr.ToolRunner(toolPath, args, options); 1429 | return runner.exec(); 1430 | }); 1431 | } 1432 | exports.exec = exec; 1433 | /** 1434 | * Exec a command and get the output. 1435 | * Output will be streamed to the live console. 1436 | * Returns promise with the exit code and collected stdout and stderr 1437 | * 1438 | * @param commandLine command to execute (can include additional args). Must be correctly escaped. 1439 | * @param args optional arguments for tool. Escaping is handled by the lib. 1440 | * @param options optional exec options. See ExecOptions 1441 | * @returns Promise exit code, stdout, and stderr 1442 | */ 1443 | function getExecOutput(commandLine, args, options) { 1444 | var _a, _b; 1445 | return __awaiter(this, void 0, void 0, function* () { 1446 | let stdout = ''; 1447 | let stderr = ''; 1448 | //Using string decoder covers the case where a mult-byte character is split 1449 | const stdoutDecoder = new string_decoder_1.StringDecoder('utf8'); 1450 | const stderrDecoder = new string_decoder_1.StringDecoder('utf8'); 1451 | const originalStdoutListener = (_a = options === null || options === void 0 ? void 0 : options.listeners) === null || _a === void 0 ? void 0 : _a.stdout; 1452 | const originalStdErrListener = (_b = options === null || options === void 0 ? void 0 : options.listeners) === null || _b === void 0 ? void 0 : _b.stderr; 1453 | const stdErrListener = (data) => { 1454 | stderr += stderrDecoder.write(data); 1455 | if (originalStdErrListener) { 1456 | originalStdErrListener(data); 1457 | } 1458 | }; 1459 | const stdOutListener = (data) => { 1460 | stdout += stdoutDecoder.write(data); 1461 | if (originalStdoutListener) { 1462 | originalStdoutListener(data); 1463 | } 1464 | }; 1465 | const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener }); 1466 | const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); 1467 | //flush any remaining characters 1468 | stdout += stdoutDecoder.end(); 1469 | stderr += stderrDecoder.end(); 1470 | return { 1471 | exitCode, 1472 | stdout, 1473 | stderr 1474 | }; 1475 | }); 1476 | } 1477 | exports.getExecOutput = getExecOutput; 1478 | //# sourceMappingURL=exec.js.map 1479 | 1480 | /***/ }), 1481 | 1482 | /***/ 665: 1483 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 1484 | 1485 | "use strict"; 1486 | 1487 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 1488 | if (k2 === undefined) k2 = k; 1489 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 1490 | }) : (function(o, m, k, k2) { 1491 | if (k2 === undefined) k2 = k; 1492 | o[k2] = m[k]; 1493 | })); 1494 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 1495 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 1496 | }) : function(o, v) { 1497 | o["default"] = v; 1498 | }); 1499 | var __importStar = (this && this.__importStar) || function (mod) { 1500 | if (mod && mod.__esModule) return mod; 1501 | var result = {}; 1502 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 1503 | __setModuleDefault(result, mod); 1504 | return result; 1505 | }; 1506 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 1507 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 1508 | return new (P || (P = Promise))(function (resolve, reject) { 1509 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 1510 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 1511 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 1512 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 1513 | }); 1514 | }; 1515 | Object.defineProperty(exports, "__esModule", ({ value: true })); 1516 | exports.argStringToArray = exports.ToolRunner = void 0; 1517 | const os = __importStar(__nccwpck_require__(857)); 1518 | const events = __importStar(__nccwpck_require__(434)); 1519 | const child = __importStar(__nccwpck_require__(317)); 1520 | const path = __importStar(__nccwpck_require__(928)); 1521 | const io = __importStar(__nccwpck_require__(994)); 1522 | const ioUtil = __importStar(__nccwpck_require__(207)); 1523 | const timers_1 = __nccwpck_require__(557); 1524 | /* eslint-disable @typescript-eslint/unbound-method */ 1525 | const IS_WINDOWS = process.platform === 'win32'; 1526 | /* 1527 | * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way. 1528 | */ 1529 | class ToolRunner extends events.EventEmitter { 1530 | constructor(toolPath, args, options) { 1531 | super(); 1532 | if (!toolPath) { 1533 | throw new Error("Parameter 'toolPath' cannot be null or empty."); 1534 | } 1535 | this.toolPath = toolPath; 1536 | this.args = args || []; 1537 | this.options = options || {}; 1538 | } 1539 | _debug(message) { 1540 | if (this.options.listeners && this.options.listeners.debug) { 1541 | this.options.listeners.debug(message); 1542 | } 1543 | } 1544 | _getCommandString(options, noPrefix) { 1545 | const toolPath = this._getSpawnFileName(); 1546 | const args = this._getSpawnArgs(options); 1547 | let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool 1548 | if (IS_WINDOWS) { 1549 | // Windows + cmd file 1550 | if (this._isCmdFile()) { 1551 | cmd += toolPath; 1552 | for (const a of args) { 1553 | cmd += ` ${a}`; 1554 | } 1555 | } 1556 | // Windows + verbatim 1557 | else if (options.windowsVerbatimArguments) { 1558 | cmd += `"${toolPath}"`; 1559 | for (const a of args) { 1560 | cmd += ` ${a}`; 1561 | } 1562 | } 1563 | // Windows (regular) 1564 | else { 1565 | cmd += this._windowsQuoteCmdArg(toolPath); 1566 | for (const a of args) { 1567 | cmd += ` ${this._windowsQuoteCmdArg(a)}`; 1568 | } 1569 | } 1570 | } 1571 | else { 1572 | // OSX/Linux - this can likely be improved with some form of quoting. 1573 | // creating processes on Unix is fundamentally different than Windows. 1574 | // on Unix, execvp() takes an arg array. 1575 | cmd += toolPath; 1576 | for (const a of args) { 1577 | cmd += ` ${a}`; 1578 | } 1579 | } 1580 | return cmd; 1581 | } 1582 | _processLineBuffer(data, strBuffer, onLine) { 1583 | try { 1584 | let s = strBuffer + data.toString(); 1585 | let n = s.indexOf(os.EOL); 1586 | while (n > -1) { 1587 | const line = s.substring(0, n); 1588 | onLine(line); 1589 | // the rest of the string ... 1590 | s = s.substring(n + os.EOL.length); 1591 | n = s.indexOf(os.EOL); 1592 | } 1593 | return s; 1594 | } 1595 | catch (err) { 1596 | // streaming lines to console is best effort. Don't fail a build. 1597 | this._debug(`error processing line. Failed with error ${err}`); 1598 | return ''; 1599 | } 1600 | } 1601 | _getSpawnFileName() { 1602 | if (IS_WINDOWS) { 1603 | if (this._isCmdFile()) { 1604 | return process.env['COMSPEC'] || 'cmd.exe'; 1605 | } 1606 | } 1607 | return this.toolPath; 1608 | } 1609 | _getSpawnArgs(options) { 1610 | if (IS_WINDOWS) { 1611 | if (this._isCmdFile()) { 1612 | let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`; 1613 | for (const a of this.args) { 1614 | argline += ' '; 1615 | argline += options.windowsVerbatimArguments 1616 | ? a 1617 | : this._windowsQuoteCmdArg(a); 1618 | } 1619 | argline += '"'; 1620 | return [argline]; 1621 | } 1622 | } 1623 | return this.args; 1624 | } 1625 | _endsWith(str, end) { 1626 | return str.endsWith(end); 1627 | } 1628 | _isCmdFile() { 1629 | const upperToolPath = this.toolPath.toUpperCase(); 1630 | return (this._endsWith(upperToolPath, '.CMD') || 1631 | this._endsWith(upperToolPath, '.BAT')); 1632 | } 1633 | _windowsQuoteCmdArg(arg) { 1634 | // for .exe, apply the normal quoting rules that libuv applies 1635 | if (!this._isCmdFile()) { 1636 | return this._uvQuoteCmdArg(arg); 1637 | } 1638 | // otherwise apply quoting rules specific to the cmd.exe command line parser. 1639 | // the libuv rules are generic and are not designed specifically for cmd.exe 1640 | // command line parser. 1641 | // 1642 | // for a detailed description of the cmd.exe command line parser, refer to 1643 | // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912 1644 | // need quotes for empty arg 1645 | if (!arg) { 1646 | return '""'; 1647 | } 1648 | // determine whether the arg needs to be quoted 1649 | const cmdSpecialChars = [ 1650 | ' ', 1651 | '\t', 1652 | '&', 1653 | '(', 1654 | ')', 1655 | '[', 1656 | ']', 1657 | '{', 1658 | '}', 1659 | '^', 1660 | '=', 1661 | ';', 1662 | '!', 1663 | "'", 1664 | '+', 1665 | ',', 1666 | '`', 1667 | '~', 1668 | '|', 1669 | '<', 1670 | '>', 1671 | '"' 1672 | ]; 1673 | let needsQuotes = false; 1674 | for (const char of arg) { 1675 | if (cmdSpecialChars.some(x => x === char)) { 1676 | needsQuotes = true; 1677 | break; 1678 | } 1679 | } 1680 | // short-circuit if quotes not needed 1681 | if (!needsQuotes) { 1682 | return arg; 1683 | } 1684 | // the following quoting rules are very similar to the rules that by libuv applies. 1685 | // 1686 | // 1) wrap the string in quotes 1687 | // 1688 | // 2) double-up quotes - i.e. " => "" 1689 | // 1690 | // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately 1691 | // doesn't work well with a cmd.exe command line. 1692 | // 1693 | // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app. 1694 | // for example, the command line: 1695 | // foo.exe "myarg:""my val""" 1696 | // is parsed by a .NET console app into an arg array: 1697 | // [ "myarg:\"my val\"" ] 1698 | // which is the same end result when applying libuv quoting rules. although the actual 1699 | // command line from libuv quoting rules would look like: 1700 | // foo.exe "myarg:\"my val\"" 1701 | // 1702 | // 3) double-up slashes that precede a quote, 1703 | // e.g. hello \world => "hello \world" 1704 | // hello\"world => "hello\\""world" 1705 | // hello\\"world => "hello\\\\""world" 1706 | // hello world\ => "hello world\\" 1707 | // 1708 | // technically this is not required for a cmd.exe command line, or the batch argument parser. 1709 | // the reasons for including this as a .cmd quoting rule are: 1710 | // 1711 | // a) this is optimized for the scenario where the argument is passed from the .cmd file to an 1712 | // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule. 1713 | // 1714 | // b) it's what we've been doing previously (by deferring to node default behavior) and we 1715 | // haven't heard any complaints about that aspect. 1716 | // 1717 | // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be 1718 | // escaped when used on the command line directly - even though within a .cmd file % can be escaped 1719 | // by using %%. 1720 | // 1721 | // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts 1722 | // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing. 1723 | // 1724 | // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would 1725 | // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the 1726 | // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args 1727 | // to an external program. 1728 | // 1729 | // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file. 1730 | // % can be escaped within a .cmd file. 1731 | let reverse = '"'; 1732 | let quoteHit = true; 1733 | for (let i = arg.length; i > 0; i--) { 1734 | // walk the string in reverse 1735 | reverse += arg[i - 1]; 1736 | if (quoteHit && arg[i - 1] === '\\') { 1737 | reverse += '\\'; // double the slash 1738 | } 1739 | else if (arg[i - 1] === '"') { 1740 | quoteHit = true; 1741 | reverse += '"'; // double the quote 1742 | } 1743 | else { 1744 | quoteHit = false; 1745 | } 1746 | } 1747 | reverse += '"'; 1748 | return reverse 1749 | .split('') 1750 | .reverse() 1751 | .join(''); 1752 | } 1753 | _uvQuoteCmdArg(arg) { 1754 | // Tool runner wraps child_process.spawn() and needs to apply the same quoting as 1755 | // Node in certain cases where the undocumented spawn option windowsVerbatimArguments 1756 | // is used. 1757 | // 1758 | // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV, 1759 | // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details), 1760 | // pasting copyright notice from Node within this function: 1761 | // 1762 | // Copyright Joyent, Inc. and other Node contributors. All rights reserved. 1763 | // 1764 | // Permission is hereby granted, free of charge, to any person obtaining a copy 1765 | // of this software and associated documentation files (the "Software"), to 1766 | // deal in the Software without restriction, including without limitation the 1767 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 1768 | // sell copies of the Software, and to permit persons to whom the Software is 1769 | // furnished to do so, subject to the following conditions: 1770 | // 1771 | // The above copyright notice and this permission notice shall be included in 1772 | // all copies or substantial portions of the Software. 1773 | // 1774 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1775 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1776 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1777 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1778 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1779 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 1780 | // IN THE SOFTWARE. 1781 | if (!arg) { 1782 | // Need double quotation for empty argument 1783 | return '""'; 1784 | } 1785 | if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) { 1786 | // No quotation needed 1787 | return arg; 1788 | } 1789 | if (!arg.includes('"') && !arg.includes('\\')) { 1790 | // No embedded double quotes or backslashes, so I can just wrap 1791 | // quote marks around the whole thing. 1792 | return `"${arg}"`; 1793 | } 1794 | // Expected input/output: 1795 | // input : hello"world 1796 | // output: "hello\"world" 1797 | // input : hello""world 1798 | // output: "hello\"\"world" 1799 | // input : hello\world 1800 | // output: hello\world 1801 | // input : hello\\world 1802 | // output: hello\\world 1803 | // input : hello\"world 1804 | // output: "hello\\\"world" 1805 | // input : hello\\"world 1806 | // output: "hello\\\\\"world" 1807 | // input : hello world\ 1808 | // output: "hello world\\" - note the comment in libuv actually reads "hello world\" 1809 | // but it appears the comment is wrong, it should be "hello world\\" 1810 | let reverse = '"'; 1811 | let quoteHit = true; 1812 | for (let i = arg.length; i > 0; i--) { 1813 | // walk the string in reverse 1814 | reverse += arg[i - 1]; 1815 | if (quoteHit && arg[i - 1] === '\\') { 1816 | reverse += '\\'; 1817 | } 1818 | else if (arg[i - 1] === '"') { 1819 | quoteHit = true; 1820 | reverse += '\\'; 1821 | } 1822 | else { 1823 | quoteHit = false; 1824 | } 1825 | } 1826 | reverse += '"'; 1827 | return reverse 1828 | .split('') 1829 | .reverse() 1830 | .join(''); 1831 | } 1832 | _cloneExecOptions(options) { 1833 | options = options || {}; 1834 | const result = { 1835 | cwd: options.cwd || process.cwd(), 1836 | env: options.env || process.env, 1837 | silent: options.silent || false, 1838 | windowsVerbatimArguments: options.windowsVerbatimArguments || false, 1839 | failOnStdErr: options.failOnStdErr || false, 1840 | ignoreReturnCode: options.ignoreReturnCode || false, 1841 | delay: options.delay || 10000 1842 | }; 1843 | result.outStream = options.outStream || process.stdout; 1844 | result.errStream = options.errStream || process.stderr; 1845 | return result; 1846 | } 1847 | _getSpawnOptions(options, toolPath) { 1848 | options = options || {}; 1849 | const result = {}; 1850 | result.cwd = options.cwd; 1851 | result.env = options.env; 1852 | result['windowsVerbatimArguments'] = 1853 | options.windowsVerbatimArguments || this._isCmdFile(); 1854 | if (options.windowsVerbatimArguments) { 1855 | result.argv0 = `"${toolPath}"`; 1856 | } 1857 | return result; 1858 | } 1859 | /** 1860 | * Exec a tool. 1861 | * Output will be streamed to the live console. 1862 | * Returns promise with return code 1863 | * 1864 | * @param tool path to tool to exec 1865 | * @param options optional exec options. See ExecOptions 1866 | * @returns number 1867 | */ 1868 | exec() { 1869 | return __awaiter(this, void 0, void 0, function* () { 1870 | // root the tool path if it is unrooted and contains relative pathing 1871 | if (!ioUtil.isRooted(this.toolPath) && 1872 | (this.toolPath.includes('/') || 1873 | (IS_WINDOWS && this.toolPath.includes('\\')))) { 1874 | // prefer options.cwd if it is specified, however options.cwd may also need to be rooted 1875 | this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath); 1876 | } 1877 | // if the tool is only a file name, then resolve it from the PATH 1878 | // otherwise verify it exists (add extension on Windows if necessary) 1879 | this.toolPath = yield io.which(this.toolPath, true); 1880 | return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { 1881 | this._debug(`exec tool: ${this.toolPath}`); 1882 | this._debug('arguments:'); 1883 | for (const arg of this.args) { 1884 | this._debug(` ${arg}`); 1885 | } 1886 | const optionsNonNull = this._cloneExecOptions(this.options); 1887 | if (!optionsNonNull.silent && optionsNonNull.outStream) { 1888 | optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); 1889 | } 1890 | const state = new ExecState(optionsNonNull, this.toolPath); 1891 | state.on('debug', (message) => { 1892 | this._debug(message); 1893 | }); 1894 | if (this.options.cwd && !(yield ioUtil.exists(this.options.cwd))) { 1895 | return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`)); 1896 | } 1897 | const fileName = this._getSpawnFileName(); 1898 | const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName)); 1899 | let stdbuffer = ''; 1900 | if (cp.stdout) { 1901 | cp.stdout.on('data', (data) => { 1902 | if (this.options.listeners && this.options.listeners.stdout) { 1903 | this.options.listeners.stdout(data); 1904 | } 1905 | if (!optionsNonNull.silent && optionsNonNull.outStream) { 1906 | optionsNonNull.outStream.write(data); 1907 | } 1908 | stdbuffer = this._processLineBuffer(data, stdbuffer, (line) => { 1909 | if (this.options.listeners && this.options.listeners.stdline) { 1910 | this.options.listeners.stdline(line); 1911 | } 1912 | }); 1913 | }); 1914 | } 1915 | let errbuffer = ''; 1916 | if (cp.stderr) { 1917 | cp.stderr.on('data', (data) => { 1918 | state.processStderr = true; 1919 | if (this.options.listeners && this.options.listeners.stderr) { 1920 | this.options.listeners.stderr(data); 1921 | } 1922 | if (!optionsNonNull.silent && 1923 | optionsNonNull.errStream && 1924 | optionsNonNull.outStream) { 1925 | const s = optionsNonNull.failOnStdErr 1926 | ? optionsNonNull.errStream 1927 | : optionsNonNull.outStream; 1928 | s.write(data); 1929 | } 1930 | errbuffer = this._processLineBuffer(data, errbuffer, (line) => { 1931 | if (this.options.listeners && this.options.listeners.errline) { 1932 | this.options.listeners.errline(line); 1933 | } 1934 | }); 1935 | }); 1936 | } 1937 | cp.on('error', (err) => { 1938 | state.processError = err.message; 1939 | state.processExited = true; 1940 | state.processClosed = true; 1941 | state.CheckComplete(); 1942 | }); 1943 | cp.on('exit', (code) => { 1944 | state.processExitCode = code; 1945 | state.processExited = true; 1946 | this._debug(`Exit code ${code} received from tool '${this.toolPath}'`); 1947 | state.CheckComplete(); 1948 | }); 1949 | cp.on('close', (code) => { 1950 | state.processExitCode = code; 1951 | state.processExited = true; 1952 | state.processClosed = true; 1953 | this._debug(`STDIO streams have closed for tool '${this.toolPath}'`); 1954 | state.CheckComplete(); 1955 | }); 1956 | state.on('done', (error, exitCode) => { 1957 | if (stdbuffer.length > 0) { 1958 | this.emit('stdline', stdbuffer); 1959 | } 1960 | if (errbuffer.length > 0) { 1961 | this.emit('errline', errbuffer); 1962 | } 1963 | cp.removeAllListeners(); 1964 | if (error) { 1965 | reject(error); 1966 | } 1967 | else { 1968 | resolve(exitCode); 1969 | } 1970 | }); 1971 | if (this.options.input) { 1972 | if (!cp.stdin) { 1973 | throw new Error('child process missing stdin'); 1974 | } 1975 | cp.stdin.end(this.options.input); 1976 | } 1977 | })); 1978 | }); 1979 | } 1980 | } 1981 | exports.ToolRunner = ToolRunner; 1982 | /** 1983 | * Convert an arg string to an array of args. Handles escaping 1984 | * 1985 | * @param argString string of arguments 1986 | * @returns string[] array of arguments 1987 | */ 1988 | function argStringToArray(argString) { 1989 | const args = []; 1990 | let inQuotes = false; 1991 | let escaped = false; 1992 | let arg = ''; 1993 | function append(c) { 1994 | // we only escape double quotes. 1995 | if (escaped && c !== '"') { 1996 | arg += '\\'; 1997 | } 1998 | arg += c; 1999 | escaped = false; 2000 | } 2001 | for (let i = 0; i < argString.length; i++) { 2002 | const c = argString.charAt(i); 2003 | if (c === '"') { 2004 | if (!escaped) { 2005 | inQuotes = !inQuotes; 2006 | } 2007 | else { 2008 | append(c); 2009 | } 2010 | continue; 2011 | } 2012 | if (c === '\\' && escaped) { 2013 | append(c); 2014 | continue; 2015 | } 2016 | if (c === '\\' && inQuotes) { 2017 | escaped = true; 2018 | continue; 2019 | } 2020 | if (c === ' ' && !inQuotes) { 2021 | if (arg.length > 0) { 2022 | args.push(arg); 2023 | arg = ''; 2024 | } 2025 | continue; 2026 | } 2027 | append(c); 2028 | } 2029 | if (arg.length > 0) { 2030 | args.push(arg.trim()); 2031 | } 2032 | return args; 2033 | } 2034 | exports.argStringToArray = argStringToArray; 2035 | class ExecState extends events.EventEmitter { 2036 | constructor(options, toolPath) { 2037 | super(); 2038 | this.processClosed = false; // tracks whether the process has exited and stdio is closed 2039 | this.processError = ''; 2040 | this.processExitCode = 0; 2041 | this.processExited = false; // tracks whether the process has exited 2042 | this.processStderr = false; // tracks whether stderr was written to 2043 | this.delay = 10000; // 10 seconds 2044 | this.done = false; 2045 | this.timeout = null; 2046 | if (!toolPath) { 2047 | throw new Error('toolPath must not be empty'); 2048 | } 2049 | this.options = options; 2050 | this.toolPath = toolPath; 2051 | if (options.delay) { 2052 | this.delay = options.delay; 2053 | } 2054 | } 2055 | CheckComplete() { 2056 | if (this.done) { 2057 | return; 2058 | } 2059 | if (this.processClosed) { 2060 | this._setResult(); 2061 | } 2062 | else if (this.processExited) { 2063 | this.timeout = timers_1.setTimeout(ExecState.HandleTimeout, this.delay, this); 2064 | } 2065 | } 2066 | _debug(message) { 2067 | this.emit('debug', message); 2068 | } 2069 | _setResult() { 2070 | // determine whether there is an error 2071 | let error; 2072 | if (this.processExited) { 2073 | if (this.processError) { 2074 | error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`); 2075 | } 2076 | else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) { 2077 | error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`); 2078 | } 2079 | else if (this.processStderr && this.options.failOnStdErr) { 2080 | error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`); 2081 | } 2082 | } 2083 | // clear the timeout 2084 | if (this.timeout) { 2085 | clearTimeout(this.timeout); 2086 | this.timeout = null; 2087 | } 2088 | this.done = true; 2089 | this.emit('done', error, this.processExitCode); 2090 | } 2091 | static HandleTimeout(state) { 2092 | if (state.done) { 2093 | return; 2094 | } 2095 | if (!state.processClosed && state.processExited) { 2096 | const message = `The STDIO streams did not close within ${state.delay / 2097 | 1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`; 2098 | state._debug(message); 2099 | } 2100 | state._setResult(); 2101 | } 2102 | } 2103 | //# sourceMappingURL=toolrunner.js.map 2104 | 2105 | /***/ }), 2106 | 2107 | /***/ 552: 2108 | /***/ (function(__unused_webpack_module, exports) { 2109 | 2110 | "use strict"; 2111 | 2112 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2113 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 2114 | return new (P || (P = Promise))(function (resolve, reject) { 2115 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 2116 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 2117 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 2118 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 2119 | }); 2120 | }; 2121 | Object.defineProperty(exports, "__esModule", ({ value: true })); 2122 | exports.PersonalAccessTokenCredentialHandler = exports.BearerCredentialHandler = exports.BasicCredentialHandler = void 0; 2123 | class BasicCredentialHandler { 2124 | constructor(username, password) { 2125 | this.username = username; 2126 | this.password = password; 2127 | } 2128 | prepareRequest(options) { 2129 | if (!options.headers) { 2130 | throw Error('The request has no headers'); 2131 | } 2132 | options.headers['Authorization'] = `Basic ${Buffer.from(`${this.username}:${this.password}`).toString('base64')}`; 2133 | } 2134 | // This handler cannot handle 401 2135 | canHandleAuthentication() { 2136 | return false; 2137 | } 2138 | handleAuthentication() { 2139 | return __awaiter(this, void 0, void 0, function* () { 2140 | throw new Error('not implemented'); 2141 | }); 2142 | } 2143 | } 2144 | exports.BasicCredentialHandler = BasicCredentialHandler; 2145 | class BearerCredentialHandler { 2146 | constructor(token) { 2147 | this.token = token; 2148 | } 2149 | // currently implements pre-authorization 2150 | // TODO: support preAuth = false where it hooks on 401 2151 | prepareRequest(options) { 2152 | if (!options.headers) { 2153 | throw Error('The request has no headers'); 2154 | } 2155 | options.headers['Authorization'] = `Bearer ${this.token}`; 2156 | } 2157 | // This handler cannot handle 401 2158 | canHandleAuthentication() { 2159 | return false; 2160 | } 2161 | handleAuthentication() { 2162 | return __awaiter(this, void 0, void 0, function* () { 2163 | throw new Error('not implemented'); 2164 | }); 2165 | } 2166 | } 2167 | exports.BearerCredentialHandler = BearerCredentialHandler; 2168 | class PersonalAccessTokenCredentialHandler { 2169 | constructor(token) { 2170 | this.token = token; 2171 | } 2172 | // currently implements pre-authorization 2173 | // TODO: support preAuth = false where it hooks on 401 2174 | prepareRequest(options) { 2175 | if (!options.headers) { 2176 | throw Error('The request has no headers'); 2177 | } 2178 | options.headers['Authorization'] = `Basic ${Buffer.from(`PAT:${this.token}`).toString('base64')}`; 2179 | } 2180 | // This handler cannot handle 401 2181 | canHandleAuthentication() { 2182 | return false; 2183 | } 2184 | handleAuthentication() { 2185 | return __awaiter(this, void 0, void 0, function* () { 2186 | throw new Error('not implemented'); 2187 | }); 2188 | } 2189 | } 2190 | exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler; 2191 | //# sourceMappingURL=auth.js.map 2192 | 2193 | /***/ }), 2194 | 2195 | /***/ 844: 2196 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 2197 | 2198 | "use strict"; 2199 | 2200 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2201 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 2202 | if (k2 === undefined) k2 = k; 2203 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 2204 | }) : (function(o, m, k, k2) { 2205 | if (k2 === undefined) k2 = k; 2206 | o[k2] = m[k]; 2207 | })); 2208 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 2209 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 2210 | }) : function(o, v) { 2211 | o["default"] = v; 2212 | }); 2213 | var __importStar = (this && this.__importStar) || function (mod) { 2214 | if (mod && mod.__esModule) return mod; 2215 | var result = {}; 2216 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 2217 | __setModuleDefault(result, mod); 2218 | return result; 2219 | }; 2220 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2221 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 2222 | return new (P || (P = Promise))(function (resolve, reject) { 2223 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 2224 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 2225 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 2226 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 2227 | }); 2228 | }; 2229 | Object.defineProperty(exports, "__esModule", ({ value: true })); 2230 | exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; 2231 | const http = __importStar(__nccwpck_require__(611)); 2232 | const https = __importStar(__nccwpck_require__(692)); 2233 | const pm = __importStar(__nccwpck_require__(988)); 2234 | const tunnel = __importStar(__nccwpck_require__(770)); 2235 | var HttpCodes; 2236 | (function (HttpCodes) { 2237 | HttpCodes[HttpCodes["OK"] = 200] = "OK"; 2238 | HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; 2239 | HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; 2240 | HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; 2241 | HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; 2242 | HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; 2243 | HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; 2244 | HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; 2245 | HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; 2246 | HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; 2247 | HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; 2248 | HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; 2249 | HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; 2250 | HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; 2251 | HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; 2252 | HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; 2253 | HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; 2254 | HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; 2255 | HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; 2256 | HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; 2257 | HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; 2258 | HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; 2259 | HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; 2260 | HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; 2261 | HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; 2262 | HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; 2263 | HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; 2264 | })(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); 2265 | var Headers; 2266 | (function (Headers) { 2267 | Headers["Accept"] = "accept"; 2268 | Headers["ContentType"] = "content-type"; 2269 | })(Headers = exports.Headers || (exports.Headers = {})); 2270 | var MediaTypes; 2271 | (function (MediaTypes) { 2272 | MediaTypes["ApplicationJson"] = "application/json"; 2273 | })(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {})); 2274 | /** 2275 | * Returns the proxy URL, depending upon the supplied url and proxy environment variables. 2276 | * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com 2277 | */ 2278 | function getProxyUrl(serverUrl) { 2279 | const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); 2280 | return proxyUrl ? proxyUrl.href : ''; 2281 | } 2282 | exports.getProxyUrl = getProxyUrl; 2283 | const HttpRedirectCodes = [ 2284 | HttpCodes.MovedPermanently, 2285 | HttpCodes.ResourceMoved, 2286 | HttpCodes.SeeOther, 2287 | HttpCodes.TemporaryRedirect, 2288 | HttpCodes.PermanentRedirect 2289 | ]; 2290 | const HttpResponseRetryCodes = [ 2291 | HttpCodes.BadGateway, 2292 | HttpCodes.ServiceUnavailable, 2293 | HttpCodes.GatewayTimeout 2294 | ]; 2295 | const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; 2296 | const ExponentialBackoffCeiling = 10; 2297 | const ExponentialBackoffTimeSlice = 5; 2298 | class HttpClientError extends Error { 2299 | constructor(message, statusCode) { 2300 | super(message); 2301 | this.name = 'HttpClientError'; 2302 | this.statusCode = statusCode; 2303 | Object.setPrototypeOf(this, HttpClientError.prototype); 2304 | } 2305 | } 2306 | exports.HttpClientError = HttpClientError; 2307 | class HttpClientResponse { 2308 | constructor(message) { 2309 | this.message = message; 2310 | } 2311 | readBody() { 2312 | return __awaiter(this, void 0, void 0, function* () { 2313 | return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { 2314 | let output = Buffer.alloc(0); 2315 | this.message.on('data', (chunk) => { 2316 | output = Buffer.concat([output, chunk]); 2317 | }); 2318 | this.message.on('end', () => { 2319 | resolve(output.toString()); 2320 | }); 2321 | })); 2322 | }); 2323 | } 2324 | } 2325 | exports.HttpClientResponse = HttpClientResponse; 2326 | function isHttps(requestUrl) { 2327 | const parsedUrl = new URL(requestUrl); 2328 | return parsedUrl.protocol === 'https:'; 2329 | } 2330 | exports.isHttps = isHttps; 2331 | class HttpClient { 2332 | constructor(userAgent, handlers, requestOptions) { 2333 | this._ignoreSslError = false; 2334 | this._allowRedirects = true; 2335 | this._allowRedirectDowngrade = false; 2336 | this._maxRedirects = 50; 2337 | this._allowRetries = false; 2338 | this._maxRetries = 1; 2339 | this._keepAlive = false; 2340 | this._disposed = false; 2341 | this.userAgent = userAgent; 2342 | this.handlers = handlers || []; 2343 | this.requestOptions = requestOptions; 2344 | if (requestOptions) { 2345 | if (requestOptions.ignoreSslError != null) { 2346 | this._ignoreSslError = requestOptions.ignoreSslError; 2347 | } 2348 | this._socketTimeout = requestOptions.socketTimeout; 2349 | if (requestOptions.allowRedirects != null) { 2350 | this._allowRedirects = requestOptions.allowRedirects; 2351 | } 2352 | if (requestOptions.allowRedirectDowngrade != null) { 2353 | this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; 2354 | } 2355 | if (requestOptions.maxRedirects != null) { 2356 | this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); 2357 | } 2358 | if (requestOptions.keepAlive != null) { 2359 | this._keepAlive = requestOptions.keepAlive; 2360 | } 2361 | if (requestOptions.allowRetries != null) { 2362 | this._allowRetries = requestOptions.allowRetries; 2363 | } 2364 | if (requestOptions.maxRetries != null) { 2365 | this._maxRetries = requestOptions.maxRetries; 2366 | } 2367 | } 2368 | } 2369 | options(requestUrl, additionalHeaders) { 2370 | return __awaiter(this, void 0, void 0, function* () { 2371 | return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); 2372 | }); 2373 | } 2374 | get(requestUrl, additionalHeaders) { 2375 | return __awaiter(this, void 0, void 0, function* () { 2376 | return this.request('GET', requestUrl, null, additionalHeaders || {}); 2377 | }); 2378 | } 2379 | del(requestUrl, additionalHeaders) { 2380 | return __awaiter(this, void 0, void 0, function* () { 2381 | return this.request('DELETE', requestUrl, null, additionalHeaders || {}); 2382 | }); 2383 | } 2384 | post(requestUrl, data, additionalHeaders) { 2385 | return __awaiter(this, void 0, void 0, function* () { 2386 | return this.request('POST', requestUrl, data, additionalHeaders || {}); 2387 | }); 2388 | } 2389 | patch(requestUrl, data, additionalHeaders) { 2390 | return __awaiter(this, void 0, void 0, function* () { 2391 | return this.request('PATCH', requestUrl, data, additionalHeaders || {}); 2392 | }); 2393 | } 2394 | put(requestUrl, data, additionalHeaders) { 2395 | return __awaiter(this, void 0, void 0, function* () { 2396 | return this.request('PUT', requestUrl, data, additionalHeaders || {}); 2397 | }); 2398 | } 2399 | head(requestUrl, additionalHeaders) { 2400 | return __awaiter(this, void 0, void 0, function* () { 2401 | return this.request('HEAD', requestUrl, null, additionalHeaders || {}); 2402 | }); 2403 | } 2404 | sendStream(verb, requestUrl, stream, additionalHeaders) { 2405 | return __awaiter(this, void 0, void 0, function* () { 2406 | return this.request(verb, requestUrl, stream, additionalHeaders); 2407 | }); 2408 | } 2409 | /** 2410 | * Gets a typed object from an endpoint 2411 | * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise 2412 | */ 2413 | getJson(requestUrl, additionalHeaders = {}) { 2414 | return __awaiter(this, void 0, void 0, function* () { 2415 | additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); 2416 | const res = yield this.get(requestUrl, additionalHeaders); 2417 | return this._processResponse(res, this.requestOptions); 2418 | }); 2419 | } 2420 | postJson(requestUrl, obj, additionalHeaders = {}) { 2421 | return __awaiter(this, void 0, void 0, function* () { 2422 | const data = JSON.stringify(obj, null, 2); 2423 | additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); 2424 | additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); 2425 | const res = yield this.post(requestUrl, data, additionalHeaders); 2426 | return this._processResponse(res, this.requestOptions); 2427 | }); 2428 | } 2429 | putJson(requestUrl, obj, additionalHeaders = {}) { 2430 | return __awaiter(this, void 0, void 0, function* () { 2431 | const data = JSON.stringify(obj, null, 2); 2432 | additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); 2433 | additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); 2434 | const res = yield this.put(requestUrl, data, additionalHeaders); 2435 | return this._processResponse(res, this.requestOptions); 2436 | }); 2437 | } 2438 | patchJson(requestUrl, obj, additionalHeaders = {}) { 2439 | return __awaiter(this, void 0, void 0, function* () { 2440 | const data = JSON.stringify(obj, null, 2); 2441 | additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); 2442 | additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); 2443 | const res = yield this.patch(requestUrl, data, additionalHeaders); 2444 | return this._processResponse(res, this.requestOptions); 2445 | }); 2446 | } 2447 | /** 2448 | * Makes a raw http request. 2449 | * All other methods such as get, post, patch, and request ultimately call this. 2450 | * Prefer get, del, post and patch 2451 | */ 2452 | request(verb, requestUrl, data, headers) { 2453 | return __awaiter(this, void 0, void 0, function* () { 2454 | if (this._disposed) { 2455 | throw new Error('Client has already been disposed.'); 2456 | } 2457 | const parsedUrl = new URL(requestUrl); 2458 | let info = this._prepareRequest(verb, parsedUrl, headers); 2459 | // Only perform retries on reads since writes may not be idempotent. 2460 | const maxTries = this._allowRetries && RetryableHttpVerbs.includes(verb) 2461 | ? this._maxRetries + 1 2462 | : 1; 2463 | let numTries = 0; 2464 | let response; 2465 | do { 2466 | response = yield this.requestRaw(info, data); 2467 | // Check if it's an authentication challenge 2468 | if (response && 2469 | response.message && 2470 | response.message.statusCode === HttpCodes.Unauthorized) { 2471 | let authenticationHandler; 2472 | for (const handler of this.handlers) { 2473 | if (handler.canHandleAuthentication(response)) { 2474 | authenticationHandler = handler; 2475 | break; 2476 | } 2477 | } 2478 | if (authenticationHandler) { 2479 | return authenticationHandler.handleAuthentication(this, info, data); 2480 | } 2481 | else { 2482 | // We have received an unauthorized response but have no handlers to handle it. 2483 | // Let the response return to the caller. 2484 | return response; 2485 | } 2486 | } 2487 | let redirectsRemaining = this._maxRedirects; 2488 | while (response.message.statusCode && 2489 | HttpRedirectCodes.includes(response.message.statusCode) && 2490 | this._allowRedirects && 2491 | redirectsRemaining > 0) { 2492 | const redirectUrl = response.message.headers['location']; 2493 | if (!redirectUrl) { 2494 | // if there's no location to redirect to, we won't 2495 | break; 2496 | } 2497 | const parsedRedirectUrl = new URL(redirectUrl); 2498 | if (parsedUrl.protocol === 'https:' && 2499 | parsedUrl.protocol !== parsedRedirectUrl.protocol && 2500 | !this._allowRedirectDowngrade) { 2501 | throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); 2502 | } 2503 | // we need to finish reading the response before reassigning response 2504 | // which will leak the open socket. 2505 | yield response.readBody(); 2506 | // strip authorization header if redirected to a different hostname 2507 | if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { 2508 | for (const header in headers) { 2509 | // header names are case insensitive 2510 | if (header.toLowerCase() === 'authorization') { 2511 | delete headers[header]; 2512 | } 2513 | } 2514 | } 2515 | // let's make the request with the new redirectUrl 2516 | info = this._prepareRequest(verb, parsedRedirectUrl, headers); 2517 | response = yield this.requestRaw(info, data); 2518 | redirectsRemaining--; 2519 | } 2520 | if (!response.message.statusCode || 2521 | !HttpResponseRetryCodes.includes(response.message.statusCode)) { 2522 | // If not a retry code, return immediately instead of retrying 2523 | return response; 2524 | } 2525 | numTries += 1; 2526 | if (numTries < maxTries) { 2527 | yield response.readBody(); 2528 | yield this._performExponentialBackoff(numTries); 2529 | } 2530 | } while (numTries < maxTries); 2531 | return response; 2532 | }); 2533 | } 2534 | /** 2535 | * Needs to be called if keepAlive is set to true in request options. 2536 | */ 2537 | dispose() { 2538 | if (this._agent) { 2539 | this._agent.destroy(); 2540 | } 2541 | this._disposed = true; 2542 | } 2543 | /** 2544 | * Raw request. 2545 | * @param info 2546 | * @param data 2547 | */ 2548 | requestRaw(info, data) { 2549 | return __awaiter(this, void 0, void 0, function* () { 2550 | return new Promise((resolve, reject) => { 2551 | function callbackForResult(err, res) { 2552 | if (err) { 2553 | reject(err); 2554 | } 2555 | else if (!res) { 2556 | // If `err` is not passed, then `res` must be passed. 2557 | reject(new Error('Unknown error')); 2558 | } 2559 | else { 2560 | resolve(res); 2561 | } 2562 | } 2563 | this.requestRawWithCallback(info, data, callbackForResult); 2564 | }); 2565 | }); 2566 | } 2567 | /** 2568 | * Raw request with callback. 2569 | * @param info 2570 | * @param data 2571 | * @param onResult 2572 | */ 2573 | requestRawWithCallback(info, data, onResult) { 2574 | if (typeof data === 'string') { 2575 | if (!info.options.headers) { 2576 | info.options.headers = {}; 2577 | } 2578 | info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); 2579 | } 2580 | let callbackCalled = false; 2581 | function handleResult(err, res) { 2582 | if (!callbackCalled) { 2583 | callbackCalled = true; 2584 | onResult(err, res); 2585 | } 2586 | } 2587 | const req = info.httpModule.request(info.options, (msg) => { 2588 | const res = new HttpClientResponse(msg); 2589 | handleResult(undefined, res); 2590 | }); 2591 | let socket; 2592 | req.on('socket', sock => { 2593 | socket = sock; 2594 | }); 2595 | // If we ever get disconnected, we want the socket to timeout eventually 2596 | req.setTimeout(this._socketTimeout || 3 * 60000, () => { 2597 | if (socket) { 2598 | socket.end(); 2599 | } 2600 | handleResult(new Error(`Request timeout: ${info.options.path}`)); 2601 | }); 2602 | req.on('error', function (err) { 2603 | // err has statusCode property 2604 | // res should have headers 2605 | handleResult(err); 2606 | }); 2607 | if (data && typeof data === 'string') { 2608 | req.write(data, 'utf8'); 2609 | } 2610 | if (data && typeof data !== 'string') { 2611 | data.on('close', function () { 2612 | req.end(); 2613 | }); 2614 | data.pipe(req); 2615 | } 2616 | else { 2617 | req.end(); 2618 | } 2619 | } 2620 | /** 2621 | * Gets an http agent. This function is useful when you need an http agent that handles 2622 | * routing through a proxy server - depending upon the url and proxy environment variables. 2623 | * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com 2624 | */ 2625 | getAgent(serverUrl) { 2626 | const parsedUrl = new URL(serverUrl); 2627 | return this._getAgent(parsedUrl); 2628 | } 2629 | _prepareRequest(method, requestUrl, headers) { 2630 | const info = {}; 2631 | info.parsedUrl = requestUrl; 2632 | const usingSsl = info.parsedUrl.protocol === 'https:'; 2633 | info.httpModule = usingSsl ? https : http; 2634 | const defaultPort = usingSsl ? 443 : 80; 2635 | info.options = {}; 2636 | info.options.host = info.parsedUrl.hostname; 2637 | info.options.port = info.parsedUrl.port 2638 | ? parseInt(info.parsedUrl.port) 2639 | : defaultPort; 2640 | info.options.path = 2641 | (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); 2642 | info.options.method = method; 2643 | info.options.headers = this._mergeHeaders(headers); 2644 | if (this.userAgent != null) { 2645 | info.options.headers['user-agent'] = this.userAgent; 2646 | } 2647 | info.options.agent = this._getAgent(info.parsedUrl); 2648 | // gives handlers an opportunity to participate 2649 | if (this.handlers) { 2650 | for (const handler of this.handlers) { 2651 | handler.prepareRequest(info.options); 2652 | } 2653 | } 2654 | return info; 2655 | } 2656 | _mergeHeaders(headers) { 2657 | if (this.requestOptions && this.requestOptions.headers) { 2658 | return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers || {})); 2659 | } 2660 | return lowercaseKeys(headers || {}); 2661 | } 2662 | _getExistingOrDefaultHeader(additionalHeaders, header, _default) { 2663 | let clientHeader; 2664 | if (this.requestOptions && this.requestOptions.headers) { 2665 | clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; 2666 | } 2667 | return additionalHeaders[header] || clientHeader || _default; 2668 | } 2669 | _getAgent(parsedUrl) { 2670 | let agent; 2671 | const proxyUrl = pm.getProxyUrl(parsedUrl); 2672 | const useProxy = proxyUrl && proxyUrl.hostname; 2673 | if (this._keepAlive && useProxy) { 2674 | agent = this._proxyAgent; 2675 | } 2676 | if (this._keepAlive && !useProxy) { 2677 | agent = this._agent; 2678 | } 2679 | // if agent is already assigned use that agent. 2680 | if (agent) { 2681 | return agent; 2682 | } 2683 | const usingSsl = parsedUrl.protocol === 'https:'; 2684 | let maxSockets = 100; 2685 | if (this.requestOptions) { 2686 | maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; 2687 | } 2688 | // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. 2689 | if (proxyUrl && proxyUrl.hostname) { 2690 | const agentOptions = { 2691 | maxSockets, 2692 | keepAlive: this._keepAlive, 2693 | proxy: Object.assign(Object.assign({}, ((proxyUrl.username || proxyUrl.password) && { 2694 | proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` 2695 | })), { host: proxyUrl.hostname, port: proxyUrl.port }) 2696 | }; 2697 | let tunnelAgent; 2698 | const overHttps = proxyUrl.protocol === 'https:'; 2699 | if (usingSsl) { 2700 | tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; 2701 | } 2702 | else { 2703 | tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; 2704 | } 2705 | agent = tunnelAgent(agentOptions); 2706 | this._proxyAgent = agent; 2707 | } 2708 | // if reusing agent across request and tunneling agent isn't assigned create a new agent 2709 | if (this._keepAlive && !agent) { 2710 | const options = { keepAlive: this._keepAlive, maxSockets }; 2711 | agent = usingSsl ? new https.Agent(options) : new http.Agent(options); 2712 | this._agent = agent; 2713 | } 2714 | // if not using private agent and tunnel agent isn't setup then use global agent 2715 | if (!agent) { 2716 | agent = usingSsl ? https.globalAgent : http.globalAgent; 2717 | } 2718 | if (usingSsl && this._ignoreSslError) { 2719 | // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process 2720 | // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options 2721 | // we have to cast it to any and change it directly 2722 | agent.options = Object.assign(agent.options || {}, { 2723 | rejectUnauthorized: false 2724 | }); 2725 | } 2726 | return agent; 2727 | } 2728 | _performExponentialBackoff(retryNumber) { 2729 | return __awaiter(this, void 0, void 0, function* () { 2730 | retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); 2731 | const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); 2732 | return new Promise(resolve => setTimeout(() => resolve(), ms)); 2733 | }); 2734 | } 2735 | _processResponse(res, options) { 2736 | return __awaiter(this, void 0, void 0, function* () { 2737 | return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { 2738 | const statusCode = res.message.statusCode || 0; 2739 | const response = { 2740 | statusCode, 2741 | result: null, 2742 | headers: {} 2743 | }; 2744 | // not found leads to null obj returned 2745 | if (statusCode === HttpCodes.NotFound) { 2746 | resolve(response); 2747 | } 2748 | // get the result from the body 2749 | function dateTimeDeserializer(key, value) { 2750 | if (typeof value === 'string') { 2751 | const a = new Date(value); 2752 | if (!isNaN(a.valueOf())) { 2753 | return a; 2754 | } 2755 | } 2756 | return value; 2757 | } 2758 | let obj; 2759 | let contents; 2760 | try { 2761 | contents = yield res.readBody(); 2762 | if (contents && contents.length > 0) { 2763 | if (options && options.deserializeDates) { 2764 | obj = JSON.parse(contents, dateTimeDeserializer); 2765 | } 2766 | else { 2767 | obj = JSON.parse(contents); 2768 | } 2769 | response.result = obj; 2770 | } 2771 | response.headers = res.message.headers; 2772 | } 2773 | catch (err) { 2774 | // Invalid resource (contents not json); leaving result obj null 2775 | } 2776 | // note that 3xx redirects are handled by the http layer. 2777 | if (statusCode > 299) { 2778 | let msg; 2779 | // if exception/error in body, attempt to get better error 2780 | if (obj && obj.message) { 2781 | msg = obj.message; 2782 | } 2783 | else if (contents && contents.length > 0) { 2784 | // it may be the case that the exception is in the body message as string 2785 | msg = contents; 2786 | } 2787 | else { 2788 | msg = `Failed request: (${statusCode})`; 2789 | } 2790 | const err = new HttpClientError(msg, statusCode); 2791 | err.result = response.result; 2792 | reject(err); 2793 | } 2794 | else { 2795 | resolve(response); 2796 | } 2797 | })); 2798 | }); 2799 | } 2800 | } 2801 | exports.HttpClient = HttpClient; 2802 | const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); 2803 | //# sourceMappingURL=index.js.map 2804 | 2805 | /***/ }), 2806 | 2807 | /***/ 988: 2808 | /***/ ((__unused_webpack_module, exports) => { 2809 | 2810 | "use strict"; 2811 | 2812 | Object.defineProperty(exports, "__esModule", ({ value: true })); 2813 | exports.checkBypass = exports.getProxyUrl = void 0; 2814 | function getProxyUrl(reqUrl) { 2815 | const usingSsl = reqUrl.protocol === 'https:'; 2816 | if (checkBypass(reqUrl)) { 2817 | return undefined; 2818 | } 2819 | const proxyVar = (() => { 2820 | if (usingSsl) { 2821 | return process.env['https_proxy'] || process.env['HTTPS_PROXY']; 2822 | } 2823 | else { 2824 | return process.env['http_proxy'] || process.env['HTTP_PROXY']; 2825 | } 2826 | })(); 2827 | if (proxyVar) { 2828 | return new URL(proxyVar); 2829 | } 2830 | else { 2831 | return undefined; 2832 | } 2833 | } 2834 | exports.getProxyUrl = getProxyUrl; 2835 | function checkBypass(reqUrl) { 2836 | if (!reqUrl.hostname) { 2837 | return false; 2838 | } 2839 | const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; 2840 | if (!noProxy) { 2841 | return false; 2842 | } 2843 | // Determine the request port 2844 | let reqPort; 2845 | if (reqUrl.port) { 2846 | reqPort = Number(reqUrl.port); 2847 | } 2848 | else if (reqUrl.protocol === 'http:') { 2849 | reqPort = 80; 2850 | } 2851 | else if (reqUrl.protocol === 'https:') { 2852 | reqPort = 443; 2853 | } 2854 | // Format the request hostname and hostname with port 2855 | const upperReqHosts = [reqUrl.hostname.toUpperCase()]; 2856 | if (typeof reqPort === 'number') { 2857 | upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); 2858 | } 2859 | // Compare request host against noproxy 2860 | for (const upperNoProxyItem of noProxy 2861 | .split(',') 2862 | .map(x => x.trim().toUpperCase()) 2863 | .filter(x => x)) { 2864 | if (upperReqHosts.some(x => x === upperNoProxyItem)) { 2865 | return true; 2866 | } 2867 | } 2868 | return false; 2869 | } 2870 | exports.checkBypass = checkBypass; 2871 | //# sourceMappingURL=proxy.js.map 2872 | 2873 | /***/ }), 2874 | 2875 | /***/ 207: 2876 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 2877 | 2878 | "use strict"; 2879 | 2880 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 2881 | if (k2 === undefined) k2 = k; 2882 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 2883 | }) : (function(o, m, k, k2) { 2884 | if (k2 === undefined) k2 = k; 2885 | o[k2] = m[k]; 2886 | })); 2887 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 2888 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 2889 | }) : function(o, v) { 2890 | o["default"] = v; 2891 | }); 2892 | var __importStar = (this && this.__importStar) || function (mod) { 2893 | if (mod && mod.__esModule) return mod; 2894 | var result = {}; 2895 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 2896 | __setModuleDefault(result, mod); 2897 | return result; 2898 | }; 2899 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2900 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 2901 | return new (P || (P = Promise))(function (resolve, reject) { 2902 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 2903 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 2904 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 2905 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 2906 | }); 2907 | }; 2908 | var _a; 2909 | Object.defineProperty(exports, "__esModule", ({ value: true })); 2910 | exports.getCmdPath = exports.tryGetExecutablePath = exports.isRooted = exports.isDirectory = exports.exists = exports.READONLY = exports.UV_FS_O_EXLOCK = exports.IS_WINDOWS = exports.unlink = exports.symlink = exports.stat = exports.rmdir = exports.rm = exports.rename = exports.readlink = exports.readdir = exports.open = exports.mkdir = exports.lstat = exports.copyFile = exports.chmod = void 0; 2911 | const fs = __importStar(__nccwpck_require__(896)); 2912 | const path = __importStar(__nccwpck_require__(928)); 2913 | _a = fs.promises 2914 | // export const {open} = 'fs' 2915 | , exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.open = _a.open, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rm = _a.rm, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink; 2916 | // export const {open} = 'fs' 2917 | exports.IS_WINDOWS = process.platform === 'win32'; 2918 | // See https://github.com/nodejs/node/blob/d0153aee367422d0858105abec186da4dff0a0c5/deps/uv/include/uv/win.h#L691 2919 | exports.UV_FS_O_EXLOCK = 0x10000000; 2920 | exports.READONLY = fs.constants.O_RDONLY; 2921 | function exists(fsPath) { 2922 | return __awaiter(this, void 0, void 0, function* () { 2923 | try { 2924 | yield exports.stat(fsPath); 2925 | } 2926 | catch (err) { 2927 | if (err.code === 'ENOENT') { 2928 | return false; 2929 | } 2930 | throw err; 2931 | } 2932 | return true; 2933 | }); 2934 | } 2935 | exports.exists = exists; 2936 | function isDirectory(fsPath, useStat = false) { 2937 | return __awaiter(this, void 0, void 0, function* () { 2938 | const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath); 2939 | return stats.isDirectory(); 2940 | }); 2941 | } 2942 | exports.isDirectory = isDirectory; 2943 | /** 2944 | * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like: 2945 | * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases). 2946 | */ 2947 | function isRooted(p) { 2948 | p = normalizeSeparators(p); 2949 | if (!p) { 2950 | throw new Error('isRooted() parameter "p" cannot be empty'); 2951 | } 2952 | if (exports.IS_WINDOWS) { 2953 | return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello 2954 | ); // e.g. C: or C:\hello 2955 | } 2956 | return p.startsWith('/'); 2957 | } 2958 | exports.isRooted = isRooted; 2959 | /** 2960 | * Best effort attempt to determine whether a file exists and is executable. 2961 | * @param filePath file path to check 2962 | * @param extensions additional file extensions to try 2963 | * @return if file exists and is executable, returns the file path. otherwise empty string. 2964 | */ 2965 | function tryGetExecutablePath(filePath, extensions) { 2966 | return __awaiter(this, void 0, void 0, function* () { 2967 | let stats = undefined; 2968 | try { 2969 | // test file exists 2970 | stats = yield exports.stat(filePath); 2971 | } 2972 | catch (err) { 2973 | if (err.code !== 'ENOENT') { 2974 | // eslint-disable-next-line no-console 2975 | console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`); 2976 | } 2977 | } 2978 | if (stats && stats.isFile()) { 2979 | if (exports.IS_WINDOWS) { 2980 | // on Windows, test for valid extension 2981 | const upperExt = path.extname(filePath).toUpperCase(); 2982 | if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) { 2983 | return filePath; 2984 | } 2985 | } 2986 | else { 2987 | if (isUnixExecutable(stats)) { 2988 | return filePath; 2989 | } 2990 | } 2991 | } 2992 | // try each extension 2993 | const originalFilePath = filePath; 2994 | for (const extension of extensions) { 2995 | filePath = originalFilePath + extension; 2996 | stats = undefined; 2997 | try { 2998 | stats = yield exports.stat(filePath); 2999 | } 3000 | catch (err) { 3001 | if (err.code !== 'ENOENT') { 3002 | // eslint-disable-next-line no-console 3003 | console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`); 3004 | } 3005 | } 3006 | if (stats && stats.isFile()) { 3007 | if (exports.IS_WINDOWS) { 3008 | // preserve the case of the actual file (since an extension was appended) 3009 | try { 3010 | const directory = path.dirname(filePath); 3011 | const upperName = path.basename(filePath).toUpperCase(); 3012 | for (const actualName of yield exports.readdir(directory)) { 3013 | if (upperName === actualName.toUpperCase()) { 3014 | filePath = path.join(directory, actualName); 3015 | break; 3016 | } 3017 | } 3018 | } 3019 | catch (err) { 3020 | // eslint-disable-next-line no-console 3021 | console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`); 3022 | } 3023 | return filePath; 3024 | } 3025 | else { 3026 | if (isUnixExecutable(stats)) { 3027 | return filePath; 3028 | } 3029 | } 3030 | } 3031 | } 3032 | return ''; 3033 | }); 3034 | } 3035 | exports.tryGetExecutablePath = tryGetExecutablePath; 3036 | function normalizeSeparators(p) { 3037 | p = p || ''; 3038 | if (exports.IS_WINDOWS) { 3039 | // convert slashes on Windows 3040 | p = p.replace(/\//g, '\\'); 3041 | // remove redundant slashes 3042 | return p.replace(/\\\\+/g, '\\'); 3043 | } 3044 | // remove redundant slashes 3045 | return p.replace(/\/\/+/g, '/'); 3046 | } 3047 | // on Mac/Linux, test the execute bit 3048 | // R W X R W X R W X 3049 | // 256 128 64 32 16 8 4 2 1 3050 | function isUnixExecutable(stats) { 3051 | return ((stats.mode & 1) > 0 || 3052 | ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || 3053 | ((stats.mode & 64) > 0 && stats.uid === process.getuid())); 3054 | } 3055 | // Get the path of cmd.exe in windows 3056 | function getCmdPath() { 3057 | var _a; 3058 | return (_a = process.env['COMSPEC']) !== null && _a !== void 0 ? _a : `cmd.exe`; 3059 | } 3060 | exports.getCmdPath = getCmdPath; 3061 | //# sourceMappingURL=io-util.js.map 3062 | 3063 | /***/ }), 3064 | 3065 | /***/ 994: 3066 | /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { 3067 | 3068 | "use strict"; 3069 | 3070 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3071 | if (k2 === undefined) k2 = k; 3072 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 3073 | }) : (function(o, m, k, k2) { 3074 | if (k2 === undefined) k2 = k; 3075 | o[k2] = m[k]; 3076 | })); 3077 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 3078 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 3079 | }) : function(o, v) { 3080 | o["default"] = v; 3081 | }); 3082 | var __importStar = (this && this.__importStar) || function (mod) { 3083 | if (mod && mod.__esModule) return mod; 3084 | var result = {}; 3085 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 3086 | __setModuleDefault(result, mod); 3087 | return result; 3088 | }; 3089 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3090 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3091 | return new (P || (P = Promise))(function (resolve, reject) { 3092 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 3093 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 3094 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 3095 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 3096 | }); 3097 | }; 3098 | Object.defineProperty(exports, "__esModule", ({ value: true })); 3099 | exports.findInPath = exports.which = exports.mkdirP = exports.rmRF = exports.mv = exports.cp = void 0; 3100 | const assert_1 = __nccwpck_require__(613); 3101 | const path = __importStar(__nccwpck_require__(928)); 3102 | const ioUtil = __importStar(__nccwpck_require__(207)); 3103 | /** 3104 | * Copies a file or folder. 3105 | * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js 3106 | * 3107 | * @param source source path 3108 | * @param dest destination path 3109 | * @param options optional. See CopyOptions. 3110 | */ 3111 | function cp(source, dest, options = {}) { 3112 | return __awaiter(this, void 0, void 0, function* () { 3113 | const { force, recursive, copySourceDirectory } = readCopyOptions(options); 3114 | const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null; 3115 | // Dest is an existing file, but not forcing 3116 | if (destStat && destStat.isFile() && !force) { 3117 | return; 3118 | } 3119 | // If dest is an existing directory, should copy inside. 3120 | const newDest = destStat && destStat.isDirectory() && copySourceDirectory 3121 | ? path.join(dest, path.basename(source)) 3122 | : dest; 3123 | if (!(yield ioUtil.exists(source))) { 3124 | throw new Error(`no such file or directory: ${source}`); 3125 | } 3126 | const sourceStat = yield ioUtil.stat(source); 3127 | if (sourceStat.isDirectory()) { 3128 | if (!recursive) { 3129 | throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`); 3130 | } 3131 | else { 3132 | yield cpDirRecursive(source, newDest, 0, force); 3133 | } 3134 | } 3135 | else { 3136 | if (path.relative(source, newDest) === '') { 3137 | // a file cannot be copied to itself 3138 | throw new Error(`'${newDest}' and '${source}' are the same file`); 3139 | } 3140 | yield copyFile(source, newDest, force); 3141 | } 3142 | }); 3143 | } 3144 | exports.cp = cp; 3145 | /** 3146 | * Moves a path. 3147 | * 3148 | * @param source source path 3149 | * @param dest destination path 3150 | * @param options optional. See MoveOptions. 3151 | */ 3152 | function mv(source, dest, options = {}) { 3153 | return __awaiter(this, void 0, void 0, function* () { 3154 | if (yield ioUtil.exists(dest)) { 3155 | let destExists = true; 3156 | if (yield ioUtil.isDirectory(dest)) { 3157 | // If dest is directory copy src into dest 3158 | dest = path.join(dest, path.basename(source)); 3159 | destExists = yield ioUtil.exists(dest); 3160 | } 3161 | if (destExists) { 3162 | if (options.force == null || options.force) { 3163 | yield rmRF(dest); 3164 | } 3165 | else { 3166 | throw new Error('Destination already exists'); 3167 | } 3168 | } 3169 | } 3170 | yield mkdirP(path.dirname(dest)); 3171 | yield ioUtil.rename(source, dest); 3172 | }); 3173 | } 3174 | exports.mv = mv; 3175 | /** 3176 | * Remove a path recursively with force 3177 | * 3178 | * @param inputPath path to remove 3179 | */ 3180 | function rmRF(inputPath) { 3181 | return __awaiter(this, void 0, void 0, function* () { 3182 | if (ioUtil.IS_WINDOWS) { 3183 | // Check for invalid characters 3184 | // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file 3185 | if (/[*"<>|]/.test(inputPath)) { 3186 | throw new Error('File path must not contain `*`, `"`, `<`, `>` or `|` on Windows'); 3187 | } 3188 | } 3189 | try { 3190 | // note if path does not exist, error is silent 3191 | yield ioUtil.rm(inputPath, { 3192 | force: true, 3193 | maxRetries: 3, 3194 | recursive: true, 3195 | retryDelay: 300 3196 | }); 3197 | } 3198 | catch (err) { 3199 | throw new Error(`File was unable to be removed ${err}`); 3200 | } 3201 | }); 3202 | } 3203 | exports.rmRF = rmRF; 3204 | /** 3205 | * Make a directory. Creates the full path with folders in between 3206 | * Will throw if it fails 3207 | * 3208 | * @param fsPath path to create 3209 | * @returns Promise 3210 | */ 3211 | function mkdirP(fsPath) { 3212 | return __awaiter(this, void 0, void 0, function* () { 3213 | assert_1.ok(fsPath, 'a path argument must be provided'); 3214 | yield ioUtil.mkdir(fsPath, { recursive: true }); 3215 | }); 3216 | } 3217 | exports.mkdirP = mkdirP; 3218 | /** 3219 | * Returns path of a tool had the tool actually been invoked. Resolves via paths. 3220 | * If you check and the tool does not exist, it will throw. 3221 | * 3222 | * @param tool name of the tool 3223 | * @param check whether to check if tool exists 3224 | * @returns Promise path to tool 3225 | */ 3226 | function which(tool, check) { 3227 | return __awaiter(this, void 0, void 0, function* () { 3228 | if (!tool) { 3229 | throw new Error("parameter 'tool' is required"); 3230 | } 3231 | // recursive when check=true 3232 | if (check) { 3233 | const result = yield which(tool, false); 3234 | if (!result) { 3235 | if (ioUtil.IS_WINDOWS) { 3236 | throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`); 3237 | } 3238 | else { 3239 | throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`); 3240 | } 3241 | } 3242 | return result; 3243 | } 3244 | const matches = yield findInPath(tool); 3245 | if (matches && matches.length > 0) { 3246 | return matches[0]; 3247 | } 3248 | return ''; 3249 | }); 3250 | } 3251 | exports.which = which; 3252 | /** 3253 | * Returns a list of all occurrences of the given tool on the system path. 3254 | * 3255 | * @returns Promise the paths of the tool 3256 | */ 3257 | function findInPath(tool) { 3258 | return __awaiter(this, void 0, void 0, function* () { 3259 | if (!tool) { 3260 | throw new Error("parameter 'tool' is required"); 3261 | } 3262 | // build the list of extensions to try 3263 | const extensions = []; 3264 | if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) { 3265 | for (const extension of process.env['PATHEXT'].split(path.delimiter)) { 3266 | if (extension) { 3267 | extensions.push(extension); 3268 | } 3269 | } 3270 | } 3271 | // if it's rooted, return it if exists. otherwise return empty. 3272 | if (ioUtil.isRooted(tool)) { 3273 | const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions); 3274 | if (filePath) { 3275 | return [filePath]; 3276 | } 3277 | return []; 3278 | } 3279 | // if any path separators, return empty 3280 | if (tool.includes(path.sep)) { 3281 | return []; 3282 | } 3283 | // build the list of directories 3284 | // 3285 | // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, 3286 | // it feels like we should not do this. Checking the current directory seems like more of a use 3287 | // case of a shell, and the which() function exposed by the toolkit should strive for consistency 3288 | // across platforms. 3289 | const directories = []; 3290 | if (process.env.PATH) { 3291 | for (const p of process.env.PATH.split(path.delimiter)) { 3292 | if (p) { 3293 | directories.push(p); 3294 | } 3295 | } 3296 | } 3297 | // find all matches 3298 | const matches = []; 3299 | for (const directory of directories) { 3300 | const filePath = yield ioUtil.tryGetExecutablePath(path.join(directory, tool), extensions); 3301 | if (filePath) { 3302 | matches.push(filePath); 3303 | } 3304 | } 3305 | return matches; 3306 | }); 3307 | } 3308 | exports.findInPath = findInPath; 3309 | function readCopyOptions(options) { 3310 | const force = options.force == null ? true : options.force; 3311 | const recursive = Boolean(options.recursive); 3312 | const copySourceDirectory = options.copySourceDirectory == null 3313 | ? true 3314 | : Boolean(options.copySourceDirectory); 3315 | return { force, recursive, copySourceDirectory }; 3316 | } 3317 | function cpDirRecursive(sourceDir, destDir, currentDepth, force) { 3318 | return __awaiter(this, void 0, void 0, function* () { 3319 | // Ensure there is not a run away recursive copy 3320 | if (currentDepth >= 255) 3321 | return; 3322 | currentDepth++; 3323 | yield mkdirP(destDir); 3324 | const files = yield ioUtil.readdir(sourceDir); 3325 | for (const fileName of files) { 3326 | const srcFile = `${sourceDir}/${fileName}`; 3327 | const destFile = `${destDir}/${fileName}`; 3328 | const srcFileStat = yield ioUtil.lstat(srcFile); 3329 | if (srcFileStat.isDirectory()) { 3330 | // Recurse 3331 | yield cpDirRecursive(srcFile, destFile, currentDepth, force); 3332 | } 3333 | else { 3334 | yield copyFile(srcFile, destFile, force); 3335 | } 3336 | } 3337 | // Change the mode for the newly created directory 3338 | yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode); 3339 | }); 3340 | } 3341 | // Buffered file copy 3342 | function copyFile(srcFile, destFile, force) { 3343 | return __awaiter(this, void 0, void 0, function* () { 3344 | if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) { 3345 | // unlink/re-link it 3346 | try { 3347 | yield ioUtil.lstat(destFile); 3348 | yield ioUtil.unlink(destFile); 3349 | } 3350 | catch (e) { 3351 | // Try to override file permission 3352 | if (e.code === 'EPERM') { 3353 | yield ioUtil.chmod(destFile, '0666'); 3354 | yield ioUtil.unlink(destFile); 3355 | } 3356 | // other errors = it doesn't exist, no work to do 3357 | } 3358 | // Copy over symlink 3359 | const symlinkFull = yield ioUtil.readlink(srcFile); 3360 | yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null); 3361 | } 3362 | else if (!(yield ioUtil.exists(destFile)) || force) { 3363 | yield ioUtil.copyFile(srcFile, destFile); 3364 | } 3365 | }); 3366 | } 3367 | //# sourceMappingURL=io.js.map 3368 | 3369 | /***/ }), 3370 | 3371 | /***/ 770: 3372 | /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { 3373 | 3374 | module.exports = __nccwpck_require__(218); 3375 | 3376 | 3377 | /***/ }), 3378 | 3379 | /***/ 218: 3380 | /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { 3381 | 3382 | "use strict"; 3383 | 3384 | 3385 | var net = __nccwpck_require__(278); 3386 | var tls = __nccwpck_require__(756); 3387 | var http = __nccwpck_require__(611); 3388 | var https = __nccwpck_require__(692); 3389 | var events = __nccwpck_require__(434); 3390 | var assert = __nccwpck_require__(613); 3391 | var util = __nccwpck_require__(23); 3392 | 3393 | 3394 | exports.httpOverHttp = httpOverHttp; 3395 | exports.httpsOverHttp = httpsOverHttp; 3396 | exports.httpOverHttps = httpOverHttps; 3397 | exports.httpsOverHttps = httpsOverHttps; 3398 | 3399 | 3400 | function httpOverHttp(options) { 3401 | var agent = new TunnelingAgent(options); 3402 | agent.request = http.request; 3403 | return agent; 3404 | } 3405 | 3406 | function httpsOverHttp(options) { 3407 | var agent = new TunnelingAgent(options); 3408 | agent.request = http.request; 3409 | agent.createSocket = createSecureSocket; 3410 | agent.defaultPort = 443; 3411 | return agent; 3412 | } 3413 | 3414 | function httpOverHttps(options) { 3415 | var agent = new TunnelingAgent(options); 3416 | agent.request = https.request; 3417 | return agent; 3418 | } 3419 | 3420 | function httpsOverHttps(options) { 3421 | var agent = new TunnelingAgent(options); 3422 | agent.request = https.request; 3423 | agent.createSocket = createSecureSocket; 3424 | agent.defaultPort = 443; 3425 | return agent; 3426 | } 3427 | 3428 | 3429 | function TunnelingAgent(options) { 3430 | var self = this; 3431 | self.options = options || {}; 3432 | self.proxyOptions = self.options.proxy || {}; 3433 | self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets; 3434 | self.requests = []; 3435 | self.sockets = []; 3436 | 3437 | self.on('free', function onFree(socket, host, port, localAddress) { 3438 | var options = toOptions(host, port, localAddress); 3439 | for (var i = 0, len = self.requests.length; i < len; ++i) { 3440 | var pending = self.requests[i]; 3441 | if (pending.host === options.host && pending.port === options.port) { 3442 | // Detect the request to connect same origin server, 3443 | // reuse the connection. 3444 | self.requests.splice(i, 1); 3445 | pending.request.onSocket(socket); 3446 | return; 3447 | } 3448 | } 3449 | socket.destroy(); 3450 | self.removeSocket(socket); 3451 | }); 3452 | } 3453 | util.inherits(TunnelingAgent, events.EventEmitter); 3454 | 3455 | TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) { 3456 | var self = this; 3457 | var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress)); 3458 | 3459 | if (self.sockets.length >= this.maxSockets) { 3460 | // We are over limit so we'll add it to the queue. 3461 | self.requests.push(options); 3462 | return; 3463 | } 3464 | 3465 | // If we are under maxSockets create a new one. 3466 | self.createSocket(options, function(socket) { 3467 | socket.on('free', onFree); 3468 | socket.on('close', onCloseOrRemove); 3469 | socket.on('agentRemove', onCloseOrRemove); 3470 | req.onSocket(socket); 3471 | 3472 | function onFree() { 3473 | self.emit('free', socket, options); 3474 | } 3475 | 3476 | function onCloseOrRemove(err) { 3477 | self.removeSocket(socket); 3478 | socket.removeListener('free', onFree); 3479 | socket.removeListener('close', onCloseOrRemove); 3480 | socket.removeListener('agentRemove', onCloseOrRemove); 3481 | } 3482 | }); 3483 | }; 3484 | 3485 | TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { 3486 | var self = this; 3487 | var placeholder = {}; 3488 | self.sockets.push(placeholder); 3489 | 3490 | var connectOptions = mergeOptions({}, self.proxyOptions, { 3491 | method: 'CONNECT', 3492 | path: options.host + ':' + options.port, 3493 | agent: false, 3494 | headers: { 3495 | host: options.host + ':' + options.port 3496 | } 3497 | }); 3498 | if (options.localAddress) { 3499 | connectOptions.localAddress = options.localAddress; 3500 | } 3501 | if (connectOptions.proxyAuth) { 3502 | connectOptions.headers = connectOptions.headers || {}; 3503 | connectOptions.headers['Proxy-Authorization'] = 'Basic ' + 3504 | new Buffer(connectOptions.proxyAuth).toString('base64'); 3505 | } 3506 | 3507 | debug('making CONNECT request'); 3508 | var connectReq = self.request(connectOptions); 3509 | connectReq.useChunkedEncodingByDefault = false; // for v0.6 3510 | connectReq.once('response', onResponse); // for v0.6 3511 | connectReq.once('upgrade', onUpgrade); // for v0.6 3512 | connectReq.once('connect', onConnect); // for v0.7 or later 3513 | connectReq.once('error', onError); 3514 | connectReq.end(); 3515 | 3516 | function onResponse(res) { 3517 | // Very hacky. This is necessary to avoid http-parser leaks. 3518 | res.upgrade = true; 3519 | } 3520 | 3521 | function onUpgrade(res, socket, head) { 3522 | // Hacky. 3523 | process.nextTick(function() { 3524 | onConnect(res, socket, head); 3525 | }); 3526 | } 3527 | 3528 | function onConnect(res, socket, head) { 3529 | connectReq.removeAllListeners(); 3530 | socket.removeAllListeners(); 3531 | 3532 | if (res.statusCode !== 200) { 3533 | debug('tunneling socket could not be established, statusCode=%d', 3534 | res.statusCode); 3535 | socket.destroy(); 3536 | var error = new Error('tunneling socket could not be established, ' + 3537 | 'statusCode=' + res.statusCode); 3538 | error.code = 'ECONNRESET'; 3539 | options.request.emit('error', error); 3540 | self.removeSocket(placeholder); 3541 | return; 3542 | } 3543 | if (head.length > 0) { 3544 | debug('got illegal response body from proxy'); 3545 | socket.destroy(); 3546 | var error = new Error('got illegal response body from proxy'); 3547 | error.code = 'ECONNRESET'; 3548 | options.request.emit('error', error); 3549 | self.removeSocket(placeholder); 3550 | return; 3551 | } 3552 | debug('tunneling connection has established'); 3553 | self.sockets[self.sockets.indexOf(placeholder)] = socket; 3554 | return cb(socket); 3555 | } 3556 | 3557 | function onError(cause) { 3558 | connectReq.removeAllListeners(); 3559 | 3560 | debug('tunneling socket could not be established, cause=%s\n', 3561 | cause.message, cause.stack); 3562 | var error = new Error('tunneling socket could not be established, ' + 3563 | 'cause=' + cause.message); 3564 | error.code = 'ECONNRESET'; 3565 | options.request.emit('error', error); 3566 | self.removeSocket(placeholder); 3567 | } 3568 | }; 3569 | 3570 | TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { 3571 | var pos = this.sockets.indexOf(socket) 3572 | if (pos === -1) { 3573 | return; 3574 | } 3575 | this.sockets.splice(pos, 1); 3576 | 3577 | var pending = this.requests.shift(); 3578 | if (pending) { 3579 | // If we have pending requests and a socket gets closed a new one 3580 | // needs to be created to take over in the pool for the one that closed. 3581 | this.createSocket(pending, function(socket) { 3582 | pending.request.onSocket(socket); 3583 | }); 3584 | } 3585 | }; 3586 | 3587 | function createSecureSocket(options, cb) { 3588 | var self = this; 3589 | TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { 3590 | var hostHeader = options.request.getHeader('host'); 3591 | var tlsOptions = mergeOptions({}, self.options, { 3592 | socket: socket, 3593 | servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host 3594 | }); 3595 | 3596 | // 0 is dummy port for v0.6 3597 | var secureSocket = tls.connect(0, tlsOptions); 3598 | self.sockets[self.sockets.indexOf(socket)] = secureSocket; 3599 | cb(secureSocket); 3600 | }); 3601 | } 3602 | 3603 | 3604 | function toOptions(host, port, localAddress) { 3605 | if (typeof host === 'string') { // since v0.10 3606 | return { 3607 | host: host, 3608 | port: port, 3609 | localAddress: localAddress 3610 | }; 3611 | } 3612 | return host; // for v0.11 or later 3613 | } 3614 | 3615 | function mergeOptions(target) { 3616 | for (var i = 1, len = arguments.length; i < len; ++i) { 3617 | var overrides = arguments[i]; 3618 | if (typeof overrides === 'object') { 3619 | var keys = Object.keys(overrides); 3620 | for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { 3621 | var k = keys[j]; 3622 | if (overrides[k] !== undefined) { 3623 | target[k] = overrides[k]; 3624 | } 3625 | } 3626 | } 3627 | } 3628 | return target; 3629 | } 3630 | 3631 | 3632 | var debug; 3633 | if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { 3634 | debug = function() { 3635 | var args = Array.prototype.slice.call(arguments); 3636 | if (typeof args[0] === 'string') { 3637 | args[0] = 'TUNNEL: ' + args[0]; 3638 | } else { 3639 | args.unshift('TUNNEL:'); 3640 | } 3641 | console.error.apply(console, args); 3642 | } 3643 | } else { 3644 | debug = function() {}; 3645 | } 3646 | exports.debug = debug; // for test 3647 | 3648 | 3649 | /***/ }), 3650 | 3651 | /***/ 613: 3652 | /***/ ((module) => { 3653 | 3654 | "use strict"; 3655 | module.exports = require("assert"); 3656 | 3657 | /***/ }), 3658 | 3659 | /***/ 317: 3660 | /***/ ((module) => { 3661 | 3662 | "use strict"; 3663 | module.exports = require("child_process"); 3664 | 3665 | /***/ }), 3666 | 3667 | /***/ 982: 3668 | /***/ ((module) => { 3669 | 3670 | "use strict"; 3671 | module.exports = require("crypto"); 3672 | 3673 | /***/ }), 3674 | 3675 | /***/ 434: 3676 | /***/ ((module) => { 3677 | 3678 | "use strict"; 3679 | module.exports = require("events"); 3680 | 3681 | /***/ }), 3682 | 3683 | /***/ 896: 3684 | /***/ ((module) => { 3685 | 3686 | "use strict"; 3687 | module.exports = require("fs"); 3688 | 3689 | /***/ }), 3690 | 3691 | /***/ 611: 3692 | /***/ ((module) => { 3693 | 3694 | "use strict"; 3695 | module.exports = require("http"); 3696 | 3697 | /***/ }), 3698 | 3699 | /***/ 692: 3700 | /***/ ((module) => { 3701 | 3702 | "use strict"; 3703 | module.exports = require("https"); 3704 | 3705 | /***/ }), 3706 | 3707 | /***/ 278: 3708 | /***/ ((module) => { 3709 | 3710 | "use strict"; 3711 | module.exports = require("net"); 3712 | 3713 | /***/ }), 3714 | 3715 | /***/ 857: 3716 | /***/ ((module) => { 3717 | 3718 | "use strict"; 3719 | module.exports = require("os"); 3720 | 3721 | /***/ }), 3722 | 3723 | /***/ 928: 3724 | /***/ ((module) => { 3725 | 3726 | "use strict"; 3727 | module.exports = require("path"); 3728 | 3729 | /***/ }), 3730 | 3731 | /***/ 193: 3732 | /***/ ((module) => { 3733 | 3734 | "use strict"; 3735 | module.exports = require("string_decoder"); 3736 | 3737 | /***/ }), 3738 | 3739 | /***/ 557: 3740 | /***/ ((module) => { 3741 | 3742 | "use strict"; 3743 | module.exports = require("timers"); 3744 | 3745 | /***/ }), 3746 | 3747 | /***/ 756: 3748 | /***/ ((module) => { 3749 | 3750 | "use strict"; 3751 | module.exports = require("tls"); 3752 | 3753 | /***/ }), 3754 | 3755 | /***/ 23: 3756 | /***/ ((module) => { 3757 | 3758 | "use strict"; 3759 | module.exports = require("util"); 3760 | 3761 | /***/ }) 3762 | 3763 | /******/ }); 3764 | /************************************************************************/ 3765 | /******/ // The module cache 3766 | /******/ var __webpack_module_cache__ = {}; 3767 | /******/ 3768 | /******/ // The require function 3769 | /******/ function __nccwpck_require__(moduleId) { 3770 | /******/ // Check if module is in cache 3771 | /******/ var cachedModule = __webpack_module_cache__[moduleId]; 3772 | /******/ if (cachedModule !== undefined) { 3773 | /******/ return cachedModule.exports; 3774 | /******/ } 3775 | /******/ // Create a new module (and put it into the cache) 3776 | /******/ var module = __webpack_module_cache__[moduleId] = { 3777 | /******/ // no module.id needed 3778 | /******/ // no module.loaded needed 3779 | /******/ exports: {} 3780 | /******/ }; 3781 | /******/ 3782 | /******/ // Execute the module function 3783 | /******/ var threw = true; 3784 | /******/ try { 3785 | /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); 3786 | /******/ threw = false; 3787 | /******/ } finally { 3788 | /******/ if(threw) delete __webpack_module_cache__[moduleId]; 3789 | /******/ } 3790 | /******/ 3791 | /******/ // Return the exports of the module 3792 | /******/ return module.exports; 3793 | /******/ } 3794 | /******/ 3795 | /************************************************************************/ 3796 | /******/ /* webpack/runtime/compat */ 3797 | /******/ 3798 | /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; 3799 | /******/ 3800 | /************************************************************************/ 3801 | /******/ 3802 | /******/ // startup 3803 | /******/ // Load entry module and return exports 3804 | /******/ // This entry module is referenced by other modules so it can't be inlined 3805 | /******/ var __webpack_exports__ = __nccwpck_require__(915); 3806 | /******/ module.exports = __webpack_exports__; 3807 | /******/ 3808 | /******/ })() 3809 | ; -------------------------------------------------------------------------------- /dist/license.txt: -------------------------------------------------------------------------------- 1 | @actions/core 2 | MIT 3 | The MIT License (MIT) 4 | 5 | Copyright 2019 GitHub 6 | 7 | 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: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | @actions/exec 14 | MIT 15 | The MIT License (MIT) 16 | 17 | Copyright 2019 GitHub 18 | 19 | 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: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | @actions/http-client 26 | MIT 27 | Actions Http Client for Node.js 28 | 29 | Copyright (c) GitHub, Inc. 30 | 31 | All rights reserved. 32 | 33 | MIT License 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 36 | associated documentation files (the "Software"), to deal in the Software without restriction, 37 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 38 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 39 | subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 44 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 45 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 46 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 47 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | 50 | @actions/io 51 | MIT 52 | The MIT License (MIT) 53 | 54 | Copyright 2019 GitHub 55 | 56 | 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: 57 | 58 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 61 | 62 | tunnel 63 | MIT 64 | The MIT License (MIT) 65 | 66 | Copyright (c) 2012 Koichi Kobayashi 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy 69 | of this software and associated documentation files (the "Software"), to deal 70 | in the Software without restriction, including without limitation the rights 71 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 72 | copies of the Software, and to permit persons to whom the Software is 73 | furnished to do so, subject to the following conditions: 74 | 75 | The above copyright notice and this permission notice shall be included in 76 | all copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 79 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 80 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 81 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 82 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 83 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 84 | THE SOFTWARE. 85 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swift-coverage-action", 3 | "version": "4.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "swift-coverage-action", 9 | "version": "4.0.0", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "@actions/core": "^1.11.1", 13 | "@actions/exec": "^1.1.1", 14 | "@actions/io": "^1.1.3" 15 | }, 16 | "devDependencies": { 17 | "@tsconfig/node20": "^20.1.5", 18 | "@types/node": "^20.19.0", 19 | "@vercel/ncc": "^0.38.3", 20 | "typescript": "^5.8.3" 21 | }, 22 | "engines": { 23 | "node": ">=20" 24 | } 25 | }, 26 | "node_modules/@actions/core": { 27 | "version": "1.11.1", 28 | "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", 29 | "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", 30 | "dependencies": { 31 | "@actions/exec": "^1.1.1", 32 | "@actions/http-client": "^2.0.1" 33 | } 34 | }, 35 | "node_modules/@actions/exec": { 36 | "version": "1.1.1", 37 | "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", 38 | "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", 39 | "dependencies": { 40 | "@actions/io": "^1.0.1" 41 | } 42 | }, 43 | "node_modules/@actions/http-client": { 44 | "version": "2.0.1", 45 | "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", 46 | "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", 47 | "dependencies": { 48 | "tunnel": "^0.0.6" 49 | } 50 | }, 51 | "node_modules/@actions/io": { 52 | "version": "1.1.3", 53 | "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", 54 | "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" 55 | }, 56 | "node_modules/@tsconfig/node20": { 57 | "version": "20.1.5", 58 | "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.5.tgz", 59 | "integrity": "sha512-Vm8e3WxDTqMGPU4GATF9keQAIy1Drd7bPwlgzKJnZtoOsTm1tduUTbDjg0W5qERvGuxPI2h9RbMufH0YdfBylA==", 60 | "dev": true, 61 | "license": "MIT" 62 | }, 63 | "node_modules/@types/node": { 64 | "version": "20.19.0", 65 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", 66 | "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", 67 | "dev": true, 68 | "license": "MIT", 69 | "dependencies": { 70 | "undici-types": "~6.21.0" 71 | } 72 | }, 73 | "node_modules/@vercel/ncc": { 74 | "version": "0.38.3", 75 | "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.3.tgz", 76 | "integrity": "sha512-rnK6hJBS6mwc+Bkab+PGPs9OiS0i/3kdTO+CkI8V0/VrW3vmz7O2Pxjw/owOlmo6PKEIxRSeZKv/kuL9itnpYA==", 77 | "dev": true, 78 | "bin": { 79 | "ncc": "dist/ncc/cli.js" 80 | } 81 | }, 82 | "node_modules/tunnel": { 83 | "version": "0.0.6", 84 | "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", 85 | "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", 86 | "engines": { 87 | "node": ">=0.6.11 <=0.7.0 || >=0.7.3" 88 | } 89 | }, 90 | "node_modules/typescript": { 91 | "version": "5.8.3", 92 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 93 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 94 | "dev": true, 95 | "license": "Apache-2.0", 96 | "bin": { 97 | "tsc": "bin/tsc", 98 | "tsserver": "bin/tsserver" 99 | }, 100 | "engines": { 101 | "node": ">=14.17" 102 | } 103 | }, 104 | "node_modules/undici-types": { 105 | "version": "6.21.0", 106 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 107 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 108 | "dev": true, 109 | "license": "MIT" 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swift-coverage-action", 3 | "version": "4.0.0", 4 | "description": "A GitHub action that converts Swift / Xcode code coverage files.", 5 | "engines": { 6 | "node": ">=20" 7 | }, 8 | "exports": { 9 | ".": "./dist/index.js" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "build": "tsc", 14 | "pack": "rm -rf dist && ncc build lib/main.js -o dist --license license.txt --target es2022" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/sersoft-gmbh/swift-coverage-action.git" 19 | }, 20 | "keywords": [ 21 | "Swift", 22 | "Xcode", 23 | "LLVM", 24 | "Coverage", 25 | "Codecov" 26 | ], 27 | "author": "ser.soft GmbH", 28 | "license": "Apache-2.0", 29 | "bugs": { 30 | "url": "https://github.com/sersoft-gmbh/swift-coverage-action/issues" 31 | }, 32 | "homepage": "https://github.com/sersoft-gmbh/swift-coverage-action#readme", 33 | "dependencies": { 34 | "@actions/core": "^1.11.1", 35 | "@actions/exec": "^1.1.1", 36 | "@actions/io": "^1.1.3" 37 | }, 38 | "devDependencies": { 39 | "@tsconfig/node20": "^20.1.5", 40 | "@types/node": "^20.19.0", 41 | "@vercel/ncc": "^0.38.3", 42 | "typescript": "^5.8.3" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core'; 2 | import { getExecOutput } from '@actions/exec'; 3 | import * as io from '@actions/io'; 4 | import { existsSync as exists, PathLike, promises as fs } from 'fs'; 5 | import * as path from 'path'; 6 | import * as os from 'os'; 7 | 8 | async function runCmd(cmd: string, ...args: string[]): Promise { 9 | const output = await getExecOutput(cmd, args.length <= 0 ? undefined : args, { silent: !core.isDebug() }); 10 | if (output.stderr.length > 0) 11 | core.warning(`Command execution wrote lines to stderr:\n${output.stderr}`); 12 | return output.stdout; 13 | } 14 | 15 | enum CovFormat { 16 | txt = 'txt', 17 | lcov = 'lcov', 18 | } 19 | 20 | declare type WalkEntry = { 21 | path: string; 22 | isDirectory: boolean; 23 | 24 | skipDescendants(): void; 25 | }; 26 | 27 | // Taken and adjusted from https://stackoverflow.com/a/65415138/1388842 28 | async function* walk(dir: string, onlyFiles: boolean = true): AsyncGenerator { 29 | const entries = await fs.readdir(dir, { withFileTypes: true }); 30 | for (const entry of entries) { 31 | const res = path.resolve(dir, entry.name); 32 | if (entry.isDirectory()) { 33 | let skipDesc = false; 34 | if (!onlyFiles) 35 | yield { path: res, isDirectory: true, skipDescendants: () => skipDesc = true }; 36 | if (!skipDesc) 37 | yield* walk(res, onlyFiles); 38 | } else { 39 | yield { path: res, isDirectory: false, skipDescendants: () => {} }; 40 | } 41 | } 42 | } 43 | 44 | async function directoryExists(path: PathLike): Promise { 45 | if (!exists(path)) return false; 46 | const stat = await fs.stat(path); 47 | return stat.isDirectory(); 48 | } 49 | 50 | async function fileExists(path: PathLike): Promise { 51 | if (!exists(path)) return false; 52 | const stat = await fs.stat(path); 53 | return stat.isFile(); 54 | } 55 | 56 | async function main() { 57 | switch (process.platform) { 58 | case 'darwin': break; 59 | case 'linux': break; 60 | default: throw new Error('This action only supports macOS and Linux!'); 61 | } 62 | 63 | core.startGroup('Validating input'); 64 | const searchPaths = core.getMultilineInput('search-paths', { required: true }) 65 | .map(p => path.resolve(p.replace(/(~|\$HOME|\${HOME})/g, os.homedir))); 66 | const outputFolder = path.resolve(core.getInput('output', { required: true }) 67 | .replace(/(~|\$HOME|\${HOME})/g, os.homedir)); 68 | const _format = core.getInput('format', { required: true }) 69 | const format = CovFormat[_format as keyof typeof CovFormat]; 70 | if (!format) throw new Error(`Invalid format: ${_format}`); 71 | const _targetNameFilter = core.getInput('target-name-filter'); 72 | const targetNameFilter = _targetNameFilter ? new RegExp(_targetNameFilter) : null; 73 | const ignoreConversionFailures = core.getBooleanInput('ignore-conversion-failures'); 74 | const failOnEmptyOutput = core.getBooleanInput('fail-on-empty-output'); 75 | core.endGroup(); 76 | 77 | await core.group('Setting up paths', async () => { 78 | await io.rmRF(outputFolder); 79 | await io.mkdirP(outputFolder); 80 | }); 81 | 82 | const profDataFiles = await core.group('Finding coverage files', async () => { 83 | let profDataFiles: string[] = []; 84 | for (const searchPath of searchPaths) { 85 | if (!await directoryExists(searchPath)) { 86 | core.info(`Skipping non-existent search path ${searchPath}...`); 87 | continue; 88 | } 89 | for await (const entry of walk(searchPath, true)) { 90 | if (/.*\.profdata$/.test(entry.path)) { 91 | profDataFiles.push(entry.path); 92 | core.debug(`Found profdata file: ${entry.path}`); 93 | } 94 | } 95 | } 96 | core.info(`Found ${profDataFiles.length} profiling data file(s):\n${profDataFiles.join('\n')}`); 97 | return profDataFiles; 98 | }); 99 | 100 | let convertedFiles: string[] = []; 101 | if (profDataFiles.length > 0) { 102 | convertedFiles = await core.group('Converting files', async () => { 103 | let outFiles: string[] = []; 104 | let conversionFailures: Error[] = []; 105 | let processedTargets = new Set(); 106 | for (const profDataFile of profDataFiles) { 107 | const profDataDir = path.dirname(profDataFile); 108 | const xcodeRegex = /(Build).*/; 109 | let buildDir: string; 110 | let isXcode: boolean; 111 | if (xcodeRegex.test(profDataDir)) { 112 | buildDir = profDataDir.replace(xcodeRegex, '$1'); 113 | isXcode = true; 114 | } else { // SPM 115 | buildDir = path.dirname(profDataDir); 116 | isXcode = false; 117 | } 118 | core.debug(`Checking contents of build dir ${buildDir} of prof data file ${profDataFile}`); 119 | for await (const entry of walk(buildDir, false)) { 120 | const typesRegex = /.*\.(app|framework|xctest)$/; 121 | if (!typesRegex.test(entry.path)) continue; 122 | entry.skipDescendants(); // Don't process any further files inside this container. 123 | if (isXcode && !/\/Build[^/]*\/Products\//.test(entry.path)) { 124 | core.info(`Skipping ${entry.path} because it is not in a Xcode build products directory...`); 125 | continue; 126 | } 127 | const type = entry.path.replace(typesRegex, '$1'); 128 | core.debug(`Found match of type ${type}: ${entry.path}`); 129 | const proj = entry.path 130 | .replace(/.*\//, '') 131 | .replace(`.${type}`, ''); 132 | core.debug(`Project name: ${proj}`); 133 | if (processedTargets.has(`${proj}.${type}`)) { 134 | core.info(`Skipping ${proj} with type ${type} because it has already been converted...`); 135 | continue; 136 | } 137 | if (targetNameFilter && !targetNameFilter.test(proj)) { 138 | core.info(`Skipping ${proj} due to target name filter...`); 139 | continue; 140 | } 141 | 142 | let dest: string; 143 | let cmd: string; 144 | let args: string[]; 145 | if (process.platform === 'darwin') { 146 | dest = path.join(entry.path, proj); 147 | if (!await fileExists(dest)) { 148 | const macOSPath = path.join(entry.path, 'Contents', 'MacOS'); 149 | dest = path.join(macOSPath, proj); 150 | if (!await fileExists(dest)) { 151 | // Try again with whitespaces removed. 152 | dest = path.join(macOSPath, proj.replace(' ', '')); 153 | } 154 | if (!await fileExists(dest)) { 155 | core.warning(`Couldn't find a suitable target file in ${entry.path}. Using the path itself...`); 156 | dest = entry.path; 157 | } 158 | } 159 | cmd = 'xcrun'; 160 | args = ['llvm-cov']; 161 | } else { 162 | dest = entry.path; 163 | cmd = 'llvm-cov'; 164 | args = []; 165 | } 166 | let fileEnding: string; 167 | switch (format) { 168 | case CovFormat.txt: 169 | args.push('show'); 170 | fileEnding = 'txt'; 171 | break; 172 | case CovFormat.lcov: 173 | args.push('export', '-format=lcov'); 174 | fileEnding = 'lcov'; 175 | break; 176 | } 177 | args.push('-instr-profile', profDataFile, dest); 178 | let converted: string; 179 | try { 180 | converted = await runCmd(cmd, ...args); 181 | } catch (error: any) { 182 | const msg = `Failed to convert ${dest}: ${error}`; 183 | if (error instanceof Error) 184 | conversionFailures.push(error); 185 | else 186 | conversionFailures.push(new Error(msg)); 187 | if (ignoreConversionFailures) core.info(msg); 188 | else core.error(msg); 189 | continue; 190 | } 191 | const projFileName = proj.replace(/\s/g, ''); 192 | const outFile = path.join(outputFolder, `${projFileName}.${type}.coverage.${fileEnding}`); 193 | core.debug(`Writing coverage report to ${outFile}`); 194 | await fs.writeFile(outFile, converted); 195 | outFiles.push(outFile); 196 | processedTargets.add(`${proj}.${type}`); 197 | } 198 | } 199 | if (conversionFailures.length > 0) { 200 | if (ignoreConversionFailures) 201 | core.info(`Failed to convert ${conversionFailures.length} file(s)...`); 202 | else 203 | throw new Error('Conversion failures:\n' + conversionFailures.map(e => e.toString()).join('\n')); 204 | } 205 | core.info(`Processed ${outFiles.length} file(s):\n${outFiles.join('\n')}`); 206 | return outFiles; 207 | }); 208 | } 209 | core.setOutput('files', JSON.stringify(convertedFiles)); 210 | if (convertedFiles.length <= 0 && failOnEmptyOutput) 211 | throw new Error('No coverage files found (or none succeeded to convert)!'); 212 | } 213 | 214 | try { 215 | main().catch(error => core.setFailed(error.message)); 216 | } catch (error: any) { 217 | core.setFailed(error.message); 218 | } 219 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src", 6 | }, 7 | "include": ["src/**/*"] 8 | } 9 | --------------------------------------------------------------------------------