自动备份本地库程序并自动进行版本控制发出不久,有同学提出建议,能不能实时备份,同时设置2个任务的方式有点繁琐。而且每天定点提交,并不能完全解决时差带来的版本不一致问题。
之前讲Git与VSCode与M的时候,实际上利用VSCode连接IRIS库可以实现Git版本控制,但是也得需要去手动的导出,提交等操作。
经过一段时间思考琢磨后,我们能不能利用原生的Studio环境在编译时进行备份呢,因为每次编译会自动保存最新的代码,从而达到实时备份还能省去手动的导出,提交等操作。
实时备份可以解决如下问题:
那么我们该如何实现编译时进行备份呢?请看如下过程:
通过
IRIS
调用Python
的Git
库,再利用M
的代码生成器在编译时运行导出方法,继而再调用Git
库的命令进行版本控制。
编写
M
相关代码。
Parameter classPath = "E:\m\cls\";
ClassMethod ExportCls(cls = "", classPath = {..#classPath})
{
#; 判断路径文件夹是否存在
if ('##class(%File).DirectoryExists(classPath)){
#; 不存在创建文件夹
d ##class(%File).CreateNewDir(classPath, "")
}
#; 导出类
d $system.OBJ.Export(cls _ ".cls", classPath _ cls _ ".xml")
q $$$OK
}
autoBackup
,有此标志的类在编译时进行导出。ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
q $$$OK
}
M.Git
,用于实现编译时进行版本控制。ClientName = autoBackup
,用于标识该类是否自动备份。Class M.Test Extends (%RegisteredObject, M.Git) [ ClientName = autoBackup ]
{
}
M.Test
进行编译,发现在E:\m\cls\
文件夹内成功导出该类文件。编写
Python
相关代码
gitpython
库,调用Git
命令进行版本控制。C:\InterSystems\IRISHealth\bin
,输入cmd
,进入命令行控制台输入命令。安装gitpython
库。irispip install --target C:\InterSystems\IRISHealth\mgr\python gitpython
C:\InterSystems\IRISHealth\bin>irispip install --target C:\InterSystems\IRISHealth\mgr\python gitpython
Collecting gitpython
Using cached GitPython-3.1.29-py3-none-any.whl (182 kB)
Collecting gitdb<5,>=4.0.1
Using cached gitdb-4.0.10-py3-none-any.whl (62 kB)
Collecting smmap<6,>=3.0.1
Using cached smmap-5.0.0-py3-none-any.whl (24 kB)
Installing collected packages: smmap, gitdb, gitpython
Successfully installed gitdb-4.0.10 gitpython-3.1.29 smmap-5.0.0
[notice] A new release of pip available: 22.2.2 -> 22.3.1
[notice] To update, run: python.exe -m pip install --upgrade pip
/// desc:初始化仓库
ClassMethod GitInit(path) As %String [ Language = python ]
{
from git import Repo
Repo.init(path)
}
Language = python
无法给参数进行设置默认值,所以需要回调一下。/// desc:调用初始化
ClassMethod RunInit(path = {..#classPath}) As %String
{
d ..GitInit(path)
}
E:\m\cls\
文件夹初始化仓库成功。d ##class(M.Git).RunInit()
/// desc:添加暂存区提交工作区
ClassMethod GitCommit(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
}
CompileBackUp()
方法。/// desc:编译时备份,备份前要确保文件夹初始化Git仓库
/// d ##class(M.Git).ExportCls("M.BackUp")
ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
d ..GitCommit(..#classPath)
q $$$OK
}
M.Test
再次编译该类。M.Test
类添加方法再次编译,测试一下类变化进行版本控制。Class M.Test Extends (%RegisteredObject, M.Git) [ ClientName = autoBackup ]
{
/// desc:查询全局变量
/// w ##class(M.Test).QueryGlobalDetail1().%ToJSON()
ClassMethod QueryGlobalDetail1(pJson As %Library.DynamicObject)
{
s array = []
s rs = ##class(%ResultSet).%New("%Global:Get")
d rs.Execute("USER","^yx(99:100)","",2,2,1)
while(rs.Next()){
s obj = {}
s obj.name = rs.Data("Name")
s obj.value = rs.Data("Value")
s obj.nameFormat = rs.Data("Name Format")
s obj.valueFormat = rs.Data("Value Format")
s obj.permissions = rs.Data("Permissions")
d array.%Push(obj)
}
q array
}
}
Git
日志。按照上述方法,我们就实现在服务端或本地的一个版本控制。
可能有小伙伴想提交到本地库之后,还想Push
到远程仓库。
其实也是可以的,下面我们来实现一下。
Gitlab
创建了一个名为python
的远程仓库。复制远程库的链接放到代码里。/// desc:先拉取再推送,确保本地版本与远程仓库是一致的再,提交并推送远程仓库
ClassMethod GitCommitPush(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
git = repo.git
# 如果是你私有仓库能确保版本一致,可直接Push。
git.config("user.name","yx")
git.config("user.email","454115408@qq.com")
git.pull("http://username:password@8.142.29.250:7091/root/python.git","master","--allow-unrelated-histori")
git.push("http://username:password@8.142.29.250:7091/root/python.git","master")
}
CompileBackUp()
方法。ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
#; 没有远程仓库使用GitCommit
#; d ..GitCommit(..#classPath)
#; 有远程仓库例如Gitlab用GitCommitPush
d ..GitCommitPush(..#classPath)
q $$$OK
}
M.Test
再次编译该类,查看GitLab
发现提交成功。M.Test
文件查看文件。ClassMethod PythonMath() As %String
{
s math = ##class(%SYS.Python).Import("math")
q math.pi
}
M.Test
文件查看文件。Git
日志。这样我们就可以在远程仓库与本地库进行版本控制,进行双保险。
如果提交失败,Studio
会提示出具体报错信息。
1
。综上所诉:只需要对类继承M.Git
,就可以实现编译时版本控制了。
Terminal
中去执行ExportCls
方法。autoBackup
。2
次编译时间小于5
分钟不进行版本控制。注:此方案仅适用于IRIS
,因为老版Caché
无法使用嵌入式Python
。
SELECT TimeChanged,TimeCreated FROM %Dictionary.CompiledClass WHERE ID = "M.M91"
M.Git
Class M.Git Extends %RegisteredObject
{
Parameter classPath = "E:\m\cls\";
/// desc:编译时备份,备份前要确保文件夹初始化Git仓库
/// d ##class(M.Git).ExportCls("M.BackUp")
ClassMethod CompileBackUp() [ CodeMode = objectgenerator ]
{
q:(%class.ClientName '= "autoBackup") $$$OK
d ..ExportCls(%class.Name)
#; 没有远程仓库使用GitCommit
#; d ..GitCommit(..#classPath)
#; 有远程仓库例如Gitlab用GitCommitPush
d ..GitCommitPush(..#classPath)
q $$$OK
}
ClassMethod RunBackUp()
{
q ..ExportCls($this)
}
/// d ##class(M.Git).ExportCls("M.BackUp")
ClassMethod ExportCls(cls = "", classPath = {..#classPath})
{
#; 判断路径文件夹是否存在
if ('##class(%File).DirectoryExists(classPath)){
#; 不存在创建文件夹
d ##class(%File).CreateNewDir(classPath, "")
}
#; 导出类
d $system.OBJ.Export(cls _ ".cls", classPath _ cls _ ".xml")
q $$$OK
}
/// desc:调用初始化
/// d ##class(M.Git).RunInit()
ClassMethod RunInit(path = {..#classPath}) As %String
{
d ..GitInit(path)
}
/// desc:初始化
/// w ##class(M.Git).GitInit({..#classPath})
ClassMethod GitInit(path) As %String [ Language = python ]
{
from git import Repo
Repo.init(path)
}
/// desc:添加暂存区提交工作区
ClassMethod GitCommit(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
}
/// desc:先拉取再推送,确保本地版本与远程仓库是一致的再,提交并推送远程仓库
ClassMethod GitCommitPush(path) As %String [ Language = python ]
{
import os
from git.repo.fun import is_git_dir
from git.repo import Repo
repo = Repo(path)
commit_message = '自动备份'
repo.index.add("*.xml")
repo.index.commit(commit_message)
git = repo.git
# 如果是你私有仓库能确保版本一致,可直接Push。
git.config("user.name","yx")
git.config("user.email","454115408@qq.com")
git.pull("http://username:password@8.142.29.250:7091/root/python.git","master","--allow-unrelated-histori")
git.push("http://username:password@8.142.29.250:7091/root/python.git","master")
}
}
M.Test
Class M.Test Extends (%RegisteredObject, M.Git) [ ClientName = autoBackup ]
{
/// desc:查询全局变量
/// w ##class(M.Test).QueryGlobalDetail1().%ToJSON()
ClassMethod QueryGlobalDetail1(pJson As %Library.DynamicObject)
{
s array = []
s rs = ##class(%ResultSet).%New("%Global:Get")
d rs.Execute("USER","^yx(99:100)","",2,2,1)
while(rs.Next()){
s obj = {}
s obj.name = rs.Data("Name")
s obj.value = rs.Data("Value")
s obj.nameFormat = rs.Data("Name Format")
s obj.valueFormat = rs.Data("Value Format")
s obj.permissions = rs.Data("Permissions")
d array.%Push(obj)
}
q array
}
ClassMethod PythonMath() As %String
{
s math = ##class(%SYS.Python).Import("math")
q math.pi
}
}