Skip to content

Commit

Permalink
Add Hammer CI WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Python1320 committed Oct 25, 2020
1 parent d4a7670 commit 44f3231
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@
/game_compiling_
/setup/__pycache__
/setup/build/setup
/*.mdmp
*.pyc
/vbspautotest/build/setup
/vbspautotest/env
8 changes: 8 additions & 0 deletions LAUNCH hammer.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

@if defined NOHAMMERAUTOUPDATE @GOTO updated


@echo.
@echo Modified mapfiles:
@git -C "%mapfolder%" status --untracked-files=no -s
@echo.

@echo # Autoupdating mapfiles...


@git -C "%mapfolder%" pull
@if ERRORLEVEL 1 @GOTO updatefail

Expand Down Expand Up @@ -47,6 +49,12 @@
@rem @set VProject=%VProject_Hammer%
@rem @echo Project: %VProject%


@if not defined NOHAMMERCI (
@cd vbspautotest
@start /low /min vbspautotest.exe
)

@TITLE "Hammer Repo Waiter"
@echo # Waiting for hammer to close...
@start /WAIT "Hammer" "%VProject_Hammer%\..\bin\hammer.exe" %HammerParams% %*
Expand Down
Binary file added extras/flashcmd.exe
Binary file not shown.
32 changes: 32 additions & 0 deletions test.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@set ORIGFOLDER=%CD%
@set CMD_LC_ROOT=%~dp0

@call common.cmd
@cd /d "%CMD_LC_ROOT%"

:docopy
@set targetvmf=%mapfolder%\ci.vmf
@set targetbsp=%mapfolder%\ci.bsp
@set leakfile=%mapfolder%\ci.lin
@del /Q /F "%targetvmf%" 2>nul >nul
@del /Q /F "%targetbsp%" 2>nul >nul
@rem The mappers need this :p
@rem del /Q /F "%leakfile%"

@COPY "%mapfolder%\%mapfile%.vmf" "%targetvmf%" >nul
@if ERRORLEVEL 1 goto failed

extras\vmfii "%targetvmf%" "%targetvmf%" --fgd "%FGDS%" > nul
@if ERRORLEVEL 1 goto failed
"%compilers_dir%\vbsp.exe" -allowdynamicpropsasstatic -leaktest -low "%targetvmf%"
@if ERRORLEVEL 1 goto failed
@if NOT exist "%targetbsp%" goto failed
"%compilers_dir%\vvis.exe" -fast -low "%targetvmf%"
@if ERRORLEVEL 1 goto failed
"%compilers_dir%\vrad.exe" -low %VRADLDR% -noskyboxrecurse -bounce 1 -noextra -fastambient -fast -ldr "%targetvmf%"
@if ERRORLEVEL 1 goto failed
@goto ok

:failed
@exit 1
:ok
Binary file added vbspautotest/install_icon_155236.ico
Binary file not shown.
4 changes: 4 additions & 0 deletions vbspautotest/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
vpk
vdf
pyinstaller
win10toast
95 changes: 95 additions & 0 deletions vbspautotest/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

import os
import winreg
import subprocess
import vdf
from pathlib import Path
from functools import lru_cache
from pprint import pprint as PrintTable
# DETECT OS VERSION

def Is64Windows():
return 'PROGRAMFILES(X86)' in os.environ

def GetProgramFiles32():
if Is64Windows():
return os.environ['PROGRAMFILES(X86)']
else:
return os.environ['PROGRAMFILES']

def GetProgramFiles64():
if Is64Windows():
return os.environ['PROGRAMW6432']
else:
return None

if Is64Windows() is True:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432Node\\Valve\\Steam")
else:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Valve\\Steam")

steampath = winreg.QueryValueEx(key, "InstallPath")[0]
@lru_cache(maxsize=32)
def GetSteamPath():
return steampath

@lru_cache(maxsize=32)
def GetSteamLibraryPaths():
with open(GetSteamPath() + "/SteamApps/LibraryFolders.vdf") as lf:
vdffile = vdf.parse(lf)
vdflocations = [val for key,val in vdffile['LibraryFolders'].items() if key.isdigit()]+[steampath]
for path in vdflocations:
print("\tFound Library path: ",path)
return vdflocations

@lru_cache(maxsize=32)
def GetGamePath(appid):
for acfpath in GetSteamLibraryPaths():
appmanifestpath = acfpath + "/SteamApps/appmanifest_%d.acf"%(appid)
if os.path.isfile(appmanifestpath):
appmanifest = vdf.parse(open(appmanifestpath))
return Path(acfpath+"\\SteamApps\\common\\"+appmanifest['AppState']['installdir']+"\\")

@lru_cache(maxsize=32)
def GetGModPath():
return GetGamePath(4000)

@lru_cache(maxsize=32)
def TF2Path(d="."):
return GetGamePath(440) / d

@lru_cache(maxsize=32)
def CSSPath(d="."):
return GetGamePath(240) / d

@lru_cache(maxsize=32)
def MapFiles(d="."):
return Path("../../mapfiles").resolve() / d

@lru_cache(maxsize=32)
def MapAssets(d="."):
return Path("../../mapdata").resolve() / d

@lru_cache(maxsize=32)
def HammerRoot():
p = Path("../game_hammer").resolve()
#assert p.exists()
return p

@lru_cache(maxsize=32)
def CompilerRoot():
p = Path("../game_compiling").resolve()
#assert p.exists()
return p

@lru_cache(maxsize=32)
def ToolkitRoot(d="."):
return Path("..").resolve() / d




def execute_batch(cmd,cwd):
p=subprocess.Popen(cmd,cwd=cwd)#, creationflags=subprocess.CREATE_NEW_CONSOLE)
p.communicate()
return p.returncode
197 changes: 197 additions & 0 deletions vbspautotest/vbspautotest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@

import ctypes,sys,os
ctypes.windll.kernel32.SetConsoleTitleW("Hammer CI")

# UGH
def install_and_import(package,package_pip):
import importlib
try:
importlib.import_module(package)
except ImportError:
import subprocess
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package_pip])
finally:
globals()[package] = importlib.import_module(package)


def callback_from_icon():
pass

install_and_import("notify","winnotify")
import notify

ICO='install_icon_155236.ico'
notify.init(ICO, callback_from_icon)


from utils import *
from pathlib import Path
import shutil
from shutil import copyfileobj
import traceback
import distutils.dir_util
import time
import win32gui
import subprocess

import win32event
import win32api
from winerror import ERROR_ALREADY_EXISTS



mutex = win32event.CreateMutex(None, False, 'hammerciautotest')
last_error = win32api.GetLastError()

if last_error == ERROR_ALREADY_EXISTS:
print("Already running")
sys.exit(43)


def my_except_hook(*exc_info):
if exc_info[0] == KeyboardInterrupt:
sys.exit(0)
else:
print("\n\n====== Something unexpected happened that we can't handle ======")
print("".join(traceback.format_exception(*exc_info)))
input("This may be a programming error.\nCopy all of this screen and make an issue at https://github.com/Metastruct/map-compiling-toolkit/issues/new\n\nPress ENTER to abort.")
sys.exit(1)

sys.excepthook = my_except_hook

def pathcheck(fatal,printthing,path):
if path.exists():
print("\t",printthing,path)
else:
print("\t",printthing,path," !!!! MISSING !!!!")
if fatal:
input("\nPress ENTER to abort.")
sys.exit(1)

print("\nChecking paths:")
pathcheck(True,"GMod\t\t",GetGModPath())
if not (GetGModPath() / 'bin/win64').exists():
input("\nERROR: You need to run GMod in x86-64 branch for devenv to work.\n\nPress ENTER to abort.")
sys.exit(1)

pathcheck(False,"Hammer\t\t",HammerRoot())
pathcheck(False,"Compiler\t",CompilerRoot())
pathcheck(False,"TF2\t\t",TF2Path("tf"))
pathcheck(False,"CSS\t\t",CSSPath("cstrike"))
pathcheck(True,"Maps\t\t",MapFiles())
pathcheck(True,"Map assets\t",MapAssets())
print("")

cifile = MapFiles("ci.vmf").resolve()
cilog = MapFiles("ci.log").resolve()

watchables={vmf:1 or vmf.stat().st_size for vmf in MapFiles().glob("*.vmf") if vmf.resolve()!=cifile and "gm_construct_m" not in str(vmf)}

def watch():
found=False
for vmf,size in watchables.items():
time.sleep(0)
st_size=vmf.stat().st_size
if st_size!=size:
watchables[vmf]=st_size
found=vmf
if found:
time.sleep(1)
for vmf,size in watchables.items():
time.sleep(0)
st_size=vmf.stat().st_size
if st_size!=size:
watchables[vmf]=st_size
found=vmf

return found

def okcb(str):
sys.stdout.write(str)
sys.stdout.flush()

def failcb(str):
sys.stderr.write(str)
sys.stderr.flush()

wasfailed=False
firstcompile=True

def dofail(ret):


if cilog.exists():
print("Found log file")
with cilog.open("rb") as f:
f.seek(-512, os.SEEK_END)
if b"LEAKED" in f.read():
print("LEAK DETECT")
notify.notify("Map leaked!",
"Hammer CI",
ICO,
False,10,0x03)
ctypes.windll.kernel32.SetConsoleTitleW("Status: MAP LEAKED")
return
notify.notify("Map compiling failed! Look at console window for more info. "+str(ret),"Hammer CI",


ICO,
False,10,0x03)
ctypes.windll.kernel32.SetConsoleTitleW("Map compiling failed.")

startup_time = time.time()

check_fails=0
last_ret=None
def checkdead():
global check_fails
global last_ret
if win32gui.FindWindow("VALVEWORLDCRAFT",None)!=0:
return False
check_fails += 1
if check_fails < 5:
return
time.sleep(1)
print("Shutting down")
notify.notify("Shutting down. Last compile: "+(last_ret==None and "Unknown" or (last_ret==0 and '[SUCCESS]' or '[FAIL]')),"Hammer CI",


ICO,
False,10,last_ret==0 and 0x01 or 0x03)
return True

while True:

if checkdead():
break

changed = watch()
if changed:
print("\n\nRUNNING CI DUE TO FILE CHANGE: ",changed)
cwd=os.getcwd()
os.chdir(ToolkitRoot())

start_time = time.time()

ret = execute_batch(["test.cmd"],ToolkitRoot())

e = int(time.time() - start_time)
print('Compiling ran for {:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
os.chdir(cwd)
last_ret = ret
ctypes.windll.kernel32.SetConsoleTitleW((ret==0 and '[SUCCESS]' or '[FAIL]') +'Compiling ran for {:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
if ret == 0:
if wasfailed or firstcompile:
notify.notify("Compiling succeeded! :)\nTook {:02d}:{:02d}:{:02d}".format(e // 3600, (e % 3600 // 60), e % 60),"Hammer CI",

ICO,
False,10,0x01)
firstcompile=False
wasfailed=False
else:
wasfailed=True
print("RETURN",ret)
dofail(ret)
time.sleep(10)
print("Resuming monitoring...")
time.sleep(1)

0 comments on commit 44f3231

Please sign in to comment.