Compare commits

..

No commits in common. "main" and "feat-mega-image" have entirely different histories.

15 changed files with 408 additions and 356 deletions

View File

@ -1,12 +1,9 @@
name: 🤓 Individual Runners
on:
push:
branches: [main]
paths:
- versions.txt
- dockerfiles/runner.dockerfile
- .gitea/workflows/create-image.yaml
# push:
# branches: [main]
# paths: ['versions.txt']
workflow_dispatch:
inputs:
@ -33,30 +30,30 @@ jobs:
build-docker:
runs-on: ubuntu-latest
env:
DOCKER_REGISTRY_DOMAIN: gitea.clubconjure.com
DOCKER_REGISTRY: gitea.clubconjure.com/conjure/
DOCKER_REGISTRY: docker.lakes.house/
UNITY_VERSION: ${{ github.event.inputs.unity_version }}
GAMECI_OS: ubuntu
GAMECI_VERSION: 3
strategy:
max-parallel: 2
matrix:
platform:
- webgl
- android
- ios
- mac-mono
- windows-mono
- linux-il2cpp
name: Build 🐳 ${{ matrix.platform }}
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == '' || github.event.inputs.platform == matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Login to Container Registry
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY_DOMAIN }}
username: ${{ secrets.PIPELINES_USERNAME }}
password: ${{ secrets.REGISTRY_TOKEN }}
registry: ${{ env.DOCKER_REGISTRY }} # usual credentials for bastion.local
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get Unity Version
if: ${{ github.event.inputs.unity_version == '' }}
@ -66,15 +63,14 @@ jobs:
echo "Unity Version: $VERSION"
- name: Docker Build
run: .gitea/workflows/scripts/build-runner-image.sh
run: .gitea/workflows/scripts/build-image.sh
id: build-image
env:
UNITY_VERSION: ${{ env.UNITY_VERSION }}
UNITY_MODULES: ${{ matrix.platform }}
GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
GAMECI_OS: ${{ env.GAMECI_OS }}
UNITY_PLATFORM: ${{ matrix.platform }}
IMAGE: ${{ env.DOCKER_REGISTRY }}unityci/editor
TAG: ${{ env.GAMECI_OS }}-${{ env.UNITY_VERSION }}-${{ matrix.platform }}-runner
GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
ONLY_RUNNER_TAG: true
- name: Docker Push
run: |

View File

@ -6,9 +6,7 @@ on:
paths:
- versions.txt
- dockerfiles/runner.dockerfile
- .gitea/workflows/create-runner.yaml
workflow_dispatch:
- .github/workflows/create-runner.yaml
jobs:
build-docker:
@ -16,7 +14,6 @@ jobs:
env:
DOCKER_REGISTRY: docker.lakes.house/
UNITY_MODULES: webgl android ios mac-mono windows-mono linux-il2cpp
GAMECI_OS: ubuntu
GAMECI_VERSION: 3
name: Build
@ -42,9 +39,8 @@ jobs:
UNITY_VERSION: ${{ env.UNITY_VERSION }}
UNITY_MODULES: ${{ env.UNITY_MODULES }}
GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
GAMECI_OS: ${{ env.GAMECI_OS }}
GAMECI_OS: ubuntu
IMAGE: ${{ env.DOCKER_REGISTRY }}unityci/editor
TAG: ${{ env.GAMECI_OS }}-${{ env.UNITY_VERSION }}-runner
- name: Docker Push
run: |

View File

