├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md └── workflows │ ├── release.yml │ ├── test.yml │ └── update-changesets.yml ├── .gitignore ├── .tool-versions ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── build ├── .releaserc.json └── build_npm.ts ├── deno.json ├── deno.lock └── src ├── cli.ts ├── index.ts ├── unityChangeset.test.ts ├── unityChangeset.ts └── unityGraphQL.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mob-sakai # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: mob-sakai # Replace with a single Patreon username 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 to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: mob-sakai 7 | 8 | --- 9 | 10 | NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one. 11 | 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Environment (please complete the following information):** 29 | - OS: [e.g. Windows 10, MacOS 10.14] 30 | - Node: [e.g. 8.15] 31 | - Version: [e.g. 1.0.0] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: mob-sakai 7 | 8 | --- 9 | 10 | NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one. 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question about this project 4 | title: '' 5 | labels: question 6 | assignees: mob-sakai 7 | 8 | --- 9 | 10 | NOTE: Your issue may already be reported! Please search on the [issue tracker](../) before creating one. 11 | 12 | **Describe what help do you need** 13 | A description of the question. 14 | 15 | **Additional context** 16 | Add any other context or screenshots about the question here. 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - beta 8 | tags-ignore: 9 | - "**" 10 | 11 | jobs: 12 | release: 13 | if: ${{ !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]') }} 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - uses: asdf-vm/actions/install@v3 19 | 20 | - run: deno task build 21 | 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: 16 25 | 26 | - uses: cycjimmy/semantic-release-action@v4 27 | with: 28 | working_directory: npm 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 31 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "**.ts" 7 | push: 8 | branches: 9 | - "develop" 10 | paths: 11 | - "**.ts" 12 | workflow_dispatch: 13 | 14 | jobs: 15 | test: 16 | if: ${{ !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]') }} 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: asdf-vm/actions/install@v3 22 | 23 | - run: deno task build 24 | -------------------------------------------------------------------------------- /.github/workflows/update-changesets.yml: -------------------------------------------------------------------------------- 1 | name: update-changesets 2 | 3 | env: 4 | WORKFLOW_FILE: update-changesets.yml 5 | 6 | on: 7 | issue_comment: 8 | types: 9 | - created 10 | 11 | jobs: 12 | build: 13 | if: startsWith(github.event.comment.body, '/update-changesets') && github.event.issue.locked 14 | name: 🛠️ Update changesets 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write 18 | actions: read 19 | steps: 20 | - name: 🚚 Checkout (gh_pages) 21 | uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | ref: gh_pages 25 | 26 | - name: 🔍 Check other workflows 27 | run: | 28 | # Get in-progress or queued workflows. 29 | gh auth login --with-token < <(echo ${{ github.token }}) 30 | RESULT=`gh run list --workflow ${{ env.WORKFLOW_FILE }} --json status --jq '[.[] | select(.status == "in_progress")] | length == 1'` 31 | 32 | # [ERROR] Other workflows are in progress. 33 | [ "$RESULT" = "false" ] && echo "::error::Other '${{ env.WORKFLOW_FILE }}' workflows are in progress." && exit 1 || : 34 | 35 | - name: 🛠️ Update changesets file 36 | run: npx unity-changeset@latest list --all --all-lifecycles > db 37 | 38 | - name: Commit & Push changes 39 | uses: actions-js/push@master 40 | with: 41 | github_token: ${{ github.token }} 42 | amend: true 43 | force: true 44 | branch: gh_pages -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | .DS_Store 106 | /npm 107 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | deno 2.0.4 2 | nodejs 16.19.1 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense を使用して利用可能な属性を学べます。 3 | // 既存の属性の説明をホバーして表示します。 4 | // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "request": "launch", 9 | "name": "list", 10 | "type": "node", 11 | "program": "${workspaceFolder}/src/cli.ts", 12 | "cwd": "${workspaceFolder}", 13 | "env": {}, 14 | "runtimeExecutable": "/Users/takashi.sakai/.asdf/shims/deno", 15 | "runtimeArgs": [ 16 | "run", 17 | "--unstable", 18 | "--inspect-brk", 19 | "--allow-all" 20 | ], 21 | "args": [ 22 | "list" 23 | ], 24 | "attachSimplePort": 9229 25 | }, 26 | { 27 | "request": "launch", 28 | "name": "list --versions --all --all-lifecycles", 29 | "type": "node", 30 | "program": "${workspaceFolder}/src/cli.ts", 31 | "cwd": "${workspaceFolder}", 32 | "env": {}, 33 | "runtimeExecutable": "/Users/takashi.sakai/.asdf/shims/deno", 34 | "runtimeArgs": [ 35 | "run", 36 | "--unstable", 37 | "--inspect-brk", 38 | "--allow-all" 39 | ], 40 | "args": [ 41 | "list", 42 | "--versions", 43 | "--all", 44 | "--all-lifecycles" 45 | ], 46 | "attachSimplePort": 9229 47 | }, 48 | { 49 | "request": "launch", 50 | "name": "6000.0.25f1", 51 | "type": "node", 52 | "program": "${workspaceFolder}/src/cli.ts", 53 | "cwd": "${workspaceFolder}", 54 | "env": {}, 55 | "runtimeExecutable": "/Users/takashi.sakai/.asdf/shims/deno", 56 | "runtimeArgs": [ 57 | "run", 58 | "--unstable", 59 | "--inspect-brk", 60 | "--allow-all" 61 | ], 62 | "args": [ 63 | "6000.0.25f1" 64 | ], 65 | "attachSimplePort": 9229 66 | }, 67 | { 68 | "request": "launch", 69 | "name": "6000.0.25f2", 70 | "type": "node", 71 | "program": "${workspaceFolder}/src/cli.ts", 72 | "cwd": "${workspaceFolder}", 73 | "env": {}, 74 | "runtimeExecutable": "/Users/takashi.sakai/.asdf/shims/deno", 75 | "runtimeArgs": [ 76 | "run", 77 | "--unstable", 78 | "--inspect-brk", 79 | "--allow-all" 80 | ], 81 | "args": [ 82 | "6000.0.25f2" 83 | ], 84 | "attachSimplePort": 9229 85 | }, 86 | { 87 | "request": "launch", 88 | "name": "--help", 89 | "type": "node", 90 | "program": "${workspaceFolder}/src/cli.ts", 91 | "cwd": "${workspaceFolder}", 92 | "env": {}, 93 | "runtimeExecutable": "/Users/takashi.sakai/.asdf/shims/deno", 94 | "runtimeArgs": [ 95 | "run", 96 | "--unstable", 97 | "--inspect-brk", 98 | "--allow-all" 99 | ], 100 | "args": [ 101 | "--help" 102 | ], 103 | "attachSimplePort": 9229 104 | } 105 | ] 106 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true, 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 mob-sakai 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 | unity-changeset 2 | === 3 | 4 | Get/List Unity editor changeset 5 | 6 | [![](https://shields.io/badge/deno.land-unity__changeset-green?logo=deno&style=flat)](https://deno.land/x/unity_changeset) 7 | [![npm](https://img.shields.io/npm/v/unity-changeset)](https://www.npmjs.com/package/unity-changeset) 8 | ![license](https://img.shields.io/npm/l/unity-changeset) 9 | ![downloads](https://img.shields.io/npm/dy/unity-changeset) 10 | ![release](https://github.com/mob-sakai/unity-changeset/workflows/release/badge.svg) 11 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 12 | 13 |



14 | 15 | ## Usage as a node module 16 | 17 | Requirement: NodeJs 14 or later 18 | 19 | ### Install 20 | 21 | ```sh 22 | npm install unity-changeset 23 | ``` 24 | 25 | ### Import 26 | 27 | ```js 28 | // javascript 29 | const { getUnityChangeset, scrapeArchivedChangesets, scrapeBetaChangesets } = require('unity-changeset'); 30 | // or, typescript 31 | const { getUnityChangeset, scrapeArchivedChangesets, scrapeBetaChangesets } = from 'unity-changeset'; 32 | ``` 33 | 34 | ### Example 35 | 36 | ```js 37 | const { getUnityChangeset, scrapeArchivedChangesets, scrapeBetaChangesets } = require('unity-changeset'); 38 | 39 | (async () => { 40 | const changeset = await getUnityChangeset('2020.1.14f1'); 41 | console.log(changeset); 42 | //=> UnityChangeset {version: '2020.1.14f1', changeset: 'd81f64f5201d'} 43 | console.log(changeset.toString()); 44 | //=> 2020.1.14f1 d81f64f5201d 45 | const changesets = await scrapeArchivedChangesets(); 46 | console.dir(changesets); 47 | //=> [ 48 | // UnityChangeset { version: '2020.1.15f1', changeset: '97d0ae02d19d' }, 49 | // UnityChangeset { version: '2020.1.14f1', changeset: 'd81f64f5201d' }, 50 | // UnityChangeset { version: '2020.1.13f1', changeset: '5e24f28bfbc0' }, 51 | // ... 52 | // ] 53 | const betaChangesets = await scrapeBetaChangesets(); 54 | console.log(betaChangesets); 55 | //=> [ 56 | // UnityChangeset { version: '2020.2.0b13', changeset: '655e1a328b90' }, 57 | // UnityChangeset { version: '2020.2.0b12', changeset: '92852ae685d8' }, 58 | // UnityChangeset { version: '2020.2.0b11', changeset: 'c499c2bf2e80' }, 59 | // ... 60 | // ] 61 | })(); 62 | ``` 63 | 64 | 65 | ## Usage as a deno module 66 | 67 | ```js 68 | const { getUnityChangeset, scrapeArchivedChangesets, scrapeBetaChangesets } = from 'https://deno.land/x/unity_changeset/src/index.ts'; 69 | 70 | // or, specific version 71 | const { getUnityChangeset, scrapeArchivedChangesets, scrapeBetaChangesets } = from 'https://deno.land/x/unity_changeset@2.0.0/src/index.ts'; 72 | ``` 73 | 74 |



75 | 76 | ## Usage as a command-line utility 77 | 78 | ### Install 79 | 80 | ```sh 81 | # Requirement: NodeJs 14 or later 82 | npm install -g unity-changeset 83 | 84 | # Use without installation 85 | npx unity-changeset ... 86 | ``` 87 | 88 | or 89 | 90 | ``` 91 | deno install -A -f -n unity-changeset https://deno.land/x/unity_changeset/src/cli.ts 92 | ``` 93 | 94 | 95 | ### Help 96 | 97 | ``` 98 | Usage: unity-changeset 99 | 100 | Description: 101 | 102 | Find Unity changesets. 103 | 104 | Options: 105 | 106 | -h, --help - Show this help. 107 | -V, --version - Show the version number for this program. 108 | ``` 109 | 110 | ``` 111 | Usage: unity-changeset list 112 | 113 | Description: 114 | 115 | List Unity changesets. 116 | 117 | Options: 118 | 119 | -h, --help - Show this help. 120 | 121 | Search options: 122 | 123 | --all - Search all changesets (alpha/beta included) 124 | --pre-release, --beta - Search only pre-release (alpha/beta) changesets 125 | --lts - Only the LTS versions 126 | --xlts - Only the LTS/XLTS versions (require 'Enterprise' or 'Industry' license to install XLTS version) 127 | 128 | Filter options: 129 | 130 | --min - Minimum version (included) 131 | --max - Maximum version (included) 132 | --grep - Regular expression (e.g. '20(18|19).4.*') 133 | --latest-lifecycle - Only the latest lifecycle (default) 134 | --all-lifecycles - All lifecycles 135 | 136 | Group options: 137 | 138 | --latest-patch - The latest patch versions only 139 | --oldest-patch - The oldest patch versions in lateat lifecycle only (Conflicts: --latest-patch) 140 | 141 | Output options: 142 | 143 | --version-only, --versions - Outputs only the version (no changesets) 144 | --minor-version-only, --minor-versions - Outputs only the minor version (no changesets) 145 | --json - Output in json format 146 | --pretty-json - Output in pretty json format 147 | ``` 148 | 149 | 150 | ### Get a changeset for specific version: 151 | 152 | ```sh 153 | $ unity-changeset 2020.2.14f1 154 | d81f64f5201d 155 | ``` 156 | 157 | 158 | ### Get a changeset for specific version 159 | 160 | ```sh 161 | $ unity-changeset list 162 | 2020.1.14f1 d81f64f5201d 163 | 2020.1.13f1 5e24f28bfbc0 164 | 2020.1.12f1 55b56f0a86e3 165 | ... 166 | 167 | # List changesets in json format: 168 | $ unity-changeset list --json 169 | [{"version":"2020.1.15f1","changeset":"97d0ae02d19d"},{"version":"2020.1.14f1","changeset":"d81f64f5201d"},...] 170 | 171 | # List changesets in pretty json format: 172 | $ unity-changeset list --pretty-json 173 | [ 174 | { 175 | "version": "2020.1.15f1", 176 | "changeset": "97d0ae02d19d" 177 | }, 178 | { 179 | "version": "2020.1.14f1", 180 | "changeset": "d81f64f5201d" 181 | }, 182 | ... 183 | ] 184 | 185 | # List changesets (alpha/beta): 186 | $ unity-changeset list --beta 187 | 2020.2.0b13 655e1a328b90 188 | 2020.2.0b12 92852ae685d8 189 | 2020.2.0b11 c499c2bf2e80 190 | ... 191 | 192 | # List changesets (all): 193 | $ unity-changeset list --all 194 | 2020.2.0b13 655e1a328b90 195 | 2020.2.0b12 92852ae685d8 196 | ... 197 | 2020.1.14f1 d81f64f5201d 198 | 2020.1.13f1 5e24f28bfbc0 199 | ... 200 | 201 | # List the available Unity versions: 202 | $ unity-changeset list --versions 203 | 2020.1.14f1 204 | 2020.1.13f1 205 | 2020.1.12f1 206 | ... 207 | 208 | # List the available Unity versions (alpha/beta): 209 | $ unity-changeset list --beta --versions 210 | 2020.2.0b13 211 | 2020.2.0b12 212 | 2020.2.0b11 213 | ... 214 | 215 | # List Unity 2018.3 or later, and 2019.1 or earlier: 216 | $ unity-changeset list --min 2018.3 --max 2019.1 217 | 2019.1.14f1 148b5891095a 218 | ... 219 | 2018.3.1f1 bb579dc42f1d 220 | 2018.3.0f2 6e9a27477296 221 | 222 | # List all Unity 2018.3 versions: 223 | $ unity-changeset list --grep 2018.3 224 | 2018.3.14f1 d0e9f15437b1 225 | 2018.3.13f1 06548a9e9582 226 | ... 227 | 2018.3.1f1 bb579dc42f1d 228 | 2018.3.0f2 6e9a27477296 229 | 230 | # List the available Unity minor versions: 231 | $ unity-changeset list --minor-versions 232 | 2020.1 233 | ... 234 | 2017.2 235 | 2017.1 236 | 237 | # List the latest Unity patch versions: 238 | $ unity-changeset list --latest-patch 239 | 2020.1.14f1 d81f64f5201d 240 | ... 241 | 2017.2.5f1 588dc79c95ed 242 | 2017.1.5f1 9758a36cfaa6 243 | ``` 244 | 245 | ### Install a specific version of Unity via UnityHub 246 | 247 | ```sh 248 | # /path/to/unity/hub: 249 | # Windows: C:\\Program\ Files\\Unity\ Hub\\Unity\ Hub.exe 250 | # MacOS: /Applications/Unity\ Hub.app/Contents/MacOS/Unity\ Hub 251 | 252 | # Show UnityHub help: 253 | $ /path/to/unity/hub -- --headless help 254 | 255 | # Install Unity 2020.1.15f1 with modules for iOS and Android: 256 | $ /path/to/unity/hub -- --headless install \ 257 | --version 2020.1.15f1 \ 258 | --changeset `unity-changeset 2020.1.15f1` \ 259 | --module ios,android 260 | ``` 261 | 262 |



263 | 264 | ## Contributing 265 | 266 | ### Issues 267 | 268 | Issues are very valuable to this project. 269 | 270 | - Ideas are a valuable source of contributions others can make 271 | - Problems show where this project is lacking 272 | - With a question you show where contributors can improve the user experience 273 | 274 | ### Pull Requests 275 | 276 | Pull requests are, a great way to get your ideas into this repository. 277 | 278 | ### Support 279 | 280 | This is an open source project that I am developing in my spare time. 281 | If you like it, please support me. 282 | With your support, I can spend more time on development. :) 283 | 284 | [![](https://user-images.githubusercontent.com/12690315/66942881-03686280-f085-11e9-9586-fc0b6011029f.png)](https://github.com/users/mob-sakai/sponsorship) 285 | 286 |



287 | 288 | ## License 289 | 290 | * MIT 291 | 292 | ## Author 293 | 294 | * ![](https://user-images.githubusercontent.com/12690315/96986908-434a0b80-155d-11eb-8275-85138ab90afa.png) [mob-sakai](https://github.com/mob-sakai) [![](https://img.shields.io/twitter/follow/mob_sakai.svg?label=Follow&style=social)](https://twitter.com/intent/follow?screen_name=mob_sakai) ![GitHub followers](https://img.shields.io/github/followers/mob-sakai?style=social) 295 | 296 | ## See Also 297 | 298 | * GitHub page : https://github.com/mob-sakai/unity-changeset 299 | * Releases : https://github.com/mob-sakai/unity-changeset/releases 300 | * Issue tracker : https://github.com/mob-sakai/unity-changeset/issues 301 | * Change log : https://github.com/mob-sakai/unity-changeset/blob/main/CHANGELOG.md 302 | -------------------------------------------------------------------------------- /build/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | { 5 | "name": "beta", 6 | "prerelease": true 7 | } 8 | ], 9 | "tagFormat": "${version}", 10 | "plugins": [ 11 | "@semantic-release/commit-analyzer", 12 | "@semantic-release/release-notes-generator", 13 | "@semantic-release/npm", 14 | "@semantic-release/github" 15 | ] 16 | } -------------------------------------------------------------------------------- /build/build_npm.ts: -------------------------------------------------------------------------------- 1 | import { build, emptyDir } from "https://deno.land/x/dnt@0.40.0/mod.ts"; 2 | 3 | // delete previous build 4 | await emptyDir("./npm"); 5 | 6 | // build 7 | await build({ 8 | entryPoints: [ 9 | "./src/index.ts", 10 | { 11 | kind: "bin", 12 | name: "unity-changeset", 13 | path: "./src/cli.ts", 14 | }, 15 | ], 16 | outDir: "./npm", 17 | shims: { 18 | deno: true, // for Deno namespace 19 | }, 20 | // package.json properties 21 | package: { 22 | name: "unity-changeset", 23 | version: "0.0.1", 24 | description: "Get/List Unity changeset", 25 | author: "mob-sakai ", 26 | license: "MIT", 27 | repository: { 28 | type: "git", 29 | url: "git+https://github.com/mob-sakai/unity-changeset.git", 30 | }, 31 | bugs: { 32 | url: "https://github.com/mob-sakai/unity-changeset/issues", 33 | }, 34 | engines: { 35 | node: ">=14", 36 | }, 37 | }, 38 | postBuild() { 39 | Deno.copyFileSync("LICENSE", "npm/LICENSE"); 40 | Deno.copyFileSync("README.md", "npm/README.md"); 41 | Deno.copyFileSync("build/.releaserc.json", "npm/.releaserc.json"); 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "run": "deno run -A src/cli.ts", 4 | "build": "deno run -A build/build_npm.ts", 5 | "test": "deno test -A src/", 6 | "lint": "deno lint src/ && deno fmt src/" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4", 3 | "remote": { 4 | "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", 5 | "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", 6 | "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", 7 | "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", 8 | "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", 9 | "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", 10 | "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", 11 | "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", 12 | "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", 13 | "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", 14 | "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", 15 | "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", 16 | "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", 17 | "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", 18 | "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d", 19 | "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", 20 | "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", 21 | "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", 22 | "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", 23 | "https://deno.land/std@0.181.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", 24 | "https://deno.land/std@0.181.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", 25 | "https://deno.land/std@0.181.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 26 | "https://deno.land/std@0.181.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", 27 | "https://deno.land/std@0.181.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", 28 | "https://deno.land/std@0.181.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", 29 | "https://deno.land/std@0.181.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", 30 | "https://deno.land/std@0.181.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", 31 | "https://deno.land/std@0.181.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 32 | "https://deno.land/std@0.181.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 33 | "https://deno.land/std@0.181.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", 34 | "https://deno.land/std@0.181.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 35 | "https://deno.land/std@0.181.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", 36 | "https://deno.land/std@0.181.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", 37 | "https://deno.land/std@0.181.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", 38 | "https://deno.land/std@0.181.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", 39 | "https://deno.land/std@0.181.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", 40 | "https://deno.land/x/code_block_writer@12.0.0/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", 41 | "https://deno.land/x/code_block_writer@12.0.0/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", 42 | "https://deno.land/x/deno_cache@0.6.2/auth_tokens.ts": "5d1d56474c54a9d152e44d43ea17c2e6a398dd1e9682c69811a313567c01ee1e", 43 | "https://deno.land/x/deno_cache@0.6.2/cache.ts": "58b53c128b742757efcad10af9a3871f23b4e200674cb5b0ddf61164fb9b2fe7", 44 | "https://deno.land/x/deno_cache@0.6.2/deno_dir.ts": "1ea355b8ba11c630d076b222b197cfc937dd81e5a4a260938997da99e8ff93a0", 45 | "https://deno.land/x/deno_cache@0.6.2/deps.ts": "12cca94516cf2d3ed42fccd4b721ecd8060679253f077d83057511045b0081aa", 46 | "https://deno.land/x/deno_cache@0.6.2/dirs.ts": "009c6f54e0b610914d6ce9f72f6f6ccfffd2d47a79a19061e0a9eb4253836069", 47 | "https://deno.land/x/deno_cache@0.6.2/disk_cache.ts": "66a1e604a8d564b6dd0500326cac33d08b561d331036bf7272def80f2f7952aa", 48 | "https://deno.land/x/deno_cache@0.6.2/file_fetcher.ts": "4f3e4a2c78a5ca1e4812099e5083f815a8525ab20d389b560b3517f6b1161dd6", 49 | "https://deno.land/x/deno_cache@0.6.2/http_cache.ts": "407135eaf2802809ed373c230d57da7ef8dff923c4abf205410b9b99886491fd", 50 | "https://deno.land/x/deno_cache@0.6.2/lib/deno_cache_dir.generated.js": "59f8defac32e8ebf2a30f7bc77e9d88f0e60098463fb1b75e00b9791a4bbd733", 51 | "https://deno.land/x/deno_cache@0.6.2/lib/snippets/deno_cache_dir-a2aecaa9536c9402/fs.js": "cbe3a976ed63c72c7cb34ef845c27013033a3b11f9d8d3e2c4aa5dda2c0c7af6", 52 | "https://deno.land/x/deno_cache@0.6.2/mod.ts": "b4004287e1c6123d7f07fe9b5b3e94ce6d990c4102949a89c527c68b19627867", 53 | "https://deno.land/x/deno_cache@0.6.2/util.ts": "f3f5a0cfc60051f09162942fb0ee87a0e27b11a12aec4c22076e3006be4cc1e2", 54 | "https://deno.land/x/dir@1.5.1/data_local_dir/mod.ts": "91eb1c4bfadfbeda30171007bac6d85aadacd43224a5ed721bbe56bc64e9eb66", 55 | "https://deno.land/x/dnt@0.40.0/lib/compiler.ts": "7f4447531581896348b8a379ab94730856b42ae50d99043f2468328360293cb1", 56 | "https://deno.land/x/dnt@0.40.0/lib/compiler_transforms.ts": "f21aba052f5dcf0b0595c734450842855c7f572e96165d3d34f8fed2fc1f7ba1", 57 | "https://deno.land/x/dnt@0.40.0/lib/mod.deps.ts": "8d6123c8e1162037e58aa8126686a03d1e2cffb250a8757bf715f80242097597", 58 | "https://deno.land/x/dnt@0.40.0/lib/npm_ignore.ts": "57fbb7e7b935417d225eec586c6aa240288905eb095847d3f6a88e290209df4e", 59 | "https://deno.land/x/dnt@0.40.0/lib/package_json.ts": "607b0a4f44acad071a4c8533b312a27d6671eac8e6a23625c8350ce29eadb2ba", 60 | "https://deno.land/x/dnt@0.40.0/lib/pkg/dnt_wasm.generated.js": "2694546844a50861d6d1610859afbf5130baca4dc6cf304541b7ec2d6d998142", 61 | "https://deno.land/x/dnt@0.40.0/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "aba69a019a6da6f084898a6c7b903b8b583bc0dbd82bfb338449cf0b5bce58fd", 62 | "https://deno.land/x/dnt@0.40.0/lib/shims.ts": "39e5c141f0315c0faf30b479b53f92b9078d92e1fd67ee34cc60b701d8e68dab", 63 | "https://deno.land/x/dnt@0.40.0/lib/test_runner/get_test_runner_code.ts": "4dc7a73a13b027341c0688df2b29a4ef102f287c126f134c33f69f0339b46968", 64 | "https://deno.land/x/dnt@0.40.0/lib/test_runner/test_runner.ts": "4d0da0500ec427d5f390d9a8d42fb882fbeccc92c92d66b6f2e758606dbd40e6", 65 | "https://deno.land/x/dnt@0.40.0/lib/transform.deps.ts": "2e159661e1c5c650de9a573babe0e319349fe493105157307ec2ad2f6a52c94e", 66 | "https://deno.land/x/dnt@0.40.0/lib/types.ts": "b8e228b2fac44c2ae902fbb73b1689f6ab889915bd66486c8a85c0c24255f5fb", 67 | "https://deno.land/x/dnt@0.40.0/lib/utils.ts": "224f15f33e7226a2fd991e438d0291d7ed8c7889807efa2e1ecb67d2d1db6720", 68 | "https://deno.land/x/dnt@0.40.0/mod.ts": "ae1890fbe592e4797e7dd88c1e270f22b8334878e9bf187c4e11ae75746fe778", 69 | "https://deno.land/x/dnt@0.40.0/transform.ts": "f68743a14cf9bf53bfc9c81073871d69d447a7f9e3453e0447ca2fb78926bb1d", 70 | "https://deno.land/x/ts_morph@20.0.0/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", 71 | "https://deno.land/x/ts_morph@20.0.0/bootstrap/ts_morph_bootstrap.js": "6645ac03c5e6687dfa8c78109dc5df0250b811ecb3aea2d97c504c35e8401c06", 72 | "https://deno.land/x/ts_morph@20.0.0/common/DenoRuntime.ts": "6a7180f0c6e90dcf23ccffc86aa8271c20b1c4f34c570588d08a45880b7e172d", 73 | "https://deno.land/x/ts_morph@20.0.0/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed", 74 | "https://deno.land/x/ts_morph@20.0.0/common/ts_morph_common.js": "2325f94f61dc5f3f98a1dab366dc93048d11b1433d718b10cfc6ee5a1cfebe8f", 75 | "https://deno.land/x/ts_morph@20.0.0/common/typescript.js": "b9edf0a451685d13e0467a7ed4351d112b74bd1e256b915a2b941054e31c1736", 76 | "https://deno.land/x/wasmbuild@0.15.1/cache.ts": "9d01b5cb24e7f2a942bbd8d14b093751fa690a6cde8e21709ddc97667e6669ed", 77 | "https://deno.land/x/wasmbuild@0.15.1/loader.ts": "8c2fc10e21678e42f84c5135d8ab6ab7dc92424c3f05d2354896a29ccfd02a63" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | // deno-fmt-ignore-file 2 | import { Command } from "https://deno.land/x/cliffy@v0.25.7/command/command.ts"; 3 | import { resolve } from "https://deno.land/std@0.181.0/path/mod.ts"; 4 | import { 5 | getUnityChangeset, 6 | listChangesets, 7 | SearchMode, 8 | FilterOptions, 9 | GroupMode, 10 | OutputMode, 11 | FormatMode, 12 | } from "./index.ts"; 13 | 14 | function getPackageVersion(): string { 15 | try { 16 | return JSON.parse(Deno.readTextFileSync( 17 | resolve(new URL(import.meta.url).pathname, "../../package.json"), 18 | )).version; 19 | } catch { 20 | return "-"; 21 | } 22 | } 23 | 24 | new Command() 25 | /* 26 | * Main command 27 | */ 28 | .name("unity-changeset") 29 | .version(getPackageVersion) 30 | .description("Find Unity changesets.") 31 | .example("unity-changeset 2018.4.36f1", "Get changeset of Unity 2018.4.36f1 ('6cd387d23174' will be output).") 32 | .arguments("") 33 | .action((_, version) => { 34 | getUnityChangeset(version) 35 | .then((c) => console.log(c.changeset)) 36 | .catch(() => { 37 | console.error("The given version was not found."); 38 | Deno.exit(1); 39 | }); 40 | }) 41 | /* 42 | * Sub command: list. 43 | */ 44 | .command( 45 | "list", 46 | new Command() 47 | .description("List Unity changesets.") 48 | .example("unity-changeset list", "List changesets.") 49 | .example("unity-changeset list --all --json", "List changesets of all versions in json format.") 50 | .example("unity-changeset list --version-only --min 2018.3 --max 2019.4", "List all versions from 2018.3 to 2019.4.") 51 | .example("unity-changeset list --version-only --grep '(2018.4|2019.4)'", "List all versions in 2018.4 and 2019.4.") 52 | .example("unity-changeset list --lts --latest-patch", "List changesets of the latest patch versions (LTS only).") 53 | // Search options. 54 | .group("Search options") 55 | .option("--all", "Search all changesets (alpha/beta included)") 56 | .option("--pre-release, --beta", "Search only pre-release (alpha/beta) changesets", { conflicts: ["all", "lts", "xlts"] }) 57 | .option("--lts", "Only the LTS versions", { conflicts: ["all", "pre-release", "xlts", "supported"] }) 58 | .option("--xlts", "Only the LTS/XLTS versions (require 'Enterprise' or 'Industry' license to install XLTS version)", { conflicts: ["all", "pre-release", "lts", "supported"] }) 59 | .option("--supported", "Only the supported versions (including Unity 6000)", { conflicts: ["all", "pre-release", "lts", "xlts"] }) 60 | // Filter options. 61 | .group("Filter options") 62 | .option("--min ", "Minimum version (included)") 63 | .option("--max ", "Maximum version (included)") 64 | .option("--grep ", "Regular expression (e.g. '20(18|19).4.*')") 65 | .option("--latest-lifecycle", "Only the latest lifecycle (default)") 66 | .option("--all-lifecycles", "All lifecycles", { conflicts: ["latest-lifecycle"] }) 67 | // Group options. 68 | .group("Group options") 69 | .option("--latest-patch", "The latest patch versions only") 70 | .option("--oldest-patch", "The oldest patch versions in lateat lifecycle only", { conflicts: ["latest-patch"] }) 71 | // Output options. 72 | .group("Output options") 73 | .option("--version-only, --versions", "Outputs only the version (no changesets)") 74 | .option("--minor-version-only, --minor-versions", "Outputs only the minor version (no changesets)", { conflicts: ["version-only"] }) 75 | .option("--json", "Output in json format") 76 | .option("--pretty-json", "Output in pretty json format") 77 | .action((options) => { 78 | // Search mode. 79 | const searchMode = options.all 80 | ? SearchMode.All 81 | : options.preRelease 82 | ? SearchMode.PreRelease 83 | : options.lts 84 | ? SearchMode.LTS 85 | : options.xlts 86 | ? SearchMode.XLTS 87 | : options.supported 88 | ? SearchMode.SUPPORTED 89 | : SearchMode.Default; 90 | 91 | // Group mode. 92 | const groupMode = (options.latestPatch || options.minorVersionOnly) 93 | ? GroupMode.LatestPatch 94 | : options.oldestPatch 95 | ? GroupMode.OldestPatch 96 | : GroupMode.All; 97 | 98 | // Filter options. 99 | const filterOptions: FilterOptions = { 100 | min: options.min || "", 101 | max: options.max || "", 102 | grep: options.grep || "", 103 | allLifecycles: (options.allLifecycles && !options.latestLifecycle) 104 | ? true 105 | : false, 106 | lts: options.lts || false, 107 | }; 108 | 109 | // Output mode. 110 | const outputMode = options.versionOnly 111 | ? OutputMode.VersionOnly 112 | : options.minorVersionOnly 113 | ? OutputMode.MinorVersionOnly 114 | : OutputMode.Changeset; 115 | 116 | // Format mode. 117 | const formatMode = options.json 118 | ? FormatMode.Json 119 | : options.prettyJson 120 | ? FormatMode.PrettyJson 121 | : FormatMode.None; 122 | 123 | listChangesets(searchMode, filterOptions, groupMode, outputMode, formatMode) 124 | .then((result) => console.log(result)); 125 | }), 126 | ) 127 | /* 128 | * Run with arguments. 129 | */ 130 | .parse(Deno.args); 131 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { UnityChangeset as UnityChangesetClass } from "./unityChangeset.ts"; 2 | import { 3 | getUnityReleases, 4 | getUnityReleasesInLTS, 5 | UnityReleaseEntitlement, 6 | UnityReleaseStream, 7 | } from "./unityGraphQL.ts"; 8 | 9 | export const UnityChangeset = UnityChangesetClass; 10 | export type UnityChangeset = UnityChangesetClass; 11 | 12 | /* 13 | * Get an Unity changeset from specific Unity version. 14 | * @param version The Unity version. 15 | * @returns An Unity changeset. 16 | */ 17 | export async function getUnityChangeset( 18 | version: string, 19 | ): Promise { 20 | const changesets = (await getUnityReleases(version, [])).filter( 21 | (c) => c.version === version, 22 | ); 23 | if (0 < changesets.length) { 24 | return changesets[0]; 25 | } 26 | 27 | throw Error(`The given version '${version}' was not found.`); 28 | } 29 | 30 | /* 31 | * Search mode. 32 | * 33 | * All: All changesets. 34 | * Default: Only non pre-release changesets. 35 | * PreRelease: Only pre-release (alpha/beta) changesets. 36 | */ 37 | export enum SearchMode { 38 | All = 0, 39 | Default = 2, 40 | PreRelease = 3, 41 | LTS = 4, 42 | XLTS = 5, 43 | SUPPORTED = 6, 44 | } 45 | 46 | /* 47 | * Group mode. 48 | * 49 | * All: All the changesets. 50 | * OldestPatch: Only the oldest patch changesets. 51 | * LatestPatch: Only the latest patch changesets. 52 | * LatestLifecycle: Only the latest lifecycle changesets. 53 | */ 54 | export enum GroupMode { 55 | All = "all", 56 | OldestPatch = "oldest-patch", 57 | LatestPatch = "latest-patch", 58 | LatestLifecycle = "latest-lifecycle", 59 | } 60 | 61 | /* 62 | * Filter options. 63 | * 64 | * min: The minimum version. eg. 2018.4 65 | * max: The maximum version. eg. 2019.4 66 | * grep: The grep pattern. eg. 20(18|19) 67 | * allLifecycles: Include all the lifecycles. 68 | * lts: Include only the LTS versions. 69 | */ 70 | export interface FilterOptions { 71 | min: string; 72 | max: string; 73 | grep: string; 74 | allLifecycles: boolean; 75 | lts: boolean; 76 | } 77 | 78 | /* 79 | * Output mode. 80 | * 81 | * Changeset: The changeset. 82 | * VersionOnly: Only the version. 83 | * MinorVersionOnly: Only the minor version. 84 | */ 85 | export enum OutputMode { 86 | Changeset = "changeset", 87 | VersionOnly = "version", 88 | MinorVersionOnly = "minor-version", 89 | } 90 | 91 | /* 92 | * Format mode. 93 | * 94 | * None: No format. 95 | * Json: JSON format. 96 | * PrettyJson: Pretty JSON format. 97 | */ 98 | export enum FormatMode { 99 | None = "none", 100 | Json = "json", 101 | PrettyJson = "pretty-json", 102 | } 103 | 104 | export function listChangesets( 105 | searchMode: SearchMode, 106 | filterOptions: FilterOptions, 107 | groupMode: GroupMode, 108 | outputMode: OutputMode, 109 | formatMode: FormatMode, 110 | ): Promise { 111 | return searchChangesets(searchMode) 112 | .then((results) => filterChangesets(results, filterOptions)) 113 | .then((results) => 114 | results.sort((a, b) => b.versionNumber - a.versionNumber) 115 | ) 116 | .then((results) => groupChangesets(results, groupMode)) 117 | .then((results) => { 118 | switch (outputMode) { 119 | case OutputMode.Changeset: 120 | return results; 121 | case OutputMode.VersionOnly: 122 | return results.map((c) => c.version); 123 | case OutputMode.MinorVersionOnly: 124 | return results.map((c) => c.minor); 125 | default: 126 | throw Error( 127 | `The given output mode '${outputMode}' was not supported`, 128 | ); 129 | } 130 | }) 131 | .then((results) => { 132 | switch (formatMode) { 133 | case FormatMode.None: 134 | return results.join("\n"); 135 | case FormatMode.Json: 136 | return JSON.stringify(results); 137 | case FormatMode.PrettyJson: 138 | return JSON.stringify(results, null, 2); 139 | default: 140 | throw Error( 141 | `The given format mode '${formatMode}' was not supported`, 142 | ); 143 | } 144 | }); 145 | } 146 | 147 | export function searchChangesets( 148 | searchMode: SearchMode, 149 | ): Promise { 150 | switch (searchMode) { 151 | case SearchMode.All: 152 | return getUnityReleases(".", [ 153 | UnityReleaseStream.LTS, 154 | UnityReleaseStream.SUPPORTED, 155 | UnityReleaseStream.TECH, 156 | UnityReleaseStream.BETA, 157 | UnityReleaseStream.ALPHA, 158 | ]); 159 | case SearchMode.Default: 160 | return getUnityReleases(".", [ 161 | UnityReleaseStream.LTS, 162 | UnityReleaseStream.SUPPORTED, 163 | UnityReleaseStream.TECH, 164 | ]); 165 | case SearchMode.PreRelease: 166 | return getUnityReleases(".", [ 167 | UnityReleaseStream.BETA, 168 | UnityReleaseStream.ALPHA, 169 | ]); 170 | case SearchMode.LTS: 171 | return getUnityReleasesInLTS(); 172 | case SearchMode.XLTS: 173 | return getUnityReleasesInLTS([UnityReleaseEntitlement.XLTS]); 174 | case SearchMode.SUPPORTED: 175 | return getUnityReleases(".", [UnityReleaseStream.SUPPORTED]); 176 | default: 177 | throw Error(`The given search mode '${searchMode}' was not supported`); 178 | } 179 | } 180 | 181 | export function filterChangesets( 182 | changesets: UnityChangeset[], 183 | options: FilterOptions, 184 | ): UnityChangeset[] { 185 | if (!changesets || changesets.length == 0) return []; 186 | 187 | // Min version number 188 | const min = options.min 189 | ? UnityChangeset.toNumber(options.min, false) 190 | : Number.MIN_VALUE; 191 | // Max version number 192 | const max = options.max 193 | ? UnityChangeset.toNumber(options.max, true) 194 | : Number.MAX_VALUE; 195 | // Grep pattern 196 | const regex = options.grep ? new RegExp(options.grep, "i") : null; 197 | // Lifecycle filter 198 | const lc = options.allLifecycles 199 | ? null 200 | : Object.values(groupBy(changesets, (r) => r.minor)).map((g) => g[0]); 201 | 202 | return changesets.filter( 203 | (c) => 204 | min <= c.versionNumber && 205 | c.versionNumber <= max && 206 | (!options.lts || c.lts) && 207 | (!regex || regex.test(c.version)) && 208 | (!lc || lc.some((l) => l.minor == c.minor && l.lifecycle == c.lifecycle)), 209 | ); 210 | } 211 | 212 | export function groupChangesets( 213 | changesets: UnityChangeset[], 214 | groupMode: GroupMode, 215 | ): UnityChangeset[] { 216 | if (!changesets || changesets.length == 0) return []; 217 | 218 | switch (groupMode) { 219 | case GroupMode.All: 220 | return changesets; 221 | case GroupMode.LatestLifecycle: 222 | return Object.values(groupBy(changesets, (r) => r.minor)) 223 | .map((g) => g.filter((v) => v.lifecycle == g[0].lifecycle)) 224 | .flat(); 225 | case GroupMode.LatestPatch: 226 | return Object.values(groupBy(changesets, (r) => r.minor)).map( 227 | (g) => g[0], 228 | ); 229 | case GroupMode.OldestPatch: 230 | return Object.values(groupBy(changesets, (r) => r.minor)) 231 | .map((g) => g.filter((v) => v.lifecycle == g[0].lifecycle)) 232 | .map((g) => g[g.length - 1]); 233 | default: 234 | throw Error(`The given group mode '${groupMode}' was not supported`); 235 | } 236 | } 237 | 238 | const groupBy = (arr: T[], key: (i: T) => K) => 239 | arr.reduce((groups, item) => { 240 | (groups[key(item)] ||= []).push(item); 241 | return groups; 242 | }, {} as Record); 243 | -------------------------------------------------------------------------------- /src/unityChangeset.test.ts: -------------------------------------------------------------------------------- 1 | // deno-fmt-ignore-file 2 | import { 3 | assertEquals, 4 | assertNotEquals, 5 | assertRejects, 6 | } from "https://deno.land/std@0.181.0/testing/asserts.ts"; 7 | import { 8 | filterChangesets, 9 | getUnityChangeset, 10 | groupChangesets, 11 | searchChangesets, 12 | GroupMode, 13 | SearchMode, 14 | UnityChangeset, 15 | } from "./index.ts"; 16 | 17 | Deno.test("UnityChangeset.toNumber min", () => { 18 | assertEquals(UnityChangeset.toNumber("2018.3", false), 201803000000); 19 | }); 20 | 21 | Deno.test("UnityChangeset.toNumber max", () => { 22 | assertEquals(UnityChangeset.toNumber("2018.3", true), 201803992599); 23 | }); 24 | 25 | // getUnityChangeset 26 | [ 27 | { version: "2018.3.0f1", expected: "f023c421e164" }, 28 | { version: "2018.3.0f2", expected: "6e9a27477296" }, 29 | { version: "2018.3.0f3", expected: undefined }, 30 | { version: "2019.1.0a9", expected: "0acd256790e8" }, 31 | { version: "2019.1.0b1", expected: "83b3ba1f99df" }, 32 | { version: "6000.1.0f1", expected: "9ea152932a88" }, 33 | ].forEach((testcase) => { 34 | Deno.test(`getUnityChangeset (${testcase.version})`, async () => { 35 | if (testcase.expected) { 36 | const changeset = (await getUnityChangeset(testcase.version)).changeset; 37 | assertEquals(changeset, testcase.expected); 38 | } 39 | else { 40 | await assertRejects(() => getUnityChangeset(testcase.version)); 41 | } 42 | }) 43 | }); 44 | 45 | Deno.test("scrapeArchivedChangesets", async () => { 46 | const changesets = await searchChangesets(SearchMode.Default); 47 | assertNotEquals(changesets.length, 0); 48 | }); 49 | 50 | Deno.test("scrapeBetaChangesets", async () => { 51 | const changesets = await searchChangesets(SearchMode.PreRelease); 52 | console.log(changesets.map((c) => c.version)); 53 | assertNotEquals(changesets.length, 0); 54 | }); 55 | 56 | // At least one changeset from unity 6000 version should be found. 57 | Deno.test("scrapeUnity6000Supported", async () => { 58 | const changesets = await searchChangesets(SearchMode.SUPPORTED); 59 | console.log(changesets.map((c) => c.version)); 60 | assertNotEquals(changesets.length, 0); 61 | 62 | const unity6000 = changesets.find(c => c.version.startsWith("6000")); 63 | assertNotEquals(unity6000, undefined); 64 | }); 65 | 66 | // searchChangesets 67 | [ 68 | { searchMode: SearchMode.All }, 69 | { searchMode: SearchMode.Default }, 70 | { searchMode: SearchMode.PreRelease }, 71 | { searchMode: SearchMode.SUPPORTED }, 72 | ].forEach((testcase) => { 73 | Deno.test(`filterChangesets(${JSON.stringify(testcase.searchMode)})`, async () => { 74 | const changesets = await searchChangesets(testcase.searchMode); 75 | assertNotEquals(changesets.length, 0); 76 | }); 77 | }); 78 | 79 | const changesetsForTest = [ 80 | new UnityChangeset("2018.2.0f1", "000000000000"), 81 | new UnityChangeset("2018.2.1f1", "000000000000"), 82 | new UnityChangeset("2018.2.2f1", "000000000000"), 83 | new UnityChangeset("2018.3.0f1", "000000000000"), 84 | new UnityChangeset("2018.3.1f1", "000000000000"), 85 | new UnityChangeset("2018.3.2f1", "000000000000"), 86 | new UnityChangeset("2018.4.0f1", "000000000000", true), 87 | new UnityChangeset("2018.4.1f1", "000000000000", true), 88 | new UnityChangeset("2018.4.2f1", "000000000000", true), 89 | new UnityChangeset("2019.1.0a1", "000000000000"), 90 | new UnityChangeset("2019.1.0a2", "000000000000"), 91 | new UnityChangeset("2019.1.0b1", "000000000000"), 92 | new UnityChangeset("2019.1.0b2", "000000000000"), 93 | new UnityChangeset("2019.1.0f1", "000000000000"), 94 | new UnityChangeset("2019.1.0f2", "000000000000"), 95 | new UnityChangeset("2019.1.1f1", "000000000000"), 96 | new UnityChangeset("2019.2.0a1", "000000000000"), 97 | new UnityChangeset("2019.2.0a2", "000000000000"), 98 | new UnityChangeset("2019.2.0b1", "000000000000"), 99 | new UnityChangeset("2019.2.0b2", "000000000000"), 100 | new UnityChangeset("2019.2.0a1", "000000000000"), 101 | new UnityChangeset("2019.2.0a2", "000000000000"), 102 | ].sort((a, b) => b.versionNumber - a.versionNumber); 103 | 104 | // filterChangesets 105 | [ 106 | { options: { min: "2018.3", max: "2018.4", grep: "", allLifecycles: false, lts: false, }, expected: 6, }, 107 | { options: { min: "2018.3", max: "", grep: "2018", allLifecycles: false, lts: false, }, expected: 6, }, 108 | { options: { min: "", max: "", grep: "", allLifecycles: false, lts: true }, expected: 3, }, 109 | { options: { min: "2019", max: "", grep: "", allLifecycles: true, lts: false, }, expected: 13, }, 110 | { options: { min: "2019", max: "", grep: "b", allLifecycles: true, lts: false, }, expected: 4, }, 111 | ].forEach((testcase) => { 112 | Deno.test(`filterChangesets(${JSON.stringify(testcase.options)})`, () => { 113 | const changesets = filterChangesets(changesetsForTest, testcase.options); 114 | console.log(changesets.map((c) => `${c.version}`)); 115 | assertEquals(changesets.length, testcase.expected); 116 | }); 117 | }); 118 | 119 | // groupChangesets 120 | [ 121 | { groupMode: GroupMode.All, expected: 22 }, 122 | { groupMode: GroupMode.LatestLifecycle, expected: 14 }, 123 | { groupMode: GroupMode.LatestPatch, expected: 5 }, 124 | { groupMode: GroupMode.OldestPatch, expected: 5 }, 125 | ].forEach((testcase) => { 126 | Deno.test(`groupChangesets(${testcase.groupMode})`, () => { 127 | const changesets = groupChangesets(changesetsForTest, testcase.groupMode); 128 | console.log(changesets.map((c) => `${c.version}`)); 129 | assertEquals(changesets.length, testcase.expected); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /src/unityChangeset.ts: -------------------------------------------------------------------------------- 1 | const REGEXP_UNITY = /^(\d+)\.(\d+)\.(\d+)([a-zA-Z]+)(\d+)/; 2 | const REGEXP_UNITY_NUM = /^(\d+)\.?(\d+)?\.?(\d+)?([a-zA-Z]+)?(\d+)?/; 3 | 4 | /* 5 | Unity Changeset 6 | */ 7 | export class UnityChangeset { 8 | version = ""; 9 | changeset = ""; 10 | versionNumber = 0; 11 | minor = ""; 12 | lifecycle = ""; 13 | lts = false; 14 | 15 | constructor(version: string, changeset: string, lts: boolean = false) { 16 | Object.assign(this, { version, changeset, lts }); 17 | 18 | const match = this.version.match(REGEXP_UNITY); 19 | if (match) { 20 | this.versionNumber = UnityChangeset.toNumber(this.version, false); 21 | this.minor = `${match[1]}.${match[2]}`; 22 | this.lifecycle = `${match[4]}`; 23 | } 24 | } 25 | 26 | toString = (): string => { 27 | return `${this.version}\t${this.changeset}`; 28 | }; 29 | 30 | static toNumber = (version: string, max: boolean): number => { 31 | const match = version.toString().match(REGEXP_UNITY_NUM); 32 | if (match === null) return 0; 33 | 34 | return parseInt(match[1] || (max ? "9999" : "0")) * 100 * 100 * 100 * 100 + 35 | parseInt(match[2] || (max ? "99" : "0")) * 100 * 100 * 100 + 36 | parseInt(match[3] || (max ? "99" : "0")) * 100 * 100 + 37 | ((match[4] || (max ? "z" : "a")).toUpperCase().charCodeAt(0) - 65) * 100 + 38 | parseInt(match[5] || (max ? "99" : "0")); 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/unityGraphQL.ts: -------------------------------------------------------------------------------- 1 | import { UnityChangeset } from "./unityChangeset.ts"; 2 | import { gql, GraphQLClient } from "npm:graphql-request@6.1.0"; 3 | 4 | const UNITY_GRAPHQL_ENDPOINT: string = "https://services.unity.com/graphql"; 5 | 6 | export enum UnityReleaseStream { 7 | SUPPORTED = "SUPPORTED", 8 | LTS = "LTS", 9 | TECH = "TECH", 10 | BETA = "BETA", 11 | ALPHA = "ALPHA", 12 | } 13 | 14 | export enum UnityReleaseEntitlement { 15 | XLTS = "XLTS", 16 | U7_ALPHA = "U7_ALPHA", 17 | } 18 | 19 | interface UnityReleasesResponse { 20 | getUnityReleases: { 21 | totalCount: number; 22 | edges: { node: { version: string; shortRevision: string; stream: UnityReleaseStream } }[]; 23 | pageInfo: { hasNextPage: boolean }; 24 | }; 25 | } 26 | 27 | interface UnityReleasesMajorVersionsResponse { 28 | getUnityReleaseMajorVersions: { version: string; }[]; 29 | } 30 | 31 | export async function getUnityReleases( 32 | version: string, 33 | stream: UnityReleaseStream[] = [], 34 | entitlements: UnityReleaseEntitlement[] = [] 35 | ): Promise { 36 | const client = new GraphQLClient(UNITY_GRAPHQL_ENDPOINT); 37 | const query = gql` 38 | query GetRelease($limit: Int, $skip: Int, $version: String!, $stream: [UnityReleaseStream!], $entitlements: [UnityReleaseEntitlement!]) 39 | { 40 | getUnityReleases( 41 | limit: $limit 42 | skip: $skip 43 | stream: $stream 44 | version: $version 45 | entitlements: $entitlements 46 | ) { 47 | totalCount 48 | edges { 49 | node { 50 | version 51 | shortRevision 52 | stream 53 | } 54 | } 55 | pageInfo { 56 | hasNextPage 57 | } 58 | } 59 | } 60 | `; 61 | 62 | const variables = { 63 | limit: 250, 64 | skip: 0, 65 | version: version, 66 | stream: stream, 67 | entitlements: entitlements, 68 | }; 69 | 70 | const results: UnityChangeset[] = []; 71 | while (true) { 72 | const data: UnityReleasesResponse = await client.request(query, variables); 73 | results.push( 74 | ...data.getUnityReleases.edges.map((edge) => 75 | new UnityChangeset(edge.node.version, edge.node.shortRevision, edge.node.stream == UnityReleaseStream.LTS) 76 | ), 77 | ); 78 | if (data.getUnityReleases.pageInfo.hasNextPage === false) { 79 | break; 80 | } 81 | 82 | variables.skip += variables.limit; 83 | } 84 | 85 | return results; 86 | } 87 | 88 | export async function getUnityReleasesInLTS( 89 | entitlements: UnityReleaseEntitlement[] = [], 90 | ): Promise { 91 | const client = new GraphQLClient(UNITY_GRAPHQL_ENDPOINT); 92 | const query = gql` 93 | query GetReleaseMajorVersions($entitlements: [UnityReleaseEntitlement!]) 94 | { 95 | getUnityReleaseMajorVersions( 96 | stream: [] 97 | platform: [] 98 | architecture: [] 99 | entitlements: $entitlements 100 | ) { 101 | version 102 | } 103 | } 104 | `; 105 | 106 | const variables = { 107 | entitlements: entitlements, 108 | }; 109 | 110 | const data: UnityReleasesMajorVersionsResponse = await client.request(query, variables); 111 | const results = await Promise.all(data.getUnityReleaseMajorVersions 112 | .map(async (v) => { 113 | return await getUnityReleases(v.version, [UnityReleaseStream.LTS], entitlements); 114 | })); 115 | 116 | return results.flat(); 117 | } 118 | --------------------------------------------------------------------------------