Running git commit and bench update through button

I am fiddling about on a virtual machine, trying to produce a workaround to custom doctypes and to developer mode.
I want to be able to create new doctypes in a custom app on the one hand, while keeping the doctypes created to a specific template.
So, I made this:

@frappe.whitelist()
def CreateUserDoctype(user_name):
	import os
	lst_of_files=['template_doctype.js','template_doctype.json','template_doctype.py','test_template_doctype.py']
	base_dir = "/home/frappe/doctypes_creator/doctypes_creator/doctypes_creator/doctype"
	trgt_dir_name = user_name.lower().replace(' ','_') + "_doctype"
	trgt_dir = os.path.join(base_dir,trgt_dir_name)
	os.makedirs(trgt_dir, exist_ok=True)
	for fil in lst_of_files:
		with open(os.path.join(base_dir, "template_doctype", fil)) as f:
			content = f.read()
			content = content.replace('Template Doctype', user_name + ' Doctype')
			content = content.replace('TemplateDoctype', user_name.replace(' ','') + 'Doctype')
			if 'test' in fil:
				newfile = os.path.join(trgt_dir,'test_' + trgt_dir_name + '.' + fil.split('.')[-1])
			else:
				newfile = os.path.join(trgt_dir,trgt_dir_name + '.' + fil.split('.')[-1])
			with open(newfile, mode="w") as ff:
				ff.write(content)
	os.system('cd /home/frappe/doctypes_creator && git add . && git commit -m "new user added" && cd /home/frappe/frappe-bench && bench update --reset --apps doctypes_creator')

And it should work… :thinking:
The thing is, that for the life of me, I can’t seem to get the last row to run.
I tried touching and deleting files, these worked.
I can run this command in a bench console (it completed successfully).
When trying to run from a button inside a form inside desk - NOTHING.
The files are created, but no git commit running nor bench update.

1 Like

Hello @abrefael

Instead of using os.system, you can use Python’s subprocess module, which gives you better control, logging, and error handling. Frappe itself uses subprocess internally (example: frappe/frappe/utils/change_log.py at 5f85705d46184215b8a7ecfd6801fc790d1050a5 · frappe/frappe · GitHub).

However, note that subprocess only works for commands on the same server. For executing commands on remote servers, I’ve had success using paramiko (SSH + SFTP). This way you can run commands, transfer files, and also capture logs properly.

Example snippet:

stdin, stdout, stderr = ssh.exec_command(cmd)
stdout.channel.recv_exit_status()  # wait for command to finish
out = stdout.read().decode().strip()
err = stderr.read().decode().strip()

This gives you more flexibility compared to os.system, especially when handling remote installs or restore automation.

I tried it. It didn’t work.
That said, I further investigated the issue.
It seems I get stuck in git commit -m "new user added".
I think that, because when I go into the doctypes_creator folder and run git commit -a the new files already shown as if I ran git add . command.
For ease of use, I placed:

#!/bin/bash

cd /home/frappe/doctypes_creator
git add .
git commit -m "new user added"
cd /home/frappe/frappe-bench
bench update --reset --apps doctypes_creator

into update.sh and made it executable (manually running it works great).
Any ideas?

Even stranger!
I changed update.sh to:

#!/bin/bash

cd /home/frappe/doctypes_creator
touch $1/__init__.py
git add .
touch /home/frappe/git.add.ran
git commit -m "new user added"
touch /home/frappe/git.commit.ran
cd /home/frappe/frappe-bench
bench update --reset --apps doctypes_creator
touch /home/frappe/bench.update.ran

Now:

  • git.add.ran exists
  • git.commit.ran exists
  • bench.update.ran exists
  • __init__.py does not exist
  • system is stuck in “Updating” until I run bench update in a new shell window

Managed to fix this (Chat GPT helped… At least it tried, and sometimes succeeded…).

I use hooks to call the script:

frappe.enqueue(
	"doctypes_creator.user_hooks.run_script_async",
	user_id=user_id,
	job_name=f"add_user_{user_id}",
	queue="short",
	now=False,
)
.
.
.
def run_script_async(user_id: str):
	(first,last) = frappe.get_value("User",user_id,["first_name","last_name"])
	subprocess.run([SCRIPT_PATH, first, last], check=True)

Then comes the script:

#!/bin/bash

set -xe
export HOME=/home/frappe
new_dir="new_doc_type_${1}_${2}"
new_dir=$(echo "$new_dir" | tr '[:upper:]' '[:lower:]' | tr -s '[:space:]' '_' | sed 's/_$//')
py_fun_name=$(echo "NewDocType$1$2" | tr -d '[:space:]')
cd /home/frappe/doctypes_creator/doctypes_creator/doctypes_creator/doctype && \
mkdir $new_dir && \
cd $new_dir && \
touch __init__.py && \
cp ~/new_doc_type/new_doc_type.js ./$new_dir.js && \
sed -i "s|New Doc Type|New Doc Type ${1} ${2}|g" $new_dir.js && \
cp ~/new_doc_type/new_doc_type.py ./$new_dir.py && \
sed -i "s|NewDocType|${py_fun_name}|g" $new_dir.py && \
cp ~/new_doc_type/new_doc_type.json ./$new_dir.json && \
sed -i "s|New Doc Type|New Doc Type ${1} ${2}|g" $new_dir.json && \
cp ~/new_doc_type/test_new_doc_type.py ./test_$new_dir.py && \
sed -i "s|TestNewDocType|Test${py_fun_name}|g" test_$new_dir.py && \
touch /home/frappe/folder.created && \
cd /home/frappe/bitum_laboratory && \
git config --global user.email "user@email.com" && git config --global user.name "User Name" && \
git add . &> /home/frappe/git.add.log || echo "git add failed" >> /home/frappe/git.add.log
export GIT_AUTHOR_NAME="User Name"
export GIT_AUTHOR_EMAIL="user@email.com"
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
git commit -am "$1 $2 user added" --no-edit &> /home/frappe/git.commit.ran || echo "git commit failed" >> /home/frappe/git.commit.ran
nohup sh -c "/home/frappe/update.sh" >/home/frappe/bench.update.log 2>&1 &

The main things are in using frappe.enqueue and running bench update in a new nohup shell:
update.sh:

#!/bin/bash
export HOME=/home/frappe
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
nvm install 18 # just in case...
cd /home/frappe/frappe-bench && bench update --reset --apps doctypes_creator

This way, when we get to: $ supervisorctl restart frappe-bench-workers: the migration doesn’t stop.