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

View File

@ -6,17 +6,14 @@ on:
paths: paths:
- versions.txt - versions.txt
- dockerfiles/runner.dockerfile - dockerfiles/runner.dockerfile
- .gitea/workflows/create-runner.yaml - .github/workflows/create-runner.yaml
workflow_dispatch:
jobs: jobs:
build-docker: build-docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
DOCKER_REGISTRY: docker.lakes.house/ DOCKER_REGISTRY: docker.lakes.house/
UNITY_MODULES: webgl android ios mac-mono windows-mono linux-il2cpp UNITY_MODULES: webgl android ios mac-mono windows-mono linux-il2cpp
GAMECI_OS: ubuntu
GAMECI_VERSION: 3 GAMECI_VERSION: 3
name: Build name: Build
@ -42,9 +39,8 @@ jobs:
UNITY_VERSION: ${{ env.UNITY_VERSION }} UNITY_VERSION: ${{ env.UNITY_VERSION }}
UNITY_MODULES: ${{ env.UNITY_MODULES }} UNITY_MODULES: ${{ env.UNITY_MODULES }}
GAMECI_VERSION: ${{ env.GAMECI_VERSION }} GAMECI_VERSION: ${{ env.GAMECI_VERSION }}
GAMECI_OS: ${{ env.GAMECI_OS }} GAMECI_OS: ubuntu
IMAGE: ${{ env.DOCKER_REGISTRY }}unityci/editor IMAGE: ${{ env.DOCKER_REGISTRY }}unityci/editor
TAG: ${{ env.GAMECI_OS }}-${{ env.UNITY_VERSION }}-runner
- name: Docker Push - name: Docker Push
run: | 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 # 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. # This is because hub doesnt remember every version of unity and uses the changset for the exact id lookup.
# if [ -z "${UNITY_CHANGESET}" ]; then if [ -z "${UNITY_CHANGESET}" ]; then
# echo "Warning: No changeset provided. Scraping one from the change logs." 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." 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}" 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) 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 if [ -z "$UNITY_CHANGESET" ]; then
# echo "Error: Could not extract changeset for Unity version ${UNITY_VERSION}." echo "Error: Could not extract changeset for Unity version ${UNITY_VERSION}."
# exit 1 exit 1
# fi fi
# fi fi
# Ensure we have some modules # Ensure we have some modules
if [ -z "${UNITY_MODULES}" ]; then if [ -z "${UNITY_MODULES}" ]; then
@ -32,6 +32,16 @@ if [ -z "${UNITY_MODULES}" ]; then
fi fi
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 # Ensure GAME_CI_VERSION is set, default to 3 if not
if [ -z "${GAMECI_VERSION}" ]; then if [ -z "${GAMECI_VERSION}" ]; then
GAMECI_VERSION=3 GAMECI_VERSION=3
@ -64,22 +74,6 @@ if [ -z "${PLATFORM}" ]; then
esac esac
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 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 # Ensure some additional build settings are set
if [ -z "${DOCKER_BUILD_ARGS}" ]; then if [ -z "${DOCKER_BUILD_ARGS}" ]; then
DOCKER_BUILD_ARGS="" DOCKER_BUILD_ARGS=""
@ -87,19 +81,21 @@ fi
BASE_TAG=${GAMECI_OS}-${UNITY_VERSION}-base-${GAMECI_VERSION} BASE_TAG=${GAMECI_OS}-${UNITY_VERSION}-base-${GAMECI_VERSION}
BASE_IMAGE=unityci/editor:${BASE_TAG} 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 "Building Docker image ${DEST_IMAGE}"
echo "- Version: ${UNITY_VERSION}" echo "- Version: ${UNITY_VERSION}"
echo "- Changeset: ${UNITY_CHANGESET}" echo "- Changeset: ${UNITY_CHANGESET}"
echo "- Platfrom: ${PLATFORM}" echo "- Platfrom: ${PLATFORM}"
echo "- Base: ${BASE_IMAGE}" echo "- Base: ${BASE_IMAGE}"
echo "- Tag: ${TAG}" echo "- Tag: ${DEST_TAG}"
echo "- Image: ${DEST_IMAGE}" echo "- Image: ${DEST_IMAGE}"
docker build \ docker buildx build \
--platform ${PLATFORM} \ --platform ${PLATFORM} \
--build-arg "VERSION=${UNITY_VERSION}" \ --build-arg "VERSION=${UNITY_VERSION}" \
--build-arg "CHANGESET=${UNITY_CHANGESET}" \
--build-arg "BASE_IMAGE=${BASE_IMAGE}" \ --build-arg "BASE_IMAGE=${BASE_IMAGE}" \
--build-arg "MODULE=${UNITY_MODULES}" \ --build-arg "MODULE=${UNITY_MODULES}" \
-t ${DEST_IMAGE} ${DOCKER_BUILD_ARGS} \ -t ${DEST_IMAGE} ${DOCKER_BUILD_ARGS} \
@ -113,6 +109,6 @@ fi
# Export IMAGE and TAG for GitHub Actions # Export IMAGE and TAG for GitHub Actions
if [ -n "$GITHUB_OUTPUT" ]; then if [ -n "$GITHUB_OUTPUT" ]; then
echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT
echo "TAG=$TAG" >> $GITHUB_OUTPUT echo "TAG=$DEST_TAG" >> $GITHUB_OUTPUT
echo "FULL_IMAGE=${DEST_IMAGE}" >> $GITHUB_OUTPUT echo "FULL_IMAGE=${DEST_IMAGE}" >> $GITHUB_OUTPUT
fi fi

