├── .github ├── bump ├── dependabot.yml ├── scripts │ ├── pre-commit.js │ └── pre-commit │ │ ├── build.sh │ │ └── readme-update.sh └── workflows │ ├── build.yml │ └── test.yml ├── .gitignore ├── .releaserc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── action.yml ├── dist ├── index.js └── licenses.txt ├── index.ts ├── main.ts ├── package-lock.json ├── package.json ├── test ├── integration.spec.ts ├── test.zip └── utils.spec.ts ├── tsconfig.json └── utils.ts /.github/bump: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | versioning-strategy: "increase-if-necessary" 8 | -------------------------------------------------------------------------------- /.github/scripts/pre-commit.js: -------------------------------------------------------------------------------- 1 | const {join} = require("path") 2 | const {readdir} = require("fs").promises 3 | const proc = require("child_process") 4 | const {promisify} = require("util") 5 | 6 | const exec = promisify(proc.exec) 7 | 8 | exports.preCommit = async ({tag, version}) => { 9 | process.env["BUILD_TAG"] = tag 10 | process.env["BUILD_VERSION"] = version 11 | let basedir = join(__dirname, 'pre-commit') 12 | 13 | let files = await readdir(basedir) 14 | files = files.map(file => join(basedir, file)) 15 | files = files.map(file => exec(file)) 16 | await Promise.all(files) 17 | } 18 | -------------------------------------------------------------------------------- /.github/scripts/pre-commit/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | npm run build 3 | -------------------------------------------------------------------------------- /.github/scripts/pre-commit/readme-update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | perl -pi -e "s/JoshPiper\/GModStore-Deployment\@([\w.]+)?/JoshPiper\/GModStore-Deployment\@$BUILD_TAG/g" README.md 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Builds 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: '>=20.0.0' 18 | - name: Install Dependencies 19 | run: npm ci 20 | - name: Build Release 21 | run: npm run release 22 | env: 23 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 24 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | - name: Install Dependencies 13 | run: npm ci && npm run build 14 | - name: Test 15 | uses: ./ 16 | with: 17 | product: ${{ secrets.GMS_ADDON_ID }} 18 | token: ${{ secrets.GMS_TOKEN }} 19 | version: 1.0.0 20 | type: private 21 | path: tests/test.zip 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /.releaserc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "branches": [ 3 | "main", 4 | "build/semantic-release" 5 | ], 6 | "plugins": [ 7 | ["@semantic-release/commit-analyzer", { 8 | "parserOpts": { 9 | "headerPattern": /^(\w*)(?:\((.*)\))?: (.*)$/, 10 | "breakingHeaderPattern": /^(\w*)(?:\((.*)\))?!: (.*)$/ 11 | }, 12 | "releaseRules": [{ 13 | "type": "*!", 14 | "release": "major" 15 | }, { 16 | "type": "build", 17 | "scope": "*deps", 18 | "release": "patch" 19 | }] 20 | }], 21 | ["@semantic-release/exec", { 22 | prepareCmd: "npm run build" 23 | }], 24 | ["@semantic-release/exec", { 25 | prepareCmd: 'perl -pi -e "s/JoshPiper\\/deployment-for-gmodstore\\@([\\w.]+)?/JoshPiper\\/deployment-for-gmodstore\\@v${nextRelease.version}/g" README.md' 26 | }], 27 | ["@semantic-release/exec", { 28 | prepareCmd: 'git commit -am "chore: Prepare ${nextRelease.version}" && git push' 29 | }], 30 | ["@semantic-release/release-notes-generator", { 31 | "parserOpts": { 32 | "headerPattern": /^(\w*)(?:\((.*)\))?!?: (.*)$/, 33 | "breakingHeaderPattern": /^(\w*)(?:\((.*)\))?!: (.*)$/ 34 | }, 35 | "writerOpts": { 36 | "transform": (commit, context) => { 37 | let discard = false 38 | const issues = [] 39 | 40 | if (commit.type === 'chore'){ 41 | discard = true 42 | } 43 | 44 | commit.notes.forEach(note => { 45 | note.title = 'BREAKING CHANGES' 46 | discard = false 47 | }) 48 | 49 | if (commit.type === 'feat') { 50 | commit.type = 'Features' 51 | } else if (commit.type === 'fix') { 52 | commit.type = 'Bug Fixes' 53 | } else if (commit.type === 'perf') { 54 | commit.type = 'Performance Improvements' 55 | } else if (commit.type === 'revert' || commit.revert) { 56 | commit.type = 'Reverts' 57 | } else if (discard) { 58 | return 59 | } else if (commit.type === 'docs') { 60 | commit.type = 'Documentation' 61 | } else if (commit.type === 'style') { 62 | commit.type = 'Styles' 63 | } else if (commit.type === 'refactor') { 64 | commit.type = 'Code Refactoring' 65 | } else if (commit.type === 'test' || commit.type === 'tests') { 66 | commit.type = 'Tests' 67 | } else if (commit.type === 'build') { 68 | if (commit.scope === 'dep' || commit.scope === 'deps'){ 69 | commit.type = 'Dependencies' 70 | commit.scope = '' 71 | } else if (commit.scope === 'dev-dep' || commit.scope === 'dev-deps'){ 72 | commit.type = 'Dependencies' 73 | commit.scope = 'development' 74 | } else { 75 | commit.type = 'Build System' 76 | } 77 | } else if (commit.type === 'ci') { 78 | commit.type = 'Continuous Integration' 79 | } 80 | 81 | if (commit.scope === '*') { 82 | commit.scope = '' 83 | } 84 | 85 | if (typeof commit.hash === 'string') { 86 | commit.shortHash = commit.hash.substring(0, 7) 87 | } 88 | 89 | if (typeof commit.subject === 'string') { 90 | let url = context.repository 91 | ? `${context.host}/${context.owner}/${context.repository}` 92 | : context.repoUrl 93 | if (url) { 94 | url = `${url}/issues/` 95 | // Issue URLs. 96 | commit.subject = commit.subject.replace(/#([0-9]+)/g, (_, issue) => { 97 | issues.push(issue) 98 | return `[#${issue}](${url}${issue})` 99 | }) 100 | } 101 | if (context.host) { 102 | // User URLs. 103 | commit.subject = commit.subject.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_, username) => { 104 | if (username.includes('/')) { 105 | return `@${username}` 106 | } 107 | 108 | return `[@${username}](${context.host}/${username})` 109 | }) 110 | } 111 | } 112 | 113 | // remove references that already appear in the subject 114 | commit.references = commit.references.filter(reference => { 115 | if (issues.indexOf(reference.issue) === -1) { 116 | return true 117 | } 118 | 119 | return false 120 | }) 121 | 122 | return commit 123 | }, 124 | } 125 | }], 126 | ["@semantic-release/github", { 127 | "successComment": false 128 | }] 129 | ] 130 | } 131 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.1](https://github.com//JoshPiper/GModStore-Deployment/compare/v1.0.0...v1.0.1) (2023-07-23) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * Corrected API Route. ([167d55e](https://github.com//JoshPiper/GModStore-Deployment/commit/167d55e3eb29c53fa45098c711dd29753dc0fcfd)) 7 | 8 | 9 | 10 | # [1.0.0](https://github.com//JoshPiper/GModStore-Deployment/compare/v0.7.0...v1.0.0) (2022-12-02) 11 | 12 | 13 | * GMS API v3 (#53) ([a5df42b](https://github.com//JoshPiper/GModStore-Deployment/commit/a5df42b6276ff38c96a4ab5920dac0050e193c47)), closes [#53](https://github.com//JoshPiper/GModStore-Deployment/issues/53) 14 | 15 | 16 | ### BREAKING CHANGES 17 | 18 | * Addon input has been removed, and replaced with Product. 19 | 20 | 21 | 22 | # [0.7.0](https://github.com//JoshPiper/GModStore-Deployment/compare/v0.6.3...v0.7.0) (2022-03-22) 23 | 24 | 25 | ### Features 26 | 27 | * Display the error if a json decode error occurs. ([f381693](https://github.com//JoshPiper/GModStore-Deployment/commit/f3816935da8225f1381e14a7c4e47984c7bb4241)) 28 | 29 | 30 | 31 | ## [0.6.3](https://github.com//JoshPiper/GModStore-Deployment/compare/v0.6.2...v0.6.3) (2021-06-10) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * guard against an invalid json response. ([#24](https://github.com//JoshPiper/GModStore-Deployment/issues/24)) ([a1272c2](https://github.com//JoshPiper/GModStore-Deployment/commit/a1272c20bfcf052f9798db95c1e16a0220441ab1)) 37 | 38 | 39 | 40 | ## [0.6.2](https://github.com//JoshPiper/GModStore-Deployment/compare/v0.6.1...v0.6.2) (2021-05-04) 41 | 42 | 43 | ### Bug Fixes 44 | 45 | * Fixed Version-Type error. ([#19](https://github.com//JoshPiper/GModStore-Deployment/issues/19)) ([9e1eca0](https://github.com//JoshPiper/GModStore-Deployment/commit/9e1eca0bd8a278cf507d6d8eec9bef471f29a40b)) 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2021, Joshua Piper 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GModStore Deployment Action 2 | 3 | Easily upload an addon build to GmodStore. 4 | 5 | ## Usage 6 | ```yml 7 | - name: Upload 8 | uses: JoshPiper/deployment-for-gmodstore@v1.0.3 9 | with: 10 | product: "00000000-0000-0000-0000-000000000000" 11 | token: "${{ secrets.GMS_TOKEN }}" 12 | version: "1.0.0" 13 | path: "addon.zip" 14 | ``` 15 | 16 | ## Inputs 17 | 18 | | Input | State | Description | 19 | |-----------|---------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 20 | | token | | Your GmodStore API Token.
This token must have versions write permission. | 21 | | product | | The product ID, found in the product dashboard. | 22 | | path | | Path to zip file to upload. | 23 | | version | | The new version name to upload.
This input is limited to 80 characters.
If type is not set, this input is parsed as a SemVer to find a pre-release suffix to use as type instead. | 24 | | type | Default: "stable"
Enum: ["stable", "beta", "alpha", "private", "demo"] | Type of version to release. | 25 | | changelog | Default: "No changelog provided." | Markdown formatted changelog. | 26 | | baseurl | Default: https://api.gmodstore.com/v3/ | Base API URL, for mocking or local proxy. | 27 | | dryrun | Default: FALSE | If we should dry-run and handle all the prep, but refrain from the actual upload. | 28 | | nointuit | Default: FALSE | Disable attempting to intuit the type field from the version field. | 29 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "Deployment-For-gmodstore" 2 | description: "Deploy scripts to gmodstore" 3 | author: "Joshua Piper " 4 | branding: 5 | icon: check 6 | color: blue 7 | 8 | runs: 9 | using: node20 10 | main: "dist/index.js" 11 | 12 | inputs: 13 | token: 14 | description: "GModStore API Token" 15 | required: true 16 | product: 17 | description: "The ID of the addon to deploy to." 18 | required: true 19 | version: 20 | description: "The new version name to deploy." 21 | required: true 22 | type: 23 | description: "The type of version to deploy. Can be stable, beta, alpha, private or demo. If omitted, uses '-type' at the end of version string (if it exists), otherwise uses stable" 24 | required: false 25 | default: "stable" 26 | changelog: 27 | description: "Markdown formatted changelog." 28 | required: true 29 | default: "No changelog." 30 | path: 31 | description: "Path to the addon. This must be in zip format." 32 | required: true 33 | baseurl: 34 | description: "API Base URL" 35 | required: true 36 | default: "https://api.gmodstore.com/v3/" 37 | -------------------------------------------------------------------------------- /dist/licenses.txt: -------------------------------------------------------------------------------- 1 | @actions/core 2 | MIT 3 | The MIT License (MIT) 4 | 5 | Copyright 2019 GitHub 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | @actions/http-client 14 | MIT 15 | Actions Http Client for Node.js 16 | 17 | Copyright (c) GitHub, Inc. 18 | 19 | All rights reserved. 20 | 21 | MIT License 22 | 23 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 24 | associated documentation files (the "Software"), to deal in the Software without restriction, 25 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 26 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 27 | subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 32 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 33 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 34 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 35 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | 38 | @vercel/ncc 39 | MIT 40 | Copyright 2018 ZEIT, Inc. 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 47 | 48 | form-data-encoder 49 | MIT 50 | The MIT License (MIT) 51 | 52 | Copyright (c) 2021-present Nick K. 53 | 54 | Permission is hereby granted, free of charge, to any person obtaining a copy 55 | of this software and associated documentation files (the "Software"), to deal 56 | in the Software without restriction, including without limitation the rights 57 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 58 | copies of the Software, and to permit persons to whom the Software is 59 | furnished to do so, subject to the following conditions: 60 | 61 | The above copyright notice and this permission notice shall be included in all 62 | copies or substantial portions of the Software. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 65 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 66 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 67 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 68 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 69 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 70 | SOFTWARE. 71 | 72 | 73 | formdata-node 74 | MIT 75 | The MIT License (MIT) 76 | 77 | Copyright (c) 2017-present Nick K. 78 | 79 | Permission is hereby granted, free of charge, to any person obtaining a copy 80 | of this software and associated documentation files (the "Software"), to deal 81 | in the Software without restriction, including without limitation the rights 82 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 83 | copies of the Software, and to permit persons to whom the Software is 84 | furnished to do so, subject to the following conditions: 85 | 86 | The above copyright notice and this permission notice shall be included in all 87 | copies or substantial portions of the Software. 88 | 89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 90 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 91 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 92 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 93 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 94 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 95 | SOFTWARE. 96 | 97 | 98 | lru-cache 99 | ISC 100 | The ISC License 101 | 102 | Copyright (c) Isaac Z. Schlueter and Contributors 103 | 104 | Permission to use, copy, modify, and/or distribute this software for any 105 | purpose with or without fee is hereby granted, provided that the above 106 | copyright notice and this permission notice appear in all copies. 107 | 108 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 109 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 110 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 111 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 112 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 113 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 114 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 115 | 116 | 117 | node-fetch 118 | MIT 119 | The MIT License (MIT) 120 | 121 | Copyright (c) 2016 David Frank 122 | 123 | Permission is hereby granted, free of charge, to any person obtaining a copy 124 | of this software and associated documentation files (the "Software"), to deal 125 | in the Software without restriction, including without limitation the rights 126 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 127 | copies of the Software, and to permit persons to whom the Software is 128 | furnished to do so, subject to the following conditions: 129 | 130 | The above copyright notice and this permission notice shall be included in all 131 | copies or substantial portions of the Software. 132 | 133 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 134 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 135 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 136 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 137 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 138 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 139 | SOFTWARE. 140 | 141 | 142 | 143 | semver 144 | ISC 145 | The ISC License 146 | 147 | Copyright (c) Isaac Z. Schlueter and Contributors 148 | 149 | Permission to use, copy, modify, and/or distribute this software for any 150 | purpose with or without fee is hereby granted, provided that the above 151 | copyright notice and this permission notice appear in all copies. 152 | 153 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 154 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 155 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 156 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 157 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 158 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 159 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 160 | 161 | 162 | tr46 163 | MIT 164 | 165 | tunnel 166 | MIT 167 | The MIT License (MIT) 168 | 169 | Copyright (c) 2012 Koichi Kobayashi 170 | 171 | Permission is hereby granted, free of charge, to any person obtaining a copy 172 | of this software and associated documentation files (the "Software"), to deal 173 | in the Software without restriction, including without limitation the rights 174 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 175 | copies of the Software, and to permit persons to whom the Software is 176 | furnished to do so, subject to the following conditions: 177 | 178 | The above copyright notice and this permission notice shall be included in 179 | all copies or substantial portions of the Software. 180 | 181 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 182 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 183 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 184 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 185 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 186 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 187 | THE SOFTWARE. 188 | 189 | 190 | uuid 191 | MIT 192 | The MIT License (MIT) 193 | 194 | Copyright (c) 2010-2020 Robert Kieffer and other contributors 195 | 196 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 197 | 198 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 199 | 200 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 201 | 202 | 203 | webidl-conversions 204 | BSD-2-Clause 205 | # The BSD 2-Clause License 206 | 207 | Copyright (c) 2014, Domenic Denicola 208 | All rights reserved. 209 | 210 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 211 | 212 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 213 | 214 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 215 | 216 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 217 | 218 | 219 | whatwg-url 220 | MIT 221 | The MIT License (MIT) 222 | 223 | Copyright (c) 2015–2016 Sebastian Mayr 224 | 225 | Permission is hereby granted, free of charge, to any person obtaining a copy 226 | of this software and associated documentation files (the "Software"), to deal 227 | in the Software without restriction, including without limitation the rights 228 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 229 | copies of the Software, and to permit persons to whom the Software is 230 | furnished to do so, subject to the following conditions: 231 | 232 | The above copyright notice and this permission notice shall be included in 233 | all copies or substantial portions of the Software. 234 | 235 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 236 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 237 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 238 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 239 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 240 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 241 | THE SOFTWARE. 242 | 243 | 244 | yallist 245 | ISC 246 | The ISC License 247 | 248 | Copyright (c) Isaac Z. Schlueter and Contributors 249 | 250 | Permission to use, copy, modify, and/or distribute this software for any 251 | purpose with or without fee is hereby granted, provided that the above 252 | copyright notice and this permission notice appear in all copies. 253 | 254 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 255 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 256 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 257 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 258 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 259 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 260 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 261 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // noinspection JSIgnoredPromiseFromCall 3 | 4 | import {main} from "./main" 5 | main() 6 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core" 2 | import fetch from "node-fetch" 3 | import * as fs from "node:fs" 4 | import { 5 | token as getToken, 6 | product as getProduct, 7 | effectiveNameVersion as getVersion, 8 | path as getPath, 9 | changelog as getChangelog, 10 | baseUrl as getBaseUrl, 11 | dry as isDryRun 12 | } from "./utils"; 13 | import {setFailed} from "@actions/core" 14 | import {FormData} from "formdata-node" 15 | // @ts-ignore 16 | import {FormDataEncoder} from "form-data-encoder" 17 | import {Readable} from "stream"; 18 | 19 | async function main(){ 20 | let token, product, version, versionType, path, changelog, baseUrl 21 | const dry = isDryRun() 22 | try { 23 | token = getToken() 24 | product = getProduct() 25 | let v = getVersion() 26 | version = v[0] 27 | versionType = v[1] 28 | path = getPath() 29 | changelog = getChangelog() 30 | baseUrl = getBaseUrl() 31 | } catch (err){ 32 | setFailed(`An error occured during input processing.\n${err}`) 33 | return 34 | } 35 | 36 | FormData 37 | 38 | let newVersion = new FormData() 39 | newVersion.append("name", version) 40 | newVersion.append("changelog", changelog) 41 | newVersion.append("file", new Blob([fs.readFileSync(path)]), path) 42 | newVersion.append("releaseType", versionType) 43 | let encoder = new FormDataEncoder(newVersion) 44 | 45 | if (!dry){ 46 | console.log(`${baseUrl}products/${product}/versions`) 47 | let response = await fetch(`${baseUrl}products/${product}/versions`, { 48 | method: "POST", 49 | body: Readable.from(encoder), 50 | headers: { 51 | "Authorization": `Bearer ${token}`, 52 | ...encoder.headers 53 | } 54 | }) 55 | 56 | if (response.status < 200 || response.status >= 300){ 57 | let body 58 | try { 59 | body = await response.json() 60 | } catch (e){ 61 | core.warning("An error occurred whilst decoding the JSON response.") 62 | core.warning("This shouldn't normally happen, and suggests an issue with the API itself.") 63 | if (e instanceof Error || typeof e === "string"){ 64 | core.error(e) 65 | } 66 | 67 | return 68 | } 69 | 70 | core.setFailed(`An error occurred during upload, with HTTP code ${response.status} and message "${body.message}".`) 71 | if (body.errors){ 72 | for (let id of Object.keys(body.errors)){ 73 | let errs = body.errors[id] 74 | let leng = errs.length 75 | if (leng === 1){ 76 | core.error(`An error occurred in the ${id} field`) 77 | } else { 78 | core.error(`Errors occurred in the ${id} field`) 79 | } 80 | for (let i = 0; i < leng; i++){ 81 | core.error(errs[i]) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | export default main 90 | export {main} 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@doctor_internet/deployment-for-gmodstore", 3 | "version": "1.0.0", 4 | "description": "GitHub Action for deploying to GModStore.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build:source": "npx ncc build index.ts --license licenses.txt", 8 | "build:source:clean": "rm dist/index.js dist/licenses.txt; rmdir dist", 9 | "build:source:watch": "npx ncc build --watch index.ts", 10 | "build:modules:clean": "((ls node_modules > /dev/null 2>&1) && rm -r ./node_modules) || true", 11 | "build": "npm run build:source", 12 | "build:full": "npm run build:modules:clean && npm run build:source:clean && npm ci && npm run build:source", 13 | "test": "npm run test:utils", 14 | "test:utils": "mocha -r ts-node/register test/utils.spec.ts", 15 | "release": "npx semantic-release" 16 | }, 17 | "repository": { 18 | "type": "github", 19 | "url": "https://github.com/JoshPiper/deployment-for-gmodstore.git" 20 | }, 21 | "keywords": [], 22 | "author": "John Internet ", 23 | "license": "ISC", 24 | "dependencies": { 25 | "@actions/core": "^1.11.1", 26 | "form-data-encoder": "^1.9.0", 27 | "formdata-node": "^6.0.3", 28 | "node-fetch": "^2.7.0", 29 | "semver": "^7.7.1", 30 | "uuid": "^11.1.0" 31 | }, 32 | "devDependencies": { 33 | "@semantic-release/exec": "^7.0.3", 34 | "@tsconfig/node20": "^20.1.4", 35 | "@types/chai": "^5.0.1", 36 | "@types/mocha": "^10.0.10", 37 | "@types/node": "^22.13.8", 38 | "@types/node-fetch": "^2.6.6", 39 | "@types/semver": "^7.5.8", 40 | "@types/sinon": "^17.0.4", 41 | "@types/uuid": "^10.0.0", 42 | "@vercel/ncc": "^0.38.3", 43 | "chai": "^5.2.0", 44 | "mocha": "^11.1.0", 45 | "nyc": "^17.1.0", 46 | "semantic-release": "^24.2.3", 47 | "sinon": "^19.0.2", 48 | "ts-node": "^10.9.2", 49 | "typescript": "^5.7.3", 50 | "yaml": "^2.7.0" 51 | }, 52 | "engines": { 53 | "node": ">=20.0.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/integration.spec.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path" 2 | import main from "../main" 3 | 4 | describe('Integration', () => { 5 | it('Uploads the Test Folder', async () => { 6 | process.env['INPUT_PRODUCT'] = "46529d74-df19-4297-865f-6d11b6a787fd" 7 | process.env['INPUT_TOKEN'] = process.env['GMS_TOKEN'] 8 | process.env['INPUT_VERSION'] = `v1.0.0-private+test:${(new Date().getTime())}` 9 | process.env['INPUT_TYPE'] = 'private' 10 | process.env['INPUT_PATH'] = join(__dirname, "test.zip") 11 | process.env['INPUT_CHANGELOG'] = ` 12 | # Version ${process.env['INPUT_VERSION']} 13 | 14 | Test Upload from deployment-for-gmodstore. 15 | Uploaded At: ${(new Date()).toString()} 16 | ` 17 | await main() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/test.zip: -------------------------------------------------------------------------------- 1 | PK -------------------------------------------------------------------------------- /test/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import {assert} from "chai" 2 | import {effectiveNameVersion, token} from "../utils"; 3 | 4 | describe('#effectiveNameVersion', () => { 5 | it('Parses Complex Versions', () => { 6 | process.env.INPUT_VERSION = "1.0.0-alpha.1.beta.2.private.1.demo" 7 | let [name, type] = effectiveNameVersion() 8 | assert.strictEqual(name, "1.0.0-alpha.1.beta.2.private.1.demo") 9 | assert.strictEqual(type, 'private') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | 4 | "files": [ 5 | "index.ts", 6 | "main.ts", 7 | "utils.ts" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /utils.ts: -------------------------------------------------------------------------------- 1 | import {getInput, InputOptions} from "@actions/core" 2 | import {validate as validUUID} from "uuid" 3 | import {parse} from "semver" 4 | 5 | /** 6 | * List of version suffixes, ordered in MOST to LEAST public. 7 | * Demo is accessible to all users. 8 | * Stable is accessible to all purchasers. 9 | * Etc 10 | */ 11 | const VERSIONS = ['demo', 'stable', 'beta', 'alpha', 'private'] as const 12 | type Version = typeof VERSIONS[number] 13 | const VERSION_MAP = new Set(VERSIONS) 14 | const VERSION_REGEX = /(.*?)-(stable|beta|alpha|private|demo)$/gi 15 | 16 | const defaultOptions: InputOptions & ((extra: Partial) => InputOptions) = function (this: Partial, extra: Partial): InputOptions { 17 | return {...this, ...extra} 18 | } 19 | defaultOptions.required = true 20 | defaultOptions.trimWhitespace = true 21 | 22 | const optional = defaultOptions({required: false}) 23 | 24 | export function token(): string { 25 | return getInput("token", defaultOptions) 26 | } 27 | 28 | export function product(): string { 29 | let pid = getInput("product", defaultOptions) 30 | 31 | if (!validUUID(pid)){ 32 | throw "Input 'product' is not a valid UUID." 33 | } 34 | 35 | return pid 36 | } 37 | 38 | export function version(): string { 39 | return getInput("version", defaultOptions) 40 | } 41 | 42 | export function hasType(): boolean { 43 | return getInput("type", optional).toLowerCase() !== "" 44 | } 45 | 46 | export function type(): Version { 47 | const type = getInput("type", optional).toLowerCase() 48 | if (type === ""){ 49 | return "stable" 50 | } else if (VERSION_MAP.has(type)) { 51 | return type 52 | } 53 | 54 | throw `Input 'type' must be one of ${[...VERSION_MAP.keys()].join(", ")}, got "${type}"` 55 | } 56 | 57 | export function path(): string { 58 | const path = getInput("path", defaultOptions) 59 | if (!path.endsWith(".zip")){ 60 | throw "Input path must end in .zip" 61 | } 62 | 63 | return path 64 | } 65 | 66 | export function changelog(): string { 67 | const log = getInput("changelog", optional) 68 | if (log === ""){ 69 | return "No changelog provided." 70 | } 71 | 72 | return log 73 | } 74 | 75 | export function baseUrl(): URL { 76 | let url = getInput("baseurl", optional) 77 | if (url === ""){ 78 | url = "https://api.gmodstore.com/v3/" 79 | } 80 | 81 | return new URL(url) 82 | } 83 | 84 | export function dry(): boolean { 85 | return getInput("dryrun", optional).toLowerCase() === "true" 86 | } 87 | 88 | /** 89 | * Set if we should disable intuiting versions, and instead only use the type input. 90 | */ 91 | export function nointuit(): boolean { 92 | return getInput("nointuit", optional).toLowerCase() === "true" 93 | } 94 | 95 | /** 96 | * Get the effective name and version to upload. 97 | * First, attempt to parse as semver. 98 | * If a single, non-numbered, pre-release version is encountered, which is a valid suffix, it is removed and used as the version type. 99 | * Otherwise, use the legacy regex. 100 | * Lastly, fall back to type input. 101 | */ 102 | export function effectiveNameVersion(): string[] { 103 | const intuit = !nointuit() 104 | const raw = version() 105 | 106 | if (hasType()){ 107 | return [version(), type()] 108 | } 109 | 110 | if (intuit){ 111 | console.log("Intuiting") 112 | const ver = parse(raw) 113 | console.log(ver) 114 | if (ver !== null){ 115 | let last: string | number | null = null 116 | const parsed = new Map() 117 | for (const value of ver.prerelease){ 118 | if (typeof last === "string"){ 119 | if (typeof value === "number"){ 120 | parsed.set(last, value) 121 | last = null 122 | } else { 123 | parsed.set(last, 0) 124 | last = null 125 | } 126 | } 127 | 128 | if (typeof value === "string"){ 129 | last = value 130 | } 131 | } 132 | if (last){ 133 | parsed.set(last, 0) 134 | } 135 | console.log(parsed) 136 | for (const suffix of VERSIONS.toReversed()){ 137 | if (parsed.has(suffix)){ 138 | console.log(`has ${suffix}`) 139 | if (parsed.size !== 1 || parsed.get(suffix) !== 0){ 140 | console.log("returning") 141 | return [ver.raw, suffix] 142 | } else { 143 | ver.prerelease = [] 144 | return [ver.format(), suffix] 145 | } 146 | } 147 | } 148 | } 149 | 150 | const res = VERSION_REGEX.exec(version()) 151 | if (res !== null){ 152 | return [res[1], res[2]] 153 | } 154 | } 155 | 156 | return [version(), type()] 157 | } 158 | --------------------------------------------------------------------------------