├── .eslintrc.js ├── .github └── workflows │ ├── ci.yml │ ├── link_check.yml │ ├── security_scan.yml │ └── verify-build.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── action.yml ├── builder-images.md ├── dist ├── index.js ├── index.js.map └── sourcemap-register.js ├── git-hooks └── pre-commit ├── package-lock.json ├── package.json ├── src ├── command.ts ├── constants.ts ├── generated │ └── inputs-outputs.ts ├── installer.ts ├── s2iExec.ts ├── types.ts └── utils │ └── execHelper.ts └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | "@redhat-actions/eslint-config", 4 | ], 5 | }; -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | on: 3 | push: 4 | pull_request: 5 | 6 | jobs: 7 | lint: 8 | name: Run ESLint 9 | runs-on: ubuntu-22.04 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | - run: npm ci 14 | - run: npm run lint 15 | 16 | check-dist: 17 | name: Check Distribution 18 | runs-on: ubuntu-22.04 19 | env: 20 | BUNDLE_FILE: "dist/index.js" 21 | BUNDLE_COMMAND: "npm run bundle" 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Install 26 | run: npm ci 27 | 28 | - name: Verify Latest Bundle 29 | uses: redhat-actions/common/bundle-verifier@v1 30 | with: 31 | bundle_file: ${{ env.BUNDLE_FILE }} 32 | bundle_command: ${{ env.BUNDLE_COMMAND }} 33 | 34 | check-inputs-outputs: 35 | name: Check Input and Output enums 36 | runs-on: ubuntu-22.04 37 | env: 38 | IO_FILE: ./src/generated/inputs-outputs.ts 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - name: Install dependencies 43 | run: npm ci 44 | 45 | - name: Verify Input and Output enums 46 | uses: redhat-actions/common/action-io-generator@v1 47 | with: 48 | io_file: ${{ env.IO_FILE }} 49 | -------------------------------------------------------------------------------- /.github/workflows/link_check.yml: -------------------------------------------------------------------------------- 1 | name: Link checker 2 | on: 3 | push: 4 | paths: 5 | - '**.md' 6 | pull_request: 7 | paths: 8 | -'**.md' 9 | schedule: 10 | - cron: '0 0 * * *' # every day at midnight 11 | 12 | jobs: 13 | markdown-link-check: 14 | name: Check links in markdown 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 19 | with: 20 | use-verbose-mode: true 21 | -------------------------------------------------------------------------------- /.github/workflows/security_scan.yml: -------------------------------------------------------------------------------- 1 | name: Vulnerability Scan with CRDA 2 | on: 3 | # push: 4 | workflow_dispatch: 5 | # pull_request_target: 6 | # types: [ assigned, opened, synchronize, reopened, labeled, edited ] 7 | # schedule: 8 | # - cron: '0 0 * * *' # every day at midnight 9 | 10 | jobs: 11 | crda-scan: 12 | runs-on: ubuntu-22.04 13 | name: Scan project vulnerability with CRDA 14 | steps: 15 | 16 | - uses: actions/checkout@v4 17 | 18 | - name: Setup Node 19 | uses: actions/setup-node@v2 20 | with: 21 | node-version: '14' 22 | 23 | - name: Install CRDA 24 | uses: redhat-actions/openshift-tools-installer@v1 25 | with: 26 | source: github 27 | github_pat: ${{ github.token }} 28 | crda: "latest" 29 | 30 | - name: CRDA Scan 31 | id: scan 32 | uses: redhat-actions/crda@v1 33 | with: 34 | crda_key: ${{ secrets.CRDA_KEY }} 35 | fail_on: never 36 | -------------------------------------------------------------------------------- /.github/workflows/verify-build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will perform a test whenever there 2 | # is some change in code done to ensure that the 3 | # changes are not buggy and we are getting desired output. 4 | 5 | name: Verify Build 6 | on: 7 | push: 8 | workflow_dispatch: 9 | pull_request: 10 | schedule: 11 | - cron: '0 0 * * *' # every day at midnight 12 | 13 | env: 14 | TEST_REPO: helloworld 15 | IMAGE_NAME: hello-world 16 | TAGS: latest v1 17 | 18 | jobs: 19 | test-s2i-job: 20 | runs-on: ubuntu-latest 21 | # This will install latest version of s2i and 22 | # to build the image and using Docker we will 23 | # run the image to verify the build. 24 | name: Install S2I and build image 25 | steps: 26 | 27 | # Checkout S2I action github repository 28 | - name: Checkout s2i-build action 29 | uses: actions/checkout@v4 30 | with: 31 | path: 's2i-build' 32 | 33 | # Checkout hello-world repository for testing 34 | - name: Checkout application 35 | uses: actions/checkout@v4 36 | with: 37 | repository: 'go-training/helloworld' 38 | path: ${{ env.TEST_REPO }} 39 | 40 | # Install s2i cli for future steps 41 | - name: Install S2i 42 | uses: redhat-actions/openshift-tools-installer@v1 43 | with: 44 | source: github 45 | github_pat: ${{ github.token }} 46 | s2i: "latest" 47 | 48 | # Build container image 49 | - name: Build 50 | id: build_image 51 | uses: ./s2i-build/ 52 | with: 53 | path_context: './${{ env.TEST_REPO }}' 54 | builder_image: 'centos/go-toolset-7-centos7' 55 | image: ${{ env.IMAGE_NAME }} 56 | tags: ${{ env.TAGS }} 57 | 58 | - name: Echo Outputs 59 | run: | 60 | echo "Image: ${{ steps.build_image.outputs.image }}" 61 | echo "Tags: ${{ steps.build_image.outputs.tags }}" 62 | 63 | # Run image to verify the build 64 | - name: Run image 65 | run: docker run ${{ env.IMAGE_NAME}}:latest 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | *.tsbuildinfo 3 | lib/ 4 | node_modules/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # s2i-build Changelog 2 | 3 | ## v2.4 4 | - Update s2i version to `v1.3.9` 5 | - Update action to run on Node20. https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/ 6 | 7 | ## v2.3 8 | - Update action to run on Node16. https://github.blog/changelog/2022-05-20-actions-can-now-run-in-a-node-js-16-runtime/ 9 | 10 | ## v2.2 11 | - Feature include `.git` folder in s2i build. https://github.com/redhat-actions/s2i-build/pull/40 12 | 13 | ## v2.1 14 | - Skip `s2i` installation if already installed in the runner. 15 | 16 | ## v2 17 | - Rename `image_tag` input to `tags`, to allow you to build multiple tags of the same image 18 | - Rename `image_name` input to `image` to maintain consistency across all redhat actions 19 | - Add outputs `image` and `tags`, which output the image name and all tags of the image that was created 20 | 21 | ## v1.1.1 22 | - Fix input `env_vars` bug 23 | 24 | ## v1.1 25 | - Add support to provide environment variables 26 | 27 | ## v1.0 28 | - Initial marketplace release 29 | 30 | ## v0.1 31 | - Initial pre-release 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Red Hat. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # s2i-build 2 | [![CI checks](https://github.com/redhat-actions/s2i-build/workflows/CI%20checks/badge.svg)](https://github.com/redhat-actions/s2i-build/actions?query=workflow%3A%22CI+checks%22) 3 | [![Verify Build](https://github.com/redhat-actions/s2i-build/workflows/Verify%20Build/badge.svg)](https://github.com/redhat-actions/s2i-build/actions?query=workflow%3A%22Verify+Build%22) 4 | [![Link checker](https://github.com/redhat-actions/s2i-build/workflows/Link%20checker/badge.svg)](https://github.com/redhat-actions/s2i-build/actions?query=workflow%3A%22Link+checker%22) 5 |

