├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── build.yml │ └── codeql.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── action.yml ├── dist ├── cpufeatures-GVCOVLZP.node ├── index.js ├── index.js.map └── sshcrypto-FT2U4C3H.node ├── package-lock.json ├── package.json ├── src ├── cache │ ├── SFTPCache.ts │ ├── SpigotArtifactArchiver.ts │ └── SpigotArtifactCache.ts ├── index.ts └── utils.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/** -diff linguist-generated=true -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # sprax 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report and help BetterChairs and its community 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | * **Action Version**: 11 | * **Action Configuration**: 12 | 13 | 14 | **Describe the bug** 15 | A clear and concise description of the bug. 16 | 17 | **To Reproduce** 18 | Steps to reproduce the behavior: 19 | 1. Go to ... 20 | 2. ... 21 | 22 | **Screenshots, Videos, Logs or Errors** 23 | If applicable, add screenshots to help explain your problem. 24 | If possible, try uploading images or large logs as an attachment instead of somewhere else to ensure its availability. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | target-branch: dev 5 | directory: / 6 | schedule: 7 | interval: monthly 8 | ignore: 9 | - dependency-name: '@types/*' 10 | 11 | - package-ecosystem: github-actions 12 | directory: / 13 | schedule: 14 | interval: monthly 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 'Build & Run' 2 | on: 3 | push: 4 | branches: [ main, dev ] 5 | pull_request: 6 | 7 | jobs: 8 | # Make sure clean build works properly 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-node@v4 14 | with: 15 | node-version: 20 16 | - name: Install dependencies and build 17 | run: | 18 | npm ci 19 | npm run build -- --loader:.node=file 20 | 21 | # Make sure the action works on a clean machine without building it 22 | run: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - uses: actions/setup-node@v4 27 | with: 28 | node-version: 20 29 | 30 | # Setup JDK version 31 | - uses: actions/setup-java@v4 32 | with: 33 | java-version: 8 34 | distribution: temurin 35 | 36 | - name: Compile 1.8 spigot version 37 | uses: ./ 38 | with: 39 | versions: 1.8 40 | 41 | - uses: actions/setup-java@v4 42 | with: 43 | java-version: 23 44 | distribution: temurin 45 | 46 | - name: Compile latest spigot version 47 | uses: ./ 48 | with: 49 | versions: latest 50 | 51 | # Run again. The Action should detect that the requested versions are already inside the local maven repo 52 | - name: Compile the same version again (latest) 53 | uses: ./ 54 | with: 55 | # These versions should match the ones above 56 | versions: latest 57 | 58 | - uses: actions/setup-java@v4 59 | with: 60 | java-version: 8 61 | distribution: temurin 62 | 63 | - name: Compile the same version again (1.8) 64 | uses: ./ 65 | with: 66 | # These versions should match the ones above 67 | versions: 1.8 68 | 69 | - name: Upload logs 70 | if: ${{ always() }} 71 | uses: actions/upload-artifact@v4 72 | with: 73 | name: logs 74 | path: /tmp/SpraxDev-Action-SpigotMC/logs/ 75 | 76 | # Run the original BuildTools in GitHub Actions to easily compare the build times etc. 77 | original-run: 78 | runs-on: ubuntu-latest 79 | steps: 80 | # Setup JDK version 81 | - uses: actions/setup-java@v4 82 | with: 83 | # This version should probably match the ones from the 'run'-job 84 | java-version: 8 85 | distribution: temurin 86 | 87 | - name: Run original Spigot-BuildTools (1.8) 88 | # These versions should match the ones from the 'run'-job 89 | # Using '--compile Spigot' as this action does the same by default 90 | run: | 91 | wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar 92 | java -jar BuildTools.jar --rev 1.8 --compile Spigot 93 | 94 | - uses: actions/setup-java@v4 95 | with: 96 | # This version should probably match the ones from the 'run'-job 97 | java-version: 23 98 | distribution: temurin 99 | 100 | - name: Run original Spigot-BuildTools (latest) 101 | # These versions should match the ones from the 'run'-job 102 | # Using '--compile Spigot' as this action does the same by default 103 | run: | 104 | wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar 105 | java -jar BuildTools.jar --rev latest --compile Spigot 106 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '26 18 * * 5' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 360 16 | permissions: 17 | # required for all workflows 18 | security-events: write 19 | 20 | # required to fetch internal or private CodeQL packs 21 | packages: read 22 | 23 | # only required for workflows in private repositories 24 | actions: read 25 | contents: read 26 | 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | include: 31 | - language: javascript-typescript 32 | build-mode: none 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v4 36 | 37 | # Initializes the CodeQL tools for scanning. 38 | - name: Initialize CodeQL 39 | uses: github/codeql-action/init@v3 40 | with: 41 | languages: ${{ matrix.language }} 42 | build-mode: ${{ matrix.build-mode }} 43 | 44 | - name: Perform CodeQL Analysis 45 | uses: github/codeql-action/analyze@v3 46 | with: 47 | category: "/language:${{matrix.language}}" 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build/ 3 | 4 | # JetBrains IDEs 5 | /.idea/ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at developer@sprax2013.de. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Action-SpigotMC 2 | We love your input! 3 | We want to make contributing to this project as easy and transparent as possible, whether it's: 4 | 5 | * Reporting a bug 6 | * Discussing the current state of the code 7 | * Submitting a fix 8 | * Proposing new features 9 | 10 | ## We Develop with GitHub 11 | We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. 12 | 13 | ## We Use [GitHub Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests 14 | Pull requests are the best way to propose changes to the codebase (we use [GitHub Flow](https://guides.github.com/introduction/flow/index.html)). 15 | We actively welcome your pull requests: 16 | 17 | 1. Fork the repo and create your branch from `main`. 18 | 2. If you've added code that should be tested, add tests. 19 | 3. If you've changed APIs, update the documentation. 20 | 5. Ensure the test suite passes. 21 | 6. Issue that pull request! 22 | 23 | ## Any contributions you make will be under the MIT Software License 24 | In short, when you submit code changes, your submissions are understood to be under the same 25 | [MIT License](https://choosealicense.com/licenses/mit/) that covers the project. 26 | Feel free to contact the maintainers if that's a concern. 27 | 28 | ## Report bugs using GitHub's [issues](https://github.com/SpraxDev/Action-SpigotMC/issues/new/choose) 29 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/SpraxDev/Action-SpigotMC/issues/new/choose) – it's that easy! 30 | 31 | ## Write bug reports with detail, background, and sample code 32 | Make sure to provide all the relevant information needed to reproduce the bug you are reporting. You can upload logs, images or videos if needed. 33 | 34 | **Great Bug Reports** tend to have: 35 | 36 | * A quick summary and/or background 37 | * Steps to reproduce 38 | * Be specific! 39 | * Give sample code if you can – Try to make it as universal as possible, so is quickly understood. 40 | * What you expected would happen 41 | * What actually happens 42 | * Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 43 | 44 | People *love* thorough bug reports. I'm not even kidding. 45 | 46 | ## Use a Consistent Coding Style 47 | * Use spaces for indentation rather than tabs 48 | * Use `const` or `let` rather than `var` 49 | 50 | ## License 51 | By contributing, you agree that your contributions will be licensed under its [MIT License](./LICENSE.md). 52 | 53 | ## Enforcement 54 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at developer@sprax2013.de. 55 | All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. 56 | The project team is obligated to maintain confidentiality with regard to the reporter of an incident. 57 | Further details of specific enforcement policies may be posted separately. 58 | 59 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined 60 | by other members of the project's leadership. 61 | 62 | ## References 63 | This document was adapted from [briandk](https://gist.github.com/briandk/3d2e8b3ec8daf5a27a62), who originally adapted it from Facebook's Draft. 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Christian Koop 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Get Support on Discord 4 | 5 | 6 | Support me on Patreon 8 | 9 |

10 | 11 |

12 | 13 | Build & Run 14 | 15 | 16 | Quality Gate Status 18 | 19 |

20 | 21 | # Action-SpigotMC 22 | This Action allows you to easily compile Minecraft Spigot 23 | and install it in your runners local maven repository. 24 | 25 | Supported: 26 | * SpigotMC (using the latest version of the official BuildTools) 27 | 28 | You configure all the versions you want, and it'll compile all the missing versions automatically. 29 | By checking for a file in the local maven repository beforehand, build times can be reduces drastically. 30 | 31 | 32 | ## Usage 33 | **Note:** Use `actions/cache` as described [here](https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-java-with-maven#caching-dependencies) to save some additional time by caching between runs! 34 | 35 | All the values already provided below are their default values. 36 | 37 | If you don't change them, you can remove them from your workflow, 38 | as they are set automatically. 39 | 40 | ```yaml 41 | - uses: SpraxDev/Action-SpigotMC@v5 42 | with: 43 | # A comma-separated list of Spigot version that should be compiled 44 | # These values are later given to the BuildTools.jar as '--rev' argument 45 | # 46 | # Example: latest, 1.19.2, 1.8.8 47 | versions: latest # Optional 48 | 49 | # Should sources be generated? 50 | # If enabled, BuildTools is provided the '--generate-source' argument 51 | generateSrc: false # Optional 52 | 53 | # Should the documentation be generated? 54 | # If enabled, BuildTools is provided the '--generate-docs' argument 55 | generateDoc: false # Optional 56 | 57 | # Should we disable the BuildTools's Java-Version-Check? 58 | # If enabled, BuildTools is provided the '--disable-java-check' argument 59 | disableJavaCheck: false # Optional 60 | 61 | # Should we download additional files to deobfuscate CraftBukkit and NMS? 62 | # If enabled, BuildTools will also install additional files that are required to deobfuscate CraftBukkit and NMS with the SpecialSource-Plugin 63 | remapped: false # Optional 64 | 65 | # Disables the check for existing files in the local maven repository 66 | # Normally, a version is skipped if it is already installed 67 | # in the local maven repository to speed up build time 68 | forceRun: false # Optional 69 | 70 | # The amount of builds allowed to run at the same time 71 | # Set to '-1' to use system's cpu core count 72 | threads: -1 # Optional 73 | 74 | # You can choose between different BuildTools to be used by this action 75 | # Available: SpigotMC 76 | buildToolProvider: SpigotMC # Optional 77 | 78 | 79 | # The host of the SFTP-Server to use as dedicated artifact cache 80 | sftpCacheHost: '' # Optional 81 | 82 | # The port of the SFTP-Server to use as dedicated artifact cache 83 | sftpCachePort: 22 # Optional 84 | 85 | # The username of the SFTP-Server to use as dedicated artifact cache 86 | sftpCacheUser: '' # Optional 87 | 88 | # The private key of the SFTP-Server to use as dedicated artifact cache 89 | # The configured value should start with `-----BEGIN OPENSSH PRIVATE KEY-----` 90 | sftpCachePrivateKey: '' # Optional 91 | 92 | # Setting this to the server's host key, will enable strictly checking the host key 93 | # something like `ssh-ed25519 [HASH]` is expected here 94 | sftpCacheExpectedHostKey: '' # Optional 95 | ``` 96 | 97 | ## Cache Spigot artifacts on a dedicated SFTP-Server 98 | Using GitHub's `actions/cache` is already great but may not be enough for some use-cases, 99 | causing all those Spigot versions to be recompiled more often than necessary. 100 | 101 | To solve this, you are able to configure your own SFTP-Server that should be used to store and restore built Spigot artifacts. 102 | 103 | To be clear, we still recommend using `actions/cache` in addition to this feature – This is not aimed to be a replacement. 104 | 105 | In theory, using this feature allows you only build a version once and then share it across all your repositories and workflows. 106 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: Compile Minecraft Spigot (via BuildTools) 2 | description: Makes it easier to compile multiple Spigot versions at the same time and speed up clean builds 3 | author: Christian Koop 4 | 5 | branding: 6 | icon: arrow-down-circle 7 | color: orange 8 | 9 | inputs: 10 | versions: 11 | required: false 12 | default: latest 13 | description: Versions to build (separate multiple with ',') 14 | generateSrc: 15 | required: false 16 | default: 'false' 17 | description: Should sources be generated? 18 | remapped: 19 | required: false 20 | default: 'false' 21 | description: Should use the SpecialSource Plugin of md_5 to deobfuscate Mojang-NMS-Packets 22 | generateDoc: 23 | required: false 24 | default: 'false' 25 | description: Should the documentation be generated? 26 | disableJavaCheck: 27 | required: false 28 | default: 'false' 29 | description: Should we disable the BuildTools's Java-Version-Check 30 | forceRun: 31 | required: false 32 | default: 'false' 33 | description: Disables the check for existing files in the local maven repository 34 | threads: 35 | required: false 36 | default: '-1' 37 | description: The amount of builds allowed to run at a time, set to '-1' to use system's cpu count 38 | buildToolProvider: 39 | required: false 40 | default: SpigotMC 41 | description: Whose BuildTool should be used? (SpigotMC [default]) 42 | finalJarOutputDir: 43 | required: false 44 | default: '' 45 | description: The directory BuildTools should put the final server jar into 46 | sftpCacheHost: 47 | required: false 48 | default: '' 49 | description: The host of the SFTP-Server to use as dedicated artifact cache 50 | sftpCachePort: 51 | required: false 52 | default: '22' 53 | description: The port of the SFTP-Server to use as dedicated artifact cache 54 | sftpCacheUser: 55 | required: false 56 | default: '' 57 | description: The username of the SFTP-Server to use as dedicated artifact cache 58 | sftpCachePrivateKey: 59 | required: false 60 | default: '' 61 | description: The private key of the SFTP-Server to use as dedicated artifact cache 62 | sftpCacheExpectedHostKey: 63 | required: false 64 | default: '' 65 | description: Setting this to the server's host key, will enable strictly checking the host key (something like `ssh-ed25519 [HASH]` is expected here) 66 | 67 | runs: 68 | using: node20 69 | main: dist/index.js 70 | -------------------------------------------------------------------------------- /dist/cpufeatures-GVCOVLZP.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpraxDev/Action-SpigotMC/d2d97bc28b91bd9b71c2406104c55f0f9ef38682/dist/cpufeatures-GVCOVLZP.node -------------------------------------------------------------------------------- /dist/sshcrypto-FT2U4C3H.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpraxDev/Action-SpigotMC/d2d97bc28b91bd9b71c2406104c55f0f9ef38682/dist/sshcrypto-FT2U4C3H.node -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "action-spigotmc", 3 | "version": "5.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "action-spigotmc", 9 | "version": "5.1.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@actions/core": "^1.11.1", 13 | "async": "^3.2.6", 14 | "fs-extra": "^11.3.0", 15 | "n-readlines": "^1.0.1", 16 | "ssh2-sftp-client": "^12.0.0", 17 | "tar": "^7.4.3", 18 | "xml-js": "^1.6.11" 19 | }, 20 | "devDependencies": { 21 | "@tsconfig/node20": "^20.1.5", 22 | "@types/async": "^3.2.24", 23 | "@types/fs-extra": "^11.0.4", 24 | "@types/n-readlines": "^1.0.6", 25 | "@types/node": "^20.19.0", 26 | "@types/ssh2-sftp-client": "^9.0.4", 27 | "esbuild": "^0.25.5", 28 | "ts-node": "^10.9.2", 29 | "typescript": "^5.8.3" 30 | }, 31 | "engines": { 32 | "node": ">=20.0.0", 33 | "npm": ">=10.0.0" 34 | } 35 | }, 36 | "node_modules/@actions/core": { 37 | "version": "1.11.1", 38 | "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", 39 | "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", 40 | "license": "MIT", 41 | "dependencies": { 42 | "@actions/exec": "^1.1.1", 43 | "@actions/http-client": "^2.0.1" 44 | } 45 | }, 46 | "node_modules/@actions/exec": { 47 | "version": "1.1.1", 48 | "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", 49 | "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", 50 | "license": "MIT", 51 | "dependencies": { 52 | "@actions/io": "^1.0.1" 53 | } 54 | }, 55 | "node_modules/@actions/http-client": { 56 | "version": "2.2.3", 57 | "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", 58 | "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", 59 | "license": "MIT", 60 | "dependencies": { 61 | "tunnel": "^0.0.6", 62 | "undici": "^5.25.4" 63 | } 64 | }, 65 | "node_modules/@actions/io": { 66 | "version": "1.1.3", 67 | "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", 68 | "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", 69 | "license": "MIT" 70 | }, 71 | "node_modules/@cspotcode/source-map-support": { 72 | "version": "0.8.1", 73 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 74 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 75 | "dev": true, 76 | "license": "MIT", 77 | "dependencies": { 78 | "@jridgewell/trace-mapping": "0.3.9" 79 | }, 80 | "engines": { 81 | "node": ">=12" 82 | } 83 | }, 84 | "node_modules/@esbuild/aix-ppc64": { 85 | "version": "0.25.5", 86 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", 87 | "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", 88 | "cpu": [ 89 | "ppc64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "aix" 96 | ], 97 | "engines": { 98 | "node": ">=18" 99 | } 100 | }, 101 | "node_modules/@esbuild/android-arm": { 102 | "version": "0.25.5", 103 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", 104 | "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", 105 | "cpu": [ 106 | "arm" 107 | ], 108 | "dev": true, 109 | "license": "MIT", 110 | "optional": true, 111 | "os": [ 112 | "android" 113 | ], 114 | "engines": { 115 | "node": ">=18" 116 | } 117 | }, 118 | "node_modules/@esbuild/android-arm64": { 119 | "version": "0.25.5", 120 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", 121 | "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", 122 | "cpu": [ 123 | "arm64" 124 | ], 125 | "dev": true, 126 | "license": "MIT", 127 | "optional": true, 128 | "os": [ 129 | "android" 130 | ], 131 | "engines": { 132 | "node": ">=18" 133 | } 134 | }, 135 | "node_modules/@esbuild/android-x64": { 136 | "version": "0.25.5", 137 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", 138 | "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", 139 | "cpu": [ 140 | "x64" 141 | ], 142 | "dev": true, 143 | "license": "MIT", 144 | "optional": true, 145 | "os": [ 146 | "android" 147 | ], 148 | "engines": { 149 | "node": ">=18" 150 | } 151 | }, 152 | "node_modules/@esbuild/darwin-arm64": { 153 | "version": "0.25.5", 154 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", 155 | "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", 156 | "cpu": [ 157 | "arm64" 158 | ], 159 | "dev": true, 160 | "license": "MIT", 161 | "optional": true, 162 | "os": [ 163 | "darwin" 164 | ], 165 | "engines": { 166 | "node": ">=18" 167 | } 168 | }, 169 | "node_modules/@esbuild/darwin-x64": { 170 | "version": "0.25.5", 171 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", 172 | "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", 173 | "cpu": [ 174 | "x64" 175 | ], 176 | "dev": true, 177 | "license": "MIT", 178 | "optional": true, 179 | "os": [ 180 | "darwin" 181 | ], 182 | "engines": { 183 | "node": ">=18" 184 | } 185 | }, 186 | "node_modules/@esbuild/freebsd-arm64": { 187 | "version": "0.25.5", 188 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", 189 | "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", 190 | "cpu": [ 191 | "arm64" 192 | ], 193 | "dev": true, 194 | "license": "MIT", 195 | "optional": true, 196 | "os": [ 197 | "freebsd" 198 | ], 199 | "engines": { 200 | "node": ">=18" 201 | } 202 | }, 203 | "node_modules/@esbuild/freebsd-x64": { 204 | "version": "0.25.5", 205 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", 206 | "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", 207 | "cpu": [ 208 | "x64" 209 | ], 210 | "dev": true, 211 | "license": "MIT", 212 | "optional": true, 213 | "os": [ 214 | "freebsd" 215 | ], 216 | "engines": { 217 | "node": ">=18" 218 | } 219 | }, 220 | "node_modules/@esbuild/linux-arm": { 221 | "version": "0.25.5", 222 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", 223 | "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", 224 | "cpu": [ 225 | "arm" 226 | ], 227 | "dev": true, 228 | "license": "MIT", 229 | "optional": true, 230 | "os": [ 231 | "linux" 232 | ], 233 | "engines": { 234 | "node": ">=18" 235 | } 236 | }, 237 | "node_modules/@esbuild/linux-arm64": { 238 | "version": "0.25.5", 239 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", 240 | "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", 241 | "cpu": [ 242 | "arm64" 243 | ], 244 | "dev": true, 245 | "license": "MIT", 246 | "optional": true, 247 | "os": [ 248 | "linux" 249 | ], 250 | "engines": { 251 | "node": ">=18" 252 | } 253 | }, 254 | "node_modules/@esbuild/linux-ia32": { 255 | "version": "0.25.5", 256 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", 257 | "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", 258 | "cpu": [ 259 | "ia32" 260 | ], 261 | "dev": true, 262 | "license": "MIT", 263 | "optional": true, 264 | "os": [ 265 | "linux" 266 | ], 267 | "engines": { 268 | "node": ">=18" 269 | } 270 | }, 271 | "node_modules/@esbuild/linux-loong64": { 272 | "version": "0.25.5", 273 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", 274 | "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", 275 | "cpu": [ 276 | "loong64" 277 | ], 278 | "dev": true, 279 | "license": "MIT", 280 | "optional": true, 281 | "os": [ 282 | "linux" 283 | ], 284 | "engines": { 285 | "node": ">=18" 286 | } 287 | }, 288 | "node_modules/@esbuild/linux-mips64el": { 289 | "version": "0.25.5", 290 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", 291 | "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", 292 | "cpu": [ 293 | "mips64el" 294 | ], 295 | "dev": true, 296 | "license": "MIT", 297 | "optional": true, 298 | "os": [ 299 | "linux" 300 | ], 301 | "engines": { 302 | "node": ">=18" 303 | } 304 | }, 305 | "node_modules/@esbuild/linux-ppc64": { 306 | "version": "0.25.5", 307 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", 308 | "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", 309 | "cpu": [ 310 | "ppc64" 311 | ], 312 | "dev": true, 313 | "license": "MIT", 314 | "optional": true, 315 | "os": [ 316 | "linux" 317 | ], 318 | "engines": { 319 | "node": ">=18" 320 | } 321 | }, 322 | "node_modules/@esbuild/linux-riscv64": { 323 | "version": "0.25.5", 324 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", 325 | "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", 326 | "cpu": [ 327 | "riscv64" 328 | ], 329 | "dev": true, 330 | "license": "MIT", 331 | "optional": true, 332 | "os": [ 333 | "linux" 334 | ], 335 | "engines": { 336 | "node": ">=18" 337 | } 338 | }, 339 | "node_modules/@esbuild/linux-s390x": { 340 | "version": "0.25.5", 341 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", 342 | "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", 343 | "cpu": [ 344 | "s390x" 345 | ], 346 | "dev": true, 347 | "license": "MIT", 348 | "optional": true, 349 | "os": [ 350 | "linux" 351 | ], 352 | "engines": { 353 | "node": ">=18" 354 | } 355 | }, 356 | "node_modules/@esbuild/linux-x64": { 357 | "version": "0.25.5", 358 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", 359 | "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", 360 | "cpu": [ 361 | "x64" 362 | ], 363 | "dev": true, 364 | "license": "MIT", 365 | "optional": true, 366 | "os": [ 367 | "linux" 368 | ], 369 | "engines": { 370 | "node": ">=18" 371 | } 372 | }, 373 | "node_modules/@esbuild/netbsd-arm64": { 374 | "version": "0.25.5", 375 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", 376 | "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", 377 | "cpu": [ 378 | "arm64" 379 | ], 380 | "dev": true, 381 | "license": "MIT", 382 | "optional": true, 383 | "os": [ 384 | "netbsd" 385 | ], 386 | "engines": { 387 | "node": ">=18" 388 | } 389 | }, 390 | "node_modules/@esbuild/netbsd-x64": { 391 | "version": "0.25.5", 392 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", 393 | "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", 394 | "cpu": [ 395 | "x64" 396 | ], 397 | "dev": true, 398 | "license": "MIT", 399 | "optional": true, 400 | "os": [ 401 | "netbsd" 402 | ], 403 | "engines": { 404 | "node": ">=18" 405 | } 406 | }, 407 | "node_modules/@esbuild/openbsd-arm64": { 408 | "version": "0.25.5", 409 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", 410 | "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", 411 | "cpu": [ 412 | "arm64" 413 | ], 414 | "dev": true, 415 | "license": "MIT", 416 | "optional": true, 417 | "os": [ 418 | "openbsd" 419 | ], 420 | "engines": { 421 | "node": ">=18" 422 | } 423 | }, 424 | "node_modules/@esbuild/openbsd-x64": { 425 | "version": "0.25.5", 426 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", 427 | "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", 428 | "cpu": [ 429 | "x64" 430 | ], 431 | "dev": true, 432 | "license": "MIT", 433 | "optional": true, 434 | "os": [ 435 | "openbsd" 436 | ], 437 | "engines": { 438 | "node": ">=18" 439 | } 440 | }, 441 | "node_modules/@esbuild/sunos-x64": { 442 | "version": "0.25.5", 443 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", 444 | "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", 445 | "cpu": [ 446 | "x64" 447 | ], 448 | "dev": true, 449 | "license": "MIT", 450 | "optional": true, 451 | "os": [ 452 | "sunos" 453 | ], 454 | "engines": { 455 | "node": ">=18" 456 | } 457 | }, 458 | "node_modules/@esbuild/win32-arm64": { 459 | "version": "0.25.5", 460 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", 461 | "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", 462 | "cpu": [ 463 | "arm64" 464 | ], 465 | "dev": true, 466 | "license": "MIT", 467 | "optional": true, 468 | "os": [ 469 | "win32" 470 | ], 471 | "engines": { 472 | "node": ">=18" 473 | } 474 | }, 475 | "node_modules/@esbuild/win32-ia32": { 476 | "version": "0.25.5", 477 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", 478 | "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", 479 | "cpu": [ 480 | "ia32" 481 | ], 482 | "dev": true, 483 | "license": "MIT", 484 | "optional": true, 485 | "os": [ 486 | "win32" 487 | ], 488 | "engines": { 489 | "node": ">=18" 490 | } 491 | }, 492 | "node_modules/@esbuild/win32-x64": { 493 | "version": "0.25.5", 494 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", 495 | "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", 496 | "cpu": [ 497 | "x64" 498 | ], 499 | "dev": true, 500 | "license": "MIT", 501 | "optional": true, 502 | "os": [ 503 | "win32" 504 | ], 505 | "engines": { 506 | "node": ">=18" 507 | } 508 | }, 509 | "node_modules/@fastify/busboy": { 510 | "version": "2.1.1", 511 | "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", 512 | "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", 513 | "license": "MIT", 514 | "engines": { 515 | "node": ">=14" 516 | } 517 | }, 518 | "node_modules/@isaacs/fs-minipass": { 519 | "version": "4.0.1", 520 | "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", 521 | "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", 522 | "license": "ISC", 523 | "dependencies": { 524 | "minipass": "^7.0.4" 525 | }, 526 | "engines": { 527 | "node": ">=18.0.0" 528 | } 529 | }, 530 | "node_modules/@jridgewell/resolve-uri": { 531 | "version": "3.1.2", 532 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 533 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 534 | "dev": true, 535 | "license": "MIT", 536 | "engines": { 537 | "node": ">=6.0.0" 538 | } 539 | }, 540 | "node_modules/@jridgewell/sourcemap-codec": { 541 | "version": "1.5.0", 542 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 543 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 544 | "dev": true, 545 | "license": "MIT" 546 | }, 547 | "node_modules/@jridgewell/trace-mapping": { 548 | "version": "0.3.9", 549 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 550 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 551 | "dev": true, 552 | "license": "MIT", 553 | "dependencies": { 554 | "@jridgewell/resolve-uri": "^3.0.3", 555 | "@jridgewell/sourcemap-codec": "^1.4.10" 556 | } 557 | }, 558 | "node_modules/@tsconfig/node10": { 559 | "version": "1.0.11", 560 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", 561 | "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", 562 | "dev": true, 563 | "license": "MIT" 564 | }, 565 | "node_modules/@tsconfig/node12": { 566 | "version": "1.0.11", 567 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 568 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 569 | "dev": true, 570 | "license": "MIT" 571 | }, 572 | "node_modules/@tsconfig/node14": { 573 | "version": "1.0.3", 574 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 575 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 576 | "dev": true, 577 | "license": "MIT" 578 | }, 579 | "node_modules/@tsconfig/node16": { 580 | "version": "1.0.4", 581 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 582 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 583 | "dev": true, 584 | "license": "MIT" 585 | }, 586 | "node_modules/@tsconfig/node20": { 587 | "version": "20.1.5", 588 | "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.5.tgz", 589 | "integrity": "sha512-Vm8e3WxDTqMGPU4GATF9keQAIy1Drd7bPwlgzKJnZtoOsTm1tduUTbDjg0W5qERvGuxPI2h9RbMufH0YdfBylA==", 590 | "dev": true, 591 | "license": "MIT" 592 | }, 593 | "node_modules/@types/async": { 594 | "version": "3.2.24", 595 | "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.24.tgz", 596 | "integrity": "sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==", 597 | "dev": true, 598 | "license": "MIT" 599 | }, 600 | "node_modules/@types/fs-extra": { 601 | "version": "11.0.4", 602 | "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", 603 | "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", 604 | "dev": true, 605 | "license": "MIT", 606 | "dependencies": { 607 | "@types/jsonfile": "*", 608 | "@types/node": "*" 609 | } 610 | }, 611 | "node_modules/@types/jsonfile": { 612 | "version": "6.1.4", 613 | "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", 614 | "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", 615 | "dev": true, 616 | "license": "MIT", 617 | "dependencies": { 618 | "@types/node": "*" 619 | } 620 | }, 621 | "node_modules/@types/n-readlines": { 622 | "version": "1.0.6", 623 | "resolved": "https://registry.npmjs.org/@types/n-readlines/-/n-readlines-1.0.6.tgz", 624 | "integrity": "sha512-0kCJ/bVdHl0yxNYbiGTInj8BPqVLMiUp4gn4zfyYJgKn0iQKiJ+mNGCJj2rFLupOrZ5y/W5l13YhOZ8jYG9DIA==", 625 | "dev": true, 626 | "license": "MIT", 627 | "dependencies": { 628 | "@types/node": "*" 629 | } 630 | }, 631 | "node_modules/@types/node": { 632 | "version": "20.19.0", 633 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", 634 | "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", 635 | "dev": true, 636 | "license": "MIT", 637 | "dependencies": { 638 | "undici-types": "~6.21.0" 639 | } 640 | }, 641 | "node_modules/@types/ssh2": { 642 | "version": "1.15.5", 643 | "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", 644 | "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", 645 | "dev": true, 646 | "license": "MIT", 647 | "dependencies": { 648 | "@types/node": "^18.11.18" 649 | } 650 | }, 651 | "node_modules/@types/ssh2-sftp-client": { 652 | "version": "9.0.4", 653 | "resolved": "https://registry.npmjs.org/@types/ssh2-sftp-client/-/ssh2-sftp-client-9.0.4.tgz", 654 | "integrity": "sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==", 655 | "dev": true, 656 | "license": "MIT", 657 | "dependencies": { 658 | "@types/ssh2": "^1.0.0" 659 | } 660 | }, 661 | "node_modules/@types/ssh2/node_modules/@types/node": { 662 | "version": "18.19.111", 663 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", 664 | "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", 665 | "dev": true, 666 | "license": "MIT", 667 | "dependencies": { 668 | "undici-types": "~5.26.4" 669 | } 670 | }, 671 | "node_modules/@types/ssh2/node_modules/undici-types": { 672 | "version": "5.26.5", 673 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 674 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 675 | "dev": true, 676 | "license": "MIT" 677 | }, 678 | "node_modules/acorn": { 679 | "version": "8.15.0", 680 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 681 | "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 682 | "dev": true, 683 | "license": "MIT", 684 | "bin": { 685 | "acorn": "bin/acorn" 686 | }, 687 | "engines": { 688 | "node": ">=0.4.0" 689 | } 690 | }, 691 | "node_modules/acorn-walk": { 692 | "version": "8.3.4", 693 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", 694 | "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", 695 | "dev": true, 696 | "license": "MIT", 697 | "dependencies": { 698 | "acorn": "^8.11.0" 699 | }, 700 | "engines": { 701 | "node": ">=0.4.0" 702 | } 703 | }, 704 | "node_modules/arg": { 705 | "version": "4.1.3", 706 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 707 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 708 | "dev": true, 709 | "license": "MIT" 710 | }, 711 | "node_modules/asn1": { 712 | "version": "0.2.6", 713 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 714 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 715 | "license": "MIT", 716 | "dependencies": { 717 | "safer-buffer": "~2.1.0" 718 | } 719 | }, 720 | "node_modules/async": { 721 | "version": "3.2.6", 722 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", 723 | "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", 724 | "license": "MIT" 725 | }, 726 | "node_modules/bcrypt-pbkdf": { 727 | "version": "1.0.2", 728 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 729 | "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", 730 | "license": "BSD-3-Clause", 731 | "dependencies": { 732 | "tweetnacl": "^0.14.3" 733 | } 734 | }, 735 | "node_modules/buffer-from": { 736 | "version": "1.1.2", 737 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 738 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 739 | "license": "MIT" 740 | }, 741 | "node_modules/buildcheck": { 742 | "version": "0.0.6", 743 | "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", 744 | "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", 745 | "optional": true, 746 | "engines": { 747 | "node": ">=10.0.0" 748 | } 749 | }, 750 | "node_modules/chownr": { 751 | "version": "3.0.0", 752 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", 753 | "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", 754 | "license": "BlueOak-1.0.0", 755 | "engines": { 756 | "node": ">=18" 757 | } 758 | }, 759 | "node_modules/concat-stream": { 760 | "version": "2.0.0", 761 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", 762 | "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", 763 | "engines": [ 764 | "node >= 6.0" 765 | ], 766 | "license": "MIT", 767 | "dependencies": { 768 | "buffer-from": "^1.0.0", 769 | "inherits": "^2.0.3", 770 | "readable-stream": "^3.0.2", 771 | "typedarray": "^0.0.6" 772 | } 773 | }, 774 | "node_modules/cpu-features": { 775 | "version": "0.0.10", 776 | "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", 777 | "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", 778 | "hasInstallScript": true, 779 | "optional": true, 780 | "dependencies": { 781 | "buildcheck": "~0.0.6", 782 | "nan": "^2.19.0" 783 | }, 784 | "engines": { 785 | "node": ">=10.0.0" 786 | } 787 | }, 788 | "node_modules/create-require": { 789 | "version": "1.1.1", 790 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 791 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 792 | "dev": true, 793 | "license": "MIT" 794 | }, 795 | "node_modules/diff": { 796 | "version": "4.0.2", 797 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 798 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 799 | "dev": true, 800 | "license": "BSD-3-Clause", 801 | "engines": { 802 | "node": ">=0.3.1" 803 | } 804 | }, 805 | "node_modules/esbuild": { 806 | "version": "0.25.5", 807 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", 808 | "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", 809 | "dev": true, 810 | "hasInstallScript": true, 811 | "license": "MIT", 812 | "bin": { 813 | "esbuild": "bin/esbuild" 814 | }, 815 | "engines": { 816 | "node": ">=18" 817 | }, 818 | "optionalDependencies": { 819 | "@esbuild/aix-ppc64": "0.25.5", 820 | "@esbuild/android-arm": "0.25.5", 821 | "@esbuild/android-arm64": "0.25.5", 822 | "@esbuild/android-x64": "0.25.5", 823 | "@esbuild/darwin-arm64": "0.25.5", 824 | "@esbuild/darwin-x64": "0.25.5", 825 | "@esbuild/freebsd-arm64": "0.25.5", 826 | "@esbuild/freebsd-x64": "0.25.5", 827 | "@esbuild/linux-arm": "0.25.5", 828 | "@esbuild/linux-arm64": "0.25.5", 829 | "@esbuild/linux-ia32": "0.25.5", 830 | "@esbuild/linux-loong64": "0.25.5", 831 | "@esbuild/linux-mips64el": "0.25.5", 832 | "@esbuild/linux-ppc64": "0.25.5", 833 | "@esbuild/linux-riscv64": "0.25.5", 834 | "@esbuild/linux-s390x": "0.25.5", 835 | "@esbuild/linux-x64": "0.25.5", 836 | "@esbuild/netbsd-arm64": "0.25.5", 837 | "@esbuild/netbsd-x64": "0.25.5", 838 | "@esbuild/openbsd-arm64": "0.25.5", 839 | "@esbuild/openbsd-x64": "0.25.5", 840 | "@esbuild/sunos-x64": "0.25.5", 841 | "@esbuild/win32-arm64": "0.25.5", 842 | "@esbuild/win32-ia32": "0.25.5", 843 | "@esbuild/win32-x64": "0.25.5" 844 | } 845 | }, 846 | "node_modules/fs-extra": { 847 | "version": "11.3.0", 848 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", 849 | "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", 850 | "license": "MIT", 851 | "dependencies": { 852 | "graceful-fs": "^4.2.0", 853 | "jsonfile": "^6.0.1", 854 | "universalify": "^2.0.0" 855 | }, 856 | "engines": { 857 | "node": ">=14.14" 858 | } 859 | }, 860 | "node_modules/graceful-fs": { 861 | "version": "4.2.11", 862 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 863 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 864 | "license": "ISC" 865 | }, 866 | "node_modules/inherits": { 867 | "version": "2.0.4", 868 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 869 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 870 | "license": "ISC" 871 | }, 872 | "node_modules/jsonfile": { 873 | "version": "6.1.0", 874 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 875 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 876 | "license": "MIT", 877 | "dependencies": { 878 | "universalify": "^2.0.0" 879 | }, 880 | "optionalDependencies": { 881 | "graceful-fs": "^4.1.6" 882 | } 883 | }, 884 | "node_modules/make-error": { 885 | "version": "1.3.6", 886 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 887 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 888 | "dev": true, 889 | "license": "ISC" 890 | }, 891 | "node_modules/minipass": { 892 | "version": "7.1.2", 893 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 894 | "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 895 | "license": "ISC", 896 | "engines": { 897 | "node": ">=16 || 14 >=14.17" 898 | } 899 | }, 900 | "node_modules/minizlib": { 901 | "version": "3.0.2", 902 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", 903 | "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", 904 | "license": "MIT", 905 | "dependencies": { 906 | "minipass": "^7.1.2" 907 | }, 908 | "engines": { 909 | "node": ">= 18" 910 | } 911 | }, 912 | "node_modules/mkdirp": { 913 | "version": "3.0.1", 914 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", 915 | "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", 916 | "license": "MIT", 917 | "bin": { 918 | "mkdirp": "dist/cjs/src/bin.js" 919 | }, 920 | "engines": { 921 | "node": ">=10" 922 | }, 923 | "funding": { 924 | "url": "https://github.com/sponsors/isaacs" 925 | } 926 | }, 927 | "node_modules/n-readlines": { 928 | "version": "1.0.1", 929 | "resolved": "https://registry.npmjs.org/n-readlines/-/n-readlines-1.0.1.tgz", 930 | "integrity": "sha512-z4SyAIVgMy7CkgsoNw7YVz40v0g4+WWvvqy8+ZdHrCtgevcEO758WQyrYcw3XPxcLxF+//RszTz/rO48nzD0wQ==", 931 | "license": "MIT", 932 | "engines": { 933 | "node": ">=6.x.x" 934 | } 935 | }, 936 | "node_modules/nan": { 937 | "version": "2.22.2", 938 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", 939 | "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", 940 | "license": "MIT", 941 | "optional": true 942 | }, 943 | "node_modules/readable-stream": { 944 | "version": "3.6.2", 945 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 946 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 947 | "license": "MIT", 948 | "dependencies": { 949 | "inherits": "^2.0.3", 950 | "string_decoder": "^1.1.1", 951 | "util-deprecate": "^1.0.1" 952 | }, 953 | "engines": { 954 | "node": ">= 6" 955 | } 956 | }, 957 | "node_modules/safe-buffer": { 958 | "version": "5.2.1", 959 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 960 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 961 | "funding": [ 962 | { 963 | "type": "github", 964 | "url": "https://github.com/sponsors/feross" 965 | }, 966 | { 967 | "type": "patreon", 968 | "url": "https://www.patreon.com/feross" 969 | }, 970 | { 971 | "type": "consulting", 972 | "url": "https://feross.org/support" 973 | } 974 | ], 975 | "license": "MIT" 976 | }, 977 | "node_modules/safer-buffer": { 978 | "version": "2.1.2", 979 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 980 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 981 | "license": "MIT" 982 | }, 983 | "node_modules/sax": { 984 | "version": "1.4.1", 985 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", 986 | "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", 987 | "license": "ISC" 988 | }, 989 | "node_modules/ssh2": { 990 | "version": "1.16.0", 991 | "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", 992 | "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", 993 | "hasInstallScript": true, 994 | "dependencies": { 995 | "asn1": "^0.2.6", 996 | "bcrypt-pbkdf": "^1.0.2" 997 | }, 998 | "engines": { 999 | "node": ">=10.16.0" 1000 | }, 1001 | "optionalDependencies": { 1002 | "cpu-features": "~0.0.10", 1003 | "nan": "^2.20.0" 1004 | } 1005 | }, 1006 | "node_modules/ssh2-sftp-client": { 1007 | "version": "12.0.0", 1008 | "resolved": "https://registry.npmjs.org/ssh2-sftp-client/-/ssh2-sftp-client-12.0.0.tgz", 1009 | "integrity": "sha512-k+ocDsx6N2eDwQlIRwJFa0I1bkQpFPhIc+cv1iplaQaIPXFt9YM1ZnXCJOW4OILS5dzE+12OlhYIF5g0AzgVfg==", 1010 | "license": "Apache-2.0", 1011 | "dependencies": { 1012 | "concat-stream": "^2.0.0", 1013 | "ssh2": "^1.16.0" 1014 | }, 1015 | "engines": { 1016 | "node": ">=18.20.4" 1017 | }, 1018 | "funding": { 1019 | "type": "individual", 1020 | "url": "https://square.link/u/4g7sPflL" 1021 | } 1022 | }, 1023 | "node_modules/string_decoder": { 1024 | "version": "1.3.0", 1025 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1026 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1027 | "license": "MIT", 1028 | "dependencies": { 1029 | "safe-buffer": "~5.2.0" 1030 | } 1031 | }, 1032 | "node_modules/tar": { 1033 | "version": "7.4.3", 1034 | "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", 1035 | "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", 1036 | "license": "ISC", 1037 | "dependencies": { 1038 | "@isaacs/fs-minipass": "^4.0.0", 1039 | "chownr": "^3.0.0", 1040 | "minipass": "^7.1.2", 1041 | "minizlib": "^3.0.1", 1042 | "mkdirp": "^3.0.1", 1043 | "yallist": "^5.0.0" 1044 | }, 1045 | "engines": { 1046 | "node": ">=18" 1047 | } 1048 | }, 1049 | "node_modules/ts-node": { 1050 | "version": "10.9.2", 1051 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 1052 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 1053 | "dev": true, 1054 | "license": "MIT", 1055 | "dependencies": { 1056 | "@cspotcode/source-map-support": "^0.8.0", 1057 | "@tsconfig/node10": "^1.0.7", 1058 | "@tsconfig/node12": "^1.0.7", 1059 | "@tsconfig/node14": "^1.0.0", 1060 | "@tsconfig/node16": "^1.0.2", 1061 | "acorn": "^8.4.1", 1062 | "acorn-walk": "^8.1.1", 1063 | "arg": "^4.1.0", 1064 | "create-require": "^1.1.0", 1065 | "diff": "^4.0.1", 1066 | "make-error": "^1.1.1", 1067 | "v8-compile-cache-lib": "^3.0.1", 1068 | "yn": "3.1.1" 1069 | }, 1070 | "bin": { 1071 | "ts-node": "dist/bin.js", 1072 | "ts-node-cwd": "dist/bin-cwd.js", 1073 | "ts-node-esm": "dist/bin-esm.js", 1074 | "ts-node-script": "dist/bin-script.js", 1075 | "ts-node-transpile-only": "dist/bin-transpile.js", 1076 | "ts-script": "dist/bin-script-deprecated.js" 1077 | }, 1078 | "peerDependencies": { 1079 | "@swc/core": ">=1.2.50", 1080 | "@swc/wasm": ">=1.2.50", 1081 | "@types/node": "*", 1082 | "typescript": ">=2.7" 1083 | }, 1084 | "peerDependenciesMeta": { 1085 | "@swc/core": { 1086 | "optional": true 1087 | }, 1088 | "@swc/wasm": { 1089 | "optional": true 1090 | } 1091 | } 1092 | }, 1093 | "node_modules/tunnel": { 1094 | "version": "0.0.6", 1095 | "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", 1096 | "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", 1097 | "license": "MIT", 1098 | "engines": { 1099 | "node": ">=0.6.11 <=0.7.0 || >=0.7.3" 1100 | } 1101 | }, 1102 | "node_modules/tweetnacl": { 1103 | "version": "0.14.5", 1104 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1105 | "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", 1106 | "license": "Unlicense" 1107 | }, 1108 | "node_modules/typedarray": { 1109 | "version": "0.0.6", 1110 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1111 | "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", 1112 | "license": "MIT" 1113 | }, 1114 | "node_modules/typescript": { 1115 | "version": "5.8.3", 1116 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 1117 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 1118 | "dev": true, 1119 | "license": "Apache-2.0", 1120 | "bin": { 1121 | "tsc": "bin/tsc", 1122 | "tsserver": "bin/tsserver" 1123 | }, 1124 | "engines": { 1125 | "node": ">=14.17" 1126 | } 1127 | }, 1128 | "node_modules/undici": { 1129 | "version": "5.29.0", 1130 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", 1131 | "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", 1132 | "license": "MIT", 1133 | "dependencies": { 1134 | "@fastify/busboy": "^2.0.0" 1135 | }, 1136 | "engines": { 1137 | "node": ">=14.0" 1138 | } 1139 | }, 1140 | "node_modules/undici-types": { 1141 | "version": "6.21.0", 1142 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 1143 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 1144 | "dev": true, 1145 | "license": "MIT" 1146 | }, 1147 | "node_modules/universalify": { 1148 | "version": "2.0.1", 1149 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", 1150 | "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", 1151 | "license": "MIT", 1152 | "engines": { 1153 | "node": ">= 10.0.0" 1154 | } 1155 | }, 1156 | "node_modules/util-deprecate": { 1157 | "version": "1.0.2", 1158 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1159 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1160 | "license": "MIT" 1161 | }, 1162 | "node_modules/v8-compile-cache-lib": { 1163 | "version": "3.0.1", 1164 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 1165 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 1166 | "dev": true, 1167 | "license": "MIT" 1168 | }, 1169 | "node_modules/xml-js": { 1170 | "version": "1.6.11", 1171 | "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", 1172 | "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", 1173 | "license": "MIT", 1174 | "dependencies": { 1175 | "sax": "^1.2.4" 1176 | }, 1177 | "bin": { 1178 | "xml-js": "bin/cli.js" 1179 | } 1180 | }, 1181 | "node_modules/yallist": { 1182 | "version": "5.0.0", 1183 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", 1184 | "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", 1185 | "license": "BlueOak-1.0.0", 1186 | "engines": { 1187 | "node": ">=18" 1188 | } 1189 | }, 1190 | "node_modules/yn": { 1191 | "version": "3.1.1", 1192 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1193 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1194 | "dev": true, 1195 | "license": "MIT", 1196 | "engines": { 1197 | "node": ">=6" 1198 | } 1199 | } 1200 | } 1201 | } 1202 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "action-spigotmc", 3 | "version": "5.1.0", 4 | "description": "This GitHub Action allows you to easily compile Minecraft Spigot and install it in your runners local maven repository.", 5 | "keywords": [ 6 | "github-actions", 7 | "minecraft", 8 | "spigot", 9 | "continuous-integration" 10 | ], 11 | "homepage": "https://github.com/SpraxDev/Action-SpigotMC#readme", 12 | "main": "dist/index.js", 13 | "private": true, 14 | "scripts": { 15 | "build": "tsc -noEmit && esbuild src/index.ts --bundle --minify-syntax --sourcemap --platform=node --outdir=dist --loader:.node=file", 16 | "start": "node dist/index.js", 17 | "dev": "npm run build && npm run start" 18 | }, 19 | "author": { 20 | "name": "Christian Koop", 21 | "url": "https://github.com/SpraxDev", 22 | "email": "contact@sprax2013.de" 23 | }, 24 | "contributors": [], 25 | "license": "MIT", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/SpraxDev/Action-SpigotMC.git" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/SpraxDev/Action-SpigotMC/issues" 32 | }, 33 | "engines": { 34 | "node": ">=20.0.0", 35 | "npm": ">=10.0.0" 36 | }, 37 | "dependencies": { 38 | "@actions/core": "^1.11.1", 39 | "async": "^3.2.6", 40 | "fs-extra": "^11.3.0", 41 | "n-readlines": "^1.0.1", 42 | "ssh2-sftp-client": "^12.0.0", 43 | "tar": "^7.4.3", 44 | "xml-js": "^1.6.11" 45 | }, 46 | "devDependencies": { 47 | "@tsconfig/node20": "^20.1.5", 48 | "@types/async": "^3.2.24", 49 | "@types/fs-extra": "^11.0.4", 50 | "@types/n-readlines": "^1.0.6", 51 | "@types/node": "^20.19.0", 52 | "@types/ssh2-sftp-client": "^9.0.4", 53 | "esbuild": "^0.25.5", 54 | "ts-node": "^10.9.2", 55 | "typescript": "^5.8.3" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/cache/SFTPCache.ts: -------------------------------------------------------------------------------- 1 | import ChildProcess from 'node:child_process'; 2 | import Fs from 'node:fs'; 3 | import Os from 'node:os'; 4 | import Path from 'node:path'; 5 | import Ssh2SftpClient from 'ssh2-sftp-client'; 6 | 7 | export default class SFTPCache { 8 | private static readonly CACHE_DIR = 'SpraxDev_Action-SpigotMC-Cache'; 9 | 10 | private readonly host: string; 11 | private readonly port: number; 12 | private readonly username: string; 13 | private readonly privateKey: string; 14 | private readonly expectedHostKey: string | null; 15 | 16 | private readonly sftpClient = new Ssh2SftpClient(); 17 | private initPromise?: Promise; 18 | 19 | constructor(host: string, port: number, username: string, privateKey: string, expectedHostKey: string | null) { 20 | this.host = host; 21 | this.port = port; 22 | this.username = username; 23 | this.privateKey = privateKey; 24 | this.expectedHostKey = expectedHostKey; 25 | } 26 | 27 | async shutdown(): Promise { 28 | if (this.initPromise != null) { 29 | await this.initPromise; 30 | } 31 | await this.sftpClient.end(); 32 | } 33 | 34 | async getSizeOfCacheForVersion(version: string): Promise { 35 | await this.ensureInit(); 36 | 37 | const remotePath = SFTPCache.constructRemoteCacheFilePath(version); 38 | if (await this.sftpClient.exists(remotePath) !== '-') { 39 | return null; 40 | } 41 | 42 | const stat = await this.sftpClient.stat(remotePath); 43 | return stat.size; 44 | } 45 | 46 | async fetchCacheForVersion(version: string, destFilePath: string): Promise { 47 | await this.ensureInit(); 48 | 49 | const remotePath = SFTPCache.constructRemoteCacheFilePath(version); 50 | if (await this.sftpClient.exists(remotePath) === '-') { 51 | await this.downloadSftpFile(remotePath, destFilePath); 52 | return true; 53 | } 54 | 55 | return false; 56 | } 57 | 58 | async uploadCacheForVersion(version: string, filePath: string): Promise { 59 | await this.ensureInit(); 60 | 61 | const remotePath = SFTPCache.constructRemoteCacheFilePath(version); 62 | await this.sftpClient.mkdir(Path.dirname(remotePath), true); 63 | await this.sftpClient.put(filePath, remotePath); 64 | } 65 | 66 | private async init(): Promise { 67 | try { 68 | await this.sftpClient.connect({ 69 | host: this.host, 70 | port: this.port, 71 | username: this.username, 72 | privateKey: this.privateKey, 73 | retries: 0, 74 | hostVerifier: (key: Buffer) => { 75 | const expectedHostKeyHash = this.expectedHostKey?.split(' ')[1]; 76 | return expectedHostKeyHash == null || key.toString('base64') === expectedHostKeyHash; 77 | } 78 | }); 79 | } catch (err: any) { 80 | let detail = err.toString(); 81 | if (detail.endsWith('Host denied (verification failed)')) { 82 | detail = 'Host key verification failed'; 83 | } 84 | throw new Error(`Failed to connect to SFTP-Server: ${detail}`); 85 | } 86 | } 87 | 88 | private async ensureInit(): Promise { 89 | if (this.initPromise == null) { 90 | this.initPromise = this.init(); 91 | } 92 | await this.initPromise; 93 | } 94 | 95 | private async downloadSftpFile(remotePath: string, destFilePath: string): Promise { 96 | if (!await this.doesSftpBinaryExist()) { 97 | await this.sftpClient.get(remotePath, destFilePath); 98 | return; 99 | } 100 | 101 | const privateKeyTmpPath = await Fs.promises.mkdtemp(Path.join(Os.tmpdir(), '/')); 102 | try { 103 | const privateKeyFilePath = Path.join(privateKeyTmpPath, 'id'); 104 | await Fs.promises.writeFile(privateKeyFilePath, this.privateKey + '\n', {mode: 0o400}); 105 | 106 | const knownHostsFilePath = Path.join(privateKeyTmpPath, 'known_hosts'); 107 | if (this.expectedHostKey != null) { 108 | await Fs.promises.writeFile(knownHostsFilePath, `[${this.host}]:${this.port} ${this.expectedHostKey}\n`); 109 | } 110 | 111 | await new Promise((resolve, reject) => { 112 | const sftpCommandArgs = [ 113 | '-i', privateKeyFilePath, 114 | '-o', 'BatchMode=yes', 115 | '-o', `StrictHostKeyChecking=${this.expectedHostKey == null ? 'no' : 'yes'}`, 116 | '-o', `UserKnownHostsFile=${knownHostsFilePath}`, 117 | this.constructSftpRemoteUrl(remotePath), 118 | Path.resolve(destFilePath) 119 | ]; 120 | const process = ChildProcess.spawn('sftp', sftpCommandArgs, {stdio: ['ignore', 'inherit', 'inherit']}); 121 | process.on('error', reject); 122 | process.on('exit', code => code === 0 ? resolve() : reject(new Error(`Exit code: ${code}`))); 123 | }); 124 | } finally { 125 | await Fs.promises.rm(privateKeyTmpPath, {recursive: true, force: true}); 126 | } 127 | } 128 | 129 | private async doesSftpBinaryExist(): Promise { 130 | return new Promise((resolve) => { 131 | const process = ChildProcess.spawn('sftp', [], {timeout: 5000, stdio: 'ignore'}); 132 | process.on('error', () => resolve(false)); 133 | process.on('exit', code => resolve(code === 1)); 134 | }); 135 | } 136 | 137 | private constructSftpRemoteUrl(remotePath: string): string { 138 | const url = new URL('sftp://'); 139 | url.hostname = this.host; 140 | url.port = this.port.toString(); 141 | url.pathname = remotePath; 142 | 143 | url.username = this.username; 144 | 145 | return url.toString(); 146 | } 147 | 148 | private static constructRemoteCacheFilePath(version: string): string { 149 | return Path.join(SFTPCache.CACHE_DIR, `${version}.tar.gz`); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/cache/SpigotArtifactArchiver.ts: -------------------------------------------------------------------------------- 1 | import Fs from 'node:fs'; 2 | import Os from 'node:os'; 3 | import Path from 'node:path'; 4 | import * as Tar from 'tar'; 5 | 6 | export default class SpigotArtifactArchiver { 7 | private static readonly ARTIFACT_DIRS = [ 8 | 'org/spigotmc/minecraft-server/', 9 | 'org/spigotmc/spigot-api/', 10 | 'org/spigotmc/spigot/' 11 | ]; 12 | 13 | async createCacheArchiveForVersion(version: string, destFilePath: string): Promise { 14 | if (!Path.isAbsolute(destFilePath)) { 15 | throw new Error('Destination path must be absolute'); 16 | } 17 | 18 | const localMavenRepo = SpigotArtifactArchiver.determineLocalMavenRepositoryPath(); 19 | const files = await this.collectFilesFromMavenRepo(localMavenRepo, version); 20 | if (files.length === 0) { 21 | throw new Error('No files found in local Maven repository'); 22 | } 23 | 24 | await Tar.create( 25 | { 26 | file: destFilePath, 27 | gzip: true, 28 | cwd: localMavenRepo 29 | }, 30 | files.map(file => Path.relative(localMavenRepo, file)) 31 | ); 32 | } 33 | 34 | async extractCacheArchive(filePath: string): Promise { 35 | if (!Path.isAbsolute(filePath)) { 36 | throw new Error('File path must be absolute'); 37 | } 38 | 39 | const localMavenRepo = SpigotArtifactArchiver.determineLocalMavenRepositoryPath(); 40 | await Fs.promises.mkdir(localMavenRepo, {recursive: true}); 41 | await Tar.extract({ 42 | file: filePath, 43 | cwd: localMavenRepo 44 | }); 45 | } 46 | 47 | private async collectFilesFromMavenRepo(localMavenRepo: string, version: string): Promise { 48 | const files: string[] = []; 49 | 50 | for (const artifactDir of SpigotArtifactArchiver.ARTIFACT_DIRS) { 51 | const artifactPath = Path.join(localMavenRepo, artifactDir, version); 52 | if (Fs.existsSync(artifactPath)) { 53 | files.push(artifactPath); 54 | } 55 | } 56 | 57 | const spigotParentArtifactDir = Path.join(localMavenRepo, 'org', 'spigotmc', 'spigot-parent', 'dev-SNAPSHOT'); 58 | if (Fs.existsSync(spigotParentArtifactDir)) { 59 | files.push(spigotParentArtifactDir); 60 | } 61 | 62 | return files; 63 | } 64 | 65 | private static determineLocalMavenRepositoryPath() { 66 | return Path.join(Os.homedir(), '.m2', 'repository'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/cache/SpigotArtifactCache.ts: -------------------------------------------------------------------------------- 1 | import Fs from 'node:fs'; 2 | import Path from 'node:path'; 3 | import SFTPCache from './SFTPCache'; 4 | import SpigotArtifactArchiver from './SpigotArtifactArchiver'; 5 | 6 | export default class SpigotArtifactCache { 7 | private readonly artifactArchiver = new SpigotArtifactArchiver(); 8 | private readonly sftpCache: SFTPCache | null; 9 | 10 | constructor(host: string, port: number, username: string, privateKey: string, expectedHostKey: string | null) { 11 | if (host === '') { 12 | this.sftpCache = null; 13 | } else { 14 | this.sftpCache = new SFTPCache(host, port, username, privateKey, expectedHostKey); 15 | } 16 | } 17 | 18 | async shutdown(): Promise { 19 | await this.sftpCache?.shutdown(); 20 | } 21 | 22 | isSftpAvailable(): boolean { 23 | return this.sftpCache != null; 24 | } 25 | 26 | async fetchAndExtractCacheForVersionIfExists(version: string, tmpDir: string, logInfo: (msg: string) => void, logError: (msg: string) => void): Promise { 27 | if (this.sftpCache == null) { 28 | throw new Error('Cache is not available'); 29 | } 30 | 31 | const cacheTmpFile = Path.join(tmpDir, `cache-${version}`); 32 | try { 33 | const cacheFileSize = await this.sftpCache.getSizeOfCacheForVersion(version); 34 | if (cacheFileSize !== null) { 35 | logInfo(`Downloading cache for version ${version} from SFTP-Server (${SpigotArtifactCache.prettifyFileSize(cacheFileSize)})...`); 36 | } 37 | 38 | if (await this.sftpCache.fetchCacheForVersion(version, cacheTmpFile)) { 39 | await this.artifactArchiver.extractCacheArchive(cacheTmpFile); 40 | return true; 41 | } 42 | } catch (err) { 43 | logError(`Failed to fetch cache from SFTP-Server (version=${version}): ${err?.toString()}`); 44 | } finally { 45 | await Fs.promises.rm(cacheTmpFile, {force: true}); 46 | } 47 | 48 | return false; 49 | } 50 | 51 | async createAndUploadCacheForVersion(version: string, tmpDir: string, logInfo: (msg: string) => void, logError: (msg: string) => void): Promise { 52 | if (this.sftpCache == null) { 53 | throw new Error('Cache is not available'); 54 | } 55 | 56 | const cacheTmpFile = Path.join(tmpDir, `cache-${version}`); 57 | try { 58 | await this.artifactArchiver.createCacheArchiveForVersion(version, cacheTmpFile); 59 | 60 | logInfo(`Uploading cache for version ${version} to SFTP-Server (${SpigotArtifactCache.prettifyFileSize((await Fs.promises.stat(cacheTmpFile)).size)})...`); 61 | await this.sftpCache.uploadCacheForVersion(version, cacheTmpFile); 62 | return true; 63 | } catch (err) { 64 | logError(`Failed to upload cache to SFTP-Server (version=${version}): ${err?.toString()}`); 65 | return false; 66 | } finally { 67 | await Fs.promises.rm(cacheTmpFile, {force: true}); 68 | } 69 | } 70 | 71 | private static prettifyFileSize(bytes: number): string { 72 | if (bytes < 0 || !Number.isFinite(bytes)) { 73 | throw new Error('The given bytes need to be a positive number'); 74 | } 75 | 76 | const base = 1024; 77 | const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; 78 | 79 | let i = Math.floor(Math.log(bytes) / Math.log(base)); 80 | if (i < 0) { 81 | i = 0; 82 | } else if (i >= units.length) { 83 | i = units.length - 1; 84 | } 85 | 86 | return (bytes / Math.pow(base, i)).toFixed(i > 0 ? 2 : 0) + ' ' + units[i]; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as ActionsCore from '@actions/core'; 2 | import Async from 'async'; 3 | import FsExtra from 'fs-extra'; 4 | import Fs from 'node:fs'; 5 | import Os from 'node:os'; 6 | import Path from 'node:path'; 7 | import XmlJs from 'xml-js'; 8 | import SpigotArtifactCache from './cache/SpigotArtifactCache'; 9 | import {downloadFile, exit, fixArgArr, isNumeric, readLastLines, resetWorkingDir, runCmd} from './utils'; 10 | 11 | const supportedBuildTools: { [key: string]: { url: string } } = { 12 | spigotmc: { 13 | url: 'https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar' 14 | } 15 | }; 16 | 17 | /* GitHub Actions inputs */ 18 | const buildToolProvider: string = (ActionsCore.getInput('buildToolProvider') || 'SpigotMC').toLowerCase(); 19 | let versions: string[] = fixArgArr((ActionsCore.getInput('versions') || 'latest').toLowerCase().split(',')); 20 | const generateSrc: boolean = ActionsCore.getInput('generateSrc') == 'true'; 21 | const generateDoc: boolean = ActionsCore.getInput('generateDoc') == 'true'; 22 | const disableJavaCheck: boolean = ActionsCore.getInput('disableJavaCheck') == 'true'; 23 | const remapped: boolean = ActionsCore.getInput('remapped') == 'true'; 24 | const finalJarOutputDir: string = ActionsCore.getInput('finalJarOutputDir') || ''; 25 | 26 | const forceRun: boolean = ActionsCore.getInput('forceRun') == 'true'; 27 | const threadCount: number = isNumeric(ActionsCore.getInput('threads')) ? parseInt(ActionsCore.getInput('threads'), 10) : Os.cpus().length; 28 | 29 | const sftpCacheHost: string = ActionsCore.getInput('sftpCacheHost') || ''; 30 | const sftpCachePort: number = isNumeric(ActionsCore.getInput('sftpCachePort')) ? parseInt(ActionsCore.getInput('sftpCachePort'), 10) : 22; 31 | const sftpCacheUser: string = ActionsCore.getInput('sftpCacheUser') || ''; 32 | const sftpCachePrivateKey: string = ActionsCore.getInput('sftpCachePrivateKey') || ''; 33 | const sftpCacheExpectedHostKey: string | null = ActionsCore.getInput('sftpCacheExpectedHostKey')?.trim() || null; 34 | 35 | const workingDir = resetWorkingDir(); 36 | const appLogFile = Path.join(workingDir.logs, 'SpraxDev_Actions-SpigotMC.log'); 37 | const appLogStream = Fs.createWriteStream(appLogFile, {encoding: 'utf-8', flags: 'a' /* append */}); 38 | 39 | let spigotArtifactCache: SpigotArtifactCache; 40 | const requestedVersionToArtifactVersionMap = new Map(); 41 | 42 | async function run(): Promise<{ code: number, msg?: string }> { 43 | spigotArtifactCache = new SpigotArtifactCache(sftpCacheHost, sftpCachePort, sftpCacheUser, sftpCachePrivateKey, sftpCacheExpectedHostKey); 44 | if (spigotArtifactCache.isSftpAvailable()) { 45 | logInfo('SFTP-Cache is configured and will be used'); 46 | } else { 47 | logInfo('SFTP-Cache is not configured and will not be used'); 48 | } 49 | 50 | return new Promise(async (resolve, reject): Promise => { 51 | try { 52 | if (versions.length == 0) return resolve({code: 0, msg: 'No version(s) provided to build'}); 53 | 54 | if (!Object.keys(supportedBuildTools).includes(buildToolProvider)) { 55 | return reject(new Error(`'${buildToolProvider}' is not a valid BuildTool-Provider (${Object.keys(supportedBuildTools).join(', ')})`)); 56 | } 57 | 58 | if (!forceRun) { 59 | versions = await removeExistingVersionsAndRestoreFromSftpCacheIfPossible(versions, remapped, (ver, jarPath) => { 60 | logInfo(`Skipping version '${ver}' because it has been found in the local maven repository: ${jarPath}`); 61 | }); 62 | 63 | if (versions.length == 0) return resolve({code: 0, msg: 'No new versions to build'}); 64 | } 65 | 66 | const buildTool = supportedBuildTools[buildToolProvider]; 67 | 68 | logInfo('Installed Java-Version:'); 69 | await runCmd('java', ['-version'], workingDir.base, appLogStream); 70 | 71 | logInfo(`\nDownloading '${buildTool.url}'...`); 72 | await downloadFile(buildTool.url, Path.join(workingDir.cache, 'BuildTools.jar')); 73 | 74 | const gotTemplateDirectory = versions.length != 1; 75 | const buildToolsArgs = ['-jar', 'BuildTools.jar', '--compile', 'Spigot', '--nogui']; 76 | 77 | if (generateSrc) { 78 | buildToolsArgs.push('--generate-source'); 79 | } 80 | 81 | if (generateDoc) { 82 | buildToolsArgs.push('--generate-docs'); 83 | } 84 | 85 | if (disableJavaCheck) { 86 | buildToolsArgs.push('--disable-java-check'); 87 | } 88 | 89 | if (remapped) { 90 | buildToolsArgs.push('--remapped'); 91 | } 92 | 93 | if (finalJarOutputDir) { 94 | const outputDir = Path.isAbsolute(finalJarOutputDir) ? finalJarOutputDir : Path.resolve(finalJarOutputDir); 95 | if (!Fs.existsSync(outputDir)) { 96 | Fs.mkdirSync(outputDir, {recursive: true}); 97 | } 98 | 99 | buildToolsArgs.push('--output-dir'); 100 | buildToolsArgs.push(Path.isAbsolute(finalJarOutputDir) ? finalJarOutputDir : Path.resolve(finalJarOutputDir)); 101 | } 102 | 103 | const tasks = []; 104 | for (const ver of versions) { 105 | tasks.push(async (): Promise => { 106 | return new Promise(async (resolveTask, rejectTask): Promise => { 107 | const start = Date.now(); 108 | 109 | const logFile = Path.join(workingDir.logs, `${ver}.log`); 110 | 111 | logInfo(`Building version '${ver}'...`); 112 | 113 | // If there is only one version to build, the cache directory is used instead of copying it first 114 | const versionDir = gotTemplateDirectory ? Path.join(workingDir.base, `${ver}`) : workingDir.cache; 115 | 116 | if (gotTemplateDirectory) { 117 | await FsExtra.copy(workingDir.cache, versionDir); 118 | } 119 | 120 | try { 121 | // set to silent because multiple builds can run at once 122 | await runCmd('java', [...buildToolsArgs, '--rev', ver], versionDir, logFile, true); 123 | 124 | if (gotTemplateDirectory) { 125 | Fs.rmSync(versionDir, {recursive: true}); // delete our task dir 126 | } 127 | 128 | const end = Date.now(); 129 | 130 | logInfo(`Finished '${ver}' (${requestedVersionToArtifactVersionMap.get(ver)}) in ${((end - start) / 60_000).toFixed(2)} minutes`); 131 | 132 | if (spigotArtifactCache.isSftpAvailable() && requestedVersionToArtifactVersionMap.has(ver)) { 133 | const artifactVersion = requestedVersionToArtifactVersionMap.get(ver)!; 134 | if (await spigotArtifactCache.createAndUploadCacheForVersion(artifactVersion, workingDir.cache, logInfo, logError)) { 135 | logInfo(`Uploaded cache for version '${ver}' (${artifactVersion}) to SFTP-Server`); 136 | } 137 | } 138 | 139 | resolveTask(); 140 | } catch (err: any) { 141 | logInfo(`An error occurred while building '${ver}'`); 142 | logError(err); 143 | 144 | logError(`\nPrinting last 30 lines from '${Path.resolve(logFile)}':`); 145 | 146 | for (const line of readLastLines(logFile, 30)) { 147 | logError(line); 148 | } 149 | 150 | rejectTask(err); 151 | } 152 | }); 153 | }); 154 | } 155 | 156 | Async.parallelLimit(tasks, threadCount, (err) => { 157 | if (err) return reject(err); 158 | 159 | resolve({code: 0}); 160 | }); 161 | } catch (err) { 162 | reject(err); 163 | } 164 | }); 165 | } 166 | 167 | async function removeExistingVersionsAndRestoreFromSftpCacheIfPossible(versionArr: string[], remapped: boolean, onExist: (ver: string, jarPath: string) => void): Promise { 168 | return new Promise(async (resolve, _reject): Promise => { 169 | const result = []; 170 | 171 | for (const ver of versionArr) { 172 | let skipVersion = false; 173 | let versionToCheck: string | null = ver != 'latest' ? ver : null; 174 | 175 | try { 176 | const verJsonBuff = await downloadFile(`https://hub.spigotmc.org/versions/${ver}.json`, null); 177 | const verJson = verJsonBuff instanceof Buffer ? JSON.parse(verJsonBuff.toString('utf-8')) : null; 178 | const bukkitRef: undefined | string = verJson?.refs?.Bukkit; 179 | 180 | if (bukkitRef) { 181 | const verPomBuff = await downloadFile(`https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/raw/pom.xml?at=${bukkitRef}`, null); 182 | 183 | if (verPomBuff instanceof Buffer) { 184 | const result = XmlJs.xml2js(verPomBuff.toString('utf-8'), { 185 | compact: true, 186 | ignoreComment: true, 187 | ignoreAttributes: true 188 | }) as any; 189 | 190 | versionToCheck = result.project?.version?._text; 191 | if (versionToCheck != null) { 192 | requestedVersionToArtifactVersionMap.set(ver, versionToCheck); 193 | } 194 | } 195 | } 196 | } catch (err: any) { 197 | logError(err); 198 | } 199 | 200 | const jarPath = Path.resolve(Path.join(Os.homedir(), `/.m2/repository/org/spigotmc/spigot/${versionToCheck}/spigot-${versionToCheck}${remapped ? '-remapped-mojang' : ''}.jar`)); 201 | if (versionToCheck) { 202 | skipVersion = Fs.existsSync(jarPath); 203 | } 204 | 205 | if (!skipVersion && spigotArtifactCache.isSftpAvailable()) { 206 | if (await spigotArtifactCache.fetchAndExtractCacheForVersionIfExists(versionToCheck ?? ver, workingDir.cache, logInfo, logError)) { 207 | logInfo(`Restored version '${versionToCheck ?? ver}' (${ver}) from SFTP-Cache`); 208 | skipVersion = Fs.existsSync(jarPath); 209 | } else { 210 | logInfo(`Version '${versionToCheck ?? ver}' (${ver}) not found in SFTP-Cache`); 211 | } 212 | } 213 | 214 | if (skipVersion) { 215 | onExist(ver, jarPath); 216 | } else { 217 | result.push(ver); 218 | } 219 | } 220 | 221 | resolve(result); 222 | }); 223 | } 224 | 225 | export function logInfo(msg?: string): void { 226 | console.log(msg); 227 | appLogStream.write(msg + '\n'); 228 | } 229 | 230 | export function logError(msg?: string | object): void { 231 | if (typeof msg != 'string') { 232 | msg = JSON.stringify(msg, null, 2); 233 | } 234 | 235 | console.error(msg); 236 | appLogStream.write(msg + '\n'); 237 | } 238 | 239 | let exitCode = 2; 240 | let exitMessage: string | Error | undefined; 241 | 242 | run() 243 | .then((result) => { 244 | exitCode = result.code; 245 | exitMessage = result.msg; 246 | }) 247 | .catch((err) => { 248 | exitCode = 1; 249 | exitMessage = err; 250 | }) 251 | .finally(async () => { 252 | await spigotArtifactCache?.shutdown(); 253 | exit(exitCode, exitMessage); 254 | }); 255 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import NReadLines from 'n-readlines'; 2 | import ChildProcess from 'node:child_process'; 3 | import Fs from 'node:fs'; 4 | import Http from 'node:http'; 5 | import Https from 'node:https'; 6 | import Os from 'node:os'; 7 | import Path from 'node:path'; 8 | import {logError, logInfo} from './index'; 9 | 10 | const packageJson = JSON.parse(Fs.readFileSync(Path.join(__dirname, '..', 'package.json'), 'utf-8')); 11 | const userAgent = `${packageJson.name || 'Action-SpigotMC'}/${packageJson.version || 'UNKNOWN_VERSION'} (+${packageJson.homepage || 'https://github.com/SpraxDev/Action-SpigotMC'})`; 12 | 13 | export function fixArgArr(arr: string[]): string[] { 14 | const result: string[] = []; 15 | 16 | for (const element of arr) { 17 | const newValue = element.trim(); 18 | 19 | if (newValue && !result.includes(newValue)) { 20 | result.push(newValue); 21 | } 22 | } 23 | 24 | return result; 25 | } 26 | 27 | export function isNumeric(str: string): boolean { 28 | return /^[0-9]+$/.test(str); 29 | } 30 | 31 | export async function runCmd(cmd: string, args: string[], workingDir: string, logStreamOrFile: string | Fs.WriteStream, silent: boolean = false): Promise { 32 | return new Promise((resolve, reject) => { 33 | const closeLogStream = typeof logStreamOrFile == 'string'; 34 | const logStream = typeof logStreamOrFile != 'string' ? logStreamOrFile : 35 | Fs.createWriteStream(logStreamOrFile, {encoding: 'utf-8', flags: 'a'}); 36 | 37 | const runningProcess = ChildProcess.spawn(cmd, args, {shell: true, cwd: workingDir, env: process.env}); 38 | 39 | runningProcess.stdout.on('data', (data) => { 40 | logStream.write(data); 41 | 42 | if (!silent) { 43 | process.stdout.write(data); // Not using console.log to prevent '\n\n' 44 | } 45 | }); 46 | runningProcess.stderr.on('data', (data) => { 47 | logStream.write(data); 48 | 49 | if (!silent) { 50 | process.stderr.write(data); // Not using console.error to prevent '\n\n' 51 | } 52 | }); 53 | 54 | runningProcess.on('close', (code) => { 55 | if (closeLogStream) { 56 | logStream.close(); 57 | } 58 | 59 | if (code != 0) { 60 | return reject({err: new Error(`process exited with code ${code}`), cmd, workingDir}); 61 | } 62 | 63 | resolve(); 64 | }); 65 | }); 66 | } 67 | 68 | /** 69 | * @param url The URL to fetch the data from 70 | * @param dest Set to `null` to get an Buffer instead of writing it to the file system 71 | * @param currRedirectDepth Internally used to track how often the function has been redirected 72 | */ 73 | export async function downloadFile(url: string, dest: string | null, currRedirectDepth: number = 0): Promise { 74 | const doGetRequest = url.toLowerCase().startsWith('http://') ? Http.get : Https.get; 75 | 76 | return new Promise((resolve, reject) => { 77 | let writeStream: Fs.WriteStream | null = null; 78 | 79 | const done = function (errored: boolean) { 80 | if (writeStream) { 81 | writeStream.close(); 82 | writeStream = null; 83 | 84 | if (errored && dest != null) { 85 | Fs.rmSync(dest, {recursive: true}); 86 | } 87 | } 88 | }; 89 | 90 | doGetRequest(url, { 91 | headers: { 92 | 'User-Agent': userAgent 93 | } 94 | }, (httpRes) => { 95 | if (httpRes.statusCode != 200) { 96 | const locHeader = httpRes.headers.location; 97 | 98 | // Follow redirect 99 | if (currRedirectDepth < 12 && locHeader && 100 | (httpRes.statusCode == 301 || httpRes.statusCode == 302 || httpRes.statusCode == 303 || 101 | httpRes.statusCode == 307 || httpRes.statusCode == 308)) { 102 | done(false); 103 | 104 | if (!/https?:\/\//g.test(locHeader)) { 105 | return reject(new Error(`Server responded with ${httpRes.statusCode} and a relative Location-Header value (${locHeader})`)); 106 | } 107 | 108 | return downloadFile(locHeader, dest, ++currRedirectDepth) 109 | .then(resolve) 110 | .catch(reject); 111 | } else { 112 | done(true); 113 | 114 | return reject(new Error(`Server responded with ${httpRes.statusCode}`)); 115 | } 116 | } 117 | 118 | if (dest != null) { 119 | writeStream = Fs.createWriteStream(dest, {encoding: 'binary'}) 120 | .on('finish', () => { 121 | done(false); 122 | 123 | return resolve(); 124 | }) 125 | .on('error', (err) => { 126 | done(true); 127 | 128 | return reject(err); 129 | }); 130 | 131 | httpRes.pipe(writeStream); 132 | } else { 133 | const chunks: Buffer[] = []; 134 | 135 | httpRes.on('data', (chunk) => { 136 | chunks.push(Buffer.from(chunk, 'binary')); 137 | }); 138 | 139 | httpRes.on('end', () => { 140 | resolve(Buffer.concat(chunks)); 141 | }); 142 | } 143 | }) 144 | .on('error', (err) => { 145 | done(true); 146 | 147 | return reject(err); 148 | }); 149 | }); 150 | } 151 | 152 | export function readLastLines(file: string, lineCount: number, encoding: BufferEncoding = 'utf-8'): string[] { 153 | const result = []; 154 | 155 | const reader = new NReadLines(file); 156 | 157 | let line; 158 | while (line = reader.next()) { 159 | result.push(line.toString(encoding)); 160 | 161 | if (result.length > lineCount) { 162 | result.shift(); 163 | } 164 | } 165 | 166 | return result; 167 | } 168 | 169 | export function resetWorkingDir(): { base: string, cache: string, logs: string } { 170 | const baseDir = Path.join(Os.tmpdir(), 'SpraxDev-Action-SpigotMC'); 171 | const cacheDir = Path.join(baseDir, 'cache'); 172 | const logDir = Path.join(baseDir, 'logs'); 173 | 174 | Fs.rmSync(baseDir, {recursive: true, force: true}); // delete dir 175 | 176 | // create directories 177 | Fs.mkdirSync(cacheDir, {recursive: true}); 178 | Fs.mkdirSync(logDir, {recursive: true}); 179 | 180 | return {base: baseDir, cache: cacheDir, logs: logDir}; 181 | } 182 | 183 | export function exit(code: number, msg?: string | Error): never { 184 | if (msg) { 185 | if (typeof msg == 'string') { 186 | logInfo(msg); 187 | } else { 188 | logError(msg); 189 | } 190 | } 191 | 192 | return process.exit(code); 193 | } 194 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "isolatedModules": true 5 | }, 6 | "include": [ 7 | "src/**/*.ts" 8 | ] 9 | } 10 | --------------------------------------------------------------------------------