Compare commits

..

110 Commits

Author SHA1 Message Date
Yang 2b2ebf2e51 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.21.0.
  Update packages.
  Support aosp_atd and google_atd targets.
  Added SORMAS to the list of projects
  Bump tmpl from 1.0.4 to 1.0.5
  Update NPM packages.
  Add tinylog to project list
  Add paths-ignore for pull_request.
  Fix typo.
2021-10-23 19:52:26 +11:00
Yang d203cdc3fb Prepare for release 2.21.0. 2021-10-23 19:51:17 +11:00
Yang 8896ab3615 Update packages. 2021-10-23 19:29:35 +11:00
Yang 8f0c30aea2 Support aosp_atd and google_atd targets. 2021-10-23 19:29:35 +11:00
Alex d1f5d2c017 Added SORMAS to the list of projects
hzi-braunschweig/SORMAS-Project#6836
2021-10-20 00:22:32 +11:00
dependabot[bot] d0dfd56912 Bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 02:52:23 +10:00
Yang 7f2c89a896 Update NPM packages. 2021-09-18 00:17:57 +10:00
Martin Winandy 1424075cfd Add tinylog to project list 2021-09-07 10:29:24 +10:00
Yang 0bc3ba22b5 Add paths-ignore for pull_request. 2021-09-07 10:28:50 +10:00
Yang acdb69812b Fix typo. 2021-08-28 20:21:11 +10:00
Yang f71c6d1f84 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.20.0.
  Update manual workflow.
  Support choosing channel to download SDK components from.
  build-tools to 31.0.0; SDK cmd-line tools to 5.0. (#174)
  Fix macos-11 VM label.
  Realm JavaScript is using Android Emulator Runner (#182)
  Add MobileRT to the list of projects (#181)
  Support non-mobile targets (Wear OS, TV) (#180)
  Bump path-parse from 1.0.6 to 1.0.7
2021-08-28 20:04:58 +10:00
Yang 511d462729 Prepare for release 2.20.0. 2021-08-28 20:03:27 +10:00
Yang d83fb3d4fb Update manual workflow. 2021-08-28 19:51:59 +10:00
Yang 1427331a62 Support choosing channel to download SDK components from. 2021-08-28 19:44:11 +10:00
Yang 1ded27b164 build-tools to 31.0.0; SDK cmd-line tools to 5.0. (#174)
* Update build-tools to 31.0.0. Add API 31 emulator to workflow. Update SDK command-line tools to 5.0

* Update test fixture dependencies.

* Test API 31 with macos-11.

* Add android:exported to AndroidManifest.

* Format
2021-08-28 17:59:37 +10:00
Yang 217e44e3c5 Fix macos-11 VM label. 2021-08-27 11:39:30 +10:00
Kenneth Geisshirt 31d8d4204a Realm JavaScript is using Android Emulator Runner (#182) 2021-08-18 21:09:31 +10:00
Tiago Santos a5bc682448 Add MobileRT to the list of projects (#181) 2021-08-16 17:58:48 +10:00
Alex Vanyo 4d1a61ed38 Support non-mobile targets (Wear OS, TV) (#180)
* Add non-mobile targets to allowlist

* Add new targets to input validator tests

* Update built input-validator.js

* Update README with new targets

* Update action.yml with new targets
2021-08-14 03:25:01 +10:00
dependabot[bot] 1bf9f17bc6 Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-13 21:03:46 +10:00
Yang 5de26e4bd2 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.19.1.
  Accept all Android SDK Licenses
  Adds Norris to the list of projects
2021-07-20 23:53:04 +10:00
Yang 5e487dbdfe Prepare for release 2.19.1. 2021-07-20 23:52:06 +10:00
Marcus Pridham 08852ad83b Accept all Android SDK Licenses
Fixes #168
2021-07-20 11:44:43 +10:00
Ubiratan Soares 25bcfbab74 Adds Norris to the list of projects 2021-07-04 21:01:19 +10:00
Yang ac874f3db5 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.19.0.
  Add option to set AVD RAM size.
2021-06-26 23:49:47 +10:00
Yang 0c3db9cd7a Prepare for release 2.19.0. 2021-06-26 23:48:51 +10:00
Yang eba1fbfb59 Add option to set AVD RAM size. 2021-06-26 23:45:24 +10:00
Yang 6e775ef330 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.18.1.
  Unzip specific emulator build into latest emulator build from sdkmanager. Support using modern build id for emulator-build.
2021-06-26 01:23:30 +10:00
Yang 8a4333421e Prepare for release 2.18.1. 2021-06-26 01:22:31 +10:00
Yang 50ef0172a3 Unzip specific emulator build into latest emulator build from sdkmanager. Support using modern build id for emulator-build. 2021-06-26 01:14:29 +10:00
Yang 97449e998a Merge branch 'main' into release/v2
* main:
  Prepare for release 2.18.0.
  Add sample workflow for AVD snapshot caching.
  Add `force-avd-creation` to Configurations table in README.md.
  Add force-avd-creation to skip avd creation if avd with same name exists. Update workflow to test snapshot caching.
  Add accompanist to adoption list
  Gradle 7.1.
  Bump glob-parent from 5.1.1 to 5.1.2
  Bump ws from 7.3.1 to 7.4.6
2021-06-20 15:28:07 +10:00
Yang fe99eb10f3 Prepare for release 2.18.0. 2021-06-20 15:27:11 +10:00
Yang 6fa269535d Add sample workflow for AVD snapshot caching. 2021-06-20 01:41:30 +10:00
Yang 9b7af03883 Add force-avd-creation to Configurations table in README.md. 2021-06-20 01:41:30 +10:00
Yang 854ed15ed0 Add force-avd-creation to skip avd creation if avd with same name exists. Update workflow to test snapshot caching. 2021-06-20 01:41:30 +10:00
Yang 98e515da59 Add accompanist to adoption list 2021-06-18 16:40:54 +10:00
Yang 9e81ae6235 Gradle 7.1. 2021-06-15 12:38:10 +10:00
dependabot[bot] 6f33a98fa4 Bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 00:53:25 +10:00
dependabot[bot] e94dda8413 Bump ws from 7.3.1 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.3.1 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.3.1...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-30 10:01:46 +10:00
Yang 226f26251f Merge branch 'main' into release/v2
* main:
  Prepare for release 2.17.0.
  Update test fixture dependencies.
  Add option to toggle Linux hardware acceleration
  Bump hosted-git-info from 2.8.8 to 2.8.9
  Bump lodash from 4.17.19 to 4.17.21
2021-05-28 17:13:49 +10:00
Yang 43421164e2 Prepare for release 2.17.0. 2021-05-28 17:12:50 +10:00
Yang a3d693d819 Update test fixture dependencies. 2021-05-28 16:42:18 +10:00
Steve Stotter 5e38ea2647 Add option to toggle Linux hardware acceleration 2021-05-28 16:36:37 +10:00
dependabot[bot] 080a93d5e1 Bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 08:29:15 +10:00
dependabot[bot] 98e4e00ead Bump lodash from 4.17.19 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 08:26:54 +10:00
Yang 599839e428 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.16.0.
  Add support for arm64-v8a for Apple Silicon Macs
  Add option to disable spellcheck
  Bump y18n from 4.0.0 to 4.0.1
  Update README.md
  Add google/android-fhir to the list of projects that use android-emulator-runner (#132)
  Add Wikipedia app to list of projects.
  Add kiwix-android to the list of projects
  Avoid Wrapping Script Code In Quotes
2021-05-07 18:32:02 +10:00
Yang 751bef68f6 Prepare for release 2.16.0. 2021-05-07 18:31:11 +10:00
Cihat Gündüz bbf0887eb9 Add support for arm64-v8a for Apple Silicon Macs
Fixes https://github.com/ReactiveCircus/android-emulator-runner/issues/145
2021-04-17 13:54:28 +10:00
Afzal Najam 84b9418977 Add option to disable spellcheck
Add input-validator and tests
2021-04-08 00:44:27 +10:00
dependabot[bot] a732f125bb Bump y18n from 4.0.0 to 4.0.1
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 16:32:08 +11:00
Yang 5201a33a90 Update README.md 2021-03-24 09:29:59 +11:00
Jing Tang cf46dc4c18 Add google/android-fhir to the list of projects that use android-emulator-runner (#132) 2021-03-24 07:51:43 +11:00
Dmitry Brant f790770d0f Add Wikipedia app to list of projects. 2021-03-18 12:14:00 +11:00
Ayush Shrivastava 505c899f12 Add kiwix-android to the list of projects
Add kiwix-android to the list of projects that use ReactiveCircus-android-emulator-runner
2021-03-10 12:22:34 +11:00
Brian Dickens 0d47f0813d Avoid Wrapping Script Code In Quotes
The exec() command only runs a program with literal argument strings.
It does not know how to do things like expanding environment variables,
or piping/redirection.  Hence the way to get "shell intelligence"
is to use exec() to run the `sh` process with the command line string
as a parameter.

But the method being used to do this was:

    exec.exec(`sh -c \\"${script}"`)

This has problems, because wrapping the script in quotes creates
issues when the script itself contains quotes.  Escaping the string
correctly is a non-trivial problem, which also would create "noise"
in the debug output which would add confusion.

http://mywiki.wooledge.org/BashFAQ/050

The easiest way to work around this here is to use the array form of
exec() to pass exactly two parameters to `sh`.  No manipulation of the
script string is needed with this approach:

    exec.exec("sh", ["-c", script])

There are more cases in the project of `sh -c` usage which should also
be changed, and likely abstracted (shellExec()?)  But this small patch
just fixes the most important case for the user-provided script.

---

Additionally, this removes the escaped backslash (`\\`) from the
start of the executed command.  That was presumably to suppress the
use of aliases:

https://unix.stackexchange.com/questions/524254/why-are-backslashes-included-in-this-shell-script

But because this is invoking a non-interactive shell session, aliases
would not apply.  Hence the extra backslash shouldn't be needed.
2021-03-09 18:49:23 +11:00
Yang d2799957d6 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.15.0.
  Change default cores to 2, add cores in workflow
  Add option to modify the number of cores
2021-03-05 10:41:47 +11:00
Yang 838986e303 Prepare for release 2.15.0. 2021-03-05 10:40:10 +11:00
Afzal Najam 27a0759d85 Change default cores to 2, add cores in workflow 2021-03-03 08:41:55 +11:00
Afzal Najam d85239a9fb Add option to modify the number of cores 2021-03-03 08:41:55 +11:00
Yang 08b092e904 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.3.
  Always add path for cmdline-tools to support macos-11.0.
  Gradle 6.8.
  Add Hash Checker app projects to consumers list
  Add LeakCanary to adopter list
2021-01-13 19:29:01 +11:00
Yang 91b2b2f06f Prepare for release 2.14.3. 2021-01-13 19:28:11 +11:00
Yang 205fbe1b31 Always add path for cmdline-tools to support macos-11.0. 2021-01-13 19:24:10 +11:00
Yang efc0dae5fa Gradle 6.8. 2021-01-09 12:41:16 +11:00
fartem 4281471e5b Add Hash Checker app projects to consumers list 2021-01-09 10:03:23 +11:00
Yang 7f22785663 Add LeakCanary to adopter list 2021-01-06 23:10:03 +11:00
Yang 94bd1ed921 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.2.
  Support non-default API 28 images
2021-01-05 18:29:16 +11:00
Yang c3e261e13b Prepare for release 2.14.2. 2021-01-05 18:28:29 +11:00
linl33 434e949c3b Support non-default API 28 images 2021-01-05 18:21:57 +11:00
Yang 70e3f6e680 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.1.
  Fix AVD creation hang when profile is not specified.
2020-12-28 14:00:37 +11:00
Yang 0d8602fe47 Prepare for release 2.14.1. 2020-12-28 13:59:41 +11:00
Yang 28373d3d4f Fix AVD creation hang when profile is not specified. 2020-12-28 13:13:03 +11:00
Yang 02cf805290 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.0.
  Support specifying SD card path or size.
  Update npm packages.
  Replace usages of deprecated ANDROID_HOME with ANDROID_ROOT_SDK. Bump build tools to 30.0.3. Update test-fixture dependencies.
  Update Compose Samples URL
2020-12-27 20:48:59 +11:00
Yang 045b7d8c5e Prepare for release 2.14.0. 2020-12-27 20:46:12 +11:00
Yang b0f3c521a5 Support specifying SD card path or size. 2020-12-27 20:37:13 +11:00
Yang 308aa234e1 Update npm packages. 2020-12-25 00:24:38 +11:00
Yang d4b3926c7d Replace usages of deprecated ANDROID_HOME with ANDROID_ROOT_SDK.
Bump build tools to 30.0.3.
Update test-fixture dependencies.
2020-12-25 00:05:51 +11:00
Vitus 877a4c47c3 Update Compose Samples URL 2020-12-11 03:24:04 +11:00
Yang e08f702234 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.13.0.
  Update cmdline-tools to 3.0.
  Update npm packages.
  Add Shopify/android-testify to list of projects
2020-11-14 19:05:52 +11:00
Yang 11559ab158 Prepare for release 2.13.0. 2020-11-14 19:04:56 +11:00
Yang b5ac8dfbfe Update cmdline-tools to 3.0. 2020-11-14 19:02:24 +11:00
Yang ad97647a61 Update npm packages. 2020-11-08 16:53:33 +11:00
Daniel Jette 4690ba2920 Add Shopify/android-testify to list of projects
Shopify/android-testify uses the Android Emulator Runner for GitHub Actions
2020-10-27 08:42:51 +11:00
Yang 07b0366e7b Merge branch 'main' into release/v2
* main:
  Prepare for release 2.12.0.
  Add support for using google_apis_playstore system images.
  Fix README.md typo
2020-10-16 14:48:14 +11:00
Yang 15da0b2f14 Prepare for release 2.12.0. 2020-10-16 14:39:02 +11:00
Yang 53151f5236 Add support for using google_apis_playstore system images. 2020-10-16 14:32:01 +11:00
Adrián Nieto Rodríguez fd240e46a6 Fix README.md typo 2020-10-15 00:01:10 +11:00
Yang c56210bc6a Merge branch 'main' into release/v2
* main:
  Prepare for release 2.11.1.
  Update npm packages.
  Bump @actions/core from 1.2.4 to 1.2.6
  Update to gradle 6.7-rc-1 for test fixture. Build with Java 15 on CI.
  Improve GitHub Actions Gradle cache.
  Add square/radiography to adopter list
  Add cashapp/copper to adapter list
  SDK Build tools 30.0.2.
  Update fixture dependencies.
  Gradle 6.6.
  Update README.md
  Update packages.
  Build tools 30.0.1.
  Use fs for writing licence files.
  Update cmdline-tools to 2.1.
2020-10-08 12:44:58 +11:00
Yang 04a32d7ac6 Prepare for release 2.11.1. 2020-10-08 12:20:45 +11:00
Yang 1eebfdbd84 Update npm packages. 2020-10-08 12:14:51 +11:00
dependabot[bot] 47cc04b42b Bump @actions/core from 1.2.4 to 1.2.6
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.4 to 1.2.6.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-02 03:38:16 +10:00
Yang dd0984ca1b Update to gradle 6.7-rc-1 for test fixture. Build with Java 15 on CI. 2020-09-20 18:50:55 +10:00
Yang fef056dbdf Improve GitHub Actions Gradle cache. 2020-09-03 14:55:52 +10:00
Yang 31e4885c24 Add square/radiography to adopter list 2020-09-02 13:05:03 +10:00
Yang a59d5e6807 Add cashapp/copper to adapter list 2020-08-16 19:13:51 +10:00
Yang 8feb8a564a SDK Build tools 30.0.2. 2020-08-15 15:00:23 +10:00
Yang d677dca18c Update fixture dependencies. 2020-08-15 00:01:17 +10:00
Yang d23e781d9a Gradle 6.6. 2020-08-11 10:34:30 +10:00
Vinay Shenoy 29a8a60dbb Update README.md
We use this action for running instrumented tests in CI. Thanks for the great work!
2020-07-28 22:24:22 +10:00
Yang 327f6f1c1f Update packages. 2020-07-18 19:43:39 +10:00
Yang 1dc0441870 Build tools 30.0.1. 2020-07-09 19:49:16 +10:00
Yang fb3c06d709 Use fs for writing licence files. 2020-07-03 16:14:01 +10:00
Yang e22ecdedee Update cmdline-tools to 2.1. 2020-06-24 23:39:11 +10:00
Yang 4a8a9af74c Merge branch 'main' into release/v2
* main:
  Prepare for release 2.11.0.
  Support running multiple actions sequentially in a single job.
  Add RxBinding to adopter list.
  Rename master branch to main.
2020-06-20 19:53:32 +10:00
Yang a72b5eb6c0 Prepare for release 2.11.0. 2020-06-20 19:52:48 +10:00
Yang b39f1a9f8b Support running multiple actions sequentially in a single job. 2020-06-20 19:48:18 +10:00
Yang a4d07e10d7 Add RxBinding to adopter list. 2020-06-19 14:46:44 +10:00
Yang 6f6ca5ebc6 Rename master branch to main. 2020-06-18 19:26:07 +10:00
Yang b62c970881 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.10.0.
  Support API 30 system images and add API 30 to workflow. Update build tools to 30.0.0. Update test projec’s compileSdk and targetSdk to API 30.
2020-06-12 19:51:10 +10:00
Yang 9ef7536c4b Prepare for release 2.10.0. 2020-06-12 19:50:37 +10:00
Yang 8d0ce4c94b Support API 30 system images and add API 30 to workflow. Update build tools to 30.0.0. Update test projec’s compileSdk and targetSdk to API 30. 2020-06-12 19:44:21 +10:00
200 changed files with 10379 additions and 9889 deletions
+124
View File
@@ -0,0 +1,124 @@
name: Main workflow
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- main
paths-ignore:
- '**.md'
jobs:
test:
runs-on: ${{ matrix.os }}
env:
JAVA_TOOL_OPTIONS: -Xmx4g
timeout-minutes: 15
strategy:
matrix:
os: [macos-latest, ubuntu-latest]
api-level: [16, 23, 29]
target: [default, google_apis]
arch: [x86]
exclude:
- os: ubuntu-latest
api-level: 23
- os: ubuntu-latest
api-level: 29
- target: google_apis
api-level: 16
- target: google_apis
api-level: 23
- target: google_apis
api-level: 29
include:
- os: macos-latest
api-level: 24
target: playstore
arch: x86
- os: macos-latest
api-level: 30
target: aosp_atd
arch: x86
- os: macos-11
api-level: 31
target: google_apis
arch: x86_64
steps:
- name: checkout
uses: actions/checkout@v2
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: build, test and lint
run: |
npm install
npm run build
npm run lint
npm test
- name: Java 15
uses: actions/setup-java@v1
with:
java-version: 15
- uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
- uses: actions/cache@v2
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
~/.android/debug.keystore
key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}
- name: assemble tests
run: |
cd ./test-fixture/
./gradlew assembleAndroidTest
- name: run emulator to generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: ./
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
profile: Galaxy Nexus
cores: 2
sdcard-path-or-size: 100M
avd-name: test
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
working-directory: ./test-fixture/
channel: canary
script: echo "Generated AVD snapshot for caching."
- name: run action
uses: ./
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
profile: Galaxy Nexus
cores: 2
ram-size: 2048M
sdcard-path-or-size: 100M
avd-name: test
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
working-directory: ./test-fixture/
channel: canary
script: |
echo $GITHUB_REPOSITORY
adb devices
./gradlew connectedDebugAndroidTest
+76
View File
@@ -0,0 +1,76 @@
name: Manually triggered workflow
on:
workflow_dispatch:
inputs:
os:
description: 'OS'
required: true
default: 'macos-latest'
api-level:
description: 'API level of the platform and system image'
required: true
default: '30'
target:
description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv or google-tv'
required: true
default: 'default'
arch:
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
default: 'x86'
emulator-options:
description: 'command-line options used when launching the emulator'
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
emulator-build:
description: 'build number of a specific version of the emulator binary to use'
channel:
description: 'Channel to download the SDK components from - `stable`, `beta`, `dev`, `canary`'
default: 'stable'
script:
description: 'custom script to run - e.g. `./gradlew connectedCheck`'
required: true
default: './gradlew connectedDebugAndroidTest'
jobs:
test:
runs-on: ${{ github.event.inputs.os }}
env:
JAVA_TOOL_OPTIONS: -Xmx4g
timeout-minutes: 15
steps:
- name: checkout
uses: actions/checkout@v2
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: build, test and lint
run: |
npm install
npm run build
npm run lint
npm test
- name: Java 15
uses: actions/setup-java@v1
with:
java-version: 15
- uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
- name: run action
uses: ./
with:
api-level: ${{ github.event.inputs.api-level }}
target: ${{ github.event.inputs.target }}
arch: ${{ github.event.inputs.arch }}
profile: Galaxy Nexus
emulator-options: ${{ github.event.inputs.emulator-options }}
emulator-build: ${{ github.event.inputs.emulator-build }}
channel: ${{ github.event.inputs.channel }}
working-directory: ./test-fixture/
script: ${{ github.event.inputs.script }}
-63
View File
@@ -1,63 +0,0 @@
name: Main workflow
on:
pull_request:
push:
branches:
- master
paths-ignore:
- '**.md'
jobs:
test:
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
matrix:
os: [macos-latest, ubuntu-latest]
api-level: [16, 23, 29]
exclude:
- os: ubuntu-latest
api-level: 23
- os: ubuntu-latest
api-level: 29
steps:
- name: checkout
uses: actions/checkout@v2
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: build, test and lint
run: |
npm install
npm run build
npm run lint
npm test
- name: Java 14
uses: actions/setup-java@v1
with:
java-version: 14
- name: Clean cache
if: matrix.os == 'macos-latest'
run: mv ~/.gradle/caches ~/.gradle/.invalid_caches
- uses: actions/cache@v1
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
- name: run action
uses: ./
with:
api-level: ${{ matrix.api-level }}
target: default
arch: x86
profile: Nexus 6
avd-name: test
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
disable-animations: true
working-directory: ./test-fixture/
script: |
echo $GITHUB_REPOSITORY
adb devices
./gradlew help
./gradlew connectedDebugAndroidTest
+95
View File
@@ -1,5 +1,100 @@
# Change Log
## v2.21.0
* Support new [ATD](https://developer.android.com/studio/preview/features#gmd-atd) targets optimized to reduce CPU and memory resources when running instrumented tests: `aosp_atd`, `google_atd`. - [#198](https://github.com/ReactiveCircus/android-emulator-runner/pull/198). Note that currently these targets require the following:
* `api-level: 30`
* `arch: x86` or `arch: arm64-v8a`
* `channel: canary`
## v2.20.0
* Support non-mobile targets: `android-wear`, `android-wear-cn`, `android-tv` and `google-tv`. - [#180](https://github.com/ReactiveCircus/android-emulator-runner/pull/180) @alexvanyo.
* Update SDK command-line tools to `5.0`. - [#174](https://github.com/ReactiveCircus/android-emulator-runner/pull/174)
* Update build tools to `31.0.0`. - [#174](https://github.com/ReactiveCircus/android-emulator-runner/pull/174)
* Add option to specify the `channel` to download SDK components from: `stable` (default), `beta`, `dev` and `canary`. - [#185](https://github.com/ReactiveCircus/android-emulator-runner/pull/185)
## v2.19.1
* Accept all Android SDK Licenses to fix `sdkmanager` installation. - [#172](https://github.com/ReactiveCircus/android-emulator-runner/pull/172) @marcuspridham.
## v2.19.0
* Add option to specify `ram-size` for the AVD. - [#165](https://github.com/ReactiveCircus/android-emulator-runner/pull/165).
## v2.18.1
* Added support for setting modern emulator build ids for `emulator-build`. Not all build ids are supported until we are able to figure out at which build id the download URL pattern changed. `7425822` (version 30.7.3) is currently the last known working build id.
## v2.18.0
* Add `force-avd-creation` which when set to `false` will skip avd creation if avd with same name exists. This enables AVD snapshot caching which can significantly reduce emulator startup time. See [README.md](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md#usage) for a sample workflow. - [#159](https://github.com/ReactiveCircus/android-emulator-runner/pull/159)
## v2.17.0
* Add option to toggle Linux hardware acceleration - [#154](https://github.com/ReactiveCircus/android-emulator-runner/pull/154) @stevestotter
## v2.16.0
* Avoid wrapping script code in quotes - [#134](https://github.com/ReactiveCircus/android-emulator-runner/pull/134) @hostilefork
* Add option to disable spellcheck - [#143](https://github.com/ReactiveCircus/android-emulator-runner/pull/143) @AfzalivE
* Add support for arm64-v8a for Apple Silicon Macs - [#146](https://github.com/ReactiveCircus/android-emulator-runner/pull/146) @Jeehut
## v2.15.0
* Added support for specifying the number of cores to use for the emulator - [#130](https://github.com/ReactiveCircus/android-emulator-runner/pull/130).
## v2.14.3
* Support `macos-11.0` (Big Sur) runner - [#124](https://github.com/ReactiveCircus/android-emulator-runner/pull/124).
## v2.14.2
* Support API 28 system images with `google_apis` or `google_apis_playstore` target - [#117](https://github.com/ReactiveCircus/android-emulator-runner/pull/117).
## v2.14.1
* Fix hang during AVD creation when `profile` is not specified - [#113](https://github.com/ReactiveCircus/android-emulator-runner/issues/113).
## v2.14.0
* Support specifying SD card path or size via `sdcard-path-or-size`.
* Update npm packages.
* Remove usages of deprecated `$ANDROID_HOME`.
## v2.13.0
* Updated to SDK command-line tools `3.0`.
## v2.12.0
Added support for using the `playstore` system images:
```
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 30
target: playstore
arch: x86
script: ./gradlew connectedCheck
```
## v2.11.1
* Update SDK command-line tools to `2.1`.
* Update @actions/core to `1.2.6`.
## v2.11.0
* Support running multiple instances of the action sequentially in a single job - [#73](https://github.com/ReactiveCircus/android-emulator-runner/issues/73).
## v2.10.0
* Support Android 11 (API 30) system images.
* Bump build tools to `30.0.0`.
## v2.9.0
* Updated to SDK command-line tools `2.0`.
+120 -44
View File
@@ -8,8 +8,8 @@ A GitHub Action for installing, configuring and running hardware-accelerated And
The old ARM-based emulators were slow and are no longer supported by Google. The modern Intel Atom (x86 and x86_64) emulators require hardware acceleration (HAXM on Mac & Windows, QEMU on Linux) from the host to run fast. This presents a challenge on CI as to be able to run hardware accelerated emulators within a docker container, **KVM** must be supported by the host VM which isn't the case for cloud-based CI providers due to infrastructural limits. If you want to learn more about this, here's an article I wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76).
The **masOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android
tests directly on the VM.
The **macOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android
tests directly on the VM. You can also achieve this on a self-hosted Linux runner, but it will need to be on a compatible instance that allows you to enable KVM - for example AWS EC2 Bare Metal instances.
This action automates the process by doing the following:
@@ -31,14 +31,14 @@ jobs:
test:
runs-on: macos-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: checkout
uses: actions/checkout@v2
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
script: ./gradlew connectedCheck
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
script: ./gradlew connectedCheck
```
We can also leverage GitHub Actions's build matrix to test across multiple configurations:
@@ -52,17 +52,17 @@ jobs:
api-level: [21, 23, 29]
target: [default, google_apis]
steps:
- name: checkout
uses: actions/checkout@v2
- name: checkout
uses: actions/checkout@v2
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: x86_64
profile: Nexus 6
script: ./gradlew connectedCheck
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: x86_64
profile: Nexus 6
script: ./gradlew connectedCheck
```
If you need specific versions of **NDK** and **CMake** installed:
@@ -72,40 +72,101 @@ jobs:
test:
runs-on: macos-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: checkout
uses: actions/checkout@v2
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
ndk: 21.0.6113669
cmake: 3.10.2.4988404
script: ./gradlew connectedCheck
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
ndk: 21.0.6113669
cmake: 3.10.2.4988404
script: ./gradlew connectedCheck
```
We can significantly reduce emulator startup time by setting up AVD snapshot caching:
1. add an `actions/cache@v2` step for caching the `avd`
2. add a `reactivecircus/android-emulator-runner@v2` step to generate a clean snapshot - specify `emulator-options` without `no-snapshot`
3. add another `reactivecircus/android-emulator-runner@v2` step to run your tests using existing AVD / snapshot - specify `emulator-options` with `no-snapshot-save`
```
jobs:
test:
runs-on: macos-latest
strategy:
matrix:
api-level: [21, 23, 29]
steps:
- name: checkout
uses: actions/checkout@v2
- name: Gradle cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }}
- name: AVD cache
uses: actions/cache@v2
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ matrix.api-level }}
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: echo "Generated AVD snapshot for caching."
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: ./gradlew connectedCheck
```
## Configurations
| | **Required** | **Default** | **Description** |
|----------------------|--------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Input** | **Required** | **Default** | **Description** |
|-|-|-|-|
| `api-level` | Required | N/A | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. **Minimum API level supported is 15**. |
| `target` | Optional | `default` | Target of the system image - `default` or `google_apis`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. Note that `x86_64` image is only available for API 21+. |
| `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `$ANDROID_HOME/tools/bin/avdmanager list` and refer to the results under "Available Android Virtual Devices". |
| `target` | Optional | `default` | Target of the system image - `default`, `google_apis` or `playstore`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). |
| `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list` and refer to the results under "Available Android Virtual Devices". |
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
| `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` |
| `sdcard-path-or-size` | Optional | N/A | Path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`. |
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
| `force-avd-creation` | Optional | `true` | Whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`. |
| `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
| `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
| `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |
| `disable-linux-hw-accel` | Optional | `true` | Whether to disable hardware acceleration on Linux machines - `true` or `false`. Note that this is true by default as Github-hosted Linux runners do not support hardware acceleration. |
| `emulator-build` | Optional | N/A | Build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0. |
| `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. |
| `ndk` | Optional | N/A | Version of NDK to install - e.g. `21.0.6113669` |
| `cmake` | Optional | N/A | Version of CMake to install - e.g. `3.10.2.4988404` |
| `channel` | Optional | stable | Channel to download the SDK components from - `stable`, `beta`, `dev`, `canary` |
| `script` | Required | N/A | Custom script to run - e.g. to run Android instrumented tests on the emulator: `./gradlew connectedCheck` |
Default `emulator-options`: `-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim`.
## Can I use this action on Linux VMs?
The short answer is yes but it's expected to be a much worse experience (on some newer API levels it might not work at all) than running it on macOS.
The short answer is yes but on Github-hosted Linux runners it's expected to be a much worse experience (on some newer API levels it might not work at all) than running it on macOS. You can get it running much faster on self-hosted Linux runners but only if the underlying instances support KVM (which most don't).
For a longer answer please refer to [this issue](https://github.com/ReactiveCircus/android-emulator-runner/issues/46).
@@ -114,19 +175,34 @@ For a longer answer please refer to [this issue](https://github.com/ReactiveCirc
These are some of the open-source projects using (or used) **Android Emulator Runner**:
- [coil-kt/coil](https://github.com/coil-kt/coil/blob/master/.github/workflows/)
- [cashapp/sqldelight](https://github.com/cashapp/sqldelight/blob/master/.github/workflows/)
- [square/workflow](https://github.com/square/workflow/blob/master/.github/workflows/)
- [square/retrofit](https://github.com/square/retrofit/blob/master/.github/workflows/)
- [coil-kt/coil](https://github.com/coil-kt/coil/blob/master/.github/workflows)
- [cashapp/sqldelight](https://github.com/cashapp/sqldelight/blob/master/.github/workflows)
- [square/workflow-kotlin](https://github.com/square/workflow-kotlin/tree/main/.github/workflows)
- [square/retrofit](https://github.com/square/retrofit/blob/master/.github/workflows)
- [natario1/CameraView](https://github.com/natario1/CameraView/tree/master/.github/workflows)
- [natario1/Transcoder](https://github.com/natario1/Transcoder/tree/master/.github/workflows)
- [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/master/.github/workflows)
- [slackhq/keeper](https://github.com/slackhq/keeper/tree/master/.github/workflows)
- [android/compose-samples](https://github.com/android/compose-samples/blob/master/.github/workflows/ci.yaml)
- [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/master/.github/workflows)
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding)
- [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/)
- [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/main/.github/workflows)
- [slackhq/keeper](https://github.com/slackhq/keeper/tree/main/.github/workflows)
- [android/compose-samples](https://github.com/android/compose-samples/tree/main/.github/workflows)
- [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows)
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding/tree/main/.github/workflows)
- [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows)
- [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/tree/master/.github/workflows)
- [ashishb/adb-enhanced](https://github.com/ashishb/adb-enhanced/tree/master/.github/workflows)
- [vgaidarji/ci-matters](https://github.com/vgaidarji/ci-matters/blob/master/.github/workflows/main.yaml)
- [simpledotorg/simple-android](https://github.com/simpledotorg/simple-android/tree/master/.github/workflows)
- [cashapp/copper](https://github.com/cashapp/copper/blob/trunk/.github/workflows/build.yaml)
- [square/radiography](https://github.com/square/radiography/blob/main/.github/workflows/android.yml)
- [Shopify/android-testify](https://github.com/Shopify/android-testify/blob/master/.github/workflows/sample_build.yml)
- [square/leakcanary](https://github.com/square/leakcanary/tree/main/.github/workflows)
- [hash-checker/hash-checker](https://github.com/hash-checker/hash-checker/tree/master/.github/workflows)
- [hash-checker/hash-checker-lite](https://github.com/hash-checker/hash-checker-lite/tree/master/.github/workflows)
- [Kiwix/kiwix-android](https://github.com/kiwix/kiwix-android/blob/develop/.github/workflows)
- [wikimedia/apps-android-wikipedia](https://github.com/wikimedia/apps-android-wikipedia/blob/master/.github/workflows)
- [google/android-fhir](https://github.com/google/android-fhir/tree/master/.github/workflows)
- [google/accompanist](https://github.com/google/accompanist/blob/main/.github/workflows)
- [dotanuki-labs/norris](https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml)
- [tinylog-org/tinylog](https://github.com/tinylog-org/tinylog/blob/v3.0/.github/workflows/build.yaml)
- [hzi-braunschweig/SORMAS-Project](https://github.com/hzi-braunschweig/SORMAS-Project/blob/development/.github/workflows/sormas_app_ci.yml)
If you are using **Android Emulator Runner** and want your project included in the list, please feel free to create an issue or open a pull request.
+3 -3
View File
@@ -4,7 +4,7 @@ Refer to the [recommendeations for versioning and releasing actions](https://git
## New major release
- From `master` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date.
- From `main` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date.
- Update `CHANGELOG.md`, push change with "Prepare for release X.Y.Z." (where X.Y.Z is the new version).
- Create a new branch e.g. `release/v1`, comment out `node_modules/` in `.gitignore`, commit the change (do not commit yet `node_modules`).
- Run `npm prune --production`.
@@ -17,9 +17,9 @@ Refer to the [recommendeations for versioning and releasing actions](https://git
## New minor / patch release
- From `master` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date.
- From `main` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date.
- Update `CHANGELOG.md`, push change with "Prepare for release X.Y.Z." (where X.Y.Z is the new version).
- Merge from `master` into the release branch e.g. `release/v1`.
- Merge from `main` into the release branch e.g. `release/v1`.
- Run `npm prune --production`.
- Commit merged changes (and the pruned `node_modules`).
- Push to remote.
+17
View File
@@ -0,0 +1,17 @@
import * as mapper from '../src/channel-id-mapper';
describe('channel id mapper tests', () => {
it('Throws if channelName is unknown', () => {
const func = () => {
mapper.getChannelId('unknown-channel');
};
expect(func).toThrowError(`Unexpected channel name: 'unknown-channel'.`);
});
it('Returns expected channelId from channelName', () => {
expect(mapper.getChannelId('stable')).toBe(0);
expect(mapper.getChannelId('beta')).toBe(1);
expect(mapper.getChannelId('dev')).toBe(2);
expect(mapper.getChannelId('canary')).toBe(3);
});
});
+129
View File
@@ -53,6 +53,41 @@ describe('target validator tests', () => {
validator.checkTarget('google_apis');
};
expect(func2).not.toThrow();
const func3 = () => {
validator.checkTarget('aosp_atd');
};
expect(func3).not.toThrow();
const func4 = () => {
validator.checkTarget('google_atd');
};
expect(func4).not.toThrow();
const func5 = () => {
validator.checkTarget('google_apis_playstore');
};
expect(func5).not.toThrow();
const func6 = () => {
validator.checkTarget('android-wear');
};
expect(func6).not.toThrow();
const func7 = () => {
validator.checkTarget('android-wear-cn');
};
expect(func7).not.toThrow();
const func8 = () => {
validator.checkTarget('android-tv');
};
expect(func8).not.toThrow();
const func9 = () => {
validator.checkTarget('google-tv');
};
expect(func9).not.toThrow();
});
});
@@ -77,6 +112,58 @@ describe('arch validator tests', () => {
});
});
describe('channel validator tests', () => {
it('Throws if channel is unknown', () => {
const func = () => {
validator.checkChannel('some-channel');
};
expect(func).toThrowError(`Value for input.channel 'some-channel' is unknown. Supported options: ${validator.VALID_CHANNELS}`);
});
it('Validates successfully with valid channel', () => {
const func1 = () => {
validator.checkChannel('stable');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkChannel('beta');
};
expect(func2).not.toThrow();
const func3 = () => {
validator.checkChannel('dev');
};
expect(func3).not.toThrow();
const func4 = () => {
validator.checkChannel('canary');
};
expect(func4).not.toThrow();
});
});
describe('force-avd-creation validator tests', () => {
it('Throws if force-avd-creation is not a boolean', () => {
const func = () => {
validator.checkForceAvdCreation('yes');
};
expect(func).toThrowError(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
});
it('Validates successfully if force-avd-creation is either true or false', () => {
const func1 = () => {
validator.checkForceAvdCreation('true');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkForceAvdCreation('false');
};
expect(func2).not.toThrow();
});
});
describe('disable-animations validator tests', () => {
it('Throws if disable-animations is not a boolean', () => {
const func = () => {
@@ -98,6 +185,48 @@ describe('disable-animations validator tests', () => {
});
});
describe('disable-spellchecker validator tests', () => {
it('Throws if disable-spellchecker is not a boolean', () => {
const func = () => {
validator.checkDisableSpellchecker('yes');
};
expect(func).toThrowError(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
});
it('Validates successfully if disable-spellchecker is either true or false', () => {
const func1 = () => {
validator.checkDisableSpellchecker('true');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkDisableSpellchecker('false');
};
expect(func2).not.toThrow();
});
});
describe('disable-linux-hw-accel validator tests', () => {
it('Throws if disable-linux-hw-accel is not a boolean', () => {
const func = () => {
validator.checkDisableLinuxHardwareAcceleration('yes');
};
expect(func).toThrowError(`Input for input.disable-linux-hw-accel should be either 'true' or 'false'.`);
});
it('Validates successfully if disable-linux-hw-accel is either true or false', () => {
const func1 = () => {
validator.checkDisableLinuxHardwareAcceleration('true');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkDisableLinuxHardwareAcceleration('false');
};
expect(func2).not.toThrow();
});
});
describe('emulator-build validator tests', () => {
it('Throws if emulator-build is not a number', () => {
const func = () => {
+22 -3
View File
@@ -2,29 +2,45 @@ name: 'Android Emulator Runner'
description: 'Installs, configures and starts an Android Emulator directly on macOS virtual machines.'
author: 'Reactive Circus'
branding:
icon: 'smartphone'
icon: 'smartphone'
color: 'green'
inputs:
api-level:
description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10'
required: true
target:
description: 'target of the system image - default or google_apis'
description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv or google-tv'
default: 'default'
arch:
description: 'CPU architecture of the system image - x86 or x86_64'
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
default: 'x86'
profile:
description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`'
cores:
description: 'the number of cores to use for the emulator'
default: 2
ram-size:
description: 'size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M`'
sdcard-path-or-size:
description: 'path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`'
avd-name:
description: 'custom AVD name used for creating the Android Virtual Device'
default: 'test'
force-avd-creation:
description: 'whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`'
default: 'true'
emulator-options:
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`'
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
disable-animations:
description: 'whether to disable animations - true or false'
default: 'true'
disable-spellchecker:
description: 'whether to disable spellchecker - `true` or `false`'
default: 'false'
disable-linux-hw-accel:
description: 'whether to disable hardware acceleration on Linux machines - `true` or `false`'
default: 'true'
emulator-build:
description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
working-directory:
@@ -33,6 +49,9 @@ inputs:
description: 'version of NDK to install - e.g. 21.0.6113669'
cmake:
description: 'version of CMake to install - e.g. 3.10.2.4988404'
channel:
description: 'Channel to download the SDK components from - `stable`, `beta`, `dev`, `canary`'
default: 'stable'
script:
description: 'custom script to run - e.g. `./gradlew connectedCheck`'
required: true
+21
View File
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getChannelId = void 0;
function getChannelId(channelName) {
if (channelName === 'stable') {
return 0;
}
else if (channelName === 'beta') {
return 1;
}
else if (channelName === 'dev') {
return 2;
}
else if (channelName === 'canary') {
return 3;
}
else {
throw new Error(`Unexpected channel name: '${channelName}'.`);
}
}
exports.getChannelId = getChannelId;
+47 -23
View File
@@ -1,43 +1,64 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.killEmulator = exports.launchEmulator = void 0;
const exec = __importStar(require("@actions/exec"));
const fs = __importStar(require("fs"));
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/**
* Creates and launches a new AVD instance with the specified configurations.
*/
function launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations) {
function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration) {
return __awaiter(this, void 0, void 0, function* () {
// create a new AVD
if (profile.trim() !== '') {
console.log(`Creating AVD with custom profile ${profile}`);
yield exec.exec(`avdmanager create avd --force -n "${avdName}" --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
if (!fs.existsSync(avdPath) || forceAvdCreation) {
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
console.log(`Creating AVD.`);
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`);
}
else {
console.log(`Creating AVD without custom profile.`);
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
if (cores) {
yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
}
if (ramSize) {
yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
}
//turn off hardware acceleration on Linux
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
console.log('Disabling Linux hardware acceleration.');
emulatorOptions += ' -accel off';
}
// start emulator
console.log('Starting emulator.');
// turn off hardware acceleration on Linux
if (process.platform === 'linux') {
emulatorOptions += ' -accel off';
}
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data) => {
if (data.toString().includes('invalid command-line parameter')) {
@@ -56,6 +77,9 @@ function launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOption
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
yield exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
}
if (disableSpellChecker) {
yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
});
}
exports.launchEmulator = launchEmulator;
+32 -3
View File
@@ -1,8 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkEmulatorBuild = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
exports.MIN_API_LEVEL = 15;
exports.VALID_TARGETS = ['default', 'google_apis'];
exports.VALID_ARCHS = ['x86', 'x86_64'];
exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
function checkApiLevel(apiLevel) {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
throw new Error(`Unexpected API level: '${apiLevel}'.`);
@@ -24,15 +26,42 @@ function checkArch(arch) {
}
}
exports.checkArch = checkArch;
function checkChannel(channel) {
if (!exports.VALID_CHANNELS.includes(channel)) {
throw new Error(`Value for input.channel '${channel}' is unknown. Supported options: ${exports.VALID_CHANNELS}.`);
}
}
exports.checkChannel = checkChannel;
function checkForceAvdCreation(forceAvdCreation) {
if (!isValidBoolean(forceAvdCreation)) {
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
}
}
exports.checkForceAvdCreation = checkForceAvdCreation;
function checkDisableAnimations(disableAnimations) {
if (disableAnimations !== 'true' && disableAnimations !== 'false') {
if (!isValidBoolean(disableAnimations)) {
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
}
}
exports.checkDisableAnimations = checkDisableAnimations;
function checkDisableSpellchecker(disableSpellchecker) {
if (!isValidBoolean(disableSpellchecker)) {
throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
}
}
exports.checkDisableSpellchecker = checkDisableSpellchecker;
function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAcceleration) {
if (!isValidBoolean(disableLinuxHardwareAcceleration)) {
throw new Error(`Input for input.disable-linux-hw-accel should be either 'true' or 'false'.`);
}
}
exports.checkDisableLinuxHardwareAcceleration = checkDisableLinuxHardwareAcceleration;
function checkEmulatorBuild(emulatorBuild) {
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
}
}
exports.checkEmulatorBuild = checkEmulatorBuild;
function isValidBoolean(value) {
return value === 'true' || value === 'false';
}
+60 -14
View File
@@ -1,19 +1,32 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const sdk_installer_1 = require("./sdk-installer");
@@ -21,6 +34,7 @@ const input_validator_1 = require("./input-validator");
const emulator_manager_1 = require("./emulator-manager");
const exec = __importStar(require("@actions/exec"));
const script_parser_1 = require("./script-parser");
const channel_id_mapper_1 = require("./channel-id-mapper");
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
@@ -39,7 +53,8 @@ function run() {
const apiLevel = Number(apiLevelInput);
console.log(`API level: ${apiLevel}`);
// target of the system image
const target = core.getInput('target');
const targetInput = core.getInput('target');
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
input_validator_1.checkTarget(target);
console.log(`target: ${target}`);
// CPU architecture of the system image
@@ -49,9 +64,23 @@ function run() {
// Hardware profile used for creating the AVD
const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`);
// Number of cores to use for emulator
const cores = core.getInput('cores');
console.log(`Cores: ${cores}`);
// RAM to use for AVD
const ramSize = core.getInput('ram-size');
console.log(`RAM size: ${ramSize}`);
// SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`);
// custom name used for creating the AVD
const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`);
// force AVD creation
const forceAvdCreationInput = core.getInput('force-avd-creation');
input_validator_1.checkForceAvdCreation(forceAvdCreationInput);
const forceAvdCreation = forceAvdCreationInput === 'true';
console.log(`force avd creation: ${forceAvdCreation}`);
// emulator options
const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`);
@@ -60,6 +89,16 @@ function run() {
input_validator_1.checkDisableAnimations(disableAnimationsInput);
const disableAnimations = disableAnimationsInput === 'true';
console.log(`disable animations: ${disableAnimations}`);
// disable spellchecker
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
input_validator_1.checkDisableSpellchecker(disableSpellcheckerInput);
const disableSpellchecker = disableSpellcheckerInput === 'true';
console.log(`disable spellchecker: ${disableSpellchecker}`);
// disable linux hardware acceleration
const disableLinuxHardwareAccelerationInput = core.getInput('disable-linux-hw-accel');
input_validator_1.checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAccelerationInput);
const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
// emulator build
const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) {
@@ -85,6 +124,11 @@ function run() {
console.log(`version of CMake to install: ${cmakeInput}`);
}
const cmakeVersion = !cmakeInput ? undefined : cmakeInput;
// channelId (up to and including) to download the SDK packages from
const channelName = core.getInput('channel');
input_validator_1.checkChannel(channelName);
const channelId = channel_id_mapper_1.getChannelId(channelName);
console.log(`Channel: ${channelId} (${channelName})`);
// custom script to run
const scriptInput = core.getInput('script', { required: true });
const scripts = script_parser_1.parseScript(scriptInput);
@@ -93,9 +137,9 @@ function run() {
console.log(`${script}`);
}));
// install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations);
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration);
// execute the custom script
try {
// move to custom working directory if set
@@ -103,7 +147,9 @@ function run() {
process.chdir(workingDirectory);
}
for (const script of scripts) {
yield exec.exec(`sh -c \\"${script}"`);
// use array form to avoid various quote escaping problems
// caused by exec(`sh -c "${script}"`)
yield exec.exec('sh', ['-c', script]);
}
}
catch (error) {
+1
View File
@@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseScript = void 0;
/**
* Convert a (potentially multi-line) script to an array of single-line script(s).
*/
+56 -36
View File
@@ -1,67 +1,87 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.installAndroidSdk = void 0;
const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec"));
const BUILD_TOOLS_VERSION = '29.0.3';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6514223_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6514223_latest.zip';
const io = __importStar(require("@actions/io"));
const tc = __importStar(require("@actions/tool-cache"));
const fs = __importStar(require("fs"));
const BUILD_TOOLS_VERSION = '31.0.0';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-7583922_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip';
/**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target.
*/
function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion) {
function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
return __awaiter(this, void 0, void 0, function* () {
const isOnMac = process.platform === 'darwin';
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
yield exec.exec(`sudo mkdir ${process.env.ANDROID_HOME}/cmdline-tools`);
yield exec.exec(`curl -fo commandlinetools.zip ${sdkUrl}`);
yield exec.exec(`sudo unzip -q commandlinetools.zip -d ${process.env.ANDROID_HOME}/cmdline-tools`);
yield exec.exec(`sudo rm -f commandlinetools.zip`);
// add paths for commandline-tools and platform-tools
core.addPath(`${process.env.ANDROID_HOME}/cmdline-tools/tools:${process.env.ANDROID_HOME}/cmdline-tools/tools/bin:${process.env.ANDROID_HOME}/platform-tools`);
// additional permission and license requirements for Linux
if (!isOnMac) {
yield exec.exec(`sh -c \\"sudo chmod -R 777 ${process.env.ANDROID_HOME}"`);
yield exec.exec(`sh -c \\"echo -e '\n84831b9409646a918e30573bab4c9c91346d8abd' > ${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license"`);
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`);
}
const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
const downloadPath = yield tc.downloadTool(sdkUrl);
yield tc.extractZip(downloadPath, cmdlineToolsPath);
yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
}
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
// set standard AVD path
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
// accept all Android SDK licenses
yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
console.log('Installing latest build tools, platform tools, and platform.');
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
console.log('Installing latest emulator.');
yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`);
yield exec.exec(`sudo rm -rf ${process.env.ANDROID_HOME}/emulator`);
yield exec.exec(`sudo unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
yield exec.exec(`sudo rm -f emulator.zip`);
}
else {
console.log('Installing latest emulator.');
yield exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
// TODO find out the correct download URLs for all build ids
const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`;
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
yield exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`);
yield io.rmRF('emulator.zip');
}
console.log('Installing system images.');
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
}
if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
}
});
}
+8 -6
View File
@@ -1,7 +1,9 @@
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
+147 -81
View File
@@ -1,81 +1,147 @@
# `@actions/core`
> Core functions for setting results, logging, registering secrets and exporting variables across actions
## Usage
#### Inputs/Outputs
You can use this library to get inputs or set outputs:
```
const core = require('@actions/core');
const myInput = core.getInput('inputName', { required: true });
// Do stuff
core.setOutput('outputKey', 'outputVal');
```
#### Exporting variables/secrets
You can also export variables and secrets for future steps. Variables get set in the environment automatically, while secrets must be scoped into the environment from a workflow using `{{ secret.FOO }}`. Secrets will also be masked from the logs:
```
const core = require('@actions/core');
// Do stuff
core.exportVariable('envVar', 'Val');
core.exportSecret('secretVar', variableWithSecretValue);
```
#### PATH Manipulation
You can explicitly add items to the path for all remaining steps in a workflow:
```
const core = require('@actions/core');
core.addPath('pathToTool');
```
#### Exit codes
You should use this library to set the failing exit code for your action:
```
const core = require('@actions/core');
try {
// Do stuff
}
catch (err) {
// setFailed logs the message and sets a failing exit code
core.setFailed(`Action failed with error ${err}`);
}
```
#### Logging
Finally, this library provides some utilities for logging:
```
const core = require('@actions/core');
const myInput = core.getInput('input');
try {
core.debug('Inside try block');
if (!myInput) {
core.warning('myInput wasnt set');
}
// Do stuff
}
catch (err) {
core.error('Error ${err}, action may still succeed though');
}
```
# `@actions/core`
> Core functions for setting results, logging, registering secrets and exporting variables across actions
## Usage
### Import the package
```js
// javascript
const core = require('@actions/core');
// typescript
import * as core from '@actions/core';
```
#### Inputs/Outputs
Action inputs can be read with `getInput`. Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
```js
const myInput = core.getInput('inputName', { required: true });
core.setOutput('outputKey', 'outputVal');
```
#### Exporting variables
Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks.
```js
core.exportVariable('envVar', 'Val');
```
#### Setting a secret
Setting a secret registers the secret with the runner to ensure it is masked in logs.
```js
core.setSecret('myPassword');
```
#### PATH Manipulation
To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH.
```js
core.addPath('/path/to/mytool');
```
#### Exit codes
You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success.
```js
const core = require('@actions/core');
try {
// Do stuff
}
catch (err) {
// setFailed logs the message and sets a failing exit code
core.setFailed(`Action failed with error ${err}`);
}
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
```
#### Logging
Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs).
```js
const core = require('@actions/core');
const myInput = core.getInput('input');
try {
core.debug('Inside try block');
if (!myInput) {
core.warning('myInput was not set');
}
if (core.isDebug()) {
// curl -v https://github.com
} else {
// curl https://github.com
}
// Do stuff
core.info('Output to the actions build log')
}
catch (err) {
core.error(`Error ${err}, action may still succeed though`);
}
```
This library can also wrap chunks of output in foldable groups.
```js
const core = require('@actions/core')
// Manually wrap output
core.startGroup('Do some function')
doSomeFunction()
core.endGroup()
// Wrap an asynchronous function call
const result = await core.group('Do something async', async () => {
const response = await doSomeHTTPRequest()
return response
})
```
#### Action state
You can use this library to save state and get state for sharing information between a given wrapper action:
**action.yml**
```yaml
name: 'Wrapper action sample'
inputs:
name:
default: 'GitHub'
runs:
using: 'node12'
main: 'main.js'
post: 'cleanup.js'
```
In action's `main.js`:
```js
const core = require('@actions/core');
core.saveState("pidToKill", 12345);
```
In action's `cleanup.js`:
```js
const core = require('@actions/core');
var pid = core.getState("pidToKill");
process.kill(pid);
```
+16 -16
View File
@@ -1,16 +1,16 @@
interface CommandProperties {
[key: string]: string;
}
/**
* Commands
*
* Command Format:
* ##[name key=value;key=value]message
*
* Examples:
* ##[warning]This is the user warning message
* ##[set-secret name=mypassword]definatelyNotAPassword!
*/
export declare function issueCommand(command: string, properties: CommandProperties, message: string): void;
export declare function issue(name: string, message: string): void;
export {};
interface CommandProperties {
[key: string]: any;
}
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* Examples:
* ::warning::This is the message
* ::set-env name=MY_VAR::some value
*/
export declare function issueCommand(command: string, properties: CommandProperties, message: any): void;
export declare function issue(name: string, message?: string): void;
export {};
+78 -65
View File
@@ -1,66 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const os = require("os");
/**
* Commands
*
* Command Format:
* ##[name key=value;key=value]message
*
* Examples:
* ##[warning]This is the user warning message
* ##[set-secret name=mypassword]definatelyNotAPassword!
*/
function issueCommand(command, properties, message) {
const cmd = new Command(command, properties, message);
process.stdout.write(cmd.toString() + os.EOL);
}
exports.issueCommand = issueCommand;
function issue(name, message) {
issueCommand(name, {}, message);
}
exports.issue = issue;
const CMD_PREFIX = '##[';
class Command {
constructor(command, properties, message) {
if (!command) {
command = 'missing.command';
}
this.command = command;
this.properties = properties;
this.message = message;
}
toString() {
let cmdStr = CMD_PREFIX + this.command;
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' ';
for (const key in this.properties) {
if (this.properties.hasOwnProperty(key)) {
const val = this.properties[key];
if (val) {
// safely append the val - avoid blowing up when attempting to
// call .replace() if message is not a string for some reason
cmdStr += `${key}=${escape(`${val || ''}`)};`;
}
}
}
}
cmdStr += ']';
// safely append the message - avoid blowing up when attempting to
// call .replace() if message is not a string for some reason
const message = `${this.message || ''}`;
cmdStr += escapeData(message);
return cmdStr;
}
}
function escapeData(s) {
return s.replace(/\r/g, '%0D').replace(/\n/g, '%0A');
}
function escape(s) {
return s
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/]/g, '%5D')
.replace(/;/g, '%3B');
}
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const os = __importStar(require("os"));
const utils_1 = require("./utils");
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* Examples:
* ::warning::This is the message
* ::set-env name=MY_VAR::some value
*/
function issueCommand(command, properties, message) {
const cmd = new Command(command, properties, message);
process.stdout.write(cmd.toString() + os.EOL);
}
exports.issueCommand = issueCommand;
function issue(name, message = '') {
issueCommand(name, {}, message);
}
exports.issue = issue;
const CMD_STRING = '::';
class Command {
constructor(command, properties, message) {
if (!command) {
command = 'missing.command';
}
this.command = command;
this.properties = properties;
this.message = message;
}
toString() {
let cmdStr = CMD_STRING + this.command;
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' ';
let first = true;
for (const key in this.properties) {
if (this.properties.hasOwnProperty(key)) {
const val = this.properties[key];
if (val) {
if (first) {
first = false;
}
else {
cmdStr += ',';
}
cmdStr += `${key}=${escapeProperty(val)}`;
}
}
}
}
cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
return cmdStr;
}
}
function escapeData(s) {
return utils_1.toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A');
}
function escapeProperty(s) {
return utils_1.toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/:/g, '%3A')
.replace(/,/g, '%2C');
}
//# sourceMappingURL=command.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":";;AAAA,yBAAwB;AAQxB;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,UAA6B,EAC7B,OAAe;IAEf,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAC/C,CAAC;AAPD,oCAOC;AAED,SAAgB,KAAK,CAAC,IAAY,EAAE,OAAe;IACjD,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACjC,CAAC;AAFD,sBAEC;AAED,MAAM,UAAU,GAAG,KAAK,CAAA;AAExB,MAAM,OAAO;IAKX,YAAY,OAAe,EAAE,UAA6B,EAAE,OAAe;QACzE,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,GAAG,iBAAiB,CAAA;SAC5B;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,QAAQ;QACN,IAAI,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,OAAO,CAAA;QAEtC,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9D,MAAM,IAAI,GAAG,CAAA;YACb,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE;gBACjC,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;oBACvC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;oBAChC,IAAI,GAAG,EAAE;wBACP,8DAA8D;wBAC9D,6DAA6D;wBAC7D,MAAM,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,IAAI,EAAE,EAAE,CAAC,GAAG,CAAA;qBAC9C;iBACF;aACF;SACF;QAED,MAAM,IAAI,GAAG,CAAA;QAEb,kEAAkE;QAClE,6DAA6D;QAC7D,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAA;QACvC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;QAE7B,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AACtD,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,CAAC;SACL,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACzB,CAAC"}
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":";;;;;;;;;AAAA,uCAAwB;AACxB,mCAAsC;AAWtC;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,UAA6B,EAC7B,OAAY;IAEZ,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAC/C,CAAC;AAPD,oCAOC;AAED,SAAgB,KAAK,CAAC,IAAY,EAAE,UAAkB,EAAE;IACtD,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACjC,CAAC;AAFD,sBAEC;AAED,MAAM,UAAU,GAAG,IAAI,CAAA;AAEvB,MAAM,OAAO;IAKX,YAAY,OAAe,EAAE,UAA6B,EAAE,OAAe;QACzE,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,GAAG,iBAAiB,CAAA;SAC5B;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,QAAQ;QACN,IAAI,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,OAAO,CAAA;QAEtC,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9D,MAAM,IAAI,GAAG,CAAA;YACb,IAAI,KAAK,GAAG,IAAI,CAAA;YAChB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE;gBACjC,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;oBACvC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;oBAChC,IAAI,GAAG,EAAE;wBACP,IAAI,KAAK,EAAE;4BACT,KAAK,GAAG,KAAK,CAAA;yBACd;6BAAM;4BACL,MAAM,IAAI,GAAG,CAAA;yBACd;wBAED,MAAM,IAAI,GAAG,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAA;qBAC1C;iBACF;aACF;SACF;QAED,MAAM,IAAI,GAAG,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAA;QACpD,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED,SAAS,UAAU,CAAC,CAAM;IACxB,OAAO,sBAAc,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,SAAS,cAAc,CAAC,CAAM;IAC5B,OAAO,sBAAc,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACzB,CAAC"}
+122 -73
View File
@@ -1,73 +1,122 @@
/**
* Interface for getInput options
*/
export interface InputOptions {
/** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */
required?: boolean;
}
/**
* The code to exit an action
*/
export declare enum ExitCode {
/**
* A code indicating that the action was successful
*/
Success = 0,
/**
* A code indicating that the action was a failure
*/
Failure = 1
}
/**
* sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable
*/
export declare function exportVariable(name: string, val: string): void;
/**
* exports the variable and registers a secret which will get masked from logs
* @param name the name of the variable to set
* @param val value of the secret
*/
export declare function exportSecret(name: string, val: string): void;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
export declare function addPath(inputPath: string): void;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
export declare function getInput(name: string, options?: InputOptions): string;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store
*/
export declare function setOutput(name: string, value: string): void;
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
export declare function setFailed(message: string): void;
/**
* Writes debug message to user log
* @param message debug message
*/
export declare function debug(message: string): void;
/**
* Adds an error issue
* @param message error issue message
*/
export declare function error(message: string): void;
/**
* Adds an warning issue
* @param message warning issue message
*/
export declare function warning(message: string): void;
/**
* Interface for getInput options
*/
export interface InputOptions {
/** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */
required?: boolean;
}
/**
* The code to exit an action
*/
export declare enum ExitCode {
/**
* A code indicating that the action was successful
*/
Success = 0,
/**
* A code indicating that the action was a failure
*/
Failure = 1
}
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
*/
export declare function exportVariable(name: string, val: any): void;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
export declare function setSecret(secret: string): void;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
export declare function addPath(inputPath: string): void;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
export declare function getInput(name: string, options?: InputOptions): string;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
export declare function setOutput(name: string, value: any): void;
/**
* Enables or disables the echoing of commands into stdout for the rest of the step.
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
*
*/
export declare function setCommandEcho(enabled: boolean): void;
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
export declare function setFailed(message: string | Error): void;
/**
* Gets whether Actions Step Debug is on or not
*/
export declare function isDebug(): boolean;
/**
* Writes debug message to user log
* @param message debug message
*/
export declare function debug(message: string): void;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
*/
export declare function error(message: string | Error): void;
/**
* Adds an warning issue
* @param message warning issue message. Errors will be converted to string via toString()
*/
export declare function warning(message: string | Error): void;
/**
* Writes info to log with console.log.
* @param message info message
*/
export declare function info(message: string): void;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
export declare function startGroup(name: string): void;
/**
* End an output group.
*/
export declare function endGroup(): void;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
export declare function group<T>(name: string, fn: () => Promise<T>): Promise<T>;
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
export declare function saveState(name: string, value: any): void;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
export declare function getState(name: string): string;
+237 -115
View File
@@ -1,116 +1,238 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("./command");
const path = require("path");
/**
* The code to exit an action
*/
var ExitCode;
(function (ExitCode) {
/**
* A code indicating that the action was successful
*/
ExitCode[ExitCode["Success"] = 0] = "Success";
/**
* A code indicating that the action was a failure
*/
ExitCode[ExitCode["Failure"] = 1] = "Failure";
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
/**
* sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable
*/
function exportVariable(name, val) {
process.env[name] = val;
command_1.issueCommand('set-env', { name }, val);
}
exports.exportVariable = exportVariable;
/**
* exports the variable and registers a secret which will get masked from logs
* @param name the name of the variable to set
* @param val value of the secret
*/
function exportSecret(name, val) {
exportVariable(name, val);
command_1.issueCommand('set-secret', {}, val);
}
exports.exportSecret = exportSecret;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
function addPath(inputPath) {
command_1.issueCommand('add-path', {}, inputPath);
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
}
exports.addPath = addPath;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
function getInput(name, options) {
const val = process.env[`INPUT_${name.replace(' ', '_').toUpperCase()}`] || '';
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}
exports.getInput = getInput;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store
*/
function setOutput(name, value) {
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
//-----------------------------------------------------------------------
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
function setFailed(message) {
process.exitCode = ExitCode.Failure;
error(message);
}
exports.setFailed = setFailed;
//-----------------------------------------------------------------------
// Logging Commands
//-----------------------------------------------------------------------
/**
* Writes debug message to user log
* @param message debug message
*/
function debug(message) {
command_1.issueCommand('debug', {}, message);
}
exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message
*/
function error(message) {
command_1.issue('error', message);
}
exports.error = error;
/**
* Adds an warning issue
* @param message warning issue message
*/
function warning(message) {
command_1.issue('warning', message);
}
exports.warning = warning;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("./command");
const file_command_1 = require("./file-command");
const utils_1 = require("./utils");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
/**
* The code to exit an action
*/
var ExitCode;
(function (ExitCode) {
/**
* A code indicating that the action was successful
*/
ExitCode[ExitCode["Success"] = 0] = "Success";
/**
* A code indicating that the action was a failure
*/
ExitCode[ExitCode["Failure"] = 1] = "Failure";
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function exportVariable(name, val) {
const convertedVal = utils_1.toCommandValue(val);
process.env[name] = convertedVal;
const filePath = process.env['GITHUB_ENV'] || '';
if (filePath) {
const delimiter = '_GitHubActionsFileCommandDelimeter_';
const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`;
file_command_1.issueCommand('ENV', commandValue);
}
else {
command_1.issueCommand('set-env', { name }, convertedVal);
}
}
exports.exportVariable = exportVariable;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
function setSecret(secret) {
command_1.issueCommand('add-mask', {}, secret);
}
exports.setSecret = setSecret;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
function addPath(inputPath) {
const filePath = process.env['GITHUB_PATH'] || '';
if (filePath) {
file_command_1.issueCommand('PATH', inputPath);
}
else {
command_1.issueCommand('add-path', {}, inputPath);
}
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
}
exports.addPath = addPath;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
function getInput(name, options) {
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}
exports.getInput = getInput;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setOutput(name, value) {
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
/**
* Enables or disables the echoing of commands into stdout for the rest of the step.
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
*
*/
function setCommandEcho(enabled) {
command_1.issue('echo', enabled ? 'on' : 'off');
}
exports.setCommandEcho = setCommandEcho;
//-----------------------------------------------------------------------
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
function setFailed(message) {
process.exitCode = ExitCode.Failure;
error(message);
}
exports.setFailed = setFailed;
//-----------------------------------------------------------------------
// Logging Commands
//-----------------------------------------------------------------------
/**
* Gets whether Actions Step Debug is on or not
*/
function isDebug() {
return process.env['RUNNER_DEBUG'] === '1';
}
exports.isDebug = isDebug;
/**
* Writes debug message to user log
* @param message debug message
*/
function debug(message) {
command_1.issueCommand('debug', {}, message);
}
exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
*/
function error(message) {
command_1.issue('error', message instanceof Error ? message.toString() : message);
}
exports.error = error;
/**
* Adds an warning issue
* @param message warning issue message. Errors will be converted to string via toString()
*/
function warning(message) {
command_1.issue('warning', message instanceof Error ? message.toString() : message);
}
exports.warning = warning;
/**
* Writes info to log with console.log.
* @param message info message
*/
function info(message) {
process.stdout.write(message + os.EOL);
}
exports.info = info;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
function startGroup(name) {
command_1.issue('group', name);
}
exports.startGroup = startGroup;
/**
* End an output group.
*/
function endGroup() {
command_1.issue('endgroup');
}
exports.endGroup = endGroup;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
function group(name, fn) {
return __awaiter(this, void 0, void 0, function* () {
startGroup(name);
let result;
try {
result = yield fn();
}
finally {
endGroup();
}
return result;
});
}
exports.group = group;
//-----------------------------------------------------------------------
// Wrapper action state
//-----------------------------------------------------------------------
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function saveState(name, value) {
command_1.issueCommand('save-state', { name }, value);
}
exports.saveState = saveState;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
function getState(name) {
return process.env[`STATE_${name}`] || '';
}
exports.getState = getState;
//# sourceMappingURL=core.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;AAAA,uCAA6C;AAE7C,6BAA4B;AAU5B;;GAEG;AACH,IAAY,QAUX;AAVD,WAAY,QAAQ;IAClB;;OAEG;IACH,6CAAW,CAAA;IAEX;;OAEG;IACH,6CAAW,CAAA;AACb,CAAC,EAVW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAUnB;AAED,yEAAyE;AACzE,YAAY;AACZ,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,cAAc,CAAC,IAAY,EAAE,GAAW;IACtD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAA;IACvB,sBAAY,CAAC,SAAS,EAAE,EAAC,IAAI,EAAC,EAAE,GAAG,CAAC,CAAA;AACtC,CAAC;AAHD,wCAGC;AAED;;;;GAIG;AACH,SAAgB,YAAY,CAAC,IAAY,EAAE,GAAW;IACpD,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACzB,sBAAY,CAAC,YAAY,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;AACrC,CAAC;AAHD,oCAGC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,SAAiB;IACvC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;IACvC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;AAC7E,CAAC;AAHD,0BAGC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CAAC,IAAY,EAAE,OAAsB;IAC3D,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACpE,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAA;KAC5D;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AARD,4BAQC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED,yEAAyE;AACzE,UAAU;AACV,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,SAAS,CAAC,OAAe;IACvC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAA;IACnC,KAAK,CAAC,OAAO,CAAC,CAAA;AAChB,CAAC;AAHD,8BAGC;AAED,yEAAyE;AACzE,mBAAmB;AACnB,yEAAyE;AAEzE;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,sBAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACpC,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,eAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AACzB,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,OAAe;IACrC,eAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;AAC3B,CAAC;AAFD,0BAEC"}
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,uCAA6C;AAC7C,iDAA+D;AAC/D,mCAAsC;AAEtC,uCAAwB;AACxB,2CAA4B;AAU5B;;GAEG;AACH,IAAY,QAUX;AAVD,WAAY,QAAQ;IAClB;;OAEG;IACH,6CAAW,CAAA;IAEX;;OAEG;IACH,6CAAW,CAAA;AACb,CAAC,EAVW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAUnB;AAED,yEAAyE;AACzE,YAAY;AACZ,yEAAyE;AAEzE;;;;GAIG;AACH,8DAA8D;AAC9D,SAAgB,cAAc,CAAC,IAAY,EAAE,GAAQ;IACnD,MAAM,YAAY,GAAG,sBAAc,CAAC,GAAG,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,YAAY,CAAA;IAEhC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;IAChD,IAAI,QAAQ,EAAE;QACZ,MAAM,SAAS,GAAG,qCAAqC,CAAA;QACvD,MAAM,YAAY,GAAG,GAAG,IAAI,KAAK,SAAS,GAAG,EAAE,CAAC,GAAG,GAAG,YAAY,GAAG,EAAE,CAAC,GAAG,GAAG,SAAS,EAAE,CAAA;QACzF,2BAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;KACtC;SAAM;QACL,sBAAY,CAAC,SAAS,EAAE,EAAC,IAAI,EAAC,EAAE,YAAY,CAAC,CAAA;KAC9C;AACH,CAAC;AAZD,wCAYC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,MAAc;IACtC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;AACtC,CAAC;AAFD,8BAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,SAAiB;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAA;IACjD,IAAI,QAAQ,EAAE;QACZ,2BAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;KACpC;SAAM;QACL,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;KACxC;IACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;AAC7E,CAAC;AARD,0BAQC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CAAC,IAAY,EAAE,OAAsB;IAC3D,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACrE,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAA;KAC5D;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AARD,4BAQC;AAED;;;;;GAKG;AACH,8DAA8D;AAC9D,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAU;IAChD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAgB;IAC7C,eAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AACvC,CAAC;AAFD,wCAEC;AAED,yEAAyE;AACzE,UAAU;AACV,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,SAAS,CAAC,OAAuB;IAC/C,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAA;IAEnC,KAAK,CAAC,OAAO,CAAC,CAAA;AAChB,CAAC;AAJD,8BAIC;AAED,yEAAyE;AACzE,mBAAmB;AACnB,yEAAyE;AAEzE;;GAEG;AACH,SAAgB,OAAO;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,GAAG,CAAA;AAC5C,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,sBAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACpC,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAuB;IAC3C,eAAK,CAAC,OAAO,EAAE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AACzE,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,OAAuB;IAC7C,eAAK,CAAC,SAAS,EAAE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAC3E,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACxC,CAAC;AAFD,oBAEC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,eAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACtB,CAAC;AAFD,gCAEC;AAED;;GAEG;AACH,SAAgB,QAAQ;IACtB,eAAK,CAAC,UAAU,CAAC,CAAA;AACnB,CAAC;AAFD,4BAEC;AAED;;;;;;;GAOG;AACH,SAAsB,KAAK,CAAI,IAAY,EAAE,EAAoB;;QAC/D,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhB,IAAI,MAAS,CAAA;QAEb,IAAI;YACF,MAAM,GAAG,MAAM,EAAE,EAAE,CAAA;SACpB;gBAAS;YACR,QAAQ,EAAE,CAAA;SACX;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CAAA;AAZD,sBAYC;AAED,yEAAyE;AACzE,uBAAuB;AACvB,yEAAyE;AAEzE;;;;;GAKG;AACH,8DAA8D;AAC9D,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAU;IAChD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED;;;;;GAKG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;AAC3C,CAAC;AAFD,4BAEC"}
+1
View File
@@ -0,0 +1 @@
export declare function issueCommand(command: string, message: any): void;
+29
View File
@@ -0,0 +1,29 @@
"use strict";
// For internal use, subject to change.
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
// We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const utils_1 = require("./utils");
function issueCommand(command, message) {
const filePath = process.env[`GITHUB_${command}`];
if (!filePath) {
throw new Error(`Unable to find environment variable for file command ${command}`);
}
if (!fs.existsSync(filePath)) {
throw new Error(`Missing file at path: ${filePath}`);
}
fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, {
encoding: 'utf8'
});
}
exports.issueCommand = issueCommand;
//# sourceMappingURL=file-command.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"file-command.js","sourceRoot":"","sources":["../src/file-command.ts"],"names":[],"mappings":";AAAA,uCAAuC;;;;;;;;;AAEvC,mCAAmC;AACnC,uDAAuD;AAEvD,uCAAwB;AACxB,uCAAwB;AACxB,mCAAsC;AAEtC,SAAgB,YAAY,CAAC,OAAe,EAAE,OAAY;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAA;IACjD,IAAI,CAAC,QAAQ,EAAE;QACb,MAAM,IAAI,KAAK,CACb,wDAAwD,OAAO,EAAE,CAClE,CAAA;KACF;IACD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAA;KACrD;IAED,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,sBAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE;QACjE,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAA;AACJ,CAAC;AAdD,oCAcC"}
+5
View File
@@ -0,0 +1,5 @@
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
*/
export declare function toCommandValue(input: any): string;
+19
View File
@@ -0,0 +1,19 @@
"use strict";
// We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
*/
function toCommandValue(input) {
if (input === null || input === undefined) {
return '';
}
else if (typeof input === 'string' || input instanceof String) {
return input;
}
return JSON.stringify(input);
}
exports.toCommandValue = toCommandValue;
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA,mCAAmC;AACnC,uDAAuD;;AAEvD;;;GAGG;AACH,SAAgB,cAAc,CAAC,KAAU;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE;QACzC,OAAO,EAAE,CAAA;KACV;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,MAAM,EAAE;QAC/D,OAAO,KAAe,CAAA;KACvB;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC;AAPD,wCAOC"}
+24 -19
View File
@@ -1,33 +1,34 @@
{
"_args": [
[
"@actions/core@1.0.0",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
"@actions/core@1.2.6",
"."
]
],
"_from": "@actions/core@1.0.0",
"_id": "@actions/core@1.0.0",
"_from": "@actions/core@1.2.6",
"_id": "@actions/core@1.2.6",
"_inBundle": false,
"_integrity": "sha512-aMIlkx96XH4E/2YZtEOeyrYQfhlas9jIRkfGPqMwXD095Rdkzo4lB6ZmbxPQSzD+e1M+Xsm98ZhuSMYGv/AlqA==",
"_integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==",
"_location": "/@actions/core",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/core@1.0.0",
"raw": "@actions/core@1.2.6",
"name": "@actions/core",
"escapedName": "@actions%2fcore",
"scope": "@actions",
"rawSpec": "1.0.0",
"rawSpec": "1.2.6",
"saveSpec": null,
"fetchSpec": "1.0.0"
"fetchSpec": "1.2.6"
},
"_requiredBy": [
"/"
"/",
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/core/-/core-1.0.0.tgz",
"_spec": "1.0.0",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"_resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
"_spec": "1.2.6",
"_where": ".",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
@@ -40,13 +41,14 @@
"test": "__tests__"
},
"files": [
"lib"
"lib",
"!.DS_Store"
],
"gitHead": "a40bce7c8d382aa3dbadaa327acbc696e9390e55",
"homepage": "https://github.com/actions/toolkit/tree/master/packages/core",
"homepage": "https://github.com/actions/toolkit/tree/main/packages/core",
"keywords": [
"core",
"actions"
"github",
"actions",
"core"
],
"license": "MIT",
"main": "lib/core.js",
@@ -56,11 +58,14 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/core"
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"version": "1.0.0"
"types": "lib/core.d.ts",
"version": "1.2.6"
}
+2
View File
@@ -1,3 +1,5 @@
The MIT License (MIT)
Copyright 2019 GitHub
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:
+3 -6
View File
@@ -4,7 +4,7 @@
#### Basic
You can use this package to execute your tools on the command line in a cross platform way:
You can use this package to execute tools in a cross platform way:
```js
const exec = require('@actions/exec');
@@ -48,13 +48,10 @@ await exec.exec('node', ['index.js', 'foo=bar'], options);
#### Exec tools not in the PATH
You can use it in conjunction with the `which` function from `@actions/io` to execute tools that are not in the PATH:
You can specify the full path for tools not in the PATH:
```js
const exec = require('@actions/exec');
const io = require('@actions/io');
const pythonPath: string = await io.which('python', true)
await exec.exec(`"${pythonPath}"`, ['main.py']);
await exec.exec('"/path/to/my-tool"', ['arg1']);
```
+3 -2
View File
@@ -1,4 +1,5 @@
import * as im from './interfaces';
import { ExecOptions } from './interfaces';
export { ExecOptions };
/**
* Exec a command.
* Output will be streamed to the live console.
@@ -9,4 +10,4 @@ import * as im from './interfaces';
* @param options optional exec options. See ExecOptions
* @returns Promise<number> exit code
*/
export declare function exec(commandLine: string, args?: string[], options?: im.ExecOptions): Promise<number>;
export declare function exec(commandLine: string, args?: string[], options?: ExecOptions): Promise<number>;
+8 -1
View File
@@ -8,8 +8,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const tr = require("./toolrunner");
const tr = __importStar(require("./toolrunner"));
/**
* Exec a command.
* Output will be streamed to the live console.
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":";;;;;;;;;;;AACA,mCAAkC;AAElC;;;;;;;;;GASG;AACH,SAAsB,IAAI,CACxB,WAAmB,EACnB,IAAe,EACf,OAAwB;;QAExB,MAAM,WAAW,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;QACpD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;SACpE;QACD,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;QAC/B,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAkB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;QACxE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;CAAA;AAdD,oBAcC"}
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AACA,iDAAkC;AAIlC;;;;;;;;;GASG;AACH,SAAsB,IAAI,CACxB,WAAmB,EACnB,IAAe,EACf,OAAqB;;QAErB,MAAM,WAAW,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;QACpD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;SACpE;QACD,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;QAC/B,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAkB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;QACxE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;CAAA;AAdD,oBAcC"}
+2
View File
@@ -24,6 +24,8 @@ export interface ExecOptions {
ignoreReturnCode?: boolean;
/** optional. How long in ms to wait for STDIO streams to close after the exit event of the process before terminating. defaults to 10000 */
delay?: number;
/** optional. input to write to the process on STDIN. */
input?: Buffer;
/** optional. Listeners for output. Callback functions that will be called on these events */
listeners?: {
stdout?: (data: Buffer) => void;
+29 -3
View File
@@ -8,10 +8,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const os = require("os");
const events = require("events");
const child = require("child_process");
const os = __importStar(require("os"));
const events = __importStar(require("events"));
const child = __importStar(require("child_process"));
const path = __importStar(require("path"));
const io = __importStar(require("@actions/io"));
const ioUtil = __importStar(require("@actions/io/lib/io-util"));
/* eslint-disable @typescript-eslint/unbound-method */
const IS_WINDOWS = process.platform === 'win32';
/*
@@ -357,6 +367,16 @@ class ToolRunner extends events.EventEmitter {
*/
exec() {
return __awaiter(this, void 0, void 0, function* () {
// root the tool path if it is unrooted and contains relative pathing
if (!ioUtil.isRooted(this.toolPath) &&
(this.toolPath.includes('/') ||
(IS_WINDOWS && this.toolPath.includes('\\')))) {
// prefer options.cwd if it is specified, however options.cwd may also need to be rooted
this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
}
// if the tool is only a file name, then resolve it from the PATH
// otherwise verify it exists (add extension on Windows if necessary)
this.toolPath = yield io.which(this.toolPath, true);
return new Promise((resolve, reject) => {
this._debug(`exec tool: ${this.toolPath}`);
this._debug('arguments:');
@@ -445,6 +465,12 @@ class ToolRunner extends events.EventEmitter {
resolve(exitCode);
}
});
if (this.options.input) {
if (!cp.stdin) {
throw new Error('child process missing stdin');
}
cp.stdin.end(this.options.input);
}
});
});
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+18 -16
View File
@@ -1,41 +1,41 @@
{
"_args": [
[
"@actions/exec@1.0.1",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
"@actions/exec@1.0.4",
"."
]
],
"_from": "@actions/exec@1.0.1",
"_id": "@actions/exec@1.0.1",
"_from": "@actions/exec@1.0.4",
"_id": "@actions/exec@1.0.4",
"_inBundle": false,
"_integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==",
"_integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==",
"_location": "/@actions/exec",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/exec@1.0.1",
"raw": "@actions/exec@1.0.4",
"name": "@actions/exec",
"escapedName": "@actions%2fexec",
"scope": "@actions",
"rawSpec": "1.0.1",
"rawSpec": "1.0.4",
"saveSpec": null,
"fetchSpec": "1.0.1"
"fetchSpec": "1.0.4"
},
"_requiredBy": [
"/",
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz",
"_spec": "1.0.1",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"_resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz",
"_spec": "1.0.4",
"_where": ".",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"description": "Actions exec lib",
"devDependencies": {
"dependencies": {
"@actions/io": "^1.0.1"
},
"description": "Actions exec lib",
"directories": {
"lib": "lib",
"test": "__tests__"
@@ -43,7 +43,6 @@
"files": [
"lib"
],
"gitHead": "a2ab4bcf78e4f7080f0d45856e6eeba16f0bbc52",
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"keywords": [
"github",
@@ -58,11 +57,14 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/exec"
},
"scripts": {
"audit-moderate": "npm install && npm audit --audit-level=moderate",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"version": "1.0.1"
"types": "lib/exec.d.ts",
"version": "1.0.4"
}
@@ -1,6 +1,6 @@
Typed Rest Client for Node.js
Actions Http Client for Node.js
Copyright (c) Microsoft Corporation
Copyright (c) GitHub, Inc.
All rights reserved.
+79
View File
@@ -0,0 +1,79 @@
<p align="center">
<img src="actions.png">
</p>
# Actions Http-Client
[![Http Status](https://github.com/actions/http-client/workflows/http-tests/badge.svg)](https://github.com/actions/http-client/actions)
A lightweight HTTP client optimized for use with actions, TypeScript with generics and async await.
## Features
- HTTP client with TypeScript generics and async/await/Promises
- Typings included so no need to acquire separately (great for intellisense and no versioning drift)
- [Proxy support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners) just works with actions and the runner
- Targets ES2019 (runner runs actions with node 12+). Only supported on node 12+.
- Basic, Bearer and PAT Support out of the box. Extensible handlers for others.
- Redirects supported
Features and releases [here](./RELEASES.md)
## Install
```
npm install @actions/http-client --save
```
## Samples
See the [HTTP](./__tests__) tests for detailed examples.
## Errors
### HTTP
The HTTP client does not throw unless truly exceptional.
* A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body.
* Redirects (3xx) will be followed by default.
See [HTTP tests](./__tests__) for detailed examples.
## Debugging
To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible:
```
export NODE_DEBUG=http
```
## Node support
The http-client is built using the latest LTS version of Node 12. It may work on previous node LTS versions but it's tested and officially supported on Node12+.
## Support and Versioning
We follow semver and will hold compatibility between major versions and increment the minor version with new features and capabilities (while holding compat).
## Contributing
We welcome PRs. Please create an issue and if applicable, a design before proceeding with code.
once:
```bash
$ npm install
```
To build:
```bash
$ npm run build
```
To run all tests:
```bash
$ npm test
```
+22
View File
@@ -0,0 +1,22 @@
## Releases
## 1.0.9
Throw HttpClientError instead of a generic Error from the \<verb>Json() helper methods when the server responds with a non-successful status code.
## 1.0.8
Fixed security issue where a redirect (e.g. 302) to another domain would pass headers. The fix was to strip the authorization header if the hostname was different. More [details in PR #27](https://github.com/actions/http-client/pull/27)
## 1.0.7
Update NPM dependencies and add 429 to the list of HttpCodes
## 1.0.6
Automatically sends Content-Type and Accept application/json headers for \<verb>Json() helper methods if not set in the client or parameters.
## 1.0.5
Adds \<verb>Json() helper methods for json over http scenarios.
## 1.0.4
Started to add \<verb>Json() helper methods. Do not use this release for that. Use >= 1.0.5 since there was an issue with types.
## 1.0.1 to 1.0.3
Adds proxy support.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+23
View File
@@ -0,0 +1,23 @@
import ifm = require('./interfaces');
export declare class BasicCredentialHandler implements ifm.IRequestHandler {
username: string;
password: string;
constructor(username: string, password: string);
prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
}
export declare class BearerCredentialHandler implements ifm.IRequestHandler {
token: string;
constructor(token: string);
prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
}
export declare class PersonalAccessTokenCredentialHandler implements ifm.IRequestHandler {
token: string;
constructor(token: string);
prepareRequest(options: any): void;
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean;
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>;
}
+58
View File
@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class BasicCredentialHandler {
constructor(username, password) {
this.username = username;
this.password = password;
}
prepareRequest(options) {
options.headers['Authorization'] =
'Basic ' +
Buffer.from(this.username + ':' + this.password).toString('base64');
}
// This handler cannot handle 401
canHandleAuthentication(response) {
return false;
}
handleAuthentication(httpClient, requestInfo, objs) {
return null;
}
}
exports.BasicCredentialHandler = BasicCredentialHandler;
class BearerCredentialHandler {
constructor(token) {
this.token = token;
}
// currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401
prepareRequest(options) {
options.headers['Authorization'] = 'Bearer ' + this.token;
}
// This handler cannot handle 401
canHandleAuthentication(response) {
return false;
}
handleAuthentication(httpClient, requestInfo, objs) {
return null;
}
}
exports.BearerCredentialHandler = BearerCredentialHandler;
class PersonalAccessTokenCredentialHandler {
constructor(token) {
this.token = token;
}
// currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401
prepareRequest(options) {
options.headers['Authorization'] =
'Basic ' + Buffer.from('PAT:' + this.token).toString('base64');
}
// This handler cannot handle 401
canHandleAuthentication(response) {
return false;
}
handleAuthentication(httpClient, requestInfo, objs) {
return null;
}
}
exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler;
@@ -1,7 +1,6 @@
/// <reference types="node" />
import url = require("url");
import http = require("http");
import ifm = require('./Interfaces');
import http = require('http');
import ifm = require('./interfaces');
export declare enum HttpCodes {
OK = 200,
MultipleChoices = 300,
@@ -24,32 +23,44 @@ export declare enum HttpCodes {
RequestTimeout = 408,
Conflict = 409,
Gone = 410,
TooManyRequests = 429,
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503,
GatewayTimeout = 504,
GatewayTimeout = 504
}
export declare enum Headers {
Accept = "accept",
ContentType = "content-type"
}
export declare enum MediaTypes {
ApplicationJson = "application/json"
}
/**
* Returns the proxy URL, depending upon the supplied url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
export declare function getProxyUrl(serverUrl: string): string;
export declare class HttpClientError extends Error {
constructor(message: string, statusCode: number);
statusCode: number;
result?: any;
}
export declare class HttpClientResponse implements ifm.IHttpClientResponse {
constructor(message: http.IncomingMessage);
message: http.IncomingMessage;
readBody(): Promise<string>;
}
export interface RequestInfo {
options: http.RequestOptions;
parsedUrl: url.Url;
httpModule: any;
}
export declare function isHttps(requestUrl: string): boolean;
export declare class HttpClient implements ifm.IHttpClient {
userAgent: string;
export declare class HttpClient {
userAgent: string | undefined;
handlers: ifm.IRequestHandler[];
requestOptions: ifm.IRequestOptions;
private _ignoreSslError;
private _socketTimeout;
private _httpProxy;
private _httpProxyBypassHosts;
private _allowRedirects;
private _allowRedirectDowngrade;
private _maxRedirects;
private _allowRetries;
private _maxRetries;
@@ -57,11 +68,7 @@ export declare class HttpClient implements ifm.IHttpClient {
private _proxyAgent;
private _keepAlive;
private _disposed;
private _certConfig;
private _ca;
private _cert;
private _key;
constructor(userAgent: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions);
constructor(userAgent?: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions);
options(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
del(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
@@ -70,6 +77,14 @@ export declare class HttpClient implements ifm.IHttpClient {
put(requestUrl: string, data: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
head(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
sendStream(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>;
/**
* Gets a typed object from an endpoint
* Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
*/
getJson<T>(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
postJson<T>(requestUrl: string, obj: any, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
putJson<T>(requestUrl: string, obj: any, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
patchJson<T>(requestUrl: string, obj: any, additionalHeaders?: ifm.IHeaders): Promise<ifm.ITypedResponse<T>>;
/**
* Makes a raw http request.
* All other methods such as get, post, patch, and request ultimately call this.
@@ -93,11 +108,17 @@ export declare class HttpClient implements ifm.IHttpClient {
* @param onResult
*/
requestRawWithCallback(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: ifm.IHttpClientResponse) => void): void;
private _prepareRequest(method, requestUrl, headers);
private _isPresigned(requestUrl);
private _mergeHeaders(headers);
private _getAgent(requestUrl);
private _getProxy(requestUrl);
private _isBypassProxy(requestUrl);
private _performExponentialBackoff(retryNumber);
/**
* Gets an http agent. This function is useful when you need an http agent that handles
* routing through a proxy server - depending upon the url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
getAgent(serverUrl: string): http.Agent;
private _prepareRequest;
private _mergeHeaders;
private _getExistingOrDefaultHeader;
private _getAgent;
private _performExponentialBackoff;
private static dateTimeDeserializer;
private _processResponse;
}
+535
View File
@@ -0,0 +1,535 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const http = require("http");
const https = require("https");
const pm = require("./proxy");
let tunnel;
var HttpCodes;
(function (HttpCodes) {
HttpCodes[HttpCodes["OK"] = 200] = "OK";
HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices";
HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently";
HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved";
HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther";
HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified";
HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy";
HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy";
HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect";
HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect";
HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest";
HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized";
HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired";
HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden";
HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound";
HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed";
HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable";
HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests";
HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
var Headers;
(function (Headers) {
Headers["Accept"] = "accept";
Headers["ContentType"] = "content-type";
})(Headers = exports.Headers || (exports.Headers = {}));
var MediaTypes;
(function (MediaTypes) {
MediaTypes["ApplicationJson"] = "application/json";
})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {}));
/**
* Returns the proxy URL, depending upon the supplied url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
function getProxyUrl(serverUrl) {
let proxyUrl = pm.getProxyUrl(new URL(serverUrl));
return proxyUrl ? proxyUrl.href : '';
}
exports.getProxyUrl = getProxyUrl;
const HttpRedirectCodes = [
HttpCodes.MovedPermanently,
HttpCodes.ResourceMoved,
HttpCodes.SeeOther,
HttpCodes.TemporaryRedirect,
HttpCodes.PermanentRedirect
];
const HttpResponseRetryCodes = [
HttpCodes.BadGateway,
HttpCodes.ServiceUnavailable,
HttpCodes.GatewayTimeout
];
const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
const ExponentialBackoffCeiling = 10;
const ExponentialBackoffTimeSlice = 5;
class HttpClientError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'HttpClientError';
this.statusCode = statusCode;
Object.setPrototypeOf(this, HttpClientError.prototype);
}
}
exports.HttpClientError = HttpClientError;
class HttpClientResponse {
constructor(message) {
this.message = message;
}
readBody() {
return new Promise(async (resolve, reject) => {
let output = Buffer.alloc(0);
this.message.on('data', (chunk) => {
output = Buffer.concat([output, chunk]);
});
this.message.on('end', () => {
resolve(output.toString());
});
});
}
}
exports.HttpClientResponse = HttpClientResponse;
function isHttps(requestUrl) {
let parsedUrl = new URL(requestUrl);
return parsedUrl.protocol === 'https:';
}
exports.isHttps = isHttps;
class HttpClient {
constructor(userAgent, handlers, requestOptions) {
this._ignoreSslError = false;
this._allowRedirects = true;
this._allowRedirectDowngrade = false;
this._maxRedirects = 50;
this._allowRetries = false;
this._maxRetries = 1;
this._keepAlive = false;
this._disposed = false;
this.userAgent = userAgent;
this.handlers = handlers || [];
this.requestOptions = requestOptions;
if (requestOptions) {
if (requestOptions.ignoreSslError != null) {
this._ignoreSslError = requestOptions.ignoreSslError;
}
this._socketTimeout = requestOptions.socketTimeout;
if (requestOptions.allowRedirects != null) {
this._allowRedirects = requestOptions.allowRedirects;
}
if (requestOptions.allowRedirectDowngrade != null) {
this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade;
}
if (requestOptions.maxRedirects != null) {
this._maxRedirects = Math.max(requestOptions.maxRedirects, 0);
}
if (requestOptions.keepAlive != null) {
this._keepAlive = requestOptions.keepAlive;
}
if (requestOptions.allowRetries != null) {
this._allowRetries = requestOptions.allowRetries;
}
if (requestOptions.maxRetries != null) {
this._maxRetries = requestOptions.maxRetries;
}
}
}
options(requestUrl, additionalHeaders) {
return this.request('OPTIONS', requestUrl, null, additionalHeaders || {});
}
get(requestUrl, additionalHeaders) {
return this.request('GET', requestUrl, null, additionalHeaders || {});
}
del(requestUrl, additionalHeaders) {
return this.request('DELETE', requestUrl, null, additionalHeaders || {});
}
post(requestUrl, data, additionalHeaders) {
return this.request('POST', requestUrl, data, additionalHeaders || {});
}
patch(requestUrl, data, additionalHeaders) {
return this.request('PATCH', requestUrl, data, additionalHeaders || {});
}
put(requestUrl, data, additionalHeaders) {
return this.request('PUT', requestUrl, data, additionalHeaders || {});
}
head(requestUrl, additionalHeaders) {
return this.request('HEAD', requestUrl, null, additionalHeaders || {});
}
sendStream(verb, requestUrl, stream, additionalHeaders) {
return this.request(verb, requestUrl, stream, additionalHeaders);
}
/**
* Gets a typed object from an endpoint
* Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
*/
async getJson(requestUrl, additionalHeaders = {}) {
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
let res = await this.get(requestUrl, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
async postJson(requestUrl, obj, additionalHeaders = {}) {
let data = JSON.stringify(obj, null, 2);
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
let res = await this.post(requestUrl, data, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
async putJson(requestUrl, obj, additionalHeaders = {}) {
let data = JSON.stringify(obj, null, 2);
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
let res = await this.put(requestUrl, data, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
async patchJson(requestUrl, obj, additionalHeaders = {}) {
let data = JSON.stringify(obj, null, 2);
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
let res = await this.patch(requestUrl, data, additionalHeaders);
return this._processResponse(res, this.requestOptions);
}
/**
* Makes a raw http request.
* All other methods such as get, post, patch, and request ultimately call this.
* Prefer get, del, post and patch
*/
async request(verb, requestUrl, data, headers) {
if (this._disposed) {
throw new Error('Client has already been disposed.');
}
let parsedUrl = new URL(requestUrl);
let info = this._prepareRequest(verb, parsedUrl, headers);
// Only perform retries on reads since writes may not be idempotent.
let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
? this._maxRetries + 1
: 1;
let numTries = 0;
let response;
while (numTries < maxTries) {
response = await this.requestRaw(info, data);
// Check if it's an authentication challenge
if (response &&
response.message &&
response.message.statusCode === HttpCodes.Unauthorized) {
let authenticationHandler;
for (let i = 0; i < this.handlers.length; i++) {
if (this.handlers[i].canHandleAuthentication(response)) {
authenticationHandler = this.handlers[i];
break;
}
}
if (authenticationHandler) {
return authenticationHandler.handleAuthentication(this, info, data);
}
else {
// We have received an unauthorized response but have no handlers to handle it.
// Let the response return to the caller.
return response;
}
}
let redirectsRemaining = this._maxRedirects;
while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
this._allowRedirects &&
redirectsRemaining > 0) {
const redirectUrl = response.message.headers['location'];
if (!redirectUrl) {
// if there's no location to redirect to, we won't
break;
}
let parsedRedirectUrl = new URL(redirectUrl);
if (parsedUrl.protocol == 'https:' &&
parsedUrl.protocol != parsedRedirectUrl.protocol &&
!this._allowRedirectDowngrade) {
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.');
}
// we need to finish reading the response before reassigning response
// which will leak the open socket.
await response.readBody();
// strip authorization header if redirected to a different hostname
if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
for (let header in headers) {
// header names are case insensitive
if (header.toLowerCase() === 'authorization') {
delete headers[header];
}
}
}
// let's make the request with the new redirectUrl
info = this._prepareRequest(verb, parsedRedirectUrl, headers);
response = await this.requestRaw(info, data);
redirectsRemaining--;
}
if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
// If not a retry code, return immediately instead of retrying
return response;
}
numTries += 1;
if (numTries < maxTries) {
await response.readBody();
await this._performExponentialBackoff(numTries);
}
}
return response;
}
/**
* Needs to be called if keepAlive is set to true in request options.
*/
dispose() {
if (this._agent) {
this._agent.destroy();
}
this._disposed = true;
}
/**
* Raw request.
* @param info
* @param data
*/
requestRaw(info, data) {
return new Promise((resolve, reject) => {
let callbackForResult = function (err, res) {
if (err) {
reject(err);
}
resolve(res);
};
this.requestRawWithCallback(info, data, callbackForResult);
});
}
/**
* Raw request with callback.
* @param info
* @param data
* @param onResult
*/
requestRawWithCallback(info, data, onResult) {
let socket;
if (typeof data === 'string') {
info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
}
let callbackCalled = false;
let handleResult = (err, res) => {
if (!callbackCalled) {
callbackCalled = true;
onResult(err, res);
}
};
let req = info.httpModule.request(info.options, (msg) => {
let res = new HttpClientResponse(msg);
handleResult(null, res);
});
req.on('socket', sock => {
socket = sock;
});
// If we ever get disconnected, we want the socket to timeout eventually
req.setTimeout(this._socketTimeout || 3 * 60000, () => {
if (socket) {
socket.end();
}
handleResult(new Error('Request timeout: ' + info.options.path), null);
});
req.on('error', function (err) {
// err has statusCode property
// res should have headers
handleResult(err, null);
});
if (data && typeof data === 'string') {
req.write(data, 'utf8');
}
if (data && typeof data !== 'string') {
data.on('close', function () {
req.end();
});
data.pipe(req);
}
else {
req.end();
}
}
/**
* Gets an http agent. This function is useful when you need an http agent that handles
* routing through a proxy server - depending upon the url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
getAgent(serverUrl) {
let parsedUrl = new URL(serverUrl);
return this._getAgent(parsedUrl);
}
_prepareRequest(method, requestUrl, headers) {
const info = {};
info.parsedUrl = requestUrl;
const usingSsl = info.parsedUrl.protocol === 'https:';
info.httpModule = usingSsl ? https : http;
const defaultPort = usingSsl ? 443 : 80;
info.options = {};
info.options.host = info.parsedUrl.hostname;
info.options.port = info.parsedUrl.port
? parseInt(info.parsedUrl.port)
: defaultPort;
info.options.path =
(info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
info.options.method = method;
info.options.headers = this._mergeHeaders(headers);
if (this.userAgent != null) {
info.options.headers['user-agent'] = this.userAgent;
}
info.options.agent = this._getAgent(info.parsedUrl);
// gives handlers an opportunity to participate
if (this.handlers) {
this.handlers.forEach(handler => {
handler.prepareRequest(info.options);
});
}
return info;
}
_mergeHeaders(headers) {
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
if (this.requestOptions && this.requestOptions.headers) {
return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
}
return lowercaseKeys(headers || {});
}
_getExistingOrDefaultHeader(additionalHeaders, header, _default) {
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
let clientHeader;
if (this.requestOptions && this.requestOptions.headers) {
clientHeader = lowercaseKeys(this.requestOptions.headers)[header];
}
return additionalHeaders[header] || clientHeader || _default;
}
_getAgent(parsedUrl) {
let agent;
let proxyUrl = pm.getProxyUrl(parsedUrl);
let useProxy = proxyUrl && proxyUrl.hostname;
if (this._keepAlive && useProxy) {
agent = this._proxyAgent;
}
if (this._keepAlive && !useProxy) {
agent = this._agent;
}
// if agent is already assigned use that agent.
if (!!agent) {
return agent;
}
const usingSsl = parsedUrl.protocol === 'https:';
let maxSockets = 100;
if (!!this.requestOptions) {
maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets;
}
if (useProxy) {
// If using proxy, need tunnel
if (!tunnel) {
tunnel = require('tunnel');
}
const agentOptions = {
maxSockets: maxSockets,
keepAlive: this._keepAlive,
proxy: {
proxyAuth: `${proxyUrl.username}:${proxyUrl.password}`,
host: proxyUrl.hostname,
port: proxyUrl.port
}
};
let tunnelAgent;
const overHttps = proxyUrl.protocol === 'https:';
if (usingSsl) {
tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
}
else {
tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
}
agent = tunnelAgent(agentOptions);
this._proxyAgent = agent;
}
// if reusing agent across request and tunneling agent isn't assigned create a new agent
if (this._keepAlive && !agent) {
const options = { keepAlive: this._keepAlive, maxSockets: maxSockets };
agent = usingSsl ? new https.Agent(options) : new http.Agent(options);
this._agent = agent;
}
// if not using private agent and tunnel agent isn't setup then use global agent
if (!agent) {
agent = usingSsl ? https.globalAgent : http.globalAgent;
}
if (usingSsl && this._ignoreSslError) {
// we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
// http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
// we have to cast it to any and change it directly
agent.options = Object.assign(agent.options || {}, {
rejectUnauthorized: false
});
}
return agent;
}
_performExponentialBackoff(retryNumber) {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
return new Promise(resolve => setTimeout(() => resolve(), ms));
}
static dateTimeDeserializer(key, value) {
if (typeof value === 'string') {
let a = new Date(value);
if (!isNaN(a.valueOf())) {
return a;
}
}
return value;
}
async _processResponse(res, options) {
return new Promise(async (resolve, reject) => {
const statusCode = res.message.statusCode;
const response = {
statusCode: statusCode,
result: null,
headers: {}
};
// not found leads to null obj returned
if (statusCode == HttpCodes.NotFound) {
resolve(response);
}
let obj;
let contents;
// get the result from the body
try {
contents = await res.readBody();
if (contents && contents.length > 0) {
if (options && options.deserializeDates) {
obj = JSON.parse(contents, HttpClient.dateTimeDeserializer);
}
else {
obj = JSON.parse(contents);
}
response.result = obj;
}
response.headers = res.message.headers;
}
catch (err) {
// Invalid resource (contents not json); leaving result obj null
}
// note that 3xx redirects are handled by the http layer.
if (statusCode > 299) {
let msg;
// if exception/error in body, attempt to get better error
if (obj && obj.message) {
msg = obj.message;
}
else if (contents && contents.length > 0) {
// it may be the case that the exception is in the body message as string
msg = contents;
}
else {
msg = 'Failed request: (' + statusCode + ')';
}
let err = new HttpClientError(msg, statusCode);
err.result = response.result;
reject(err);
}
else {
resolve(response);
}
});
}
}
exports.HttpClient = HttpClient;
@@ -1,13 +1,8 @@
/// <reference types="node" />
import http = require("http");
import url = require("url");
import http = require('http');
export interface IHeaders {
[key: string]: any;
}
export interface IBasicCredentials {
username: string;
password: string;
}
export interface IHttpClient {
options(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
get(requestUrl: string, additionalHeaders?: IHeaders): Promise<IHttpClientResponse>;
@@ -31,32 +26,24 @@ export interface IHttpClientResponse {
}
export interface IRequestInfo {
options: http.RequestOptions;
parsedUrl: url.Url;
parsedUrl: URL;
httpModule: any;
}
export interface IRequestOptions {
headers?: IHeaders;
socketTimeout?: number;
ignoreSslError?: boolean;
proxy?: IProxyConfiguration;
cert?: ICertConfiguration;
allowRedirects?: boolean;
allowRedirectDowngrade?: boolean;
maxRedirects?: number;
maxSockets?: number;
keepAlive?: boolean;
presignedUrlPatterns?: RegExp[];
deserializeDates?: boolean;
allowRetries?: boolean;
maxRetries?: number;
}
export interface IProxyConfiguration {
proxyUrl: string;
proxyUsername?: string;
proxyPassword?: string;
proxyBypassHosts?: string[];
}
export interface ICertConfiguration {
caFile?: string;
certFile?: string;
keyFile?: string;
passphrase?: string;
export interface ITypedResponse<T> {
statusCode: number;
result: T | null;
headers: Object;
}
+70
View File
@@ -0,0 +1,70 @@
{
"_args": [
[
"@actions/http-client@1.0.9",
"."
]
],
"_from": "@actions/http-client@1.0.9",
"_id": "@actions/http-client@1.0.9",
"_inBundle": false,
"_integrity": "sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg==",
"_location": "/@actions/http-client",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/http-client@1.0.9",
"name": "@actions/http-client",
"escapedName": "@actions%2fhttp-client",
"scope": "@actions",
"rawSpec": "1.0.9",
"saveSpec": null,
"fetchSpec": "1.0.9"
},
"_requiredBy": [
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz",
"_spec": "1.0.9",
"_where": ".",
"author": {
"name": "GitHub, Inc."
},
"bugs": {
"url": "https://github.com/actions/http-client/issues"
},
"dependencies": {
"tunnel": "0.0.6"
},
"description": "Actions Http Client",
"devDependencies": {
"@types/jest": "^25.1.4",
"@types/node": "^12.12.31",
"jest": "^25.1.0",
"prettier": "^2.0.4",
"proxy": "^1.0.1",
"ts-jest": "^25.2.1",
"typescript": "^3.8.3"
},
"homepage": "https://github.com/actions/http-client#readme",
"keywords": [
"Actions",
"Http"
],
"license": "MIT",
"main": "index.js",
"name": "@actions/http-client",
"repository": {
"type": "git",
"url": "git+https://github.com/actions/http-client.git"
},
"scripts": {
"audit-check": "npm audit --audit-level=moderate",
"build": "rm -Rf ./_out && tsc && cp package*.json ./_out && cp *.md ./_out && cp LICENSE ./_out && cp actions.png ./_out",
"format": "prettier --write *.ts && prettier --write **/*.ts",
"format-check": "prettier --check *.ts && prettier --check **/*.ts",
"test": "jest"
},
"version": "1.0.9"
}
+2
View File
@@ -0,0 +1,2 @@
export declare function getProxyUrl(reqUrl: URL): URL | undefined;
export declare function checkBypass(reqUrl: URL): boolean;
+57
View File
@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function getProxyUrl(reqUrl) {
let usingSsl = reqUrl.protocol === 'https:';
let proxyUrl;
if (checkBypass(reqUrl)) {
return proxyUrl;
}
let proxyVar;
if (usingSsl) {
proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY'];
}
else {
proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY'];
}
if (proxyVar) {
proxyUrl = new URL(proxyVar);
}
return proxyUrl;
}
exports.getProxyUrl = getProxyUrl;
function checkBypass(reqUrl) {
if (!reqUrl.hostname) {
return false;
}
let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
if (!noProxy) {
return false;
}
// Determine the request port
let reqPort;
if (reqUrl.port) {
reqPort = Number(reqUrl.port);
}
else if (reqUrl.protocol === 'http:') {
reqPort = 80;
}
else if (reqUrl.protocol === 'https:') {
reqPort = 443;
}
// Format the request hostname and hostname with port
let upperReqHosts = [reqUrl.hostname.toUpperCase()];
if (typeof reqPort === 'number') {
upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`);
}
// Compare request host against noproxy
for (let upperNoProxyItem of noProxy
.split(',')
.map(x => x.trim().toUpperCase())
.filter(x => x)) {
if (upperReqHosts.some(x => x === upperNoProxyItem)) {
return true;
}
}
return false;
}
exports.checkBypass = checkBypass;
+2
View File
@@ -1,3 +1,5 @@
The MIT License (MIT)
Copyright 2019 GitHub
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:
+17 -14
View File
@@ -1,33 +1,34 @@
{
"_args": [
[
"@actions/io@1.0.1",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
"@actions/io@1.0.2",
"."
]
],
"_from": "@actions/io@1.0.1",
"_id": "@actions/io@1.0.1",
"_from": "@actions/io@1.0.2",
"_id": "@actions/io@1.0.2",
"_inBundle": false,
"_integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==",
"_integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==",
"_location": "/@actions/io",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/io@1.0.1",
"raw": "@actions/io@1.0.2",
"name": "@actions/io",
"escapedName": "@actions%2fio",
"scope": "@actions",
"rawSpec": "1.0.1",
"rawSpec": "1.0.2",
"saveSpec": null,
"fetchSpec": "1.0.1"
"fetchSpec": "1.0.2"
},
"_requiredBy": [
"/@actions/exec",
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz",
"_spec": "1.0.1",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"_resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz",
"_spec": "1.0.2",
"_where": ".",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
@@ -39,7 +40,6 @@
"files": [
"lib"
],
"gitHead": "a2ab4bcf78e4f7080f0d45856e6eeba16f0bbc52",
"homepage": "https://github.com/actions/toolkit/tree/master/packages/io",
"keywords": [
"github",
@@ -54,11 +54,14 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/io"
},
"scripts": {
"audit-moderate": "npm install && npm audit --audit-level=moderate",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"version": "1.0.1"
"types": "lib/io.d.ts",
"version": "1.0.2"
}
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
+8 -4
View File
@@ -22,13 +22,17 @@ These can then be extracted in platform specific ways:
const tc = require('@actions/tool-cache');
if (process.platform === 'win32') {
const node12Path = tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip');
const node12ExtractedFolder = await tc.extractZip(node12Path, 'path/to/extract/to');
// Or alternately
const node12Path = tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to');
}
else if (process.platform === 'darwin') {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0.pkg');
const node12ExtractedFolder = await tc.extractXar(node12Path, 'path/to/extract/to');
}
else {
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
@@ -37,7 +41,7 @@ else {
#### Cache
Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for private runners (private runners are still in development but are on the roadmap).
Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for self-hosted runners.
You'll often want to add it to the path as part of this step:
@@ -57,7 +61,7 @@ You can also cache files for reuse.
```js
const tc = require('@actions/tool-cache');
tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0');
const cachedPath = await tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0');
```
#### Find
+16
View File
@@ -0,0 +1,16 @@
export interface IToolReleaseFile {
filename: string;
platform: string;
platform_version?: string;
arch: string;
download_url: string;
}
export interface IToolRelease {
version: string;
stable: boolean;
release_url: string;
files: IToolReleaseFile[];
}
export declare function _findMatch(versionSpec: string, stable: boolean, candidates: IToolRelease[], archFilter: string): Promise<IToolRelease | undefined>;
export declare function _getOsVersion(): string;
export declare function _readLinuxVersionFile(): string;
+106
View File
@@ -0,0 +1,106 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const semver = __importStar(require("semver"));
const core_1 = require("@actions/core");
// needs to be require for core node modules to be mocked
/* eslint @typescript-eslint/no-require-imports: 0 */
const os = require("os");
const cp = require("child_process");
const fs = require("fs");
function _findMatch(versionSpec, stable, candidates, archFilter) {
return __awaiter(this, void 0, void 0, function* () {
const platFilter = os.platform();
let result;
let match;
let file;
for (const candidate of candidates) {
const version = candidate.version;
core_1.debug(`check ${version} satisfies ${versionSpec}`);
if (semver.satisfies(version, versionSpec) &&
(!stable || candidate.stable === stable)) {
file = candidate.files.find(item => {
core_1.debug(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`);
let chk = item.arch === archFilter && item.platform === platFilter;
if (chk && item.platform_version) {
const osVersion = module.exports._getOsVersion();
if (osVersion === item.platform_version) {
chk = true;
}
else {
chk = semver.satisfies(osVersion, item.platform_version);
}
}
return chk;
});
if (file) {
core_1.debug(`matched ${candidate.version}`);
match = candidate;
break;
}
}
}
if (match && file) {
// clone since we're mutating the file list to be only the file that matches
result = Object.assign({}, match);
result.files = [file];
}
return result;
});
}
exports._findMatch = _findMatch;
function _getOsVersion() {
// TODO: add windows and other linux, arm variants
// right now filtering on version is only an ubuntu and macos scenario for tools we build for hosted (python)
const plat = os.platform();
let version = '';
if (plat === 'darwin') {
version = cp.execSync('sw_vers -productVersion').toString();
}
else if (plat === 'linux') {
// lsb_release process not in some containers, readfile
// Run cat /etc/lsb-release
// DISTRIB_ID=Ubuntu
// DISTRIB_RELEASE=18.04
// DISTRIB_CODENAME=bionic
// DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
const lsbContents = module.exports._readLinuxVersionFile();
if (lsbContents) {
const lines = lsbContents.split('\n');
for (const line of lines) {
const parts = line.split('=');
if (parts.length === 2 && parts[0].trim() === 'DISTRIB_RELEASE') {
version = parts[1].trim();
break;
}
}
}
}
return version;
}
exports._getOsVersion = _getOsVersion;
function _readLinuxVersionFile() {
const lsbFile = '/etc/lsb-release';
let contents = '';
if (fs.existsSync(lsbFile)) {
contents = fs.readFileSync(lsbFile).toString();
}
return contents;
}
exports._readLinuxVersionFile = _readLinuxVersionFile;
//# sourceMappingURL=manifest.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,+CAAgC;AAChC,wCAAmC;AAEnC,yDAAyD;AACzD,qDAAqD;AAErD,yBAAyB;AACzB,oCAAoC;AACpC,yBAAyB;AAqDzB,SAAsB,UAAU,CAC9B,WAAmB,EACnB,MAAe,EACf,UAA0B,EAC1B,UAAkB;;QAElB,MAAM,UAAU,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAA;QAEhC,IAAI,MAAgC,CAAA;QACpC,IAAI,KAA+B,CAAA;QAEnC,IAAI,IAAkC,CAAA;QACtC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;YAClC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAA;YAEjC,YAAK,CAAC,SAAS,OAAO,cAAc,WAAW,EAAE,CAAC,CAAA;YAClD,IACE,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC;gBACtC,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,EACxC;gBACA,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBACjC,YAAK,CACH,GAAG,IAAI,CAAC,IAAI,MAAM,UAAU,OAAO,IAAI,CAAC,QAAQ,MAAM,UAAU,EAAE,CACnE,CAAA;oBAED,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,CAAA;oBAClE,IAAI,GAAG,IAAI,IAAI,CAAC,gBAAgB,EAAE;wBAChC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAA;wBAEhD,IAAI,SAAS,KAAK,IAAI,CAAC,gBAAgB,EAAE;4BACvC,GAAG,GAAG,IAAI,CAAA;yBACX;6BAAM;4BACL,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAA;yBACzD;qBACF;oBAED,OAAO,GAAG,CAAA;gBACZ,CAAC,CAAC,CAAA;gBAEF,IAAI,IAAI,EAAE;oBACR,YAAK,CAAC,WAAW,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;oBACrC,KAAK,GAAG,SAAS,CAAA;oBACjB,MAAK;iBACN;aACF;SACF;QAED,IAAI,KAAK,IAAI,IAAI,EAAE;YACjB,4EAA4E;YAC5E,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;YACjC,MAAM,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAA;SACtB;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CAAA;AAtDD,gCAsDC;AAED,SAAgB,aAAa;IAC3B,kDAAkD;IAClD,6GAA6G;IAC7G,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAA;IAC1B,IAAI,OAAO,GAAG,EAAE,CAAA;IAEhB,IAAI,IAAI,KAAK,QAAQ,EAAE;QACrB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,QAAQ,EAAE,CAAA;KAC5D;SAAM,IAAI,IAAI,KAAK,OAAO,EAAE;QAC3B,uDAAuD;QACvD,2BAA2B;QAC3B,oBAAoB;QACpB,wBAAwB;QACxB,0BAA0B;QAC1B,2CAA2C;QAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAA;QAC1D,IAAI,WAAW,EAAE;YACf,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;gBACxB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;gBAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,iBAAiB,EAAE;oBAC/D,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;oBACzB,MAAK;iBACN;aACF;SACF;KACF;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AA7BD,sCA6BC;AAED,SAAgB,qBAAqB;IACnC,MAAM,OAAO,GAAG,kBAAkB,CAAA;IAClC,IAAI,QAAQ,GAAG,EAAE,CAAA;IAEjB,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QAC1B,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;KAC/C;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AATD,sDASC"}
+12
View File
@@ -0,0 +1,12 @@
/**
* Internal class for retries
*/
export declare class RetryHelper {
private maxAttempts;
private minSeconds;
private maxSeconds;
constructor(maxAttempts: number, minSeconds: number, maxSeconds: number);
execute<T>(action: () => Promise<T>, isRetryable?: (e: Error) => boolean): Promise<T>;
private getSleepAmount;
private sleep;
}
+70
View File
@@ -0,0 +1,70 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
/**
* Internal class for retries
*/
class RetryHelper {
constructor(maxAttempts, minSeconds, maxSeconds) {
if (maxAttempts < 1) {
throw new Error('max attempts should be greater than or equal to 1');
}
this.maxAttempts = maxAttempts;
this.minSeconds = Math.floor(minSeconds);
this.maxSeconds = Math.floor(maxSeconds);
if (this.minSeconds > this.maxSeconds) {
throw new Error('min seconds should be less than or equal to max seconds');
}
}
execute(action, isRetryable) {
return __awaiter(this, void 0, void 0, function* () {
let attempt = 1;
while (attempt < this.maxAttempts) {
// Try
try {
return yield action();
}
catch (err) {
if (isRetryable && !isRetryable(err)) {
throw err;
}
core.info(err.message);
}
// Sleep
const seconds = this.getSleepAmount();
core.info(`Waiting ${seconds} seconds before trying again`);
yield this.sleep(seconds);
attempt++;
}
// Last attempt
return yield action();
});
}
getSleepAmount() {
return (Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) +
this.minSeconds);
}
sleep(seconds) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
});
}
}
exports.RetryHelper = RetryHelper;
//# sourceMappingURL=retry-helper.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"retry-helper.js","sourceRoot":"","sources":["../src/retry-helper.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,oDAAqC;AAErC;;GAEG;AACH,MAAa,WAAW;IAKtB,YAAY,WAAmB,EAAE,UAAkB,EAAE,UAAkB;QACrE,IAAI,WAAW,GAAG,CAAC,EAAE;YACnB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;SACrE;QAED,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxC,IAAI,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE;YACrC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;SAC3E;IACH,CAAC;IAEK,OAAO,CACX,MAAwB,EACxB,WAAmC;;YAEnC,IAAI,OAAO,GAAG,CAAC,CAAA;YACf,OAAO,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;gBACjC,MAAM;gBACN,IAAI;oBACF,OAAO,MAAM,MAAM,EAAE,CAAA;iBACtB;gBAAC,OAAO,GAAG,EAAE;oBACZ,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE;wBACpC,MAAM,GAAG,CAAA;qBACV;oBAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;iBACvB;gBAED,QAAQ;gBACR,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;gBACrC,IAAI,CAAC,IAAI,CAAC,WAAW,OAAO,8BAA8B,CAAC,CAAA;gBAC3D,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;gBACzB,OAAO,EAAE,CAAA;aACV;YAED,eAAe;YACf,OAAO,MAAM,MAAM,EAAE,CAAA;QACvB,CAAC;KAAA;IAEO,cAAc;QACpB,OAAO,CACL,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,UAAU,CAChB,CAAA;IACH,CAAC;IAEa,KAAK,CAAC,OAAe;;YACjC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAA;QACpE,CAAC;KAAA;CACF;AAxDD,kCAwDC"}
+20 -4
View File
@@ -1,3 +1,4 @@
import * as mm from './manifest';
export declare class HTTPError extends Error {
readonly httpStatusCode: number | undefined;
constructor(httpStatusCode: number | undefined);
@@ -6,9 +7,11 @@ export declare class HTTPError extends Error {
* Download a tool from an url and stream it into a file
*
* @param url url of tool to download
* @param dest path to download tool
* @param auth authorization header
* @returns path to downloaded tool
*/
export declare function downloadTool(url: string): Promise<string>;
export declare function downloadTool(url: string, dest?: string, auth?: string): Promise<string>;
/**
* Extract a .7z file
*
@@ -26,14 +29,23 @@ export declare function downloadTool(url: string): Promise<string>;
*/
export declare function extract7z(file: string, dest?: string, _7zPath?: string): Promise<string>;
/**
* Extract a tar
* Extract a compressed tar archive
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @param flags flags for the tar. Optional.
* @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional.
* @returns path to the destination directory
*/
export declare function extractTar(file: string, dest?: string, flags?: string): Promise<string>;
export declare function extractTar(file: string, dest?: string, flags?: string | string[]): Promise<string>;
/**
* Extract a xar compatible archive
*
* @param file path to the archive
* @param dest destination directory. Optional.
* @param flags flags for the xar. Optional.
* @returns path to the destination directory
*/
export declare function extractXar(file: string, dest?: string, flags?: string | string[]): Promise<string>;
/**
* Extract a zip
*
@@ -77,3 +89,7 @@ export declare function find(toolName: string, versionSpec: string, arch?: strin
* @param arch optional arch. defaults to arch of computer
*/
export declare function findAllVersions(toolName: string, arch?: string): string[];
export declare type IToolRelease = mm.IToolRelease;
export declare type IToolReleaseFile = mm.IToolReleaseFile;
export declare function getManifestFromRepo(owner: string, repo: string, auth?: string, branch?: string): Promise<IToolRelease[]>;
export declare function findFromManifest(versionSpec: string, stable: boolean, manifest: IToolRelease[], archFilter?: string): Promise<IToolRelease | undefined>;
+259 -91
View File
@@ -8,17 +8,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = require("@actions/core");
const io = require("@actions/io");
const fs = require("fs");
const os = require("os");
const path = require("path");
const httpm = require("typed-rest-client/HttpClient");
const semver = require("semver");
const uuidV4 = require("uuid/v4");
const core = __importStar(require("@actions/core"));
const io = __importStar(require("@actions/io"));
const fs = __importStar(require("fs"));
const mm = __importStar(require("./manifest"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const httpm = __importStar(require("@actions/http-client"));
const semver = __importStar(require("semver"));
const stream = __importStar(require("stream"));
const util = __importStar(require("util"));
const v4_1 = __importDefault(require("uuid/v4"));
const exec_1 = require("@actions/exec/lib/exec");
const assert_1 = require("assert");
const retry_helper_1 = require("./retry-helper");
class HTTPError extends Error {
constructor(httpStatusCode) {
super(`Unexpected HTTP response: ${httpStatusCode}`);
@@ -28,86 +42,90 @@ class HTTPError extends Error {
}
exports.HTTPError = HTTPError;
const IS_WINDOWS = process.platform === 'win32';
const IS_MAC = process.platform === 'darwin';
const userAgent = 'actions/tool-cache';
// On load grab temp directory and cache directory and remove them from env (currently don't want to expose this)
let tempDirectory = process.env['RUNNER_TEMP'] || '';
let cacheRoot = process.env['RUNNER_TOOL_CACHE'] || '';
// If directories not found, place them in common temp locations
if (!tempDirectory || !cacheRoot) {
let baseLocation;
if (IS_WINDOWS) {
// On windows use the USERPROFILE env variable
baseLocation = process.env['USERPROFILE'] || 'C:\\';
}
else {
if (process.platform === 'darwin') {
baseLocation = '/Users';
}
else {
baseLocation = '/home';
}
}
if (!tempDirectory) {
tempDirectory = path.join(baseLocation, 'actions', 'temp');
}
if (!cacheRoot) {
cacheRoot = path.join(baseLocation, 'actions', 'cache');
}
}
/**
* Download a tool from an url and stream it into a file
*
* @param url url of tool to download
* @param dest path to download tool
* @param auth authorization header
* @returns path to downloaded tool
*/
function downloadTool(url) {
function downloadTool(url, dest, auth) {
return __awaiter(this, void 0, void 0, function* () {
// Wrap in a promise so that we can resolve from within stream callbacks
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const http = new httpm.HttpClient(userAgent, [], {
allowRetries: true,
maxRetries: 3
});
const destPath = path.join(tempDirectory, uuidV4());
yield io.mkdirP(tempDirectory);
core.debug(`Downloading ${url}`);
core.debug(`Downloading ${destPath}`);
if (fs.existsSync(destPath)) {
throw new Error(`Destination file path ${destPath} already exists`);
dest = dest || path.join(_getTempDirectory(), v4_1.default());
yield io.mkdirP(path.dirname(dest));
core.debug(`Downloading ${url}`);
core.debug(`Destination ${dest}`);
const maxAttempts = 3;
const minSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', 10);
const maxSeconds = _getGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 20);
const retryHelper = new retry_helper_1.RetryHelper(maxAttempts, minSeconds, maxSeconds);
return yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
return yield downloadToolAttempt(url, dest || '', auth);
}), (err) => {
if (err instanceof HTTPError && err.httpStatusCode) {
// Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests
if (err.httpStatusCode < 500 &&
err.httpStatusCode !== 408 &&
err.httpStatusCode !== 429) {
return false;
}
const response = yield http.get(url);
if (response.message.statusCode !== 200) {
const err = new HTTPError(response.message.statusCode);
core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`);
throw err;
}
const file = fs.createWriteStream(destPath);
file.on('open', () => __awaiter(this, void 0, void 0, function* () {
try {
const stream = response.message.pipe(file);
stream.on('close', () => {
core.debug('download complete');
resolve(destPath);
});
}
catch (err) {
core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`);
reject(err);
}
}));
file.on('error', err => {
file.end();
reject(err);
});
}
catch (err) {
reject(err);
}
}));
// Otherwise retry
return true;
});
});
}
exports.downloadTool = downloadTool;
function downloadToolAttempt(url, dest, auth) {
return __awaiter(this, void 0, void 0, function* () {
if (fs.existsSync(dest)) {
throw new Error(`Destination file path ${dest} already exists`);
}
// Get the response headers
const http = new httpm.HttpClient(userAgent, [], {
allowRetries: false
});
let headers;
if (auth) {
core.debug('set auth');
headers = {
authorization: auth
};
}
const response = yield http.get(url, headers);
if (response.message.statusCode !== 200) {
const err = new HTTPError(response.message.statusCode);
core.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`);
throw err;
}
// Download the response body
const pipeline = util.promisify(stream.pipeline);
const responseMessageFactory = _getGlobal('TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', () => response.message);
const readStream = responseMessageFactory();
let succeeded = false;
try {
yield pipeline(readStream, fs.createWriteStream(dest));
core.debug('download complete');
succeeded = true;
return dest;
}
finally {
// Error, delete dest before retry
if (!succeeded) {
core.debug('download failed');
try {
yield io.rmRF(dest);
}
catch (err) {
core.debug(`Failed to delete '${dest}'. ${err.message}`);
}
}
}
});
}
/**
* Extract a .7z file
*
@@ -127,14 +145,15 @@ function extract7z(file, dest, _7zPath) {
return __awaiter(this, void 0, void 0, function* () {
assert_1.ok(IS_WINDOWS, 'extract7z() not supported on current OS');
assert_1.ok(file, 'parameter "file" is required');
dest = dest || (yield _createExtractFolder(dest));
dest = yield _createExtractFolder(dest);
const originalCwd = process.cwd();
process.chdir(dest);
if (_7zPath) {
try {
const logLevel = core.isDebug() ? '-bb1' : '-bb0';
const args = [
'x',
'-bb1',
logLevel,
'-bd',
'-sccUTF-8',
file
@@ -182,11 +201,11 @@ function extract7z(file, dest, _7zPath) {
}
exports.extract7z = extract7z;
/**
* Extract a tar
* Extract a compressed tar archive
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @param flags flags for the tar. Optional.
* @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional.
* @returns path to the destination directory
*/
function extractTar(file, dest, flags = 'xz') {
@@ -194,13 +213,81 @@ function extractTar(file, dest, flags = 'xz') {
if (!file) {
throw new Error("parameter 'file' is required");
}
dest = dest || (yield _createExtractFolder(dest));
const tarPath = yield io.which('tar', true);
yield exec_1.exec(`"${tarPath}"`, [flags, '-C', dest, '-f', file]);
// Create dest
dest = yield _createExtractFolder(dest);
// Determine whether GNU tar
core.debug('Checking tar --version');
let versionOutput = '';
yield exec_1.exec('tar --version', [], {
ignoreReturnCode: true,
silent: true,
listeners: {
stdout: (data) => (versionOutput += data.toString()),
stderr: (data) => (versionOutput += data.toString())
}
});
core.debug(versionOutput.trim());
const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR');
// Initialize args
let args;
if (flags instanceof Array) {
args = flags;
}
else {
args = [flags];
}
if (core.isDebug() && !flags.includes('v')) {
args.push('-v');
}
let destArg = dest;
let fileArg = file;
if (IS_WINDOWS && isGnuTar) {
args.push('--force-local');
destArg = dest.replace(/\\/g, '/');
// Technically only the dest needs to have `/` but for aesthetic consistency
// convert slashes in the file arg too.
fileArg = file.replace(/\\/g, '/');
}
if (isGnuTar) {
// Suppress warnings when using GNU tar to extract archives created by BSD tar
args.push('--warning=no-unknown-keyword');
}
args.push('-C', destArg, '-f', fileArg);
yield exec_1.exec(`tar`, args);
return dest;
});
}
exports.extractTar = extractTar;
/**
* Extract a xar compatible archive
*
* @param file path to the archive
* @param dest destination directory. Optional.
* @param flags flags for the xar. Optional.
* @returns path to the destination directory
*/
function extractXar(file, dest, flags = []) {
return __awaiter(this, void 0, void 0, function* () {
assert_1.ok(IS_MAC, 'extractXar() not supported on current OS');
assert_1.ok(file, 'parameter "file" is required');
dest = yield _createExtractFolder(dest);
let args;
if (flags instanceof Array) {
args = flags;
}
else {
args = [flags];
}
args.push('-x', '-C', dest, '-f', file);
if (core.isDebug()) {
args.push('-v');
}
const xarPath = yield io.which('xar', true);
yield exec_1.exec(`"${xarPath}"`, _unique(args));
return dest;
});
}
exports.extractXar = extractXar;
/**
* Extract a zip
*
@@ -213,7 +300,7 @@ function extractZip(file, dest) {
if (!file) {
throw new Error("parameter 'file' is required");
}
dest = dest || (yield _createExtractFolder(dest));
dest = yield _createExtractFolder(dest);
if (IS_WINDOWS) {
yield extractZipWin(file, dest);
}
@@ -231,7 +318,7 @@ function extractZipWin(file, dest) {
const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '');
const command = `$ErrorActionPreference = 'Stop' ; try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}')`;
// run powershell
const powershellPath = yield io.which('powershell');
const powershellPath = yield io.which('powershell', true);
const args = [
'-NoLogo',
'-Sta',
@@ -247,8 +334,12 @@ function extractZipWin(file, dest) {
}
function extractZipNix(file, dest) {
return __awaiter(this, void 0, void 0, function* () {
const unzipPath = yield io.which('unzip');
yield exec_1.exec(`"${unzipPath}"`, [file], { cwd: dest });
const unzipPath = yield io.which('unzip', true);
const args = [file];
if (!core.isDebug()) {
args.unshift('-q');
}
yield exec_1.exec(`"${unzipPath}"`, args, { cwd: dest });
});
}
/**
@@ -339,7 +430,7 @@ function find(toolName, versionSpec, arch) {
let toolPath = '';
if (versionSpec) {
versionSpec = semver.clean(versionSpec) || '';
const cachePath = path.join(cacheRoot, toolName, versionSpec, arch);
const cachePath = path.join(_getCacheDirectory(), toolName, versionSpec, arch);
core.debug(`checking cache: ${cachePath}`);
if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) {
core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`);
@@ -361,7 +452,7 @@ exports.find = find;
function findAllVersions(toolName, arch) {
const versions = [];
arch = arch || os.arch();
const toolPath = path.join(cacheRoot, toolName);
const toolPath = path.join(_getCacheDirectory(), toolName);
if (fs.existsSync(toolPath)) {
const children = fs.readdirSync(toolPath);
for (const child of children) {
@@ -376,11 +467,56 @@ function findAllVersions(toolName, arch) {
return versions;
}
exports.findAllVersions = findAllVersions;
function getManifestFromRepo(owner, repo, auth, branch = 'master') {
return __awaiter(this, void 0, void 0, function* () {
let releases = [];
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`;
const http = new httpm.HttpClient('tool-cache');
const headers = {};
if (auth) {
core.debug('set auth');
headers.authorization = auth;
}
const response = yield http.getJson(treeUrl, headers);
if (!response.result) {
return releases;
}
let manifestUrl = '';
for (const item of response.result.tree) {
if (item.path === 'versions-manifest.json') {
manifestUrl = item.url;
break;
}
}
headers['accept'] = 'application/vnd.github.VERSION.raw';
let versionsRaw = yield (yield http.get(manifestUrl, headers)).readBody();
if (versionsRaw) {
// shouldn't be needed but protects against invalid json saved with BOM
versionsRaw = versionsRaw.replace(/^\uFEFF/, '');
try {
releases = JSON.parse(versionsRaw);
}
catch (_a) {
core.debug('Invalid json');
}
}
return releases;
});
}
exports.getManifestFromRepo = getManifestFromRepo;
function findFromManifest(versionSpec, stable, manifest, archFilter = os.arch()) {
return __awaiter(this, void 0, void 0, function* () {
// wrap the internal impl
const match = yield mm._findMatch(versionSpec, stable, manifest, archFilter);
return match;
});
}
exports.findFromManifest = findFromManifest;
function _createExtractFolder(dest) {
return __awaiter(this, void 0, void 0, function* () {
if (!dest) {
// create a temp dir
dest = path.join(tempDirectory, uuidV4());
dest = path.join(_getTempDirectory(), v4_1.default());
}
yield io.mkdirP(dest);
return dest;
@@ -388,7 +524,7 @@ function _createExtractFolder(dest) {
}
function _createToolPath(tool, version, arch) {
return __awaiter(this, void 0, void 0, function* () {
const folderPath = path.join(cacheRoot, tool, semver.clean(version) || version, arch || '');
const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || '');
core.debug(`destination ${folderPath}`);
const markerPath = `${folderPath}.complete`;
yield io.rmRF(folderPath);
@@ -398,7 +534,7 @@ function _createToolPath(tool, version, arch) {
});
}
function _completeToolPath(tool, version, arch) {
const folderPath = path.join(cacheRoot, tool, semver.clean(version) || version, arch || '');
const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || '');
const markerPath = `${folderPath}.complete`;
fs.writeFileSync(markerPath, '');
core.debug('finished caching tool');
@@ -435,4 +571,36 @@ function _evaluateVersions(versions, versionSpec) {
}
return version;
}
/**
* Gets RUNNER_TOOL_CACHE
*/
function _getCacheDirectory() {
const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || '';
assert_1.ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined');
return cacheDirectory;
}
/**
* Gets RUNNER_TEMP
*/
function _getTempDirectory() {
const tempDirectory = process.env['RUNNER_TEMP'] || '';
assert_1.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined');
return tempDirectory;
}
/**
* Gets a global variable
*/
function _getGlobal(key, defaultValue) {
/* eslint-disable @typescript-eslint/no-explicit-any */
const value = global[key];
/* eslint-enable @typescript-eslint/no-explicit-any */
return value !== undefined ? value : defaultValue;
}
/**
* Returns an array of unique values.
* @param values Values to make unique.
*/
function _unique(values) {
return Array.from(new Set(values));
}
//# sourceMappingURL=tool-cache.js.map
File diff suppressed because one or more lines are too long
-140
View File
@@ -1,140 +0,0 @@
# `@actions/core`
> Core functions for setting results, logging, registering secrets and exporting variables across actions
## Usage
### Import the package
```js
// javascript
const core = require('@actions/core');
// typescript
import * as core from '@actions/core';
```
#### Inputs/Outputs
Action inputs can be read with `getInput`. Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
```js
const myInput = core.getInput('inputName', { required: true });
core.setOutput('outputKey', 'outputVal');
```
#### Exporting variables
Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks.
```js
core.exportVariable('envVar', 'Val');
```
#### Setting a secret
Setting a secret registers the secret with the runner to ensure it is masked in logs.
```js
core.setSecret('myPassword');
```
#### PATH Manipulation
To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH.
```js
core.addPath('/path/to/mytool');
```
#### Exit codes
You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success.
```js
const core = require('@actions/core');
try {
// Do stuff
}
catch (err) {
// setFailed logs the message and sets a failing exit code
core.setFailed(`Action failed with error ${err}`);
}
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
```
#### Logging
Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs).
```js
const core = require('@actions/core');
const myInput = core.getInput('input');
try {
core.debug('Inside try block');
if (!myInput) {
core.warning('myInput was not set');
}
// Do stuff
}
catch (err) {
core.error(`Error ${err}, action may still succeed though`);
}
```
This library can also wrap chunks of output in foldable groups.
```js
const core = require('@actions/core')
// Manually wrap output
core.startGroup('Do some function')
doSomeFunction()
core.endGroup()
// Wrap an asynchronous function call
const result = await core.group('Do something async', async () => {
const response = await doSomeHTTPRequest()
return response
})
```
#### Action state
You can use this library to save state and get state for sharing information between a given wrapper action:
**action.yml**
```yaml
name: 'Wrapper action sample'
inputs:
name:
default: 'GitHub'
runs:
using: 'node12'
main: 'main.js'
post: 'cleanup.js'
```
In action's `main.js`:
```js
const core = require('@actions/core');
core.saveState("pidToKill", 12345);
```
In action's `cleanup.js`:
```js
const core = require('@actions/core');
var pid = core.getState("pidToKill");
process.kill(pid);
```
@@ -1,16 +0,0 @@
interface CommandProperties {
[key: string]: string;
}
/**
* Commands
*
* Command Format:
* ##[name key=value;key=value]message
*
* Examples:
* ##[warning]This is the user warning message
* ##[set-secret name=mypassword]definitelyNotAPassword!
*/
export declare function issueCommand(command: string, properties: CommandProperties, message: string): void;
export declare function issue(name: string, message?: string): void;
export {};
@@ -1,66 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const os = require("os");
/**
* Commands
*
* Command Format:
* ##[name key=value;key=value]message
*
* Examples:
* ##[warning]This is the user warning message
* ##[set-secret name=mypassword]definitelyNotAPassword!
*/
function issueCommand(command, properties, message) {
const cmd = new Command(command, properties, message);
process.stdout.write(cmd.toString() + os.EOL);
}
exports.issueCommand = issueCommand;
function issue(name, message = '') {
issueCommand(name, {}, message);
}
exports.issue = issue;
const CMD_STRING = '::';
class Command {
constructor(command, properties, message) {
if (!command) {
command = 'missing.command';
}
this.command = command;
this.properties = properties;
this.message = message;
}
toString() {
let cmdStr = CMD_STRING + this.command;
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' ';
for (const key in this.properties) {
if (this.properties.hasOwnProperty(key)) {
const val = this.properties[key];
if (val) {
// safely append the val - avoid blowing up when attempting to
// call .replace() if message is not a string for some reason
cmdStr += `${key}=${escape(`${val || ''}`)},`;
}
}
}
}
cmdStr += CMD_STRING;
// safely append the message - avoid blowing up when attempting to
// call .replace() if message is not a string for some reason
const message = `${this.message || ''}`;
cmdStr += escapeData(message);
return cmdStr;
}
}
function escapeData(s) {
return s.replace(/\r/g, '%0D').replace(/\n/g, '%0A');
}
function escape(s) {
return s
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/]/g, '%5D')
.replace(/;/g, '%3B');
}
//# sourceMappingURL=command.js.map
@@ -1 +0,0 @@
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":";;AAAA,yBAAwB;AAQxB;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,UAA6B,EAC7B,OAAe;IAEf,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAC/C,CAAC;AAPD,oCAOC;AAED,SAAgB,KAAK,CAAC,IAAY,EAAE,UAAkB,EAAE;IACtD,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACjC,CAAC;AAFD,sBAEC;AAED,MAAM,UAAU,GAAG,IAAI,CAAA;AAEvB,MAAM,OAAO;IAKX,YAAY,OAAe,EAAE,UAA6B,EAAE,OAAe;QACzE,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,GAAG,iBAAiB,CAAA;SAC5B;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,QAAQ;QACN,IAAI,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,OAAO,CAAA;QAEtC,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9D,MAAM,IAAI,GAAG,CAAA;YACb,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE;gBACjC,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;oBACvC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;oBAChC,IAAI,GAAG,EAAE;wBACP,8DAA8D;wBAC9D,6DAA6D;wBAC7D,MAAM,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,IAAI,EAAE,EAAE,CAAC,GAAG,CAAA;qBAC9C;iBACF;aACF;SACF;QAED,MAAM,IAAI,UAAU,CAAA;QAEpB,kEAAkE;QAClE,6DAA6D;QAC7D,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAA;QACvC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;QAE7B,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AACtD,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,CAAC;SACL,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACzB,CAAC"}
@@ -1,112 +0,0 @@
/**
* Interface for getInput options
*/
export interface InputOptions {
/** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */
required?: boolean;
}
/**
* The code to exit an action
*/
export declare enum ExitCode {
/**
* A code indicating that the action was successful
*/
Success = 0,
/**
* A code indicating that the action was a failure
*/
Failure = 1
}
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable
*/
export declare function exportVariable(name: string, val: string): void;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
export declare function setSecret(secret: string): void;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
export declare function addPath(inputPath: string): void;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
export declare function getInput(name: string, options?: InputOptions): string;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store
*/
export declare function setOutput(name: string, value: string): void;
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
export declare function setFailed(message: string): void;
/**
* Writes debug message to user log
* @param message debug message
*/
export declare function debug(message: string): void;
/**
* Adds an error issue
* @param message error issue message
*/
export declare function error(message: string): void;
/**
* Adds an warning issue
* @param message warning issue message
*/
export declare function warning(message: string): void;
/**
* Writes info to log with console.log.
* @param message info message
*/
export declare function info(message: string): void;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
export declare function startGroup(name: string): void;
/**
* End an output group.
*/
export declare function endGroup(): void;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
export declare function group<T>(name: string, fn: () => Promise<T>): Promise<T>;
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store
*/
export declare function saveState(name: string, value: string): void;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
export declare function getState(name: string): string;
-195
View File
@@ -1,195 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("./command");
const os = require("os");
const path = require("path");
/**
* The code to exit an action
*/
var ExitCode;
(function (ExitCode) {
/**
* A code indicating that the action was successful
*/
ExitCode[ExitCode["Success"] = 0] = "Success";
/**
* A code indicating that the action was a failure
*/
ExitCode[ExitCode["Failure"] = 1] = "Failure";
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
/**
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable
*/
function exportVariable(name, val) {
process.env[name] = val;
command_1.issueCommand('set-env', { name }, val);
}
exports.exportVariable = exportVariable;
/**
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
function setSecret(secret) {
command_1.issueCommand('add-mask', {}, secret);
}
exports.setSecret = setSecret;
/**
* Prepends inputPath to the PATH (for this action and future actions)
* @param inputPath
*/
function addPath(inputPath) {
command_1.issueCommand('add-path', {}, inputPath);
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
}
exports.addPath = addPath;
/**
* Gets the value of an input. The value is also trimmed.
*
* @param name name of the input to get
* @param options optional. See InputOptions.
* @returns string
*/
function getInput(name, options) {
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}
exports.getInput = getInput;
/**
* Sets the value of an output.
*
* @param name name of the output to set
* @param value value to store
*/
function setOutput(name, value) {
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
//-----------------------------------------------------------------------
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
* @param message add error issue message
*/
function setFailed(message) {
process.exitCode = ExitCode.Failure;
error(message);
}
exports.setFailed = setFailed;
//-----------------------------------------------------------------------
// Logging Commands
//-----------------------------------------------------------------------
/**
* Writes debug message to user log
* @param message debug message
*/
function debug(message) {
command_1.issueCommand('debug', {}, message);
}
exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message
*/
function error(message) {
command_1.issue('error', message);
}
exports.error = error;
/**
* Adds an warning issue
* @param message warning issue message
*/
function warning(message) {
command_1.issue('warning', message);
}
exports.warning = warning;
/**
* Writes info to log with console.log.
* @param message info message
*/
function info(message) {
process.stdout.write(message + os.EOL);
}
exports.info = info;
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
function startGroup(name) {
command_1.issue('group', name);
}
exports.startGroup = startGroup;
/**
* End an output group.
*/
function endGroup() {
command_1.issue('endgroup');
}
exports.endGroup = endGroup;
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
function group(name, fn) {
return __awaiter(this, void 0, void 0, function* () {
startGroup(name);
let result;
try {
result = yield fn();
}
finally {
endGroup();
}
return result;
});
}
exports.group = group;
//-----------------------------------------------------------------------
// Wrapper action state
//-----------------------------------------------------------------------
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store
*/
function saveState(name, value) {
command_1.issueCommand('save-state', { name }, value);
}
exports.saveState = saveState;
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
function getState(name) {
return process.env[`STATE_${name}`] || '';
}
exports.getState = getState;
//# sourceMappingURL=core.js.map
@@ -1 +0,0 @@
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,uCAA6C;AAE7C,yBAAwB;AACxB,6BAA4B;AAU5B;;GAEG;AACH,IAAY,QAUX;AAVD,WAAY,QAAQ;IAClB;;OAEG;IACH,6CAAW,CAAA;IAEX;;OAEG;IACH,6CAAW,CAAA;AACb,CAAC,EAVW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAUnB;AAED,yEAAyE;AACzE,YAAY;AACZ,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,cAAc,CAAC,IAAY,EAAE,GAAW;IACtD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAA;IACvB,sBAAY,CAAC,SAAS,EAAE,EAAC,IAAI,EAAC,EAAE,GAAG,CAAC,CAAA;AACtC,CAAC;AAHD,wCAGC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,MAAc;IACtC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;AACtC,CAAC;AAFD,8BAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,SAAiB;IACvC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;IACvC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;AAC7E,CAAC;AAHD,0BAGC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CAAC,IAAY,EAAE,OAAsB;IAC3D,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACrE,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAA;KAC5D;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AARD,4BAQC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED,yEAAyE;AACzE,UAAU;AACV,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,SAAS,CAAC,OAAe;IACvC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAA;IACnC,KAAK,CAAC,OAAO,CAAC,CAAA;AAChB,CAAC;AAHD,8BAGC;AAED,yEAAyE;AACzE,mBAAmB;AACnB,yEAAyE;AAEzE;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,sBAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACpC,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,eAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AACzB,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,OAAe;IACrC,eAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;AAC3B,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACxC,CAAC;AAFD,oBAEC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,eAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACtB,CAAC;AAFD,gCAEC;AAED;;GAEG;AACH,SAAgB,QAAQ;IACtB,eAAK,CAAC,UAAU,CAAC,CAAA;AACnB,CAAC;AAFD,4BAEC;AAED;;;;;;;GAOG;AACH,SAAsB,KAAK,CAAI,IAAY,EAAE,EAAoB;;QAC/D,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhB,IAAI,MAAS,CAAA;QAEb,IAAI;YACF,MAAM,GAAG,MAAM,EAAE,EAAE,CAAA;SACpB;gBAAS;YACR,QAAQ,EAAE,CAAA;SACX;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CAAA;AAZD,sBAYC;AAED,yEAAyE;AACzE,uBAAuB;AACvB,yEAAyE;AAEzE;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED;;;;;GAKG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;AAC3C,CAAC;AAFD,4BAEC"}
@@ -1,67 +0,0 @@
{
"_args": [
[
"@actions/core@1.2.0",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
]
],
"_from": "@actions/core@1.2.0",
"_id": "@actions/core@1.2.0",
"_inBundle": false,
"_integrity": "sha512-ZKdyhlSlyz38S6YFfPnyNgCDZuAF2T0Qv5eHflNWytPS8Qjvz39bZFMry9Bb/dpSnqWcNeav5yM2CTYpJeY+Dw==",
"_location": "/@actions/tool-cache/@actions/core",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/core@1.2.0",
"name": "@actions/core",
"escapedName": "@actions%2fcore",
"scope": "@actions",
"rawSpec": "1.2.0",
"saveSpec": null,
"fetchSpec": "1.2.0"
},
"_requiredBy": [
"/@actions/tool-cache"
],
"_resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.0.tgz",
"_spec": "1.2.0",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"description": "Actions core lib",
"devDependencies": {
"@types/node": "^12.0.2"
},
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/core",
"keywords": [
"github",
"actions",
"core"
],
"license": "MIT",
"main": "lib/core.js",
"name": "@actions/core",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/core"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"version": "1.2.0"
}
+20 -17
View File
@@ -1,42 +1,42 @@
{
"_args": [
[
"@actions/tool-cache@1.1.2",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
"@actions/tool-cache@1.6.1",
"."
]
],
"_from": "@actions/tool-cache@1.1.2",
"_id": "@actions/tool-cache@1.1.2",
"_from": "@actions/tool-cache@1.6.1",
"_id": "@actions/tool-cache@1.6.1",
"_inBundle": false,
"_integrity": "sha512-IJczPaZr02ECa3Lgws/TJEVco9tjOujiQSZbO3dHuXXjhd5vrUtfOgGwhmz3/f97L910OraPZ8SknofUk6RvOQ==",
"_integrity": "sha512-F+vwEDwfqcHMKuSkj79pihOnsAMv23EkG76nMpc82UsnXwyQdyEsktGxrB0SNtm7pRqTXEIOoAPTgrSQclXYTg==",
"_location": "/@actions/tool-cache",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@actions/tool-cache@1.1.2",
"raw": "@actions/tool-cache@1.6.1",
"name": "@actions/tool-cache",
"escapedName": "@actions%2ftool-cache",
"scope": "@actions",
"rawSpec": "1.1.2",
"rawSpec": "1.6.1",
"saveSpec": null,
"fetchSpec": "1.1.2"
"fetchSpec": "1.6.1"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.1.2.tgz",
"_spec": "1.1.2",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"_resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.6.1.tgz",
"_spec": "1.6.1",
"_where": ".",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/core": "^1.1.0",
"@actions/exec": "^1.0.1",
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^1.0.8",
"@actions/io": "^1.0.1",
"semver": "^6.1.0",
"typed-rest-client": "^1.4.0",
"uuid": "^3.3.2"
},
"description": "Actions tool-cache lib",
@@ -54,7 +54,7 @@
"lib",
"scripts"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"homepage": "https://github.com/actions/toolkit/tree/main/packages/tool-cache",
"keywords": [
"github",
"actions",
@@ -68,11 +68,14 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/tool-cache"
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"version": "1.1.2"
"types": "lib/tool-cache.d.ts",
"version": "1.6.1"
}
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/node-tunnel.iml" filepath="$PROJECT_DIR$/.idea/node-tunnel.iml" />
</modules>
</component>
</project>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+797
View File
@@ -0,0 +1,797 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="3caed8aa-31ae-4b3d-ad18-6f9796663516" name="Default" comment="">
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.travis.yml" afterPath="$PROJECT_DIR$/.travis.yml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/CHANGELOG.md" afterPath="$PROJECT_DIR$/CHANGELOG.md" />
</list>
<ignored path="$PROJECT_DIR$/.tmp/" />
<ignored path="$PROJECT_DIR$/temp/" />
<ignored path="$PROJECT_DIR$/tmp/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file leaf-file-name="package.json" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="34">
<caret line="2" column="19" lean-forward="false" selection-start-line="2" selection-start-column="19" selection-end-line="2" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="README.md" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="2312">
<caret line="136" column="67" lean-forward="false" selection-start-line="136" selection-start-column="67" selection-end-line="136" selection-end-column="67" />
<folding>
<marker date="1497272379133" expanded="true" signature="590:646" ph="{...}" />
<marker date="1497272379133" expanded="true" signature="601:644" ph="{&quot;host&quot;: 'localhost'...}" />
<marker date="1497272379133" expanded="true" signature="674:737" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="884:1330" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="964:1328" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1103:1192" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1290:1324" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="1357:1419" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="1514:2209" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="1540:1623" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1842:2207" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1981:2070" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2168:2202" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="2237:2300" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="2395:3180" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="2475:3178" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="2615:2704" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2802:2836" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="3207:3269" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="3366:4398" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="3392:3475" ph="//..." />
<marker date="1497272379133" expanded="true" signature="3694:4396" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="3834:3923" ph="//..." />
<marker date="1497272379133" expanded="true" signature="4021:4055" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="4426:4489" ph="{&quot;host&quot;: 'example.com'...}" />
</folding>
</first_editor>
<second_editor />
</state>
</provider>
</entry>
</file>
<file leaf-file-name=".travis.yml" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/.travis.yml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="102">
<caret line="6" column="0" lean-forward="true" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="tunnel.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/tunnel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="697">
<caret line="41" column="19" lean-forward="false" selection-start-line="41" selection-start-column="19" selection-end-line="41" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="http-over-http-error.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/test/http-over-http-error.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="935">
<caret line="55" column="26" lean-forward="true" selection-start-line="55" selection-start-column="26" selection-end-line="55" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="http-over-http-error2.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/test/http-over-http-error2.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1207">
<caret line="71" column="0" lean-forward="false" selection-start-line="71" selection-start-column="0" selection-end-line="71" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="https-over-http.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/test/https-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1479">
<caret line="87" column="0" lean-forward="false" selection-start-line="87" selection-start-column="0" selection-end-line="87" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="https-over-https.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/test/https-over-https.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="http-over-http.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/test/http-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1088">
<caret line="64" column="26" lean-forward="true" selection-start-line="64" selection-start-column="26" selection-end-line="64" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="CHANGELOG.md" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="102">
<caret line="6" column="0" lean-forward="false" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
<folding />
</first_editor>
<second_editor />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JavaScript File" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>max</find>
<find>onconne</find>
</findStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/test/http-over-http-error.js" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/package.json" />
<option value="$PROJECT_DIR$/test/http-over-http-error2.js" />
<option value="$PROJECT_DIR$/test/https-over-http-localaddress.js" />
<option value="$PROJECT_DIR$/test/https-over-http.js" />
<option value="$PROJECT_DIR$/lib/tunnel.js" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/.travis.yml" />
</list>
</option>
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER">
<package-json value="$PROJECT_DIR$/package.json" />
</component>
<component name="JsFlowSettings">
<service-enabled>false</service-enabled>
<exe-path />
<annotation-enable>false</annotation-enable>
<other-services-enabled>false</other-services-enabled>
<auto-save>true</auto-save>
</component>
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="NodeModulesDirectoryManager">
<handled-path value="$PROJECT_DIR$/node_modules" />
</component>
<component name="ProjectFrameBounds">
<option name="x" value="785" />
<option name="y" value="40" />
<option name="width" value="1788" />
<option name="height" value="1407" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource ProjectPane="true" />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="Scratches" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="node-tunnel" type="b2602c69:ProjectViewProjectNode" />
<item name="node-tunnel" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="node-tunnel" type="b2602c69:ProjectViewProjectNode" />
<item name="node-tunnel" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="node-tunnel" type="b2602c69:ProjectViewProjectNode" />
<item name="node-tunnel" type="462c0819:PsiDirectoryNode" />
<item name="test" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="HbShouldOpenHtmlAsHb" value="" />
<property name="nodejs_interpreter_path" value="$PROJECT_DIR$/../../nvmw/v6.10.3/node" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\koichik\git\koichik\node-tunnel\test" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager">
<configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js">
<node-interpreter>project</node-interpreter>
<node-options />
<gulpfile />
<tasks />
<arguments />
<envs />
</configuration>
<configuration default="true" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application">
<method />
</configuration>
<configuration default="true" type="DartTestRunConfigurationType" factoryName="Dart Test">
<method />
</configuration>
<configuration default="true" type="JavaScriptTestRunnerJest" factoryName="Jest">
<node-interpreter value="project" />
<working-dir value="" />
<envs />
<scope-kind value="ALL" />
<method />
</configuration>
<configuration default="true" type="JavaScriptTestRunnerKarma" factoryName="Karma">
<config-file value="" />
<node-interpreter value="project" />
<envs />
<method />
</configuration>
<configuration default="true" type="JavaScriptTestRunnerProtractor" factoryName="Protractor">
<config-file value="" />
<node-interpreter value="project" />
<envs />
<method />
</configuration>
<configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">
<method />
</configuration>
<configuration default="true" type="NodeJSConfigurationType" factoryName="Node.js" path-to-node="project" working-dir="">
<method />
</configuration>
<configuration default="true" type="cucumber.js" factoryName="Cucumber.js">
<option name="cucumberJsArguments" value="" />
<option name="executablePath" />
<option name="filePath" />
<method />
</configuration>
<configuration default="true" type="js.build_tools.npm" factoryName="npm">
<command value="run" />
<scripts />
<node-interpreter value="project" />
<envs />
<method />
</configuration>
<configuration default="true" type="mocha-javascript-test-runner" factoryName="Mocha">
<node-interpreter>project</node-interpreter>
<node-options />
<working-directory />
<pass-parent-env>true</pass-parent-env>
<envs />
<ui />
<extra-mocha-options />
<test-kind>DIRECTORY</test-kind>
<test-directory />
<recursive>false</recursive>
<method />
</configuration>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="3caed8aa-31ae-4b3d-ad18-6f9796663516" name="Default" comment="" />
<created>1497256565348</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1497256565348</updated>
<workItem from="1497256566573" duration="8794000" />
<workItem from="1497272051717" duration="2328000" />
<workItem from="1536577850117" duration="8708000" />
<workItem from="1536636907096" duration="739000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="20569000" />
</component>
<component name="ToolWindowManager">
<frame x="785" y="40" width="1788" height="1407" extended-state="0" />
<editor active="true" />
<layout>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="SvgViewer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.32967034" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="npm" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager />
<watches-manager />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="34">
<caret line="2" column="19" lean-forward="false" selection-start-line="2" selection-start-column="19" selection-end-line="2" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="2312">
<caret line="136" column="67" lean-forward="false" selection-start-line="136" selection-start-column="67" selection-end-line="136" selection-end-column="67" />
<folding>
<marker date="1497272379133" expanded="true" signature="590:646" ph="{...}" />
<marker date="1497272379133" expanded="true" signature="601:644" ph="{&quot;host&quot;: 'localhost'...}" />
<marker date="1497272379133" expanded="true" signature="674:737" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="884:1330" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="964:1328" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1103:1192" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1290:1324" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="1357:1419" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="1514:2209" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="1540:1623" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1842:2207" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1981:2070" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2168:2202" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="2237:2300" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="2395:3180" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="2475:3178" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="2615:2704" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2802:2836" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="3207:3269" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="3366:4398" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="3392:3475" ph="//..." />
<marker date="1497272379133" expanded="true" signature="3694:4396" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="3834:3923" ph="//..." />
<marker date="1497272379133" expanded="true" signature="4021:4055" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="4426:4489" ph="{&quot;host&quot;: 'example.com'...}" />
</folding>
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.travis.yml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="102">
<caret line="6" column="0" lean-forward="true" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http-error.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="935">
<caret line="55" column="26" lean-forward="true" selection-start-line="55" selection-start-column="26" selection-end-line="55" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http-error2.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1207">
<caret line="71" column="0" lean-forward="false" selection-start-line="71" selection-start-column="0" selection-end-line="71" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1479">
<caret line="87" column="0" lean-forward="false" selection-start-line="87" selection-start-column="0" selection-end-line="87" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-https.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1088">
<caret line="64" column="26" lean-forward="true" selection-start-line="64" selection-start-column="26" selection-end-line="64" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/tunnel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="697">
<caret line="41" column="19" lean-forward="false" selection-start-line="41" selection-start-column="19" selection-end-line="41" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="34">
<caret line="2" column="19" lean-forward="false" selection-start-line="2" selection-start-column="19" selection-end-line="2" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="2312">
<caret line="136" column="67" lean-forward="false" selection-start-line="136" selection-start-column="67" selection-end-line="136" selection-end-column="67" />
<folding>
<marker date="1497272379133" expanded="true" signature="590:646" ph="{...}" />
<marker date="1497272379133" expanded="true" signature="601:644" ph="{&quot;host&quot;: 'localhost'...}" />
<marker date="1497272379133" expanded="true" signature="674:737" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="884:1330" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="964:1328" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1103:1192" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1290:1324" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="1357:1419" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="1514:2209" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="1540:1623" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1842:2207" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1981:2070" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2168:2202" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="2237:2300" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="2395:3180" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="2475:3178" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="2615:2704" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2802:2836" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="3207:3269" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="3366:4398" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="3392:3475" ph="//..." />
<marker date="1497272379133" expanded="true" signature="3694:4396" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="3834:3923" ph="//..." />
<marker date="1497272379133" expanded="true" signature="4021:4055" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="4426:4489" ph="{&quot;host&quot;: 'example.com'...}" />
</folding>
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/tunnel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2550">
<caret line="150" column="0" lean-forward="false" selection-start-line="150" selection-start-column="0" selection-end-line="150" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="51">
<caret line="3" column="21" lean-forward="false" selection-start-line="3" selection-start-column="21" selection-end-line="3" selection-end-column="21" />
<folding />
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.travis.yml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="119">
<caret line="7" column="0" lean-forward="true" selection-start-line="7" selection-start-column="0" selection-end-line="7" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<marker date="1497272379133" expanded="true" signature="590:646" ph="{...}" />
<marker date="1497272379133" expanded="true" signature="601:644" ph="{&quot;host&quot;: 'localhost'...}" />
<marker date="1497272379133" expanded="true" signature="674:737" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="884:1330" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="964:1328" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1103:1192" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1290:1324" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="1357:1419" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="1514:2209" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="1540:1623" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1842:2207" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1981:2070" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2168:2202" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="2237:2300" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="2395:3180" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="2475:3178" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="2615:2704" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2802:2836" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="3207:3269" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="3366:4398" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="3392:3475" ph="//..." />
<marker date="1497272379133" expanded="true" signature="3694:4396" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="3834:3923" ph="//..." />
<marker date="1497272379133" expanded="true" signature="4021:4055" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="4426:4489" ph="{&quot;host&quot;: 'example.com'...}" />
</folding>
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.travis.yml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/tunnel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2550">
<caret line="150" column="0" lean-forward="false" selection-start-line="150" selection-start-column="0" selection-end-line="150" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-https.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-https-error.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="136">
<caret line="8" column="0" lean-forward="false" selection-start-line="7" selection-start-column="0" selection-end-line="8" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http-error.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1309">
<caret line="77" column="0" lean-forward="false" selection-start-line="77" selection-start-column="0" selection-end-line="77" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-https.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-https.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-https-error.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="2312">
<caret line="136" column="67" lean-forward="false" selection-start-line="136" selection-start-column="67" selection-end-line="136" selection-end-column="67" />
<folding>
<marker date="1497272379133" expanded="true" signature="590:646" ph="{...}" />
<marker date="1497272379133" expanded="true" signature="601:644" ph="{&quot;host&quot;: 'localhost'...}" />
<marker date="1497272379133" expanded="true" signature="674:737" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="884:1330" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="964:1328" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1103:1192" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1290:1324" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="1357:1419" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="1514:2209" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="1540:1623" ph="//..." />
<marker date="1497272379133" expanded="true" signature="1842:2207" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="1981:2070" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2168:2202" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="2237:2300" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="2395:3180" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="2475:3178" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="2615:2704" ph="//..." />
<marker date="1497272379133" expanded="true" signature="2802:2836" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="3207:3269" ph="{&quot;host&quot;: 'example.com'...}" />
<marker date="1497272379133" expanded="true" signature="3366:4398" ph="{&quot;maxSockets&quot;: poolSize...}" />
<marker date="1497272379133" expanded="true" signature="3392:3475" ph="//..." />
<marker date="1497272379133" expanded="true" signature="3694:4396" ph="{&quot;host&quot;: proxyHost...}" />
<marker date="1497272379133" expanded="true" signature="3834:3923" ph="//..." />
<marker date="1497272379133" expanded="true" signature="4021:4055" ph="{&quot;User-Agent&quot;: 'Node'...}" />
<marker date="1497272379133" expanded="true" signature="4426:4489" ph="{&quot;host&quot;: 'example.com'...}" />
</folding>
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="34">
<caret line="2" column="19" lean-forward="false" selection-start-line="2" selection-start-column="19" selection-end-line="2" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http-error.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="935">
<caret line="55" column="26" lean-forward="true" selection-start-line="55" selection-start-column="26" selection-end-line="55" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http-error2.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1207">
<caret line="71" column="0" lean-forward="false" selection-start-line="71" selection-start-column="0" selection-end-line="71" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-http-localaddress.js" />
<entry file="file://$PROJECT_DIR$/test/https-over-https.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/http-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1088">
<caret line="64" column="26" lean-forward="true" selection-start-line="64" selection-start-column="26" selection-end-line="64" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/https-over-http.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1479">
<caret line="87" column="0" lean-forward="false" selection-start-line="87" selection-start-column="0" selection-end-line="87" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/tunnel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="697">
<caret line="41" column="19" lean-forward="false" selection-start-line="41" selection-start-column="19" selection-end-line="41" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
<state split_layout="SPLIT">
<first_editor relative-caret-position="102">
<caret line="6" column="0" lean-forward="false" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
<folding />
</first_editor>
<second_editor />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.travis.yml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="102">
<caret line="6" column="0" lean-forward="true" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</component>
</project>
-2
View File
@@ -1,2 +0,0 @@
/.idea
/node_modules
+6
View File
@@ -0,0 +1,6 @@
language: node_js
node_js:
- "4"
- "6"
- "8"
- "10"
+9
View File
@@ -1,5 +1,14 @@
# Changelog
- 0.0.6 (2018/09/11)
- Fix `localAddress` not working (#25)
- Fix `Host:` header for CONNECT method by @tmurakam (#29, #30)
- Fix default port for https (#32)
- Fix error handling when the proxy send illegal response body (#33)
- 0.0.5 (2017/06/12)
- Fix socket leak.
- 0.0.4 (2016/01/23)
- supported Node v0.12 or later.
+10 -4
View File
@@ -1,5 +1,9 @@
# node-tunnel - HTTP/HTTPS Agents for tunneling proxies
[![Build Status](https://img.shields.io/travis/koichik/node-tunnel.svg?style=flat)](https://travis-ci.org/koichik/node-tunnel)
[![Dependency Status](http://img.shields.io/david/koichik/node-tunnel.svg?style=flat)](https://david-dm.org/koichik/node-tunnel#info=dependencies)
[![DevDependency Status](http://img.shields.io/david/dev/koichik/node-tunnel.svg?style=flat)](https://david-dm.org/koichik/node-tunnel#info=devDependencies)
## Example
```javascript
@@ -29,7 +33,7 @@ var req = https.request({
```javascript
var tunnelingAgent = tunnel.httpOverHttp({
maxSockets: poolSize, // Defaults to 5
maxSockets: poolSize, // Defaults to http.Agent.defaultMaxSockets
proxy: { // Proxy settings
host: proxyHost, // Defaults to 'localhost'
@@ -57,7 +61,7 @@ var req = http.request({
```javascript
var tunnelingAgent = tunnel.httpsOverHttp({
maxSockets: poolSize, // Defaults to 5
maxSockets: poolSize, // Defaults to http.Agent.defaultMaxSockets
// CA for origin server if necessary
ca: [ fs.readFileSync('origin-server-ca.pem')],
@@ -92,7 +96,7 @@ var req = https.request({
```javascript
var tunnelingAgent = tunnel.httpOverHttps({
maxSockets: poolSize, // Defaults to 5
maxSockets: poolSize, // Defaults to http.Agent.defaultMaxSockets
proxy: { // Proxy settings
host: proxyHost, // Defaults to 'localhost'
@@ -130,7 +134,7 @@ var req = http.request({
```javascript
var tunnelingAgent = tunnel.httpsOverHttps({
maxSockets: poolSize, // Defaults to 5
maxSockets: poolSize, // Defaults to http.Agent.defaultMaxSockets
// CA for origin server if necessary
ca: [ fs.readFileSync('origin-server-ca.pem')],
@@ -173,6 +177,8 @@ var req = https.request({
## CONTRIBUTORS
* [Aleksis Brezas (abresas)](https://github.com/abresas)
* [Jackson Tian (JacksonTian)](https://github.com/JacksonTian)
* [Dmitry Sorin (1999)](https://github.com/1999)
## License
+26 -9
View File
@@ -25,6 +25,7 @@ function httpsOverHttp(options) {
var agent = new TunnelingAgent(options);
agent.request = http.request;
agent.createSocket = createSecureSocket;
agent.defaultPort = 443;
return agent;
}
@@ -38,6 +39,7 @@ function httpsOverHttps(options) {
var agent = new TunnelingAgent(options);
agent.request = https.request;
agent.createSocket = createSecureSocket;
agent.defaultPort = 443;
return agent;
}
@@ -106,8 +108,14 @@ TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
var connectOptions = mergeOptions({}, self.proxyOptions, {
method: 'CONNECT',
path: options.host + ':' + options.port,
agent: false
agent: false,
headers: {
host: options.host + ':' + options.port
}
});
if (options.localAddress) {
connectOptions.localAddress = options.localAddress;
}
if (connectOptions.proxyAuth) {
connectOptions.headers = connectOptions.headers || {};
connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
@@ -139,20 +147,29 @@ TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
connectReq.removeAllListeners();
socket.removeAllListeners();
if (res.statusCode === 200) {
assert.equal(head.length, 0);
debug('tunneling connection has established');
self.sockets[self.sockets.indexOf(placeholder)] = socket;
cb(socket);
} else {
if (res.statusCode !== 200) {
debug('tunneling socket could not be established, statusCode=%d',
res.statusCode);
res.statusCode);
socket.destroy();
var error = new Error('tunneling socket could not be established, ' +
'statusCode=' + res.statusCode);
'statusCode=' + res.statusCode);
error.code = 'ECONNRESET';
options.request.emit('error', error);
self.removeSocket(placeholder);
return;
}
if (head.length > 0) {
debug('got illegal response body from proxy');
socket.destroy();
var error = new Error('got illegal response body from proxy');
error.code = 'ECONNRESET';
options.request.emit('error', error);
self.removeSocket(placeholder);
return;
}
debug('tunneling connection has established');
self.sockets[self.sockets.indexOf(placeholder)] = socket;
return cb(socket);
}
function onError(cause) {
+16 -16
View File
@@ -1,32 +1,32 @@
{
"_args": [
[
"tunnel@0.0.4",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
"tunnel@0.0.6",
"."
]
],
"_from": "tunnel@0.0.4",
"_id": "tunnel@0.0.4",
"_from": "tunnel@0.0.6",
"_id": "tunnel@0.0.6",
"_inBundle": false,
"_integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=",
"_integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"_location": "/tunnel",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "tunnel@0.0.4",
"raw": "tunnel@0.0.6",
"name": "tunnel",
"escapedName": "tunnel",
"rawSpec": "0.0.4",
"rawSpec": "0.0.6",
"saveSpec": null,
"fetchSpec": "0.0.4"
"fetchSpec": "0.0.6"
},
"_requiredBy": [
"/typed-rest-client"
"/@actions/http-client"
],
"_resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz",
"_spec": "0.0.4",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"_resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"_spec": "0.0.6",
"_where": ".",
"author": {
"name": "Koichi Kobayashi",
"email": "koichik@improvement.jp"
@@ -36,8 +36,8 @@
},
"description": "Node HTTP/HTTPS Agents for tunneling proxies",
"devDependencies": {
"mocha": "*",
"should": "*"
"mocha": "^5.2.0",
"should": "^13.2.3"
},
"directories": {
"lib": "./lib"
@@ -61,7 +61,7 @@
"url": "git+https://github.com/koichik/node-tunnel.git"
},
"scripts": {
"test": "./node_modules/mocha/bin/mocha"
"test": "mocha"
},
"version": "0.0.4"
"version": "0.0.6"
}
-108
View File
@@ -1,108 +0,0 @@
var http = require('http');
var net = require('net');
var should = require('should');
var tunnel = require('../index');
describe('HTTP over HTTP', function() {
it('should finish without error', function(done) {
var serverPort = 3000;
var proxyPort = 3001;
var poolSize = 3;
var N = 10;
var serverConnect = 0;
var proxyConnect = 0;
var clientConnect = 0;
var server;
var proxy;
var agent;
server = http.createServer(function(req, res) {
tunnel.debug('SERVER: got request');
++serverConnect;
res.writeHead(200);
res.end('Hello' + req.url);
tunnel.debug('SERVER: sending response');
});
server.listen(serverPort, setupProxy);
function setupProxy() {
proxy = http.createServer(function(req, res) {
should.fail();
});
proxy.on('upgrade', onConnect); // for v0.6
proxy.on('connect', onConnect); // for v0.7 or later
function onConnect(req, clientSocket, head) {
tunnel.debug('PROXY: got CONNECT request');
req.method.should.equal('CONNECT');
req.url.should.equal('localhost:' + serverPort);
req.headers.should.not.have.property('transfer-encoding');
req.headers.should.have.property('proxy-authorization',
'Basic ' + new Buffer('user:password').toString('base64'));
++proxyConnect;
tunnel.debug('PROXY: creating a tunnel');
var serverSocket = net.connect(serverPort, function() {
tunnel.debug('PROXY: replying to client CONNECT request');
clientSocket.write('HTTP/1.1 200 Connection established\r\n\r\n');
clientSocket.pipe(serverSocket);
serverSocket.write(head);
serverSocket.pipe(clientSocket);
// workaround, see joyent/node#2524
serverSocket.on('end', function() {
clientSocket.end();
});
});
}
proxy.listen(proxyPort, setupClient);
}
function setupClient() {
agent = tunnel.httpOverHttp({
maxSockets: poolSize,
proxy: {
port: proxyPort,
proxyAuth: 'user:password'
}
});
for (var i = 0; i < N; ++i) {
doClientRequest(i);
}
function doClientRequest(i) {
tunnel.debug('CLIENT: Making HTTP request (%d)', i);
var req = http.get({
port: serverPort,
path: '/' + i,
agent: agent
}, function(res) {
tunnel.debug('CLIENT: got HTTP response (%d)', i);
res.setEncoding('utf8');
res.on('data', function(data) {
data.should.equal('Hello/' + i);
});
res.on('end', function() {
++clientConnect;
if (clientConnect === N) {
proxy.close();
server.close();
}
});
});
}
}
server.on('close', function() {
serverConnect.should.equal(N);
proxyConnect.should.equal(poolSize);
clientConnect.should.equal(N);
agent.sockets.should.be.empty;
agent.requests.should.be.empty;
done();
});
});
});
-130
View File
@@ -1,130 +0,0 @@
var http = require('http');
var https = require('https');
var net = require('net');
var fs = require('fs');
var path = require('path');
var should = require('should');
var tunnel = require('../index');
function readPem(file) {
return fs.readFileSync(path.join('test/keys', file + '.pem'));
}
var proxyKey = readPem('proxy1-key');
var proxyCert = readPem('proxy1-cert');
var proxyCA = readPem('ca2-cert');
var clientKey = readPem('client1-key');
var clientCert = readPem('client1-cert');
var clientCA = readPem('ca3-cert');
describe('HTTP over HTTPS', function() {
it('should finish without error', function(done) {
var serverPort = 3004;
var proxyPort = 3005;
var poolSize = 3;
var N = 10;
var serverConnect = 0;
var proxyConnect = 0;
var clientConnect = 0;
var server;
var proxy;
var agent;
server = http.createServer(function(req, res) {
tunnel.debug('SERVER: got request');
++serverConnect;
res.writeHead(200);
res.end('Hello' + req.url);
tunnel.debug('SERVER: sending response');
});
server.listen(serverPort, setupProxy);
function setupProxy() {
proxy = https.createServer({
key: proxyKey,
cert: proxyCert,
ca: [clientCA],
requestCert: true,
rejectUnauthorized: true
}, function(req, res) {
should.fail();
});
proxy.on('upgrade', onConnect); // for v0.6
proxy.on('connect', onConnect); // for v0.7 or later
function onConnect(req, clientSocket, head) {
tunnel.debug('PROXY: got CONNECT request');
req.method.should.equal('CONNECT');
req.url.should.equal('localhost:' + serverPort);
req.headers.should.not.have.property('transfer-encoding');
++proxyConnect;
tunnel.debug('PROXY: creating a tunnel');
var serverSocket = net.connect(serverPort, function() {
tunnel.debug('PROXY: replying to client CONNECT request');
clientSocket.write('HTTP/1.1 200 Connection established\r\n\r\n');
clientSocket.pipe(serverSocket);
serverSocket.write(head);
serverSocket.pipe(clientSocket);
// workaround, see joyent/node#2524
serverSocket.on('end', function() {
clientSocket.end();
});
});
}
proxy.listen(proxyPort, setupClient);
}
function setupClient() {
agent = tunnel.httpOverHttps({
maxSockets: poolSize,
proxy: {
port: proxyPort,
key: clientKey,
cert: clientCert,
ca: [proxyCA],
rejectUnauthorized: true
}
});
for (var i = 0; i < N; ++i) {
doClientRequest(i);
}
function doClientRequest(i) {
tunnel.debug('CLIENT: Making HTTP request (%d)', i);
var req = http.get({
port: serverPort,
path: '/' + i,
agent: agent
}, function(res) {
tunnel.debug('CLIENT: got HTTP response (%d)', i);
res.setEncoding('utf8');
res.on('data', function(data) {
data.should.equal('Hello/' + i);
});
res.on('end', function() {
++clientConnect;
if (clientConnect === N) {
proxy.close();
server.close();
}
});
});
}
}
server.on('close', function() {
serverConnect.should.equal(N);
proxyConnect.should.equal(poolSize);
clientConnect.should.equal(N);
var name = 'localhost:' + serverPort;
agent.sockets.should.be.empty;
agent.requests.should.be.empty;
done();
});
});
});
-130
View File
@@ -1,130 +0,0 @@
var http = require('http');
var https = require('https');
var net = require('net');
var fs = require('fs');
var path = require('path');
var should = require('should');
var tunnel = require('../index');
function readPem(file) {
return fs.readFileSync(path.join('test/keys', file + '.pem'));
}
var serverKey = readPem('server1-key');
var serverCert = readPem('server1-cert');
var serverCA = readPem('ca1-cert');
var clientKey = readPem('client1-key');
var clientCert = readPem('client1-cert');
var clientCA = readPem('ca3-cert');
describe('HTTPS over HTTP', function() {
it('should finish without error', function(done) {
var serverPort = 3002;
var proxyPort = 3003;
var poolSize = 3;
var N = 10;
var serverConnect = 0;
var proxyConnect = 0;
var clientConnect = 0;
var server;
var proxy;
var agent;
server = https.createServer({
key: serverKey,
cert: serverCert,
ca: [clientCA],
requestCert: true,
rejectUnauthorized: true
}, function(req, res) {
tunnel.debug('SERVER: got request');
++serverConnect;
res.writeHead(200);
res.end('Hello' + req.url);
tunnel.debug('SERVER: sending response');
});
server.listen(serverPort, setupProxy);
function setupProxy() {
proxy = http.createServer(function(req, res) {
should.fail();
});
proxy.on('upgrade', onConnect); // for v0.6
proxy.on('connect', onConnect); // for v0.7 or later
function onConnect(req, clientSocket, head) {
tunnel.debug('PROXY: got CONNECT request');
req.method.should.equal('CONNECT');
req.url.should.equal('localhost:' + serverPort);
req.headers.should.not.have.property('transfer-encoding');
++proxyConnect;
var serverSocket = net.connect(serverPort, function() {
tunnel.debug('PROXY: replying to client CONNECT request');
clientSocket.write('HTTP/1.1 200 Connection established\r\n\r\n');
clientSocket.pipe(serverSocket);
serverSocket.write(head);
serverSocket.pipe(clientSocket);
// workaround, see joyent/node#2524
serverSocket.on('end', function() {
clientSocket.end();
});
});
}
proxy.listen(proxyPort, setupClient);
}
function setupClient() {
agent = tunnel.httpsOverHttp({
maxSockets: poolSize,
key: clientKey,
cert: clientCert,
ca: [serverCA],
rejectUnauthorized: true,
proxy: {
port: proxyPort
}
});
for (var i = 0; i < N; ++i) {
doClientRequest(i);
}
function doClientRequest(i) {
tunnel.debug('CLIENT: Making HTTPS request (%d)', i);
var req = https.get({
port: serverPort,
path: '/' + i,
agent: agent
}, function(res) {
tunnel.debug('CLIENT: got HTTPS response (%d)', i);
res.setEncoding('utf8');
res.on('data', function(data) {
data.should.equal('Hello/' + i);
});
res.on('end', function() {
++clientConnect;
if (clientConnect === N) {
proxy.close();
server.close();
}
});
});
}
}
server.on('close', function() {
serverConnect.should.equal(N);
proxyConnect.should.equal(poolSize);
clientConnect.should.equal(N);
var name = 'localhost:' + serverPort;
agent.sockets.should.be.empty;
agent.requests.should.be.empty;
done();
});
});
});
-261
View File
@@ -1,261 +0,0 @@
var http = require('http');
var https = require('https');
var net = require('net');
var fs = require('fs');
var path = require('path');
var should = require('should');
var tunnel = require('../index');
function readPem(file) {
return fs.readFileSync(path.join('test/keys', file + '.pem'));
}
var serverKey = readPem('server2-key');
var serverCert = readPem('server2-cert');
var serverCA = readPem('ca1-cert');
var proxyKey = readPem('proxy2-key');
var proxyCert = readPem('proxy2-cert');
var proxyCA = readPem('ca2-cert');
var client1Key = readPem('client1-key');
var client1Cert = readPem('client1-cert');
var client1CA = readPem('ca3-cert');
var client2Key = readPem('client2-key');
var client2Cert = readPem('client2-cert');
var client2CA = readPem('ca4-cert');
describe('HTTPS over HTTPS authentication failed', function() {
it('should finish without error', function(done) {
var serverPort = 3008;
var proxyPort = 3009;
var serverConnect = 0;
var proxyConnect = 0;
var clientRequest = 0;
var clientConnect = 0;
var clientError = 0;
var server;
var proxy;
server = https.createServer({
key: serverKey,
cert: serverCert,
ca: [client1CA],
requestCert: true,
rejectUnauthorized: true
}, function(req, res) {
tunnel.debug('SERVER: got request', req.url);
++serverConnect;
req.on('data', function(data) {
});
req.on('end', function() {
res.writeHead(200);
res.end('Hello, ' + serverConnect);
tunnel.debug('SERVER: sending response');
});
req.resume();
});
//server.addContext('server2', {
// key: serverKey,
// cert: serverCert,
// ca: [client1CA],
//});
server.listen(serverPort, setupProxy);
function setupProxy() {
proxy = https.createServer({
key: proxyKey,
cert: proxyCert,
ca: [client2CA],
requestCert: true,
rejectUnauthorized: true
}, function(req, res) {
should.fail();
});
//proxy.addContext('proxy2', {
// key: proxyKey,
// cert: proxyCert,
// ca: [client2CA],
//});
proxy.on('upgrade', onConnect); // for v0.6
proxy.on('connect', onConnect); // for v0.7 or later
function onConnect(req, clientSocket, head) {
req.method.should.equal('CONNECT');
req.url.should.equal('localhost:' + serverPort);
req.headers.should.not.have.property('transfer-encoding');
++proxyConnect;
var serverSocket = net.connect(serverPort, function() {
tunnel.debug('PROXY: replying to client CONNECT request');
clientSocket.write('HTTP/1.1 200 Connection established\r\n\r\n');
clientSocket.pipe(serverSocket);
serverSocket.write(head);
serverSocket.pipe(clientSocket);
// workaround, see #2524
serverSocket.on('end', function() {
clientSocket.end();
});
});
}
proxy.listen(proxyPort, setupClient);
}
function setupClient() {
function doRequest(name, options, host) {
tunnel.debug('CLIENT: Making HTTPS request (%s)', name);
++clientRequest;
var agent = tunnel.httpsOverHttps(options);
var req = https.get({
host: 'localhost',
port: serverPort,
path: '/' + encodeURIComponent(name),
headers: {
host: host ? host : 'localhost',
},
rejectUnauthorized: true,
agent: agent
}, function(res) {
tunnel.debug('CLIENT: got HTTPS response (%s)', name);
++clientConnect;
res.on('data', function(data) {
});
res.on('end', function() {
req.emit('finish');
});
res.resume();
});
req.on('error', function(err) {
tunnel.debug('CLIENT: failed HTTP response (%s)', name, err);
++clientError;
req.emit('finish');
});
req.on('finish', function() {
if (clientConnect + clientError === clientRequest) {
proxy.close();
server.close();
}
});
}
doRequest('no cert origin nor proxy', { // invalid
maxSockets: 1,
ca: [serverCA],
rejectUnauthorized: true,
// no certificate for origin server
proxy: {
port: proxyPort,
ca: [proxyCA],
rejectUnauthorized: true,
headers: {
host: 'proxy2'
}
// no certificate for proxy
}
}, 'server2');
doRequest('no cert proxy', { // invalid
maxSockets: 1,
ca: [serverCA],
rejectUnauthorized: true,
// client certification for origin server
key: client1Key,
cert: client1Cert,
proxy: {
port: proxyPort,
ca: [proxyCA],
rejectUnauthorized: true,
headers: {
host: 'proxy2'
}
// no certificate for proxy
}
}, 'server2');
doRequest('no cert origin', { // invalid
maxSockets: 1,
ca: [serverCA],
rejectUnauthorized: true,
// no certificate for origin server
proxy: {
port: proxyPort,
servername: 'proxy2',
ca: [proxyCA],
rejectUnauthorized: true,
headers: {
host: 'proxy2'
},
// client certification for proxy
key: client2Key,
cert: client2Cert
}
}, 'server2');
doRequest('invalid proxy server name', { // invalid
maxSockets: 1,
ca: [serverCA],
rejectUnauthorized: true,
// client certification for origin server
key: client1Key,
cert: client1Cert,
proxy: {
port: proxyPort,
ca: [proxyCA],
rejectUnauthorized: true,
// client certification for proxy
key: client2Key,
cert: client2Cert,
}
}, 'server2');
doRequest('invalid origin server name', { // invalid
maxSockets: 1,
ca: [serverCA],
rejectUnauthorized: true,
// client certification for origin server
key: client1Key,
cert: client1Cert,
proxy: {
port: proxyPort,
servername: 'proxy2',
ca: [proxyCA],
rejectUnauthorized: true,
headers: {
host: 'proxy2'
},
// client certification for proxy
key: client2Key,
cert: client2Cert
}
});
doRequest('valid', { // valid
maxSockets: 1,
ca: [serverCA],
rejectUnauthorized: true,
// client certification for origin server
key: client1Key,
cert: client1Cert,
proxy: {
port: proxyPort,
servername: 'proxy2',
ca: [proxyCA],
rejectUnauthorized: true,
headers: {
host: 'proxy2'
},
// client certification for proxy
key: client2Key,
cert: client2Cert
}
}, 'server2');
}
server.on('close', function() {
serverConnect.should.equal(1);
proxyConnect.should.equal(3);
clientConnect.should.equal(1);
clientError.should.equal(5);
done();
});
});
});
-146
View File
@@ -1,146 +0,0 @@
var http = require('http');
var https = require('https');
var net = require('net');
var fs = require('fs');
var path = require('path');
var should = require('should');
var tunnel = require('../index.js');
function readPem(file) {
return fs.readFileSync(path.join('test/keys', file + '.pem'));
}
var serverKey = readPem('server1-key');
var serverCert = readPem('server1-cert');
var serverCA = readPem('ca1-cert');
var proxyKey = readPem('proxy1-key');
var proxyCert = readPem('proxy1-cert');
var proxyCA = readPem('ca2-cert');
var client1Key = readPem('client1-key');
var client1Cert = readPem('client1-cert');
var client1CA = readPem('ca3-cert');
var client2Key = readPem('client2-key');
var client2Cert = readPem('client2-cert');
var client2CA = readPem('ca4-cert');
describe('HTTPS over HTTPS', function() {
it('should finish without error', function(done) {
var serverPort = 3006;
var proxyPort = 3007;
var poolSize = 3;
var N = 5;
var serverConnect = 0;
var proxyConnect = 0;
var clientConnect = 0;
var server;
var proxy;
var agent;
server = https.createServer({
key: serverKey,
cert: serverCert,
ca: [client1CA],
requestCert: true,
rejectUnauthorized: true
}, function(req, res) {
tunnel.debug('SERVER: got request');
++serverConnect;
res.writeHead(200);
res.end('Hello' + req.url);
tunnel.debug('SERVER: sending response');
});
server.listen(serverPort, setupProxy);
function setupProxy() {
proxy = https.createServer({
key: proxyKey,
cert: proxyCert,
ca: [client2CA],
requestCert: true,
rejectUnauthorized: true
}, function(req, res) {
should.fail();
});
proxy.on('upgrade', onConnect); // for v0.6
proxy.on('connect', onConnect); // for v0.7 or later
function onConnect(req, clientSocket, head) {
tunnel.debug('PROXY: got CONNECT request');
req.method.should.equal('CONNECT');
req.url.should.equal('localhost:' + serverPort);
req.headers.should.not.have.property('transfer-encoding');
++proxyConnect;
var serverSocket = net.connect(serverPort, function() {
tunnel.debug('PROXY: replying to client CONNECT request');
clientSocket.write('HTTP/1.1 200 Connection established\r\n\r\n');
clientSocket.pipe(serverSocket);
serverSocket.write(head);
serverSocket.pipe(clientSocket);
// workaround, see joyent/node#2524
serverSocket.on('end', function() {
clientSocket.end();
});
});
}
proxy.listen(proxyPort, setupClient);
}
function setupClient() {
agent = tunnel.httpsOverHttps({
maxSockets: poolSize,
// client certification for origin server
key: client1Key,
cert: client1Cert,
ca: [serverCA],
rejectUnauthroized: true,
proxy: {
port: proxyPort,
// client certification for proxy
key: client2Key,
cert: client2Cert,
ca: [proxyCA],
rejectUnauthroized: true
}
});
for (var i = 0; i < N; ++i) {
doClientRequest(i);
}
function doClientRequest(i) {
tunnel.debug('CLIENT: Making HTTPS request (%d)', i);
var req = https.get({
port: serverPort,
path: '/' + i,
agent: agent
}, function(res) {
tunnel.debug('CLIENT: got HTTPS response (%d)', i);
res.setEncoding('utf8');
res.on('data', function(data) {
data.should.equal('Hello/' + i);
});
res.on('end', function() {
++clientConnect;
if (clientConnect === N) {
proxy.close();
server.close();
}
});
});
}
}
server.on('close', function() {
serverConnect.should.equal(N);
proxyConnect.should.equal(poolSize);
clientConnect.should.equal(N);
var name = 'localhost:' + serverPort;
agent.sockets.should.be.empty;
agent.requests.should.be.empty;
done();
});
});
});
-157
View File
@@ -1,157 +0,0 @@
all: server1-cert.pem server2-cert.pem proxy1-cert.pem proxy2-cert.pem client1-cert.pem client2-cert.pem
#
# Create Certificate Authority: ca1
# ('password' is used for the CA password.)
#
ca1-cert.pem: ca1.cnf
openssl req -new -x509 -days 9999 -config ca1.cnf -keyout ca1-key.pem -out ca1-cert.pem
#
# Create Certificate Authority: ca2
# ('password' is used for the CA password.)
#
ca2-cert.pem: ca2.cnf
openssl req -new -x509 -days 9999 -config ca2.cnf -keyout ca2-key.pem -out ca2-cert.pem
#
# Create Certificate Authority: ca3
# ('password' is used for the CA password.)
#
ca3-cert.pem: ca3.cnf
openssl req -new -x509 -days 9999 -config ca3.cnf -keyout ca3-key.pem -out ca3-cert.pem
#
# Create Certificate Authority: ca4
# ('password' is used for the CA password.)
#
ca4-cert.pem: ca4.cnf
openssl req -new -x509 -days 9999 -config ca4.cnf -keyout ca4-key.pem -out ca4-cert.pem
#
# server1 is signed by ca1.
#
server1-key.pem:
openssl genrsa -out server1-key.pem 1024
server1-csr.pem: server1.cnf server1-key.pem
openssl req -new -config server1.cnf -key server1-key.pem -out server1-csr.pem
server1-cert.pem: server1-csr.pem ca1-cert.pem ca1-key.pem
openssl x509 -req \
-days 9999 \
-passin "pass:password" \
-in server1-csr.pem \
-CA ca1-cert.pem \
-CAkey ca1-key.pem \
-CAcreateserial \
-out server1-cert.pem
#
# server2 is signed by ca1.
#
server2-key.pem:
openssl genrsa -out server2-key.pem 1024
server2-csr.pem: server2.cnf server2-key.pem
openssl req -new -config server2.cnf -key server2-key.pem -out server2-csr.pem
server2-cert.pem: server2-csr.pem ca1-cert.pem ca1-key.pem
openssl x509 -req \
-days 9999 \
-passin "pass:password" \
-in server2-csr.pem \
-CA ca1-cert.pem \
-CAkey ca1-key.pem \
-CAcreateserial \
-out server2-cert.pem
server2-verify: server2-cert.pem ca1-cert.pem
openssl verify -CAfile ca1-cert.pem server2-cert.pem
#
# proxy1 is signed by ca2.
#
proxy1-key.pem:
openssl genrsa -out proxy1-key.pem 1024
proxy1-csr.pem: proxy1.cnf proxy1-key.pem
openssl req -new -config proxy1.cnf -key proxy1-key.pem -out proxy1-csr.pem
proxy1-cert.pem: proxy1-csr.pem ca2-cert.pem ca2-key.pem
openssl x509 -req \
-days 9999 \
-passin "pass:password" \
-in proxy1-csr.pem \
-CA ca2-cert.pem \
-CAkey ca2-key.pem \
-CAcreateserial \
-out proxy1-cert.pem
#
# proxy2 is signed by ca2.
#
proxy2-key.pem:
openssl genrsa -out proxy2-key.pem 1024
proxy2-csr.pem: proxy2.cnf proxy2-key.pem
openssl req -new -config proxy2.cnf -key proxy2-key.pem -out proxy2-csr.pem
proxy2-cert.pem: proxy2-csr.pem ca2-cert.pem ca2-key.pem
openssl x509 -req \
-days 9999 \
-passin "pass:password" \
-in proxy2-csr.pem \
-CA ca2-cert.pem \
-CAkey ca2-key.pem \
-CAcreateserial \
-out proxy2-cert.pem
proxy2-verify: proxy2-cert.pem ca2-cert.pem
openssl verify -CAfile ca2-cert.pem proxy2-cert.pem
#
# client1 is signed by ca3.
#
client1-key.pem:
openssl genrsa -out client1-key.pem 1024
client1-csr.pem: client1.cnf client1-key.pem
openssl req -new -config client1.cnf -key client1-key.pem -out client1-csr.pem
client1-cert.pem: client1-csr.pem ca3-cert.pem ca3-key.pem
openssl x509 -req \
-days 9999 \
-passin "pass:password" \
-in client1-csr.pem \
-CA ca3-cert.pem \
-CAkey ca3-key.pem \
-CAcreateserial \
-out client1-cert.pem
#
# client2 is signed by ca4.
#
client2-key.pem:
openssl genrsa -out client2-key.pem 1024
client2-csr.pem: client2.cnf client2-key.pem
openssl req -new -config client2.cnf -key client2-key.pem -out client2-csr.pem
client2-cert.pem: client2-csr.pem ca4-cert.pem ca4-key.pem
openssl x509 -req \
-days 9999 \
-passin "pass:password" \
-in client2-csr.pem \
-CA ca4-cert.pem \
-CAkey ca4-key.pem \
-CAcreateserial \
-out client2-cert.pem
clean:
rm -f *.pem *.srl
test: client-verify server2-verify proxy1-verify proxy2-verify client-verify
-14
View File
@@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICKjCCAZMCCQDQ8o4kHKdCPDANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV
UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA
dGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEyWjB9
MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK
EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MTEgMB4G
CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEFAANL
ADBIAkEAnzpAqcoXZxWJz/WFK7BXwD23jlREyG11x7gkydteHvn6PrVBbB5yfu6c
bk8w3/Ar608AcyMQ9vHjkLQKH7cjEQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAKha
HqjCfTIut+m/idKy3AoFh48tBHo3p9Nl5uBjQJmahKdZAaiksL24Pl+NzPQ8LIU+
FyDHFp6OeJKN6HzZ72Bh9wpBVu6Uj1hwhZhincyTXT80wtSI/BoUAW8Ls2kwPdus
64LsJhhxqj2m4vPKNRbHB2QxnNrGi30CUf3kt3Ia
-----END CERTIFICATE-----
-10
View File
@@ -1,10 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH
EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD
EwZhZ2VudDExIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ
KoZIhvcNAQEBBQADSwAwSAJBAJ86QKnKF2cVic/1hSuwV8A9t45URMhtdce4JMnb
Xh75+j61QWwecn7unG5PMN/wK+tPAHMjEPbx45C0Ch+3IxECAwEAAaAlMCMGCSqG
SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB
AF+AfG64hNyYHum46m6i7RgnUBrJSOynGjs23TekV4he3QdMSAAPPqbll8W14+y3
vOo7/yQ2v2uTqxCjakUNPPs=
-----END CERTIFICATE REQUEST-----
-9
View File
@@ -1,9 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAJ86QKnKF2cVic/1hSuwV8A9t45URMhtdce4JMnbXh75+j61QWwe
cn7unG5PMN/wK+tPAHMjEPbx45C0Ch+3IxECAwEAAQJBAI2cU1IuR+4IO87WPyAB
76kruoo87AeNQkjjvuQ/00+b/6IS45mcEP5Kw0NukbqBhIw2di9uQ9J51DJ/ZfQr
+YECIQDUHaN3ZjIdJ7/w8Yq9Zzz+3kY2F/xEz6e4ftOFW8bY2QIhAMAref+WYckC
oECgOLAvAxB1lI4j7oCbAaawfxKdnPj5AiEAi95rXx09aGpAsBGmSdScrPdG1v6j
83/2ebrvoZ1uFqkCIB0AssnrRVjUB6GZTNTyU3ERfdkx/RX1zvr8WkFR/lXpAiB7
cUZ1i8ZkZrPrdVgw2cb28UJM7qZHQnXcMHTXFFvxeQ==
-----END RSA PRIVATE KEY-----
-19
View File
@@ -1,19 +0,0 @@
[ req ]
default_bits = 1024
days = 999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
[ req_distinguished_name ]
C = US
ST = CA
L = SF
O = Joyent
OU = Node.js
CN = agent1
emailAddress = ry@tinyclouds.org
[ req_attributes ]
challengePassword = A challenge password
-13
View File
@@ -1,13 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV
UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR
cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy
WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD
VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg
MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF
AANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC
WKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA
C8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9
1LHwrmh29rK8kBPEjmymCQ==
-----END CERTIFICATE-----
-10
View File
@@ -1,10 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH
EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD
EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ
KoZIhvcNAQEBBQADSwAwSAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf
+6fVgdpVhYg5QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAaAlMCMGCSqG
SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB
AJnll2pt5l0pzskQSpjjLVTlFDFmJr/AZ3UK8v0WxBjYjCe5Jx4YehkChpxIyDUm
U3J9q9MDUf0+Y2+EGkssFfk=
-----END CERTIFICATE REQUEST-----
-9
View File
@@ -1,9 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5
QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH
9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p
OHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf
WRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb
AFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa
cgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I
-----END RSA PRIVATE KEY-----
-19
View File
@@ -1,19 +0,0 @@
[ req ]
default_bits = 1024
days = 999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
[ req_distinguished_name ]
C = US
ST = CA
L = SF
O = Joyent
OU = Node.js
CN = agent2
emailAddress = ry@tinyclouds.org
[ req_attributes ]
challengePassword = A challenge password
-14
View File
@@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICKjCCAZMCCQCDBr594bsJmTANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV
UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMjEgMB4GCSqGSIb3DQEJARYRcnlA
dGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEyWjB9
MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK
EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MzEgMB4G
CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEFAANL
ADBIAkEAtlNDZ+bHeBI0B2gD/IWqA7Aq1hwsnS4+XpnLesjTQcL2JwFFpkR0oWrw
yjrYhCogi7c5gjKrLZF1d2JD5JgHgQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJoK
bXwsImk7vJz9649yrmsXwnuGbEKVYMvqcGyjaZNP9lYEG41y5CeRzxhWy2rlYdhE
f2nqE2lg75oJP7LQqfQY7aCqwahM3q/GQbsfKVCGjF7TVyq9TQzd8iW+FEJIQzSE
3aN85hR67+3VAXeSzmkGSVBO2m1SJIug4qftIkc2
-----END CERTIFICATE-----

Some files were not shown because too many files have changed in this diff Show More