A build routine is simply a function that receives a build request and returns a build response, as long as those conditions are met, you can do whatever you please from within that function. Here is a bare-bones example build routine.
import randomimport subprocessfrom monarch.builder import*from subprocess import STDOUT, check_outputimport osimport base64defroutine(req: BuildRequest) -> BuildResponse: agent_id = req.params.get("id")# definitely exists - was passed by monarch itself host = req.params.get("host") port = req.params.get("port")# config.json will be embedded into the Go binary at compile time (go embed)withopen("config/config.json", "w")as f: j ={"id": agent_id,"host": host,"port": port,} j_string = json.dumps(j) f.write(j_string) out = random.randbytes(10).decode('utf-8') cmd ="go build -o %s ."% out data =b"" my_env = os.environ.copy() my_env["GOOS"]= req.params.get("os") my_env["GOARCH"]= req.params.get("arch")try: error =check_output(cmd.split(), stderr=STDOUT, env=my_env)except subprocess.CalledProcessError as e:# return build error error = e.output.decode('utf-8')# path doesn't exist if build failedifnot os.path.exists(out): status =1else: status =0 error =""withopen(out, "rb")as f: data = f.read() res =BuildResponse(status, error, base64.b64encode(data).decode('utf-8'))return res
It uses the host, port and ID parameters provided by the Monarch server to build the binary with a configuration file. It also ensures that any errors that may occur are caught and dealt with.
The returned build must be represented as base64 encoded data, as this is the chosen representation of raw bytes for Monarch. A successful build response from the Python build service to the Monarch build client would look like so: