forked from cenx1/msaccess-devops
-
Notifications
You must be signed in to change notification settings - Fork 3
/
basDeploy.bas
1518 lines (1257 loc) · 55.5 KB
/
basDeploy.bas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Attribute VB_Name = "basDeploy"
'=======================================================================================
' This module is updated automatically from the Code Templates
' **IMPORTANT** database. If you need to make custom changes, please change
' the following line to turn off the automatic updating for this
' item. (True for automatic updates, False to disable updates)
'=======================================================================================
'@AutoUpdate = True
'---------------------------------------------------------------------------------------
' Module : basDeploy
' Author : Adam Waller
' Date : 8/24/2019
' Purpose : Deploy an update to an Access Database application.
' : Version number is stored in a custom property in the local database.
'---------------------------------------------------------------------------------------
Option Compare Database
Option Private Module
Option Explicit
Private Const ModuleName As String = "basDeploy"
'---------------------------------------------------------------------------------------
' DEFAULT USER CONFIGURED OPTIONS
' (Uses saved registry values instead, if they exist. See SaveOptions sub below)
'---------------------------------------------------------------------------------------
Private Const DEPLOYMENT_DATABASE_NAME As String = "YOURBACKENDDBNAME"
Private Const DevOpsProjectName As String = "PROJECTNAMEDevOps"
' Specify the path to the deployment folder. (UNC path not supported)
Private Const DEFAULT_DEPLOY_FOLDER As String = "T:\Apps\Deploy\"
' Pipe delimited list of root project folders that should not be deployed.
Private Const IGNORE_FOLDERS As String = "dev|cache|archive"
'---------------------------------------------------------------------------------------
' Used for debug output display
Private Const cstrSpacer As String = "---------------------------------------------------------------------------------------"
Private Const VersionDelimiter As String = "."
' Constants so we don't have to use the VBE reference in projects
Private Const vbext_ct_StdModule As Integer = 1
Private Const vbext_ct_ClassModule As Integer = 2
' Collection of versions as read from `Latest Versions.csv`
Private mVersions As Collection
Private mUpdates As Collection
' Enum to improve code readability.
' These match the columns in the
' Latest Versions.csv file.
Private Enum eVersion
evName
evVersion
evDate
evFile
evType
evNotes
End Enum
Public Enum eVersionType
' Return codes for version check routines.
evtUninitialized
evtStableRelease
evtBetaRelease
evtDevelopmentVersion ' Is a development Version
[_Last]
End Enum
' Release type used when updating version
' or deploying add-in
Public Enum eReleaseType
Major_Vxxx = 0
Minor_xVxx = 1
Patch_xxVx = 2
Build_xxxV = 3
Same_Version = 4
End Enum
' These check a few items to verify you are ready to deploy.
Public Function DeployChecks() As Boolean
Const FunctionName As String = ModuleName & ".DeployChecks"
DeployChecks = False
' Verify correct database backend is being deployed.
' This should be called from one of your project's functions (or however you determine the backend database)
' NOTE: YOURCLASS is a class you would build to determine backend database and other information for use here.
' You may also decide to just make it a reglar property or function if you like.
If Not YOURCLASS.IsDeploymentDatabase Then
If MsgBox("Wrong Deployment Database Detected!" & vbNewLine & vbNewLine & _
"Deploy Database: " & DEPLOYMENT_DATABASE_NAME & vbNewLine & vbNewLine & _
"Current Database: " & YOURCLASS.BackendDatabase & vbNewLine & vbNewLine & _
"Are you certain you want to deploy to the " & YOURCLASS.BackendDatabase & " database?" & vbNewLine & vbNewLine & _
"Press 'YES' to deploy with current database." _
, vbDefaultButton2 + vbYesNo, "Incorrect Deploy Database Detected") <> vbYes Then Exit Function
End If
' Verify you have the mapped drive actually connected.
If GetDeploymentFolder = vbNullString Then Exit Function
' Verify Version Type is correct
If VersionType = evtUninitialized Or (VersionType > (eVersionType.[_Last] - 1)) Then
Log.Prompt FunctionName, "Version type is not initialized." _
, "Options: " & vbNewLine & DeploymentList _
, "Current VersionType: " & VersionType, vbOKOnly, "Incorrect Version Type!", eelError, True
Exit Function
End If
' Verify you are intending to release THIS type of release.
If Log.Prompt(FunctionName, "You are about to release a new " & vbNewLine & vbNewLine & VersionTypeName _
, "Is this correct?" & vbNewLine & "If not, correct version type and re-deploy.", "Select 'YES' to deploy release." _
, vbInformation + vbYesNoCancel, "Confirm Release Type", eelDebugInfo, True) <> vbYes Then Exit Function
DeployChecks = True
End Function
Public Function DeploymentList() As String
Dim fReleaseCount As Long
Dim strSpacer As String
Dim lngCol(0 To 2) As Long
Const cstrTitle As String = "Version Types "
' Set up column sizes
lngCol(0) = 30
lngCol(1) = 10
'lngCol(2) = 0
strSpacer = Space(lngCol(0) + lngCol(1) + lngCol(2))
strSpacer = Replace(strSpacer, " ", "-")
With New clsConcat
.AppendOnAdd = vbNewLine
.Add strSpacer
.Add Space((Len(strSpacer) - Len(cstrTitle)) / 2) & cstrTitle
.Add ListResult("Release Name", "ID", vbNullString, lngCol), vbNewLine, strSpacer
For fReleaseCount = 1 To eVersionType.[_Last] - 1
.Add ListResult(GetVersionTypeName(fReleaseCount), CStr(fReleaseCount), vbNullString, lngCol)
Next fReleaseCount
.Remove 1
DeploymentList = .GetStr
End With
End Function
'---------------------------------------------------------------------------------------
' Procedure : Deploy
' Author : Adam Waller, hecon5
' Date : 1/5/2017, 2022 JUL 14
' Purpose : Deploys the program for end users to install and run.
' : Returns true if the deployement process was completed.
'---------------------------------------------------------------------------------------
'
Public Function Deploy(Optional blnIgnorePendingUpdates As Boolean = False _
, Optional ReleaseType As eReleaseType = Same_Version) As Boolean
Const FunctionName As String = ModuleName & ".Deploy"
Dim strPath As String
Dim strTools As String
Dim strName As String
Dim strCmd As String
Dim strIcon As String
Dim objComponent As Object
Dim strFile As String
Dim intCnt As Integer
' Make sure we don't accidentally deploy a nested library!
If CodeProject.FullName <> CurrentProject.FullName Then
Debug.Print " ** WARNING ** " & CodeProject.Name & " is not the top-level project!"
Debug.Print " Switching to " & CurrentProject.Name & "..."
Set VBE.ActiveVBProject = GetVBProjectForCurrentDB
' Fire off deployment from primary database.
Run "[" & GetVBProjectForCurrentDB.Name & "].Deploy"
Exit Function
End If
' Show debug output
Debug.Print vbNewLine & cstrSpacer
Debug.Print "Deployment Started - " & Now()
' Check deployment state readiness
If Not DeployChecks Then
Debug.Print "Failed Checks, deploy aborted - " & Now()
Debug.Print cstrSpacer
Exit Function
End If
' Check for any updates to dependent libraries
If CheckForUpdates Then
If Not blnIgnorePendingUpdates Then
Debug.Print cstrSpacer
Debug.Print " *** UPDATES AVAILABLE *** "
Debug.Print "Please install before deployment or set flag "
Debug.Print "to continue deployment anyway. I.e. `Deploy True`" & vbNewLine & cstrSpacer
Exit Function
End If
End If
' Check for reference issues with dependent modules
If HasDuplicateProjects Then
Select Case Eval("MsgBox('Would you like to run ''LocalizeReferences'' first?@Some VBA projects appear duplicated which usually indicates non-local references.@Select ''No'' to continue anyway or ''Cancel'' to cancel the deployment.@" & _
"(Library databases that are only used as a part of other applications are typically not deployed as ClickOnce installers.)@',35)")
Case vbYes
LocalizeReferences
Exit Function
Case vbNo
' Continue anyway.
Case Else
Exit Function
End Select
End If
' ' Commented out here, but you will want to clear any logging stored locally to ensure users
' ' aren't sending you your own errors during testing...
' ControlLog.Reset True
' Log.Clear False
' Increment build number
IncrementAppVersion ReleaseType
' List project and new build number
Debug.Print " ~ " & DeploymentProjectName & " ~ Version " & AppVersion
Debug.Print cstrSpacer
' Update project description
VBE.ActiveVBProject.description = "Version " & AppVersion & " deployed on " & date
' Get deployment folder (Create if needed)
' Note: This is the version-specific folder for this release.
strPath = GetDeploymentVersionFolder
' Check flag for ClickOnce deployment.
If IsClickOnce Then
' Copy project files
Debug.Print "Copying Files";
Debug.Print vbNewLine & CopyFiles(CodeProject.path & PathSep, strPath, True) & " files copied."
' Get tools folder
strTools = GetDeploymentFolder & "_Tools" & PathSep
' Copy manifest templates to project
strName = DeploymentProjectName
' Build shell command
strCmd = "cmd /s /c " & """""" & strTools & "Deploy.bat"" """ & strName & """" & " """ & AppVersion & """"
' Add application icon if one exists in the application folder.
' If you allow multiple deployment types, this associates the logo as needed.
' If specific deployment icon not found, use first icon found.
strIcon = Dir(CodeProject.path & PathSep & "*" & VersionTypeName & ".ico")
If strIcon = vbNullString Then strIcon = Dir(CodeProject.path & PathSep & "*.ico")
If strIcon <> vbNullString Then strCmd = strCmd & " """ & strIcon & """"
' Compile and build clickonce installation
Shell strCmd, vbNormalFocus
' Print final status message.
Debug.Print "Files Copied. Please review command window for any errors." & vbNewLine & cstrSpacer
Else
' Code templates are handled just a little differently
If CodeProject.Name = "Code Templates.accdb" Then
' Build path for exported template components.
strPath = GetDeploymentFolder & "Code Templates" & PathSep
' Loop through all component objects, exporting each one to a file.
For Each objComponent In GetVBProjectForCurrentDB.VBComponents
With objComponent
If .Name <> "basInternal" Then
strFile = strPath & .Name & ".bas"
' Remove any existing file before exporting
If Dir(strFile) <> vbNullString Then Kill strFile
.Export strFile
intCnt = intCnt + 1
End If
End With
Next objComponent
Debug.Print intCnt & " templates deployed to " & strPath
Else
' Probably a code library without a click-once installer.
' Deploy just the library versioned folder. (Do not include dependent
' libraries or we could cause some real issues with versions overwriting
' each other's dependencies as different libraries are updated.)
Debug.Print "Copying " & CodeProject.Name
CreateObject("Scripting.FileSystemObject").CopyFile CodeProject.FullName, strPath
Debug.Print "Library deployed."
End If
End If
' Update list of latest versions.
LoadVersionList
UpdateVersionInList
SaveVersionList
Deploy = True
End Function
'---------------------------------------------------------------------------------------
' Procedure : UpdateDependencies
' Author : Adam Waller
' Date : 4/3/2020
' Purpose : Updates all dependencies in current project with latest versions.
' : Displays errors if files are not found.
'---------------------------------------------------------------------------------------
'
Public Sub UpdateDependencies()
Dim blnUpdatedLibraries As Boolean
' Make sure we check for updates before updating dependencies. :-)
If mUpdates Is Nothing Then CheckForUpdates
' See if we actually have some updates to process.
If mUpdates.Count = 0 Then
' Nothing to update
Debug.Print "No updates found."
Else
blnUpdatedLibraries = (UpdateLibraries > 0)
UpdateVBAComponents
Debug.Print cstrSpacer
Debug.Print "Update complete. ";
' Prompt user to restart database if needed.
If blnUpdatedLibraries Then
Debug.Print "DATABASE RESTART REQUIRED"
Debug.Print "Please close and reopen this database to apply changes."
Else
Debug.Print
' Make sure we are working in the current project
Set VBE.ActiveVBProject = GetVBProjectForCurrentDB
Debug.Print "Compiling and saving modules..."
' Compile and save all code modules
Application.RunCommand acCmdCompileAndSaveAllModules
Debug.Print "Done."
End If
End If
' Reset the updates collection
Set mUpdates = Nothing
End Sub
'---------------------------------------------------------------------------------------
' Procedure : UpdateVBAComponents
' Author : Adam Waller
' Date : 4/3/2020
' Purpose : Update the VBA objects like modules and classes
'---------------------------------------------------------------------------------------
'
Private Function UpdateVBAComponents() As Integer
Dim varUpdate As Variant
Dim strFile As String
Dim intUpdated As Integer
Dim cmp As Object ' VBComponent
' Loop through updates and process any components
For Each varUpdate In mUpdates
' Check for component type update
If varUpdate(evType) = "Component" Then
' Make sure it comes from Code Templates
If GetFileNameFromPath(CStr(varUpdate(evFile))) = "Code Templates.accdb" Then
' Coming from our code templates. Get path to latest file.
strFile = GetDeploymentFolder & "Code Templates" & PathSep & varUpdate(evName) & ".bas"
' Make sure file exists
If Dir(strFile) = vbNullString Then
Debug.Print "ERROR: Could not find " & strFile
Else
If varUpdate(evName) = "basDeploy" Then
UpdateDeployModule
Else
Set cmp = GetVBProjectForCurrentDB.VBComponents(varUpdate(evName))
If cmp.Type = vbext_ct_ClassModule Or cmp.Type = vbext_ct_StdModule Then
' Remove existing module and replace with file
With GetVBProjectForCurrentDB.VBComponents
.Remove .Item(varUpdate(evName))
.Import strFile
End With
Else
' Other components like forms. Replace code module from file.
' (Could extend this later to replace entire object, but start with this.)
With cmp.CodeModule
.DeleteLines 1, .CountOfLines
.AddFromFile strFile
End With
End If
Debug.Print "Updated " & varUpdate(evName)
intUpdated = intUpdated + 1
End If
End If
End If
End If
Next varUpdate
' Return number of components updated
UpdateVBAComponents = intUpdated
End Function
'---------------------------------------------------------------------------------------
' Procedure : UpdateLibraries
' Author : Adam Waller
' Date : 4/3/2020
' Purpose : Update library databases. (Only auto-update the linked library itself,
' : not any other dependencies, lest we create version issues.)
'---------------------------------------------------------------------------------------
'
Private Function UpdateLibraries() As Integer
Dim varUpdate As Variant
Dim strExisting As String
Dim strFile As String
Dim intUpdated As Integer
' Loop through updates and process any components
For Each varUpdate In mUpdates
' Check for (library) file type update
If varUpdate(evType) = "File" Then
' Build full path to file
strFile = GetDeploymentFolder & varUpdate(evName) & PathSep & varUpdate(evVersion) & PathSep & varUpdate(evFile)
' Make sure file exists
If Dir(strFile) = vbNullString Then
Debug.Print "ERROR: Could not find " & strFile
Else
' Check for referenced file
strExisting = CodeProject.Path & PathSep & varUpdate(evFile)
If Dir(strExisting) = vbNullString Then
Debug.Print "ERROR: Could not find existing library: " & strExisting
Else
' Replace existing file
CreateObject("Scripting.FileSystemObject").CopyFile strFile, strExisting, True
Debug.Print "Updated " & varUpdate(evName) & " to version " & varUpdate(evVersion)
intUpdated = intUpdated + 1
End If
End If
End If
Next varUpdate
' Return number of libraries updated.
UpdateLibraries = intUpdated
End Function
'---------------------------------------------------------------------------------------
' Procedure : GetIgnoredFolders
' Author : Adam Waller
' Date : 8/24/2019
' Purpose : Returns the pipe delimited list of ignored folders
'---------------------------------------------------------------------------------------
'
Private Function GetIgnoredFolders() As String
' Use value saved in registry, or fall back to default constant
GetIgnoredFolders = GetSetting(DevOpsProjectName, "SE API", "Ignore Folders", IGNORE_FOLDERS)
End Function
Public Property Get Deploy_Folder() As String
Dim strDeployPath As String
' Use path saved in registry, or fall back to default constant
strDeployPath = GetSetting(DevOpsProjectName, "SE API", "Deploy Folder " & VersionTypeName, StripSlash(DEFAULT_DEPLOY_FOLDER) & PathSep & VersionTypeName & PathSep)
If Len(strDeployPath) = 0 Then strDeployPath = DEFAULT_DEPLOY_FOLDER
Deploy_Folder = strDeployPath
End Property
Private Property Let Deploy_Folder(NewVal As String)
SaveSetting DevOpsProjectName, "SE API", "Deploy Folder " & VersionTypeName, NewVal
End Property
'---------------------------------------------------------------------------------------
' Procedure : GetDeploymentFolder
' Author : Adam Waller
' Date : 8/24/2019
' Purpose : Returns path to base deployment folder.
'---------------------------------------------------------------------------------------
'
Private Function GetDeploymentFolder() As String
Dim strPath As String
Dim strTest As String
strPath = Deploy_Folder
' Make sure the folder exists before we continue.
' (It might not in an environment where the registry override is being used,
' and the application is being deployed from a new development computer or
' user profile that doesn't have the override configured.)
TestPath:
strTest = Left$(strPath, Len(strPath) - 1)
If Len(Nz(Dir(strTest, vbDirectory))) < 3 Then
If MsgBox("Deployment path '" & strPath & "' not found." & vbNewLine & _
"Would you like to enter a custom path to use instead?" & vbNewLine & _
"The custom path will be saved in this user profile for future deployments.", vbQuestion + vbYesNo) = vbYes Then
strPath = InputBox("Enter path to deployment folder:", , Deploy_Folder)
' Abort if cancelled
If strPath = vbNullString Then Exit Function
' Save the new selection
SaveOptions strPath, GetIgnoredFolders
' Test the newly entered path before using it.
GoTo TestPath
Else
' Revert to default if they didn't want to create a custom one.
strPath = Deploy_Folder
End If
End If
' Return path
GetDeploymentFolder = strPath
End Function
'---------------------------------------------------------------------------------------
' Procedure : SaveOptions
' Author : Adam Waller
' Date : 8/24/2019
' Purpose : Save a set of user options to the registry, to override the default
' : constants for this computer/user profile.
'---------------------------------------------------------------------------------------
'
Private Sub SaveOptions(strDeployFolder As String, strIgnoreFolders As String)
Deploy_Folder = strDeployFolder
SaveSetting DevOpsProjectName, "SE API", "Ignore Folders", strIgnoreFolders
Debug.Print "Settings saved."
End Sub
'---------------------------------------------------------------------------------------
' Procedure : GetDeploymentVersionFolder
' Author : Adam Waller
' Date : 8/24/2019
' Purpose : Returns the full path to the deployment folder used for this release,
' : including both the application name and version number.
' : (I.e. T:\Apps\Deploy\SE API\1.0.0.12\
'---------------------------------------------------------------------------------------
'
Private Function GetDeploymentVersionFolder() As String
Dim strPath As String
Dim strProject As String
Dim strVersion As String
strPath = GetDeploymentFolder
strProject = DeploymentProjectName
strVersion = AppVersion
' Build out full path for deployment
strPath = strPath & strProject
If Dir(strPath, vbDirectory) = vbNullString Then
' Create project folder
MkDir strPath
End If
strPath = strPath & PathSep & strVersion
If Dir(strPath, vbDirectory) = vbNullString Then
' Create version folder
MkDir strPath
End If
' Return full path
GetDeploymentVersionFolder = strPath & PathSep
End Function
'---------------------------------------------------------------------------------------
' Procedure : AppVersion
' Author : Adam Waller
' Date : 1/5/2017
' Purpose : Get the version from the database property.
'---------------------------------------------------------------------------------------
'
Public Property Get AppVersion() As String
Dim strVersion As String
strVersion = GetDBProperty("AppVersion")
If strVersion = vbNullString Then strVersion = "1.0.0.0"
AppVersion = strVersion
End Property
Public Property Let AppVersion(strVersion As String)
Dim VerSplit As Variant
Dim ArrPosition As Long
Dim NewValue As String
VerSplit = Split(strVersion, VersionDelimiter)
If UBound(VerSplit) <> 3 Then
ReDim Preserve VerSplit(3)
For ArrPosition = 0 To 3
' We substitute in a non-number if it's empty, to ensure it's false.
If Not IsNumeric(Nz(VerSplit(ArrPosition), "a")) Then VerSplit(ArrPosition) = "0"
Next ArrPosition
NewValue = Join(VerSplit, VersionDelimiter)
Else
NewValue = strVersion
End If
SetDBProperty "AppVersion", NewValue
' You may need to correct this for zulu time, this uses VBA's "Now()" function
SetDBProperty "ReleaseDate", Now()
End Property
Public Property Get BuildNumber() As Long
Dim tAppStr As Variant
tAppStr = Split(AppVersion, VersionDelimiter)
BuildNumber = CLng(tAppStr(Build_xxxV))
End Property
' This returns the release date in LOCAL time.
' Note: release date is set when the AppVersion is updated.
Public Property Get ReleaseDate() As Date
ReleaseDate = ParseTimeStampToLocalDate(ReleaseDateStr)
End Property
Public Property Get ReleaseDateStr() As String
ReleaseDateStr = GetDBProperty("ReleaseDate")
End Property
Public Property Get VersionType() As eVersionType
VersionType = GetDBProperty("VersionType")
End Property
Public Property Let VersionType(NewVal As eVersionType)
SetDBProperty "VersionType", NewVal
End Property
Public Property Get VersionTypeName() As String
VersionTypeName = GetVersionTypeName(VersionType)
End Property
Private Function GetVersionTypeName(VersionTypeEnum As eVersionType) As String
Select Case VersionTypeEnum
Case evtUninitialized: GetVersionTypeName = "Uninitialized"
Case evtStableRelease: GetVersionTypeName = "Stable Release"
Case evtBetaRelease: GetVersionTypeName = "Beta Release"
Case evtDevelopmentVersion: GetVersionTypeName = "Development Version"
Case Else: GetVersionTypeName = vbNullString
End Select
End Function
Private Property Get DeploymentProjectName()
DeploymentProjectName = VBE.ActiveVBProject.Name & " " & VersionTypeName
End Property
'---------------------------------------------------------------------------------------
' Procedure : GetDBProperty
' Author : Adam Waller
' Date : 9/1/2017
' Purpose : Get a database property
'---------------------------------------------------------------------------------------
'
Public Function GetDBProperty(strName As String) As Variant
Dim prp As Object ' Access.AccessObjectProperty
For Each prp In PropertyParent.Properties
If prp.Name = strName Then
GetDBProperty = prp.Value
Exit For
End If
Next prp
Set prp = Nothing
End Function
'---------------------------------------------------------------------------------------
' Procedure : SetDBProperty
' Author : Adam Waller
' Date : 9/1/2017
' Purpose : Set a database property
'---------------------------------------------------------------------------------------
'
Public Sub SetDBProperty(strName As String _
, varValue As Variant _
, Optional prpType = DB_TEXT)
Dim prp As Object ' Access.AccessObjectProperty
Dim prpAccdb As Property
Dim blnFound As Boolean
Dim dbs As Database
For Each prp In PropertyParent.Properties
If prp.Name = strName Then
blnFound = True
' Skip set on matching value
If prp.Value = varValue Then Exit Sub
Exit For
End If
Next prp
On Error Resume Next
If blnFound Then
PropertyParent.Properties(strName).Value = varValue
Else
If CurrentProject.ProjectType = acADP Then
PropertyParent.Properties.Add strName, varValue
Else
' Normal accdb database property
Set dbs = CurrentDb
Set prpAccdb = dbs.CreateProperty(strName, prpType, varValue)
dbs.Properties.Append prpAccdb
Set dbs = Nothing
End If
End If
If Err Then Err.Clear
On Error GoTo 0
End Sub
'---------------------------------------------------------------------------------------
' Procedure : PropertyParent
' Author : Adam Waller
' Date : 1/30/2017
' Purpose : Get the correct parent type for database properties (including custom)
'---------------------------------------------------------------------------------------
'
Private Function PropertyParent() As Object
' Get correct parent project type
If CurrentProject.ProjectType = acADP Then
Set PropertyParent = CurrentProject
Else
Set PropertyParent = CurrentDb
End If
End Function
'---------------------------------------------------------------------------------------
' Procedure : IncrementAppVersion
' Author : Adam Waller, hecon5
' Date : 1/6/2017; 2022-JUL-012
' Purpose : Increments the build version (1.0.0.x)
'---------------------------------------------------------------------------------------
'
Public Sub IncrementAppVersion(Optional ReleaseType As eReleaseType = Patch_xxVx)
Dim varParts As Variant
Dim BuildNumber As Long
varParts = Split(AppVersion, VersionDelimiter)
ReDim Preserve varParts(3) ' Ensure there are 4 (3 = zero referenced) parts; ClickOnce doesn't like only 3...
If ReleaseType = Same_Version Or ReleaseType = Build_xxxV Then GoTo Bump_Build
varParts(ReleaseType) = varParts(ReleaseType) + 1
If ReleaseType < Minor_xVxx Then varParts(Minor_xVxx) = 0
If ReleaseType < Patch_xxVx Then varParts(Patch_xxVx) = 0
Bump_Build:
' Always increment build number; this ensures a unique build
' there can be small discrepancies between builds, even if no
' code changes, and this helps make it obvious when troubleshooting.
If ReleaseType < Build_xxxV Then
varParts(Build_xxxV) = 0
Else
BuildNumber = Nz(varParts(Build_xxxV), 0)
BuildNumber = BuildNumber + 1
varParts(Build_xxxV) = BuildNumber
End If
AppVersion = Join(varParts, VersionDelimiter)
End Sub
'---------------------------------------------------------------------------------------
' Procedure : CopyFiles
' Author : Adam Waller
' Date : 1/5/2017
' Purpose : Recursive function to copy files from one folder to another.
' : (Set to ignore certain files)
'---------------------------------------------------------------------------------------
'
Private Function CopyFiles(strSource As String _
, strDest As String _
, blnOverwrite As Boolean) As Double
Dim strFile As String
Dim dblCnt As Double
Dim strBase As String
Dim objFSO As Object
Dim oFolder As Object
Dim oFile As Object
Dim blnExists As Boolean
Dim varSkip As Variant
Dim varItem As Variant
' Requires FSO to copy open database files. (VBA.FileCopy gives a permission denied error.)
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = objFSO.GetFolder(strSource)
' Ignore certain types of base folders
strBase = CurrentProject.Path
varSkip = Split(GetIgnoredFolders, "|")
For Each varItem In varSkip
If strSource = strBase & PathSep & CStr(varItem) & PathSep Then
' Ignore this folder
Exit Function
End If
Next varItem
' Copy files then folders
For Each oFile In objFSO.GetFolder(strSource).Files
strFile = oFile.Name
Select Case True
' Files to skip
Case strFile Like ".*" ' Dot files
Case strFile Like ".git*" ' version files
Case strFile Like "*.laccdb" ' Lock files
Case strFile Like "*backup*" ' saved backups (from VCS, most likely)
Case strFile Like "*.json" ' JSON Files (settings, configuration, stray data).
Case strFile Like "*.bas" ' Stray code files.
Case strFile Like "*.cls"
Case strFile Like "*.frm"
Case strFile Like "*.mod"
Case strFile Like "*.log" ' Log files
Case strFile Like "*.gz"
Case strFile Like "*.zip"
Case Else
blnExists = Dir(strDest & strFile) <> vbNullString
If blnExists And Not blnOverwrite Then
' Skip this file
Else
If blnExists Then Kill strDest & strFile
oFile.Copy strDest & strFile
' Show progress point as each file is copied
dblCnt = dblCnt + 1
Debug.Print ".";
End If
End Select
Next oFile
' Copy folders
For Each oFolder In objFSO.GetFolder(strSource).SubFolders
strFile = oFolder.Name
Select Case True
' Folders to skip
Case strFile = CodeProject.Name & ".src" ' This project
Case strFile Like "*.src" ' Other source files
Case strFile Like ".git*"
Case strFile Like ".*" ' Dot folders (usually also source code files)
Case Else
' Check if folder already exists in destination
If Dir(strDest & strFile, vbDirectory) = vbNullString Then
MkDir strDest & strFile
' Show progress after creating folder but before copying files
Debug.Print ".";
End If
' Recursively copy files from this folder
dblCnt = dblCnt + CopyFiles(strSource & strFile & PathSep, strDest & strFile & PathSep, blnOverwrite)
End Select
Next oFolder
' Release reference to objects.
Set objFSO = Nothing
Set oFile = Nothing
Set oFolder = Nothing
' Return count of files copied.
CopyFiles = dblCnt
End Function
'---------------------------------------------------------------------------------------
' Procedure : CheckForUpdates
' Author : Adam Waller
' Date : 1/27/2017
' Purpose : Check for updates to library databases or template modules
'---------------------------------------------------------------------------------------
'
Public Function CheckForUpdates() As Boolean
Const vbext_rk_Project As Integer = 1
Dim ref As Access.Reference
Dim varLatest As Variant
Dim strCurrent As String
Dim strLatest As String
Dim objComponent As Object
Dim strName As String
Dim intCnt As Integer
Dim intLines As Integer
Dim blnUpdatesAvailable As Boolean
Dim intAutoUpdateCount As Integer
' We shouldn't be running this on deployed applications.
If InStr(1, CurrentProject.Path, PathSep & "AppData" & PathSep) > 1 Then Exit Function
' Reload version file before checking for updates.
LoadVersionList
Set mUpdates = New Collection
' Check references for updates.
For Each ref In Application.References
If ref.Kind = vbext_rk_Project Then
strCurrent = GetCurrentRefVersion(ref)
varLatest = GetLatestVersionDetails(ref.Name)
If IsArray(varLatest) Then
If UBound(varLatest) > 2 Then
strLatest = varLatest(1)
If strLatest <> vbNullString Then
' Compare current with latest.
If strCurrent <> strLatest Then
Debug.Print "UPDATE AVAILABLE: " & ref.Name & " (" & _
GetFileNameFromPath(VBE.VBProjects(ref.Name).FileName) & _
") can be updated from " & strCurrent & " to " & strLatest
blnUpdatesAvailable = True
intAutoUpdateCount = intAutoUpdateCount + 1
mUpdates.Add varLatest
End If
End If
End If
End If
End If
Next ref
' Check code modules for updates
For Each objComponent In GetVBProjectForCurrentDB.VBComponents
strName = objComponent.Name
' Look for matching item in list
For intCnt = 2 To mVersions.Count
If UBound(mVersions(intCnt)) = 4 Then
If (mVersions(intCnt)(evName) = strName) _
And (mVersions(intCnt)(evType) = "Component") Then
' Check for different "version"
intLines = GetCodeLineCount(objComponent.CodeModule)
If mVersions(intCnt)(1) <> intLines _
And mVersions(intCnt)(evFile) <> CurrentProject.Name Then
If AllowAutoUpdate(objComponent.CodeModule) Then
Debug.Print "MODULE UPDATE AVAILABLE: " & strName & _
" can be updated from """ & mVersions(intCnt)(evFile) & """ (" & _
mVersions(intCnt)(evVersion) - intLines & " lines on " & _
mVersions(intCnt)(evDate) & ".)"
blnUpdatesAvailable = True
intAutoUpdateCount = intAutoUpdateCount + 1
mUpdates.Add mVersions(intCnt)
Else
Debug.Print "Manual* update available: " & strName & _
" can be updated from """ & mVersions(intCnt)(evFile) & """ (" & _
mVersions(intCnt)(evVersion) - intLines & " lines on " & _
mVersions(intCnt)(evDate) & ".) *This module is currently flagged to disable automatic updates."
End If
End If
End If
End If
Next intCnt
Next objComponent
' Offer to run auto-update on the available components.
If intAutoUpdateCount > 0 Then
Debug.Print "=========================================================================="
Debug.Print " " & intAutoUpdateCount;
If intAutoUpdateCount = 1 Then
Debug.Print " update is ";
Else
Debug.Print " updates are ";
End If
Debug.Print "available for automatic installation. If you would like" & vbNewLine _
& " to apply these updates now, please run the following command:"
Debug.Print "=========================================================================="
Debug.Print "UpdateDependencies"
Debug.Print
End If
Set ref = Nothing
Set objComponent = Nothing
CheckForUpdates = blnUpdatesAvailable
End Function
'---------------------------------------------------------------------------------------
' Procedure : LoadVersionList
' Author : Adam Waller
' Date : 1/27/2017
' Purpose : Loads a list of the current versions.
'---------------------------------------------------------------------------------------
'
Private Sub LoadVersionList()
Dim strFile As String
Dim intFile As Integer
Dim strLine As String
strFile = GetDeploymentFolder & "Latest Versions.csv"
intFile = FreeFile
' Initialize collection
Set mVersions = New Collection
' Start with header if file does not exist.