¿Cómo se utiliza el control de versiones con el desarrollo de Access?
Estoy involucrado en la actualización de una solución de Access. Tiene una buena cantidad de VBA, varias consultas, una pequeña cantidad de tablas y algunos formularios para la entrada de datos y la generación de informes. Es un candidato ideal para Access.
Quiero realizar cambios en el diseño de la tabla, VBA, las consultas y los formularios. ¿Cómo puedo realizar un seguimiento de mis cambios con el control de versiones? (Usamos Subversion, pero esto se aplica a cualquier versión). Puedo colocar todo el mdb en Subversion, pero eso almacenará un archivo binario y no podré decir que acabo de cambiar una línea de código VBA.
Pensé en copiar el código VBA en archivos separados y guardarlos, pero pude ver que rápidamente no estaban sincronizados con lo que hay en la base de datos.
Escribimos nuestro propio script en VBScript, que utiliza el Application.SaveAsText() no documentado en Access para exportar todo el código, formulario, macro y módulos de informes. Aquí está, debería darle algunos consejos. (Cuidado: algunos de los mensajes están en alemán, pero puedes cambiarlos fácilmente).
EDITAR: Para resumir varios comentarios a continuación:
Nuestro proyecto asume un archivo .adp. Para que esto funcione con .mdb/.accdb, debe cambiar OpenAccessProject() a OpenCurrentDatabase(). (Actualizado para usar OpenAccessProject()
si ve una extensión .adp; de lo contrario, use OpenCurrentDatabase()
).
descomponer.vbs:
' Usage:
' CScript decompose.vbs <input file> <path>
' Converts all modules, classes, forms and macros from an Access Project file (.adp) <input file> to
' text and saves the results in separate files to <path>. Requires Microsoft Access.
'
Option Explicit
const acForm = 2
const acModule = 5
const acMacro = 4
const acReport = 3
' BEGIN CODE
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
dim sADPFilename
If (WScript.Arguments.Count = 0) then
MsgBox "Bitte den Dateinamen angeben!", vbExclamation, "Error"
Wscript.Quit()
End if
sADPFilename = fso.GetAbsolutePathName(WScript.Arguments(0))
Dim sExportpath
If (WScript.Arguments.Count = 1) then
sExportpath = ""
else
sExportpath = WScript.Arguments(1)
End If
exportModulesTxt sADPFilename, sExportpath
If (Err <> 0) and (Err.Description <> NULL) Then
MsgBox Err.Description, vbExclamation, "Error"
Err.Clear
End If
Function exportModulesTxt(sADPFilename, sExportpath)
Dim myComponent
Dim sModuleType
Dim sTempname
Dim sOutstring
dim myType, myName, myPath, sStubADPFilename
myType = fso.GetExtensionName(sADPFilename)
myName = fso.GetBaseName(sADPFilename)
myPath = fso.GetParentFolderName(sADPFilename)
If (sExportpath = "") then
sExportpath = myPath & "\Source\"
End If
sStubADPFilename = sExportpath & myName & "_stub." & myType
WScript.Echo "copy stub to " & sStubADPFilename & "..."
On Error Resume Next
fso.CreateFolder(sExportpath)
On Error Goto 0
fso.CopyFile sADPFilename, sStubADPFilename
WScript.Echo "starting Access..."
Dim oApplication
Set oApplication = CreateObject("Access.Application")
WScript.Echo "opening " & sStubADPFilename & " ..."
If (Right(sStubADPFilename,4) = ".adp") Then
oApplication.OpenAccessProject sStubADPFilename
Else
oApplication.OpenCurrentDatabase sStubADPFilename
End If
oApplication.Visible = false
dim dctDelete
Set dctDelete = CreateObject("Scripting.Dictionary")
WScript.Echo "exporting..."
Dim myObj
For Each myObj In oApplication.CurrentProject.AllForms
WScript.Echo " " & myObj.fullname
oApplication.SaveAsText acForm, myObj.fullname, sExportpath & "\" & myObj.fullname & ".form"
oApplication.DoCmd.Close acForm, myObj.fullname
dctDelete.Add "FO" & myObj.fullname, acForm
Next
For Each myObj In oApplication.CurrentProject.AllModules
WScript.Echo " " & myObj.fullname
oApplication.SaveAsText acModule, myObj.fullname, sExportpath & "\" & myObj.fullname & ".bas"
dctDelete.Add "MO" & myObj.fullname, acModule
Next
For Each myObj In oApplication.CurrentProject.AllMacros
WScript.Echo " " & myObj.fullname
oApplication.SaveAsText acMacro, myObj.fullname, sExportpath & "\" & myObj.fullname & ".mac"
dctDelete.Add "MA" & myObj.fullname, acMacro
Next
For Each myObj In oApplication.CurrentProject.AllReports
WScript.Echo " " & myObj.fullname
oApplication.SaveAsText acReport, myObj.fullname, sExportpath & "\" & myObj.fullname & ".report"
dctDelete.Add "RE" & myObj.fullname, acReport
Next
WScript.Echo "deleting..."
dim sObjectname
For Each sObjectname In dctDelete
WScript.Echo " " & Mid(sObjectname, 3)
oApplication.DoCmd.DeleteObject dctDelete(sObjectname), Mid(sObjectname, 3)
Next
oApplication.CloseCurrentDatabase
oApplication.CompactRepair sStubADPFilename, sStubADPFilename & "_"
oApplication.Quit
fso.CopyFile sStubADPFilename & "_", sStubADPFilename
fso.DeleteFile sStubADPFilename & "_"
End Function
Public Function getErr()
Dim strError
strError = vbCrLf & "----------------------------------------------------------------------------------------------------------------------------------------" & vbCrLf & _
"From " & Err.source & ":" & vbCrLf & _
" Description: " & Err.Description & vbCrLf & _
" Code: " & Err.Number & vbCrLf
getErr = strError
End Function
Si necesita un comando en el que se puede hacer clic, en lugar de usar la línea de comando, cree un archivo llamado "decompose.cmd" con
cscript decompose.vbs youraccessapplication.adp
De forma predeterminada, todos los archivos exportados van a la subcarpeta "Scripts" de su aplicación Access. El archivo .adp/mdb también se copia en esta ubicación (con un sufijo "stub") y se eliminan todos los módulos exportados, lo que lo hace realmente pequeño.
DEBE registrar este código auxiliar con los archivos fuente, porque la mayoría de las configuraciones de acceso y las barras de menú personalizadas no se pueden exportar de ninguna otra manera. Solo asegúrese de confirmar los cambios solo en este archivo, si realmente cambió alguna configuración o menú.
Nota: Si tiene algún Autoexec-Makros definido en su aplicación, es posible que deba mantener presionada la tecla Shift cuando invoque la descomposición para evitar que se ejecute e interfiera con la exportación.
Por supuesto, también existe el script inverso, para construir la aplicación desde el directorio "Fuente":
componer.vbs:
' Usage:
' WScript compose.vbs <file> <path>
' Converts all modules, classes, forms and macros in a directory created by "decompose.vbs"
' and composes then into an Access Project file (.adp). This overwrites any existing Modules with the
' same names without warning!!!
' Requires Microsoft Access.
Option Explicit
const acForm = 2
const acModule = 5
const acMacro = 4
const acReport = 3
Const acCmdCompileAndSaveAllModules = &H7E
' BEGIN CODE
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
dim sADPFilename
If (WScript.Arguments.Count = 0) then
MsgBox "Please enter the file name!", vbExclamation, "Error"
Wscript.Quit()
End if
sADPFilename = fso.GetAbsolutePathName(WScript.Arguments(0))
Dim sPath
If (WScript.Arguments.Count = 1) then
sPath = ""
else
sPath = WScript.Arguments(1)
End If
importModulesTxt sADPFilename, sPath
If (Err <> 0) and (Err.Description <> NULL) Then
MsgBox Err.Description, vbExclamation, "Error"
Err.Clear
End If
Function importModulesTxt(sADPFilename, sImportpath)
Dim myComponent
Dim sModuleType
Dim sTempname
Dim sOutstring
' Build file and pathnames
dim myType, myName, myPath, sStubADPFilename
myType = fso.GetExtensionName(sADPFilename)
myName = fso.GetBaseName(sADPFilename)
myPath = fso.GetParentFolderName(sADPFilename)
' if no path was given as argument, use a relative directory
If (sImportpath = "") then
sImportpath = myPath & "\Source\"
End If
sStubADPFilename = sImportpath & myName & "_stub." & myType
' check for existing file and ask to overwrite with the stub
if (fso.FileExists(sADPFilename)) Then
WScript.StdOut.Write sADPFilename & " exists. Overwrite? (y/n) "
dim sInput
sInput = WScript.StdIn.Read(1)
if (sInput <> "y") Then
WScript.Quit
end if
fso.CopyFile sADPFilename, sADPFilename & ".bak"
end if
fso.CopyFile sStubADPFilename, sADPFilename
' launch MSAccess
WScript.Echo "starting Access..."
Dim oApplication
Set oApplication = CreateObject("Access.Application")
WScript.Echo "opening " & sADPFilename & " ..."
If (Right(sStubADPFilename,4) = ".adp") Then
oApplication.OpenAccessProject sADPFilename
Else
oApplication.OpenCurrentDatabase sADPFilename
End If
oApplication.Visible = false
Dim folder
Set folder = fso.GetFolder(sImportpath)
' load each file from the import path into the stub
Dim myFile, objectname, objecttype
for each myFile in folder.Files
objecttype = fso.GetExtensionName(myFile.Name)
objectname = fso.GetBaseName(myFile.Name)
WScript.Echo " " & objectname & " (" & objecttype & ")"
if (objecttype = "form") then
oApplication.LoadFromText acForm, objectname, myFile.Path
elseif (objecttype = "bas") then
oApplication.LoadFromText acModule, objectname, myFile.Path
elseif (objecttype = "mac") then
oApplication.LoadFromText acMacro, objectname, myFile.Path
elseif (objecttype = "report") then
oApplication.LoadFromText acReport, objectname, myFile.Path
end if
next
oApplication.RunCommand acCmdCompileAndSaveAllModules
oApplication.Quit
End Function
Public Function getErr()
Dim strError
strError = vbCrLf & "----------------------------------------------------------------------------------------------------------------------------------------" & vbCrLf & _
"From " & Err.source & ":" & vbCrLf & _
" Description: " & Err.Description & vbCrLf & _
" Code: " & Err.Number & vbCrLf
getErr = strError
End Function
Nuevamente, esto va con un complemento "compose.cmd" que contiene:
cscript compose.vbs youraccessapplication.adp
Le pide que confirme la sobrescritura de su aplicación actual y primero crea una copia de seguridad, si lo hace. Luego recopila todos los archivos fuente en el directorio fuente y los vuelve a insertar en el código auxiliar.
¡Divertirse!
La solución de composición/descomposición publicada por Oliver es excelente, pero tiene algunos problemas:
- Los archivos están codificados como UCS-2 (UTF-16), lo que puede hacer que los sistemas/herramientas de control de versiones consideren que los archivos son binarios.
- Los archivos contienen mucha información que cambia con frecuencia: sumas de verificación, información de la impresora y más. Este es un problema grave si desea diferencias limpias o necesita cooperar en el proyecto.
Estaba planeando solucionar este problema yo mismo, pero descubrí que ya hay una buena solución disponible: timabell/msaccess-vcs-integration en GitHub. Probé msaccess-vcs-integration y funciona muy bien.
Actualizado el 3 de marzo de 2015 : el proyecto fue originalmente mantenido/propiedad de bkidwell en Github, pero se transfirió a timabell ; el enlace de arriba al proyecto se actualiza en consecuencia. Hay algunas bifurcaciones del proyecto original de bkidwell, por ejemplo de ArminBra y de matonb , que AFAICT no deberían usarse.
La desventaja de usar msaccess-vcs-integration en comparación con la solución de descomposición de Olivers:
- Es significativamente más lento. Estoy seguro de que el problema de la velocidad se puede solucionar, pero no necesito exportar mi proyecto a texto con tanta frecuencia...
- No crea un proyecto auxiliar de Access con el material exportado eliminado. Esto también se puede solucionar (adoptando código del script de descomposición), pero nuevamente, no es tan importante.
De todos modos, mi recomendación clara es msaccess-vcs-integration. Resolvió todos los problemas que tuve al usar Git en los archivos exportados.