@ -0,0 +1,101 @@
#!/bin/sh
# Ensure UNITY_VERSION is set, pull from arguments if not
if [ -z "${UNITY_VERSION}" ]; then
if [ -n "$1" ]; then
UNITY_VERSION=$1
else
echo "Error: UNITY_VERSION is not set."
exit 1
fi
fi
# Ensure UNITY_PLATFORM is set, pull from arguments if not
if [ -z "${UNITY_PLATFORM}" ]; then
if [ -n "$2" ]; then
UNITY_PLATFORM=$2
else
echo "Error: UNITY_PLATFORM is not set."
exit 1
fi
fi
# Ensure IMAGE is set, pull from arguments if not
if [ -z "${IMAGE}" ]; then
if [ -n "$3" ]; then
IMAGE=$3
else
echo "Error: IMAGE is not set."
exit 1
fi
fi
# Ensure GAME_CI_VERSION is set, default to 3 if not
if [ -z "${GAMECI_VERSION}" ]; then
GAMECI_VERSION=3
fi
# Ensure IMAGE_OS is set, default to ubuntu if not
if [ -z "${GAMECI_OS}" ]; then
# windows-il2cpp requires windows OS
if [ "${UNITY_PLATFORM}" = "windows-il2cpp" ]; then
GAMECI_OS="windows"
else
GAMECI_OS="ubuntu"
fi
# TODO: MacOS probably requires a mac image.
# Might be worth just putting this in the strategy at this point
fi
# Ensure PLATFORM is set, default to the current system if not
if [ -z "${PLATFORM}" ]; then
PLATFORM=$(uname -m)
case "${PLATFORM}" in
x86_64) PLATFORM="linux/amd64" ;;
arm64) PLATFORM="linux/arm64" ;;
*)
echo "Error: Unsupported platform ${PLATFORM}."
exit 1
;;
esac
fi
BASE_IMAGE=unityci/editor:${GAMECI_OS}-${UNITY_VERSION}-${UNITY_PLATFORM}-${GAMECI_VERSION}
SHORT_TAG=${GAMECI_OS}-${UNITY_VERSION}-${UNITY_PLATFORM}
TAG=${SHORT_TAG}-runner
FULL_IMAGE=${IMAGE}:${TAG}
ADDITIONAL_TAGS=""
if [ "${ONLY_RUNNER_TAG}" != "true" ]; then
ADDITIONAL_TAGS="$ADDITIONAL_TAGS -t ${IMAGE}:${SHORT_TAG}"
ADDITIONAL_TAGS="$ADDITIONAL_TAGS -t ${IMAGE}:${SHORT_TAG}-${GAMECI_VERSION}"
fi
echo "Building Docker image ${FULL_IMAGE}"
echo "- Platfrom: ${PLATFORM}"
echo "- Base: ${BASE_IMAGE}"
echo "- Tag: ${TAG}"
echo "- Image: ${IMAGE}:${TAG}"
echo "- Additional Tags: ${ADDITIONAL_TAGS}"
docker build \
--platform ${PLATFORM} \
--build-arg BASE_IMAGE=${BASE_IMAGE} \
${ADDITIONAL_TAGS} \
-t ${FULL_IMAGE} \
${DOCKER_BUILD_ARGS} \
-f ./dockerfiles/individual.dockerfile .
if [ $? -ne 0 ]; then
echo "Error: Docker build failed."
exit 1
fi
# Export IMAGE and TAG for GitHub Actions
if [ -n "$GITHUB_OUTPUT" ]; then
echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT
echo "TAG=${TAG}" >> $GITHUB_OUTPUT
echo "FULL_IMAGE=${FULL_IMAGE}" >> $GITHUB_OUTPUT
fi

56
.gitea/workflows/scripts/build-runner-image.sh Executable file → Normal file
View File