View File

@ -1,136 +1,167 @@
#!/usr/bin/env python3
import requests
import sys
import os import os
import re import re
import requests
import base64
import json
DOCKER_REGISTRY = os.getenv('DOCKER_REGISTRY') def get_docker_hub_tags(repository, limit=1000):
DOCKER_USERNAME = os.getenv('DOCKER_USERNAME') """
DOCKER_PASSWORD = os.getenv('DOCKER_PASSWORD') Get tags for a Docker Hub repository
IMAGE = os.getenv('IMAGE') """
url = f"https://hub.docker.com/v2/repositories/{repository}/tags"
README_PATH="README.md" params = {"page_size": limit}
DOCKERHUB_REGISTRY = "registry-1.docker.io"
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
def get_tags_with_size(registry : str, authorization : str, repository : str) -> list[object]: # Check for Docker Hub credentials in environment variables
url = f"https://{registry}/v2/{repository}/tags/list" username = os.getenv('DOCKER_USERNAME')
headers = { password = os.getenv('DOCKER_PASSWORD')
"Authorization": f"{authorization}",
"Accept": "application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json" # Create session for potential authentication
} session = requests.Session()
print(f"Querying {url}...") # Authenticate if credentials are provided
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}
if username and password: if username and password:
resp = requests.get(auth_url, params=params, auth=(username, password)) auth_url = "https://hub.docker.com/v2/users/login/"
else: auth_data = {"username": username, "password": password}
resp = requests.get(auth_url, params=params)
resp.raise_for_status() try:
token = resp.json()["token"] auth_response = session.post(auth_url, json=auth_data)
return f"Bearer {token}" auth_response.raise_for_status()
# Token is automatically stored in the session cookies
def login_registry(username : str, password : str) -> str: except requests.exceptions.RequestException as e:
credentials = f"{username}:{password}" print(f"Warning: Docker Hub authentication failed: {e}")
encoded_credentials = base64.b64encode(credentials.encode()).decode() # Continue without authentication
return f"Basic {encoded_credentials}"
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}"
def create_table(repository : str, tags : list[object]) -> str:
markdown : str = ""
versions : dict[str, dict[str, str]] = {} all_tags = []
all_modules : set[str] = set()
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
# Build a table of versions def get_private_registry_tags(registry, repository):
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: Get tags for a repository in a private registry
matches = pattern.match(tag['name']) """
os = matches.group("os").strip('-') # Check for registry credentials in environment variables
version = matches.group("version").strip('-') username = os.getenv('DOCKER_USERNAME')
modules = matches.group("modules").strip('-') if matches.group('modules') else "all" password = os.getenv('DOCKER_PASSWORD')
all_modules.add(modules)
if version not in versions: # Ensure registry URL starts with https://
versions[version] = {} registry_url = registry if registry.startswith(('http://', 'https://')) else f'https://{registry}'
versions[version][modules] = create_label(repository, tag)
# Print the table heading # Try to get an auth token first (token-based auth)
sorted_mods = sorted([m for m in all_modules if m]) headers = {}
markdown += f"|Unity|{'|'.join(sorted_mods)}|\n" url = f"{registry_url}/v2/{repository}/tags/list"
markdown += f"|-----|{'|'.join('-' * len(m) for m in sorted_mods)}|\n"
# 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:
# Use token auth or no auth
response = requests.get(url, headers=headers)
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()
return data.get("tags", [])
# For each item, check each available module and see if this version has that available. def format_tag(registry, repository, platform, tag ):
for ver, mods in versions.items(): # Get color for platform, default to blue if not found
row = [ mods[mod] if mod in mods else '' for mod in sorted_mods ] url = f"https://hub.docker.com/r/{repository}/tags?name={tag}"
markdown += f"|{ver}|{'|'.join(row)}|\n"
return markdown if registry:
url = f"https://{registry}/{repository}/tag/{tag}"
def replace_readme_table(readme : str, table : str) -> str: return f"[🐳 View]({url})"
pattern = r"(<!-- table -->).*(<!-- /table -->)"
return re.sub(pattern, r'\1\n' + table + r'\2', readme, flags=re.DOTALL)
def main(): def main():
auth_token : str
registry : str # Check for registry in environment variable if not specified in args
registry = os.getenv('DOCKER_REGISTRY')
repository = os.getenv('IMAGE')
if DOCKER_REGISTRY: if registry:
registry = DOCKER_REGISTRY print(f"Fetching tags for {repository} from {registry}...")
auth_token = login_registry(DOCKER_USERNAME, DOCKER_PASSWORD) tags = get_private_registry_tags(registry, repository)
else: else:
registry = DOCKERHUB_REGISTRY print(f"Fetching tags for {repository} from Docker Hub...")
auth_token = login_dockerhub(DOCKER_USERNAME, DOCKER_PASSWORD, IMAGE) tags = get_docker_hub_tags(repository, 100)
# Sort tags
tags.sort()
print('Fetching Tags...') versions={}
tags = get_tags_with_size(registry, auth_token, IMAGE)
print('Writing Readme...') pattern = re.compile(r"(\w+)-(\d+\.\d+\.\d+\w*)-(\w+)(-\w+)?-runner")
table = create_table(IMAGE, tags) for tag in tags:
if os.path.exists(README_PATH): matches = pattern.match(tag)
with open(README_PATH, "r", encoding='utf-8') as f: if matches:
readme = f.read() groups = matches.groups()
with open(README_PATH, "w", encoding='utf-8') as f: version = groups[1]
f.write(replace_readme_table(readme, table)) 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__": if __name__ == "__main__":
main() main()

View File

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

View File

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

View File

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

3
.gitignore vendored
View File

@ -1,2 +1 @@
gameci 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: A table of available Docker images for Unity CI/CD:
<!-- table --> <!-- table -->
|Unity|all|android|ios|linux-il2cpp|mac-mono|webgl|windows-mono| | unity | 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| | 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 --> <!-- /table -->
## Included Software # Usage
### 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`
### SDKs
- AWS CLI
- Azure SDK
## How TODO:
... https://github.com/mob-sakai/unity-changeset/blob/main/src/unityGraphQL.ts
TODO
...
> [!NOTE] use this library to get the change set.
> 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. Might as well orchestrate the build with it too.
> 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.

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 # # Builder #
########################### ###########################
FROM $BASE_IMAGE AS editor
FROM $HUB_IMAGE AS builder FROM $HUB_IMAGE AS builder
# Install editor # Install editor
ARG VERSION ARG VERSION
COPY --from=editor "$UNITY_PATH/" /opt/unity/editors/$VERSION/ ARG CHANGESET
RUN unity-hub install --version "$VERSION" --changeset "$CHANGESET" \
# Install CMake | 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)
RUN apt-get update && apt-get install -y cmake
COPY --chmod=770 scripts/install-module.sh /bin/install-module
# Install modules for that editor # Install modules for that editor
ARG MODULE ARG MODULE="non-existent-module"
RUN install-module "$VERSION" "$MODULE" android RUN for mod in $MODULE; do \
RUN install-module "$VERSION" "$MODULE" ios if [ "$mod" = "base" ] ; then \
RUN install-module "$VERSION" "$MODULE" appletv echo "running default modules for this baseOs"; \
RUN install-module "$VERSION" "$MODULE" linux-mono else \
RUN install-module "$VERSION" "$MODULE" linux-il2cpp unity-hub install-modules --version "$VERSION" --module "$mod" --childModules | tee /var/log/install-module-${mod}.log && \
RUN install-module "$VERSION" "$MODULE" webgl grep 'Missing module\|Completed with errors' /var/log/install-module-${mod}.log | exit $(wc -l); \
RUN install-module "$VERSION" "$MODULE" windows fi \
RUN install-module "$VERSION" "$MODULE" vuforia-ar done \
RUN install-module "$VERSION" "$MODULE" windows-mono # Set execute permissions for modules
RUN install-module "$VERSION" "$MODULE" lumin && chmod -R 755 /opt/unity/editors/$VERSION/Editor/Data/PlaybackEngines
RUN install-module "$VERSION" "$MODULE" mac-mono
RUN install-module "$VERSION" "$MODULE" mac-il2cpp 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' \
RUN install-module "$VERSION" "$MODULE" universal-windows-platform && exit 0 \
RUN install-module "$VERSION" "$MODULE" uwp-il2cpp || unity-hub install-modules --version "$VERSION" --module "linux-server" --childModules | \
RUN install-module "$VERSION" "$MODULE" uwp-.net tee /var/log/install-module-linux-server.log && grep 'Missing module' /var/log/install-module-linux-server.log | exit $(wc -l);
RUN install-module "$VERSION" "$MODULE" linux-server
RUN install-module "$VERSION" "$MODULE" windows-server 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 # # Editor #
@ -46,57 +45,41 @@ ARG MODULE
COPY --from=builder /opt/unity/editors/$VERSION/ "$UNITY_PATH/" COPY --from=builder /opt/unity/editors/$VERSION/ "$UNITY_PATH/"
RUN echo $VERSION > "$UNITY_PATH/version" RUN echo $VERSION > "$UNITY_PATH/version"
# Tools
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y \ apt-get install -y \
build-essential \ git \
cmake \ curl \
curl \ gcc \
gcc \ make \
git \ libssl-dev \
libsqlite3-dev \ zlib1g-dev \
libssl-dev \ libsqlite3-dev
make \
pkg-config \
zlib1g-dev \
zip unzip
# 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_SHORT_VERSION=3.4
ARG BLENDER_FULL_VERSION=3.4.1 ARG BLENDER_FULL_VERSION=3.4.1
RUN echo "BLENDER_FULL_VERSION: $BLENDER_FULL_VERSION" && \ RUN echo "BLENDER_FULL_VERSION: $BLENDER_FULL_VERSION" && \
echo echo "BLENDER_SHORT_VERSION: $BLENDER_SHORT_VERSION" && \ echo echo "BLENDER_SHORT_VERSION: $BLENDER_SHORT_VERSION" && \
apt-get install -y wget && \ apt-get install -y wget && \
wget https://download.blender.org/release/Blender$BLENDER_SHORT_VERSION/blender-$BLENDER_FULL_VERSION-linux-x64.tar.xz && \ 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 && \ tar -xf blender-$BLENDER_FULL_VERSION-linux-x64.tar.xz && \
rm 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" ENV PATH="$PATH:/blender-$BLENDER_FULL_VERSION-linux-x64"
# Runtimes, Languages, & Package Managers # Add custom scripts
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
COPY scripts/build.sh /build.sh COPY scripts/build.sh /build.sh
RUN chmod +x /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 # 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