mirror of
https://github.com/chickensoft-games/setup-godot.git
synced 2025-08-14 12:55:08 +00:00
feat: initial commit
This commit is contained in:
commit
e128ba100d
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@ -0,0 +1,4 @@
|
||||
dist/
|
||||
lib/
|
||||
node_modules/
|
||||
jest.config.js
|
85
.eslintrc.json
Normal file
85
.eslintrc.json
Normal file
@ -0,0 +1,85 @@
|
||||
{
|
||||
"plugins": [
|
||||
"jest",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:github/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 9,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"i18n-text/no-en": "off",
|
||||
"eslint-comments/no-use": "off",
|
||||
"import/no-namespace": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{
|
||||
"accessibility": "no-public"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-require-imports": "error",
|
||||
"@typescript-eslint/array-type": "error",
|
||||
"@typescript-eslint/await-thenable": "error",
|
||||
"@typescript-eslint/ban-ts-comment": "error",
|
||||
"camelcase": "off",
|
||||
"@typescript-eslint/consistent-type-assertions": "error",
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"error",
|
||||
{
|
||||
"allowExpressions": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/func-call-spacing": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"@typescript-eslint/no-array-constructor": "error",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/no-extraneous-class": "error",
|
||||
"@typescript-eslint/no-for-in-array": "error",
|
||||
"@typescript-eslint/no-inferrable-types": "error",
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/no-useless-constructor": "error",
|
||||
"@typescript-eslint/no-var-requires": "error",
|
||||
"@typescript-eslint/prefer-for-of": "warn",
|
||||
"@typescript-eslint/prefer-function-type": "warn",
|
||||
"@typescript-eslint/prefer-includes": "error",
|
||||
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
||||
"@typescript-eslint/promise-function-async": "error",
|
||||
"@typescript-eslint/require-array-sort-compare": "error",
|
||||
"@typescript-eslint/restrict-plus-operands": "error",
|
||||
"semi": "off",
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unbound-method": "error"
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true,
|
||||
"jest/globals": true
|
||||
},
|
||||
"globals": {
|
||||
"NodeJS": true
|
||||
}
|
||||
}
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist/** -diff linguist-generated=true
|
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
53
.github/workflows/check-dist.yml
vendored
Normal file
53
.github/workflows/check-dist.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# `dist/index.js` is a special file in Actions.
|
||||
# When you reference an action with `uses:` in a workflow,
|
||||
# `index.js` is the code that will run.
|
||||
# For our project, we generate this file through a build process from other source files.
|
||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||
name: Check dist/
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-dist:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set Node.js 16.x
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Rebuild the dist/ directory
|
||||
run: |
|
||||
npm run build
|
||||
npm run package
|
||||
|
||||
- name: Compare the expected and actual dist/ directories
|
||||
run: |
|
||||
if [ "$(git diff --ignore-all-space --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||
echo "Detected uncommitted changes after build. See status below:"
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
id: diff
|
||||
|
||||
# If index.js was different than expected, upload the expected version as an artifact
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
45
.github/workflows/test.yml
vendored
Normal file
45
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: 'build-test'
|
||||
on: # rebuild any PRs and main branch changes
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: 🧪 Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
# Don't cancel other OS runners if one fails.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Put the operating systems you want to run on here.
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
DOTNET_NOLOGO: true
|
||||
defaults:
|
||||
run:
|
||||
# Use bash shells on all platforms.
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
name: 🧾 Checkout
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
name: 💽 Setup .NET SDK
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- uses: ./
|
||||
name: 🤖 Setup Godot
|
||||
with:
|
||||
# Version must include major, minor, and patch, and be >= 4.0.0
|
||||
# Pre-release label is optional.
|
||||
version: 4.0.0-beta16
|
||||
|
||||
- name: 🔬 Verify Setup
|
||||
run: |
|
||||
dotnet --version
|
||||
godot --version
|
99
.gitignore
vendored
Normal file
99
.gitignore
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
# Dependency directory
|
||||
node_modules
|
||||
|
||||
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# OS metadata
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Ignore built ts files
|
||||
__tests__/runner/*
|
||||
lib/**/*
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
dist/
|
||||
lib/
|
||||
node_modules/
|
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid"
|
||||
}
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Chickensoft Games and contributors
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
70
README.md
Normal file
70
README.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Setup Godot
|
||||
|
||||
Setup Godot for headless use with macOS, Windows, and Linux CI/CD runners.
|
||||
|
||||
- ✅ Godot 4 Only.
|
||||
- ✅ Setup and run Godot on the OS you are developing for.
|
||||
- ✅ Caches Godot 4 installation for speedier workflows.
|
||||
- ✅ Adds environment variables (`GODOT4`, `GODOT`) to the system path.
|
||||
- ✅ Installs Godot on the runner — do whatever you want with it afterwards!
|
||||
|
||||
> **Godot 3.x and below are not supported.**
|
||||
|
||||
## Usage
|
||||
|
||||
Example workflow:
|
||||
|
||||
```yaml
|
||||
name: 🚥 Status Checks
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: 👀 Evaluate on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
# Don't cancel other OS runners if one fails.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Put the operating systems you want to run on here.
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
DOTNET_NOLOGO: true
|
||||
defaults:
|
||||
run:
|
||||
# Use bash shells on all platforms.
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
name: 🧾 Checkout
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
name: 💽 Setup .NET SDK
|
||||
with:
|
||||
# Use the .NET SDK from global.json in the root of the repository.
|
||||
global-json-file: global.json
|
||||
|
||||
- name: 📦 Restore Dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- uses: chickensoft-games/setup-godot
|
||||
name: 🤖 Setup Godot
|
||||
with:
|
||||
# Version must include major, minor, and patch, and be >= 4.0.0
|
||||
# Pre-release label is optional.
|
||||
version: 4.0.0-beta16
|
||||
|
||||
- name: 🔬 Verify Setup
|
||||
run: |
|
||||
dotnet --version
|
||||
godot --version
|
||||
|
||||
- name: 🧑🔬 Generate .NET Bindings
|
||||
run: godot --headless --build-solutions --quit || exit 0
|
||||
|
||||
- name: 🦺 Build Projects
|
||||
run: dotnet build
|
||||
|
||||
# Do whatever you want!
|
||||
```
|
75
__tests__/main.test.ts
Normal file
75
__tests__/main.test.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import {describe, expect, test} from '@jest/globals'
|
||||
|
||||
import {getGodotUrl, getPlatform, parseVersion} from '../src/utils'
|
||||
|
||||
describe('parseVersion', () => {
|
||||
test('parses valid godot versions', () => {
|
||||
expect(parseVersion('3.5.2')).toEqual({
|
||||
major: '3',
|
||||
minor: '5',
|
||||
patch: '2',
|
||||
label: ''
|
||||
})
|
||||
|
||||
expect(parseVersion('4.0.0-beta1')).toEqual({
|
||||
major: '4',
|
||||
minor: '0',
|
||||
patch: '0',
|
||||
label: 'beta1'
|
||||
})
|
||||
|
||||
expect(parseVersion('4.0.0-beta.16')).toEqual({
|
||||
major: '4',
|
||||
minor: '0',
|
||||
patch: '0',
|
||||
label: 'beta.16'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getGodotUrl', () => {
|
||||
test('4.0.0-beta1', () => {
|
||||
expect(getGodotUrl('4.0.0-beta1', getPlatform('linux'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta1/mono/Godot_v4.0-beta1_mono_linux_x86_64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0-beta1', getPlatform('win32'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta1/mono/Godot_v4.0-beta1_mono_win64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0-beta1', getPlatform('darwin'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta1/mono/Godot_v4.0-beta1_mono_macos.universal.zip'
|
||||
)
|
||||
})
|
||||
test('4.0.0-beta.16', () => {
|
||||
expect(getGodotUrl('4.0.0-beta.16', getPlatform('linux'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta16/mono/Godot_v4.0-beta16_mono_linux_x86_64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0-beta.16', getPlatform('win32'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta16/mono/Godot_v4.0-beta16_mono_win64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0-beta.16', getPlatform('darwin'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta16/mono/Godot_v4.0-beta16_mono_macos.universal.zip'
|
||||
)
|
||||
})
|
||||
test('4.0.0-beta8', () => {
|
||||
expect(getGodotUrl('4.0.0-beta8', getPlatform('linux'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta8/mono/Godot_v4.0-beta8_mono_linux_x86_64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0-beta8', getPlatform('win32'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta8/mono/Godot_v4.0-beta8_mono_win64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0-beta8', getPlatform('darwin'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/beta8/mono/Godot_v4.0-beta8_mono_macos.universal.zip'
|
||||
)
|
||||
})
|
||||
test('4.0.0', () => {
|
||||
expect(getGodotUrl('4.0.0', getPlatform('linux'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/mono/Godot_v4.0_mono_linux_x86_64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0', getPlatform('win32'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/mono/Godot_v4.0_mono_win64.zip'
|
||||
)
|
||||
expect(getGodotUrl('4.0.0', getPlatform('darwin'))).toEqual(
|
||||
'https://downloads.tuxfamily.org/godotengine/4.0/mono/Godot_v4.0_mono_macos.universal.zip'
|
||||
)
|
||||
})
|
||||
})
|
38
action.yml
Normal file
38
action.yml
Normal file
@ -0,0 +1,38 @@
|
||||
name: 'Setup Godot Action'
|
||||
description: >-
|
||||
Setup Godot for headless use with macOS, Windows, and Linux CI/CD runners.
|
||||
author: 'Chickensoft'
|
||||
branding:
|
||||
icon: 'hard-drive'
|
||||
color: 'white'
|
||||
inputs:
|
||||
version:
|
||||
description: >-
|
||||
Godot 4 version: e.g., 4.0.0-beta1, 4.0.0-beta.16, 4.0.0, etc. Must
|
||||
include major, minor, and patch (additional pre-release label is
|
||||
optional).
|
||||
required: true
|
||||
path:
|
||||
description: >-
|
||||
Path to install Godot to, relative to the current working directory of
|
||||
the action.
|
||||
default: 'godot'
|
||||
downloads-path:
|
||||
description: >-
|
||||
Path to download Godot to, relative to the current working directory of
|
||||
the action.
|
||||
default: 'downloads'
|
||||
bin-path:
|
||||
description: >-
|
||||
Path for binaries to be installed to, relative to the current working
|
||||
directory of the action. This is the path that will be added to the
|
||||
system path.
|
||||
default: 'bin'
|
||||
godot-sharp-release:
|
||||
description: >-
|
||||
Whether to use the release or debug version of GodotSharp.dll. The
|
||||
appropriate version will be symlinked in bin-path.
|
||||
default: 'false'
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
7
cspell.json
Normal file
7
cspell.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"words": [
|
||||
"Chickensoft",
|
||||
"NOLOGO",
|
||||
"OPTOUT"
|
||||
]
|
||||
}
|
64321
dist/index.js
generated
vendored
Normal file
64321
dist/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/index.js.map
generated
vendored
Normal file
1
dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1164
dist/licenses.txt
generated
vendored
Normal file
1164
dist/licenses.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/sourcemap-register.js
generated
vendored
Normal file
1
dist/sourcemap-register.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9
jest.config.js
Normal file
9
jest.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
testMatch: ['**/*.test.ts'],
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true
|
||||
}
|
11835
package-lock.json
generated
Normal file
11835
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
47
package.json
Normal file
47
package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "setup-godot",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "Setup Godot for headless use with macOS, Windows, and Linux runners.",
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"format": "prettier --write '**/*.ts'",
|
||||
"format-check": "prettier --check '**/*.ts'",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"package": "ncc build --source-map --license licenses.txt",
|
||||
"package-local": "export NODE_OPTIONS=--openssl-legacy-provider; ncc build --source-map --license licenses.txt",
|
||||
"test": "jest",
|
||||
"all": "npm run build && npm run format && npm run lint && npm run package && npm test",
|
||||
"all-local": "npm run build && npm run format && npm run lint && npm run package-local && npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/chickensoft-games/setup-godot.git"
|
||||
},
|
||||
"keywords": [
|
||||
"actions",
|
||||
"node",
|
||||
"setup"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^3.1.2",
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/tool-cache": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@typescript-eslint/parser": "^5.49.0",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-plugin-github": "^4.6.0",
|
||||
"eslint-plugin-jest": "^27.2.1",
|
||||
"jest": "^29.4.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prettier": "^2.8.3",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
187
src/main.ts
Normal file
187
src/main.ts
Normal file
@ -0,0 +1,187 @@
|
||||
import * as cache from '@actions/cache'
|
||||
import * as core from '@actions/core'
|
||||
import * as toolsCache from '@actions/tool-cache'
|
||||
import * as fs from 'fs'
|
||||
import * as os from 'os'
|
||||
import path from 'path'
|
||||
|
||||
import {
|
||||
findExecutablesRecursively,
|
||||
getGodotFilenameFromVersionString,
|
||||
getGodotUrl,
|
||||
getPlatform,
|
||||
Platform
|
||||
} from './utils'
|
||||
|
||||
async function run(platform: Platform | undefined = undefined): Promise<void> {
|
||||
platform = platform ?? getPlatform(process.platform)
|
||||
|
||||
// Get action inputs
|
||||
const pathRelative = core.getInput('path').replace(/\s/g, '')
|
||||
const downloadsRelativePath = core
|
||||
.getInput('downloads-path')
|
||||
.replace(/\s/g, '')
|
||||
const version = core.getInput('version').replace(/\s/g, '')
|
||||
const binRelativePath = core.getInput('bin-path').replace(/\s/g, '')
|
||||
const godotSharpRelease = core.getBooleanInput('godot-sharp-release')
|
||||
|
||||
// Compute derived information
|
||||
const userDir = os.homedir()
|
||||
const downloadsDir = path.join(userDir, downloadsRelativePath)
|
||||
const installationDir = path.join(userDir, pathRelative)
|
||||
const versionName = getGodotFilenameFromVersionString(version, platform)
|
||||
const godotUrl = getGodotUrl(version, platform)
|
||||
const godotDownloadPath = path.join(downloadsDir, `${versionName}.zip`)
|
||||
const godotInstallationPath = platform.getUnzippedPath(
|
||||
installationDir,
|
||||
versionName
|
||||
)
|
||||
const binDir = path.join(userDir, binRelativePath)
|
||||
|
||||
// Log values
|
||||
core.startGroup('🤖 Godot Action Inputs')
|
||||
core.info(`🤖 Godot version: ${version}`)
|
||||
core.info(`🤖 Godot version name: ${versionName}`)
|
||||
core.info(`🤖 Godot download url: ${godotUrl}`)
|
||||
core.info(`🧑💼 User directory: ${userDir}`)
|
||||
core.info(`🌏 Downloads directory: ${downloadsDir}`)
|
||||
core.info(`📥 Godot download path: ${godotDownloadPath}`)
|
||||
core.info(`📦 Godot installation directory: ${installationDir}`)
|
||||
core.info(`🤖 Godot installation path: ${godotInstallationPath}`)
|
||||
core.info(`📂 Bin directory: ${binDir}`)
|
||||
core.info(`🤖 GodotSharp release: ${godotSharpRelease}`)
|
||||
core.endGroup()
|
||||
|
||||
try {
|
||||
// Ensure paths we are using exist.
|
||||
core.startGroup(`📂 Ensuring working directories exist...`)
|
||||
fs.mkdirSync(downloadsDir, {recursive: true})
|
||||
fs.mkdirSync(installationDir, {recursive: true})
|
||||
fs.mkdirSync(binDir, {recursive: true})
|
||||
core.info(`✅ Working directories exist`)
|
||||
core.endGroup()
|
||||
|
||||
// See if Godot is already installed.
|
||||
core.startGroup(`🤔 Checking if Godot is already in cache...`)
|
||||
const cached = await cache.restoreCache([godotInstallationPath], godotUrl)
|
||||
|
||||
let executables: string[]
|
||||
if (!cached) {
|
||||
// Download Godot
|
||||
core.info(`🙃 Previous Godot download not found in cache`)
|
||||
core.endGroup()
|
||||
|
||||
core.startGroup(`📥 Downloading Godot to ${godotDownloadPath}...`)
|
||||
const godotDownloadedPath = await toolsCache.downloadTool(
|
||||
godotUrl,
|
||||
godotDownloadPath
|
||||
)
|
||||
core.info(`✅ Godot downloaded to ${godotDownloadedPath}`)
|
||||
core.endGroup()
|
||||
|
||||
// Extract Godot
|
||||
core.startGroup(`📦 Extracting Godot to ${installationDir}...`)
|
||||
const godotExtractedPath = await toolsCache.extractZip(
|
||||
godotDownloadedPath,
|
||||
installationDir
|
||||
)
|
||||
core.info(`✅ Godot extracted to ${godotExtractedPath}`)
|
||||
core.endGroup()
|
||||
|
||||
// Show extracted files recursively and list executables.
|
||||
core.startGroup(`📄 Showing extracted files recursively...`)
|
||||
executables = await findExecutablesRecursively(
|
||||
platform,
|
||||
installationDir,
|
||||
''
|
||||
)
|
||||
core.info(`✅ Files shown`)
|
||||
core.endGroup()
|
||||
|
||||
// Save extracted Godot contents to cache
|
||||
core.startGroup(`💾 Saving extracted Godot download to cache...`)
|
||||
await cache.saveCache([godotInstallationPath], godotUrl)
|
||||
core.info(`✅ Godot saved to cache`)
|
||||
core.endGroup()
|
||||
} else {
|
||||
core.info(`🎉 Previous Godot download found in cache!`)
|
||||
core.endGroup()
|
||||
|
||||
core.startGroup(`📄 Showing cached files recursively...`)
|
||||
executables = await findExecutablesRecursively(
|
||||
platform,
|
||||
installationDir,
|
||||
''
|
||||
)
|
||||
core.info(`✅ Files shown`)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
core.startGroup(`🚀 Executables:`)
|
||||
for (const executable of executables) {
|
||||
core.info(` 🚀 ${executable}`)
|
||||
}
|
||||
core.info(`✅ Executables shown`)
|
||||
core.endGroup()
|
||||
|
||||
const godotExecutable = executables.find(exe =>
|
||||
platform!.isGodotExecutable(path.basename(exe))
|
||||
)
|
||||
const godotSharp = executables.find(exe => {
|
||||
const file = exe.toLowerCase()
|
||||
return (
|
||||
file.endsWith('godotsharp.dll') &&
|
||||
(godotSharpRelease ? file.includes('release') : file.includes('debug'))
|
||||
)
|
||||
})!
|
||||
|
||||
if (!godotExecutable) {
|
||||
throw new Error('🚨 No Godot executable found!')
|
||||
}
|
||||
|
||||
if (!godotSharp) {
|
||||
throw new Error('🚨 No GodotSharp.dll found!')
|
||||
}
|
||||
|
||||
core.startGroup(`🚀 Resolve Godot Executables:`)
|
||||
core.info(`🚀 Godot executable found at ${godotExecutable}`)
|
||||
core.info(`🚀 GodotSharp.dll found at ${godotSharp}`)
|
||||
core.endGroup()
|
||||
|
||||
// Add bin directory to PATH
|
||||
core.startGroup(`🔦 Update PATH...`)
|
||||
core.addPath(binDir)
|
||||
core.info(`🔦 Added Bin Directory to PATH: ${binDir}`)
|
||||
// Add path containing GodotSharp.dll to PATH
|
||||
core.endGroup()
|
||||
|
||||
// Create symlink to Godot executable
|
||||
const godotAlias = path.join(binDir, 'godot')
|
||||
const godotSharpDirAlias = path.join(binDir, 'GodotSharp')
|
||||
core.startGroup(`🔗 Creating symlinks to executables...`)
|
||||
fs.linkSync(godotExecutable, godotAlias)
|
||||
core.info(`✅ Symlink to Godot created`)
|
||||
// Create symlink to GodotSharp directory
|
||||
const godotSharpDir = path.join(path.dirname(godotSharp), '../..')
|
||||
// fs.mkdirSync(godotSharpDirAlias, {recursive: true})
|
||||
fs.symlinkSync(godotSharpDir, godotSharpDirAlias)
|
||||
core.info(`✅ Symlink to GodotSharp created at ${godotSharpDirAlias}`)
|
||||
core.endGroup()
|
||||
|
||||
// Add environment variables
|
||||
core.startGroup(`🔧 Adding environment variables...`)
|
||||
core.exportVariable('GODOT', godotAlias)
|
||||
core.info(` GODOT=${godotAlias}`)
|
||||
core.exportVariable('GODOT4', godotAlias)
|
||||
core.info(` GODOT4=${godotAlias}`)
|
||||
core.info(`✅ Environment variables added`)
|
||||
core.endGroup()
|
||||
|
||||
core.info(`✅ Finished!`)
|
||||
} catch (error) {
|
||||
const message = `${error}`
|
||||
core.setFailed(message)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
211
src/utils.ts
Normal file
211
src/utils.ts
Normal file
@ -0,0 +1,211 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export interface Platform {
|
||||
/** Godot installation filename suffix. */
|
||||
godotFilenameSuffix: string
|
||||
/**
|
||||
* Returns true if the given path is most likely the Godot executable for
|
||||
* the platform.
|
||||
* @param basename File basename to check.
|
||||
*/
|
||||
isGodotExecutable(basename: string): boolean
|
||||
/**
|
||||
* Returns the path to the unzipped file for the platform.
|
||||
* @param installationDir Installation directory.
|
||||
* @param versionName Version name.
|
||||
*/
|
||||
getUnzippedPath(installationDir: string, versionName: string): string
|
||||
}
|
||||
|
||||
export class Linux implements Platform {
|
||||
godotFilenameSuffix = '_mono_linux_x86_64'
|
||||
isGodotExecutable(basename: string): boolean {
|
||||
return basename.toLowerCase().endsWith('x86_64')
|
||||
}
|
||||
getUnzippedPath(installationDir: string, versionName: string): string {
|
||||
return path.join(installationDir, versionName)
|
||||
}
|
||||
}
|
||||
|
||||
export class Windows implements Platform {
|
||||
godotFilenameSuffix = '_mono_win64'
|
||||
isGodotExecutable(basename: string): boolean {
|
||||
return basename.toLowerCase().endsWith('_win64.exe')
|
||||
}
|
||||
getUnzippedPath(installationDir: string, versionName: string): string {
|
||||
return path.join(installationDir, versionName)
|
||||
}
|
||||
}
|
||||
|
||||
export class MacOS implements Platform {
|
||||
godotFilenameSuffix = '_mono_macos.universal'
|
||||
isGodotExecutable(basename: string): boolean {
|
||||
return basename.toLowerCase() === 'godot'
|
||||
}
|
||||
getUnzippedPath(installationDir: string, versionName: string): string {
|
||||
return path.join(installationDir, 'Godot_mono.app')
|
||||
}
|
||||
}
|
||||
|
||||
/** Semantic version representation */
|
||||
interface SemanticVersion {
|
||||
/** Version major number */
|
||||
major: string
|
||||
/** Version minor number */
|
||||
minor: string
|
||||
/** Version patch number */
|
||||
patch: string
|
||||
/** Pre-release label (e.g., `beta.16`) */
|
||||
label: string
|
||||
}
|
||||
|
||||
/** Godot download url prefix. */
|
||||
const GODOT_URL_PREFIX = 'https://downloads.tuxfamily.org/godotengine/'
|
||||
/** Godot filename prefix. */
|
||||
const GODOT_FILENAME_PREFIX = 'Godot_v'
|
||||
|
||||
/**
|
||||
* Official semantic version regex.
|
||||
* See https://semver.org
|
||||
*/
|
||||
const SEMANTIC_VERSION_REGEX =
|
||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
|
||||
export function parseVersion(version: string): SemanticVersion {
|
||||
const match = version.match(SEMANTIC_VERSION_REGEX)
|
||||
if (match === null) {
|
||||
throw new Error(`⛔️ Invalid version: ${version}`)
|
||||
}
|
||||
|
||||
const major = match[1] || ''
|
||||
const minor = match[2] || ''
|
||||
const patch = match[3] || ''
|
||||
const label = match[4] || ''
|
||||
return {major, minor, patch, label}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Godot download url for the given version and platform.
|
||||
* @param versionString Version string.
|
||||
* @param platform Current platform instance.
|
||||
* @returns Godot binary download url.
|
||||
*/
|
||||
export function getGodotUrl(versionString: string, platform: Platform): string {
|
||||
const version = parseVersion(versionString)
|
||||
const major = version.major
|
||||
const minor = version.minor
|
||||
const patch = version.patch
|
||||
const label = version.label.replace('.', '')
|
||||
|
||||
const filename = getGodotFilename(version, platform)
|
||||
|
||||
let url = `${GODOT_URL_PREFIX + major}.${minor}`
|
||||
if (patch !== '' && patch !== '0') {
|
||||
url += `.${patch}`
|
||||
}
|
||||
url += '/'
|
||||
if (label !== '') {
|
||||
url += `${label}/`
|
||||
}
|
||||
url += `mono/${filename}.zip`
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
export function getGodotFilename(
|
||||
version: SemanticVersion,
|
||||
platform: Platform
|
||||
): string {
|
||||
const major = version.major
|
||||
const minor = version.minor
|
||||
const patch = version.patch
|
||||
const label = version.label.replace('.', '')
|
||||
|
||||
let filename = GODOT_FILENAME_PREFIX + major
|
||||
|
||||
if (minor !== '') {
|
||||
filename += `.${minor}`
|
||||
}
|
||||
if (patch !== '' && patch !== '0') {
|
||||
filename += `.${patch}`
|
||||
}
|
||||
if (label !== '') {
|
||||
filename += `-${label}`
|
||||
}
|
||||
|
||||
return filename + platform.godotFilenameSuffix
|
||||
}
|
||||
|
||||
export function getGodotFilenameFromVersionString(
|
||||
versionString: string,
|
||||
platform: Platform
|
||||
): string {
|
||||
return getGodotFilename(parseVersion(versionString), platform)
|
||||
}
|
||||
|
||||
export function getPlatform(processPlatform: NodeJS.Platform): Platform {
|
||||
switch (processPlatform) {
|
||||
case 'linux':
|
||||
core.info('🐧 Running on Linux')
|
||||
return new Linux()
|
||||
case 'win32':
|
||||
core.info('⧉ Running on Windows')
|
||||
return new Windows()
|
||||
case 'darwin':
|
||||
core.info('🍏 Running on macOS')
|
||||
return new MacOS()
|
||||
default:
|
||||
throw new Error(`⛔️ Unrecognized platform: ${process.platform}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function findExecutablesRecursively(
|
||||
platform: Platform,
|
||||
dir: string,
|
||||
indent: string
|
||||
): Promise<string[]> {
|
||||
core.info(`${indent}📁 ${dir}`)
|
||||
let executables: string[] = []
|
||||
const files = await fs.promises.readdir(dir, {withFileTypes: true})
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file.name)
|
||||
if (file.isDirectory()) {
|
||||
const additionalExecutables = await findExecutablesRecursively(
|
||||
platform,
|
||||
filePath,
|
||||
`${indent} `
|
||||
)
|
||||
executables = executables.concat(additionalExecutables)
|
||||
} else {
|
||||
// Test if file is executable. GodotSharp.dll is always considered an
|
||||
// executable.
|
||||
let isExecutable = file.name === 'GodotSharp.dll' ? true : false
|
||||
if (!isExecutable) {
|
||||
if (platform instanceof Windows) {
|
||||
// fs.constants.X_OK doesn't seem to work on Windows.
|
||||
// Resort to checking the file extension.
|
||||
if (file.name.toLowerCase().endsWith('.exe')) {
|
||||
isExecutable = true
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
fs.accessSync(filePath, fs.constants.X_OK)
|
||||
isExecutable = true
|
||||
} catch (error) {
|
||||
// File is not executable.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isExecutable) {
|
||||
core.info(`${indent} 🚀 ${file.name}`)
|
||||
executables.push(filePath)
|
||||
} else {
|
||||
core.info(`${indent} 📄 ${file.name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return executables
|
||||
}
|
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.test.ts"
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user