@ -11,16 +11,16 @@ fi
# Fetch the changelog and extract the changeset ID for the specified Unity version
# This is because hub doesnt remember every version of unity and uses the changset for the exact id lookup.
# if [ -z "${UNITY_CHANGESET}" ]; then
# echo "Warning: No changeset provided. Scraping one from the change logs."
# echo "This might take a while. Use the UNITY_CHANGESET to avoid this lookup."
# CHANGELOG_URL="https://unity.com/releases/editor/whats-new/${UNITY_VERSION}"
# UNITY_CHANGESET=$(curl -s -r 0-500 "$CHANGELOG_URL" | grep -oP 'unityhub://(?:[0-9a-z.])+/\K([a-z0-9]+)' | head -n 1)
# if [ -z "$UNITY_CHANGESET" ]; then
# echo "Error: Could not extract changeset for Unity version ${UNITY_VERSION}."
# exit 1
# fi
# fi
if [ -z "${UNITY_CHANGESET}" ]; then
echo "Warning: No changeset provided. Scraping one from the change logs."
echo "This might take a while. Use the UNITY_CHANGESET to avoid this lookup."
CHANGELOG_URL="https://unity.com/releases/editor/whats-new/${UNITY_VERSION}"
UNITY_CHANGESET=$(curl -s -r 0-500 "$CHANGELOG_URL" | grep -oP 'unityhub://(?:[0-9a-z.])+/\K([a-z0-9]+)' | head -n 1)
if [ -z "$UNITY_CHANGESET" ]; then
echo "Error: Could not extract changeset for Unity version ${UNITY_VERSION}."
exit 1
fi
fi
# Ensure we have some modules
if [ -z "${UNITY_MODULES}" ]; then
@ -32,6 +32,16 @@ if [ -z "${UNITY_MODULES}" ]; then
fi
fi
# Ensure IMAGE is set, pull from arguments if not
if [ -z "${IMAGE}" ]; then
if [ -n "$3" ]; then
IMAGE=$3
else
echo "Error: IMAGE is not set."
exit 1
fi
fi
# Ensure GAME_CI_VERSION is set, default to 3 if not
if [ -z "${GAMECI_VERSION}" ]; then
GAMECI_VERSION=3
@ -64,22 +74,6 @@ if [ -z "${PLATFORM}" ]; then
esac
fi
# Ensure IMAGE is set, pull from arguments if not
if [ -z "${IMAGE}" ]; then
if [ -n "$3" ]; then
IMAGE=$3
else
echo "Error: IMAGE is not set."
exit 1
fi
fi
# Ensure the TAG is set, default to latest if not
if [ -z "${TAG}" ]; then
TAG="${GAMECI_OS}-${UNITY_VERSION}-runner"
fi
# Ensure some additional build settings are set
if [ -z "${DOCKER_BUILD_ARGS}" ]; then
DOCKER_BUILD_ARGS=""
@ -87,19 +81,21 @@ fi
BASE_TAG=${GAMECI_OS}-${UNITY_VERSION}-base-${GAMECI_VERSION}
BASE_IMAGE=unityci/editor:${BASE_TAG}
DEST_IMAGE=${IMAGE}:${TAG}
DEST_TAG=${GAMECI_OS}-${UNITY_VERSION}-runner
DEST_IMAGE=${IMAGE}:${DEST_TAG}
echo "Building Docker image ${DEST_IMAGE}"
echo "- Version: ${UNITY_VERSION}"
echo "- Changeset: ${UNITY_CHANGESET}"
echo "- Platfrom: ${PLATFORM}"
echo "- Base: ${BASE_IMAGE}"
echo "- Tag: ${TAG}"
echo "- Tag: ${DEST_TAG}"
echo "- Image: ${DEST_IMAGE}"
docker build \
docker buildx build \
--platform ${PLATFORM} \
--build-arg "VERSION=${UNITY_VERSION}" \
--build-arg "CHANGESET=${UNITY_CHANGESET}" \
--build-arg "BASE_IMAGE=${BASE_IMAGE}" \
--build-arg "MODULE=${UNITY_MODULES}" \
-t ${DEST_IMAGE} ${DOCKER_BUILD_ARGS} \
@ -113,6 +109,6 @@ fi
# Export IMAGE and TAG for GitHub Actions
if [ -n "$GITHUB_OUTPUT" ]; then
echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT
echo "TAG=$TAG" >> $GITHUB_OUTPUT
echo "TAG=$DEST_TAG" >> $GITHUB_OUTPUT
echo "FULL_IMAGE=${DEST_IMAGE}" >> $GITHUB_OUTPUT
fi

View File

