Skip to content

Commit

Permalink
Merge pull request #2 from Dinomite-Studios/development
Browse files Browse the repository at this point in the history
Release 1.0.0
  • Loading branch information
FejZa authored Apr 22, 2021
2 parents dad9f68 + ff95697 commit f67af50
Show file tree
Hide file tree
Showing 16 changed files with 2,069 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These are supported funding model platforms

github: FejZa
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0]

Initial release of library.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
# unity-azure-pipelines-tasks-lib
A shared library for the Unity Tools for Azure DevOps extension.
# Unity Tools for Azure DevOps - Shared Library

A library containing common implementations for pipeline tasks available in the [Unity Tools for Azure DevOps](https://github.com/Dinomite-Studios/unity-azure-pipelines-tasks) extension for [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/).

[![npm version](https://badge.fury.io/js/%40dinomite-studios%2Funity-azure-pipelines-tasks-lib.svg)](https://badge.fury.io/js/%40dinomite-studios%2Funity-azure-pipelines-tasks-lib)
[![Discord](https://img.shields.io/discord/541963006649696256.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/RpHSpxkEP6)

## Branches

| Branch | Description | Status |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| main | The main branch is built and deployed to [www.npmjs.com](https://www.npmjs.com/package/@dinomite-studios/unity-azure-pipelines-tasks-lib) | ![Build Status](https://dev.azure.com/dinomite/Unity%20Tools%20for%20Azure%20DevOps/_apis/build/status/unity-azure-pipelines-tasks-lib?branchName=main) |
| development | This branch contains latest in development features, fixes and changes and is merged to `main` once stable. | ![Build Status](https://dev.azure.com/dinomite/Unity%20Tools%20for%20Azure%20DevOps/_apis/build/status/unity-azure-pipelines-tasks-lib?branchName=development) |

## Contributions

Found and fixed a bug or improved on something? Contributions are welcome! Please target your pull request against the `development` branch or file an issue on [GitHub](https://github.com/Dinomite-Studios/unity-azure-pipelines-tasks-lib/issues) so someone else can try and implement or fix it. You are also welcome to join our [Discord](https://discord.gg/RpHSpxkEP6) for help and discussions.
42 changes: 42 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
trigger:
- main
- development

jobs:
- job:
displayName: "Build, Test and Publish"
pool:
vmImage: "ubuntu-latest"

variables:
- group: "npm-public-registry-user"

steps:
- task: NodeTool@0
inputs:
versionSpec: "10.x"
displayName: "Install Node.js"

- script: |
npm install
npm run build
displayName: "Run npm install and build"
- script: |
npm run test
displayName: "Run tests"
- script: |
npm install -g npm-cli-login
displayName: "Install NPM login utility"
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
- script: |
npm-cli-login -u $(npm.username) -p $(npm.password) -e $(npm.email)
displayName: "Login to public NPM registry"
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
- script: |
npm run publish
displayName: "Publish"
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
6 changes: 6 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './lib/unity-log-streamer';
export * from './lib/unity-tool-runner';
export * from './lib/unity-path-tools';
export * from './lib/unity-version-tools';
export * from './lib/utilities';
export * from './lib/models';
50 changes: 50 additions & 0 deletions lib/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Supported operating systems.
*/
export enum OS {
Windows = 0,
MacOS,
Linux
};

/**
* Information about a Unity engine version.
*/
export interface UnityVersionInfo {

/**
* Version identifier, e.g. 2019.3.5f1.
*/
version: string;

/**
* Version revision if available, e.g. d691e07d38ef.
*/
revision?: string;

/**
* Is this a beta version of Unity?
*/
isBeta: boolean;

/**
* Is this an alpha version of Unity?
*/
isAlpha: boolean;
};

/**
* The result of an attempt to determine a project's last used Unity editor version.
*/
export interface UnityVersionInfoResult {

/**
* Information found about the project's last used Unity editor version.
*/
info?: UnityVersionInfo;

/**
* Error information in case the version information could not be retrieved.
*/
error?: string;
}
90 changes: 90 additions & 0 deletions lib/unity-log-streamer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import tail from 'tail';

/**
* Watches the unity log output and streams it back to the console.
*/
export class UnityLogStreamer {

/**
* Prints the Unity log open headers to the console.
*/
public static printOpen(): void {
console.log("================================ UNITY LOG ===================================")
}

/**
* Streams the contents of a given logfile to the console and finished the Unity process.
* @param logFilePath The path to the log file that should be streamed to console.
* @param execResult The result retrieved from kicking of the Unity process.
*/
public static async stream(logFilePath: string, execResult: Q.Promise<number>): Promise<number> {
const logTail = new tail.Tail(logFilePath, {
fromBeginning: true,
follow: true,
logger: console,
useWatchFile: true,
flushAtEOF: true,
fsWatchOptions: {
interval: 1009
}
});

let exitCode = -1;

logTail.on("error", (error) => {
console.error('ERROR: ', error);
});

logTail.on("line", (data) => {
console.log(data);
if (data.includes('Crash!!!')) {
exitCode = -1;
}
});

try {
exitCode = await execResult;
logTail.unwatch();
return exitCode;
} catch (e) {
if (logTail) {
logTail.unwatch();
}

if (e instanceof Error) {
// WORKAROUND for license activation
// The unity exe might return the error code 3221225477 and throw an
// error because it can't write the license file on the agent, due to
// missing access rights. Nontheless the Unity process has finished
// its operation at this point and we can ignore this specific error,
// since the license is going to get released anyways after the pipeline
// has finished.
if (e.message.includes('exit code 3221225477')) {
exitCode = 0;
}

// WORKAROUND for Unity testing
// Exit code 2 means the Unity process did run successfully but at least one
// test has failed. In this case we want to handle the exit code as a non-error code.
if (e.message.includes('exit code 2')) {
exitCode = 2;
}
}

if (e instanceof Error) {
console.error(e.message);
} else {
console.error(e);
}

return exitCode;
}
}

/**
* Prints the Unity log close header to the console.
*/
public static printClose(): void {
console.log("=============================== UNITY LOG END ================================");
}
}
74 changes: 74 additions & 0 deletions lib/unity-path-tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Utilities } from './utilities';
import { OS, UnityVersionInfo } from './models';
import path = require('path');

export class UnityPathTools {

/**
* Gets the path to the Unity editors folder depending on the process platform.
* @param mode Path lookup mode: unityHub, environmentVariable or customUnityEditorsPath.
* @param customPath Contains the custom path specified by the user, if custom path mode selected.
*/
public static getUnityEditorsPath(mode: string, customPath: string | null | undefined = null): string {
if (mode === 'unityHub') {
const os = Utilities.getOS();

switch (os) {
case OS.Windows: return path.join('C:', 'Program Files', 'Unity', 'Hub', 'Editor');
case OS.MacOS: return path.join('/', 'Applications', 'Unity', 'Hub', 'Editor');
case OS.Linux: return path.join('~', 'Unity', 'Hub', 'Editor');
default: throw new Error('Operating system not supported!');
}
} else if (mode === 'environmentVariable') {
const environmentVariablePath = process.env.UNITYHUB_EDITORS_FOLDER_LOCATION as string;
if (!environmentVariablePath) {
throw Error('Environment variable UNITYHUB_EDITORS_FOLDER_LOCATION does not exist on agent.');
}

return environmentVariablePath;
} else if (mode === 'specify') {
if (!customPath) {
throw Error(`${customPath} is not a valid Unity editors folder path.`);
}

return customPath;
} else {
throw Error('Invalid path mode for editors folder lookup specified.');
}
}

/**
* Generates the full path to the Unity executable.
* @param unityEditorsPath The path to the Unity editors folder on the agent.
* @param unityVersion The Unity editor version to use.
*
* @returns The full path to the Unity editor executable file.
*/
public static getUnityExecutableFullPath(unityEditorsPath: string, unityVersion: UnityVersionInfo): string {
const unityEditorDirectory = this.getUnityEditorDirectory(unityEditorsPath, unityVersion);

const os = Utilities.getOS();
switch (os) {
case OS.Windows: return path.join(unityEditorDirectory, 'Unity.exe');
case OS.MacOS: return path.join(unityEditorDirectory, 'Unity.app', 'Contents', 'MacOS', 'Unity');
case OS.Linux: return path.join(unityEditorDirectory, 'Unity');
}
}

/**
*
* @param unityEditorsPath The path to the Unity editors folder on the agent.
* @param unityVersion The Unity editor version to use.
*
* @returns The full path to the folder containing the Unity instance executable.
*/
public static getUnityEditorDirectory(unityEditorsPath: string, unityVersion: UnityVersionInfo): string {
const os = Utilities.getOS();

switch (os) {
case OS.Windows: return path.join(unityEditorsPath, unityVersion.version, 'Editor');
case OS.MacOS: return path.join(unityEditorsPath, unityVersion.version);
case OS.Linux: return path.join(unityEditorsPath, unityVersion.version, 'Editor');
}
}
}
31 changes: 31 additions & 0 deletions lib/unity-tool-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fs = require('fs-extra');
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner";
import { UnityLogStreamer } from './unity-log-streamer';
import { Utilities } from './utilities';

/**
* The unity tool runner wraps the azure pipelines task lib tool runner
* and adds log streaming to the console.
*/
export class UnityToolRunner {

/**
* Executes the provided tool runner (cmd) and streams the Unity
* log to the console.
* @param tool The configured Unity cmd tool runner instance.
* @param logFilePath Log file path to monitor and stream to console.
* @returns Unity exit code.
*/
public static async run(tool: ToolRunner, logFilePath: string): Promise<number> {
const execResult = tool.exec();
while (execResult.isPending() && !fs.existsSync(logFilePath)) {
await Utilities.sleep(1000);
}

UnityLogStreamer.printOpen();
const result = await UnityLogStreamer.stream(logFilePath, execResult);
UnityLogStreamer.printClose();

return result;
}
}
Loading

0 comments on commit f67af50

Please sign in to comment.