├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ ├── check_url.yml │ ├── main.yml │ ├── self_check.yml │ └── unit_test.yml ├── .gitignore ├── .node-version ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── NOTES.md ├── README.md ├── action.yml ├── dist ├── LICENCE └── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── constants.ts ├── installer.ts └── setup-babashka.ts ├── test ├── install-url.test.ts └── setup-babashka.test.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@typescript-eslint" 4 | ], 5 | "extends": [ 6 | "plugin:github/recommended" 7 | ], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": 2020, 11 | "sourceType": "module", 12 | "project": "./tsconfig.json" 13 | }, 14 | "rules": { 15 | "i18n-text/no-en": "off", 16 | "eslint-comments/no-use": "off", 17 | "import/no-namespace": "off", 18 | "no-unused-vars": "off", 19 | "@typescript-eslint/no-unused-vars": "error", 20 | "@typescript-eslint/explicit-member-accessibility": [ 21 | "error", 22 | { 23 | "accessibility": "no-public" 24 | } 25 | ], 26 | "@typescript-eslint/no-require-imports": "error", 27 | "@typescript-eslint/array-type": "error", 28 | "@typescript-eslint/await-thenable": "error", 29 | "@typescript-eslint/ban-ts-comment": "error", 30 | "camelcase": "off", 31 | "@typescript-eslint/consistent-type-assertions": "error", 32 | "@typescript-eslint/explicit-function-return-type": [ 33 | "error", 34 | { 35 | "allowExpressions": true 36 | } 37 | ], 38 | "@typescript-eslint/func-call-spacing": [ 39 | "error", 40 | "never" 41 | ], 42 | "@typescript-eslint/no-array-constructor": "error", 43 | "@typescript-eslint/no-empty-interface": "error", 44 | "@typescript-eslint/no-explicit-any": "error", 45 | "@typescript-eslint/no-extraneous-class": "error", 46 | "@typescript-eslint/no-for-in-array": "error", 47 | "@typescript-eslint/no-inferrable-types": "error", 48 | "@typescript-eslint/no-misused-new": "error", 49 | "@typescript-eslint/no-namespace": "error", 50 | "@typescript-eslint/no-non-null-assertion": "warn", 51 | "@typescript-eslint/no-unnecessary-qualifier": "error", 52 | "@typescript-eslint/no-unnecessary-type-assertion": "error", 53 | "@typescript-eslint/no-useless-constructor": "error", 54 | "@typescript-eslint/no-var-requires": "error", 55 | "@typescript-eslint/prefer-for-of": "warn", 56 | "@typescript-eslint/prefer-function-type": "warn", 57 | "@typescript-eslint/prefer-includes": "error", 58 | "@typescript-eslint/prefer-string-starts-ends-with": "error", 59 | "@typescript-eslint/promise-function-async": "error", 60 | "@typescript-eslint/require-array-sort-compare": "error", 61 | "@typescript-eslint/restrict-plus-operands": "error", 62 | "semi": "off", 63 | "@typescript-eslint/semi": [ 64 | "error", 65 | "never" 66 | ], 67 | "@typescript-eslint/type-annotation-spacing": "error", 68 | "@typescript-eslint/unbound-method": "error" 69 | }, 70 | "env": { 71 | "node": true, 72 | "es6": true 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.github/workflows/check_url.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | check: 5 | runs-on: ${{ matrix.os }} 6 | timeout-minutes: 10 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, windows-latest] 10 | steps: 11 | 12 | - name: Setup Babashka 13 | id: setup-babashka-1 14 | uses: turtlequeue/setup-babashka@main 15 | with: 16 | babashka-version: 1.3.189 17 | 18 | - name: Check if bb runs fine 19 | run: bb --version 20 | 21 | - name: Setup Babashka from cache 22 | id: setup-babashka-2 23 | uses: turtlequeue/setup-babashka@main 24 | with: 25 | babashka-version: 1.3.189 26 | fail-on-cache-miss: true 27 | 28 | - name: Check if bb runs fine from cache 29 | run: bb --version 30 | 31 | - name: Test Cache 32 | run: | 33 | if [ -z "${{ steps.setup-babashka-1.outputs.cache-hit }}" ]; then 34 | echo "Cache-hit output not set in first run. Failing the job." 35 | exit 1 36 | elif [ "${{ steps.setup-babashka-2.outputs.cache-hit }}" != "true" ]; then 37 | echo "Cache not used in the second run. Failing the job." 38 | exit 1 39 | fi 40 | shell: bash 41 | 42 | check-url: 43 | runs-on: ubuntu-latest 44 | timeout-minutes: 10 45 | strategy: 46 | matrix: 47 | babashka-url: ['https://github.com/babashka/babashka/releases/download/v1.3.189/babashka-1.3.189-linux-amd64-static.tar.gz'] 48 | steps: 49 | - name: Setup Babashka 50 | id: setup-babashka-1 51 | uses: turtlequeue/setup-babashka@main 52 | with: 53 | babashka-version: 1.3.189 54 | babashka-url: ${{ matrix.babashka-url }} 55 | 56 | - name: Check if bb runs fine 57 | run: bb --version 58 | 59 | - name: Setup Babashka from cache 60 | id: setup-babashka-2 61 | uses: turtlequeue/setup-babashka@main 62 | with: 63 | babashka-version: 1.3.189 64 | babashka-url: ${{ matrix.babashka-url }} 65 | fail-on-cache-miss: true 66 | 67 | - name: Check if bb runs fine from cache 68 | run: bb --version 69 | 70 | - name: Test Cache 71 | run: | 72 | if [ -z "${{ steps.setup-babashka-1.outputs.cache-hit }}" ]; then 73 | echo "Cache-hit output not set in first run. Failing the job." 74 | exit 1 75 | elif [ "${{ steps.setup-babashka-2.outputs.cache-hit }}" != "true" ]; then 76 | echo "Cache not used in the second run. Failing the job." 77 | exit 1 78 | fi 79 | shell: bash 80 | 81 | check-windows: 82 | # https://ci.appveyor.com/project/borkdude/babashka 83 | runs-on: windows-latest 84 | strategy: 85 | matrix: 86 | babashka-url: ['https://ci.appveyor.com/api/buildjobs/hgg0343wy6uj4xog/artifacts/babashka-1.3.190-SNAPSHOT-windows-amd64.zip'] 87 | steps: 88 | - name: Setup Babashka 89 | id: setup-babashka-1 90 | uses: turtlequeue/setup-babashka@main 91 | 92 | with: 93 | babashka-version: 1.3.190-SNAPSHOT 94 | babashka-url: ${{ matrix.babashka-url }} 95 | 96 | - name: Check if bb runs fine 97 | run: bb --version 98 | 99 | - name: Setup Babashka from cache 100 | id: setup-babashka-2 101 | uses: turtlequeue/setup-babashka@main 102 | 103 | with: 104 | babashka-version: 1.3.190-SNAPSHOT 105 | babashka-url: ${{ matrix.babashka-url }} 106 | fail-on-cache-miss: true 107 | 108 | - name: Check if bb runs fine from cache 109 | run: bb --version 110 | 111 | - name: Test Cache 112 | run: | 113 | if [ -z "${{ steps.setup-babashka-1.outputs.cache-hit }}" ]; then 114 | echo "Cache-hit output not set in first run. Failing the job." 115 | exit 1 116 | elif [ "${{ steps.setup-babashka-2.outputs.cache-hit }}" != "true" ]; then 117 | echo "Cache not used in the second run. Failing the job." 118 | exit 1 119 | fi 120 | shell: bash 121 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main dev test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | maindevtest: 7 | # runs-on: ubuntu-latest 8 | runs-on: windows-latest 9 | steps: 10 | - name: Setup Babashka 11 | id: setup-babashka 12 | uses: turtlequeue/setup-babashka@main 13 | with: 14 | babashka-version: 1.3.189 15 | 16 | - name: Check bb runs 17 | run: bb --version 18 | 19 | - name: Test Cache 20 | run: | 21 | if [ -z "${{ steps.setup-babashka.outputs.cache-hit }}" ]; then 22 | echo "Cache-hit output not set in first run. Failing the job." 23 | exit 1 24 | fi 25 | shell: bash 26 | -------------------------------------------------------------------------------- /.github/workflows/self_check.yml: -------------------------------------------------------------------------------- 1 | # name: Self check 2 | 3 | on: [push] 4 | 5 | jobs: 6 | check: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, macos-latest, windows-latest] 11 | babashka-version: [1.1.172, 1.3.189] 12 | steps: 13 | - name: Setup Babashka 14 | id: setup-babashka-1 15 | uses: turtlequeue/setup-babashka@main 16 | with: 17 | babashka-version: ${{ matrix.babashka-version }} 18 | 19 | - name: Check if bb runs fine 20 | run: bb --version 21 | 22 | - name: Setup Babashka from cache 23 | id: setup-babashka-2 24 | uses: turtlequeue/setup-babashka@main 25 | with: 26 | babashka-version: ${{ matrix.babashka-version }} 27 | fail-on-cache-miss: true 28 | 29 | - name: Check if bb runs fine from cache 30 | run: bb --version 31 | 32 | - name: Test Cache 33 | run: | 34 | if [ -z "${{ steps.setup-babashka-1.outputs.cache-hit }}" ]; then 35 | echo "Cache-hit output not set in first run. Failing the job." 36 | exit 1 37 | elif [ "${{ steps.setup-babashka-2.outputs.cache-hit }}" != "true" ]; then 38 | echo "Cache not used in the second run. Failing the job." 39 | exit 1 40 | fi 41 | shell: bash 42 | -------------------------------------------------------------------------------- /.github/workflows/unit_test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v4 11 | with: 12 | node-version: 20 13 | - run: yarn 14 | - run: yarn test 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | .env.test 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | 75 | # next.js build output 76 | .next 77 | 78 | # nuxt.js build output 79 | .nuxt 80 | 81 | # vuepress build output 82 | .vuepress/dist 83 | 84 | # Serverless directories 85 | .serverless/ 86 | 87 | # FuseBox cache 88 | .fusebox/ 89 | 90 | # DynamoDB Local files 91 | .dynamodb/ 92 | 93 | # OS metadata 94 | .DS_Store 95 | Thumbs.db 96 | 97 | # Ignore built ts files 98 | __tests__/runner/* 99 | lib/**/* 100 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 20.12.0 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.12.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' from 19 | a Contributor if it was added to the Program by such Contributor itself or 20 | anyone acting on such Contributor's behalf. Contributions do not include 21 | additions to the Program which: (i) are separate modules of software 22 | distributed in conjunction with the Program under their own license 23 | agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement, 34 | including all Contributors. 35 | 36 | 2. GRANT OF RIGHTS 37 | a) Subject to the terms of this Agreement, each Contributor hereby grants 38 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 39 | reproduce, prepare derivative works of, publicly display, publicly perform, 40 | distribute and sublicense the Contribution of such Contributor, if any, and 41 | such derivative works, in source code and object code form. 42 | b) Subject to the terms of this Agreement, each Contributor hereby grants 43 | Recipient a non-exclusive, worldwide, royalty-free patent license under 44 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 45 | transfer the Contribution of such Contributor, if any, in source code and 46 | object code form. This patent license shall apply to the combination of the 47 | Contribution and the Program if, at the time the Contribution is added by 48 | the Contributor, such addition of the Contribution causes such combination 49 | to be covered by the Licensed Patents. The patent license shall not apply 50 | to any other combinations which include the Contribution. No hardware per 51 | se is licensed hereunder. 52 | c) Recipient understands that although each Contributor grants the licenses to 53 | its Contributions set forth herein, no assurances are provided by any 54 | Contributor that the Program does not infringe the patent or other 55 | intellectual property rights of any other entity. Each Contributor 56 | disclaims any liability to Recipient for claims brought by any other entity 57 | based on infringement of intellectual property rights or otherwise. As a 58 | condition to exercising the rights and licenses granted hereunder, each 59 | Recipient hereby assumes sole responsibility to secure any other 60 | intellectual property rights needed, if any. For example, if a third party 61 | patent license is required to allow Recipient to distribute the Program, it 62 | is Recipient's responsibility to acquire that license before distributing 63 | the Program. 64 | d) Each Contributor represents that to its knowledge it has sufficient 65 | copyright rights in its Contribution, if any, to grant the copyright 66 | license set forth in this Agreement. 67 | 68 | 3. REQUIREMENTS 69 | 70 | A Contributor may choose to distribute the Program in object code form under its 71 | own license agreement, provided that: 72 | 73 | a) it complies with the terms and conditions of this Agreement; and 74 | b) its license agreement: 75 | i) effectively disclaims on behalf of all Contributors all warranties and 76 | conditions, express and implied, including warranties or conditions of 77 | title and non-infringement, and implied warranties or conditions of 78 | merchantability and fitness for a particular purpose; 79 | ii) effectively excludes on behalf of all Contributors all liability for 80 | damages, including direct, indirect, special, incidental and 81 | consequential damages, such as lost profits; 82 | iii) states that any provisions which differ from this Agreement are offered 83 | by that Contributor alone and not by any other party; and 84 | iv) states that source code for the Program is available from such 85 | Contributor, and informs licensees how to obtain it in a reasonable 86 | manner on or through a medium customarily used for software exchange. 87 | 88 | When the Program is made available in source code form: 89 | 90 | a) it must be made available under this Agreement; and 91 | b) a copy of this Agreement must be included with each copy of the Program. 92 | Contributors may not remove or alter any copyright notices contained within 93 | the Program. 94 | 95 | Each Contributor must identify itself as the originator of its Contribution, if 96 | any, in a manner that reasonably allows subsequent Recipients to identify the 97 | originator of the Contribution. 98 | 99 | 4. COMMERCIAL DISTRIBUTION 100 | 101 | Commercial distributors of software may accept certain responsibilities with 102 | respect to end users, business partners and the like. While this license is 103 | intended to facilitate the commercial use of the Program, the Contributor who 104 | includes the Program in a commercial product offering should do so in a manner 105 | which does not create potential liability for other Contributors. Therefore, if 106 | a Contributor includes the Program in a commercial product offering, such 107 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 108 | every other Contributor ("Indemnified Contributor") against any losses, damages 109 | and costs (collectively "Losses") arising from claims, lawsuits and other legal 110 | actions brought by a third party against the Indemnified Contributor to the 111 | extent caused by the acts or omissions of such Commercial Contributor in 112 | connection with its distribution of the Program in a commercial product 113 | offering. The obligations in this section do not apply to any claims or Losses 114 | relating to any actual or alleged intellectual property infringement. In order 115 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial 116 | Contributor in writing of such claim, and b) allow the Commercial Contributor to 117 | control, and cooperate with the Commercial Contributor in, the defense and any 118 | related settlement negotiations. The Indemnified Contributor may participate in 119 | any such claim at its own expense. 120 | 121 | For example, a Contributor might include the Program in a commercial product 122 | offering, Product X. That Contributor is then a Commercial Contributor. If that 123 | Commercial Contributor then makes performance claims, or offers warranties 124 | related to Product X, those performance claims and warranties are such 125 | Commercial Contributor's responsibility alone. Under this section, the 126 | Commercial Contributor would have to defend claims against the other 127 | Contributors related to those performance claims and warranties, and if a court 128 | requires any other Contributor to pay any damages as a result, the Commercial 129 | Contributor must pay those damages. 130 | 131 | 5. NO WARRANTY 132 | 133 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 134 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 135 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 136 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 137 | Recipient is solely responsible for determining the appropriateness of using and 138 | distributing the Program and assumes all risks associated with its exercise of 139 | rights under this Agreement , including but not limited to the risks and costs 140 | of program errors, compliance with applicable laws, damage to or loss of data, 141 | programs or equipment, and unavailability or interruption of operations. 142 | 143 | 6. DISCLAIMER OF LIABILITY 144 | 145 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 146 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 147 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 148 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 149 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 150 | OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 151 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 152 | 153 | 7. GENERAL 154 | 155 | If any provision of this Agreement is invalid or unenforceable under applicable 156 | law, it shall not affect the validity or enforceability of the remainder of the 157 | terms of this Agreement, and without further action by the parties hereto, such 158 | provision shall be reformed to the minimum extent necessary to make such 159 | provision valid and enforceable. 160 | 161 | If Recipient institutes patent litigation against any entity (including a 162 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 163 | (excluding combinations of the Program with other software or hardware) 164 | infringes such Recipient's patent(s), then such Recipient's rights granted under 165 | Section 2(b) shall terminate as of the date such litigation is filed. 166 | 167 | All Recipient's rights under this Agreement shall terminate if it fails to 168 | comply with any of the material terms or conditions of this Agreement and does 169 | not cure such failure in a reasonable period of time after becoming aware of 170 | such noncompliance. If all Recipient's rights under this Agreement terminate, 171 | Recipient agrees to cease use and distribution of the Program as soon as 172 | reasonably practicable. However, Recipient's obligations under this Agreement 173 | and any licenses granted by Recipient relating to the Program shall continue and 174 | survive. 175 | 176 | Everyone is permitted to copy and distribute copies of this Agreement, but in 177 | order to avoid inconsistency the Agreement is copyrighted and may only be 178 | modified in the following manner. The Agreement Steward reserves the right to 179 | publish new versions (including revisions) of this Agreement from time to time. 180 | No one other than the Agreement Steward has the right to modify this Agreement. 181 | The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation 182 | may assign the responsibility to serve as the Agreement Steward to a suitable 183 | separate entity. Each new version of the Agreement will be given a 184 | distinguishing version number. The Program (including Contributions) may always 185 | be distributed subject to the version of the Agreement under which it was 186 | received. In addition, after a new version of the Agreement is published, 187 | Contributor may elect to distribute the Program (including its Contributions) 188 | under the new version. Except as expressly stated in Sections 2(a) and 2(b) 189 | above, Recipient receives no rights or licenses to the intellectual property of 190 | any Contributor under this Agreement, whether expressly, by implication, 191 | estoppel or otherwise. All rights in the Program not expressly granted under 192 | this Agreement are reserved. 193 | 194 | This Agreement is governed by the laws of the State of New York and the 195 | intellectual property laws of the United States of America. No party to this 196 | Agreement will bring a legal action under this Agreement more than one year 197 | after the cause of action arose. Each party waives its rights to a jury trial in 198 | any resulting litigation. -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # notes for myself 2 | 3 | # Development 4 | 5 | ```bash 6 | nvm use 7 | yarn install 8 | yarn package 9 | nodemon -e ts --exec yarn test 10 | ``` 11 | 12 | # Test in CI 13 | 14 | ```bash 15 | git addm 16 | git cia --no-edit 17 | yarn package && git addm && git cia --no-edit && git push -f 18 | ``` 19 | 20 | # Push a release 21 | 22 | ```bash 23 | yarn package && git addm && git cia --no-edit && git tag -a -f -m "bump JS deps" v1.4.0 && git push -f --follow-tags 24 | ``` 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # setup-babashka 2 | 3 | This action sets up [Babashka](https://github.com/babashka/babashka) environment for using in GitHub Actions. 4 | So you can use a clojure interpreter in your CI environment. 5 | 6 | # Usage 7 | 8 | ```yaml 9 | name: Simple example of using the babashka action 10 | 11 | on: [push] 12 | 13 | jobs: 14 | simple: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Setup Babashka 18 | uses: turtlequeue/setup-babashka@v1.7.0 19 | with: 20 | babashka-version: 1.3.189 21 | 22 | - name: Check bb runs 23 | run: bb --version 24 | ``` 25 | 26 | # Rationale 27 | 28 | You may be using this already: 29 | ``` shell 30 | $ bash < <(curl -s https://raw.githubusercontent.com/babashka/babashka/master/install) 31 | ``` 32 | 33 | That's great! And this is what this action uses under the hood. 34 | 35 | However this action is useful still for: 36 | 37 | - supporting more platforms (windows) 38 | - pinning the babashka version 39 | - using the github api to cache between runs 40 | - (advanced) using a CI build by specifying a `babashka-url` (see below) 41 | 42 | 43 | # Use a CI version 44 | 45 | You probably don't want to do that. 46 | Using a CI build of babashka is possible. They can be found on the babashka CI build artefacts or on [appveyor](https://ci.appveyor.com/project/borkdude/babashka) for windows builds. 47 | 48 | Keep in mind that: 49 | - the CI builds from babashka will expire 50 | - the platform detection is up to you 51 | 52 | ```yaml 53 | name: install babashka 54 | 55 | on: [push] 56 | 57 | jobs: 58 | simple: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Setup Babashka 62 | uses: turtlequeue/setup-babashka@v1.7.0 63 | with: 64 | # this will eventually expire 65 | babashka-version: 0.8.157-SNAPSHOT 66 | babashka-url: https://29579-201467090-gh.circle-artifacts.com/0/release/babashka-0.8.157-SNAPSHOT-linux-amd64-static.tar.gz 67 | 68 | - name: Check bb runs 69 | run: bb --version 70 | ``` 71 | 72 | # License 73 | Copyright © 2022-2024 Turtlequeue Ltd 74 | 75 | # Contributors 76 | 77 | * [Johan Lindbergh](https://github.com/jlindbergh) 78 | * [Nick Pellegrino](https://github.com/nickpell) 79 | 80 | Distributed under the EPL License. See LICENSE. 81 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Babashka - A Clojure babushka for the grey areas of Bash.' 2 | description: 'Setup your runner with babashka' 3 | author: 'nha' 4 | branding: 5 | icon: 'bold' 6 | color: 'green' 7 | inputs: 8 | babashka-version: 9 | description: 'The babashka/babashka version to make available on the path. Only exact version allowed.' 10 | required: true 11 | babashka-url: 12 | description: 'The babashka/babashka url to make available on the path. Useful for CI builds. Example https://16810-201467090-gh.circle-artifacts.com/0/release/babashka-0.3.3-SNAPSHOT-linux-amd64.tar.gz When using "babashka-url" the parameter "babashka-version" is still required, to allow for appropriate caching between runs. Example: babashka-version: 0.3.3-SNAPSHOT' 13 | fail-on-cache-miss: 14 | description: 'Fail the action run if the result was not returned from the cache. Useful for tests. "true" or false otherwise' 15 | runs: 16 | using: 'node20' 17 | main: 'dist/index.js' 18 | -------------------------------------------------------------------------------- /dist/LICENCE: -------------------------------------------------------------------------------- 1 | @actions/core 2 | MIT 3 | The MIT License (MIT) 4 | 5 | Copyright 2019 GitHub 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | @actions/exec 14 | MIT 15 | The MIT License (MIT) 16 | 17 | Copyright 2019 GitHub 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | @actions/http-client 26 | MIT 27 | Actions Http Client for Node.js 28 | 29 | Copyright (c) GitHub, Inc. 30 | 31 | All rights reserved. 32 | 33 | MIT License 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 36 | associated documentation files (the "Software"), to deal in the Software without restriction, 37 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 38 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 39 | subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 44 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 45 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 46 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 47 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | 50 | @actions/io 51 | MIT 52 | The MIT License (MIT) 53 | 54 | Copyright 2019 GitHub 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 61 | 62 | @actions/tool-cache 63 | MIT 64 | The MIT License (MIT) 65 | 66 | Copyright 2019 GitHub 67 | 68 | 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: 69 | 70 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 71 | 72 | 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. 73 | 74 | @fastify/busboy 75 | MIT 76 | Copyright Brian White. All rights reserved. 77 | 78 | Permission is hereby granted, free of charge, to any person obtaining a copy 79 | of this software and associated documentation files (the "Software"), to 80 | deal in the Software without restriction, including without limitation the 81 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 82 | sell copies of the Software, and to permit persons to whom the Software is 83 | furnished to do so, subject to the following conditions: 84 | 85 | The above copyright notice and this permission notice shall be included in 86 | all copies or substantial portions of the Software. 87 | 88 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 89 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 90 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 91 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 92 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 94 | IN THE SOFTWARE. 95 | 96 | semver 97 | ISC 98 | The ISC License 99 | 100 | Copyright (c) Isaac Z. Schlueter and Contributors 101 | 102 | Permission to use, copy, modify, and/or distribute this software for any 103 | purpose with or without fee is hereby granted, provided that the above 104 | copyright notice and this permission notice appear in all copies. 105 | 106 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 107 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 109 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 110 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 111 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 112 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 113 | 114 | 115 | tunnel 116 | MIT 117 | The MIT License (MIT) 118 | 119 | Copyright (c) 2012 Koichi Kobayashi 120 | 121 | Permission is hereby granted, free of charge, to any person obtaining a copy 122 | of this software and associated documentation files (the "Software"), to deal 123 | in the Software without restriction, including without limitation the rights 124 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 125 | copies of the Software, and to permit persons to whom the Software is 126 | furnished to do so, subject to the following conditions: 127 | 128 | The above copyright notice and this permission notice shall be included in 129 | all copies or substantial portions of the Software. 130 | 131 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 132 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 133 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 134 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 135 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 136 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 137 | THE SOFTWARE. 138 | 139 | 140 | undici 141 | MIT 142 | MIT License 143 | 144 | Copyright (c) Matteo Collina and Undici contributors 145 | 146 | Permission is hereby granted, free of charge, to any person obtaining a copy 147 | of this software and associated documentation files (the "Software"), to deal 148 | in the Software without restriction, including without limitation the rights 149 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 150 | copies of the Software, and to permit persons to whom the Software is 151 | furnished to do so, subject to the following conditions: 152 | 153 | The above copyright notice and this permission notice shall be included in all 154 | copies or substantial portions of the Software. 155 | 156 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 157 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 158 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 159 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 160 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 161 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 162 | SOFTWARE. 163 | 164 | 165 | uuid 166 | MIT 167 | The MIT License (MIT) 168 | 169 | Copyright (c) 2010-2020 Robert Kieffer and other contributors 170 | 171 | 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: 172 | 173 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 174 | 175 | 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. 176 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | silent: false, 5 | verbose: true, 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "setup-babashka", 3 | "version": "1.7.0", 4 | "private": false, 5 | "description": "setup babashka action", 6 | "main": "src/setup-babashka.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "format": "prettier --write **/*.ts", 10 | "format-check": "prettier --check **/*.ts", 11 | "lint": "eslint src/**/*.ts", 12 | "package": "ncc build --license LICENCE", 13 | "all": "npm run build && npm run format && npm run lint && npm run package", 14 | "test": "jest" 15 | }, 16 | "test": { 17 | "type": "git", 18 | "url": "git+https://github.com/turtlequeue/setup-babashka.git" 19 | }, 20 | "keywords": [ 21 | "actions", 22 | "clojure", 23 | "babashka", 24 | "github", 25 | "setup" 26 | ], 27 | "author": "nha", 28 | "license": "SEE LICENSE IN LICENSE", 29 | "dependencies": { 30 | "@actions/core": "^1.10.1", 31 | "@actions/exec": "^1.1.1", 32 | "@actions/tool-cache": "^2.0.1", 33 | "uuid": "^9.0.1" 34 | }, 35 | "devDependencies": { 36 | "@types/jest": "^29.5.12", 37 | "@types/node": "^20.12.2", 38 | "@types/semver": "^7.5.8", 39 | "@types/uuid": "^9.0.8", 40 | "@vercel/ncc": "^0.38.1", 41 | "eslint": "^8.57.0", 42 | "eslint-plugin-github": "^4.10.2", 43 | "jest": "^29.7.0", 44 | "prettier": "^3.2.5", 45 | "ts-jest": "^29.1.2", 46 | "tsx": "^4.7.1", 47 | "typescript": "^5.4.3", 48 | "typescript-formatter": "^7.2.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ToolName = 'Babashka'; 2 | 3 | export enum Inputs { 4 | BabashkaVersion = 'babashka-version', // Input 5 | BabashkaURL = 'babashka-url', // Input 6 | FailOnCacheMiss = 'fail-on-cache-miss', // Input for cache, restore action 7 | // TODO 8 | // ScriptInstallUrl = 'script-install-url' // override the default https://raw.githubusercontent.com/babashka/babashka/master/install 9 | } 10 | 11 | export enum Outputs { 12 | CacheHit = 'cache-hit', // Output from cache, restore action 13 | CachePrimaryKey = 'cache-primary-key', // Output from restore action 14 | CacheMatchedKey = 'cache-matched-key' // Output from restore action 15 | } 16 | -------------------------------------------------------------------------------- /src/installer.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as exec from '@actions/exec' 3 | import * as io from '@actions/io' 4 | import * as tc from '@actions/tool-cache' 5 | import * as path from 'path' 6 | import * as os from 'os' 7 | import { v4 as uuidv4 } from 'uuid' 8 | import { ok } from 'assert' 9 | import fs from 'fs' 10 | import { ToolName, Inputs, Outputs } from './constants' 11 | 12 | 13 | // Another approach would be to copy the logic from 14 | // https://raw.githubusercontent.com/babashka/babashka/master/install 15 | // 16 | // Pros: easier 17 | // Cons: need to keep up to date in case the script changes and so might be less reliable. One extra http call. 18 | // 19 | // Decision: optimize for reliability, and use the official install script 20 | // 21 | // TODO follow general organization of 22 | // https://github.com/actions/cache/blob/0c45773b623bea8c8e75f6c82b208c3cf94ea4f9/src/restoreImpl.ts#L12 23 | // https://github.com/actions/toolkit/blob/59e9d284e9f7d2bd1a24d2c2e83f19923caaac30/packages/tool-cache/__tests__/tool-cache.test.ts#L209-L224 24 | // 25 | 26 | 27 | // exported for tests only 28 | export async function _downloadFile(fileURL: string): Promise { 29 | try { 30 | const downloadedPath = await tc.downloadTool(fileURL) 31 | core.debug(`Downloaded file from ${fileURL} to ${downloadedPath}`) 32 | return downloadedPath 33 | } catch (error) { 34 | core.error(`Error downloading file from ${fileURL}: ${error}`) 35 | core.setFailed(`could not download file ${fileURL}`) 36 | throw error 37 | } 38 | } 39 | 40 | function _getTempDirectory(): string { 41 | const tempDirectory = process.env['RUNNER_TEMP'] || '' 42 | ok(tempDirectory, 'Expected RUNNER_TEMP to be defined') 43 | return tempDirectory 44 | } 45 | 46 | // useful for testing babashka CI snapshot builds 47 | export async function _installFromUrl( 48 | url: string, 49 | version: string, 50 | failOnCacheMiss: boolean = false 51 | ): Promise { 52 | // 53 | // TODO replaces so that it matches the CI builds 54 | // https://github.com/babashka/babashka/blob/126d2ff7287c398e488143422c7573337cf580a0/.circleci/script/release#L18 55 | // https://github.com/babashka/babashka/blob/77daea7362d8e2562c89c315b1fbcefde6fa56a5/appveyor.yml#L63 56 | // os.arch() os.platform() 57 | // 58 | 59 | // TODO allow a version like "-1" to skip caching ? 60 | const toolPath = tc.find(ToolName, version, os.arch()) 61 | 62 | 63 | if (core.isDebug()) { 64 | const allBabashkaVersions = tc.findAllVersions(ToolName) 65 | core.debug(`Versions of babashka available: ${allBabashkaVersions}`) 66 | } 67 | 68 | if (toolPath) { 69 | core.info(`${ToolName} found in cache ${toolPath}`) 70 | core.setOutput(Outputs.CacheHit, 'true') // for tests 71 | core.addPath(toolPath); 72 | return; 73 | } else { 74 | core.info(`${ToolName} not found in cache`) 75 | const downloadedFilePath = await _downloadFile(url) 76 | core.setOutput(Outputs.CacheHit, 'false') // for tests 77 | 78 | 79 | if (failOnCacheMiss) { 80 | core.setFailed(`Cache miss for ${ToolName} version ${version} arch ${os.arch()} and failOnCacheMiss is true.`); 81 | return; 82 | } 83 | 84 | if (core.isDebug()) { 85 | if (fs.existsSync(downloadedFilePath)) { 86 | core.info(`Downloaded ${url} in ${downloadedFilePath}`) 87 | const stats = fs.statSync(downloadedFilePath) 88 | const fileSizeInBytes = stats.size 89 | const fileSizeInMegabytes = fileSizeInBytes / (1024 * 1024) 90 | core.debug(`File is ${fileSizeInMegabytes}MB, isDir:${stats.isDirectory()}, isFile:${stats.isFile()}`) 91 | // archive should be like ~20MB (~80MB+ decompressed) - may be an issue otherwise 92 | } 93 | else { 94 | core.error(`could not download file ${url}`); 95 | return 96 | } 97 | } 98 | 99 | let folder 100 | if (url.endsWith('.tar.gz')) { 101 | folder = await tc.extractTar(downloadedFilePath, '.') 102 | } else if (url.endsWith('.zip')) { 103 | folder = await tc.extractZip(downloadedFilePath, '.') 104 | } else if (url.endsWith('.7z')) { 105 | folder = await tc.extract7z(downloadedFilePath, '.') 106 | } 107 | 108 | if (!folder) { 109 | core.error(`Unsupported babashka-url ${url}`) 110 | core.setFailed('babashka-url format is unknown. Must me .tar.gz, .zip or .7z') 111 | return 112 | } else { 113 | const stats = fs.statSync(folder) 114 | const fileSizeInBytes = stats.size 115 | const fileSizeInMegabytes = fileSizeInBytes / (1024 * 1024) 116 | core.debug(`Extracted folder ${folder} is ${fileSizeInMegabytes}MB, isDir:${stats.isDirectory()}, isFile:${stats.isFile()}`) 117 | const extractedFiles = fs.readdirSync(folder) 118 | core.debug(`Extracted files are ${extractedFiles}`) 119 | } 120 | 121 | // bb should now be just here 122 | let executable 123 | if (process.platform !== 'win32') { 124 | executable = 'bb' 125 | } else { 126 | executable = 'bb.exe' 127 | } 128 | 129 | const cachedPath = await tc.cacheFile( 130 | executable, 131 | executable, 132 | ToolName, 133 | version, // semver, should end with -SNAPSHOT here 134 | os.arch() 135 | ) 136 | 137 | core.info(`cachedPath ${cachedPath}`) 138 | core.info(`toolpath ${toolPath}`) 139 | core.addPath(cachedPath) 140 | } 141 | 142 | core.addPath(toolPath) 143 | 144 | return 145 | } 146 | 147 | // the usual way to install 148 | export async function _installFromVersion(version: string, failOnCacheMiss: boolean = false): Promise { 149 | let toolPath = tc.find(ToolName, version, os.arch()) 150 | 151 | if (core.isDebug()) { 152 | const allBabashkaVersions = tc.findAllVersions(ToolName) 153 | core.debug(`Versions of babashka available: ${allBabashkaVersions}`) 154 | } 155 | 156 | if (toolPath) { 157 | core.info(`${ToolName} found in cache ${toolPath}`) 158 | core.setOutput(Outputs.CacheHit, 'true') // for tests 159 | core.addPath(toolPath); 160 | return; 161 | } else { 162 | core.info(`${ToolName} not found in cache`) 163 | core.setOutput(Outputs.CacheHit, 'false') // for tests 164 | 165 | if (failOnCacheMiss) { 166 | core.setFailed(`Cache miss for ${ToolName} version ${version} arch ${os.arch()} and ${Inputs.FailOnCacheMiss} is true.`); 167 | return; 168 | } 169 | 170 | if (process.platform !== 'win32') { 171 | // Linux, macOS: rely on babashka's bash installer 172 | const tmpPath = path.join(_getTempDirectory(), uuidv4()) 173 | await io.mkdirP(tmpPath) 174 | 175 | core.info(`temporary directory ${tmpPath}`) 176 | 177 | const installerFile = await _downloadFile('https://raw.githubusercontent.com/babashka/babashka/master/install') 178 | core.info(`Downloaded installer file ${installerFile}`) 179 | 180 | core.startGroup('master_script'); 181 | await exec.exec('bash', [ 182 | installerFile, 183 | '--dir', 184 | tmpPath, 185 | '--version', 186 | version 187 | ]) 188 | core.endGroup(); 189 | 190 | core.info(`babashka installed to ${tmpPath}`) 191 | 192 | toolPath = await tc.cacheDir(tmpPath, ToolName, version, os.arch()) 193 | } 194 | else { 195 | // Windows: rely on a known url and powershell extraction 196 | core.info(`Windows detected, setting up bb.exe`) 197 | 198 | await exec.exec('powershell', [ 199 | '-command', 200 | `if (Test-Path('bb.exe')) { return } else { (New-Object Net.WebClient).DownloadFile('https://github.com/babashka/babashka/releases/download/v${version}/babashka-${version}-windows-amd64.zip', 'bb.zip') }` 201 | ]) 202 | await exec.exec('powershell', [ 203 | '-command', 204 | "if (Test-Path('bb.exe')) { return } else { Expand-Archive bb.zip . }" 205 | ]) 206 | 207 | const bbExePath = path.join(process.cwd(), "bb.exe"); 208 | 209 | core.info(`exists? bb.exe ${fs.existsSync(bbExePath)}`); 210 | toolPath = await tc.cacheFile( 211 | bbExePath, 212 | 'bb.exe', 213 | ToolName, 214 | version, 215 | os.arch() 216 | ) 217 | } 218 | 219 | core.addPath(toolPath) 220 | core.info(`babashka setup at ${toolPath}`) 221 | } 222 | } 223 | 224 | export async function getBabashka( 225 | url: string | undefined, 226 | version: string, 227 | failOnCacheMiss: boolean 228 | ): Promise { 229 | if (url && url.length) { 230 | return _installFromUrl(url, version, failOnCacheMiss) 231 | } else { 232 | return _installFromVersion(version, failOnCacheMiss) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/setup-babashka.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as installer from './installer' 3 | 4 | export function assertIsError(error: unknown): asserts error is Error { 5 | // https://stackoverflow.com/a/70993058/1327651 6 | // GH doesn't seem to have node assert 7 | if (!(error instanceof Error)) { 8 | throw error 9 | } 10 | } 11 | 12 | async function run(): Promise { 13 | try { 14 | const version = core.getInput('babashka-version', {required: true}) 15 | const url = core.getInput('babashka-url') 16 | const failOnCacheMiss = core.getInput('fail-on-cache-miss') === 'true'; 17 | 18 | await installer.getBabashka(url, version, failOnCacheMiss) 19 | } catch (error) { 20 | assertIsError(error) 21 | core.setFailed(error.message) 22 | } 23 | } 24 | 25 | run() 26 | -------------------------------------------------------------------------------- /test/install-url.test.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core'; 2 | import * as tc from '@actions/tool-cache'; 3 | import fs from 'fs'; 4 | import { Inputs, Outputs } from '../src/constants'; 5 | import { _installFromUrl } from '../src/installer'; 6 | 7 | // https://github.com/actions/cache/blob/0c45773b623bea8c8e75f6c82b208c3cf94ea4f9/src/stateProvider.ts 8 | 9 | jest.mock('@actions/core', () => ({ 10 | ...jest.requireActual('@actions/core'), 11 | setOutput: jest.fn(), 12 | debug: jest.fn(), 13 | info: jest.fn(), 14 | error: jest.fn(), 15 | setFailed: jest.fn() 16 | })); 17 | 18 | jest.mock('@actions/tool-cache', () => ({ 19 | ...jest.requireActual('@actions/tool-cache'), 20 | find: jest.fn(), 21 | findAllVersions: jest.fn().mockReturnValue(['1.0.0', '2.0.0']), 22 | downloadTool: jest.fn(), 23 | cacheFile: jest.fn(), 24 | addPath: jest.fn(), 25 | extractTar: jest.fn(), 26 | extractZip: jest.fn(), 27 | extract7z: jest.fn(), 28 | })); 29 | 30 | jest.mock('os', () => ({ 31 | ...jest.requireActual('os'), 32 | arch: jest.fn().mockReturnValue('x64') 33 | })); 34 | 35 | beforeEach(() => { 36 | jest.clearAllMocks(); 37 | }); 38 | 39 | jest.spyOn(fs, 'existsSync').mockReturnValue(true); 40 | jest.spyOn(fs, 'statSync').mockReturnValue({ 41 | size: 1024, 42 | isFile: () => true, 43 | isDirectory: () => false, 44 | } as fs.Stats); // to satisfy TypeScript's type checking. 45 | 46 | test('tc.downloadTool is called with the correct URL', async () => { 47 | const mockDownloadedFilePath = '/dummy/downloaded/path'; 48 | const downloadToolMock = tc.downloadTool as jest.MockedFunction; 49 | downloadToolMock.mockResolvedValue(mockDownloadedFilePath); 50 | 51 | await _installFromUrl('https://example.com/bb.tar.gz', '1.0.0'); 52 | expect(downloadToolMock).toHaveBeenCalledWith('https://example.com/bb.tar.gz'); 53 | }); 54 | -------------------------------------------------------------------------------- /test/setup-babashka.test.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as tc from '@actions/tool-cache' 3 | import { _downloadFile, _installFromUrl } from '../src/installer' 4 | import { Inputs, Outputs } from '../src/constants' 5 | 6 | // https://github.com/actions/checkout/blob/master/__test__/input-helper.test.ts 7 | 8 | jest.mock('@actions/core', () => ({ 9 | ...jest.requireActual('@actions/core'), 10 | debug: jest.fn(), 11 | error: jest.fn(), 12 | setFailed: jest.fn() 13 | })); 14 | 15 | jest.mock('@actions/tool-cache', () => ({ 16 | ...jest.requireActual('@actions/tool-cache'), 17 | downloadTool: jest.fn(), 18 | find: jest.fn(), 19 | })); 20 | 21 | beforeEach(() => { 22 | jest.resetAllMocks(); 23 | }); 24 | 25 | afterEach(() => { 26 | jest.resetAllMocks(); 27 | }); 28 | 29 | describe('_downloadFile impl test', () => { 30 | 31 | test('_downloadFile should download a file from the given URL', async () => { 32 | const mockDownloadedPath = '/dummy/downloaded/path'; 33 | (tc.downloadTool as jest.MockedFunction).mockResolvedValue(mockDownloadedPath); 34 | 35 | const fileURL = 'https://example.com/example-file.txt'; 36 | const downloadedPath = await _downloadFile(fileURL); 37 | 38 | expect(tc.downloadTool).toHaveBeenCalledWith(fileURL); 39 | expect(downloadedPath).toBe(mockDownloadedPath); 40 | expect(core.debug).toHaveBeenCalledWith(`Downloaded file from ${fileURL} to ${mockDownloadedPath}`); 41 | }); 42 | 43 | test('downloadFile should throw an error when downloading fails', async () => { 44 | const fileURL = 'https://example.com/non-existent-file.txt'; 45 | (tc.downloadTool as jest.MockedFunction).mockRejectedValue(new Error('Download failed')); 46 | 47 | await expect(_downloadFile(fileURL)).rejects.toThrow(new Error('Download failed')); 48 | expect(core.error).toHaveBeenCalledWith(`Error downloading file from ${fileURL}: Error: Download failed`); 49 | }); 50 | 51 | }) 52 | 53 | describe('FailOnCacheMiss behavior', () => { 54 | test('should fail action on cache miss when FailOnCacheMiss is true', async () => { 55 | (tc.find as jest.MockedFunction).mockReturnValue(''); // Simulate cache miss 56 | const setFailedMock = core.setFailed as jest.MockedFunction; 57 | 58 | await _installFromUrl('https://example.com/bb.tar.gz', '1.0.0', true); 59 | 60 | expect(setFailedMock).toHaveBeenCalledWith(expect.stringContaining('Cache miss for Babashka version 1.0.0')); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | "outDir": "./lib", /* Redirect output structure to the directory. */ 15 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 18 | // "removeComments": true, /* Do not emit comments to output. */ 19 | // "noEmit": true, /* Do not emit outputs. */ 20 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 21 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 22 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 23 | 24 | /* Strict Type-Checking Options */ 25 | "strict": true, /* Enable all strict type-checking options. */ 26 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 27 | // "strictNullChecks": true, /* Enable strict null checks. */ 28 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 29 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 30 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 31 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 32 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 33 | 34 | /* Additional Checks */ 35 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 36 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 37 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 38 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 39 | 40 | /* Module Resolution Options */ 41 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 42 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 43 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 44 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 45 | // "typeRoots": [], /* List of folders to include type definitions from. */ 46 | // "types": [], /* Type declaration files to be included in compilation. */ 47 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 48 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 49 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 50 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 51 | 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | 58 | /* Experimental Options */ 59 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | }, 62 | "exclude": ["node_modules", "**/*.test.ts"] 63 | } 64 | --------------------------------------------------------------------------------