@ -1,136 +1,167 @@
#!/usr/bin/env python3
import requests
import sys
import os
import re
import requests
import base64
import json
DOCKER_REGISTRY = os.getenv('DOCKER_REGISTRY')
DOCKER_USERNAME = os.getenv('DOCKER_USERNAME')
DOCKER_PASSWORD = os.getenv('DOCKER_PASSWORD')
IMAGE = os.getenv('IMAGE')
def get_docker_hub_tags(repository, limit=1000):
"""
Get tags for a Docker Hub repository
"""
url = f"https://hub.docker.com/v2/repositories/{repository}/tags"
params = {"page_size": limit}
README_PATH="README.md"
DOCKERHUB_REGISTRY = "registry-1.docker.io"
# Check for Docker Hub credentials in environment variables
username = os.getenv('DOCKER_USERNAME')
password = os.getenv('DOCKER_PASSWORD')
def format_bytes(size_bytes : int) -> str:
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if size_bytes < 1024.0:
human_size = f"{size_bytes:.2f} {unit}"
break
size_bytes /= 1024.0
return human_size
# Create session for potential authentication
session = requests.Session()
def get_tags_with_size(registry : str, authorization : str, repository : str) -> list[object]:
url = f"https://{registry}/v2/{repository}/tags/list"
headers = {
"Authorization": f"{authorization}",
"Accept": "application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json"
}
print(f"Querying {url}...")
resp = requests.get(url, headers=headers)
resp.raise_for_status()
all_tags = resp.json()['tags']
tags = []
for t in all_tags:
url = f"https://{registry}/v2/{repository}/manifests/{t}"
print(f"- {t} looking up {url}...")
resp = requests.get(url, headers=headers)
resp.raise_for_status()
layers = resp.json()['layers']
tag = {}
tag['name'] = t
tag['registry'] = registry
tag['size'] = sum(layer['size'] for layer in layers)
tag['layers'] = layers
tags.append(tag)
return tags
def login_dockerhub(username : str, password : str, forRepository : str) -> str:
# Get token for Docker Hub API
auth_url = "https://auth.docker.io/token"
service = "registry.docker.io"
scope = f"repository:{forRepository}:pull"
params = {"service": service, "scope": scope}
# Authenticate if credentials are provided
if username and password:
resp = requests.get(auth_url, params=params, auth=(username, password))
auth_url = "https://hub.docker.com/v2/users/login/"
auth_data = {"username": username, "password": password}
try:
auth_response = session.post(auth_url, json=auth_data)
auth_response.raise_for_status()
# Token is automatically stored in the session cookies
except requests.exceptions.RequestException as e:
print(f"Warning: Docker Hub authentication failed: {e}")
# Continue without authentication
all_tags = []
while url:
response = session.get(url, params=params)
if response.status_code != 200:
print(f"Error: Unable to fetch tags. Status code: {response.status_code}")
print(f"Response: {response.text}")
sys.exit(1)
data = response.json()
all_tags.extend([tag["name"] for tag in data["results"]])
# Get next page URL if it exists
url = data.get("next")
# Clear params since the next URL already includes them
params = {}
return all_tags
def get_private_registry_tags(registry, repository):
"""
Get tags for a repository in a private registry
"""
# Check for registry credentials in environment variables
username = os.getenv('DOCKER_USERNAME')
password = os.getenv('DOCKER_PASSWORD')
# Ensure registry URL starts with https://
registry_url = registry if registry.startswith(('http://', 'https://')) else f'https://{registry}'
# Try to get an auth token first (token-based auth)
headers = {}
url = f"{registry_url}/v2/{repository}/tags/list"
# Make the request with appropriate authentication
if username and password:
# Use basic auth if we have credentials but no token
response = requests.get(url, headers=headers, auth=(username, password))
else:
resp = requests.get(auth_url, params=params)
resp.raise_for_status()
token = resp.json()["token"]
return f"Bearer {token}"
# Use token auth or no auth
response = requests.get(url, headers=headers)
def login_registry(username : str, password : str) -> str:
credentials = f"{username}:{password}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()
return f"Basic {encoded_credentials}"
if response.status_code != 200:
print(f"Error: Unable to fetch tags. Status code: {response.status_code}")
print(f"Response: {response.text}")
sys.exit(1)
def create_label(repository: str, tag : object) -> str:
name = tag['name']
registry = tag['registry']
size = format_bytes(tag['size'])
link = f"https://{registry}/repo/{repository}/tag/{name}"
if registry == DOCKERHUB_REGISTRY:
link = f"https://hub.docker.com/layers/{repository}/{name}"
return f"[🐳 View]({link})<br>📦 {size}"
data = response.json()
return data.get("tags", [])
def create_table(repository : str, tags : list[object]) -> str:
markdown : str = ""
def format_tag(registry, repository, platform, tag ):
# Get color for platform, default to blue if not found
url = f"https://hub.docker.com/r/{repository}/tags?name={tag}"
versions : dict[str, dict[str, str]] = {}
all_modules : set[str] = set()
if registry:
url = f"https://{registry}/{repository}/tag/{tag}"
# Build a table of versions
pattern = re.compile(r"(?P<os>\w+)(?P<version>-\d+\.\d+\.\d+\w*)(?P<modules>-[a-zA-Z-0-9]+)?-runner")
for tag in tags:
matches = pattern.match(tag['name'])
os = matches.group("os").strip('-')
version = matches.group("version").strip('-')
modules = matches.group("modules").strip('-') if matches.group('modules') else "all"
all_modules.add(modules)
if version not in versions:
versions[version] = {}
versions[version][modules] = create_label(repository, tag)
# Print the table heading
sorted_mods = sorted([m for m in all_modules if m])
markdown += f"|Unity|{'|'.join(sorted_mods)}|\n"
markdown += f"|-----|{'|'.join('-' * len(m) for m in sorted_mods)}|\n"
# For each item, check each available module and see if this version has that available.
for ver, mods in versions.items():
row = [ mods[mod] if mod in mods else '' for mod in sorted_mods ]
markdown += f"|{ver}|{'|'.join(row)}|\n"
return markdown
def replace_readme_table(readme : str, table : str) -> str:
pattern = r"(<!-- table -->).*(<!-- /table -->)"
return re.sub(pattern, r'\1\n' + table + r'\2', readme, flags=re.DOTALL)
return f"[🐳 View]({url})"
def main():
auth_token : str
registry : str
if DOCKER_REGISTRY:
registry = DOCKER_REGISTRY
auth_token = login_registry(DOCKER_USERNAME, DOCKER_PASSWORD)
# Check for registry in environment variable if not specified in args
registry = os.getenv('DOCKER_REGISTRY')
repository = os.getenv('IMAGE')
if registry:
print(f"Fetching tags for {repository} from {registry}...")
tags = get_private_registry_tags(registry, repository)
else:
registry = DOCKERHUB_REGISTRY
auth_token = login_dockerhub(DOCKER_USERNAME, DOCKER_PASSWORD, IMAGE)
print(f"Fetching tags for {repository} from Docker Hub...")
tags = get_docker_hub_tags(repository, 100)
print('Fetching Tags...')
tags = get_tags_with_size(registry, auth_token, IMAGE)
# Sort tags
tags.sort()
print('Writing Readme...')
table = create_table(IMAGE, tags)
if os.path.exists(README_PATH):
with open(README_PATH, "r", encoding='utf-8') as f:
readme = f.read()
with open(README_PATH, "w", encoding='utf-8') as f:
f.write(replace_readme_table(readme, table))
versions={}
pattern = re.compile(r"(\w+)-(\d+\.\d+\.\d+\w*)-(\w+)(-\w+)?-runner")
for tag in tags:
matches = pattern.match(tag)
if matches:
groups = matches.groups()
version = groups[1]
component = groups[2]
if groups[3]: # If there is a variance, then apply it to the component
component = f"{component} ({groups[3].strip('-')})"
if version not in versions:
versions[version] = {}
versions[version][component] = tag
# Get all unique components across all versions
print(versions)
all_components = set()
for version_components in versions.values():
all_components.update(version_components.keys())
all_components = sorted(list(all_components))
# Create markdown table header with components as columns
markdown = "| unity |"
for component in all_components:
markdown += f" {component} |"
markdown += "\n|---------|" + "----------|" * len(all_components) + "\n"
for version in sorted(versions.keys()):
markdown += f"| {version} |"
for component in all_components:
tag = versions[version].get(component, "")
if tag:
markdown += format_tag(registry, repository, component, tag) + " |"
else:
markdown += "❌ N/A |"
markdown += "\n"
# Read existing README.md
print("Updating README")
readme_path = "README.md"
if os.path.exists(readme_path):
with open(readme_path, 'r') as f:
content = f.read()
# Replace content between markers
pattern = r'(<!-- table -->).*(<!-- /table -->)'
new_content = re.sub(pattern, r'\1\n' + markdown + r'\2', content, flags=re.DOTALL)
# Write back to README.md
with open(readme_path, 'w') as f:
f.write(new_content)
print("Done!")
if __name__ == "__main__":
main()