6 | [![tag badge](https://img.shields.io/github/v/tag/redhat-actions/s2i-build)](https://github.com/redhat-actions/s2i-build/tags) 7 | [![license badge](https://img.shields.io/github/license/redhat-actions/s2i-build)](./LICENSE) 8 | [![size badge](https://img.shields.io/github/size/redhat-actions/s2i-build/dist/index.js)](./dist) 9 | 10 | `s2i-build` is a Github Action to build OCI-compatible container images from source code. 11 | 12 | [Source-to-Image (S2I)](https://github.com/openshift/source-to-image) is a toolkit and workflow for building reproducible 13 | container images from source code. 14 | S2I produces images by injecting source code into a base S2I container image 15 | and letting the container prepare that source code for execution. The base 16 | S2I container images contain the language runtime and build tools needed for 17 | building and running the source code. 18 | 19 | This Action will install [the latest](https://github.com/openshift/source-to-image/releases/tag/v1.3.9) version of S2I. 20 | 21 | To install any specific version of `s2i` use [**openshift-tools-installer**](https://github.com/marketplace/actions/openshift-tools-installer). 22 | **NOTE:** 23 | `s2i-build` only works on Linux platforms, because it relies on the Docker daemon.
24 | If you are using GitHub's Ubuntu runners, the Docker daemon will already be available. 25 | Otherwise, you can use [docker-setup-buildx](https://github.com/marketplace/actions/docker-setup-buildx) to set up and start the Docker daemon. 26 | 27 | Also see [**buildah-build**](https://github.com/marketplace/actions/buildah-build) for more configurable method of building images, from scratch or from a Dockerfile. 28 | 29 | Once an image has been built, [**push-to-registry**](https://github.com/marketplace/actions/push-to-registry) can be used to push it to an image registry. 30 | 31 | 32 | 33 | ## Action Inputs 34 | 35 | | Input Name | Description | Default | 36 | | ---------- | ----------- | ------- | 37 | | builder_image | The path of the S2I builder image. A curated list of builder images can be found [here](./builder-images.md). | **Required** 38 | | env_vars | List of environment variable key-value pairs to pass to the S2I builder context. (eg. `key=value`, `mysecret=${{ secrets.MY_SECRET }}`). | None 39 | | image | Name to give to the output image. | **Required** 40 | | tags | The tags of the image to build. For multiple tags, separate by a space. For example, `latest ${{ github.sha }}` | `latest` 41 | | log_level | [Log level](https://github.com/openshift/source-to-image/blob/master/docs/cli.md#log-levels) when running S2I. Can be 0 (least verbose) to 5 (most verbose). | `1` 42 | | path_context | The location of the path to run S2I from. This should be the path where your source code is stored. | `.` 43 | | include_git | Include all files during the S2I build including the .git folder. Set to `true` to enable. | `false` 44 | 45 | 46 | 47 | ## Action Outputs 48 | 49 | `image`: The name of the built image.
50 | For example, `spring-image`. 51 | 52 | `tags`: A list of the tags that were created, separated by spaces.
53 | For example, `latest v1`. 54 | 55 | ## Builder Images 56 | 57 | Please refer to [this curated list of well maintained builder images](./builder-images.md). 58 | 59 | Many more images can be found under [sclorg](https://github.com/sclorg/). 60 | 61 | ## Examples 62 | 63 | Below is an example end to end workflow to build and push a Java application image using s2i-build. 64 | 65 | ```yaml 66 | # This workflow builds a container image of a java 67 | # application using the source to image build strategy, 68 | # and pushes the image to quay.io. 69 | 70 | steps: 71 | env: 72 | IMAGE_NAME: my-java-app 73 | TAGS: v1 ${{ github.sha }} 74 | 75 | - name: Checkout 76 | uses: actions/checkout@v2 77 | 78 | # Setup S2i and Build container image 79 | - name: Setup and Build 80 | id: build_image 81 | uses: redhat-actions/s2i-build@v2 82 | with: 83 | path_context: '.' 84 | # Builder image for a java project 85 | builder_image: 'registry.access.redhat.com/openjdk/openjdk-11-rhel7' 86 | image: ${{ env.IMAGE_NAME }} 87 | tags: ${{ env.TAGS }} 88 | 89 | # Push Image to Quay registry 90 | - name: Push To Quay Action 91 | uses: redhat-actions/push-to-registry@v2 92 | with: 93 | image: ${{ steps.build_image.outputs.image }} 94 | tags: ${{ steps.build_image.outputs.tags }} 95 | registry: quay.io/${{ secrets.QUAY_USERNAME }} 96 | username: ${{ secrets.QUAY_USERNAME }} 97 | password: ${{ secrets.QUAY_PASSWORD }} 98 | ``` 99 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Source to Image Build' 2 | description: 'Build a container image from source code' 3 | author: 'Red Hat' 4 | branding: 5 | icon: circle 6 | color: red 7 | inputs: 8 | builder_image: 9 | description: 'The path of the s2i builder image.' 10 | required: true 11 | image: 12 | description: 'The Name of the image to build' 13 | required: true 14 | tags: 15 | description: 'The tags of the image to build. For multiple tags, seperate by a space. For example, "latest v1".' 16 | default: 'latest' 17 | required: false 18 | path_context: 19 | description: 'The location of the path to run s2i from' 20 | default: '.' 21 | required: false 22 | log_level: 23 | description: 'Log level when running the S2I' 24 | default: '1' 25 | required: false 26 | env_vars: 27 | description: 'List of environment variable key-value pairs to pass to the s2i builder context' 28 | required: false 29 | include_git: 30 | description: 'Include all files in tar during build which includes .git directory' 31 | required: false 32 | default: 'false' 33 | outputs: 34 | image: 35 | description: 'Name of the image built' 36 | tags: 37 | description: 'List of the tags that were created, separated by spaces' 38 | runs: 39 | using: 'node20' 40 | main: 'dist/index.js' 41 | -------------------------------------------------------------------------------- /builder-images.md: -------------------------------------------------------------------------------- 1 | ## Builder Images 2 | 3 | Here is a non-exhaustive list of well maintained S2I builder images. Many more S2I builders can be found on [sclorg](https://github.com/sclorg/). 4 | 5 | - **go** 6 | - `centos/go-toolset-7-centos7` 7 | - `registry.access.redhat.com/devtools/go-toolset-rhel7` 8 | 9 | - **java** 10 | - `registry.access.redhat.com/ubi8/openjdk-8` 11 | - `registry.access.redhat.com/ubi9/openjdk-11` 12 | - `registry.access.redhat.com/ubi9/openjdk-17` 13 | - `fabric8/s2i-java` 14 | 15 | - **nodejs** 16 | - [`centos/nodejs-6-centos7`](https://hub.docker.com/r/centos/nodejs-6-centos7) 17 | - [`centos/nodejs-8-centos7`](https://hub.docker.com/r/centos/nodejs-8-centos7) 18 | - [`centos/nodejs-10-centos7`](https://hub.docker.com/r/centos/nodejs-10-centos7) 19 | 20 | - **perl** 21 | - [`centos/perl-524-centos7`](https://hub.docker.com/r/centos/perl-524-centos7) 22 | - [`centos/perl-526-centos7`](https://hub.docker.com/r/centos/perl-526-centos7) 23 | - `registry.access.redhat.com/rhscl/perl-526-rhel7` 24 | 25 | - **php** 26 | - [`centos/php-70-centos7`](https://hub.docker.com/r/centos/php-70-centos7) 27 | - [`centos/php-71-centos7`](https://hub.docker.com/r/centos/php-71-centos7) 28 | - [`centos/php-72-centos7`](https://hub.docker.com/r/centos/php-72-centos7) 29 | 30 | - **python** 31 | - [`centos/python-27-centos7`](https://hub.docker.com/r/centos/python-27-centos7) 32 | - [`centos/python-35-centos7`](https://hub.docker.com/r/centos/python-35-centos7) 33 | - [`centos/python-36-centos7`](https://hub.docker.com/r/centos/python-36-centos7) 34 | - [`centos/python-38-centos7`](https://hub.docker.com/r/centos/python-38-centos7) 35 | - `registry.access.redhat.com/rhscl/python-27-rhel7` 36 | - `registry.access.redhat.com/rhscl/python-36-rhel7` 37 | 38 | - **ruby** 39 | - [`centos/ruby-23-centos7`](https://hub.docker.com/r/centos/ruby-23-centos7) 40 | - [`centos/ruby-24-centos7`](https://hub.docker.com/r/centos/ruby-24-centos7) 41 | - [`centos/ruby-25-centos7`](https://hub.docker.com/r/centos/ruby-25-centos7) 42 | - `registry.access.redhat.com/rhscl/ruby-25-rhel7` 43 | 44 | - **dotnet** 45 | - `registry.access.redhat.com/dotnet/dotnet-22-rhel7` 46 | - `registry.access.redhat.com/dotnet/dotnetcore-11-rhel7` 47 | 48 | *Note: The `centos` images above are also available with RHEL as a base. Just replace `centos7` with `rhel7` or `rhel8`*. 49 | -------------------------------------------------------------------------------- /dist/sourcemap-register.js: -------------------------------------------------------------------------------- 1 | (()=>{var e={650:e=>{var r=Object.prototype.toString;var n=typeof Buffer.alloc==="function"&&typeof Buffer.allocUnsafe==="function"&&typeof Buffer.from==="function";function isArrayBuffer(e){return r.call(e).slice(8,-1)==="ArrayBuffer"}function fromArrayBuffer(e,r,t){r>>>=0;var o=e.byteLength-r;if(o<0){throw new RangeError("'offset' is out of bounds")}if(t===undefined){t=o}else{t>>>=0;if(t>o){throw new RangeError("'length' is out of bounds")}}return n?Buffer.from(e.slice(r,r+t)):new Buffer(new Uint8Array(e.slice(r,r+t)))}function fromString(e,r){if(typeof r!=="string"||r===""){r="utf8"}if(!Buffer.isEncoding(r)){throw new TypeError('"encoding" must be a valid string encoding')}return n?Buffer.from(e,r):new Buffer(e,r)}function bufferFrom(e,r,t){if(typeof e==="number"){throw new TypeError('"value" argument must not be a number')}if(isArrayBuffer(e)){return fromArrayBuffer(e,r,t)}if(typeof e==="string"){return fromString(e,r)}return n?Buffer.from(e):new Buffer(e)}e.exports=bufferFrom},274:(e,r,n)=>{var t=n(339);var o=Object.prototype.hasOwnProperty;var i=typeof Map!=="undefined";function ArraySet(){this._array=[];this._set=i?new Map:Object.create(null)}ArraySet.fromArray=function ArraySet_fromArray(e,r){var n=new ArraySet;for(var t=0,o=e.length;t=0){return r}}else{var n=t.toSetString(e);if(o.call(this._set,n)){return this._set[n]}}throw new Error('"'+e+'" is not in the set.')};ArraySet.prototype.at=function ArraySet_at(e){if(e>=0&&e{var t=n(190);var o=5;var i=1<>1;return r?-n:n}r.encode=function base64VLQ_encode(e){var r="";var n;var i=toVLQSigned(e);do{n=i&a;i>>>=o;if(i>0){n|=u}r+=t.encode(n)}while(i>0);return r};r.decode=function base64VLQ_decode(e,r,n){var i=e.length;var s=0;var l=0;var c,p;do{if(r>=i){throw new Error("Expected more digits in base 64 VLQ value.")}p=t.decode(e.charCodeAt(r++));if(p===-1){throw new Error("Invalid base64 digit: "+e.charAt(r-1))}c=!!(p&u);p&=a;s=s+(p<{var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");r.encode=function(e){if(0<=e&&e{r.GREATEST_LOWER_BOUND=1;r.LEAST_UPPER_BOUND=2;function recursiveSearch(e,n,t,o,i,a){var u=Math.floor((n-e)/2)+e;var s=i(t,o[u],true);if(s===0){return u}else if(s>0){if(n-u>1){return recursiveSearch(u,n,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return n1){return recursiveSearch(e,u,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return u}else{return e<0?-1:e}}}r.search=function search(e,n,t,o){if(n.length===0){return-1}var i=recursiveSearch(-1,n.length,e,n,t,o||r.GREATEST_LOWER_BOUND);if(i<0){return-1}while(i-1>=0){if(t(n[i],n[i-1],true)!==0){break}--i}return i}},680:(e,r,n)=>{var t=n(339);function generatedPositionAfter(e,r){var n=e.generatedLine;var o=r.generatedLine;var i=e.generatedColumn;var a=r.generatedColumn;return o>n||o==n&&a>=i||t.compareByGeneratedPositionsInflated(e,r)<=0}function MappingList(){this._array=[];this._sorted=true;this._last={generatedLine:-1,generatedColumn:0}}MappingList.prototype.unsortedForEach=function MappingList_forEach(e,r){this._array.forEach(e,r)};MappingList.prototype.add=function MappingList_add(e){if(generatedPositionAfter(this._last,e)){this._last=e;this._array.push(e)}else{this._sorted=false;this._array.push(e)}};MappingList.prototype.toArray=function MappingList_toArray(){if(!this._sorted){this._array.sort(t.compareByGeneratedPositionsInflated);this._sorted=true}return this._array};r.H=MappingList},758:(e,r)=>{function swap(e,r,n){var t=e[r];e[r]=e[n];e[n]=t}function randomIntInRange(e,r){return Math.round(e+Math.random()*(r-e))}function doQuickSort(e,r,n,t){if(n{var t;var o=n(339);var i=n(345);var a=n(274).I;var u=n(449);var s=n(758).U;function SourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}return n.sections!=null?new IndexedSourceMapConsumer(n,r):new BasicSourceMapConsumer(n,r)}SourceMapConsumer.fromSourceMap=function(e,r){return BasicSourceMapConsumer.fromSourceMap(e,r)};SourceMapConsumer.prototype._version=3;SourceMapConsumer.prototype.__generatedMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_generatedMappings",{configurable:true,enumerable:true,get:function(){if(!this.__generatedMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__generatedMappings}});SourceMapConsumer.prototype.__originalMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_originalMappings",{configurable:true,enumerable:true,get:function(){if(!this.__originalMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__originalMappings}});SourceMapConsumer.prototype._charIsMappingSeparator=function SourceMapConsumer_charIsMappingSeparator(e,r){var n=e.charAt(r);return n===";"||n===","};SourceMapConsumer.prototype._parseMappings=function SourceMapConsumer_parseMappings(e,r){throw new Error("Subclasses must implement _parseMappings")};SourceMapConsumer.GENERATED_ORDER=1;SourceMapConsumer.ORIGINAL_ORDER=2;SourceMapConsumer.GREATEST_LOWER_BOUND=1;SourceMapConsumer.LEAST_UPPER_BOUND=2;SourceMapConsumer.prototype.eachMapping=function SourceMapConsumer_eachMapping(e,r,n){var t=r||null;var i=n||SourceMapConsumer.GENERATED_ORDER;var a;switch(i){case SourceMapConsumer.GENERATED_ORDER:a=this._generatedMappings;break;case SourceMapConsumer.ORIGINAL_ORDER:a=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;a.map((function(e){var r=e.source===null?null:this._sources.at(e.source);r=o.computeSourceURL(u,r,this._sourceMapURL);return{source:r,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name===null?null:this._names.at(e.name)}}),this).forEach(e,t)};SourceMapConsumer.prototype.allGeneratedPositionsFor=function SourceMapConsumer_allGeneratedPositionsFor(e){var r=o.getArg(e,"line");var n={source:o.getArg(e,"source"),originalLine:r,originalColumn:o.getArg(e,"column",0)};n.source=this._findSourceIndex(n.source);if(n.source<0){return[]}var t=[];var a=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,i.LEAST_UPPER_BOUND);if(a>=0){var u=this._originalMappings[a];if(e.column===undefined){var s=u.originalLine;while(u&&u.originalLine===s){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}else{var l=u.originalColumn;while(u&&u.originalLine===r&&u.originalColumn==l){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}}return t};r.SourceMapConsumer=SourceMapConsumer;function BasicSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sources");var u=o.getArg(n,"names",[]);var s=o.getArg(n,"sourceRoot",null);var l=o.getArg(n,"sourcesContent",null);var c=o.getArg(n,"mappings");var p=o.getArg(n,"file",null);if(t!=this._version){throw new Error("Unsupported version: "+t)}if(s){s=o.normalize(s)}i=i.map(String).map(o.normalize).map((function(e){return s&&o.isAbsolute(s)&&o.isAbsolute(e)?o.relative(s,e):e}));this._names=a.fromArray(u.map(String),true);this._sources=a.fromArray(i,true);this._absoluteSources=this._sources.toArray().map((function(e){return o.computeSourceURL(s,e,r)}));this.sourceRoot=s;this.sourcesContent=l;this._mappings=c;this._sourceMapURL=r;this.file=p}BasicSourceMapConsumer.prototype=Object.create(SourceMapConsumer.prototype);BasicSourceMapConsumer.prototype.consumer=SourceMapConsumer;BasicSourceMapConsumer.prototype._findSourceIndex=function(e){var r=e;if(this.sourceRoot!=null){r=o.relative(this.sourceRoot,r)}if(this._sources.has(r)){return this._sources.indexOf(r)}var n;for(n=0;n1){v.source=l+_[1];l+=_[1];v.originalLine=i+_[2];i=v.originalLine;v.originalLine+=1;v.originalColumn=a+_[3];a=v.originalColumn;if(_.length>4){v.name=c+_[4];c+=_[4]}}m.push(v);if(typeof v.originalLine==="number"){d.push(v)}}}s(m,o.compareByGeneratedPositionsDeflated);this.__generatedMappings=m;s(d,o.compareByOriginalPositions);this.__originalMappings=d};BasicSourceMapConsumer.prototype._findMapping=function SourceMapConsumer_findMapping(e,r,n,t,o,a){if(e[n]<=0){throw new TypeError("Line must be greater than or equal to 1, got "+e[n])}if(e[t]<0){throw new TypeError("Column must be greater than or equal to 0, got "+e[t])}return i.search(e,r,o,a)};BasicSourceMapConsumer.prototype.computeColumnSpans=function SourceMapConsumer_computeColumnSpans(){for(var e=0;e=0){var t=this._generatedMappings[n];if(t.generatedLine===r.generatedLine){var i=o.getArg(t,"source",null);if(i!==null){i=this._sources.at(i);i=o.computeSourceURL(this.sourceRoot,i,this._sourceMapURL)}var a=o.getArg(t,"name",null);if(a!==null){a=this._names.at(a)}return{source:i,line:o.getArg(t,"originalLine",null),column:o.getArg(t,"originalColumn",null),name:a}}}return{source:null,line:null,column:null,name:null}};BasicSourceMapConsumer.prototype.hasContentsOfAllSources=function BasicSourceMapConsumer_hasContentsOfAllSources(){if(!this.sourcesContent){return false}return this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some((function(e){return e==null}))};BasicSourceMapConsumer.prototype.sourceContentFor=function SourceMapConsumer_sourceContentFor(e,r){if(!this.sourcesContent){return null}var n=this._findSourceIndex(e);if(n>=0){return this.sourcesContent[n]}var t=e;if(this.sourceRoot!=null){t=o.relative(this.sourceRoot,t)}var i;if(this.sourceRoot!=null&&(i=o.urlParse(this.sourceRoot))){var a=t.replace(/^file:\/\//,"");if(i.scheme=="file"&&this._sources.has(a)){return this.sourcesContent[this._sources.indexOf(a)]}if((!i.path||i.path=="/")&&this._sources.has("/"+t)){return this.sourcesContent[this._sources.indexOf("/"+t)]}}if(r){return null}else{throw new Error('"'+t+'" is not in the SourceMap.')}};BasicSourceMapConsumer.prototype.generatedPositionFor=function SourceMapConsumer_generatedPositionFor(e){var r=o.getArg(e,"source");r=this._findSourceIndex(r);if(r<0){return{line:null,column:null,lastColumn:null}}var n={source:r,originalLine:o.getArg(e,"line"),originalColumn:o.getArg(e,"column")};var t=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,o.getArg(e,"bias",SourceMapConsumer.GREATEST_LOWER_BOUND));if(t>=0){var i=this._originalMappings[t];if(i.source===n.source){return{line:o.getArg(i,"generatedLine",null),column:o.getArg(i,"generatedColumn",null),lastColumn:o.getArg(i,"lastGeneratedColumn",null)}}}return{line:null,column:null,lastColumn:null}};t=BasicSourceMapConsumer;function IndexedSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sections");if(t!=this._version){throw new Error("Unsupported version: "+t)}this._sources=new a;this._names=new a;var u={line:-1,column:0};this._sections=i.map((function(e){if(e.url){throw new Error("Support for url field in sections not implemented.")}var n=o.getArg(e,"offset");var t=o.getArg(n,"line");var i=o.getArg(n,"column");if(t{var t=n(449);var o=n(339);var i=n(274).I;var a=n(680).H;function SourceMapGenerator(e){if(!e){e={}}this._file=o.getArg(e,"file",null);this._sourceRoot=o.getArg(e,"sourceRoot",null);this._skipValidation=o.getArg(e,"skipValidation",false);this._sources=new i;this._names=new i;this._mappings=new a;this._sourcesContents=null}SourceMapGenerator.prototype._version=3;SourceMapGenerator.fromSourceMap=function SourceMapGenerator_fromSourceMap(e){var r=e.sourceRoot;var n=new SourceMapGenerator({file:e.file,sourceRoot:r});e.eachMapping((function(e){var t={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){t.source=e.source;if(r!=null){t.source=o.relative(r,t.source)}t.original={line:e.originalLine,column:e.originalColumn};if(e.name!=null){t.name=e.name}}n.addMapping(t)}));e.sources.forEach((function(t){var i=t;if(r!==null){i=o.relative(r,t)}if(!n._sources.has(i)){n._sources.add(i)}var a=e.sourceContentFor(t);if(a!=null){n.setSourceContent(t,a)}}));return n};SourceMapGenerator.prototype.addMapping=function SourceMapGenerator_addMapping(e){var r=o.getArg(e,"generated");var n=o.getArg(e,"original",null);var t=o.getArg(e,"source",null);var i=o.getArg(e,"name",null);if(!this._skipValidation){this._validateMapping(r,n,t,i)}if(t!=null){t=String(t);if(!this._sources.has(t)){this._sources.add(t)}}if(i!=null){i=String(i);if(!this._names.has(i)){this._names.add(i)}}this._mappings.add({generatedLine:r.line,generatedColumn:r.column,originalLine:n!=null&&n.line,originalColumn:n!=null&&n.column,source:t,name:i})};SourceMapGenerator.prototype.setSourceContent=function SourceMapGenerator_setSourceContent(e,r){var n=e;if(this._sourceRoot!=null){n=o.relative(this._sourceRoot,n)}if(r!=null){if(!this._sourcesContents){this._sourcesContents=Object.create(null)}this._sourcesContents[o.toSetString(n)]=r}else if(this._sourcesContents){delete this._sourcesContents[o.toSetString(n)];if(Object.keys(this._sourcesContents).length===0){this._sourcesContents=null}}};SourceMapGenerator.prototype.applySourceMap=function SourceMapGenerator_applySourceMap(e,r,n){var t=r;if(r==null){if(e.file==null){throw new Error("SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, "+'or the source map\'s "file" property. Both were omitted.')}t=e.file}var a=this._sourceRoot;if(a!=null){t=o.relative(a,t)}var u=new i;var s=new i;this._mappings.unsortedForEach((function(r){if(r.source===t&&r.originalLine!=null){var i=e.originalPositionFor({line:r.originalLine,column:r.originalColumn});if(i.source!=null){r.source=i.source;if(n!=null){r.source=o.join(n,r.source)}if(a!=null){r.source=o.relative(a,r.source)}r.originalLine=i.line;r.originalColumn=i.column;if(i.name!=null){r.name=i.name}}}var l=r.source;if(l!=null&&!u.has(l)){u.add(l)}var c=r.name;if(c!=null&&!s.has(c)){s.add(c)}}),this);this._sources=u;this._names=s;e.sources.forEach((function(r){var t=e.sourceContentFor(r);if(t!=null){if(n!=null){r=o.join(n,r)}if(a!=null){r=o.relative(a,r)}this.setSourceContent(r,t)}}),this)};SourceMapGenerator.prototype._validateMapping=function SourceMapGenerator_validateMapping(e,r,n,t){if(r&&typeof r.line!=="number"&&typeof r.column!=="number"){throw new Error("original.line and original.column are not numbers -- you probably meant to omit "+"the original mapping entirely and only map the generated position. If so, pass "+"null for the original mapping instead of an object with empty or null values.")}if(e&&"line"in e&&"column"in e&&e.line>0&&e.column>=0&&!r&&!n&&!t){return}else if(e&&"line"in e&&"column"in e&&r&&"line"in r&&"column"in r&&e.line>0&&e.column>=0&&r.line>0&&r.column>=0&&n){return}else{throw new Error("Invalid mapping: "+JSON.stringify({generated:e,source:n,original:r,name:t}))}};SourceMapGenerator.prototype._serializeMappings=function SourceMapGenerator_serializeMappings(){var e=0;var r=1;var n=0;var i=0;var a=0;var u=0;var s="";var l;var c;var p;var f;var g=this._mappings.toArray();for(var h=0,d=g.length;h0){if(!o.compareByGeneratedPositionsInflated(c,g[h-1])){continue}l+=","}}l+=t.encode(c.generatedColumn-e);e=c.generatedColumn;if(c.source!=null){f=this._sources.indexOf(c.source);l+=t.encode(f-u);u=f;l+=t.encode(c.originalLine-1-i);i=c.originalLine-1;l+=t.encode(c.originalColumn-n);n=c.originalColumn;if(c.name!=null){p=this._names.indexOf(c.name);l+=t.encode(p-a);a=p}}s+=l}return s};SourceMapGenerator.prototype._generateSourcesContent=function SourceMapGenerator_generateSourcesContent(e,r){return e.map((function(e){if(!this._sourcesContents){return null}if(r!=null){e=o.relative(r,e)}var n=o.toSetString(e);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null}),this)};SourceMapGenerator.prototype.toJSON=function SourceMapGenerator_toJSON(){var e={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null){e.file=this._file}if(this._sourceRoot!=null){e.sourceRoot=this._sourceRoot}if(this._sourcesContents){e.sourcesContent=this._generateSourcesContent(e.sources,e.sourceRoot)}return e};SourceMapGenerator.prototype.toString=function SourceMapGenerator_toString(){return JSON.stringify(this.toJSON())};r.h=SourceMapGenerator},351:(e,r,n)=>{var t;var o=n(591).h;var i=n(339);var a=/(\r?\n)/;var u=10;var s="$$$isSourceNode$$$";function SourceNode(e,r,n,t,o){this.children=[];this.sourceContents={};this.line=e==null?null:e;this.column=r==null?null:r;this.source=n==null?null:n;this.name=o==null?null:o;this[s]=true;if(t!=null)this.add(t)}SourceNode.fromStringWithSourceMap=function SourceNode_fromStringWithSourceMap(e,r,n){var t=new SourceNode;var o=e.split(a);var u=0;var shiftNextLine=function(){var e=getNextLine();var r=getNextLine()||"";return e+r;function getNextLine(){return u=0;r--){this.prepend(e[r])}}else if(e[s]||typeof e==="string"){this.children.unshift(e)}else{throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e)}return this};SourceNode.prototype.walk=function SourceNode_walk(e){var r;for(var n=0,t=this.children.length;n0){r=[];for(n=0;n{function getArg(e,r,n){if(r in e){return e[r]}else if(arguments.length===3){return n}else{throw new Error('"'+r+'" is a required argument.')}}r.getArg=getArg;var n=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;var t=/^data:.+\,.+$/;function urlParse(e){var r=e.match(n);if(!r){return null}return{scheme:r[1],auth:r[2],host:r[3],port:r[4],path:r[5]}}r.urlParse=urlParse;function urlGenerate(e){var r="";if(e.scheme){r+=e.scheme+":"}r+="//";if(e.auth){r+=e.auth+"@"}if(e.host){r+=e.host}if(e.port){r+=":"+e.port}if(e.path){r+=e.path}return r}r.urlGenerate=urlGenerate;function normalize(e){var n=e;var t=urlParse(e);if(t){if(!t.path){return e}n=t.path}var o=r.isAbsolute(n);var i=n.split(/\/+/);for(var a,u=0,s=i.length-1;s>=0;s--){a=i[s];if(a==="."){i.splice(s,1)}else if(a===".."){u++}else if(u>0){if(a===""){i.splice(s+1,u);u=0}else{i.splice(s,2);u--}}}n=i.join("/");if(n===""){n=o?"/":"."}if(t){t.path=n;return urlGenerate(t)}return n}r.normalize=normalize;function join(e,r){if(e===""){e="."}if(r===""){r="."}var n=urlParse(r);var o=urlParse(e);if(o){e=o.path||"/"}if(n&&!n.scheme){if(o){n.scheme=o.scheme}return urlGenerate(n)}if(n||r.match(t)){return r}if(o&&!o.host&&!o.path){o.host=r;return urlGenerate(o)}var i=r.charAt(0)==="/"?r:normalize(e.replace(/\/+$/,"")+"/"+r);if(o){o.path=i;return urlGenerate(o)}return i}r.join=join;r.isAbsolute=function(e){return e.charAt(0)==="/"||n.test(e)};function relative(e,r){if(e===""){e="."}e=e.replace(/\/$/,"");var n=0;while(r.indexOf(e+"/")!==0){var t=e.lastIndexOf("/");if(t<0){return r}e=e.slice(0,t);if(e.match(/^([^\/]+:\/)?\/*$/)){return r}++n}return Array(n+1).join("../")+r.substr(e.length+1)}r.relative=relative;var o=function(){var e=Object.create(null);return!("__proto__"in e)}();function identity(e){return e}function toSetString(e){if(isProtoString(e)){return"$"+e}return e}r.toSetString=o?identity:toSetString;function fromSetString(e){if(isProtoString(e)){return e.slice(1)}return e}r.fromSetString=o?identity:fromSetString;function isProtoString(e){if(!e){return false}var r=e.length;if(r<9){return false}if(e.charCodeAt(r-1)!==95||e.charCodeAt(r-2)!==95||e.charCodeAt(r-3)!==111||e.charCodeAt(r-4)!==116||e.charCodeAt(r-5)!==111||e.charCodeAt(r-6)!==114||e.charCodeAt(r-7)!==112||e.charCodeAt(r-8)!==95||e.charCodeAt(r-9)!==95){return false}for(var n=r-10;n>=0;n--){if(e.charCodeAt(n)!==36){return false}}return true}function compareByOriginalPositions(e,r,n){var t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0||n){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0){return t}t=e.generatedLine-r.generatedLine;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByOriginalPositions=compareByOriginalPositions;function compareByGeneratedPositionsDeflated(e,r,n){var t=e.generatedLine-r.generatedLine;if(t!==0){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0||n){return t}t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsDeflated=compareByGeneratedPositionsDeflated;function strcmp(e,r){if(e===r){return 0}if(e===null){return 1}if(r===null){return-1}if(e>r){return 1}return-1}function compareByGeneratedPositionsInflated(e,r){var n=e.generatedLine-r.generatedLine;if(n!==0){return n}n=e.generatedColumn-r.generatedColumn;if(n!==0){return n}n=strcmp(e.source,r.source);if(n!==0){return n}n=e.originalLine-r.originalLine;if(n!==0){return n}n=e.originalColumn-r.originalColumn;if(n!==0){return n}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsInflated=compareByGeneratedPositionsInflated;function parseSourceMapInput(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}r.parseSourceMapInput=parseSourceMapInput;function computeSourceURL(e,r,n){r=r||"";if(e){if(e[e.length-1]!=="/"&&r[0]!=="/"){e+="/"}r=e+r}if(n){var t=urlParse(n);if(!t){throw new Error("sourceMapURL could not be parsed")}if(t.path){var o=t.path.lastIndexOf("/");if(o>=0){t.path=t.path.substring(0,o+1)}}r=join(urlGenerate(t),r)}return normalize(r)}r.computeSourceURL=computeSourceURL},997:(e,r,n)=>{n(591).h;r.SourceMapConsumer=n(952).SourceMapConsumer;n(351)},284:(e,r,n)=>{e=n.nmd(e);var t=n(997).SourceMapConsumer;var o=n(17);var i;try{i=n(147);if(!i.existsSync||!i.readFileSync){i=null}}catch(e){}var a=n(650);function dynamicRequire(e,r){return e.require(r)}var u=false;var s=false;var l=false;var c="auto";var p={};var f={};var g=/^data:application\/json[^,]+base64,/;var h=[];var d=[];function isInBrowser(){if(c==="browser")return true;if(c==="node")return false;return typeof window!=="undefined"&&typeof XMLHttpRequest==="function"&&!(window.require&&window.module&&window.process&&window.process.type==="renderer")}function hasGlobalProcessEventEmitter(){return typeof process==="object"&&process!==null&&typeof process.on==="function"}function globalProcessVersion(){if(typeof process==="object"&&process!==null){return process.version}else{return""}}function globalProcessStderr(){if(typeof process==="object"&&process!==null){return process.stderr}}function globalProcessExit(e){if(typeof process==="object"&&process!==null&&typeof process.exit==="function"){return process.exit(e)}}function handlerExec(e){return function(r){for(var n=0;n"}var n=this.getLineNumber();if(n!=null){r+=":"+n;var t=this.getColumnNumber();if(t){r+=":"+t}}}var o="";var i=this.getFunctionName();var a=true;var u=this.isConstructor();var s=!(this.isToplevel()||u);if(s){var l=this.getTypeName();if(l==="[object Object]"){l="null"}var c=this.getMethodName();if(i){if(l&&i.indexOf(l)!=0){o+=l+"."}o+=i;if(c&&i.indexOf("."+c)!=i.length-c.length-1){o+=" [as "+c+"]"}}else{o+=l+"."+(c||"")}}else if(u){o+="new "+(i||"")}else if(i){o+=i}else{o+=r;a=false}if(a){o+=" ("+r+")"}return o}function cloneCallSite(e){var r={};Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach((function(n){r[n]=/^(?:is|get)/.test(n)?function(){return e[n].call(e)}:e[n]}));r.toString=CallSiteToString;return r}function wrapCallSite(e,r){if(r===undefined){r={nextPosition:null,curPosition:null}}if(e.isNative()){r.curPosition=null;return e}var n=e.getFileName()||e.getScriptNameOrSourceURL();if(n){var t=e.getLineNumber();var o=e.getColumnNumber()-1;var i=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;var a=i.test(globalProcessVersion())?0:62;if(t===1&&o>a&&!isInBrowser()&&!e.isEval()){o-=a}var u=mapSourcePosition({source:n,line:t,column:o});r.curPosition=u;e=cloneCallSite(e);var s=e.getFunctionName;e.getFunctionName=function(){if(r.nextPosition==null){return s()}return r.nextPosition.name||s()};e.getFileName=function(){return u.source};e.getLineNumber=function(){return u.line};e.getColumnNumber=function(){return u.column+1};e.getScriptNameOrSourceURL=function(){return u.source};return e}var l=e.isEval()&&e.getEvalOrigin();if(l){l=mapEvalOrigin(l);e=cloneCallSite(e);e.getEvalOrigin=function(){return l};return e}return e}function prepareStackTrace(e,r){if(l){p={};f={}}var n=e.name||"Error";var t=e.message||"";var o=n+": "+t;var i={nextPosition:null,curPosition:null};var a=[];for(var u=r.length-1;u>=0;u--){a.push("\n at "+wrapCallSite(r[u],i));i.nextPosition=i.curPosition}i.curPosition=i.nextPosition=null;return o+a.reverse().join("")}function getErrorSource(e){var r=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(e.stack);if(r){var n=r[1];var t=+r[2];var o=+r[3];var a=p[n];if(!a&&i&&i.existsSync(n)){try{a=i.readFileSync(n,"utf8")}catch(e){a=""}}if(a){var u=a.split(/(?:\r\n|\r|\n)/)[t-1];if(u){return n+":"+t+"\n"+u+"\n"+new Array(o).join(" ")+"^"}}}return null}function printErrorAndExit(e){var r=getErrorSource(e);var n=globalProcessStderr();if(n&&n._handle&&n._handle.setBlocking){n._handle.setBlocking(true)}if(r){console.error();console.error(r)}console.error(e.stack);globalProcessExit(1)}function shimEmitUncaughtException(){var e=process.emit;process.emit=function(r){if(r==="uncaughtException"){var n=arguments[1]&&arguments[1].stack;var t=this.listeners(r).length>0;if(n&&!t){return printErrorAndExit(arguments[1])}}return e.apply(this,arguments)}}var S=h.slice(0);var _=d.slice(0);r.wrapCallSite=wrapCallSite;r.getErrorSource=getErrorSource;r.mapSourcePosition=mapSourcePosition;r.retrieveSourceMap=v;r.install=function(r){r=r||{};if(r.environment){c=r.environment;if(["node","browser","auto"].indexOf(c)===-1){throw new Error("environment "+c+" was unknown. Available options are {auto, browser, node}")}}if(r.retrieveFile){if(r.overrideRetrieveFile){h.length=0}h.unshift(r.retrieveFile)}if(r.retrieveSourceMap){if(r.overrideRetrieveSourceMap){d.length=0}d.unshift(r.retrieveSourceMap)}if(r.hookRequire&&!isInBrowser()){var n=dynamicRequire(e,"module");var t=n.prototype._compile;if(!t.__sourceMapSupport){n.prototype._compile=function(e,r){p[r]=e;f[r]=undefined;return t.call(this,e,r)};n.prototype._compile.__sourceMapSupport=true}}if(!l){l="emptyCacheBetweenOperations"in r?r.emptyCacheBetweenOperations:false}if(!u){u=true;Error.prepareStackTrace=prepareStackTrace}if(!s){var o="handleUncaughtExceptions"in r?r.handleUncaughtExceptions:true;try{var i=dynamicRequire(e,"worker_threads");if(i.isMainThread===false){o=false}}catch(e){}if(o&&hasGlobalProcessEventEmitter()){s=true;shimEmitUncaughtException()}}};r.resetRetrieveHandlers=function(){h.length=0;d.length=0;h=S.slice(0);d=_.slice(0);v=handlerExec(d);m=handlerExec(h)}},147:e=>{"use strict";e.exports=require("fs")},17:e=>{"use strict";e.exports=require("path")}};var r={};function __webpack_require__(n){var t=r[n];if(t!==undefined){return t.exports}var o=r[n]={id:n,loaded:false,exports:{}};var i=true;try{e[n](o,o.exports,__webpack_require__);i=false}finally{if(i)delete r[n]}o.loaded=true;return o.exports}(()=>{__webpack_require__.nmd=e=>{e.paths=[];if(!e.children)e.children=[];return e}})();if(typeof __webpack_require__!=="undefined")__webpack_require__.ab=__dirname+"/";var n={};(()=>{__webpack_require__(284).install()})();module.exports=n})(); -------------------------------------------------------------------------------- /git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ### Copy this into .git/hooks and overwrite the empty one. 4 | ### This will ensure the bundle and ins-outs verification checks won't fail for you. 5 | 6 | echo "----- Pre-commit -----" 7 | set -ex 8 | npx action-io-generator -o src/generated/inputs-outputs.ts 9 | npm run lint 10 | npm run bundle 11 | git add -v dist/ src/generated -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "s2i-build", 3 | "version": "1.0.0", 4 | "engines": { 5 | "node": "16" 6 | }, 7 | "description": "", 8 | "main": "dist/index.js", 9 | "scripts": { 10 | "compile": "tsc -p .", 11 | "bundle": "ncc build src/s2iExec.ts --source-map --minify", 12 | "clean": "rm -rf lib/ dist/", 13 | "lint": "eslint . --max-warnings=0" 14 | }, 15 | "keywords": [], 16 | "author": "Red Hat Inc.", 17 | "license": "MIT", 18 | "dependencies": { 19 | "@actions/core": "^1.10.1", 20 | "@actions/exec": "^1.1.1", 21 | "@actions/io": "^1.1.3", 22 | "@actions/tool-cache": "^2.0.1", 23 | "glob": "^7", 24 | "mz": "^2.7.0", 25 | "valid-url": "^1.0.9" 26 | }, 27 | "devDependencies": { 28 | "@redhat-actions/action-io-generator": "^1.5.0", 29 | "@redhat-actions/eslint-config": "^1.3.2", 30 | "@redhat-actions/tsconfig": "^1.2.0", 31 | "@types/glob": "^8.1.0", 32 | "@types/mz": "^2.7.8", 33 | "@types/node": "^20", 34 | "@types/valid-url": "^1.0.7", 35 | "@typescript-eslint/eslint-plugin": "^7.2.0", 36 | "@typescript-eslint/parser": "^7.2.0", 37 | "@vercel/ncc": "^0.38.1", 38 | "eslint": "^8.57.0", 39 | "typescript": "^5.4.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/command.ts: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as core from "@actions/core"; 7 | import * as exec from "@actions/exec"; 8 | import * as io from "@actions/io"; 9 | import * as path from "path"; 10 | import { CommandResult } from "./types"; 11 | import { joinList } from "./utils/execHelper"; 12 | 13 | export class Command { 14 | public static async execute( 15 | executable: string, 16 | args: string[], 17 | execOptions: exec.ExecOptions & { group?: boolean } = {}, 18 | ): Promise { 19 | let stdout = ""; 20 | let stderr = ""; 21 | 22 | const finalExecOptions = { ...execOptions }; 23 | finalExecOptions.ignoreReturnCode = true; // the return code is processed below 24 | 25 | finalExecOptions.listeners = { 26 | stdline: (line): void => { 27 | stdout += line + "\n"; 28 | }, 29 | errline: (line): void => { 30 | stderr += line + "\n"; 31 | }, 32 | }; 33 | 34 | if (execOptions.group) { 35 | const groupName = [ executable, ...args ].join(" "); 36 | core.startGroup(groupName); 37 | } 38 | 39 | try { 40 | const exitCode = await exec.exec(executable, args, finalExecOptions); 41 | 42 | if (execOptions.ignoreReturnCode !== true && exitCode !== 0) { 43 | // Throwing the stderr as part of the Error makes the stderr show up in the action outline, 44 | // which saves some clicking when debugging. 45 | let error = `${path.basename(executable)} exited with code ${exitCode}`; 46 | if (stderr) { 47 | error += `\n${stderr}`; 48 | } 49 | throw new Error(error); 50 | } 51 | 52 | return { 53 | exitCode, output: stdout, error: stderr, 54 | }; 55 | } 56 | 57 | finally { 58 | if (execOptions.group) { 59 | core.endGroup(); 60 | } 61 | } 62 | } 63 | 64 | public static async tag(image: string, tags: string[]): Promise { 65 | // get docker cli 66 | const dockerPath = await io.which("docker", true); 67 | 68 | const args: string[] = [ "tag" ]; 69 | for (const tag of tags) { 70 | args.push(`${image}:${tag}`); 71 | } 72 | core.info(`Tagging the built image with tags ${joinList(tags)}`); 73 | await Command.execute(dockerPath, args); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | export const S2I_BASE_URL = "https://github.com/openshift/source-to-image/releases/download/v1.3.9/source-to-image-v1.3.9-574a2640-"; 6 | 7 | export const S2I_WIN_ZIP = "windows-amd64.zip"; 8 | export const S2I_LINUX_TAR_GZ = "linux-amd64.tar.gz"; 9 | export const S2I_MACOSX_TAR_GZ = "darwin-amd64.tar.gz"; 10 | -------------------------------------------------------------------------------- /src/generated/inputs-outputs.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by action-io-generator. Do not edit by hand! 2 | export enum Inputs { 3 | /** 4 | * The path of the s2i builder image. 5 | * Required: true 6 | * Default: None. 7 | */ 8 | BUILDER_IMAGE = "builder_image", 9 | /** 10 | * List of environment variable key-value pairs to pass to the s2i builder context 11 | * Required: false 12 | * Default: None. 13 | */ 14 | ENV_VARS = "env_vars", 15 | /** 16 | * The Name of the image to build 17 | * Required: true 18 | * Default: None. 19 | */ 20 | IMAGE = "image", 21 | /** 22 | * Include all files in tar during build which includes .git directory 23 | * Required: false 24 | * Default: "false" 25 | */ 26 | INCLUDE_GIT = "include_git", 27 | /** 28 | * Log level when running the S2I 29 | * Required: false 30 | * Default: "1" 31 | */ 32 | LOG_LEVEL = "log_level", 33 | /** 34 | * The location of the path to run s2i from 35 | * Required: false 36 | * Default: "." 37 | */ 38 | PATH_CONTEXT = "path_context", 39 | /** 40 | * The tags of the image to build. For multiple tags, seperate by a space. For example, "latest v1". 41 | * Required: false 42 | * Default: "latest" 43 | */ 44 | TAGS = "tags", 45 | } 46 | 47 | export enum Outputs { 48 | /** 49 | * Name of the image built 50 | * Required: false 51 | * Default: None. 52 | */ 53 | IMAGE = "image", 54 | /** 55 | * List of the tags that were created, separated by spaces 56 | * Required: false 57 | * Default: None. 58 | */ 59 | TAGS = "tags", 60 | } 61 | -------------------------------------------------------------------------------- /src/installer.ts: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | import * as core from "@actions/core"; 6 | import * as fs from "mz/fs"; 7 | import * as glob from "glob"; 8 | import * as ioUtil from "@actions/io/lib/io-util"; 9 | import * as tc from "@actions/tool-cache"; 10 | import { BinaryVersion, FindBinaryStatus } from "./utils/execHelper"; 11 | import { 12 | S2I_BASE_URL, S2I_WIN_ZIP, S2I_MACOSX_TAR_GZ, S2I_LINUX_TAR_GZ, 13 | } from "./constants"; 14 | 15 | export class Installer { 16 | static async installS2i(versionToUse: BinaryVersion, runnerOS: string): Promise { 17 | if (versionToUse.valid === false) { 18 | return { found: false, reason: versionToUse.reason }; 19 | } 20 | 21 | const url = await Installer.getS2iURLToDownload(versionToUse, runnerOS); 22 | if (!url) { 23 | return { found: false, reason: "Unable to determine URL where to download s2i executable." }; 24 | } 25 | 26 | core.debug(`downloading: ${url}`); 27 | const s2iBinary = await Installer.downloadAndExtract(url, runnerOS); 28 | return s2iBinary; 29 | } 30 | 31 | static async getS2iURLToDownload(version: BinaryVersion, runnerOS: string): Promise { 32 | let url: string | undefined = `${S2I_BASE_URL}`; 33 | if (!version.valid) { 34 | return undefined; 35 | } 36 | 37 | if (version.type === "url") { 38 | return version.value; 39 | } 40 | 41 | if (version.type === "latest") { 42 | url = await Installer.latest(runnerOS); 43 | return url; 44 | } 45 | 46 | const bundle = Installer.getS2iBundleByOS(runnerOS); 47 | if (!bundle) { 48 | core.debug("Unable to find s2i bundle url"); 49 | return undefined; 50 | } 51 | 52 | url += bundle; 53 | 54 | core.debug(`archive URL: ${url}`); 55 | return url; 56 | } 57 | 58 | static async latest(runnerOS: string): Promise { 59 | const bundle = Installer.getS2iBundleByOS(runnerOS); 60 | if (!bundle) { 61 | core.debug("Unable to find s2i bundle url"); 62 | return undefined; 63 | } 64 | 65 | let url = `${S2I_BASE_URL}`; 66 | url += `${bundle}`; 67 | 68 | core.debug(`latest stable s2i version: ${url}`); 69 | return url; 70 | } 71 | 72 | static async downloadAndExtract(url: string, runnerOS: string): Promise { 73 | if (!url) { 74 | return { found: false, reason: "URL where to download s2i is not valid." }; 75 | } 76 | 77 | let downloadDir = ""; 78 | const pathS2iArchive = await tc.downloadTool(url); 79 | if (runnerOS === "Windows") { 80 | downloadDir = await tc.extractZip(pathS2iArchive); 81 | } 82 | else { 83 | downloadDir = await tc.extractTar(pathS2iArchive); 84 | } 85 | 86 | let s2iBinary: string = Installer.s2iBinaryByOS(runnerOS); 87 | s2iBinary = await Installer.findS2iFile(downloadDir, s2iBinary); 88 | if (!(await ioUtil.exists(s2iBinary))) { 89 | return { 90 | found: false, 91 | reason: `An error occurred while downloading and extracting s2i binary from ${url}.` 92 | + `File ${s2iBinary} not found.`, 93 | }; 94 | } 95 | fs.chmodSync(s2iBinary, "0755"); 96 | return { found: true, path: s2iBinary }; 97 | } 98 | 99 | static getS2iBundleByOS(runnerOS: string): string | undefined { 100 | let url = ""; 101 | // determine the bundle path based on the OS type 102 | switch (runnerOS) { 103 | case "Linux": { 104 | url += `${S2I_LINUX_TAR_GZ}`; 105 | break; 106 | } 107 | case "macOS": { 108 | url += `${S2I_MACOSX_TAR_GZ}`; 109 | break; 110 | } 111 | case "Windows": { 112 | url += `${S2I_WIN_ZIP}`; 113 | break; 114 | } 115 | default: { 116 | return undefined; 117 | } 118 | } 119 | return url; 120 | } 121 | 122 | private static s2iBinaryByOS(osType: string): string { 123 | if (osType.includes("Windows")) return "s2i.exe"; 124 | return "s2i"; 125 | } 126 | 127 | static async findS2iFile(folder: string, file: string): Promise { 128 | return new Promise((resolve, reject) => { 129 | glob(`${folder}/**/${file}`, (err, res) => { 130 | if (err) { 131 | reject(new Error(`Unable to find s2i exewcutable inside the directory ${folder}`)); 132 | } 133 | else { 134 | resolve(res[0]); 135 | } 136 | }); 137 | }); 138 | } 139 | 140 | /** 141 | * Adds s2i to the PATH environment variable. 142 | * 143 | * @param s2iPath the full path to the s2i binary. Must be a non null. 144 | * @param osType the OS type. One of 'Linux', 'Darwin' or 'Windows_NT'. 145 | */ 146 | static addS2iToPath(s2iPath: string, osType: string): void { 147 | if (!s2iPath) { 148 | core.debug("Unable to add null or empty s2i path to the PATH."); 149 | return; 150 | } 151 | let dir = ""; 152 | if (osType.includes("Windows")) { 153 | dir = s2iPath.substr(0, s2iPath.lastIndexOf("\\")); 154 | } 155 | else { 156 | dir = s2iPath.substr(0, s2iPath.lastIndexOf("/")); 157 | } 158 | core.addPath(dir); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/s2iExec.ts: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | import * as core from "@actions/core"; 6 | import * as fs from "fs"; 7 | import * as io from "@actions/io"; 8 | import * as path from "path"; 9 | import { Command } from "./command"; 10 | import { Installer } from "./installer"; 11 | import { Inputs, Outputs } from "./generated/inputs-outputs"; 12 | import { 13 | BinaryVersion, convertStringToBinaryVersion, FindBinaryStatus, getReason, 14 | } from "./utils/execHelper"; 15 | 16 | export async function run(): Promise { 17 | const DEFAULT_TAG = "latest"; 18 | const builderImage = core.getInput(Inputs.BUILDER_IMAGE, { required: true }); 19 | const image = core.getInput(Inputs.IMAGE, { required: true }); 20 | const tags = core.getInput(Inputs.TAGS, { required: false }); 21 | const pathContext = core.getInput(Inputs.PATH_CONTEXT, { required: false }); 22 | const logLevel = core.getInput(Inputs.LOG_LEVEL, { required: false }); 23 | const envVars = core.getInput(Inputs.ENV_VARS, { required: false }); 24 | const includeGit = core.getInput(Inputs.INCLUDE_GIT, { required: false }) || "false"; 25 | const runnerOS = process.env.RUNNER_OS || process.platform; 26 | 27 | const tagsList: string[] = tags.split(" "); 28 | 29 | let s2iPath = await io.which("s2i", false); 30 | 31 | if (s2iPath === "") { 32 | const s2iVersion = "v1.3.9"; 33 | core.info(`⏳ s2i is not installed. Installing s2i ${s2iVersion}`); 34 | const binaryVersion: BinaryVersion = convertStringToBinaryVersion(s2iVersion); 35 | const s2iBinary: FindBinaryStatus = await Installer.installS2i(binaryVersion, runnerOS); 36 | 37 | if (s2iBinary.found === false) { 38 | throw new Error(getReason(s2iBinary)); 39 | } 40 | 41 | core.info(`✅ Sucessfully installed s2i.`); 42 | 43 | s2iPath = s2iBinary.path; 44 | } 45 | else { 46 | core.info(`ℹ️ s2i is already installed, skipping installation`); 47 | } 48 | Installer.addS2iToPath(s2iPath, runnerOS); 49 | 50 | core.debug(runnerOS); 51 | 52 | // info message if user doesn't provides any tag 53 | if (tagsList.length === 0) { 54 | core.info(`Input "${Inputs.TAGS}" is not provided, using default tag "${DEFAULT_TAG}"`); 55 | tagsList.push(DEFAULT_TAG); 56 | } 57 | 58 | const buildCmd = [ 59 | "build", pathContext, builderImage, `${image}:${tagsList[0]}`, "--loglevel", logLevel, 60 | ]; 61 | 62 | if (includeGit && includeGit !== "false") { 63 | buildCmd.push("--copy"); 64 | buildCmd.push("--exclude=''"); 65 | } 66 | 67 | if (envVars) { 68 | const sha = process.env.GITHUB_SHA; 69 | const shortSha = sha ? sha.substring(0, 7) : ""; 70 | 71 | const envFileName = `s2i-${shortSha}.env`; 72 | const envFilePath = path.join(process.cwd(), envFileName); 73 | 74 | await fs.promises.writeFile(envFilePath, envVars); 75 | 76 | const envCount = envVars.split("\n").length; 77 | core.info(`Writing ${envCount} environment variables to ${envFilePath}`); 78 | 79 | buildCmd.push("--environment-file"); 80 | buildCmd.push(envFilePath); 81 | } 82 | 83 | await Command.execute(s2iPath, buildCmd); 84 | 85 | if (tagsList.length > 1) { 86 | await Command.tag(image, tagsList); 87 | } 88 | 89 | core.setOutput(Outputs.IMAGE, image); 90 | core.setOutput(Outputs.TAGS, tags); 91 | } 92 | 93 | run().catch(core.setFailed); 94 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | **************************************************************************************************/ 5 | 6 | export type CommandResult = { 7 | exitCode: number 8 | output: string 9 | error: string 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/execHelper.ts: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | import * as validUrl from "valid-url"; 6 | 7 | interface BinaryVersionValid { 8 | readonly valid: true; 9 | readonly type: "url" | "number" | "latest"; 10 | readonly value: string; 11 | } 12 | 13 | interface BinaryVersionNotValid { 14 | readonly valid: false; 15 | readonly reason: string; 16 | } 17 | 18 | export type BinaryVersion = BinaryVersionValid | BinaryVersionNotValid; 19 | 20 | export interface BinaryFound { 21 | readonly found: true; 22 | readonly path: string; 23 | } 24 | 25 | export interface BinaryNotFound { 26 | readonly found: false; 27 | readonly reason?: string; 28 | } 29 | 30 | export type FindBinaryStatus = BinaryFound | BinaryNotFound; 31 | 32 | export function convertStringToBinaryVersion(version: string): BinaryVersion { 33 | if (!version) { 34 | return { valid: false, reason: "The action was run without any version as input." }; 35 | } 36 | if (version === "latest") { 37 | return { valid: true, type: "latest", value: version }; 38 | } 39 | if (validUrl.isWebUri(version)) { 40 | return { valid: true, type: "url", value: version }; 41 | } 42 | 43 | const regexVersion = /[0-9]+[.]{1}[0-9]+[.]{0,1}[0-9]*/; 44 | const versionObj = regexVersion.exec(version); 45 | if (versionObj && versionObj.length > 0) { 46 | return { valid: true, type: "number", value: version }; 47 | } 48 | 49 | return { valid: false, reason: "Version is written in an unknown format" }; 50 | } 51 | 52 | export function getReason(version: BinaryNotFound): string { 53 | return version.reason ? version.reason : "error"; 54 | } 55 | 56 | /** 57 | * Joins a string array into a user-friendly list. 58 | * Eg, `joinList([ "tim", "erin", "john" ], "and")` => "tim, erin and john" 59 | */ 60 | export function joinList(strings_: readonly string[], andOrOr: "and" | "or" = "and"): string { 61 | // we have to duplicate "strings" here since we modify the array below and it's passed by reference 62 | const strings = Array.from(strings_).filter((s) => { 63 | if (!s) { 64 | return false; 65 | } 66 | return true; 67 | }); 68 | 69 | // separate the last string from the others since we have to prepend andOrOr to it 70 | const lastString = strings.splice(strings.length - 1, 1)[0]; 71 | 72 | let joined = strings.join(", "); 73 | if (strings.length > 0) { 74 | joined = `${joined} ${andOrOr} ${lastString}`; 75 | } 76 | else { 77 | joined = lastString; 78 | } 79 | return joined; 80 | } 81 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@redhat-actions/tsconfig", 3 | "compilerOptions": { 4 | "rootDir": "src/", 5 | "outDir": "out/" 6 | }, 7 | "include": [ 8 | "src/" 9 | ], 10 | } --------------------------------------------------------------------------------