Compare commits

...

86 Commits
v0.9 ... v0.14

Author SHA1 Message Date
Webber
34e4b86924 Fix case where no tags does not trigger false 2020-05-01 20:32:41 +02:00
Webber
e9514b4e37 Stop hammering patches 🔨 2020-05-01 17:55:27 +02:00
Webber
d4d91e75c3 Configure codecov threshold 2020-05-01 17:55:27 +02:00
Webber
2085eff264 Add missing test for ref getters 2020-05-01 17:55:27 +02:00
Webber
98a1b078fc Update styles to latest unicorn 🦄 and prettier 🦋 2020-05-01 17:55:27 +02:00
Webber
afef854ea0 update packages to latest 2020-05-01 17:55:27 +02:00
Webber
a0a5de2a83 Cover all versioning paths 2020-05-01 16:15:56 +02:00
Webber
c146049b33 Mock all output methods from core 2020-05-01 16:15:56 +02:00
Webber
e46399169f Add tests for System model 2020-05-01 16:15:56 +02:00
Webber
b41026b36e Introduce smart fetching, based on type of local repo. 2020-05-01 16:15:56 +02:00
Webber
cd1d215dfa Remove unshallow fetch 2020-05-01 16:15:56 +02:00
Webber
d01e844eea Fix versioning for push event. 2020-05-01 16:15:56 +02:00
Webber
40564afbaf Fix failed test 2020-04-28 02:02:34 +02:00
Webber
b6f8040f4a Add tests for the versioning model 2020-04-28 02:02:34 +02:00
Webber
22c7d0e516 Improve versioning readme 📜 2020-04-26 21:45:10 +02:00
Webber
f85e50e499 Fix bug in hasAnyVersionTags 🐜 2020-04-26 21:45:10 +02:00
Webber
d75d7890d0 Implement versioning strategies in js 🧉 2020-04-26 21:45:10 +02:00
Webber
2e81e61af3 Add additional tests 🧪 2020-04-26 21:45:10 +02:00
Webber
ac76e9d562 Update readme with versioning examples 🧉 2020-04-26 21:45:10 +02:00
Webber
39a160b789 Allow versioning and version parameters. 2020-04-26 21:45:10 +02:00
Webber
e8a2eaad72 Cleanup default-build-script 2020-04-26 21:45:10 +02:00
Webber
6f1d03d8a8 Refactor Builder 2020-04-26 21:45:10 +02:00
Webber
5ee9c59113 Create VersionApplicator 2020-04-26 21:45:10 +02:00
Webber
03510d4a55 Abstract out the stdout reporter 2020-04-26 21:45:10 +02:00
Webber
ad65b0ece4 Abstract out the argument parser 2020-04-26 21:45:10 +02:00
Webber
a513e5b640 Add semantic versioning logic 2020-04-26 21:45:10 +02:00
Webber
328b0d8ac0 Add system extension for Process 2020-04-26 21:45:10 +02:00
Webber
e53dcf13e3 Add .editorconfig for the default-build-script project 2020-04-26 21:45:10 +02:00
dependabot-preview[bot]
3338b392a0 [Security] Bump acorn from 6.4.0 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1. **This update includes a security fix.**
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-13 22:32:17 +00:00
dependabot-preview[bot]
7e115b5cc3 Bump eslint-plugin-unicorn from 17.0.1 to 17.1.0
Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 17.0.1 to 17.1.0.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v17.0.1...v17.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-10 18:22:23 +00:00
dependabot-preview[bot]
758108295c Bump eslint-plugin-react from 7.18.3 to 7.19.0
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.18.3 to 7.19.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.18.3...v7.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-10 09:00:11 +00:00
Webber
8684472273 install deps during build phase 2020-03-09 23:47:22 +01:00
Webber
38b6a42f5e fix dist file 2020-03-09 23:47:22 +01:00
litefeel
fbeaf77867 Apply merge request suggestions 2020-03-09 23:23:53 +01:00
litefeel
6f3a2bd992 Fixed compatibility 2020-03-09 23:23:53 +01:00
litefeel
e1eda1e876 Change UNITY_LICENSE_PATH to UNITY_LICENSE_FILE 2020-03-09 23:23:53 +01:00
litefeel
0088ca3094 Add UNITY_LICENSE_PATH 2020-03-09 23:23:53 +01:00
dependabot-preview[bot]
465f15a945 Bump eslint-plugin-unicorn from 16.1.1 to 17.0.1
Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 16.1.1 to 17.0.1.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v16.1.1...v17.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-09 21:53:00 +00:00
dependabot-preview[bot]
ffe381e28a Bump @babel/core from 7.8.6 to 7.8.7
Bumps [@babel/core](https://github.com/babel/babel) from 7.8.6 to 7.8.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.8.6...v7.8.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-05 15:36:08 +00:00
dependabot-preview[bot]
085359d24f Bump @babel/preset-env from 7.8.6 to 7.8.7
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.8.6 to 7.8.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.8.6...v7.8.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-05 15:26:03 +00:00
dependabot-preview[bot]
b47dcdab84 Bump @babel/core from 7.8.4 to 7.8.6
Bumps [@babel/core](https://github.com/babel/babel) from 7.8.4 to 7.8.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.8.4...v7.8.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-28 09:18:56 +00:00
dependabot-preview[bot]
0c23c7d0a3 Bump @babel/preset-env from 7.8.4 to 7.8.6
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.8.4 to 7.8.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.8.4...v7.8.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-28 09:09:38 +00:00
dependabot-preview[bot]
f3099b6226 Bump lint-staged from 10.0.7 to 10.0.8
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 10.0.7 to 10.0.8.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v10.0.7...v10.0.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-28 09:00:06 +00:00
dependabot-preview[bot]
a969d3322f Bump babel-eslint from 10.0.3 to 10.1.0
Bumps [babel-eslint](https://github.com/babel/babel-eslint) from 10.0.3 to 10.1.0.
- [Release notes](https://github.com/babel/babel-eslint/releases)
- [Commits](https://github.com/babel/babel-eslint/compare/v10.0.3...v10.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 23:02:57 +00:00
dependabot-preview[bot]
8f63acf12b Bump @actions/github from 2.1.0 to 2.1.1
Bumps [@actions/github](https://github.com/actions/toolkit/tree/HEAD/packages/github) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/master/packages/github/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/github)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-21 08:40:38 +00:00
Webber
1813ee2895 add dist build 2020-02-15 18:02:01 +01:00
dependabot-preview[bot]
6e377601bf Bump @zeit/ncc from 0.21.0 to 0.21.1
Bumps [@zeit/ncc](https://github.com/zeit/ncc) from 0.21.0 to 0.21.1.
- [Release notes](https://github.com/zeit/ncc/releases)
- [Commits](https://github.com/zeit/ncc/compare/0.21.0...0.21.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-15 18:02:01 +01:00
dependabot-preview[bot]
f7321735d5 Bump husky from 4.2.2 to 4.2.3
Bumps [husky](https://github.com/typicode/husky) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/typicode/husky/releases)
- [Changelog](https://github.com/typicode/husky/blob/master/CHANGELOG.md)
- [Commits](https://github.com/typicode/husky/compare/v4.2.2...v4.2.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-13 08:13:42 +00:00
dependabot-preview[bot]
19429bf324 Bump eslint-plugin-unicorn from 16.0.0 to 16.1.1
Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 16.0.0 to 16.1.1.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v16.0.0...v16.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-12 11:37:51 +01:00
dependabot-preview[bot]
04eaffcd1f Bump husky from 4.2.1 to 4.2.2
Bumps [husky](https://github.com/typicode/husky) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/typicode/husky/releases)
- [Changelog](https://github.com/typicode/husky/blob/master/CHANGELOG.md)
- [Commits](https://github.com/typicode/husky/compare/v4.2.1...v4.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-12 09:52:47 +01:00
Webber
2c1ef01beb Update readme to reflect v0.11 2020-02-11 20:59:27 +01:00
Webber
dfccf539d1 Add custom parameters to test workflow 2020-02-11 20:59:27 +01:00
Webber
c1a7c5f70b Separate custom parameters 2020-02-11 20:59:27 +01:00
dependabot-preview[bot]
7e4851cc3a Bump @zeit/ncc from 0.20.5 to 0.21.0
Bumps [@zeit/ncc](https://github.com/zeit/ncc) from 0.20.5 to 0.21.0.
- [Release notes](https://github.com/zeit/ncc/releases)
- [Commits](https://github.com/zeit/ncc/compare/0.20.5...0.21.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-11 13:55:58 +01:00
dependabot-preview[bot]
bbb5661b43 Bump husky from 4.0.0-beta.5 to 4.2.1
Bumps [husky](https://github.com/typicode/husky) from 4.0.0-beta.5 to 4.2.1.
- [Release notes](https://github.com/typicode/husky/releases)
- [Changelog](https://github.com/typicode/husky/blob/master/CHANGELOG.md)
- [Commits](https://github.com/typicode/husky/compare/v4.0.0-beta.5...v4.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-11 13:04:47 +01:00
dependabot-preview[bot]
678fb210be Bump eslint-plugin-flowtype from 4.5.2 to 4.6.0
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 4.5.2 to 4.6.0.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v4.5.2...v4.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-11 11:34:58 +01:00
dependabot-preview[bot]
90af9768a7 Bump eslint from 6.7.2 to 6.8.0
Bumps [eslint](https://github.com/eslint/eslint) from 6.7.2 to 6.8.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v6.7.2...v6.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-11 10:23:53 +01:00
dependabot-preview[bot]
6ab433b631 Bump @babel/core from 7.7.5 to 7.8.4
Bumps [@babel/core](https://github.com/babel/babel) from 7.7.5 to 7.8.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.5...v7.8.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-10 17:55:59 +01:00
dependabot-preview[bot]
d5c4c09fc4 Bump lint-staged from 9.5.0 to 10.0.7
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 9.5.0 to 10.0.7.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v9.5.0...v10.0.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-10 17:38:42 +01:00
dependabot-preview[bot]
3e69d63ec4 Bump eslint-config-prettier from 6.7.0 to 6.10.0
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 6.7.0 to 6.10.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/commits/v6.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-10 17:13:29 +01:00
dependabot-preview[bot]
c47d5a4d2e Bump eslint-plugin-react from 7.17.0 to 7.18.3
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.17.0 to 7.18.3.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.17.0...v7.18.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-10 16:49:56 +01:00
dependabot-preview[bot]
9dd75201b0 Bump eslint-plugin-import from 2.19.1 to 2.20.1
Bumps [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import) from 2.19.1 to 2.20.1.
- [Release notes](https://github.com/benmosher/eslint-plugin-import/releases)
- [Changelog](https://github.com/benmosher/eslint-plugin-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/benmosher/eslint-plugin-import/compare/v2.19.1...v2.20.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-10 14:16:53 +01:00
Webber
308a9e173d fix: upgrade @actions/github from 2.0.0 to 2.0.1 2020-02-09 16:04:51 +01:00
snyk-bot
f5a61eed4a fix: upgrade @actions/exec from 1.0.2 to 1.0.3
Snyk has created this PR to upgrade @actions/exec from 1.0.2 to 1.0.3.

See this package in NPM:
https://www.npmjs.com/package/@actions/exec

See this project in Snyk:
https://app.snyk.io/org/webbertakken/project/e69ca31d-f4f2-4223-ae93-0245ffac3626?utm_source=github&utm_medium=upgrade-pr
2020-02-09 10:28:35 +01:00
Webber
051513bf4a Sort badges by color ugliness 🦄 2020-02-09 01:53:32 +01:00
Webber
d9382df690 put badges on 1 line 2020-02-09 01:53:32 +01:00
Webber
0df7fa7855 Add badges for lgtm, codecov, snyk 2020-02-09 01:24:03 +01:00
Webber
d1c1d276e0 Add token as env, so PRs will trigger codecov 2020-02-09 01:24:03 +01:00
Webber
ad1f47dd3a Set token in runner env 2020-02-09 01:24:03 +01:00
Webber
a0d197e1fa Integrate codecov 2020-02-09 01:24:03 +01:00
Webber
cc244fa58b Add updated index.js 2020-02-08 19:25:58 +01:00
dependabot-preview[bot]
c70fdac383 Bump @actions/core from 1.2.0 to 1.2.2
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.0 to 1.2.2.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/master/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-08 19:25:58 +01:00
dependabot-preview[bot]
81487e2140 Bump @babel/preset-env from 7.7.7 to 7.8.4
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.7.7 to 7.8.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.7...v7.8.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-08 19:09:38 +01:00
dependabot-preview[bot]
ef06de9255 Bump jest from 24.9.0 to 25.1.0
Bumps [jest](https://github.com/facebook/jest) from 24.9.0 to 25.1.0.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v24.9.0...v25.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-08 18:50:59 +01:00
dependabot-preview[bot]
64499d66cc Bump eslint-plugin-unicorn from 14.0.1 to 16.0.0
Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 14.0.1 to 16.0.0.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v14.0.1...v16.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-08 17:34:17 +01:00
dependabot-preview[bot]
59ab903a78 Bump @babel/cli from 7.7.5 to 7.8.4
Bumps [@babel/cli](https://github.com/babel/babel) from 7.7.5 to 7.8.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.7.5...v7.8.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-08 15:12:50 +01:00
Webber Takken
2fa5b33111 Update README.md 2020-02-01 21:24:33 +01:00
Webber
0399609b07 Give more info about what is different 2020-02-01 21:02:56 +01:00
Webber
1c91a3bf31 Fix custom parameters 2020-02-01 21:02:56 +01:00
Webber
ae7f659e9f Remove unneeded trailing slash 2020-02-01 20:43:49 +01:00
Webber
b781b891ec Align diff test with rename 2020-02-01 20:43:49 +01:00
Webber
f3a984165e Rename builder folder to action folder (unity actions convention) 2020-02-01 20:43:49 +01:00
Webber
d8896dc4f5 Update references 2020-01-27 23:15:26 +01:00
Webber
4051832dc0 Add some more basic tests 🤷‍♂️ 2020-01-27 23:15:26 +01:00
Webber
fe2311ef4b Hint enabling cache if not already enabled. 2020-01-27 23:15:26 +01:00
Webber
37d5ce498f Add more complete steps to create workflow 2020-01-27 23:15:26 +01:00
96 changed files with 4202 additions and 2623 deletions

View File

@@ -2,4 +2,4 @@
* *
# Files required for the action # Files required for the action
!builder/ !action/

View File

@@ -1,2 +1,2 @@
**/node_modules/** **/node_modules/**
**/builder/** **/action/**

View File

@@ -6,21 +6,23 @@ on:
env: env:
UNITY_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>" UNITY_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>"
CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de'
jobs: jobs:
tests: tests:
name: Tests name: Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 12.x
- run: yarn - run: yarn
- run: yarn lint - run: yarn lint
- run: yarn test - 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 || { echo "build command should always succeed" ; exit 61; }
- run: yarn build --quiet && git diff --quiet builder || { echo "builder should be auto generated" ; exit 62; } - run: yarn build --quiet && git diff --quiet action || { echo "action should be auto generated" ; git diff action ; exit 62; }
buildForAllPlatforms: buildForAllPlatforms:
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }} name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
@@ -48,7 +50,9 @@ jobs:
# - Switch # Build a Nintendo Switch player. # - Switch # Build a Nintendo Switch player.
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
with:
lfs: true
- uses: actions/cache@v1.1.0 - uses: actions/cache@v1.1.0
with: with:
path: ${{ matrix.projectPath }}/Library path: ${{ matrix.projectPath }}/Library
@@ -61,6 +65,7 @@ jobs:
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: Build name: Build

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.idea .idea
node_modules node_modules
coverage/

View File

@@ -1,2 +1,2 @@
**/node_modules/** **/node_modules/**
**/dist/** **/action/**

181
README.md
View File

@@ -1,6 +1,9 @@
# Unity - Builder # Unity - Builder
[![Actions status](https://github.com/webbertakken/unity-builder/workflows/Actions%20%F0%9F%98%8E/badge.svg?event=push&branch=master)](https://github.com/webbertakken/unity-builder/actions?query=branch%3Amaster+event%3Apush+workflow%3A%22Actions+%F0%9F%98%8E%22) [![Actions status](https://github.com/webbertakken/unity-builder/workflows/Actions%20%F0%9F%98%8E/badge.svg?event=push&branch=master)](https://github.com/webbertakken/unity-builder/actions?query=branch%3Amaster+event%3Apush+workflow%3A%22Actions+%F0%9F%98%8E%22)
[![snyk - known vulnerabilities](https://snyk.io/test/github/webbertakken/unity-builder/badge.svg)](https://snyk.io/test/github/webbertakken/unity-builder)
[![lgtm - code quality](https://img.shields.io/lgtm/grade/javascript/g/webbertakken/unity-builder.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/webbertakken/unity-builder/context:javascript)
[![codecov - test coverage](https://codecov.io/gh/webbertakken/unity-builder/branch/master/graph/badge.svg)](https://codecov.io/gh/webbertakken/unity-builder)
--- ---
@@ -31,10 +34,24 @@ collection repository for workflow documentation and reference implementation.
## Usage ## 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. 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 ```yaml
- uses: webbertakken/unity-builder@v0.9 - uses: webbertakken/unity-builder@v0.11
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with: with:
@@ -43,6 +60,75 @@ Create or edit the file called `.github/workflows/main.yml` and add a job to it.
targetPlatform: WebGL 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@v0.11
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: A complete workflow that builds every available platform could look like this:
```yaml ```yaml
@@ -81,8 +167,17 @@ jobs:
- tvOS # Build to Apple's tvOS platform. - tvOS # Build to Apple's tvOS platform.
- Switch # Build a Nintendo Switch player. - Switch # Build a Nintendo Switch player.
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- uses: webbertakken/unity-builder@v0.9 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@v0.11
with: with:
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
@@ -93,10 +188,7 @@ jobs:
path: build path: build
``` ```
> **Notes:** > **Note:** _Environment variables are set for all jobs in the workflow like this._
>
> - Don't forget to replace _&lt;test-project&gt;_ with your project name.
> - By default the enabled scenes from the project's settings will be built.
## Configuration options ## Configuration options
@@ -153,7 +245,7 @@ There are two conditions for a custom buildCommand:
_**example:**_ _**example:**_
```yaml ```yaml
- uses: webbertakken/unity-builder@master - uses: webbertakken/unity-builder@<version>
with: with:
buildMethod: EditorNamespace.BuilderClassName.StaticBulidMethod buildMethod: EditorNamespace.BuilderClassName.StaticBulidMethod
``` ```
@@ -161,6 +253,77 @@ _**example:**_
_**required:** `false`_ _**required:** `false`_
_**default:** Built-in script that will run a build out of the box._ _**default:** Built-in script that will run a build out of the box._
#### versioning
The versioning strategy to use.
Strategies only work when no custom buildMethod is specified.
_**required:** `false`_
_**default:** `Auto`_
#### _These are the available strategies:_
##### None
No version will be set by Builder.
```yaml
- uses: webbertakken/unity-builder@<version>
with:
versioning: None
```
Note that the version set in the project will be used instead.
##### Semantic (default)
Builder automatically generates a version based on [semantic versioning](https://semver.org/) out of the box.
The version works as follows: `<major>.<minor>.<patch>` for example `0.1.2`.
The latest tag dictates `<major>.<minor>` and the number of commits since that tag is used in `<patch>`.
```yaml
- uses: webbertakken/unity-builder@<version>
with:
versioning: Semantic
```
This strategy works well for the following reasons:
- All builds have their unique version
- No version related commits are created
- No knowledge of git or versioning is required
- Developer keeps control over `major` and `minor` versions using tags.
- Zero configuration; It works out of the box
##### Tag
Uses the tag that points at `HEAD` as the version.
```yaml
- uses: webbertakken/unity-builder@<version>
with:
versioning: Tag
```
This strategy works well when using a pipeline that specifically runs for tags.
The tag must be a version tag.
##### Custom
Allows specifying a custom version in the `version` field.
```yaml
- uses: webbertakken/unity-builder@<version>
with:
versioning: Custom
version: <some_version>
```
If there is a use case missing from Builder, feel free to create a feature request.
#### customParameters #### customParameters
Custom parameters to configure the build. Custom parameters to configure the build.
@@ -172,7 +335,7 @@ Parameters without a value will be considered booleans (with a value of true).
_**example:**_ _**example:**_
```yaml ```yaml
- uses: webbertakken/unity-builder@master - uses: webbertakken/unity-builder@<version>
with: with:
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
``` ```

View File

@@ -32,4 +32,4 @@ branding:
color: 'gray-dark' color: 'gray-dark'
runs: runs:
using: 'node12' using: 'node12'
main: 'builder/index.js' main: 'action/index.js'

View File

@@ -0,0 +1,4 @@
root = true
[*.cs]
resharper_check_namespace_highlighting = do_not_show

View File

@@ -57,6 +57,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Assets\Editor\Builder.cs" /> <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"> <Reference Include="UnityEditor.TestRunner">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.TestRunner.dll</HintPath> <HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.TestRunner.dll</HintPath>
</Reference> </Reference>
@@ -688,6 +695,9 @@
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/Boo.Lang.dll</HintPath> <HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/Boo.Lang.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include=".editorconfig" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View 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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b6e5ef18d769419d887b56665969442b
timeCreated: 1587503329

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 46d2ec4a86604575be2b2d02b0df7b74
timeCreated: 1587503354

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 166f919334c44e7a80ae916667974e7d
timeCreated: 1587503566

View File

@@ -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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e59b491a4124442ea7277f76761cdc8a
timeCreated: 1587503545

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b5da3bd7e18c43d79243410166c8dc9a
timeCreated: 1587493708

View File

@@ -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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 29c1880a390c4af7be821b7877602815
timeCreated: 1587494270

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1c3bddf6d8984cde9208e3f0fe584879
timeCreated: 1587490700

View 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;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cdec7fa0f5bb44958fdf74d4658a4601
timeCreated: 1587495075

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4d375e209fd14fc5bc2f3dc3c78ac574
timeCreated: 1587490750

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 30483367ddc84699a0da377ccb93769a
timeCreated: 1587504315

View File

@@ -0,0 +1,10 @@
namespace UnityBuilderAction.Versioning
{
public static class VersionGenerator
{
public static string Generate()
{
return Git.GenerateSemanticCommitVersion();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9892e03ae8314b7eacd793c8002de007
timeCreated: 1587490842

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
if [[ -n "$UNITY_LICENSE" ]]; then if [[ -n "$UNITY_LICENSE" ]] || [[ -n "$UNITY_LICENSE_FILE" ]]; then
# #
# PERSONAL LICENSE MODE # PERSONAL LICENSE MODE
# #
@@ -15,8 +15,13 @@ if [[ -n "$UNITY_LICENSE" ]]; then
# Set the license file path # Set the license file path
FILE_PATH=UnityLicenseFile.ulf FILE_PATH=UnityLicenseFile.ulf
# Copy license file from Github variables if [[ -n "$UNITY_LICENSE" ]]; then
echo "$UNITY_LICENSE" | tr -d '\r' > $FILE_PATH # 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 # Activate license
ACTIVATION_OUTPUT=$(xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \ ACTIVATION_OUTPUT=$(xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \

View File

@@ -65,7 +65,7 @@ fi
# #
# Display custom parameters # Display custom parameters
# #
echo "Using custom parameters \"$CUSTOM_PARAMETERS\"." echo "Using custom parameters $CUSTOM_PARAMETERS."
# The build specification below may require Unity 2019.2.11f1 or later (not tested below). # 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 # Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
@@ -109,7 +109,9 @@ xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
-customBuildTarget "$BUILD_TARGET" \ -customBuildTarget "$BUILD_TARGET" \
-customBuildPath "$CUSTOM_BUILD_PATH" \ -customBuildPath "$CUSTOM_BUILD_PATH" \
-executeMethod "$BUILD_METHOD" \ -executeMethod "$BUILD_METHOD" \
"$CUSTOM_PARAMETERS" -versioning "$VERSIONING" \
-version "$VERSION" \
$CUSTOM_PARAMETERS
# Catch exit code # Catch exit code
BUILD_EXIT_CODE=$? BUILD_EXIT_CODE=$?

View File

@@ -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

39
codecov.yml Normal file
View 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

View File

@@ -5,4 +5,5 @@ module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
transform: { '^.+\\.(js|jsx)?$': 'babel-jest' }, transform: { '^.+\\.(js|jsx)?$': 'babel-jest' },
transformIgnorePatterns: [`/node_modules/(?!${esModules})`], transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
setupFilesAfterEnv: ['./src/jest.setup.js'],
}; };

View File

@@ -2,44 +2,45 @@
"name": "unity-builder", "name": "unity-builder",
"version": "0.5.0", "version": "0.5.0",
"description": "Build Unity projects for different platforms.", "description": "Build Unity projects for different platforms.",
"main": "builder/index.js", "main": "action/index.js",
"repository": "git@github.com:webbertakken/unity-builder.git", "repository": "git@github.com:webbertakken/unity-builder.git",
"author": "Webber <webber@takken.io>", "author": "Webber <webber@takken.io>",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build": "ncc build src --out builder --minify", "prebuild": "yarn",
"build": "ncc build src --out action --minify",
"lint": "prettier --check \"src/**/*.js\" && eslint src", "lint": "prettier --check \"src/**/*.js\" && eslint src",
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.2.0", "@actions/core": "^1.2.4",
"@actions/exec": "1.0.2", "@actions/exec": "1.0.4",
"@actions/github": "^2.0.0" "@actions/github": "^2.1.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "7.7.5", "@babel/cli": "7.8.4",
"@babel/core": "7.7.5", "@babel/core": "7.9.6",
"@babel/preset-env": "7.7.7", "@babel/preset-env": "7.9.6",
"@zeit/ncc": "0.20.5", "@zeit/ncc": "0.22.1",
"babel-eslint": "10.0.3", "babel-eslint": "10.1.0",
"eslint": "6.7.2", "eslint": "6.8.0",
"eslint-config-airbnb": "18.0.1", "eslint-config-airbnb": "18.1.0",
"eslint-config-prettier": "6.7.0", "eslint-config-prettier": "6.11.0",
"eslint-plugin-flowtype": "4.5.2", "eslint-plugin-flowtype": "4.7.0",
"eslint-plugin-import": "2.19.1", "eslint-plugin-import": "2.20.2",
"eslint-plugin-jsx-a11y": "6.2.3", "eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-prettier": "3.1.2", "eslint-plugin-prettier": "3.1.3",
"eslint-plugin-react": "7.17.0", "eslint-plugin-react": "7.19.0",
"eslint-plugin-unicorn": "14.0.1", "eslint-plugin-unicorn": "19.0.1",
"husky": "4.0.0-beta.5", "husky": "4.2.5",
"jest": "24.9.0", "jest": "25.5.3",
"lint-staged": "9.5.0", "lint-staged": "10.2.2",
"lodash-es": "4.17.15", "lodash-es": "4.17.15",
"prettier": "1.19.1" "prettier": "2.0.5"
}, },
"husky": { "husky": {
"hooks": { "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": { "lint-staged": {

View File

@@ -1,21 +1,22 @@
import { Action, Docker, Input, ImageTag, BuildParameters } from './model'; import { Action, BuildParameters, Cache, Docker, Input, ImageTag } from './model';
const core = require('@actions/core'); const core = require('@actions/core');
async function action() { async function action() {
Action.checkCompatibility(); Action.checkCompatibility();
Cache.verify();
const { dockerfile, workspace, builderFolder } = Action; const { dockerfile, workspace, actionFolder } = Action;
const buildParameters = BuildParameters.create(Input.getFromUser()); const buildParameters = BuildParameters.create(await Input.getFromUser());
const baseImage = new ImageTag(buildParameters); const baseImage = new ImageTag(buildParameters);
// Build docker image // Build docker image
const builtImage = await Docker.build({ path: builderFolder, dockerfile, baseImage }); const builtImage = await Docker.build({ path: actionFolder, dockerfile, baseImage });
// Run docker image // Run docker image
await Docker.run(builtImage, { workspace, ...buildParameters }); await Docker.run(builtImage, { workspace, ...buildParameters });
} }
action().catch(error => { action().catch((error) => {
core.setFailed(error.message); core.setFailed(error.message);
}); });

49
src/jest.setup.js Normal file
View 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,
};
},
});

View 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,
};

View 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."`;

View File

@@ -25,12 +25,12 @@ class Action {
return path.dirname(path.dirname(__filename)); return path.dirname(path.dirname(__filename));
} }
static get builderFolder() { static get actionFolder() {
return `${Action.rootFolder}/builder`; return `${Action.rootFolder}/action`;
} }
static get dockerfile() { static get dockerfile() {
return `${Action.builderFolder}/Dockerfile`; return `${Action.actionFolder}/Dockerfile`;
} }
static get workspace() { static get workspace() {

View File

@@ -20,11 +20,11 @@ describe('Action', () => {
expect(fs.existsSync(rootFolder)).toStrictEqual(true); expect(fs.existsSync(rootFolder)).toStrictEqual(true);
}); });
it('returns the builder folder', () => { it('returns the action folder', () => {
const { builderFolder } = Action; const { actionFolder } = Action;
expect(path.basename(builderFolder)).toStrictEqual('builder'); expect(path.basename(actionFolder)).toStrictEqual('action');
expect(fs.existsSync(builderFolder)).toStrictEqual(true); expect(fs.existsSync(actionFolder)).toStrictEqual(true);
}); });
it('returns the docker file', () => { it('returns the docker file', () => {

View File

@@ -3,23 +3,25 @@ import Platform from './platform';
class BuildParameters { class BuildParameters {
static create(parameters) { static create(parameters) {
const { const {
unityVersion, version,
targetPlatform, targetPlatform,
projectPath, projectPath,
buildName, buildName,
buildsPath, buildsPath,
buildMethod, buildMethod,
buildVersion,
customParameters, customParameters,
} = parameters; } = parameters;
return { return {
version: unityVersion, version,
platform: targetPlatform, platform: targetPlatform,
projectPath, projectPath,
buildName, buildName,
buildPath: `${buildsPath}/${targetPlatform}`, buildPath: `${buildsPath}/${targetPlatform}`,
buildFile: this.parseBuildFile(buildName, targetPlatform), buildFile: this.parseBuildFile(buildName, targetPlatform),
buildMethod, buildMethod,
buildVersion,
customParameters, customParameters,
}; };
} }

View File

@@ -0,0 +1,83 @@
import BuildParameters from './build-parameters';
import Platform from './platform';
describe('BuildParameters', () => {
describe('create', () => {
const someParameters = {
version: 'someVersion',
targetPlatform: 'somePlatform',
projectPath: 'path/to/project',
buildName: 'someBuildName',
buildsPath: 'someBuildsPath',
buildMethod: 'Namespace.Class.Method',
customParameters: '-someParam someValue',
};
it('does not throw', () => {
expect(() => BuildParameters.create(someParameters)).not.toThrow();
});
it('returns the version', () => {
expect(BuildParameters.create(someParameters).version).toStrictEqual(someParameters.version);
});
it('returns the platform', () => {
expect(BuildParameters.create(someParameters).platform).toStrictEqual(
someParameters.targetPlatform,
);
});
it('returns the project path', () => {
expect(BuildParameters.create(someParameters).projectPath).toStrictEqual(
someParameters.projectPath,
);
});
it('returns the build name', () => {
expect(BuildParameters.create(someParameters).buildName).toStrictEqual(
someParameters.buildName,
);
});
it('returns the build path', () => {
expect(BuildParameters.create(someParameters).buildPath).toStrictEqual(
`${someParameters.buildsPath}/${someParameters.targetPlatform}`,
);
});
describe('build file', () => {
it('returns the build file', () => {
expect(BuildParameters.create(someParameters).buildFile).toStrictEqual(
someParameters.buildName,
);
});
test.each([Platform.types.StandaloneWindows, Platform.types.StandaloneWindows64])(
'appends exe for %s',
(targetPlatform) => {
expect(
BuildParameters.create({ ...someParameters, targetPlatform }).buildFile,
).toStrictEqual(`${someParameters.buildName}.exe`);
},
);
test.each([Platform.types.Android])('appends apk for %s', (targetPlatform) => {
expect(
BuildParameters.create({ ...someParameters, targetPlatform }).buildFile,
).toStrictEqual(`${someParameters.buildName}.apk`);
});
});
it('returns the build method', () => {
expect(BuildParameters.create(someParameters).buildMethod).toStrictEqual(
someParameters.buildMethod,
);
});
it('returns the custom parameters', () => {
expect(BuildParameters.create(someParameters).customParameters).toStrictEqual(
someParameters.customParameters,
);
});
});
});

25
src/model/cache.js Normal file
View File

@@ -0,0 +1,25 @@
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;
}
// eslint-disable-next-line no-console
console.log(`
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
View 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();
});
});
});

View File

@@ -12,7 +12,7 @@ class Docker {
--build-arg IMAGE=${baseImage} \ --build-arg IMAGE=${baseImage} \
--tag ${tag}`; --tag ${tag}`;
await exec(command, null, { silent }); await exec(command, undefined, { silent });
return tag; return tag;
} }
@@ -27,6 +27,7 @@ class Docker {
buildPath, buildPath,
buildFile, buildFile,
buildMethod, buildMethod,
buildVersion,
customParameters, customParameters,
} = parameters; } = parameters;
@@ -34,17 +35,19 @@ class Docker {
--workdir /github/workspace \ --workdir /github/workspace \
--rm \ --rm \
--env UNITY_LICENSE \ --env UNITY_LICENSE \
--env UNITY_LICENSE_FILE \
--env UNITY_EMAIL \ --env UNITY_EMAIL \
--env UNITY_PASSWORD \ --env UNITY_PASSWORD \
--env UNITY_SERIAL \ --env UNITY_SERIAL \
--env UNITY_VERSION=${version} \ --env UNITY_VERSION="${version}" \
--env PROJECT_PATH=${projectPath} \ --env PROJECT_PATH="${projectPath}" \
--env BUILD_TARGET=${platform} \ --env BUILD_TARGET="${platform}" \
--env BUILD_NAME=${buildName} \ --env BUILD_NAME="${buildName}" \
--env BUILD_PATH=${buildPath} \ --env BUILD_PATH="${buildPath}" \
--env BUILD_FILE=${buildFile} \ --env BUILD_FILE="${buildFile}" \
--env BUILD_METHOD=${buildMethod} \ --env BUILD_METHOD="${buildMethod}" \
--env CUSTOM_PARAMETERS=${customParameters} \ --env VERSION="${buildVersion}" \
--env CUSTOM_PARAMETERS="${customParameters}" \
--env HOME=/github/home \ --env HOME=/github/home \
--env GITHUB_REF \ --env GITHUB_REF \
--env GITHUB_SHA \ --env GITHUB_SHA \
@@ -67,7 +70,7 @@ class Docker {
--volume "${workspace}":"/github/workspace" \ --volume "${workspace}":"/github/workspace" \
${image}`; ${image}`;
await exec(command, null, { silent }); await exec(command, undefined, { silent });
} }
} }

View File

@@ -4,7 +4,7 @@ import ImageTag from './image-tag';
describe('Docker', () => { describe('Docker', () => {
it('builds', async () => { it('builds', async () => {
const path = Action.builderFolder; const path = Action.actionFolder;
const dockerfile = `${path}/Dockerfile`; const dockerfile = `${path}/Dockerfile`;
const baseImage = new ImageTag({ const baseImage = new ImageTag({
repository: '', repository: '',

View File

@@ -0,0 +1,8 @@
class CommandExecutionError extends Error {
constructor(message) {
super(message);
this.name = 'CommandExecutionError';
}
}
export default CommandExecutionError;

View 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());
});
});

View File

@@ -0,0 +1,8 @@
class NotImplementedException extends Error {
constructor(message) {
super(message);
this.name = 'NotImplementedException';
}
}
export default NotImplementedException;

View 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());
});
});

View File

@@ -0,0 +1,8 @@
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
export default ValidationError;

View 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());
});
});

View File

@@ -31,16 +31,16 @@ describe('UnityImageVersion', () => {
expect(image.builderPlatform).toStrictEqual(some.builderPlatform); 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(); 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; const { platform } = some;
expect(() => new ImageTag({ version, platform })).toThrow(); 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(); expect(() => new ImageTag({ platform })).toThrow();
}); });
}); });

View File

@@ -1,8 +1,23 @@
import Action from './action'; import Action from './action';
import BuildParameters from './build-parameters'; import BuildParameters from './build-parameters';
import Cache from './cache';
import Docker from './docker'; import Docker from './docker';
import Input from './input'; import Input from './input';
import ImageTag from './image-tag'; import ImageTag from './image-tag';
import Platform from './platform'; import Platform from './platform';
import Project from './project';
import Unity from './unity';
import Versioning from './versioning';
export { Action, BuildParameters, Docker, Input, ImageTag, Platform }; export {
Action,
BuildParameters,
Cache,
Docker,
Input,
ImageTag,
Platform,
Project,
Unity,
Versioning,
};

17
src/model/index.test.js Normal file
View 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();
});
});

View File

@@ -1,25 +1,36 @@
import Platform from './platform'; import Platform from './platform';
import Versioning from './versioning';
const core = require('@actions/core'); const core = require('@actions/core');
class Input { class Input {
static getFromUser() { static async getFromUser() {
// Input variables specified in workflows using "with" prop. // Input variables specified in workflows using "with" prop.
const unityVersion = core.getInput('unityVersion'); const version = core.getInput('unityVersion');
const targetPlatform = core.getInput('targetPlatform') || Platform.default; const targetPlatform = core.getInput('targetPlatform') || Platform.default;
const projectPath = core.getInput('projectPath') || '.'; const rawProjectPath = core.getInput('projectPath') || '.';
const buildName = core.getInput('buildName') || targetPlatform; const buildName = core.getInput('buildName') || targetPlatform;
const buildsPath = core.getInput('buildsPath') || 'build'; const buildsPath = core.getInput('buildsPath') || 'build';
const buildMethod = core.getInput('buildMethod'); // processed in docker file const buildMethod = core.getInput('buildMethod'); // processed in docker file
const versioningStrategy = core.getInput('versioning') || 'Semantic';
const specifiedVersion = core.getInput('version') || '';
const customParameters = core.getInput('customParameters') || ''; const customParameters = core.getInput('customParameters') || '';
// Sanitise input
const projectPath = rawProjectPath.replace(/\/$/, '');
// Parse input
const buildVersion = await Versioning.determineVersion(versioningStrategy, specifiedVersion);
// Return validated input
return { return {
unityVersion, version,
targetPlatform, targetPlatform,
projectPath, projectPath,
buildName, buildName,
buildsPath, buildsPath,
buildMethod, buildMethod,
buildVersion,
customParameters, customParameters,
}; };
} }

View File

@@ -1,9 +1,27 @@
import Input from './input'; import Input from './input';
import Versioning from './versioning';
const determineVersion = jest
.spyOn(Versioning, 'determineVersion')
.mockImplementation(() => '1.3.37');
afterEach(() => {
jest.clearAllMocks();
});
describe('Input', () => { describe('Input', () => {
describe('getFromUser', () => { describe('getFromUser', () => {
it('does not throw', () => { it('does not throw', async () => {
expect(() => Input.getFromUser()).not.toThrow(); await expect(Input.getFromUser()).resolves.not.toBeNull();
});
it('returns an object', async () => {
await expect(typeof (await Input.getFromUser())).toStrictEqual('object');
});
it('calls version generator once', async () => {
await Input.getFromUser();
expect(determineVersion).toHaveBeenCalledTimes(1);
}); });
}); });
}); });

View 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);
});
});
});

24
src/model/project.js Normal file
View File

@@ -0,0 +1,24 @@
import * as core from '@actions/core';
import Unity from './unity';
import Action from './action';
class Project {
static get relativePath() {
// Todo - properly use Input for this.
const projectPath = core.getInput('projectPath') || '.';
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
View 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');
});
});
});

44
src/model/system.js Normal file
View File

@@ -0,0 +1,44 @@
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 exitCode = await exec(command, arguments_, { silent: true, listeners, ...options });
if (debug !== '') {
core.debug(debug);
}
if (result !== '') {
core.info(result);
}
if (exitCode !== 0) {
throw new Error(error);
}
if (error !== '') {
core.warning(error);
}
return result;
}
}
export default System;

56
src/model/system.test.js Normal file
View File

@@ -0,0 +1,56 @@
import * as core from '@actions/core';
import System from './system';
jest.spyOn(core, 'debug').mockImplementation(() => {});
const info = jest.spyOn(core, 'info').mockImplementation(() => {});
jest.spyOn(core, 'warning').mockImplementation(() => {});
jest.spyOn(core, 'error').mockImplementation(() => {});
afterEach(() => {
jest.clearAllMocks();
});
describe('System', () => {
describe('run', () => {
it('runs a command successfully', async () => {
await expect(System.run('true')).resolves.not.toBeNull();
});
it('outputs results', async () => {
await expect(System.run('echo test')).resolves.toStrictEqual('test\n');
});
it('throws on when error code is not 0', async () => {
await expect(System.run('false')).rejects.toThrowError();
});
it('throws when no arguments are given', async () => {
await expect(System.run()).rejects.toThrowError();
});
it('outputs info', async () => {
await expect(System.run('echo test')).resolves.not.toBeNull();
expect(info).toHaveBeenLastCalledWith('test\n');
});
it('outputs info only once', async () => {
await expect(System.run('echo 1')).resolves.not.toBeNull();
expect(info).toHaveBeenCalledTimes(1);
expect(info).toHaveBeenLastCalledWith('1\n');
info.mockClear();
await expect(System.run('echo 2')).resolves.not.toBeNull();
await expect(System.run('echo 3')).resolves.not.toBeNull();
expect(info).toHaveBeenCalledTimes(2);
expect(info).toHaveBeenLastCalledWith('3\n');
});
it('allows pipes using buffer', async () => {
await expect(
System.run('sh', undefined, {
input: Buffer.from('git tag --list --merged HEAD | grep v[0-9]* | wc -l'),
}),
).resolves.toBeParsableToANumber();
});
});
});

7
src/model/unity.js Normal file
View File

@@ -0,0 +1,7 @@
class Unity {
static get libraryFolder() {
return 'Library';
}
}
export default Unity;

13
src/model/unity.test.js Normal file
View File

@@ -0,0 +1,13 @@
import Unity from './unity';
describe('Unity', () => {
describe('libraryFolder', () => {
it('does not throw', () => {
expect(() => Unity.libraryFolder).not.toThrow();
});
it('returns a string', () => {
expect(typeof Unity.libraryFolder).toStrictEqual('string');
});
});
});

204
src/model/versioning.js Normal file
View File

@@ -0,0 +1,204 @@
import * as core from '@actions/core';
import NotImplementedException from './error/not-implemented-exception';
import ValidationError from './error/validation-error';
import System from './system';
export default class Versioning {
static get strategies() {
return { None: 'None', Semantic: 'Semantic', Tag: 'Tag', Custom: 'Custom' };
}
/**
* Get the branch name of the (related) branch
*/
static get branch() {
// Todo - use optional chaining (https://github.com/zeit/ncc/issues/534)
return this.headRef || (this.ref && this.ref.slice(11));
}
/**
* For pull requests we can reliably use GITHUB_HEAD_REF
*/
static get headRef() {
return process.env.GITHUB_HEAD_REF;
}
/**
* For branches GITHUB_REF will have format `refs/heads/feature-branch-1`
*/
static get ref() {
return process.env.GITHUB_REF;
}
/**
* Regex to parse version description into separate fields
*/
static get descriptionRegex() {
return /^v([\d.]+)-(\d+)-g(\w+)-?(\w+)*/g;
}
static async determineVersion(strategy, inputVersion) {
// Validate input
if (!Object.hasOwnProperty.call(this.strategies, strategy)) {
throw new ValidationError(
`Versioning strategy should be one of ${Object.values(this.strategies).join(', ')}.`,
);
}
let version;
switch (strategy) {
case this.strategies.None:
version = 'none';
break;
case this.strategies.Custom:
version = inputVersion;
break;
case this.strategies.Semantic:
version = await this.generateSemanticVersion();
break;
case this.strategies.Tag:
version = await this.generateTagVersion();
break;
default:
throw new NotImplementedException(`Strategy ${strategy} is not implemented.`);
}
return version;
}
/**
* Automatically generates a version based on SemVer out of the box.
*
* The version works as follows: `<major>.<minor>.<patch>` for example `0.1.2`.
*
* The latest tag dictates `<major>.<minor>`
* The number of commits since that tag dictates`<patch>`.
*
* @See: https://semver.org/
*/
static async generateSemanticVersion() {
await this.fetch();
if (await this.isDirty()) {
throw new Error('Branch is dirty. Refusing to base semantic version on uncommitted changes');
}
if (!(await this.hasAnyVersionTags())) {
const version = `0.0.${await this.getTotalNumberOfCommits()}`;
core.info(`Generated version ${version} (no version tags found).`);
return version;
}
const { tag, commits, hash } = await this.parseSemanticVersion();
core.info(`Found semantic version ${tag}.${commits} for ${this.branch}@${hash}`);
return `${tag}.${commits}`;
}
/**
* Generate the proper version for unity based on an existing tag.
*/
static async generateTagVersion() {
let tag = await this.getTag();
if (tag.charAt(0) === 'v') {
tag = tag.slice(1);
}
return tag;
}
/**
* Parses the versionDescription into their named parts.
*/
static async parseSemanticVersion() {
const description = await this.getVersionDescription();
try {
const [match, tag, commits, hash] = this.descriptionRegex.exec(description);
return {
match,
tag,
commits,
hash,
};
} catch (error) {
throw new Error(`Failed to parse git describe output: "${description}".`);
}
}
/**
* Retrieves refs from the configured remote.
*
* Fetch unshallow for incomplete repository, but fall back to normal fetch.
*
* Note: `--all` should not be used, and would break fetching for push event.
*/
static async fetch() {
try {
await System.run('git', ['fetch', '--unshallow']);
} catch (error) {
core.warning(error);
await System.run('git', ['fetch']);
}
}
/**
* Retrieves information about the branch.
*
* Format: `v0.12-24-gd2198ab`
*
* In this format v0.12 is the latest tag, 24 are the number of commits since, and gd2198ab
* identifies the current commit.
*/
static async getVersionDescription() {
return System.run('git', [
'describe',
'--long',
'--tags',
'--always',
'--debug',
`origin/${this.branch}`,
]);
}
/**
* Returns whether there are uncommitted changes that are not ignored.
*/
static async isDirty() {
const output = await System.run('git', ['status', '--porcelain']);
return output !== '';
}
/**
* Get the tag if there is one pointing at HEAD
*/
static async getTag() {
return System.run('git', ['tag', '--points-at', 'HEAD']);
}
/**
* Whether or not the repository has any version tags yet.
*/
static async hasAnyVersionTags() {
const numberOfCommitsAsString = await System.run('sh', undefined, {
input: Buffer.from('git tag --list --merged HEAD | grep v[0-9]* | wc -l'),
silent: false,
});
const numberOfCommits = Number.parseInt(numberOfCommitsAsString, 10);
return numberOfCommits !== 0;
}
/**
* Get the total number of commits on head.
*/
static async getTotalNumberOfCommits() {
const numberOfCommitsAsString = await System.run('git', ['rev-list', '--count', 'HEAD']);
return Number.parseInt(numberOfCommitsAsString, 10);
}
}

View File

@@ -0,0 +1,307 @@
import * as core from '@actions/core';
import NotImplementedException from './error/not-implemented-exception';
import System from './system';
import Versioning from './versioning';
afterEach(() => {
jest.restoreAllMocks();
});
describe('Versioning', () => {
describe('strategies', () => {
it('returns an object', () => {
expect(typeof Versioning.strategies).toStrictEqual('object');
});
it('has items', () => {
expect(Object.values(Versioning.strategies).length).toBeGreaterThan(2);
});
it('has an opt out option', () => {
expect(Versioning.strategies).toHaveProperty('None');
});
it('has the semantic option', () => {
expect(Versioning.strategies).toHaveProperty('Semantic');
});
it('has a strategy for tags', () => {
expect(Versioning.strategies).toHaveProperty('Tag');
});
it('has an option that allows custom input', () => {
expect(Versioning.strategies).toHaveProperty('Custom');
});
});
describe('branch', () => {
it('returns headRef when set', () => {
const headReference = jest
.spyOn(Versioning, 'headRef', 'get')
.mockReturnValue('feature-branch-1');
expect(Versioning.branch).toStrictEqual('feature-branch-1');
expect(headReference).toHaveBeenCalledTimes(1);
});
it('returns part of Ref when set', () => {
jest.spyOn(Versioning, 'headRef', 'get').mockReturnValue(undefined);
const reference = jest
.spyOn(Versioning, 'ref', 'get')
.mockReturnValue('refs/heads/feature-branch-2');
expect(Versioning.branch).toStrictEqual('feature-branch-2');
expect(reference).toHaveBeenCalledTimes(2);
});
it('prefers headRef over ref when set', () => {
const headReference = jest
.spyOn(Versioning, 'headRef', 'get')
.mockReturnValue('feature-branch-1');
const reference = jest
.spyOn(Versioning, 'ref', 'get')
.mockReturnValue('refs/heads/feature-2');
expect(Versioning.branch).toStrictEqual('feature-branch-1');
expect(headReference).toHaveBeenCalledTimes(1);
expect(reference).toHaveBeenCalledTimes(0);
});
it('returns undefined when headRef and ref are not set', () => {
const headReference = jest.spyOn(Versioning, 'headRef', 'get').mockReturnValue(undefined);
const reference = jest.spyOn(Versioning, 'ref', 'get').mockReturnValue(undefined);
expect(Versioning.branch).not.toBeDefined();
expect(headReference).toHaveBeenCalledTimes(1);
expect(reference).toHaveBeenCalledTimes(1);
});
});
describe('headRef', () => {
it('does not throw', () => {
expect(() => Versioning.headRef).not.toThrow();
});
});
describe('ref', () => {
it('does not throw', () => {
expect(() => Versioning.ref).not.toThrow();
});
});
describe('descriptionRegex', () => {
it('is a valid regex', () => {
expect(Versioning.descriptionRegex).toBeInstanceOf(RegExp);
});
test.each(['v1.1-1-g12345678', 'v0.1-2-g12345678', 'v0.0-500-gA9B6C3D0-dirty'])(
'is happy with valid %s',
(description) => {
expect(Versioning.descriptionRegex.test(description)).toBeTruthy();
},
);
test.each([undefined, 'v0', 'v0.1', 'v0.1.2', 'v0.1-2', 'v0.1-2-g'])(
'does not like %s',
(description) => {
expect(Versioning.descriptionRegex.test(description)).toBeFalsy();
// Also never expect without the v to work for any of these cases.
expect(Versioning.descriptionRegex.test(description?.substr(1))).toBeFalsy();
},
);
});
describe('determineVersion', () => {
test.each([undefined, 0, 'somethingRandom'])(
'throws for invalid strategy %s',
async (strategy) => {
await expect(Versioning.determineVersion(strategy)).rejects.toThrowErrorMatchingSnapshot();
},
);
describe('opt out strategy', () => {
it("returns 'none'", async () => {
await expect(Versioning.determineVersion('None', 'v1.0')).resolves.toMatchInlineSnapshot(
`"none"`,
);
});
});
describe('custom strategy', () => {
test.each([undefined, 0, 'v0.1', '1', 'CamelCase', 'dashed-version'])(
'returns the inputVersion for %s',
async (inputVersion) => {
await expect(Versioning.determineVersion('Custom', inputVersion)).resolves.toStrictEqual(
inputVersion,
);
},
);
});
describe('semantic strategy', () => {
it('refers to generateSemanticVersion', async () => {
const generateSemanticVersion = jest
.spyOn(Versioning, 'generateSemanticVersion')
.mockResolvedValue('1.3.37');
await expect(Versioning.determineVersion('Semantic')).resolves.toStrictEqual('1.3.37');
expect(generateSemanticVersion).toHaveBeenCalledTimes(1);
});
});
describe('tag strategy', () => {
it('refers to generateTagVersion', async () => {
const generateTagVersion = jest
.spyOn(Versioning, 'generateTagVersion')
.mockResolvedValue('0.1');
await expect(Versioning.determineVersion('Tag')).resolves.toStrictEqual('0.1');
expect(generateTagVersion).toHaveBeenCalledTimes(1);
});
});
describe('not implemented strategy', () => {
it('throws a not implemented exception', async () => {
const strategy = 'Test';
jest.spyOn(Versioning, 'strategies', 'get').mockReturnValue({ [strategy]: strategy });
await expect(Versioning.determineVersion(strategy)).rejects.toThrowError(
NotImplementedException,
);
});
});
});
describe('generateTagVersion', () => {
it('removes the v', async () => {
jest.spyOn(Versioning, 'getTag').mockResolvedValue('v1.3.37');
await expect(Versioning.generateTagVersion()).resolves.toStrictEqual('1.3.37');
});
});
describe('parseSemanticVersion', () => {
it('returns the named parts', async () => {
jest.spyOn(Versioning, 'getVersionDescription').mockResolvedValue('v0.1-2-g12345678');
await expect(Versioning.parseSemanticVersion()).resolves.toMatchObject({
tag: '0.1',
commits: '2',
hash: '12345678',
});
});
it('throws when no match could be made', async () => {
jest.spyOn(Versioning, 'getVersionDescription').mockResolvedValue('no-match-can-be-made');
await expect(Versioning.parseSemanticVersion()).rejects.toThrowErrorMatchingInlineSnapshot(
`"Failed to parse git describe output: \\"no-match-can-be-made\\"."`,
);
});
});
describe('getVersionDescription', () => {
it('returns the commands output', async () => {
const runOutput = 'someValue';
jest.spyOn(System, 'run').mockResolvedValue(runOutput);
await expect(Versioning.getVersionDescription()).resolves.toStrictEqual(runOutput);
});
});
describe('fetch', () => {
it('awaits the command', async () => {
jest.spyOn(core, 'warning').mockImplementation(() => {});
jest.spyOn(System, 'run').mockResolvedValue(undefined);
await expect(Versioning.fetch()).resolves.not.toThrow();
});
it('falls back to the second strategy when the first fails', async () => {
jest.spyOn(core, 'warning').mockImplementation(() => {});
const gitFetch = jest
.spyOn(System, 'run')
.mockResolvedValue(undefined)
.mockRejectedValueOnce(undefined);
await expect(Versioning.fetch()).resolves.not.toThrow();
expect(gitFetch).toHaveBeenCalledTimes(2);
});
});
describe('generateSemanticVersion', () => {
it('returns a proper version from description', async () => {
jest.spyOn(System, 'run').mockResolvedValue(undefined);
jest.spyOn(core, 'info').mockImplementation(() => {});
jest.spyOn(Versioning, 'isDirty').mockResolvedValue(false);
jest.spyOn(Versioning, 'hasAnyVersionTags').mockResolvedValue(true);
jest.spyOn(Versioning, 'getTotalNumberOfCommits').mockResolvedValue(2);
jest.spyOn(Versioning, 'parseSemanticVersion').mockResolvedValue({
match: '0.1-2-g1b345678',
tag: '0.1',
commits: '2',
hash: '1b345678',
});
await expect(Versioning.generateSemanticVersion()).resolves.toStrictEqual('0.1.2');
});
it('throws when dirty', async () => {
jest.spyOn(System, 'run').mockResolvedValue(undefined);
jest.spyOn(core, 'info').mockImplementation(() => {});
jest.spyOn(Versioning, 'isDirty').mockResolvedValue(true);
await expect(Versioning.generateSemanticVersion()).rejects.toThrowError();
});
it('falls back to commits only, when no tags are present', async () => {
const commits = Math.round(Math.random() * 10);
jest.spyOn(System, 'run').mockResolvedValue(undefined);
jest.spyOn(core, 'info').mockImplementation(() => {});
jest.spyOn(Versioning, 'isDirty').mockResolvedValue(false);
jest.spyOn(Versioning, 'hasAnyVersionTags').mockResolvedValue(false);
jest.spyOn(Versioning, 'getTotalNumberOfCommits').mockResolvedValue(commits);
await expect(Versioning.generateSemanticVersion()).resolves.toStrictEqual(`0.0.${commits}`);
});
});
describe('isDirty', () => {
it('returns true when there are files listed', async () => {
const runOutput = 'file.ext\nfile2.ext';
jest.spyOn(System, 'run').mockResolvedValue(runOutput);
await expect(Versioning.isDirty()).resolves.toStrictEqual(true);
});
it('returns false when there is no output', async () => {
const runOutput = '';
jest.spyOn(System, 'run').mockResolvedValue(runOutput);
await expect(Versioning.isDirty()).resolves.toStrictEqual(false);
});
});
describe('getTag', () => {
it('returns the commands output', async () => {
const runOutput = 'v1.0';
jest.spyOn(System, 'run').mockResolvedValue(runOutput);
await expect(Versioning.getTag()).resolves.toStrictEqual(runOutput);
});
});
describe('hasAnyVersionTags', () => {
it('returns false when the command returns 0', async () => {
const runOutput = '0';
jest.spyOn(System, 'run').mockResolvedValue(runOutput);
await expect(Versioning.hasAnyVersionTags()).resolves.toStrictEqual(false);
});
it('returns true when the command returns >= 0', async () => {
const runOutput = '9';
jest.spyOn(System, 'run').mockResolvedValue(runOutput);
await expect(Versioning.hasAnyVersionTags()).resolves.toStrictEqual(true);
});
});
describe('getTotalNumberOfCommits', () => {
it('returns a number from the command', async () => {
jest.spyOn(System, 'run').mockResolvedValue('9');
await expect(Versioning.getTotalNumberOfCommits()).resolves.toStrictEqual(9);
});
});
});

4822
yarn.lock

File diff suppressed because it is too large Load Diff