View File

@ -6,18 +6,14 @@ on:
branches: [main]
paths:
- versions.txt
- dockerfiles/runner.dockerfile
- Dockerfile
- .github/workflows/create-image.yaml
jobs:
build-docker:
runs-on: ubuntu-latest
env:
GAMECI_OS: ubuntu
GAMECI_VERSION: 3
strategy:
max-parallel: 2
matrix:
platform:
- webgl
@ -47,15 +43,13 @@ jobs:
echo "Unity Version: $VERSION"
- name: Docker Build
run: .gitea/workflows/scripts/build-runner-image.sh
run: .gitea/workflows/scripts/build-individual-image.sh
id: build-image
env:
UNITY_VERSION: ${{ env.UNITY_VERSION }}
UNITY_MODULES: ${{ matrix.platform }}
GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
GAMECI_OS: ${{ env.GAMECI_OS }}
UNITY_PLATFORM: ${{ matrix.platform }}
IMAGE: "${{ vars.DOCKERHUB_USERNAME }}/unity-runner"
TAG: ${{ env.GAMECI_OS }}-${{ env.UNITY_VERSION }}-${{ matrix.platform }}-runner
GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
- name: Docker Push
run: |

View File

@ -14,8 +14,6 @@ jobs:
runs-on: ubuntu-latest
env:
UNITY_MODULES: webgl android ios mac-mono windows-mono linux-il2cpp
GAMECI_OS: ubuntu
GAMECI_VERSION: 3
environment:
name: Docker Hub
@ -43,9 +41,8 @@ jobs:
UNITY_VERSION: ${{ env.UNITY_VERSION }}
UNITY_MODULES: ${{ env.UNITY_MODULES }}
GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
GAMECI_OS: ${{ env.GAMECI_OS }}
GAMECI_OS: ubuntu
IMAGE: "${{ vars.DOCKERHUB_USERNAME }}/unity-runner"
TAG: ${{ env.GAMECI_OS }}-${{ env.UNITY_VERSION }}-runner
- name: Docker Push
run: |

View File

@ -3,7 +3,7 @@ name: Update README with Docker Images
on:
workflow_dispatch: # Allow manual triggering
workflow_run: # Trigger from images
workflows: ["Individual Runners", "😎 Uber Runner"]
workflows: ["Create Images"]
types: [completed]
push:
paths:
@ -42,5 +42,4 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add README.md
git pull
git diff --quiet && git diff --staged --quiet || (git commit -m "Update Docker images table in README" && git push)

1
.gitignore vendored
View File

@ -1,2 +1 @@
gameci
test

View File

