mirror of
https://github.com/game-ci/unity-builder.git
synced 2025-07-04 12:25:19 -04:00
Add Android Build Settings
This commit is contained in:
parent
3523c6a934
commit
6ece6447b2
44
README.md
44
README.md
@ -301,6 +301,50 @@ Configure the android `versionCode`.
|
||||
|
||||
When not specified, the version code is generated from the version using the `major * 1000000 + minor * 1000 + patch` scheme;
|
||||
|
||||
#### androidAppBundle
|
||||
|
||||
Set this flag to `true` to build '.aab' instead of '.apk'.
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** `false`_
|
||||
|
||||
#### androidKeystoreName
|
||||
|
||||
Configure the android `keystoreName`.
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** ""_
|
||||
|
||||
#### androidKeystoreBase64
|
||||
|
||||
Configure the base64 contents of the android keystore file.
|
||||
|
||||
The contents will be decoded from base64 with `echo $androidKeystoreBase64 | base64 --decode > $androidKeystoreName`;
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** ""_
|
||||
|
||||
#### androidKeystorePass
|
||||
|
||||
Configure the android `keystorePass`.
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** ""_
|
||||
|
||||
#### androidKeyaliasName
|
||||
|
||||
Configure the android `keyaliasName`.
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** ""_
|
||||
|
||||
#### androidKeyaliasPass
|
||||
|
||||
Configure the android `keyaliasPass`.
|
||||
|
||||
_**required:** `false`_
|
||||
_**default:** ""_
|
||||
|
||||
#### allowDirtyBuild
|
||||
|
||||
Allows the branch of the build to be dirty, and still generate the build.
|
||||
|
24
action.yml
24
action.yml
@ -38,6 +38,30 @@ inputs:
|
||||
required: false
|
||||
default: ''
|
||||
description: 'The android versionCode'
|
||||
androidAppBundle:
|
||||
required: false
|
||||
default: false
|
||||
description: 'Whether to build .aab instead of .apk'
|
||||
androidKeystoreName:
|
||||
required: false
|
||||
default: ''
|
||||
description: 'The android keystoreName'
|
||||
androidKeystoreBase64:
|
||||
required: false
|
||||
default: ''
|
||||
description: 'The base64 contents of the android keystore file'
|
||||
androidKeystorePass:
|
||||
required: false
|
||||
default: ''
|
||||
description: 'The android keystorePass'
|
||||
androidKeyaliasName:
|
||||
required: false
|
||||
default: ''
|
||||
description: 'The android keyaliasName'
|
||||
androidKeyaliasPass:
|
||||
required: false
|
||||
default: ''
|
||||
description: 'The android keyaliasPass'
|
||||
customParameters:
|
||||
required: false
|
||||
default: ''
|
||||
|
@ -30,6 +30,10 @@ namespace UnityBuilderAction
|
||||
VersionApplicator.SetVersion(options["version"]);
|
||||
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
|
||||
|
||||
// Apply Android settings
|
||||
if (buildOptions.target == BuildTarget.Android)
|
||||
AndroidSettings.Apply(options);
|
||||
|
||||
// Perform build
|
||||
BuildReport buildReport = BuildPipeline.BuildPlayer(buildOptions);
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityBuilderAction.Input
|
||||
{
|
||||
public class AndroidSettings
|
||||
{
|
||||
public static void Apply(Dictionary<string, string> options)
|
||||
{
|
||||
EditorUserBuildSettings.buildAppBundle = options["customBuildPath"].EndsWith(".aab");
|
||||
if (options.TryGetValue("androidKeystoreName", out string keystoreName) && !string.IsNullOrEmpty(keystoreName))
|
||||
PlayerSettings.Android.keystoreName = keystoreName;
|
||||
if (options.TryGetValue("androidKeystorePass", out string keystorePass) && !string.IsNullOrEmpty(keystorePass))
|
||||
PlayerSettings.Android.keystorePass = keystorePass;
|
||||
if (options.TryGetValue("androidKeyaliasName", out string keyaliasName) && !string.IsNullOrEmpty(keyaliasName))
|
||||
PlayerSettings.Android.keyaliasName = keyaliasName;
|
||||
if (options.TryGetValue("androidKeyaliasPass", out string keyaliasPass) && !string.IsNullOrEmpty(keyaliasPass))
|
||||
PlayerSettings.Android.keyaliasPass = keyaliasPass;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d51cf8acfff8c941bb753e82750b60a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityBuilderAction.Input
|
||||
@ -7,6 +8,7 @@ namespace UnityBuilderAction.Input
|
||||
public class ArgumentsParser
|
||||
{
|
||||
static string EOL = Environment.NewLine;
|
||||
static readonly string[] Secrets = { "androidKeystorePass", "androidKeyaliasName", "androidKeyaliasPass" };
|
||||
|
||||
public static Dictionary<string, string> GetValidatedOptions()
|
||||
{
|
||||
@ -66,9 +68,11 @@ namespace UnityBuilderAction.Input
|
||||
// Parse optional value
|
||||
bool flagHasValue = next < args.Length && !args[next].StartsWith("-");
|
||||
string value = flagHasValue ? args[next].TrimStart('-') : "";
|
||||
bool secret = Secrets.Contains(flag);
|
||||
string displayValue = secret ? "*HIDDEN*" : "\"" + value + "\"";
|
||||
|
||||
// Assign
|
||||
Console.WriteLine($"Found flag \"{flag}\" with value \"{value}\".");
|
||||
Console.WriteLine($"Found flag \"{flag}\" with value {displayValue}.");
|
||||
providedArguments.Add(flag, value);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ namespace UnityBuilderAction.Versioning
|
||||
static void Apply(string version)
|
||||
{
|
||||
PlayerSettings.bundleVersion = version;
|
||||
PlayerSettings.macOS.buildNumber = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -62,6 +62,16 @@ else
|
||||
#
|
||||
fi
|
||||
|
||||
#
|
||||
# Create Android keystore, if needed
|
||||
#
|
||||
if [[ -z $ANDROID_KEYSTORE_NAME || -z $ANDROID_KEYSTORE_BASE64 ]]; then
|
||||
echo "Not creating Android keystore."
|
||||
else
|
||||
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > "$ANDROID_KEYSTORE_NAME"
|
||||
echo "Created Android keystore."
|
||||
fi
|
||||
|
||||
#
|
||||
# Display custom parameters
|
||||
#
|
||||
@ -111,6 +121,10 @@ xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
|
||||
-executeMethod "$BUILD_METHOD" \
|
||||
-version "$VERSION" \
|
||||
-androidVersionCode "$ANDROID_VERSION_CODE" \
|
||||
-androidKeystoreName "$ANDROID_KEYSTORE_NAME" \
|
||||
-androidKeystorePass "$ANDROID_KEYSTORE_PASS" \
|
||||
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
|
||||
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
|
||||
$CUSTOM_PARAMETERS
|
||||
|
||||
# Catch exit code
|
||||
|
@ -5,7 +5,11 @@ import Versioning from './versioning';
|
||||
|
||||
class BuildParameters {
|
||||
static async create() {
|
||||
const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform);
|
||||
const buildFile = this.parseBuildFile(
|
||||
Input.buildName,
|
||||
Input.targetPlatform,
|
||||
Input.androidAppBundle,
|
||||
);
|
||||
const buildVersion = await Versioning.determineVersion(
|
||||
Input.versioningStrategy,
|
||||
Input.specifiedVersion,
|
||||
@ -26,17 +30,22 @@ class BuildParameters {
|
||||
buildMethod: Input.buildMethod,
|
||||
buildVersion,
|
||||
androidVersionCode,
|
||||
androidKeystoreName: Input.androidKeystoreName,
|
||||
androidKeystoreBase64: Input.androidKeystoreBase64,
|
||||
androidKeystorePass: Input.androidKeystorePass,
|
||||
androidKeyaliasName: Input.androidKeyaliasName,
|
||||
androidKeyaliasPass: Input.androidKeyaliasPass,
|
||||
customParameters: Input.customParameters,
|
||||
};
|
||||
}
|
||||
|
||||
static parseBuildFile(filename, platform) {
|
||||
static parseBuildFile(filename, platform, androidAppBundle) {
|
||||
if (Platform.isWindows(platform)) {
|
||||
return `${filename}.exe`;
|
||||
}
|
||||
|
||||
if (Platform.isAndroid(platform)) {
|
||||
return `${filename}.apk`;
|
||||
return androidAppBundle ? `${filename}.aab` : `${filename}.apk`;
|
||||
}
|
||||
|
||||
return filename;
|
||||
|
@ -103,11 +103,21 @@ describe('BuildParameters', () => {
|
||||
test.each([Platform.types.Android])('appends apk for %s', async (targetPlatform) => {
|
||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(false);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildFile: `${targetPlatform}.apk` }),
|
||||
);
|
||||
});
|
||||
|
||||
test.each([Platform.types.Android])('appends aab for %s', async (targetPlatform) => {
|
||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(true);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ buildFile: `${targetPlatform}.aab` }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the build method', async () => {
|
||||
const mockValue = 'Namespace.ClassName.BuildMethod';
|
||||
jest.spyOn(Input, 'buildMethod', 'get').mockReturnValue(mockValue);
|
||||
@ -116,6 +126,46 @@ describe('BuildParameters', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the android keystore name', async () => {
|
||||
const mockValue = 'keystore.keystore';
|
||||
jest.spyOn(Input, 'androidKeystoreName', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ androidKeystoreName: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the android keystore base64-encoded content', async () => {
|
||||
const mockValue = 'secret';
|
||||
jest.spyOn(Input, 'androidKeystoreBase64', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ androidKeystoreBase64: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the android keystore pass', async () => {
|
||||
const mockValue = 'secret';
|
||||
jest.spyOn(Input, 'androidKeystorePass', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ androidKeystorePass: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the android keyalias name', async () => {
|
||||
const mockValue = 'secret';
|
||||
jest.spyOn(Input, 'androidKeyaliasName', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ androidKeyaliasName: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the android keyalias pass', async () => {
|
||||
const mockValue = 'secret';
|
||||
jest.spyOn(Input, 'androidKeyaliasPass', 'get').mockReturnValue(mockValue);
|
||||
await expect(BuildParameters.create()).resolves.toEqual(
|
||||
expect.objectContaining({ androidKeyaliasPass: mockValue }),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the custom parameters', async () => {
|
||||
const mockValue = '-profile SomeProfile -someBoolean -someValue exampleValue';
|
||||
jest.spyOn(Input, 'customParameters', 'get').mockReturnValue(mockValue);
|
||||
|
@ -28,8 +28,13 @@ class Docker {
|
||||
buildFile,
|
||||
buildMethod,
|
||||
buildVersion,
|
||||
customParameters,
|
||||
androidVersionCode,
|
||||
androidKeystoreName,
|
||||
androidKeystoreBase64,
|
||||
androidKeystorePass,
|
||||
androidKeyaliasName,
|
||||
androidKeyaliasPass,
|
||||
customParameters,
|
||||
} = parameters;
|
||||
|
||||
const command = `docker run \
|
||||
@ -49,6 +54,11 @@ class Docker {
|
||||
--env BUILD_METHOD="${buildMethod}" \
|
||||
--env VERSION="${buildVersion}" \
|
||||
--env ANDROID_VERSION_CODE="${androidVersionCode}" \
|
||||
--env ANDROID_KEYSTORE_NAME="${androidKeystoreName}" \
|
||||
--env ANDROID_KEYSTORE_BASE64="${androidKeystoreBase64}" \
|
||||
--env ANDROID_KEYSTORE_PASS="${androidKeystorePass}" \
|
||||
--env ANDROID_KEYALIAS_NAME="${androidKeyaliasName}" \
|
||||
--env ANDROID_KEYALIAS_PASS="${androidKeyaliasPass}" \
|
||||
--env CUSTOM_PARAMETERS="${customParameters}" \
|
||||
--env HOME=/github/home \
|
||||
--env GITHUB_REF \
|
||||
|
@ -45,6 +45,32 @@ class Input {
|
||||
return core.getInput('androidVersionCode');
|
||||
}
|
||||
|
||||
static get androidAppBundle() {
|
||||
const input = core.getInput('androidAppBundle') || 'false';
|
||||
|
||||
return input === 'true' ? 'true' : 'false';
|
||||
}
|
||||
|
||||
static get androidKeystoreName() {
|
||||
return core.getInput('androidKeystoreName') || '';
|
||||
}
|
||||
|
||||
static get androidKeystoreBase64() {
|
||||
return core.getInput('androidKeystoreBase64') || '';
|
||||
}
|
||||
|
||||
static get androidKeystorePass() {
|
||||
return core.getInput('androidKeystorePass') || '';
|
||||
}
|
||||
|
||||
static get androidKeyaliasName() {
|
||||
return core.getInput('androidKeyaliasName') || '';
|
||||
}
|
||||
|
||||
static get androidKeyaliasPass() {
|
||||
return core.getInput('androidKeyaliasPass') || '';
|
||||
}
|
||||
|
||||
static get allowDirtyBuild() {
|
||||
const input = core.getInput('allowDirtyBuild') || 'false';
|
||||
|
||||
|
@ -131,6 +131,89 @@ describe('Input', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('androidAppBundle', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.androidAppBundle).toStrictEqual('false');
|
||||
});
|
||||
|
||||
it('returns true when string true is passed', () => {
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue('true');
|
||||
expect(Input.androidAppBundle).toStrictEqual('true');
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns false when string false is passed', () => {
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue('false');
|
||||
expect(Input.androidAppBundle).toStrictEqual('false');
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('androidKeystoreName', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.androidKeystoreName).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'keystore.keystore';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.androidKeystoreName).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('androidKeystoreBase64', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.androidKeystoreBase64).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'secret';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.androidKeystoreBase64).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('androidKeystorePass', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.androidKeystorePass).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'secret';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.androidKeystorePass).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('androidKeyaliasName', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.androidKeyaliasName).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'secret';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.androidKeyaliasName).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('androidKeyaliasPass', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.androidKeyaliasPass).toStrictEqual('');
|
||||
});
|
||||
|
||||
it('takes input from the users workflow', () => {
|
||||
const mockValue = 'secret';
|
||||
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
|
||||
expect(Input.androidKeyaliasPass).toStrictEqual(mockValue);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allowDirtyBuild', () => {
|
||||
it('returns the default value', () => {
|
||||
expect(Input.allowDirtyBuild).toStrictEqual('false');
|
||||
|
Loading…
Reference in New Issue
Block a user