mirror of
https://github.com/game-ci/unity-builder.git
synced 2026-01-29 12:19:06 +08:00
Compare commits
138 Commits
v0.5
...
check-inte
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03391a19f1 | ||
|
|
fdcb37075f | ||
|
|
229b0d02f8 | ||
|
|
c68bdf8177 | ||
|
|
938926799f | ||
|
|
5efb4868ad | ||
|
|
7749b8862d | ||
|
|
98a56c4169 | ||
|
|
720ee0c896 | ||
|
|
d42c251af3 | ||
|
|
bfe6be7ce2 | ||
|
|
f15f40d265 | ||
|
|
866f364f64 | ||
|
|
a245f08e75 | ||
|
|
21c211bbdd | ||
|
|
3718e05961 | ||
|
|
0159028bb1 | ||
|
|
054c6bfab3 | ||
|
|
8c9ff3249e | ||
|
|
7386c669ad | ||
|
|
ce865270c4 | ||
|
|
7e17091251 | ||
|
|
02ff5bbef2 | ||
|
|
8c177b1bad | ||
|
|
699621ed21 | ||
|
|
44bde7feb9 | ||
|
|
5328bda08e | ||
|
|
34e4b86924 | ||
|
|
e9514b4e37 | ||
|
|
d4d91e75c3 | ||
|
|
2085eff264 | ||
|
|
98a1b078fc | ||
|
|
afef854ea0 | ||
|
|
a0a5de2a83 | ||
|
|
c146049b33 | ||
|
|
e46399169f | ||
|
|
b41026b36e | ||
|
|
cd1d215dfa | ||
|
|
d01e844eea | ||
|
|
40564afbaf | ||
|
|
b6f8040f4a | ||
|
|
22c7d0e516 | ||
|
|
f85e50e499 | ||
|
|
d75d7890d0 | ||
|
|
2e81e61af3 | ||
|
|
ac76e9d562 | ||
|
|
39a160b789 | ||
|
|
e8a2eaad72 | ||
|
|
6f1d03d8a8 | ||
|
|
5ee9c59113 | ||
|
|
03510d4a55 | ||
|
|
ad65b0ece4 | ||
|
|
a513e5b640 | ||
|
|
328b0d8ac0 | ||
|
|
e53dcf13e3 | ||
|
|
3338b392a0 | ||
|
|
7e115b5cc3 | ||
|
|
758108295c | ||
|
|
8684472273 | ||
|
|
38b6a42f5e | ||
|
|
fbeaf77867 | ||
|
|
6f3a2bd992 | ||
|
|
e1eda1e876 | ||
|
|
0088ca3094 | ||
|
|
465f15a945 | ||
|
|
ffe381e28a | ||
|
|
085359d24f | ||
|
|
b47dcdab84 | ||
|
|
0c23c7d0a3 | ||
|
|
f3099b6226 | ||
|
|
a969d3322f | ||
|
|
8f63acf12b | ||
|
|
1813ee2895 | ||
|
|
6e377601bf | ||
|
|
f7321735d5 | ||
|
|
19429bf324 | ||
|
|
04eaffcd1f | ||
|
|
2c1ef01beb | ||
|
|
dfccf539d1 | ||
|
|
c1a7c5f70b | ||
|
|
7e4851cc3a | ||
|
|
bbb5661b43 | ||
|
|
678fb210be | ||
|
|
90af9768a7 | ||
|
|
6ab433b631 | ||
|
|
d5c4c09fc4 | ||
|
|
3e69d63ec4 | ||
|
|
c47d5a4d2e | ||
|
|
9dd75201b0 | ||
|
|
308a9e173d | ||
|
|
f5a61eed4a | ||
|
|
051513bf4a | ||
|
|
d9382df690 | ||
|
|
0df7fa7855 | ||
|
|
d1c1d276e0 | ||
|
|
ad1f47dd3a | ||
|
|
a0d197e1fa | ||
|
|
cc244fa58b | ||
|
|
c70fdac383 | ||
|
|
81487e2140 | ||
|
|
ef06de9255 | ||
|
|
64499d66cc | ||
|
|
59ab903a78 | ||
|
|
2fa5b33111 | ||
|
|
0399609b07 | ||
|
|
1c91a3bf31 | ||
|
|
ae7f659e9f | ||
|
|
b781b891ec | ||
|
|
f3a984165e | ||
|
|
d8896dc4f5 | ||
|
|
4051832dc0 | ||
|
|
fe2311ef4b | ||
|
|
37d5ce498f | ||
|
|
6bff9d7c68 | ||
|
|
7d51d12262 | ||
|
|
b382ae9023 | ||
|
|
4c8f96d75c | ||
|
|
afddcfa5fd | ||
|
|
ca2bcea3ad | ||
|
|
d5552eaa01 | ||
|
|
32081adc59 | ||
|
|
ad034dd2a5 | ||
|
|
cad4a8a0e5 | ||
|
|
1d1f81c0bb | ||
|
|
a84535fc04 | ||
|
|
a2db13a084 | ||
|
|
b6f8dac777 | ||
|
|
1de4638512 | ||
|
|
4b07d18a89 | ||
|
|
adb8cfef12 | ||
|
|
57d2023972 | ||
|
|
d2be807d0d | ||
|
|
99bb63390a | ||
|
|
d27df56d26 | ||
|
|
a6607a341e | ||
|
|
2d3095660d | ||
|
|
0badec78bf | ||
|
|
c3b8284b44 |
@@ -2,4 +2,4 @@
|
||||
*
|
||||
|
||||
# Files required for the action
|
||||
!builder/
|
||||
!action/
|
||||
|
||||
@@ -11,8 +11,11 @@ tab_width = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = 0
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
max_line_length = off
|
||||
|
||||
[COMMIT_EDITMSG]
|
||||
max_line_length = 0
|
||||
max_line_length = off
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
**/node_modules/**
|
||||
**/builder/**
|
||||
**/action/**
|
||||
|
||||
29
.github/workflows/activation.yml
vendored
Normal file
29
.github/workflows/activation.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
#name: Acquire activation file
|
||||
#
|
||||
#on: [push]
|
||||
#
|
||||
#jobs:
|
||||
# activation:
|
||||
# name: Request manual activation file (${{ matrix.unityVersion }}) 🔑
|
||||
# runs-on: ubuntu-latest
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# unityVersion:
|
||||
# - 2019.2.11f1
|
||||
# - 2019.3.15f1
|
||||
#
|
||||
# steps:
|
||||
# # Request manual activation file
|
||||
# - name: Request manual activation file
|
||||
# id: getManualLicenseFile
|
||||
# uses: webbertakken/unity-request-manual-activation-file@v1.1
|
||||
# with:
|
||||
# unityVersion: ${{ matrix.unityVersion }}
|
||||
#
|
||||
# # Upload artifact (Unity_v20XX.X.XXXX.alf)
|
||||
# - name: Expose as artifact
|
||||
# uses: actions/upload-artifact@v1
|
||||
# with:
|
||||
# name: ${{ steps.getManualLicenseFile.outputs.filePath }}
|
||||
# path: ${{ steps.getManualLicenseFile.outputs.filePath }}
|
||||
49
.github/workflows/main.yml
vendored
49
.github/workflows/main.yml
vendored
@@ -2,12 +2,27 @@ name: Actions 😎
|
||||
|
||||
on:
|
||||
pull_request: {}
|
||||
push: { branches: [master] }
|
||||
push: { branches: [master, check-internal-push] }
|
||||
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||
CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de'
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: yarn
|
||||
- run: yarn lint
|
||||
- run: yarn test --coverage
|
||||
- run: bash <(curl -s https://codecov.io/bash)
|
||||
- run: yarn build || { echo "build command should always succeed" ; exit 61; }
|
||||
- run: yarn build --quiet && git diff --quiet action || { echo "action should be auto generated" ; git diff action ; exit 62; }
|
||||
|
||||
buildForAllPlatforms:
|
||||
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
|
||||
runs-on: ubuntu-latest
|
||||
@@ -18,7 +33,15 @@ jobs:
|
||||
- test-project
|
||||
unityVersion:
|
||||
- 2019.2.11f1
|
||||
# - 2019.3.0f1 # requires different license file/method
|
||||
- 2019.3.15f1
|
||||
include:
|
||||
# Please be polite; don't copy my personal licenses.
|
||||
# These are here because they are needed to allowing pull requests from forks to unity-builder.
|
||||
# You should be using ${{ secrets.UNITY_LICENSE_2019_3_15 }} here.
|
||||
- unityVersion: 2019.2.11f1
|
||||
license: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"d39b8e2f4d364b2e98b06afa0c6e08c5\"/>\n <Binding Key=\"2\" Value=\"d39b8e2f4d364b2e98b06afa0c6e08c5\"/>\n </MachineBindings>\n <MachineID Value=\"Xxo1ZKbdPu/IATrc0mPBYANJFF0=\"/>\n <SerialHash Value=\"1efd68fa935192b6090ac03c77d289a9f588c55a\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUg2WFMtUE00NS1SM0M4LUUyWlotWkdWOA==\"/>\n <SerialMasked Value=\"F4-H6XS-PM45-R3C8-E2ZZ-XXXX\"/>\n <StartDate Value=\"2018-05-02T00:00:00\"/>\n <UpdateDate Value=\"2019-11-25T18:23:38\"/>\n <InitialActivationDate Value=\"2018-05-02T14:21:28\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2019.2.11f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>JHdOBFmBNq2H8BrGFzir/StLoYo=</DigestValue></Reference></SignedInfo><SignatureValue>aENLHd37a51RtP2/g7YU0Pexf5mx0/ENXYGtrPzqwZ8NQt2AsSdxGnl0CUB45/GuNXfJVDt2HWot\ncNYZB2OylVBn1WHQbKZlPmm8gEAMz0MYbr4Isb5i5buryBrZlmbEOjnRI+pEg1CBwlgMo6xdtjjE\n/d7cC293QIUO91kdzRXftYou1dNaUyuPL9ZH65vdB2pDXGRNxgUVD+GnnqZA7b5L2HXqNQclcWAK\n5Yd1BeF3VzR1iLw9G/SmH5oOhnpXSmqbL4qk7LVP2/mgXpFk5kP4X8VC3z47obNhBIGq40dwWyEe\nUYk5/nRAOkZawDT+tcu96e06gPC9Cxk5PdbRbA==</SignatureValue></Signature></root>"
|
||||
- unityVersion: 2019.3.15f1
|
||||
license: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"33bf639e81e54693a8f9bf57c8900e5a\"/>\n <Binding Key=\"2\" Value=\"33bf639e81e54693a8f9bf57c8900e5a\"/>\n </MachineBindings>\n <MachineID Value=\"xWka2iXdDJejhZdi/zU2RUeXUi4=\"/>\n <SerialHash Value=\"1efd68fa935192b6090ac03c77d289a9f588c55a\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUg2WFMtUE00NS1SM0M4LUUyWlotWkdWOA==\"/>\n <SerialMasked Value=\"F4-H6XS-PM45-R3C8-E2ZZ-XXXX\"/>\n <StartDate Value=\"2018-05-02T00:00:00\"/>\n <UpdateDate Value=\"2020-06-14T13:49:47\"/>\n <InitialActivationDate Value=\"2018-05-02T14:21:28\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2019.3.15f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>bpzWx3PZ0lqWDo1m9aLQuZ4cweo=</DigestValue></Reference></SignedInfo><SignatureValue>QcDm4/qAXZuUMQbUVk63vO6u66Bp8PnqqWQcZZOcym/rGUZLj1sr66EquF3X3w1L7aqiwMGtbY2b\nkPttcalFeaBkc5NsJMrexWjuBCxQvhbmVFQnTjvC6vNS+k1wrkz7If1oPkz/XaDtCfUs8oxc9iPe\nPzzUJIVYLZoDtpPq2XbgVn9/TiVb3Zu6ldKgvtNRYUjrB3KywtvL9OcIFll3htRcBZPG43kxryJc\nDD2TL5Nw1JuX6MejBBuYTZsZNpGX9Pjop9+uFUZ4GI9h8a5g6wJUfXzsGw7j4gkvDkC9MvyWiksi\n2hNXw1QNeB6JfQsd4sAuhYh/CqTm2gCz9i9ZpA==</SignatureValue></Signature></root>"
|
||||
targetPlatform:
|
||||
- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
|
||||
- StandaloneWindows # Build a Windows standalone.
|
||||
@@ -26,7 +49,7 @@ jobs:
|
||||
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
||||
- iOS # Build an iOS player.
|
||||
# - Android # Build an Android .apk standalone app.
|
||||
- WebGL # WebGL.
|
||||
# - WebGL # WebGL.
|
||||
# - WSAPlayer # Build an Windows Store Apps player.
|
||||
# - PS4 # Build a PS4 Standalone.
|
||||
# - XboxOne # Build a Xbox One Standalone.
|
||||
@@ -34,13 +57,25 @@ jobs:
|
||||
# - Switch # Build a Nintendo Switch player.
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: ./
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
lfs: true
|
||||
- uses: actions/cache@v1.1.0
|
||||
with:
|
||||
path: ${{ matrix.projectPath }}/Library
|
||||
key: Library-${{ matrix.projectPath }}-${{ matrix.targetPlatform }}
|
||||
restore-keys: |
|
||||
Library-${{ matrix.projectPath }}-
|
||||
Library-
|
||||
- uses: webbertakken/unity-builder@8ae8dbb
|
||||
env:
|
||||
UNITY_LICENSE: ${{ matrix.license }}
|
||||
with:
|
||||
projectPath: ${{ matrix.projectPath }}
|
||||
unityVersion: ${{ matrix.unityVersion }}
|
||||
targetPlatform: ${{ matrix.targetPlatform }}
|
||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Build
|
||||
name: Build (${{ matrix.unityVersion }})
|
||||
path: build
|
||||
|
||||
15
.github/workflows/test.yml
vendored
15
.github/workflows/test.yml
vendored
@@ -1,15 +0,0 @@
|
||||
name: Test Action
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: yarn
|
||||
- run: yarn lint
|
||||
- run: yarn test
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.idea
|
||||
node_modules
|
||||
coverage/
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
**/node_modules/**
|
||||
**/dist/**
|
||||
**/action/**
|
||||
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at webber@takken.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
39
CONTRIBUTING.md
Normal file
39
CONTRIBUTING.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Contributing
|
||||
|
||||
## How to Contribute
|
||||
|
||||
#### Code of Conduct
|
||||
|
||||
This repository has adopted the Contributor Covenant as it's
|
||||
Code of Conduct. It is expected that participants adhere to it.
|
||||
|
||||
#### Proposing a Change
|
||||
|
||||
If you are unsure about whether or not a change is desired,
|
||||
you can create an issue. This is useful because it creates
|
||||
the possibility for a discussion that's visible to everyone.
|
||||
|
||||
When fixing a bug it is fine to submit a pull request right away.
|
||||
|
||||
#### Sending a Pull Request
|
||||
|
||||
Steps to be performed to submit a pull request:
|
||||
|
||||
1. Fork the repository and create your branch from `master`.
|
||||
2. Run `yarn` in the repository root.
|
||||
3. If you've fixed a bug or added code that should be tested, add tests!
|
||||
4. Fill out the description, link any related issues and submit your pull request.
|
||||
|
||||
#### Pull Request Prerequisites
|
||||
|
||||
You have [Node](https://nodejs.org/) installed at v12.2.0+ and [Yarn](https://yarnpkg.com/) at v1.18.0+.
|
||||
|
||||
Please note that commit hooks will run automatically to perform some tasks;
|
||||
|
||||
- format your code
|
||||
- run tests
|
||||
- build distributable files
|
||||
|
||||
#### License
|
||||
|
||||
By contributing to this repository, you agree that your contributions will be licensed under its MIT license.
|
||||
189
README.md
189
README.md
@@ -1,6 +1,8 @@
|
||||
# Unity - Builder
|
||||
|
||||
[](https://github.com/webbertakken/unity-builder/actions?query=branch%3Amaster+workflow%3A%22Actions+%F0%9F%98%8E%22)
|
||||
[](https://github.com/webbertakken/unity-builder/actions?query=branch%3Amaster+event%3Apush+workflow%3A%22Actions+%F0%9F%98%8E%22)
|
||||
[](https://lgtm.com/projects/g/webbertakken/unity-builder/context:javascript)
|
||||
[](https://codecov.io/gh/webbertakken/unity-builder)
|
||||
|
||||
---
|
||||
|
||||
@@ -31,10 +33,24 @@ collection repository for workflow documentation and reference implementation.
|
||||
|
||||
## Usage
|
||||
|
||||
#### Setup builder
|
||||
|
||||
By default the enabled scenes from the project's settings will be built.
|
||||
|
||||
Create or edit the file called `.github/workflows/main.yml` and add a job to it.
|
||||
|
||||
##### Personal License
|
||||
|
||||
Personal licenses require a one-time manual activation step (per unity version).
|
||||
|
||||
Make sure you
|
||||
[acquire and activate](https://github.com/marketplace/actions/unity-request-activation-file)
|
||||
your license file and add it as a secret.
|
||||
|
||||
Then, define the build step as follows:
|
||||
|
||||
```yaml
|
||||
- uses: webbertakken/unity-builder@v0.5
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
env:
|
||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||
with:
|
||||
@@ -43,6 +59,75 @@ Create or edit the file called `.github/workflows/main.yml` and add a job to it.
|
||||
targetPlatform: WebGL
|
||||
```
|
||||
|
||||
##### Professional license
|
||||
|
||||
Professional licenses do not need any manual steps.
|
||||
|
||||
Instead, three variables will need to be set.
|
||||
|
||||
- `UNITY_EMAIL` (should contain the email address for your Unity account)
|
||||
- `UNITY_PASSWORD` (the password that you use to login to Unity)
|
||||
- `UNITY_SERIAL` (the serial provided by Unity)
|
||||
|
||||
Define the build step as follows:
|
||||
|
||||
```yaml
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
env:
|
||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||
with:
|
||||
projectPath: path/to/your/project
|
||||
unityVersion: 2020.X.XXXX
|
||||
targetPlatform: WebGL
|
||||
```
|
||||
|
||||
That is all you need to build your project.
|
||||
|
||||
#### Storing the build
|
||||
|
||||
To be able to access your built files,
|
||||
they need to be uploaded as artifacts.
|
||||
To do this it is recommended to use Github Actions official
|
||||
[upload artifact action](https://github.com/marketplace/actions/upload-artifact)
|
||||
after any build action.
|
||||
|
||||
By default, Builder outputs it's builds to a folder named `build`.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Build
|
||||
path: build
|
||||
```
|
||||
|
||||
Builds can now be downloaded as Artifacts in the Actions tab.
|
||||
|
||||
#### Caching
|
||||
|
||||
In order to make builds run faster, you can cache Library files from previous
|
||||
builds. To do so simply add Github Actions official
|
||||
[cache action](https://github.com/marketplace/actions/cache) before any unity steps.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
- uses: actions/cache@v1.1.0
|
||||
with:
|
||||
path: path/to/your/project/Library
|
||||
key: Library-MyProjectName-TargetPlatform
|
||||
restore-keys: |
|
||||
Library-MyProjectName-
|
||||
Library-
|
||||
```
|
||||
|
||||
This simple addition could speed up your build by more than 50%.
|
||||
|
||||
## Complete example
|
||||
|
||||
A complete workflow that builds every available platform could look like this:
|
||||
|
||||
```yaml
|
||||
@@ -81,8 +166,17 @@ jobs:
|
||||
- tvOS # Build to Apple's tvOS platform.
|
||||
- Switch # Build a Nintendo Switch player.
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: webbertakken/unity-builder@v0.5
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
lfs: true
|
||||
- uses: actions/cache@v1.1.0
|
||||
with:
|
||||
path: ${{ matrix.projectPath }}/Library
|
||||
key: Library-${{ matrix.projectPath }}-${{ matrix.targetPlatform }}
|
||||
restore-keys: |
|
||||
Library-${{ matrix.projectPath }}-
|
||||
Library-
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
with:
|
||||
projectPath: ${{ matrix.projectPath }}
|
||||
unityVersion: ${{ matrix.unityVersion }}
|
||||
@@ -93,10 +187,7 @@ jobs:
|
||||
path: build
|
||||
```
|
||||
|
||||
> **Notes:**
|
||||
>
|
||||
> - Don't forget to replace _<test-project>_ with your project name.
|
||||
> - By default the enabled scenes from the project's settings will be built.
|
||||
> **Note:** _Environment variables are set for all jobs in the workflow like this._
|
||||
|
||||
## Configuration options
|
||||
|
||||
@@ -127,10 +218,10 @@ _**required:** `true`_
|
||||
|
||||
#### buildName
|
||||
|
||||
Name of the build.
|
||||
Name of the build. Also the folder in which the build will be stored within `buildsPath`.
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** `testBuild`_
|
||||
_**default:** `<build_target>`_
|
||||
|
||||
#### buildsPath
|
||||
|
||||
@@ -153,7 +244,7 @@ There are two conditions for a custom buildCommand:
|
||||
_**example:**_
|
||||
|
||||
```yaml
|
||||
- uses: webbertakken/unity-builder@master
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
with:
|
||||
buildMethod: EditorNamespace.BuilderClassName.StaticBulidMethod
|
||||
```
|
||||
@@ -161,6 +252,82 @@ _**example:**_
|
||||
_**required:** `false`_
|
||||
_**default:** Built-in script that will run a build out of the box._
|
||||
|
||||
#### versioning
|
||||
|
||||
Configure a specific versioning strategy
|
||||
|
||||
```yaml
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
with:
|
||||
versioning: Semantic
|
||||
```
|
||||
|
||||
Find the available strategies below:
|
||||
|
||||
##### Semantic
|
||||
|
||||
Versioning out of the box! **(recommended)**
|
||||
|
||||
> Compatible with **all platforms**.
|
||||
> Does **not** modify your repository.
|
||||
> Requires **zero configuration**.
|
||||
|
||||
How it works:
|
||||
|
||||
> Generates a version based on [semantic versioning](https://semver.org/).
|
||||
> Follows `<major>.<minor>.<patch>` for example `0.17.2`.
|
||||
> The latest tag dictates `<major>.<minor>` (defaults to 0.0 for no tag).
|
||||
> The number of commits (since the last tag, if any) is used for `<patch>`.
|
||||
|
||||
No configuration required.
|
||||
|
||||
##### Custom
|
||||
|
||||
Allows specifying a custom version in the `version` field. **(advanced users)**
|
||||
|
||||
> This strategy is useful when your project or pipeline has some kind of orchestration
|
||||
> that determines the versions.
|
||||
|
||||
##### None
|
||||
|
||||
No version will be set by Builder. **(not recommended)**
|
||||
|
||||
> Not recommended unless you generate a new version in a pre-commit hook. Manually
|
||||
> setting versions is error-prone.
|
||||
|
||||
#### allowDirtyBuild
|
||||
|
||||
Allows the branch of the build to be dirty, and still generate the build.
|
||||
|
||||
```yaml
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
with:
|
||||
allowDirtyBuild: true
|
||||
```
|
||||
|
||||
Note that it is generally bad practice to modify your branch
|
||||
in a CI Pipeline. However there are exceptions where this might
|
||||
be needed. (use with care).
|
||||
|
||||
#### customParameters
|
||||
|
||||
Custom parameters to configure the build.
|
||||
|
||||
Parameters must start with a hyphen (`-`) and may be followed by a value (without hyphen).
|
||||
|
||||
Parameters without a value will be considered booleans (with a value of true).
|
||||
|
||||
_**example:**_
|
||||
|
||||
```yaml
|
||||
- uses: webbertakken/unity-builder@<version>
|
||||
with:
|
||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
||||
```
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** ""_
|
||||
|
||||
## More actions
|
||||
|
||||
Visit
|
||||
|
||||
@@ -32,4 +32,4 @@ branding:
|
||||
color: 'gray-dark'
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'builder/index.js'
|
||||
main: 'action/index.js'
|
||||
|
||||
4
action/default-build-script/.editorconfig
Normal file
4
action/default-build-script/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*.cs]
|
||||
resharper_check_namespace_highlighting = do_not_show
|
||||
@@ -57,6 +57,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Assets\Editor\Builder.cs" />
|
||||
<Compile Include="Assets\Editor\Input\ArgumentsParser.cs" />
|
||||
<Compile Include="Assets\Editor\Reporting\StdOutReporter.cs" />
|
||||
<Compile Include="Assets\Editor\System\ProcessExtensions.cs" />
|
||||
<Compile Include="Assets\Editor\Versioning\VersionApplicator.cs" />
|
||||
<Compile Include="Assets\Editor\Versioning\Git.cs" />
|
||||
<Compile Include="Assets\Editor\Versioning\VersionGenerator.cs" />
|
||||
<Compile Include="Assets\Editor\Versioning\GitException.cs" />
|
||||
<Reference Include="UnityEditor.TestRunner">
|
||||
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.TestRunner.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -688,6 +695,9 @@
|
||||
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/Boo.Lang.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include=".editorconfig" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
44
action/default-build-script/Assets/Editor/Builder.cs
Normal file
44
action/default-build-script/Assets/Editor/Builder.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityBuilderAction.Input;
|
||||
using UnityBuilderAction.Reporting;
|
||||
using UnityBuilderAction.Versioning;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
namespace UnityBuilderAction
|
||||
{
|
||||
static class Builder
|
||||
{
|
||||
public static void BuildProject()
|
||||
{
|
||||
// Gather values from args
|
||||
var options = ArgumentsParser.GetValidatedOptions();
|
||||
|
||||
// Gather values from project
|
||||
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
|
||||
|
||||
// Define BuildPlayer Options
|
||||
var buildOptions = new BuildPlayerOptions {
|
||||
scenes = scenes,
|
||||
locationPathName = options["customBuildPath"],
|
||||
target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]),
|
||||
};
|
||||
|
||||
// Set version for this build
|
||||
VersionApplicator.SetVersion(options["version"]);
|
||||
|
||||
// Perform build
|
||||
BuildReport buildReport = BuildPipeline.BuildPlayer(buildOptions);
|
||||
|
||||
// Summary
|
||||
BuildSummary summary = buildReport.summary;
|
||||
StdOutReporter.ReportSummary(summary);
|
||||
|
||||
// Result
|
||||
BuildResult result = summary.result;
|
||||
StdOutReporter.ExitWithResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
action/default-build-script/Assets/Editor/Input.meta
Normal file
3
action/default-build-script/Assets/Editor/Input.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6e5ef18d769419d887b56665969442b
|
||||
timeCreated: 1587503329
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityBuilderAction.Input
|
||||
{
|
||||
public class ArgumentsParser
|
||||
{
|
||||
static string EOL = Environment.NewLine;
|
||||
|
||||
public static Dictionary<string, string> GetValidatedOptions()
|
||||
{
|
||||
ParseCommandLineArguments(out var validatedOptions);
|
||||
|
||||
if (!validatedOptions.TryGetValue("projectPath", out var projectPath)) {
|
||||
Console.WriteLine("Missing argument -projectPath");
|
||||
EditorApplication.Exit(110);
|
||||
}
|
||||
|
||||
if (!validatedOptions.TryGetValue("buildTarget", out var buildTarget)) {
|
||||
Console.WriteLine("Missing argument -buildTarget");
|
||||
EditorApplication.Exit(120);
|
||||
}
|
||||
|
||||
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
|
||||
EditorApplication.Exit(121);
|
||||
}
|
||||
|
||||
if (!validatedOptions.TryGetValue("customBuildPath", out var customBuildPath)) {
|
||||
Console.WriteLine("Missing argument -customBuildPath");
|
||||
EditorApplication.Exit(130);
|
||||
}
|
||||
|
||||
const string defaultCustomBuildName = "TestBuild";
|
||||
if (!validatedOptions.TryGetValue("customBuildName", out var customBuildName)) {
|
||||
Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}.");
|
||||
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
||||
} else if (customBuildName == "") {
|
||||
Console.WriteLine($"Invalid argument -customBuildName, defaulting to {defaultCustomBuildName}.");
|
||||
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
||||
}
|
||||
|
||||
return validatedOptions;
|
||||
}
|
||||
|
||||
static void ParseCommandLineArguments(out Dictionary<string, string> providedArguments)
|
||||
{
|
||||
providedArguments = new Dictionary<string, string>();
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
|
||||
Console.WriteLine(
|
||||
$"{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"# Parsing settings #{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"{EOL}"
|
||||
);
|
||||
|
||||
// Extract flags with optional values
|
||||
for (int current = 0, next = 1; current < args.Length; current++, next++) {
|
||||
// Parse flag
|
||||
bool isFlag = args[current].StartsWith("-");
|
||||
if (!isFlag) continue;
|
||||
string flag = args[current].TrimStart('-');
|
||||
|
||||
// Parse optional value
|
||||
bool flagHasValue = next < args.Length && !args[next].StartsWith("-");
|
||||
string value = flagHasValue ? args[next].TrimStart('-') : "";
|
||||
|
||||
// Assign
|
||||
Console.WriteLine($"Found flag \"{flag}\" with value \"{value}\".");
|
||||
providedArguments.Add(flag, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46d2ec4a86604575be2b2d02b0df7b74
|
||||
timeCreated: 1587503354
|
||||
3
action/default-build-script/Assets/Editor/Reporting.meta
Normal file
3
action/default-build-script/Assets/Editor/Reporting.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 166f919334c44e7a80ae916667974e7d
|
||||
timeCreated: 1587503566
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
namespace UnityBuilderAction.Reporting
|
||||
{
|
||||
public class StdOutReporter
|
||||
{
|
||||
static string EOL = Environment.NewLine;
|
||||
|
||||
public static void ReportSummary(BuildSummary summary)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"# Build results #{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"{EOL}" +
|
||||
$"Duration: {summary.totalTime.ToString()}{EOL}" +
|
||||
$"Warnings: {summary.totalWarnings.ToString()}{EOL}" +
|
||||
$"Errors: {summary.totalErrors.ToString()}{EOL}" +
|
||||
$"Size: {summary.totalSize.ToString()} bytes{EOL}" +
|
||||
$"{EOL}"
|
||||
);
|
||||
}
|
||||
|
||||
public static void ExitWithResult(BuildResult result)
|
||||
{
|
||||
if (result == BuildResult.Succeeded) {
|
||||
Console.WriteLine("Build succeeded!");
|
||||
EditorApplication.Exit(0);
|
||||
}
|
||||
|
||||
if (result == BuildResult.Failed) {
|
||||
Console.WriteLine("Build failed!");
|
||||
EditorApplication.Exit(101);
|
||||
}
|
||||
|
||||
if (result == BuildResult.Cancelled) {
|
||||
Console.WriteLine("Build cancelled!");
|
||||
EditorApplication.Exit(102);
|
||||
}
|
||||
|
||||
if (result == BuildResult.Unknown) {
|
||||
Console.WriteLine("Build result is unknown!");
|
||||
EditorApplication.Exit(103);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e59b491a4124442ea7277f76761cdc8a
|
||||
timeCreated: 1587503545
|
||||
3
action/default-build-script/Assets/Editor/System.meta
Normal file
3
action/default-build-script/Assets/Editor/System.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5da3bd7e18c43d79243410166c8dc9a
|
||||
timeCreated: 1587493708
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
// Execute an application or binary with given arguments
|
||||
//
|
||||
// See: https://stackoverflow.com/questions/4291912/process-start-how-to-get-the-output
|
||||
public static int Run(this Process process, string application,
|
||||
string arguments, string workingDirectory, out string output,
|
||||
out string errors)
|
||||
{
|
||||
// Configure how to run the application
|
||||
process.StartInfo = new ProcessStartInfo {
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
FileName = application,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory
|
||||
};
|
||||
|
||||
// Read the output
|
||||
var outputBuilder = new StringBuilder();
|
||||
var errorsBuilder = new StringBuilder();
|
||||
process.OutputDataReceived += (_, args) => outputBuilder.AppendLine(args.Data);
|
||||
process.ErrorDataReceived += (_, args) => errorsBuilder.AppendLine(args.Data);
|
||||
|
||||
// Run the application and wait for it to complete
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
// Format the output
|
||||
output = outputBuilder.ToString().TrimEnd();
|
||||
errors = errorsBuilder.ToString().TrimEnd();
|
||||
|
||||
return process.ExitCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29c1880a390c4af7be821b7877602815
|
||||
timeCreated: 1587494270
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c3bddf6d8984cde9208e3f0fe584879
|
||||
timeCreated: 1587490700
|
||||
116
action/default-build-script/Assets/Editor/Versioning/Git.cs
Normal file
116
action/default-build-script/Assets/Editor/Versioning/Git.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityBuilderAction.Versioning
|
||||
{
|
||||
public static class Git
|
||||
{
|
||||
const string application = @"git";
|
||||
|
||||
/// <summary>
|
||||
/// Generate a version based on the latest tag and the amount of commits.
|
||||
/// Format: 0.1.2 (where 2 is the amount of commits).
|
||||
///
|
||||
/// If no tag is present in the repository then v0.0 is assumed.
|
||||
/// This would result in 0.0.# where # is the amount of commits.
|
||||
/// </summary>
|
||||
public static string GenerateSemanticCommitVersion()
|
||||
{
|
||||
string version;
|
||||
if (HasAnyVersionTags()) {
|
||||
version = GetSemanticCommitVersion();
|
||||
Console.WriteLine("Repository has a valid version tag.");
|
||||
} else {
|
||||
version = $"0.0.{GetTotalNumberOfCommits()}";
|
||||
Console.WriteLine("Repository does not have tags to base the version on.");
|
||||
}
|
||||
|
||||
Console.WriteLine($"Version is {version}");
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of the current tag.
|
||||
///
|
||||
/// The tag must point at HEAD for this method to work.
|
||||
///
|
||||
/// Output Format:
|
||||
/// #.* (where # is the major version and * can be any number of any type of character)
|
||||
/// </summary>
|
||||
public static string GetTagVersion()
|
||||
{
|
||||
string version = Run(@"tag --points-at HEAD | grep v[0-9]*");
|
||||
|
||||
version = version.Substring(1);
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the total number of commits.
|
||||
/// </summary>
|
||||
static int GetTotalNumberOfCommits()
|
||||
{
|
||||
string numberOfCommitsAsString = Run(@"git rev-list --count HEAD");
|
||||
|
||||
return int.Parse(numberOfCommitsAsString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the repository has any version tags yet.
|
||||
/// </summary>
|
||||
static bool HasAnyVersionTags()
|
||||
{
|
||||
return "0" != Run(@"tag --list --merged HEAD | grep v[0-9]* | wc -l");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the build version from git based on the most recent matching tag and
|
||||
/// commit history. This returns the version as: {major.minor.build} where 'build'
|
||||
/// represents the nth commit after the tagged commit.
|
||||
/// Note: The initial 'v' and the commit hash are removed.
|
||||
/// </summary>
|
||||
static string GetSemanticCommitVersion()
|
||||
{
|
||||
// v0.1-2-g12345678 (where 2 is the amount of commits, g stands for git)
|
||||
string version = GetVersionString();
|
||||
// 0.1-2
|
||||
version = version.Substring(1, version.LastIndexOf('-') - 1);
|
||||
// 0.1.2
|
||||
version = version.Replace('-', '.');
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get version string.
|
||||
///
|
||||
/// Format: `v0.1-2-g12345678` (where 2 is the amount of commits since the last tag)
|
||||
///
|
||||
/// See: https://softwareengineering.stackexchange.com/questions/141973/how-do-you-achieve-a-numeric-versioning-scheme-with-git
|
||||
/// </summary>
|
||||
static string GetVersionString()
|
||||
{
|
||||
return Run(@"describe --tags --long --match ""v[0-9]*""");
|
||||
|
||||
// Todo - implement split function based on this more complete query
|
||||
// return Run(@"describe --long --tags --dirty --always");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs git binary with any given arguments and returns the output.
|
||||
/// </summary>
|
||||
static string Run(string arguments)
|
||||
{
|
||||
using (var process = new System.Diagnostics.Process()) {
|
||||
string workingDirectory = Application.dataPath;
|
||||
|
||||
int exitCode = process.Run(application, arguments, workingDirectory, out string output, out string errors);
|
||||
if (exitCode != 0) { throw new GitException(exitCode, errors); }
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdec7fa0f5bb44958fdf74d4658a4601
|
||||
timeCreated: 1587495075
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace UnityBuilderAction.Versioning
|
||||
{
|
||||
public class GitException : InvalidOperationException
|
||||
{
|
||||
public readonly int code;
|
||||
|
||||
public GitException(int code, string errors) : base(errors)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d375e209fd14fc5bc2f3dc3c78ac574
|
||||
timeCreated: 1587490750
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityBuilderAction.Versioning
|
||||
{
|
||||
public class VersionApplicator
|
||||
{
|
||||
public static void SetVersion(string version)
|
||||
{
|
||||
if (version == "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
Apply(version);
|
||||
}
|
||||
|
||||
static void Apply(string version)
|
||||
{
|
||||
PlayerSettings.bundleVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30483367ddc84699a0da377ccb93769a
|
||||
timeCreated: 1587504315
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace UnityBuilderAction.Versioning
|
||||
{
|
||||
public static class VersionGenerator
|
||||
{
|
||||
public static string Generate()
|
||||
{
|
||||
return Git.GenerateSemanticCommitVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9892e03ae8314b7eacd793c8002de007
|
||||
timeCreated: 1587490842
|
||||
@@ -0,0 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Untracked/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Versioning/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
1
action/index.js
Normal file
1
action/index.js
Normal file
File diff suppressed because one or more lines are too long
106
action/steps/activate.sh
Normal file
106
action/steps/activate.sh
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -n "$UNITY_LICENSE" ]] || [[ -n "$UNITY_LICENSE_FILE" ]]; then
|
||||
#
|
||||
# PERSONAL LICENSE MODE
|
||||
#
|
||||
# This will activate Unity, using a license file
|
||||
#
|
||||
# Note that this is the ONLY WAY for PERSONAL LICENSES in 2020.
|
||||
# * See for more details: https://gitlab.com/gableroux/unity3d-gitlab-ci-example/issues/5#note_72815478
|
||||
#
|
||||
# The license file can be acquired using `webbertakken/request-manual-activation-file` action.
|
||||
echo "Requesting activation (personal license)"
|
||||
|
||||
# Set the license file path
|
||||
FILE_PATH=UnityLicenseFile.ulf
|
||||
|
||||
if [[ -n "$UNITY_LICENSE" ]]; then
|
||||
# Copy license file from Github variables
|
||||
echo "$UNITY_LICENSE" | tr -d '\r' > $FILE_PATH
|
||||
elif [[ -n "$UNITY_LICENSE_FILE" ]]; then
|
||||
# Copy license file from file system
|
||||
cat "$UNITY_LICENSE_FILE" | tr -d '\r' > $FILE_PATH
|
||||
fi
|
||||
|
||||
# Activate license
|
||||
ACTIVATION_OUTPUT=$(xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
/opt/Unity/Editor/Unity \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile /dev/stdout \
|
||||
-quit \
|
||||
-manualLicenseFile $FILE_PATH)
|
||||
|
||||
# Store the exit code from the verify command
|
||||
UNITY_EXIT_CODE=$?
|
||||
|
||||
# The exit code for personal activation is always 1;
|
||||
# Determine whether activation was successful.
|
||||
#
|
||||
# Successful output should include the following:
|
||||
#
|
||||
# "LICENSE SYSTEM [2020120 18:51:20] Next license update check is after 2019-11-25T18:23:38"
|
||||
#
|
||||
ACTIVATION_SUCCESSFUL=$(echo $ACTIVATION_OUTPUT | grep 'Next license update check is after' | wc -l)
|
||||
|
||||
# Set exit code to 0 if activation was successful
|
||||
if [[ $ACTIVATION_SUCCESSFUL -eq 1 ]]; then
|
||||
UNITY_EXIT_CODE=0
|
||||
fi;
|
||||
|
||||
# Remove license file
|
||||
rm -f $FILE_PATH
|
||||
|
||||
elif [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
|
||||
#
|
||||
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||
#
|
||||
# This will activate unity, using the activating process.
|
||||
#
|
||||
# Note: This is the preferred way for PROFESSIONAL LICENSES.
|
||||
#
|
||||
echo "Requesting activation (professional license)"
|
||||
|
||||
# Activate license
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
/opt/Unity/Editor/Unity \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile /dev/stdout \
|
||||
-quit \
|
||||
-serial "$UNITY_SERIAL" \
|
||||
-username "$UNITY_EMAIL" \
|
||||
-password "$UNITY_PASSWORD"
|
||||
|
||||
# Store the exit code from the verify command
|
||||
UNITY_EXIT_CODE=$?
|
||||
|
||||
else
|
||||
#
|
||||
# NO LICENSE ACTIVATION STRATEGY MATCHED
|
||||
#
|
||||
# This will exit since no activation strategies could be matched.
|
||||
#
|
||||
echo "License activation strategy could not be determined."
|
||||
echo ""
|
||||
echo "Visit https://github.com/webbertakken/unity-builder#usage for more"
|
||||
echo "details on how to set up one of the possible activation strategies."
|
||||
|
||||
# Immediately exit as no UNITY_EXIT_CODE can be derrived.
|
||||
exit 1;
|
||||
|
||||
fi
|
||||
|
||||
#
|
||||
# Display information about the result
|
||||
#
|
||||
if [ $UNITY_EXIT_CODE -eq 0 ]; then
|
||||
# Activation was a success
|
||||
echo "Activation complete."
|
||||
else
|
||||
# Activation failed so exit with the code from the license verification step
|
||||
echo "Unclassified error occured while trying to activate license."
|
||||
echo "Exit code was: $UNITY_EXIT_CODE"
|
||||
exit $UNITY_EXIT_CODE
|
||||
fi
|
||||
@@ -4,53 +4,28 @@
|
||||
# Set project path
|
||||
#
|
||||
|
||||
UNITY_PROJECT_PATH=$GITHUB_WORKSPACE/$PROJECT_PATH
|
||||
UNITY_PROJECT_PATH="$GITHUB_WORKSPACE/$PROJECT_PATH"
|
||||
echo "Using project path \"$UNITY_PROJECT_PATH\"."
|
||||
|
||||
#
|
||||
# Set the name for the build
|
||||
# Display the name for the build, doubles as the output name
|
||||
#
|
||||
|
||||
if [ -z "$BUILD_NAME" ]; then
|
||||
BUILD_NAME="build-$(date '+%F-%H%M')"
|
||||
fi
|
||||
echo "Using build name \"$BUILD_NAME\"."
|
||||
|
||||
#
|
||||
# Set the builds target platform;
|
||||
#
|
||||
# Web: WebGL
|
||||
# Desktop: StandaloneOSX, StandaloneWindows, StandaloneWindows64, StandaloneLinux64
|
||||
# Console: PS4, XboxOne, Switch
|
||||
# Mobile: Android, iOS
|
||||
# Other: tvOS, Lumin, BJM, WSAPlayer
|
||||
#
|
||||
# Default to WebGL (no particular reason)
|
||||
# Display the build's target platform;
|
||||
#
|
||||
|
||||
if [ -z "$BUILD_TARGET" ]; then
|
||||
BUILD_TARGET=WebGL
|
||||
fi
|
||||
echo "Using build target \"$BUILD_TARGET\"."
|
||||
|
||||
#
|
||||
# Set builds path
|
||||
# Display build path and file
|
||||
#
|
||||
|
||||
if [ -z "$BUILDS_PATH" ]; then
|
||||
BUILDS_PATH=build
|
||||
fi
|
||||
BUILDS_FULL_PATH=$GITHUB_WORKSPACE/$BUILDS_PATH
|
||||
|
||||
# TODO - Cleanup
|
||||
BUILD_FOLDER=$BUILD_TARGET-$UNITY_VERSION
|
||||
CURRENT_BUILD_PATH=$BUILDS_PATH/$BUILD_FOLDER
|
||||
CURRENT_BUILD_FULL_PATH=$BUILDS_FULL_PATH/$BUILD_FOLDER
|
||||
|
||||
# TODO - Determine the file or folder based on BUILD_TARGET
|
||||
CUSTOM_BUILD_PATH=$BUILDS_FULL_PATH/$BUILD_FOLDER/$BUILD_TARGET
|
||||
|
||||
echo "Using build path \"$CURRENT_BUILD_PATH\"."
|
||||
echo "Using build path \"$BUILD_PATH\" to save file \"$BUILD_FILE\"."
|
||||
BUILD_PATH_FULL="$GITHUB_WORKSPACE/$BUILD_PATH"
|
||||
CUSTOM_BUILD_PATH="$BUILD_PATH_FULL/$BUILD_FILE"
|
||||
|
||||
#
|
||||
# Set the build method, must reference one of:
|
||||
@@ -71,25 +46,26 @@ if [ -z "$BUILD_METHOD" ]; then
|
||||
#
|
||||
echo "Using built-in build method."
|
||||
# Create Editor directory if it does not exist
|
||||
mkdir -p $UNITY_PROJECT_PATH/Assets/Editor/
|
||||
mkdir -p "$UNITY_PROJECT_PATH/Assets/Editor/"
|
||||
# Copy the build script of Unity Builder action
|
||||
cp -r /UnityBuilderAction/Assets/Editor $UNITY_PROJECT_PATH/Assets/Editor/
|
||||
cp -r "/UnityBuilderAction/Assets/Editor" "$UNITY_PROJECT_PATH/Assets/Editor/"
|
||||
# Set the Build method to that of UnityBuilder Action
|
||||
BUILD_METHOD="UnityBuilderAction.Builder.BuildProject"
|
||||
# Verify recursive paths
|
||||
ls -Ralph $UNITY_PROJECT_PATH/Assets/Editor/
|
||||
ls -Ralph "$UNITY_PROJECT_PATH/Assets/Editor/"
|
||||
#
|
||||
else
|
||||
# User has provided their own build method.
|
||||
# Assume they also bring their own script.
|
||||
#
|
||||
echo "User set build method to $BUILD_METHOD."
|
||||
echo "Using build method \"$BUILD_METHOD\"."
|
||||
#
|
||||
fi
|
||||
|
||||
# Set build method to execute as flag + argument
|
||||
EXECUTE_BUILD_METHOD="-executeMethod $BUILD_METHOD"
|
||||
|
||||
#
|
||||
# Display custom parameters
|
||||
#
|
||||
echo "Using custom parameters $CUSTOM_PARAMETERS."
|
||||
|
||||
# The build specification below may require Unity 2019.2.11f1 or later (not tested below).
|
||||
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
|
||||
@@ -98,25 +74,15 @@ EXECUTE_BUILD_METHOD="-executeMethod $BUILD_METHOD"
|
||||
# Build info
|
||||
#
|
||||
|
||||
echo ""
|
||||
echo "###########################"
|
||||
echo "# All builds dir #"
|
||||
echo "###########################"
|
||||
echo ""
|
||||
|
||||
echo "Creating \"$BUILDS_FULL_PATH\" if it does not exist."
|
||||
mkdir -p $BUILDS_FULL_PATH
|
||||
ls -alh $BUILDS_FULL_PATH
|
||||
|
||||
echo ""
|
||||
echo "###########################"
|
||||
echo "# Current build dir #"
|
||||
echo "###########################"
|
||||
echo ""
|
||||
|
||||
echo "Creating \"$CURRENT_BUILD_FULL_PATH\" if it does not exist."exist."
|
||||
mkdir -p $CURRENT_BUILD_FULL_PATH
|
||||
ls -alh $CURRENT_BUILD_FULL_PATH
|
||||
echo "Creating \"$BUILD_PATH_FULL\" if it does not exist."
|
||||
mkdir -p "$BUILD_PATH_FULL"
|
||||
ls -alh "$BUILD_PATH_FULL"
|
||||
|
||||
echo ""
|
||||
echo "###########################"
|
||||
@@ -142,7 +108,10 @@ xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
-buildTarget "$BUILD_TARGET" \
|
||||
-customBuildTarget "$BUILD_TARGET" \
|
||||
-customBuildPath "$CUSTOM_BUILD_PATH" \
|
||||
$EXECUTE_BUILD_METHOD
|
||||
-executeMethod "$BUILD_METHOD" \
|
||||
-versioning "$VERSIONING" \
|
||||
-version "$VERSION" \
|
||||
$CUSTOM_PARAMETERS
|
||||
|
||||
# Catch exit code
|
||||
BUILD_EXIT_CODE=$?
|
||||
@@ -154,6 +123,13 @@ else
|
||||
echo "Build failed, with exit code $BUILD_EXIT_CODE";
|
||||
fi
|
||||
|
||||
# Add permissions to make app runnable
|
||||
if [[ "$BUILD_TARGET" == "StandaloneOSX" ]]; then
|
||||
ADD_PERMISSIONS_PATH=$BUILD_PATH_FULL/StandaloneOSX.app/Contents/MacOS/*
|
||||
echo "Making the following path executable: $ADD_PERMISSIONS_PATH"
|
||||
chmod +x $ADD_PERMISSIONS_PATH
|
||||
fi
|
||||
|
||||
#
|
||||
# Results
|
||||
#
|
||||
@@ -164,4 +140,4 @@ echo "# Build directory #"
|
||||
echo "###########################"
|
||||
echo ""
|
||||
|
||||
ls -alh $CURRENT_BUILD_FULL_PATH
|
||||
ls -alh "$BUILD_PATH_FULL"
|
||||
16
action/steps/return_license.sh
Normal file
16
action/steps/return_license.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -n "$UNITY_SERIAL" ]]; then
|
||||
#
|
||||
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||
#
|
||||
# This will return the license that is currently in use.
|
||||
#
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
/opt/Unity/Editor/Unity \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile /dev/stdout \
|
||||
-quit \
|
||||
-returnlicense
|
||||
fi
|
||||
@@ -1,147 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityBuilderAction
|
||||
{
|
||||
static class Builder
|
||||
{
|
||||
private static string EOL = Environment.NewLine;
|
||||
|
||||
private static void ParseCommandLineArguments(out Dictionary<string, string> providedArguments)
|
||||
{
|
||||
providedArguments = new Dictionary<string, string>();
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
|
||||
Console.WriteLine(
|
||||
$"{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"# Parsing settings #{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"{EOL}"
|
||||
);
|
||||
|
||||
// Extract flags with optional values
|
||||
for (int current = 0, next = 1; current < args.Length; current++, next++) {
|
||||
// Parse flag
|
||||
bool isFlag = args[current].StartsWith("-");
|
||||
if (!isFlag) continue;
|
||||
string flag = args[current].TrimStart('-');
|
||||
|
||||
// Parse optional value
|
||||
bool flagHasValue = next < args.Length && !args[next].StartsWith("-");
|
||||
string value = flagHasValue ? args[next].TrimStart('-') : "";
|
||||
|
||||
// Assign
|
||||
Console.WriteLine($"Found flag \"{flag}\" with value \"{value}\".");
|
||||
providedArguments.Add(flag, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetValidatedOptions()
|
||||
{
|
||||
ParseCommandLineArguments(out var validatedOptions);
|
||||
|
||||
if (!validatedOptions.TryGetValue("projectPath", out var projectPath)) {
|
||||
Console.WriteLine("Missing argument -projectPath");
|
||||
EditorApplication.Exit(110);
|
||||
}
|
||||
|
||||
if (!validatedOptions.TryGetValue("buildTarget", out var buildTarget)) {
|
||||
Console.WriteLine("Missing argument -buildTarget");
|
||||
EditorApplication.Exit(120);
|
||||
}
|
||||
|
||||
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
|
||||
EditorApplication.Exit(121);
|
||||
}
|
||||
|
||||
if (!validatedOptions.TryGetValue("customBuildPath", out var customBuildPath)) {
|
||||
Console.WriteLine("Missing argument -customBuildPath");
|
||||
EditorApplication.Exit(130);
|
||||
}
|
||||
|
||||
string defaultCustomBuildName = "TestBuild";
|
||||
if (!validatedOptions.TryGetValue("customBuildName", out var customBuildName)) {
|
||||
Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}.");
|
||||
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
||||
}
|
||||
else if (customBuildName == "") {
|
||||
Console.WriteLine($"Invalid argument -customBuildName, defaulting to {defaultCustomBuildName}.");
|
||||
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
||||
}
|
||||
|
||||
return validatedOptions;
|
||||
}
|
||||
|
||||
public static void BuildProject()
|
||||
{
|
||||
// Gather values from args
|
||||
var options = GetValidatedOptions();
|
||||
|
||||
// Gather values from project
|
||||
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
|
||||
|
||||
// Define BuildPlayer Options
|
||||
var buildOptions = new BuildPlayerOptions {
|
||||
scenes = scenes,
|
||||
locationPathName = options["customBuildPath"],
|
||||
target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]),
|
||||
};
|
||||
|
||||
// Perform build
|
||||
BuildReport buildReport = BuildPipeline.BuildPlayer(buildOptions);
|
||||
|
||||
// Summary
|
||||
BuildSummary summary = buildReport.summary;
|
||||
ReportSummary(summary);
|
||||
|
||||
// Result
|
||||
BuildResult result = summary.result;
|
||||
ExitWithResult(result);
|
||||
}
|
||||
|
||||
private static void ReportSummary(BuildSummary summary)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"# Build results #{EOL}" +
|
||||
$"###########################{EOL}" +
|
||||
$"{EOL}" +
|
||||
$"Duration: {summary.totalTime.ToString()}{EOL}" +
|
||||
$"Warnings: {summary.totalWarnings.ToString()}{EOL}" +
|
||||
$"Errors: {summary.totalErrors.ToString()}{EOL}" +
|
||||
$"Size: {summary.totalSize.ToString()} bytes{EOL}" +
|
||||
$"{EOL}"
|
||||
);
|
||||
}
|
||||
|
||||
private static void ExitWithResult(BuildResult result)
|
||||
{
|
||||
if (result == BuildResult.Succeeded) {
|
||||
Console.WriteLine("Build succeeded!");
|
||||
EditorApplication.Exit(0);
|
||||
}
|
||||
|
||||
if (result == BuildResult.Failed) {
|
||||
Console.WriteLine("Build failed!");
|
||||
EditorApplication.Exit(101);
|
||||
}
|
||||
|
||||
if (result == BuildResult.Cancelled) {
|
||||
Console.WriteLine("Build cancelled!");
|
||||
EditorApplication.Exit(102);
|
||||
}
|
||||
|
||||
if (result == BuildResult.Unknown) {
|
||||
Console.WriteLine("Build result is unknown!");
|
||||
EditorApplication.Exit(103);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -n "$UNITY_LICENSE" ]]; then
|
||||
#
|
||||
# PERSONAL LICENSE MODE
|
||||
#
|
||||
# This will activate Unity, using a license file
|
||||
#
|
||||
# Note that this is the ONLY WAY for PERSONAL LICENSES in 2019.
|
||||
# * See for more details: https://gitlab.com/gableroux/unity3d-gitlab-ci-example/issues/5#note_72815478
|
||||
#
|
||||
# The license file can be acquired using `webbertakken/request-manual-activation-file` action.
|
||||
|
||||
# Set the license file path
|
||||
FILE_PATH=UnityLicenseFile.ulf
|
||||
|
||||
# Copy license file from Github variables
|
||||
echo "$UNITY_LICENSE" | tr -d '\r' > $FILE_PATH
|
||||
|
||||
#
|
||||
# Activate license
|
||||
#
|
||||
# This is expected to always exit with code 1 (both success and failure).
|
||||
#
|
||||
echo "Requesting activation"
|
||||
ACTIVATION_OUTPUT=$(xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
/opt/Unity/Editor/Unity \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile /dev/stdout \
|
||||
-quit \
|
||||
-manualLicenseFile $FILE_PATH)
|
||||
# Convert to exit code 0 by echoing the current exit code.
|
||||
echo $?
|
||||
# Exit code is now 0
|
||||
|
||||
# TODO - remove debugging
|
||||
echo $ACTIVATION_OUTPUT
|
||||
echo $ACTIVATION_OUTPUT | grep 'config is NOT valid, switching to default'
|
||||
echo $ACTIVATION_OUTPUT | grep 'config is NOT valid, switching to default' | wc -l
|
||||
|
||||
# TODO - Derive exit code by grepping success statement
|
||||
UNITY_EXIT_CODE=$(echo $ACTIVATION_OUTPUT | grep 'config is NOT valid, switching to default' | wc -l)
|
||||
|
||||
# Display information about the result
|
||||
if [ $UNITY_EXIT_CODE -eq 0 ]; then
|
||||
echo "Activation (personal) complete."
|
||||
else
|
||||
echo "Unclassified error occured while trying to activate (personal) license."
|
||||
echo "Exit code was: $UNITY_EXIT_CODE"
|
||||
fi
|
||||
|
||||
# Remove license file
|
||||
rm -f $FILE_PATH
|
||||
|
||||
# Exit with the code from the license verification step
|
||||
if [ $UNITY_EXIT_CODE -ne 0 ]; then
|
||||
exit $UNITY_EXIT_CODE
|
||||
fi
|
||||
|
||||
else
|
||||
#
|
||||
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||
#
|
||||
# This will activate unity, using the activating process.
|
||||
#
|
||||
# Note: This is the preferred way for PROFESSIONAL LICENSES.
|
||||
#
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
/opt/Unity/Editor/Unity \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile /dev/stdout \
|
||||
-quit \
|
||||
-serial "$UNITY_SERIAL" \
|
||||
-username "$UNITY_EMAIL" \
|
||||
-password "$UNITY_PASSWORD"
|
||||
|
||||
# Store the exit code from the verify command
|
||||
UNITY_EXIT_CODE=$?
|
||||
|
||||
# Display information about the result
|
||||
if [ $UNITY_EXIT_CODE -eq 0 ]; then
|
||||
echo "Activation (professional) complete."
|
||||
else
|
||||
echo "Unclassified error occured while trying to activate (professional) license."
|
||||
echo "Exit code was: $UNITY_EXIT_CODE"
|
||||
fi
|
||||
|
||||
# Exit with the code from the license verification step
|
||||
exit $UNITY_EXIT_CODE
|
||||
|
||||
fi
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||
#
|
||||
# This will return the license that is currently in use.
|
||||
#
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
/opt/Unity/Editor/Unity \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile /dev/stdout \
|
||||
-quit \
|
||||
-returnlicense
|
||||
39
codecov.yml
Normal file
39
codecov.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
codecov:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: '70...100'
|
||||
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
# basic
|
||||
target: auto
|
||||
threshold: 100% # don't require patch to have coverage per se
|
||||
base: auto
|
||||
|
||||
project:
|
||||
default:
|
||||
# basic
|
||||
target: auto
|
||||
threshold: 5%
|
||||
base: auto
|
||||
flags:
|
||||
- unit
|
||||
paths:
|
||||
- 'src'
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: yes
|
||||
loop: yes
|
||||
method: no
|
||||
macro: no
|
||||
|
||||
comment:
|
||||
layout: 'reach,diff,flags,tree'
|
||||
behavior: default
|
||||
require_changes: no
|
||||
@@ -5,4 +5,5 @@ module.exports = {
|
||||
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
|
||||
transform: { '^.+\\.(js|jsx)?$': 'babel-jest' },
|
||||
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
|
||||
setupFilesAfterEnv: ['./src/jest.setup.js'],
|
||||
};
|
||||
|
||||
47
package.json
47
package.json
@@ -2,44 +2,45 @@
|
||||
"name": "unity-builder",
|
||||
"version": "0.5.0",
|
||||
"description": "Build Unity projects for different platforms.",
|
||||
"main": "builder/index.js",
|
||||
"main": "action/index.js",
|
||||
"repository": "git@github.com:webbertakken/unity-builder.git",
|
||||
"author": "Webber <webber@takken.io>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "ncc build src --out builder --minify",
|
||||
"prebuild": "yarn",
|
||||
"build": "ncc build src --out action --minify",
|
||||
"lint": "prettier --check \"src/**/*.js\" && eslint src",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.0",
|
||||
"@actions/exec": "1.0.2",
|
||||
"@actions/github": "^2.0.0"
|
||||
"@actions/core": "^1.2.4",
|
||||
"@actions/exec": "1.0.4",
|
||||
"@actions/github": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.7.5",
|
||||
"@babel/core": "7.7.5",
|
||||
"@babel/preset-env": "7.7.7",
|
||||
"@zeit/ncc": "0.20.5",
|
||||
"babel-eslint": "10.0.3",
|
||||
"eslint": "6.7.2",
|
||||
"eslint-config-airbnb": "18.0.1",
|
||||
"eslint-config-prettier": "6.7.0",
|
||||
"eslint-plugin-flowtype": "4.5.2",
|
||||
"eslint-plugin-import": "2.19.1",
|
||||
"@babel/cli": "7.8.4",
|
||||
"@babel/core": "7.9.6",
|
||||
"@babel/preset-env": "7.9.6",
|
||||
"@zeit/ncc": "0.22.1",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-config-airbnb": "18.1.0",
|
||||
"eslint-config-prettier": "6.11.0",
|
||||
"eslint-plugin-flowtype": "4.7.0",
|
||||
"eslint-plugin-import": "2.20.2",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-prettier": "3.1.2",
|
||||
"eslint-plugin-react": "7.17.0",
|
||||
"eslint-plugin-unicorn": "14.0.1",
|
||||
"husky": "4.0.0-beta.5",
|
||||
"jest": "24.9.0",
|
||||
"lint-staged": "9.5.0",
|
||||
"eslint-plugin-prettier": "3.1.3",
|
||||
"eslint-plugin-react": "7.19.0",
|
||||
"eslint-plugin-unicorn": "19.0.1",
|
||||
"husky": "4.2.5",
|
||||
"jest": "25.5.3",
|
||||
"lint-staged": "10.2.2",
|
||||
"lodash-es": "4.17.15",
|
||||
"prettier": "1.19.1"
|
||||
"prettier": "2.0.5"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged && yarn build && git add builder/index.js"
|
||||
"pre-commit": "lint-staged && yarn build && git add action/index.js"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
|
||||
21
src/index.js
21
src/index.js
@@ -1,22 +1,23 @@
|
||||
import Action from './model/action';
|
||||
import Docker from './model/docker';
|
||||
import ImageTag from './model/image-tag';
|
||||
import Input from './model/input';
|
||||
import { Action, BuildParameters, Cache, Docker, ImageTag } from './model';
|
||||
|
||||
const core = require('@actions/core');
|
||||
|
||||
async function action() {
|
||||
Action.checkCompatibility();
|
||||
Cache.verify();
|
||||
|
||||
const { dockerfile, workspace, builderFolder } = Action;
|
||||
const { version, platform, projectPath, buildName, buildsPath, method } = Input.getFromUser();
|
||||
const { dockerfile, workspace, actionFolder } = Action;
|
||||
|
||||
const baseImage = new ImageTag({ version, platform });
|
||||
const builtImage = await Docker.build({ path: builderFolder, dockerfile, baseImage });
|
||||
const buildParameters = await BuildParameters.create();
|
||||
const baseImage = new ImageTag(buildParameters);
|
||||
|
||||
await Docker.run(builtImage, { workspace, platform, projectPath, buildName, buildsPath, method });
|
||||
// Build docker image
|
||||
const builtImage = await Docker.build({ path: actionFolder, dockerfile, baseImage });
|
||||
|
||||
// Run docker image
|
||||
await Docker.run(builtImage, { workspace, ...buildParameters });
|
||||
}
|
||||
|
||||
action().catch(error => {
|
||||
action().catch((error) => {
|
||||
core.setFailed(error.message);
|
||||
});
|
||||
|
||||
49
src/jest.setup.js
Normal file
49
src/jest.setup.js
Normal file
@@ -0,0 +1,49 @@
|
||||
expect.extend({
|
||||
toBeOfType(received, expectedType) {
|
||||
const type = typeof received;
|
||||
|
||||
const pass = type === expectedType;
|
||||
const message = () => `
|
||||
Expected value to be of type ${this.utils.printExpected(expectedType)},
|
||||
but received ${this.utils.printReceived(type)}`;
|
||||
|
||||
return {
|
||||
message,
|
||||
pass,
|
||||
};
|
||||
},
|
||||
|
||||
toBeEitherAFunctionOrAnObject(received) {
|
||||
const type = typeof received;
|
||||
|
||||
const pass = ['object', 'function'].includes(type);
|
||||
const message = () => `Expected a ${this.utils.printExpected('function')}
|
||||
or an ${this.utils.printExpected('object')},
|
||||
but received ${type}`;
|
||||
|
||||
return {
|
||||
message,
|
||||
pass,
|
||||
};
|
||||
},
|
||||
|
||||
toBeParsableToANumber(received) {
|
||||
let pass = false;
|
||||
let errorMessage = '';
|
||||
|
||||
try {
|
||||
Number.parseInt(received, 10);
|
||||
pass = true;
|
||||
} catch (error) {
|
||||
errorMessage = error;
|
||||
}
|
||||
|
||||
const message = () => `Expected ${this.utils.printExpected(received)} to be parsable as a number
|
||||
, but received error: ${this.utils.printReceived(errorMessage)}.`;
|
||||
|
||||
return {
|
||||
message,
|
||||
pass,
|
||||
};
|
||||
},
|
||||
});
|
||||
17
src/model/__mocks__/input.js
Normal file
17
src/model/__mocks__/input.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Import this named export into your test file:
|
||||
import Platform from '../platform';
|
||||
|
||||
export const mockGetFromUser = jest.fn().mockResolvedValue({
|
||||
version: '',
|
||||
targetPlatform: Platform.types.Test,
|
||||
projectPath: '.',
|
||||
buildName: Platform.types.Test,
|
||||
buildsPath: 'build',
|
||||
buildMethod: undefined,
|
||||
buildVersion: '1.3.37',
|
||||
customParameters: '',
|
||||
});
|
||||
|
||||
export default {
|
||||
getFromUser: mockGetFromUser,
|
||||
};
|
||||
38
src/model/__mocks__/versioning.js
Normal file
38
src/model/__mocks__/versioning.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
|
||||
// Import these named export into your test file:
|
||||
export const mockProjectPath = jest.fn().mockResolvedValue('mockProjectPath');
|
||||
export const mockIsDirtyAllowed = jest.fn().mockResolvedValue(false);
|
||||
export const mockBranch = jest.fn().mockResolvedValue('mockBranch');
|
||||
export const mockHeadRef = jest.fn().mockResolvedValue('mockHeadRef');
|
||||
export const mockRef = jest.fn().mockResolvedValue('mockRef');
|
||||
export const mockDetermineVersion = jest.fn().mockResolvedValue('1.2.3');
|
||||
export const mockGenerateSemanticVersion = jest.fn().mockResolvedValue('2.3.4');
|
||||
export const mockGenerateTagVersion = jest.fn().mockResolvedValue('1.0');
|
||||
export const mockParseSemanticVersion = jest.fn().mockResolvedValue({});
|
||||
export const mockFetch = jest.fn().mockImplementation(() => {});
|
||||
export const mockGetVersionDescription = jest.fn().mockResolvedValue('1.2-3-g12345678-dirty');
|
||||
export const mockIsDirty = jest.fn().mockResolvedValue(false);
|
||||
export const mockGetTag = jest.fn().mockResolvedValue('v1.0');
|
||||
export const mockHasAnyVersionTags = jest.fn().mockResolvedValue(true);
|
||||
export const mockGetTotalNumberOfCommits = jest.fn().mockResolvedValue(3);
|
||||
export const mockGit = jest.fn().mockImplementation(() => {});
|
||||
|
||||
export default {
|
||||
projectPath: mockProjectPath,
|
||||
isDirtyAllowed: mockIsDirtyAllowed,
|
||||
branch: mockBranch,
|
||||
headRef: mockHeadRef,
|
||||
ref: mockRef,
|
||||
determineVersion: mockDetermineVersion,
|
||||
generateSemanticVersion: mockGenerateSemanticVersion,
|
||||
generateTagVersion: mockGenerateTagVersion,
|
||||
parseSemanticVersion: mockParseSemanticVersion,
|
||||
fetch: mockFetch,
|
||||
getVersionDescription: mockGetVersionDescription,
|
||||
isDirty: mockIsDirty,
|
||||
getTag: mockGetTag,
|
||||
hasAnyVersionTags: mockHasAnyVersionTags,
|
||||
getTotalNumberOfCommits: mockGetTotalNumberOfCommits,
|
||||
git: mockGit,
|
||||
};
|
||||
7
src/model/__snapshots__/versioning.test.js.snap
Normal file
7
src/model/__snapshots__/versioning.test.js.snap
Normal file
@@ -0,0 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Versioning determineVersion throws for invalid strategy 0 1`] = `"Versioning strategy should be one of None, Semantic, Tag, Custom."`;
|
||||
|
||||
exports[`Versioning determineVersion throws for invalid strategy somethingRandom 1`] = `"Versioning strategy should be one of None, Semantic, Tag, Custom."`;
|
||||
|
||||
exports[`Versioning determineVersion throws for invalid strategy undefined 1`] = `"Versioning strategy should be one of None, Semantic, Tag, Custom."`;
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'path';
|
||||
|
||||
export default class Action {
|
||||
class Action {
|
||||
static get supportedPlatforms() {
|
||||
return ['linux'];
|
||||
}
|
||||
@@ -25,12 +25,12 @@ export default class Action {
|
||||
return path.dirname(path.dirname(__filename));
|
||||
}
|
||||
|
||||
static get builderFolder() {
|
||||
return `${Action.rootFolder}/builder`;
|
||||
static get actionFolder() {
|
||||
return `${Action.rootFolder}/action`;
|
||||
}
|
||||
|
||||
static get dockerfile() {
|
||||
return `${Action.builderFolder}/Dockerfile`;
|
||||
return `${Action.actionFolder}/Dockerfile`;
|
||||
}
|
||||
|
||||
static get workspace() {
|
||||
@@ -44,3 +44,5 @@ export default class Action {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Action;
|
||||
|
||||
@@ -20,11 +20,11 @@ describe('Action', () => {
|
||||
expect(fs.existsSync(rootFolder)).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('returns the builder folder', () => {
|
||||
const { builderFolder } = Action;
|
||||
it('returns the action folder', () => {
|
||||
const { actionFolder } = Action;
|
||||
|
||||
expect(path.basename(builderFolder)).toStrictEqual('builder');
|
||||
expect(fs.existsSync(builderFolder)).toStrictEqual(true);
|
||||
expect(path.basename(actionFolder)).toStrictEqual('action');
|
||||
expect(fs.existsSync(actionFolder)).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('returns the docker file', () => {
|
||||
|
||||
39
src/model/build-parameters.js
Normal file
39
src/model/build-parameters.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import Input from './input';
|
||||
import Platform from './platform';
|
||||
import Versioning from './versioning';
|
||||
|
||||
class BuildParameters {
|
||||
static async create() {
|
||||
const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform);
|
||||
const buildVersion = await Versioning.determineVersion(
|
||||
Input.versioningStrategy,
|
||||
Input.specifiedVersion,
|
||||
);
|
||||
|
||||
return {
|
||||
version: Input.unityVersion,
|
||||
platform: Input.targetPlatform,
|
||||
projectPath: Input.projectPath,
|
||||
buildName: Input.buildName,
|
||||
buildPath: `${Input.buildsPath}/${Input.targetPlatform}`,
|
||||
buildFile,
|
||||
buildMethod: Input.buildMethod,
|
||||
buildVersion,
|
||||
customParameters: Input.customParameters,
|
||||
};
|
||||
}
|
||||
|
||||
static parseBuildFile(filename, platform) {
|
||||
if (Platform.isWindows(platform)) {
|
||||
return `${filename}.exe`;
|
||||
}
|
||||
|
||||
if (Platform.isAndroid(platform)) {
|
||||
return `${filename}.apk`;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
export default BuildParameters;
|
||||
111
src/model/build-parameters.test.js
Normal file
111
src/model/build-parameters.test.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import Versioning from './versioning';
|
||||
import BuildParameters from './build-parameters';
|
||||
import Input from './input';
|
||||
import Platform from './platform';
|
||||
|
||||
const determineVersion = jest
|
||||
.spyOn(Versioning, 'determineVersion')
|
||||
.mockImplementation(() => '1.3.37');
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('BuildParameters', () => {
|
||||
describe('create', () => {
|
||||
it('does not throw', async () => {
|
||||
await expect(BuildParameters.create()).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('determines the version only once', async () => {
|
||||
await BuildParameters.create();
|
||||
expect(determineVersion).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns the version', async () => {
|
||||
const mockValue = 'someVersion';
|
||||
jest.spyOn(Input, 'unityVersion', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ version: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the platform', async () => {
|
||||
const mockValue = 'somePlatform';
|
||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ platform: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the project path', async () => {
|
||||
const mockValue = 'path/to/project';
|
||||
jest.spyOn(Input, 'projectPath', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ projectPath: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the build name', async () => {
|
||||
const mockValue = 'someBuildName';
|
||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildName: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the build path', async () => {
|
||||
const mockPath = 'somePath';
|
||||
const mockPlatform = 'somePlatform';
|
||||
const expectedBuildPath = `${mockPath}/${mockPlatform}`;
|
||||
jest.spyOn(Input, 'buildsPath', 'get').mockReturnValue(mockPath);
|
||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockPlatform);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildPath: expectedBuildPath }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the build file', async () => {
|
||||
const mockValue = 'someBuildName';
|
||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildFile: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
test.each([Platform.types.StandaloneWindows, Platform.types.StandaloneWindows64])(
|
||||
'appends exe for %s',
|
||||
async (targetPlatform) => {
|
||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildFile: `${targetPlatform}.exe` }),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.each([Platform.types.Android])('appends apk for %s', async (targetPlatform) => {
|
||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildFile: `${targetPlatform}.apk` }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the build method', async () => {
|
||||
const mockValue = 'Namespace.ClassName.BuildMethod';
|
||||
jest.spyOn(Input, 'buildMethod', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildMethod: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the custom parameters', async () => {
|
||||
const mockValue = '-profile SomeProfile -someBoolean -someValue exampleValue';
|
||||
jest.spyOn(Input, 'customParameters', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ customParameters: mockValue }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
26
src/model/cache.js
Normal file
26
src/model/cache.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as core from '@actions/core';
|
||||
import fs from 'fs';
|
||||
import Action from './action';
|
||||
import Project from './project';
|
||||
|
||||
class Cache {
|
||||
static verify() {
|
||||
if (!fs.existsSync(Project.libraryFolder)) {
|
||||
this.notifyAboutCachingPossibility();
|
||||
}
|
||||
}
|
||||
|
||||
static notifyAboutCachingPossibility() {
|
||||
if (Action.isRunningLocally) {
|
||||
return;
|
||||
}
|
||||
|
||||
core.warning(`
|
||||
Library folder does not exist.
|
||||
Consider setting up caching to speed up your workflow,
|
||||
if this is not your first build.
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
export default Cache;
|
||||
11
src/model/cache.test.js
Normal file
11
src/model/cache.test.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import Cache from './cache';
|
||||
|
||||
jest.mock('./input');
|
||||
|
||||
describe('Cache', () => {
|
||||
describe('Verification', () => {
|
||||
it('does not throw', () => {
|
||||
expect(() => Cache.verify()).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { exec } from '@actions/exec';
|
||||
import ImageTag from './image-tag';
|
||||
|
||||
export default class Docker {
|
||||
class Docker {
|
||||
static async build(buildParameters, silent = false) {
|
||||
const { path, dockerfile, baseImage } = buildParameters;
|
||||
const { version, platform } = baseImage;
|
||||
@@ -12,25 +12,42 @@ export default class Docker {
|
||||
--build-arg IMAGE=${baseImage} \
|
||||
--tag ${tag}`;
|
||||
|
||||
await exec(command, null, { silent });
|
||||
await exec(command, undefined, { silent });
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static async run(image, parameters, silent = false) {
|
||||
const { workspace, platform, projectPath, buildName, buildsPath, method } = parameters;
|
||||
const { version } = image;
|
||||
const {
|
||||
version,
|
||||
workspace,
|
||||
platform,
|
||||
projectPath,
|
||||
buildName,
|
||||
buildPath,
|
||||
buildFile,
|
||||
buildMethod,
|
||||
buildVersion,
|
||||
customParameters,
|
||||
} = parameters;
|
||||
|
||||
const command = `docker run \
|
||||
--workdir /github/workspace \
|
||||
--rm \
|
||||
--env UNITY_LICENSE \
|
||||
--env UNITY_VERSION=${version} \
|
||||
--env PROJECT_PATH=${projectPath} \
|
||||
--env BUILD_TARGET=${platform} \
|
||||
--env BUILD_NAME=${buildName} \
|
||||
--env BUILDS_PATH=${buildsPath} \
|
||||
--env BUILD_METHOD=${method} \
|
||||
--env UNITY_LICENSE_FILE \
|
||||
--env UNITY_EMAIL \
|
||||
--env UNITY_PASSWORD \
|
||||
--env UNITY_SERIAL \
|
||||
--env UNITY_VERSION="${version}" \
|
||||
--env PROJECT_PATH="${projectPath}" \
|
||||
--env BUILD_TARGET="${platform}" \
|
||||
--env BUILD_NAME="${buildName}" \
|
||||
--env BUILD_PATH="${buildPath}" \
|
||||
--env BUILD_FILE="${buildFile}" \
|
||||
--env BUILD_METHOD="${buildMethod}" \
|
||||
--env VERSION="${buildVersion}" \
|
||||
--env CUSTOM_PARAMETERS="${customParameters}" \
|
||||
--env HOME=/github/home \
|
||||
--env GITHUB_REF \
|
||||
--env GITHUB_SHA \
|
||||
@@ -53,6 +70,8 @@ export default class Docker {
|
||||
--volume "${workspace}":"/github/workspace" \
|
||||
${image}`;
|
||||
|
||||
await exec(command, null, { silent });
|
||||
await exec(command, undefined, { silent });
|
||||
}
|
||||
}
|
||||
|
||||
export default Docker;
|
||||
|
||||
@@ -4,7 +4,7 @@ import ImageTag from './image-tag';
|
||||
|
||||
describe('Docker', () => {
|
||||
it('builds', async () => {
|
||||
const path = Action.builderFolder;
|
||||
const path = Action.actionFolder;
|
||||
const dockerfile = `${path}/Dockerfile`;
|
||||
const baseImage = new ImageTag({
|
||||
repository: '',
|
||||
|
||||
8
src/model/error/command-execution-error.js
Normal file
8
src/model/error/command-execution-error.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class CommandExecutionError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'CommandExecutionError';
|
||||
}
|
||||
}
|
||||
|
||||
export default CommandExecutionError;
|
||||
14
src/model/error/command-execution-error.test.js
Normal file
14
src/model/error/command-execution-error.test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import CommandExecutionError from './command-execution-error';
|
||||
|
||||
describe('CommandExecutionError', () => {
|
||||
it('instantiates', () => {
|
||||
expect(() => new CommandExecutionError()).not.toThrow();
|
||||
});
|
||||
|
||||
test.each([1, 'one', { name: '!' }])('Displays title %s', (message) => {
|
||||
const error = new CommandExecutionError(message);
|
||||
|
||||
expect(error.name).toStrictEqual('CommandExecutionError');
|
||||
expect(error.message).toStrictEqual(message.toString());
|
||||
});
|
||||
});
|
||||
8
src/model/error/not-implemented-exception.js
Normal file
8
src/model/error/not-implemented-exception.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class NotImplementedException extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'NotImplementedException';
|
||||
}
|
||||
}
|
||||
|
||||
export default NotImplementedException;
|
||||
14
src/model/error/not-implemented-exception.test.js
Normal file
14
src/model/error/not-implemented-exception.test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import NotImplementedException from './not-implemented-exception';
|
||||
|
||||
describe('NotImplementedException', () => {
|
||||
it('instantiates', () => {
|
||||
expect(() => new NotImplementedException()).not.toThrow();
|
||||
});
|
||||
|
||||
test.each([1, 'one', { name: '!' }])('Displays title %s', (message) => {
|
||||
const error = new NotImplementedException(message);
|
||||
|
||||
expect(error.name).toStrictEqual('NotImplementedException');
|
||||
expect(error.message).toStrictEqual(message.toString());
|
||||
});
|
||||
});
|
||||
8
src/model/error/validation-error.js
Normal file
8
src/model/error/validation-error.js
Normal file
@@ -0,0 +1,8 @@
|
||||
class ValidationError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'ValidationError';
|
||||
}
|
||||
}
|
||||
|
||||
export default ValidationError;
|
||||
14
src/model/error/validation-error.test.js
Normal file
14
src/model/error/validation-error.test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import ValidationError from './validation-error';
|
||||
|
||||
describe('ValidationError', () => {
|
||||
it('instantiates', () => {
|
||||
expect(() => new ValidationError()).not.toThrow();
|
||||
});
|
||||
|
||||
test.each([1, 'one', { name: '!' }])('Displays title %s', (message) => {
|
||||
const error = new ValidationError(message);
|
||||
|
||||
expect(error.name).toStrictEqual('ValidationError');
|
||||
expect(error.message).toStrictEqual(message.toString());
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { has, get, trimEnd, trimStart } from 'lodash-es';
|
||||
import Platform from './platform';
|
||||
|
||||
export default class ImageTag {
|
||||
class ImageTag {
|
||||
constructor(imageProperties) {
|
||||
const {
|
||||
repository = 'gableroux',
|
||||
@@ -13,14 +14,14 @@ export default class ImageTag {
|
||||
throw new Error(`Invalid version "${version}".`);
|
||||
}
|
||||
|
||||
if (!has(ImageTag.targetPlatformToBuilderPlatformMap, platform)) {
|
||||
if (!has(ImageTag.targetPlatformToImageSuffixMap, platform)) {
|
||||
throw new Error(`Platform "${platform}" is currently not supported.`);
|
||||
}
|
||||
|
||||
const builderPlatform = get(
|
||||
ImageTag.targetPlatformToBuilderPlatformMap,
|
||||
ImageTag.targetPlatformToImageSuffixMap,
|
||||
platform,
|
||||
ImageTag.builderPlatforms.generic,
|
||||
ImageTag.imageSuffixes.generic,
|
||||
);
|
||||
|
||||
Object.assign(this, { repository, name, version, platform, builderPlatform });
|
||||
@@ -30,7 +31,7 @@ export default class ImageTag {
|
||||
return /^20\d{2}\.\d\.\w{3,4}|3$/;
|
||||
}
|
||||
|
||||
static get builderPlatforms() {
|
||||
static get imageSuffixes() {
|
||||
return {
|
||||
generic: '',
|
||||
webgl: 'webgl',
|
||||
@@ -42,31 +43,31 @@ export default class ImageTag {
|
||||
};
|
||||
}
|
||||
|
||||
static get targetPlatformToBuilderPlatformMap() {
|
||||
const { generic, webgl, mac, windows, android, ios, facebook } = ImageTag.builderPlatforms;
|
||||
static get targetPlatformToImageSuffixMap() {
|
||||
const { generic, webgl, mac, windows, android, ios, facebook } = ImageTag.imageSuffixes;
|
||||
|
||||
// @see: https://docs.unity3d.com/ScriptReference/BuildTarget.html
|
||||
return {
|
||||
StandaloneOSX: mac,
|
||||
StandaloneWindows: windows,
|
||||
StandaloneWindows64: windows,
|
||||
StandaloneLinux64: windows,
|
||||
iOS: ios,
|
||||
Android: android,
|
||||
WebGL: webgl,
|
||||
WSAPlayer: windows,
|
||||
PS4: windows,
|
||||
XboxOne: windows,
|
||||
tvOS: windows,
|
||||
Switch: windows,
|
||||
[Platform.types.StandaloneOSX]: mac,
|
||||
[Platform.types.StandaloneWindows]: windows,
|
||||
[Platform.types.StandaloneWindows64]: windows,
|
||||
[Platform.types.StandaloneLinux64]: windows,
|
||||
[Platform.types.iOS]: ios,
|
||||
[Platform.types.Android]: android,
|
||||
[Platform.types.WebGL]: webgl,
|
||||
[Platform.types.WSAPlayer]: windows,
|
||||
[Platform.types.PS4]: windows,
|
||||
[Platform.types.XboxOne]: windows,
|
||||
[Platform.types.tvOS]: windows,
|
||||
[Platform.types.Switch]: windows,
|
||||
// Unsupported
|
||||
Lumin: windows,
|
||||
BJM: windows,
|
||||
Stadia: windows,
|
||||
Facebook: facebook,
|
||||
NoTarget: generic,
|
||||
[Platform.types.Lumin]: windows,
|
||||
[Platform.types.BJM]: windows,
|
||||
[Platform.types.Stadia]: windows,
|
||||
[Platform.types.Facebook]: facebook,
|
||||
[Platform.types.NoTarget]: generic,
|
||||
// Test specific
|
||||
Test: generic,
|
||||
[Platform.types.Test]: generic,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,3 +85,5 @@ export default class ImageTag {
|
||||
return `${image}:${tag}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default ImageTag;
|
||||
|
||||
@@ -31,16 +31,16 @@ describe('UnityImageVersion', () => {
|
||||
expect(image.builderPlatform).toStrictEqual(some.builderPlatform);
|
||||
});
|
||||
|
||||
test.each(['2000.0.0f0', '2011.1.11f1'])('accepts %p version format', version => {
|
||||
test.each(['2000.0.0f0', '2011.1.11f1'])('accepts %p version format', (version) => {
|
||||
expect(() => new ImageTag({ version, platform: some.platform })).not.toThrow();
|
||||
});
|
||||
|
||||
test.each(['some version', '', 1, null])('throws for incorrect versions %p', version => {
|
||||
test.each(['some version', '', 1])('throws for incorrect versions %p', (version) => {
|
||||
const { platform } = some;
|
||||
expect(() => new ImageTag({ version, platform })).toThrow();
|
||||
});
|
||||
|
||||
test.each([undefined, 'nonExisting'])('throws for unsupported target %p', platform => {
|
||||
test.each([undefined, 'nonExisting'])('throws for unsupported target %p', (platform) => {
|
||||
expect(() => new ImageTag({ platform })).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
23
src/model/index.js
Normal file
23
src/model/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Action from './action';
|
||||
import BuildParameters from './build-parameters';
|
||||
import Cache from './cache';
|
||||
import Docker from './docker';
|
||||
import Input from './input';
|
||||
import ImageTag from './image-tag';
|
||||
import Platform from './platform';
|
||||
import Project from './project';
|
||||
import Unity from './unity';
|
||||
import Versioning from './versioning';
|
||||
|
||||
export {
|
||||
Action,
|
||||
BuildParameters,
|
||||
Cache,
|
||||
Docker,
|
||||
Input,
|
||||
ImageTag,
|
||||
Platform,
|
||||
Project,
|
||||
Unity,
|
||||
Versioning,
|
||||
};
|
||||
17
src/model/index.test.js
Normal file
17
src/model/index.test.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as Index from '.';
|
||||
|
||||
describe('Index', () => {
|
||||
test.each([
|
||||
'Action',
|
||||
'BuildParameters',
|
||||
'Cache',
|
||||
'Docker',
|
||||
'ImageTag',
|
||||
'Input',
|
||||
'Platform',
|
||||
'Project',
|
||||
'Unity',
|
||||
])('exports %s', (exportedModule) => {
|
||||
expect(Index[exportedModule]).toBeEitherAFunctionOrAnObject();
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,55 @@
|
||||
import Platform from './platform';
|
||||
|
||||
const core = require('@actions/core');
|
||||
|
||||
export default class Input {
|
||||
static getFromUser() {
|
||||
// Input variables specified in workflows using "with" prop.
|
||||
const version = core.getInput('unityVersion');
|
||||
const platform = core.getInput('targetPlatform');
|
||||
const projectPath = core.getInput('projectPath');
|
||||
const buildName = core.getInput('buildName');
|
||||
const buildsPath = core.getInput('buildsPath');
|
||||
const buildMethod = core.getInput('buildMethod');
|
||||
/**
|
||||
* Input variables specified in workflows using "with" prop.
|
||||
*
|
||||
* Note that input is always passed as a string, even booleans.
|
||||
*/
|
||||
class Input {
|
||||
static get unityVersion() {
|
||||
return core.getInput('unityVersion');
|
||||
}
|
||||
|
||||
return {
|
||||
version,
|
||||
platform,
|
||||
projectPath,
|
||||
buildName,
|
||||
buildsPath,
|
||||
method: buildMethod,
|
||||
};
|
||||
static get targetPlatform() {
|
||||
return core.getInput('targetPlatform') || Platform.default;
|
||||
}
|
||||
|
||||
static get projectPath() {
|
||||
const rawProjectPath = core.getInput('projectPath') || '.';
|
||||
return rawProjectPath.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
static get buildName() {
|
||||
return core.getInput('buildName') || this.targetPlatform;
|
||||
}
|
||||
|
||||
static get buildsPath() {
|
||||
return core.getInput('buildsPath') || 'build';
|
||||
}
|
||||
|
||||
static get buildMethod() {
|
||||
return core.getInput('buildMethod'); // processed in docker file
|
||||
}
|
||||
|
||||
static get versioningStrategy() {
|
||||
return core.getInput('versioning') || 'Semantic';
|
||||
}
|
||||
|
||||
static get specifiedVersion() {
|
||||
return core.getInput('version') || '';
|
||||
}
|
||||
|
||||
static get allowDirtyBuild() {
|
||||
const input = core.getInput('allowDirtyBuild') || 'false';
|
||||
|
||||
return input === 'true' ? 'true' : 'false';
|
||||
}
|
||||
|
||||
static get customParameters() {
|
||||
return core.getInput('customParameters') || '';
|
||||
}
|
||||
}
|
||||
|
||||
export default Input;
|
||||
|
||||
@@ -1,9 +1,151 @@
|
||||
import * as core from '@actions/core';
|
||||
|
||||
import Input from './input';
|
||||
import Platform from './platform';
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('Input', () => {
|
||||
describe('getFromUser', () => {
|
||||
it('does not throw', () => {
|
||||
expect(() => Input.getFromUser()).not.toThrow();
|
||||
describe('unityVersion', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.unityVersion).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = '2020.4.99f9';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.unityVersion).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('targetPlatform', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.targetPlatform).toStrictEqual(Platform.default);
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'Android';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.targetPlatform).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('projectPath', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.projectPath).toStrictEqual('.');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'customProjectPath';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.projectPath).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildName', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.buildName).toStrictEqual(Input.targetPlatform);
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'Build';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.buildName).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('takes special characters as input', () => {
|
||||
const mockValue = '1ßúëld2';
|
||||
jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.buildName).toStrictEqual(mockValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildsPath', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.buildsPath).toStrictEqual('build');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'customBuildsPath';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.buildsPath).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildMethod', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.buildMethod).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'Namespace.ClassName.Method';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.buildMethod).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('versioningStrategy', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.versioningStrategy).toStrictEqual('Semantic');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'Anything';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.versioningStrategy).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('specifiedVersion', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.specifiedVersion).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = '1.33.7';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.specifiedVersion).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allowDirtyBuild', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.allowDirtyBuild).toStrictEqual('false');
|
||||
});
|
||||
|
||||
it('returns true when string true is passed', () => {
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue('true');
|
||||
expect(Input.allowDirtyBuild).toStrictEqual('true');
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns false when string false is passed', () => {
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue('false');
|
||||
expect(Input.allowDirtyBuild).toStrictEqual('false');
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('customParameters', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.customParameters).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = '-imAFlag';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.customParameters).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
51
src/model/platform.js
Normal file
51
src/model/platform.js
Normal file
@@ -0,0 +1,51 @@
|
||||
class Platform {
|
||||
static get default() {
|
||||
return Platform.types.StandaloneWindows64;
|
||||
}
|
||||
|
||||
static get types() {
|
||||
return {
|
||||
StandaloneOSX: 'StandaloneOSX',
|
||||
StandaloneWindows: 'StandaloneWindows',
|
||||
StandaloneWindows64: 'StandaloneWindows64',
|
||||
StandaloneLinux64: 'StandaloneLinux64',
|
||||
iOS: 'iOS',
|
||||
Android: 'Android',
|
||||
WebGL: 'WebGL',
|
||||
WSAPlayer: 'WSAPlayer',
|
||||
PS4: 'PS4',
|
||||
XboxOne: 'XboxOne',
|
||||
tvOS: 'tvOS',
|
||||
Switch: 'Switch',
|
||||
// Unsupported
|
||||
Lumin: 'Lumin',
|
||||
BJM: 'BJM',
|
||||
Stadia: 'Stadia',
|
||||
Facebook: 'Facebook',
|
||||
NoTarget: 'NoTarget',
|
||||
// Test specific
|
||||
Test: 'Test',
|
||||
};
|
||||
}
|
||||
|
||||
static isWindows(platform) {
|
||||
switch (platform) {
|
||||
case Platform.types.StandaloneWindows:
|
||||
case Platform.types.StandaloneWindows64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static isAndroid(platform) {
|
||||
switch (platform) {
|
||||
case Platform.types.Android:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Platform;
|
||||
37
src/model/platform.test.js
Normal file
37
src/model/platform.test.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import Platform from './platform';
|
||||
|
||||
describe('Platform', () => {
|
||||
describe('default', () => {
|
||||
it('does not throw', () => {
|
||||
expect(() => Platform.default).not.toThrow();
|
||||
});
|
||||
|
||||
it('returns a string', () => {
|
||||
expect(typeof Platform.default).toStrictEqual('string');
|
||||
});
|
||||
|
||||
it('returns a platform', () => {
|
||||
expect(Object.values(Platform.types)).toContain(Platform.default);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isWindows', () => {
|
||||
it('returns true for windows', () => {
|
||||
expect(Platform.isWindows(Platform.types.StandaloneWindows64)).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('returns false for MacOS', () => {
|
||||
expect(Platform.isWindows(Platform.types.StandaloneOSX)).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAndroid', () => {
|
||||
it('returns true for Android', () => {
|
||||
expect(Platform.isAndroid(Platform.types.Android)).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('returns false for Windows', () => {
|
||||
expect(Platform.isAndroid(Platform.types.StandaloneWindows64)).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
23
src/model/project.js
Normal file
23
src/model/project.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Input from './input';
|
||||
import Unity from './unity';
|
||||
import Action from './action';
|
||||
|
||||
class Project {
|
||||
static get relativePath() {
|
||||
const { projectPath } = Input;
|
||||
|
||||
return `${projectPath}`;
|
||||
}
|
||||
|
||||
static get absolutePath() {
|
||||
const { workspace } = Action;
|
||||
|
||||
return `${workspace}/${this.relativePath}`;
|
||||
}
|
||||
|
||||
static get libraryFolder() {
|
||||
return `${this.relativePath}/${Unity.libraryFolder}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default Project;
|
||||
35
src/model/project.test.js
Normal file
35
src/model/project.test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import Project from './project';
|
||||
|
||||
jest.mock('./input');
|
||||
|
||||
describe('Platform', () => {
|
||||
describe('relativePath', () => {
|
||||
it('does not throw', () => {
|
||||
expect(() => Project.relativePath).not.toThrow();
|
||||
});
|
||||
|
||||
it('returns a string', () => {
|
||||
expect(typeof Project.relativePath).toStrictEqual('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('absolutePath', () => {
|
||||
it('does not throw', () => {
|
||||
expect(() => Project.absolutePath).not.toThrow();
|
||||
});
|
||||
|
||||
it('returns a string', () => {
|
||||
expect(typeof Project.absolutePath).toStrictEqual('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('libraryFolder', () => {
|
||||
it('does not throw', () => {
|
||||
expect(() => Project.libraryFolder).not.toThrow();
|
||||
});
|
||||
|
||||
it('returns a string', () => {
|
||||
expect(typeof Project.libraryFolder).toStrictEqual('string');
|
||||
});
|
||||
});
|
||||
});
|
||||
62
src/model/system.js
Normal file
62
src/model/system.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import * as core from '@actions/core';
|
||||
import { exec } from '@actions/exec';
|
||||
|
||||
class System {
|
||||
static async run(command, arguments_, options) {
|
||||
let result = '';
|
||||
let error = '';
|
||||
let debug = '';
|
||||
|
||||
const listeners = {
|
||||
stdout: (dataBuffer) => {
|
||||
result += dataBuffer.toString();
|
||||
},
|
||||
stderr: (dataBuffer) => {
|
||||
error += dataBuffer.toString();
|
||||
},
|
||||
debug: (dataString) => {
|
||||
debug += dataString.toString();
|
||||
},
|
||||
};
|
||||
|
||||
const showOutput = () => {
|
||||
if (debug !== '') {
|
||||
core.debug(debug);
|
||||
}
|
||||
|
||||
if (result !== '') {
|
||||
core.info(result);
|
||||
}
|
||||
|
||||
if (error !== '') {
|
||||
core.warning(error);
|
||||
}
|
||||
};
|
||||
|
||||
const throwContextualError = (message) => {
|
||||
let commandAsString = command;
|
||||
if (Array.isArray(arguments_)) {
|
||||
commandAsString += ` ${arguments_.join(' ')}`;
|
||||
} else if (typeof arguments_ === 'string') {
|
||||
commandAsString += ` ${arguments_}`;
|
||||
}
|
||||
|
||||
throw new Error(`Failed to run "${commandAsString}".\n ${message}`);
|
||||
};
|
||||
|
||||
try {
|
||||
const exitCode = await exec(command, arguments_, { silent: true, listeners, ...options });
|
||||
showOutput();
|
||||
if (exitCode !== 0) {
|
||||
throwContextualError(`Command returned non-zero exit code.\nError: ${error}`);
|
||||
}
|
||||
} catch (inCommandError) {
|
||||
showOutput();
|
||||
throwContextualError(`In-command error caught: ${inCommandError}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default System;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user