@ -1,83 +1,19 @@
# Unity Runners for Gitea
# Unity Docker Images
Gitea [Act Runner]() images with Unity3D installed and additional modules for packaging, building, and testing your Unity3D projects and games.
Built on the work done by [GameCI](https://game.ci/docs/docker/docker-images/), these images are designed to run your build scripts directly in the container.
## Why?
The [`game-ci/unity-builder`](https://game.ci/docs/github/builder) action builds your project inside a Docker container. However with the way Gitea works, this causes issues as you end up with docker-in-docker containers which are not entirely stable, have issues with caching, and annoying to manage with files.
Using this image, you are directly able to launch your runner scripts inside the same container that Unity is in.
> [!TIP]
> You can use either the `jobs.<name>.container.image` to override the job's current container image, or you can directly add a label to your runner such as:
> ```yaml
> - unity-6000.0.35f1-android:docker://lachee/unity-runner:6000.0.35f1-android-runner
> - unity-6000.0.35f1-webgl:docker://lachee/unity-runner:6000.0.35f1-webgl-runner
> ```
> There is a included script that will automate your labels as a cron job.
## Images
A table of available Docker images for Unity CI/CD:
<!-- table -->
|Unity|all|android|ios|linux-il2cpp|mac-mono|webgl|windows-mono|
|-----|---|-------|---|------------|--------|-----|------------|
|6000.0.35f1|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-runner)<br>📦 15.40 GB|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-android-runner)<br>📦 12.39 GB|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-ios-runner)<br>📦 10.47 GB|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-linux-il2cpp-runner)<br>📦 10.58 GB|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-mac-mono-runner)<br>📦 10.70 GB|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-webgl-runner)<br>📦 11.60 GB|[🐳 View](https://docker.lakes.house/repo/unityci/editor/tag/ubuntu-6000.0.35f1-windows-mono-runner)<br>📦 11.07 GB|
| unity | android | ios | linux (il2cpp) | mac (mono) | webgl | windows (mono) |
|---------|----------|----------|----------|----------|----------|----------|
| 6000.0.35f1 |[🐳 View](https://docker.lakes.house/unityci/editor/tag/ubuntu-6000.0.35f1-android-runner) |[🐳 View](https://docker.lakes.house/unityci/editor/tag/ubuntu-6000.0.35f1-ios-runner) |[🐳 View](https://docker.lakes.house/unityci/editor/tag/ubuntu-6000.0.35f1-linux-il2cpp-runner) |[🐳 View](https://docker.lakes.house/unityci/editor/tag/ubuntu-6000.0.35f1-mac-mono-runner) |[🐳 View](https://docker.lakes.house/unityci/editor/tag/ubuntu-6000.0.35f1-webgl-runner) |[🐳 View](https://docker.lakes.house/unityci/editor/tag/ubuntu-6000.0.35f1-windows-mono-runner) |
<!-- /table -->
## Included Software
### Tools
- Unity3D (as `unity-editor`)
- Blender `3.4`
- CMake
- CURL
- GCC
- Git
- Make
- Zip & Unzip
### Language and Runtime
- Bash `5.1.16(1)-release`
- Node.js `20.19.5`
- Python3 `3.10.12`
- lib-sqlite3
- libssl
### Package Management
- NPM `11.6.0`
- PNPM `10.17.0`
- pip3 `22.0.2`
# Usage
### SDKs
- AWS CLI
- Azure SDK
## How
...
TODO
...
TODO:
https://github.com/mob-sakai/unity-changeset/blob/main/src/unityGraphQL.ts
> [!NOTE]
> Depending on your size of project, you might find it would randomly fail. This is likely your gitea-runner running out of memory and terminating.
> I personally found proxmox did this a lot as the memory grew faster than what the host could balloon. I recommend setting 4GB **MIN** so it always has space to balloon.
> [!TIP]
> I run a 10GB VM with 500GB just for Unity3D builds. I have a custom `unity` label to prevent my other runners getting builds.
## Uber Image?
There is a experimental UberRunner.dockerfile. However there have been some issues trying to build and host it:
- The image is **MASSIVE**, totally around 25GB for a single docker image.
- Unless you precache the image on your runners, it would take a long time downloading each run
- It requires a lot of memory and storage to build
- GitHub runners cannot build it, they run out of storage
- My upload speed is terrible, so i cannot upload them
- Runners tend to run out of memory and kill the process. I had to dedicate building it to my "-large" runners (with 10GB memory & 500GB storage).
- Android requires additional steps so you just end up reinventing the wheel.
- There is no point?
- Sure you only need one image, but you still have the same storage concerns than lots of smaller images
- Let docker be smart and cache the layers. Didn't need to make a mega bundle just for better caching
- If you need everything / weird setups, just run in [`host`](https://docs.gitea.com/usage/actions/act-runner#labels) mode with unity pre-installed on a VM.
- It would cache everything for you too!
- Less hassle having to deal with licensing.
use this library to get the change set.
Might as well orchestrate the build with it too.

View File

@ -0,0 +1,41 @@
ARG BASE_IMAGE=unityci/editor
FROM $BASE_IMAGE
RUN apt-get update && \
apt-get install -y \
git \
curl \
gcc \
make \
libssl-dev \
zlib1g-dev \
libsqlite3-dev
# Set up the scripts
RUN git clone --depth=1 https://github.com/game-ci/unity-builder.git /gameci && \
cp -rf /gameci/dist/platforms/ubuntu/steps /steps && \
cp -rf /gameci/dist/default-build-script /UnityBuilderAction && \
cp /gameci/dist/platforms/ubuntu/entrypoint.sh /entrypoint.sh
# Set up Node.js environment for github actions
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && \
npm install -g npm@latest
# Install Blender
ARG BLENDER_SHORT_VERSION=3.4
ARG BLENDER_FULL_VERSION=3.4.1
RUN echo "BLENDER_FULL_VERSION: $BLENDER_FULL_VERSION" && \
echo echo "BLENDER_SHORT_VERSION: $BLENDER_SHORT_VERSION" && \
apt-get install -y wget && \
wget https://download.blender.org/release/Blender$BLENDER_SHORT_VERSION/blender-$BLENDER_FULL_VERSION-linux-x64.tar.xz && \
tar -xf blender-$BLENDER_FULL_VERSION-linux-x64.tar.xz && \
rm blender-$BLENDER_FULL_VERSION-linux-x64.tar.xz
ENV PATH="$PATH:/blender-$BLENDER_FULL_VERSION-linux-x64"
# Add custom scripts
COPY scripts/build.sh /build.sh
RUN chmod +x /build.sh
# Done
ENTRYPOINT [ "/entrypoint.sh" ]

View File

@ -4,36 +4,35 @@ ARG HUB_IMAGE="unityci/hub"
###########################
# Builder #
###########################
FROM $BASE_IMAGE AS editor
FROM $HUB_IMAGE AS builder
# Install editor
ARG VERSION
COPY --from=editor "$UNITY_PATH/" /opt/unity/editors/$VERSION/
# Install CMake
RUN apt-get update && apt-get install -y cmake
COPY --chmod=770 scripts/install-module.sh /bin/install-module
ARG CHANGESET
RUN unity-hub install --version "$VERSION" --changeset "$CHANGESET" \
| tee /var/log/install-editor.log && grep 'Failed to install\|Error while installing an editor\|Completed with errors' /var/log/install-editor.log | exit $(wc -l)
# Install modules for that editor
ARG MODULE
RUN install-module "$VERSION" "$MODULE" android
RUN install-module "$VERSION" "$MODULE" ios
RUN install-module "$VERSION" "$MODULE" appletv
RUN install-module "$VERSION" "$MODULE" linux-mono
RUN install-module "$VERSION" "$MODULE" linux-il2cpp
RUN install-module "$VERSION" "$MODULE" webgl
RUN install-module "$VERSION" "$MODULE" windows
RUN install-module "$VERSION" "$MODULE" vuforia-ar
RUN install-module "$VERSION" "$MODULE" windows-mono
RUN install-module "$VERSION" "$MODULE" lumin
RUN install-module "$VERSION" "$MODULE" mac-mono
RUN install-module "$VERSION" "$MODULE" mac-il2cpp
RUN install-module "$VERSION" "$MODULE" universal-windows-platform
RUN install-module "$VERSION" "$MODULE" uwp-il2cpp
RUN install-module "$VERSION" "$MODULE" uwp-.net
RUN install-module "$VERSION" "$MODULE" linux-server
RUN install-module "$VERSION" "$MODULE" windows-server
ARG MODULE="non-existent-module"
RUN for mod in $MODULE; do \
if [ "$mod" = "base" ] ; then \
echo "running default modules for this baseOs"; \
else \
unity-hub install-modules --version "$VERSION" --module "$mod" --childModules | tee /var/log/install-module-${mod}.log && \
grep 'Missing module\|Completed with errors' /var/log/install-module-${mod}.log | exit $(wc -l); \
fi \
done \
# Set execute permissions for modules
&& chmod -R 755 /opt/unity/editors/$VERSION/Editor/Data/PlaybackEngines
RUN echo "$VERSION-$MODULE" | grep -q -vP '^(2021.2.(?![0-4](?![0-9]))|2021.[3-9]|202[2-9]|[6-9][0-9]{3}|[1-9][0-9]{4,}).*linux' \
&& exit 0 \
|| unity-hub install-modules --version "$VERSION" --module "linux-server" --childModules | \
tee /var/log/install-module-linux-server.log && grep 'Missing module' /var/log/install-module-linux-server.log | exit $(wc -l);
RUN echo "$VERSION-$MODULE" | grep -q -vP '^(2021.2.(?![0-4](?![0-9]))|2021.[3-9]|202[2-9]|[6-9][0-9]{3}|[1-9][0-9]{4,}).*windows' \
&& exit 0 \
|| unity-hub install-modules --version "$VERSION" --module "windows-server" --childModules | \
tee /var/log/install-module-windows-server.log && grep 'Missing module' /var/log/install-module-windows-server.log | exit $(wc -l);
###########################
# Editor #
@ -46,21 +45,28 @@ ARG MODULE
COPY --from=builder /opt/unity/editors/$VERSION/ "$UNITY_PATH/"
RUN echo $VERSION > "$UNITY_PATH/version"
# Tools
RUN apt-get update && \
apt-get install -y \
build-essential \
cmake \
git \
curl \
gcc \
git \
libsqlite3-dev \
libssl-dev \
make \
pkg-config \
libssl-dev \
zlib1g-dev \
zip unzip
libsqlite3-dev
# Set up the scripts
RUN git clone --depth=1 https://github.com/game-ci/unity-builder.git /gameci && \
cp -rf /gameci/dist/platforms/ubuntu/steps /steps && \
cp -rf /gameci/dist/default-build-script /UnityBuilderAction && \
cp /gameci/dist/platforms/ubuntu/entrypoint.sh /entrypoint.sh
# Set up Node.js environment for github actions
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && \
npm install -g npm@latest
# Install Blender
ARG BLENDER_SHORT_VERSION=3.4
ARG BLENDER_FULL_VERSION=3.4.1
RUN echo "BLENDER_FULL_VERSION: $BLENDER_FULL_VERSION" && \
@ -71,32 +77,9 @@ RUN echo "BLENDER_FULL_VERSION: $BLENDER_FULL_VERSION" && \
rm blender-$BLENDER_FULL_VERSION-linux-x64.tar.xz
ENV PATH="$PATH:/blender-$BLENDER_FULL_VERSION-linux-x64"
# Runtimes, Languages, & Package Managers
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && npm install -g npm@latest
RUN curl -fsSL https://get.pnpm.io/install.sh | bash -
RUN apt-get install -y python3 python3-pip
# SDKs
RUN cd /tmp && curl -sL https://aka.ms/InstallAzureCLIDeb | bash
RUN cd /tmp && curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
unzip awscliv2.zip && ./aws/install
# Scripts
RUN git clone --depth=1 https://github.com/game-ci/unity-builder.git /gameci && \
cp -rf /gameci/dist/platforms/ubuntu/steps /steps && \
cp -rf /gameci/dist/default-build-script /UnityBuilderAction && \
cp /gameci/dist/platforms/ubuntu/entrypoint.sh /entrypoint.sh
# Add custom scripts
COPY scripts/build.sh /build.sh
RUN chmod +x /build.sh
LABEL com.unity3d.version="$VERSION"
LABEL com.unity3d.modules="$MODULE"
LABEL org.blender.version="$BLENDER_FULL_VERSION"
# Done
# ENTRYPOINT [ "/entrypoint.sh" ]
ENTRYPOINT [ "/bin/bash" ]
# ENTRYPOINT [ "/bin/bash" ]~

View File

@ -1,4 +0,0 @@
UNITY_VERSION=6000.0.35f1 \
UNITY_MODULES="webgl android ios mac-mono windows-mono linux-il2cpp" \
IMAGE=docker.lakes.house/unityci/editor \
.gitea/workflows/scripts/build-runner-image.sh

View File

@ -1,18 +0,0 @@
#!/bin/bash
VERSION="$1"
ENABLED_MODULES="$2"
MODULE_TO_INSTALL="$3"
CHILDLESS="$4"
# Check if MODULE_TO_INSTALL is in ENABLED_MODULES
if [[ " $ENABLED_MODULES " =~ " $MODULE_TO_INSTALL " ]]; then
CMD="unity-hub install-modules --version \"$VERSION\" --module \"$MODULE_TO_INSTALL\""
if [[ "$CHILDLESS" != "childless" ]]; then
CMD="$CMD --childModules"
fi
echo "Running: $CMD"
$CMD
else
echo "Module '$MODULE_TO_INSTALL' is not enabled. Skipping installation."
fi

5
test.sh Normal file
View File

@ -0,0 +1,5 @@
UNITY_VERSION=6000.0.35f1 \
UNITY_CHANGESET="9a3bc604008a" \
UNITY_MODULES="webgl linux-server windows-mono mac-mono linux-il2cpp" \
IMAGE=docker.lakes.house/unityci/editor \
./.gitea/workflows/scripts/build-runner-image.sh