├── .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 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
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 |
--------------------------------------------------------------------------------