-
Notifications
You must be signed in to change notification settings - Fork 267
Expand file tree
/
Copy pathRun-AlPipeline.ps1
More file actions
3279 lines (3066 loc) · 156 KB
/
Copy pathRun-AlPipeline.ps1
File metadata and controls
3279 lines (3066 loc) · 156 KB
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
<#
.Synopsis
Run AL Pipeline
.Description
Run AL Pipeline
.Parameter pipelineName
The name of the pipeline or project.
.Parameter baseFolder
The baseFolder serves as the base Folder for all other parameters including a path (appFolders, testFolders, testResultFile, outputFolder, packagesFolder and buildArtifactFolder). This folder will be shared with the container as c:\sources
.Parameter sharedFolder
If a folder on the host computer is specified in the sharedFolder parameter, it will be shared with the container as c:\shared
.Parameter licenseFile
License file to use for AL Pipeline.
.Parameter accept_insiderEula
Switch, which you need to specify if you are going to create a container with an insider build of Business Central on Docker containers (See https://go.microsoft.com/fwlink/?linkid=2245051)
.Parameter containerName
This is the containerName going to be used for the build/test container. If not specified, the container name will be the pipeline name followed by -bld.
.Parameter generateErrorLog
Switch parameter on whether to generate an alerts log file. Default is false.
If set to true, the `errorLog` argument is used when compiling the apps. The generated file will be named <appname>.errorLog.json and is placed in the same folder as the app file.
.Parameter imageName
If imageName is specified it will be used to build an image, which serves as a cache for faster container generation.
Only specify imageName if you are going to create multiple containers from the same artifacts.
If installTestRunner, installTestFramework, installTestLibraries, or installPerformanceToolkit are also specified,
the test toolkit will be baked into the image so it does not have to be imported on every container creation.
.Parameter enableTaskScheduler
Include this switch if the Task Scheduler should be running inside the build/test container, as some app features rely on the Task Scheduler.
.Parameter assignPremiumPlan
Include this switch if the primary user in Business Central should have assign premium plan, as some app features require premium plan.
.Parameter tenant
If you specify a tenant name, a tenant with this name will be created and used for the entire process.
.Parameter memoryLimit
MemoryLimit is default set to 8Gb. This is fine for compiling small and medium size apps, but if your have a number of apps or your apps are large and complex, you might need to assign more memory.
.Parameter auth
Set auth to Windows, NavUserPassword or AAD depending on which authentication mechanism your container should use
.Parameter credential
These are the credentials used for the container. If not provided, the Run-AlPipeline function will generate a random password and use that.
.Parameter companyName
company to use for test execution (blank for default)
.Parameter codeSignCertPfxFile
A secure url to a code signing certificate for signing apps. Apps will only be signed if useDevEndpoint is NOT specified.
.Parameter codeSignCertPfxPassword
Password for the code signing certificate specified by codeSignCertPfxFile. Apps will only be signed if useDevEndpoint is NOT specified.
.Parameter keyVaultCertPfxFile
A secure url to a certificate for keyVault accessing from the container. This will be used in a call to Set-BcContainerKeyVaultAadAppAndCertificate after the container is created.
.Parameter keyVaultCertPfxPassword
Password for the keyVault certificate specified by keyVaultCertPfxFile. This will be used in a call to Set-BcContainerKeyVaultAadAppAndCertificate after the container is created.
.Parameter keyVaultClientId
ClientId for the keyVault certificate specified by keyVaultCertPfxFile. This will be used in a call to Set-BcContainerKeyVaultAadAppAndCertificate after the container is created.
.Parameter installApps
Array or comma separated list of 3rd party apps to install before compiling apps.
.Parameter installTestApps
Array or comma separated list of 3rd party test apps to install before compiling test apps.
.Parameter installOnlyReferencedApps
Switch indicating whether you want to only install referenced apps in InstallApps and InstallTestApps
.Parameter generateDependencyArtifact
Switch indicating whether you want to generate a folder with all installed dependency apps used during build
.Parameter previousApps
Array or comma separated list of previous version of apps
.Parameter appFolders
Array or comma separated list of folders with apps to be compiled, signed and published
.Parameter testFolders
Array or comma separated list of folders with test apps to be compiled, published and run
.Parameter bcptTestFolders
Array or comma separated list of folders with bcpt test apps to be compiled, published and run
.Parameter pageScriptingTests
Array or comma separated list of filespecs with pageScripting tests, to be run after the apps have been compiled and tested
.Parameter additionalCountries
Array or comma separated list of countries to test
.Parameter restoreDatabases
Array or comma separated list of events, indicating when you want to start with clean databases in the container. Possible events are: BeforeBcpTests, BeforePageScriptingTests, BeforeEachTestApp, BeforeEachBcptTestApp, BeforeEachPageScriptingTest
.Parameter appVersion
Major and Minor version for build (ex. "18.0"). Will be stamped into the build part of the app.json version number property.
.Parameter appBuild
Build number for build. Will be stamped into the build part of the app.json version number property.
.Parameter appRevision
Revision number for build. Will be stamped into the revision part of the app.json version number property.
.Parameter applicationInsightsKey
ApplicationInsightsKey to be stamped into app.json for all apps
.Parameter applicationInsightsConnectionString
ApplicationInsightsConnectionString to be stamped into app.json for all apps
.Parameter buildOutputFile
Filename in which you want the build output to be written. Default is none, meaning that build output will not be written to a file, but only on screen.
.Parameter containerEventLogFile
Filename in which you want the build output to be written. Default is none, meaning that build output will not be written to a file, but only on screen.
.Parameter testResultsFile
Filename in which you want the test results to be written. Default is TestResults.xml, meaning that test results will be written to this filename in the base folder. This parameter is ignored if doNotRunTests is included.
.Parameter bcptTestResultsFile
Filename in which you want the bcpt test results to be written. Default is TestResults.xml, meaning that test results will be written to this filename in the base folder. This parameter is ignored if doNotRunBcptTests is included.
.Parameter pageScriptingTestResultsFile
File in which you want the page scripting test results to be written in JUnit format. Default is PageScriptingTestResults.xml.
.Parameter pageScriptingTestResultsFolder
Folder in which you want the page scripting test results to be written. Default is PageScriptingTestResults, meaning that test result details will be written to folders underneath this folder, relative to the base folder. This parameter is ignored if doNotRunPageScriptingTests is included.
.Parameter testResultsFormat
Format of test results file. Possible values are XUnit or JUnit. Both formats are XML based test result formats.
.Parameter packagesFolder
This is the folder (relative to base folder) where symbols are downloaded and compiled apps are placed. Only relevant when not using useDevEndpoint
.Parameter outputFolder
This is the folder (relative to base folder) where compiled apps are placed. Only relevant when not using useDevEndpoint.
.Parameter artifact
The description of which artifact to use. This can either be a URL (from Get-BcArtifactUrl) or in the format storageAccount/type/version/country/select, where these values are transferred as parameters to Get-BcArtifactUrl. Default value is ///us/current.
.Parameter useGenericImage
Specify a private (or special) generic image to use for the Container OS. Default is calling Get-BestGenericImageName.
.Parameter buildArtifactFolder
If this folder is specified, the build artifacts will be copied to this folder.
.Parameter createRuntimePackages
Include this switch if you want to create runtime packages of all apps. The runtime packages will also be signed (if certificate is provided) and copied to artifacts folder.
.Parameter installTestRunner
Include this switch to include the test runner in the container before compiling apps and test apps. The Test Runner includes the following apps: Microsoft Test Runner.
.Parameter installTestFramework
Include this switch to include the test framework in the container before compiling apps and test apps. The Test Framework includes the following apps: Microsoft Any, Microsoft Library Assert, Microsoft Library Variable Storage and Microsoft Test Runner.
.Parameter installTestLibraries
Include this switch to include the test libraries in the container before compiling apps and test apps. The Test Libraries includes all the Test Framework apps and the following apps: Microsoft System Application Test Library and Microsoft Tests-TestLibraries
.Parameter installPerformanceToolkit
Include this switch to install test Performance Test Toolkit. This includes the apps from the Test Framework and the Microsoft Business Central Performance Toolkit app
.Parameter azureDevOps
Include this switch if you want compile errors and test errors to surface directly in Azure Devops pipeline.
.Parameter gitLab
Include this switch if you want compile errors and test errors to surface directly in GitLab.
.Parameter gitHubActions
Include this switch if you want compile errors and test errors to surface directly in GitHubActions.
.Parameter Failon
Specify if you want Compilation to fail on Error or Warning
.Parameter TreatTestFailuresAsWarnings
Include this switch if you want to treat test failures as warnings instead of errors
.Parameter useDevEndpoint
Including the useDevEndpoint switch will cause the pipeline to publish apps through the development endpoint (like VS Code). This should ONLY be used when running the pipeline locally and will cause some changes in how things are done.
.Parameter doNotBuildTests
Include this switch to indicate that you do not want to build nor tests.
.Parameter doNotRunTests
Include this switch to indicate that you do not want to execute tests. Test Apps will still be published and installed, test execution can later be performed from the UI.
.Parameter doNotRunBcptTests
Include this switch to indicate that you do not want to execute bcpt tests. Test Apps will still be published and installed, test execution can later be performed from the UI.
.Parameter doNotRunPageScriptingTests
Include this switch to indicate that you do not want to execute page scripting tests.
.Parameter doNotPerformUpgrade
Include this switch to indicate that you do not want to perform the upgrade. This means that the previousApps are never actually published to the container.
.Parameter doNotPublishApps
Include this switch to indicate that you do not want to publish the app. Including this switch will also mean that upgrade won't happen and tests won't run.
.Parameter uninstallRemovedApps
Include this switch to indicate that you want to uninstall apps, which are included in previousApps, but not included (upgraded) in apps, i.e. removed apps
.Parameter useCompilerFolder
Include this switch to indicate that you want to use the compiler folder instead of creating a docker container for app compilation.
.Parameter reUseContainer
Including the reUseContainer switch causes pipeline to reuse the container with the given name if it exists
.Parameter keepContainer
Including the keepContainer switch causes the container to not be deleted after the pipeline finishes.
.Parameter updateLaunchJson
Specifies the name of the configuration in launch.json, which should be updated with container information to be able to start debugging right away.
.Parameter artifactCachePath
Artifacts Cache folder (if needed)
.Parameter vsixFile
Specify a URL or path to a .vsix file in order to override the .vsix file in the image with this.
Use Get-LatestAlLanguageExtensionUrl to get latest AL Language extension from Marketplace.
Use Get-AlLanguageExtensionFromArtifacts -artifactUrl (Get-BCArtifactUrl -select NextMajor -accept_insiderEula) to get latest insider .vsix
.Parameter enableCodeCop
Include this switch to include Code Cop Rules during compilation.
.Parameter enableAppSourceCop
Only relevant for AppSource apps. Include this switch to include AppSource Cop during compilation.
.Parameter enableUICop
Include this switch to include UI Cop during compilation.
.Parameter enablePerTenantExtensionCop
Only relevant for Per Tenant Extensions. Include this switch to include Per Tenant Extension Cop during compilation.
. Parameter enableCodeAnalyzersOnTestApps
Include this switch to include CodeCops and other analyzers during compilation of test apps.
.Parameter customCodeCops
Use custom AL Cops into the container and include them, in addition to the default cops, during compilation.
.Parameter useDefaultAppSourceRuleSet
Apply the default ruleset for passing AppSource validation
.Parameter rulesetFile
Filename of the custom ruleset file
.Parameter enableExternalRulesets
Include this switch to enable external rulesets when compiling
.Parameter preProcessorSymbols
PreProcessorSymbols to set when compiling the app.
.Parameter generatecrossreferences
Include this flag to generate cross references when compiling
.Parameter bcAuthContext
Authorization Context created by New-BcAuthContext. By specifying BcAuthContext and environment, the pipeline will run using the online Business Central Environment as target
.Parameter environment
Environment to use for the pipeline
.Parameter escapeFromCops
If One of the cops causes an error in an app, then show the error, recompile the app without cops and continue
.Parameter reportSuppressedDiagnostics
Report diagnostics that are suppressed by #pragma warning disable directives when compiling.
.Parameter AppSourceCopMandatoryAffixes
Only relevant for AppSource Apps when AppSourceCop is enabled. This needs to be an array (or a string with comma separated list) of affixes used in the app.
.Parameter AppSourceCopSupportedCountries
Only relevant for AppSource Apps when AppSourceCop is enabled. This needs to be an array (or a string with a comma separated list) of supported countries for this app.
.Parameter obsoleteTagMinAllowedMajorMinor
Only relevant for AppSource Apps. Objects that are pending obsoletion with an obsolete tag version lower than the minimum set in the AppSourceCop.json file are not allowed. (AS0105)
.Parameter features
Features to set when compiling the app.
.Parameter SourceRepositoryUrl
Repository holding the source code for the app. Will be stamped into the app manifest.
.Parameter SourceCommit
The commit identifier for the source code for the app. Will be stamped into the app manifest.
.Parameter BuildBy
Information about which product built the app. Will be stamped into the app manifest.
.Parameter BuildUrl
The URL for the build job, which built the app. Will be stamped into the app manifest.
.Parameter PipelineInitialize
Override for Pipeline Initialize
.Parameter PipelineFinalize
Override for Pipeline Finalize
.Parameter DockerPull
Override function parameter for docker pull
.Parameter NewBcContainer
Override function parameter for New-BcContainer
.Parameter NewBcCompilerFolder
Override function parameter for New-BcCompilerFolder
.Parameter SetBcContainerKeyVaultAadAppAndCertificate
Override function parameter for Set-BcContainerKeyVaultAadAppAndCertificate
.Parameter ImportTestToolkitToBcContainer
Override function parameter for Import-TestToolkitToBcContainer
.Parameter CompileAppInBcContainer
Override function parameter for Compile-AppInBcContainer
.Parameter CompileAppWithBcCompilerFolder
Override function parameter for Compile-AppWithBcCompilerFolder
.Parameter PreCompileApp
Custom script to run before compiling an app.
The script should accept the type of the app and a reference to the compilation parameters.
Possible values for $appType are: app, testApp, bcptApp
Example:
{
param(
[string] $appType,
[ref] $compilationParams
)
...
# Change the output folder based on the app type
switch($appType) {
"app" {
$compilationParams.Value.appOutputFolder = "MyApps"
}
"testApp" {
$compilationParams.Value.appOutputFolder = "MyTestApps"
}
"bcptApp" {
$compilationParams.Value.appOutputFolder = "MyBcptApps"
}
}
...
}
.Parameter PostCompileApp
Custom script to run after compiling an app.
The script should accept the file path of the produced .app file, the type of the app, and a hashtable of the compilation parameters.
Possible values for $appType are: app, testApp, bcptApp
Example:
{
param(
[string] $appFilePath,
[string] $appType,
[hashtable] $compilationParams
)
...
}
.Parameter GetBcContainerAppInfo
Override function parameter for Get-BcContainerAppInfo
.Parameter PublishBcContainerApp
Override function parameter for Publish-BcContainerApp
.Parameter UnPublishBcContainerApp
Override function parameter for UnPublish-BcContainerApp
.Parameter InstallBcAppFromAppSource
Override function parameter for Install-BcAppFromAppSource
.Parameter SignBcContainerApp
Override function parameter for Sign-BcContainerApp
.Parameter BackupBcContainerDatabases
Override function parameter for Backup-BcContainerDatabases
.Parameter RestoreDatabasesInBcContainer
Override function parameter for Restore-DatabasesInBcContainer
.Parameter RunTestsInBcContainer
Override function parameter for Run-TestsInBcContainer
.Parameter RunBCPTTestsInBcContainer
Override function parameter for Run-BCPTTestsInBcContainer
.Parameter GetBcContainerAppRuntimePackage
Override function parameter Get-BcContainerAppRuntimePackage
.Parameter RemoveBcContainer
Override function parameter for Remove-BcContainer
.Parameter RemoveBcCompilerFolder
Override function parameter for Remove-BcCompilerFolder
.Parameter GetBestGenericImageName
Override function parameter for Get-BestGenericImageName
.Parameter GetBcContainerEventLog
Override function parameter for Get-BcContainerEventLog
.Parameter InstallMissingDependencies
Override function parameter for Installing missing dependencies
.Parameter RunPageScriptingTests
Override function parameter for Running Page Scripting Tests
.Example
Please visit https://www.freddysblog.com for descriptions
.Example
Please visit https://github.com/microsoft/bcsamples-bingmaps.pte for Per Tenant Extension example
.Example
Please visit https://github.com/microsoft/bcsamples-bingmaps.appsource for AppSource example
#>
function Run-AlPipeline {
Param(
[string] $pipelineName,
[string] $baseFolder = "",
[string] $sharedFolder = "",
[string] $licenseFile,
[switch] $accept_insiderEula,
[string] $containerName = "$($pipelineName.Replace('.','-') -replace '[^a-zA-Z0-9---]', '')-bld".ToLowerInvariant(),
[string] $imageName = 'my',
[switch] $enableTaskScheduler,
[switch] $assignPremiumPlan,
[string] $tenant = "default",
[string] $memoryLimit,
[string] $auth = 'UserPassword',
[PSCredential] $credential,
[string] $companyName = "",
[string] $codeSignCertPfxFile = "",
[SecureString] $codeSignCertPfxPassword = $null,
[switch] $codeSignCertIsSelfSigned,
[string] $keyVaultCertPfxFile = "",
[SecureString] $keyVaultCertPfxPassword = $null,
[string] $keyVaultClientId = "",
$installApps = @(),
$installTestApps = @(),
[switch] $installOnlyReferencedApps,
[switch] $generateDependencyArtifact,
$previousApps = @(),
$appFolders = @("app", "application"),
$testFolders = @("test", "testapp"),
$bcptTestFolders = @("bcpttest", "bcpttestapp"),
$bcptTestSuites = @(),
$pageScriptingTests = @(),
$additionalCountries = @(),
[ValidateSet('BeforeBcpTests', 'BeforePageScriptingTests', 'BeforeEachTestApp', 'BeforeEachBcptTestApp', 'BeforeEachPageScriptingTest')]
[string[]] $restoreDatabases = @(),
[string] $appVersion = "",
[int] $appBuild = 0,
[int] $appRevision = 0,
[string] $applicationInsightsKey,
[string] $applicationInsightsConnectionString,
[string] $buildOutputFile = "",
[string] $containerEventLogFile = "",
[string] $testResultsFile = "TestResults.xml",
[string] $bcptTestResultsFile = "bcptTestResults.json",
[string] $pageScriptingTestResultsFile = "PageScriptingTestResults.xml",
[string] $pageScriptingTestResultsFolder = "PageScriptingTestResults",
[Parameter(Mandatory=$false)]
[ValidateSet('XUnit','JUnit')]
[string] $testResultsFormat = "JUnit",
[string] $packagesFolder = ".packages",
[string] $outputFolder = ".output",
[string] $artifact = "///us/Current",
[string] $useGenericImage = "",
[string] $buildArtifactFolder = "",
[switch] $createRuntimePackages,
[switch] $installTestRunner,
[switch] $installTestFramework,
[switch] $installTestLibraries,
[switch] $installPerformanceToolkit,
[switch] $CopySymbolsFromContainer,
[switch] $UpdateDependencies,
[switch] $azureDevOps = $bcContainerHelperConfig.IsAzureDevOps,
[switch] $gitLab = $bcContainerHelperConfig.IsGitLab,
[switch] $gitHubActions = $bcContainerHelperConfig.IsGitHubActions,
[ValidateSet('none','error','warning','newWarning')]
[string] $failOn = "none",
[switch] $treatTestFailuresAsWarnings,
[switch] $useDevEndpoint,
[switch] $doNotBuildTests,
[switch] $doNotRunTests,
[switch] $doNotRunBcptTests,
[switch] $doNotRunPageScriptingTests,
[switch] $doNotPerformUpgrade,
[switch] $doNotPublishApps,
[switch] $uninstallRemovedApps,
[switch] $useCompilerFolder = $bcContainerHelperConfig.useCompilerFolder,
[switch] $reUseContainer,
[switch] $keepContainer,
[string] $updateLaunchJson = "",
[string] $artifactCachePath = "",
[string] $vsixFile = "",
[switch] $enableCodeCop,
[switch] $enableAppSourceCop,
[switch] $enableUICop,
[switch] $enablePerTenantExtensionCop,
[switch] $enableCodeAnalyzersOnTestApps,
$customCodeCops = @(),
[switch] $useDefaultAppSourceRuleSet,
[string] $rulesetFile = "",
[switch] $generateErrorLog,
[switch] $enableExternalRulesets,
[string[]] $preProcessorSymbols = @(),
[switch] $generatecrossreferences,
[switch] $escapeFromCops,
[switch] $reportSuppressedDiagnostics,
[Hashtable] $bcAuthContext,
[string] $environment,
$AppSourceCopMandatoryAffixes = @(),
$AppSourceCopSupportedCountries = @(),
[string] $obsoleteTagMinAllowedMajorMinor = "",
[string[]] $features = @(),
[string] $sourceRepositoryUrl = '',
[string] $sourceCommit = '',
[string] $buildBy = "BcContainerHelper,$BcContainerHelperVersion",
[string] $buildUrl = '',
[scriptblock] $PipelineInitialize,
[scriptblock] $DockerPull,
[scriptblock] $NewBcContainer,
[scriptblock] $NewBcCompilerFolder,
[scriptblock] $SetBcContainerKeyVaultAadAppAndCertificate,
[scriptblock] $ImportTestToolkitToBcContainer,
[scriptblock] $CompileAppInBcContainer,
[scriptblock] $CompileAppWithBcCompilerFolder,
[scriptblock] $PreCompileApp,
[scriptblock] $PostCompileApp,
[scriptblock] $GetBcContainerAppInfo,
[scriptblock] $PublishBcContainerApp,
[scriptblock] $UnPublishBcContainerApp,
[scriptblock] $InstallBcAppFromAppSource,
[scriptblock] $SignBcContainerApp,
[scriptblock] $ImportTestDataInBcContainer,
[scriptblock] $BackupBcContainerDatabases,
[scriptblock] $RestoreDatabasesInBcContainer,
[scriptblock] $RunTestsInBcContainer,
[scriptblock] $RunBCPTTestsInBcContainer,
[scriptblock] $GetBcContainerAppRuntimePackage,
[scriptblock] $RemoveBcContainer,
[scriptblock] $RemoveBcCompilerFolder,
[scriptblock] $GetBestGenericImageName,
[scriptblock] $GetBcContainerEventLog,
[scriptblock] $InstallMissingDependencies,
[scriptblock] $RunPageScriptingTests,
[scriptblock] $PipelineFinalize
)
function CheckRelativePath([string] $baseFolder, [string] $sharedFolder, $path, $name) {
if ($path -and $path -notlike 'https://*') {
if (-not [System.IO.Path]::IsPathRooted($path)) {
if (Test-Path -Path (Join-Path $baseFolder $path)) {
$path = Join-Path $baseFolder $path -Resolve
}
else {
$path = Join-Path $baseFolder $path
}
}
else {
if (!(($path -like "$($baseFolder)*") -or (($sharedFolder) -and ($path -like "$($sharedFolder)*")))) {
if ($sharedFolder) {
throw "$name is ($path) must be a subfolder to baseFolder ($baseFolder) or sharedFolder ($sharedFolder)"
}
else {
throw "$name is ($path) must be a subfolder to baseFolder ($baseFolder)"
}
}
}
}
$path
}
function UpdateLaunchJson {
Param(
[string] $launchJsonFile,
[System.Collections.Specialized.OrderedDictionary] $launchSettings
)
if (Test-Path $launchJsonFile) {
Write-Host "Modifying $launchJsonFile"
$launchJson = [System.IO.File]::ReadAllLines($LaunchJsonFile) | ConvertFrom-Json
}
else {
Write-Host "Creating $launchJsonFile"
$dir = [System.IO.Path]::GetDirectoryName($launchJsonFile)
if (!(Test-Path $dir)) {
New-Item -Path $dir -ItemType Directory | Out-Null
}
$launchJson = @{ "version" = "0.2.0"; "configurations" = @() } | ConvertTo-Json | ConvertFrom-Json
}
$launchSettings | ConvertTo-Json | Out-Host
$oldSettings = $launchJson.configurations | Where-Object { $_.name -eq $launchsettings.name }
if ($oldSettings) {
$oldSettings.PSObject.Properties | ForEach-Object {
$prop = $_.Name
if (!($launchSettings.Keys | Where-Object { $_ -eq $prop } )) {
$launchSettings += @{ "$prop" = $oldSettings."$prop" }
}
}
}
$launchJson.configurations = @($launchJson.configurations | Where-Object { $_.name -ne $launchsettings.name })
$launchJson.configurations += $launchSettings
$launchJson | ConvertTo-Json -Depth 10 | Set-Content $launchJsonFile
}
function GetInstalledApps {
Param(
[hashtable] $bcAuthContext,
[string] $environment,
[bool] $useCompilerFolder,
[string] $packagesFolder,
[bool] $filesOnly
)
if ($bcAuthContext -and $environment -and !($environment -like 'https://*' -or $environment -like 'http://*')) {
# PublishedAs is either "Global", " PTE" or " Dev" (with leading space)
$installedExtensions = Get-BcInstalledExtensions -bcAuthContext $bcAuthContext -environment $environment
$installedApps = $installedExtensions | Where-Object { $_.IsInstalled } | ForEach-Object {
@{ "AppId" = $_.id; "Publisher" = $_.publisher; "Name" = $_.displayName; "Version" = [System.Version]::new($_.VersionMajor,$_.VersionMinor,$_.VersionBuild,$_.VersionRevision) }
}
$message = "Apps in environment $environment"
}
elseif ($useCompilerFolder) {
$compilerFolder = (GetCompilerFolder)
$existingAppFiles = @(Get-ChildItem -Path (Join-Path $packagesFolder '*.app') | Select-Object -ExpandProperty FullName)
$installedApps = @(GetAppInfo -AppFiles $existingAppFiles -compilerFolder $compilerFolder -cacheAppinfoPath (Join-Path $packagesFolder 'cache_AppInfo.json'))
$compilerFolderAppFiles = @(Get-ChildItem -Path (Join-Path $compilerFolder 'symbols/*.app') | Select-Object -ExpandProperty FullName)
$installedApps += @(GetAppInfo -AppFiles $compilerFolderAppFiles -compilerFolder $compilerFolder -cacheAppinfoPath (Join-Path $compilerFolder 'symbols/cache_AppInfo.json'))
$message = "Apps in compiler folder"
}
elseif ($filesOnly) {
# Make sure container has been created
GetBuildContainer | Out-Null
$installedApps = Get-ChildItem -Path (Join-Path $packagesFolder '*.app') | ForEach-Object {
$appJson = Get-AppJsonFromAppFile -appFile $_.FullName
return @{
"AppId" = $appJson.id
"Name" = $appJson.name
"Publisher" = $appJson.publisher
"Version" = $appJson.version
}
}
$message = "Apps in packages folder"
}
else {
$Parameters = @{
"containerName" = (GetBuildContainer)
"tenant" = $tenant
"tenantSpecificProperties" = $true
}
$installedApps = @(Invoke-Command -ScriptBlock $GetBcContainerAppInfo -ArgumentList $Parameters | Where-Object { $_.IsInstalled })
$message = "Installed apps"
}
Write-GroupStart -Message $message
$seen = @{}
$installedApps | ForEach-Object {
if (-not $seen.ContainsKey($_.AppId)) {
$seen[$_.AppId] = $true
Write-Host "- $($_.AppId):$($_.Name)"
return @{ "Id" = "$($_.AppId)"; "Name" = "$($_.Name)"; "Publisher" = "$($_.Publisher)"; "Version" = "$($_.Version)" }
}
}
Write-GroupEnd
}
function RunPageScriptingTests {
param(
[string] $containerName,
[PSCredential] $credential,
[array] $pageScriptingTests,
[array] $restoreDatabases,
[string] $pageScriptingTestResultsFile,
[string] $pageScriptingTestResultsFolder,
[string] $startAddress,
[scriptblock] $RestoreDatabasesInBcContainer,
[switch] $returnTrueIfAllPassed
)
# Install npm package for page scripting tests
pwsh -command { npm i @microsoft/bc-replay@0.1.119 --save --silent }
${env:containerUsername} = $credential.UserName
${env:containerPassword} = $credential.Password | Get-PlainText
$allPassed = $true
$usedNames = @()
$pageScriptingTests | ForEach-Object {
$thisFailed = $false
if ($restoreDatabases -contains 'BeforeEachPageScriptingTest') {
Write-GroupStart -Message "Restoring databases before each page scripting test"
Invoke-Command -ScriptBlock $RestoreDatabasesInBcContainer -ArgumentList @{"containerName" = $containerName }
Write-GroupEnd
}
$testSpec = $_
$name = $testSpec -replace '[\\/]', '-' -replace ':', '' -replace '\*', 'all' -replace '\?', 'x' -replace '\.yml$', ''
if ($usedNames -contains $name) {
throw "PageScriptingTests contains two similar test specs (resulting in identical results folders), please rename your test specs ($testSpec)."
}
$usedNames += $name
$path = $testSpec
if (-not [System.IO.Path]::IsPathRooted($path)) { $path = Join-Path $baseFolder $path }
if (-not (Test-Path $path)) { throw "No page scripting tests found matching $testSpec" }
Write-Host "Running Page Scripting Tests for $testSpec (test name: $name)"
$resultsFolder = Join-Path $pageScriptingTestResultsFolder $name
New-Item -Path $resultsFolder -ItemType Directory | Out-Null
try {
pwsh -command {
param(
[string]$TestPath,
[string]$ResultsFolder,
[string]$StartAddress
)
Write-Host "Running: npx replay $TestPath -ResultDir $ResultsFolder -StartAddress $StartAddress -Authentication 'UserPassword' -usernameKey 'containerUsername' -passwordkey 'containerPassword'"
npx replay $TestPath -ResultDir $ResultsFolder -StartAddress $StartAddress -Authentication 'UserPassword' -usernameKey 'containerUsername' -passwordkey 'containerPassword'
} -args $path, $resultsFolder, $startAddress
if ($? -ne "True") {
Write-Host "Page Scripting Tests failed for $testSpec"
$thisFailed = $true
}
}
catch {
$thisFailed = $true
Write-Host -ForegroundColor Red "Page Scripting Tests failed for $testSpec : $($_.Exception.Message)"
}
if ($thisFailed) {
Write-Host "Page Scripting Tests failed for $testSpec"
$allPassed = $false
}
$testResultsFile = Join-Path $resultsFolder "results.xml"
$playwrightReportFolder = Join-Path $resultsFolder 'playwright-report'
if ((Test-Path $testResultsFile -PathType Leaf) -and (Test-Path $playwrightReportFolder -PathType Container)) {
$thisXml = [xml](Get-Content $testResultsFile -Encoding UTF8)
$thisXml.testsuites.testsuite.Name = $name
$resultsXml = $thisXml
if (Test-Path $pageScriptingTestResultsFile) {
# Merge results and aggregate counts
$resultsXml = [xml](Get-Content $pageScriptingTestResultsFile -Encoding UTF8)
$resultsXml.testsuites.AppendChild($resultsXml.ImportNode($thisXml.testsuites.testsuite, $true))
}
foreach ($property in 'tests', 'failures', 'skipped', 'errors', 'time') {
$resultsXml.testsuites."$property" = "$(([double[]]$resultsXml.testsuites.testsuite."$property" | Measure-Object -Sum).Sum)"
}
$resultsXml.Save($pageScriptingTestResultsFile)
Remove-Item $testResultsFile -Force
if ($thisFailed) {
Write-Host "Moving Playwright report folder"
Move-Item -Path "$playwrightReportFolder/*" -Destination $resultsFolder -Force
Write-Host "Removing Playwright report folder"
Remove-Item -Path $playwrightReportFolder -Force
}
else {
if (Test-Path $resultsFolder) {
Write-Host "Removing results folder"
Remove-Item -Path $resultsFolder -Recurse -Force -ErrorAction SilentlyContinue
}
else {
Write-Host "Results folder $resultsFolder not found"
}
}
}
}
if ($returnTrueIfAllPassed) {
return $allPassed
}
}
$script:existingContainerName = ''
$script:existingCompilerFolder = ''
function PullGenericImage {
Measure-Command {
Write-Host -ForegroundColor Yellow @'
_____ _ _ _ _ _
| __ \ | | (_) (_) (_)
| |__) | _| | |_ _ __ __ _ __ _ ___ _ __ ___ _ __ _ ___ _ _ __ ___ __ _ __ _ ___
| ___/ | | | | | | '_ \ / _` | / _` |/ _ \ '_ \ / _ \ '__| |/ __| | | '_ ` _ \ / _` |/ _` |/ _ \
| | | |_| | | | | | | | (_| | | (_| | __/ | | | __/ | | | (__ | | | | | | | (_| | (_| | __/
|_| \__,_|_|_|_|_| |_|\__, | \__, |\___|_| |_|\___|_| |_|\___| |_|_| |_| |_|\__,_|\__, |\___|
__/ | __/ | __/ |
|___/ |___/ |___/
'@
Write-PSCallStack
if (!$useGenericImage) {
$Parameters = @{
"filesOnly" = $filesOnly
}
$useGenericImage = Invoke-Command -ScriptBlock $GetBestGenericImageName -ArgumentList $Parameters
}
Write-Host "Pulling $useGenericImage"
Invoke-Command -ScriptBlock $DockerPull -ArgumentList $useGenericImage
} | ForEach-Object { Write-Host -ForegroundColor Yellow "`nPulling generic image took $([int]$_.TotalSeconds) seconds" }
}
# Create build container and return containerName
function GetBuildContainer {
if (!$createContainer -or $script:existingContainerName) {
# Either we are not using a container (return blank)
# Or we have a container (return existing)
# Or we should not create a new (return blank)
return $script:existingContainerName
}
PullGenericImage
Measure-Command {
Write-Host -ForegroundColor Yellow @'
_____ _ _ _____ _ _
/ ____| | | (_) / ____| | | (_)
| | _ __ ___ __ _| |_ _ _ __ __ _ | | ___ _ __ | |_ __ _ _ _ __ ___ _ __
| | | '__/ _ \/ _` | __| | '_ \ / _` | | | / _ \| '_ \| __/ _` | | '_ \ / _ \ '__|
| |____| | | __/ (_| | |_| | | | | (_| | | |___| (_) | | | | || (_| | | | | | __/ |
\_____|_| \___|\__,_|\__|_|_| |_|\__, | \_____\___/|_| |_|\__\__,_|_|_| |_|\___|_|
__/ |
|___/
'@
Write-PSCallStack
$Parameters = @{}
$useExistingContainer = $false
if ($createContainer -and ($filesOnly -or !$doNotPublishApps)) {
# If we are going to build using a filesOnly container or we are going to publish apps, we need a container
if (Test-BcContainer -containerName $containerName) {
if ($bcAuthContext) {
if ($artifactUrl -eq (Get-BcContainerArtifactUrl -containerName $containerName)) {
$useExistingContainer = ((Get-BcContainerPath -containerName $containerName -path $baseFolder) -ne "")
}
}
elseif ($reUseContainer) {
$containerArtifactUrl = Get-BcContainerArtifactUrl -containerName $containerName
if ($artifactUrl -ne $containerArtifactUrl) {
Write-Host "WARNING: Reusing a container based on $($containerArtifactUrl.Split('?')[0]), should be $($ArtifactUrl.Split('?')[0])"
}
if ((Get-BcContainerPath -containerName $containerName -path $baseFolder) -eq "") {
throw "$baseFolder is not shared with container $containerName"
}
$useExistingContainer = $true
}
}
}
if ($useExistingContainer) {
Write-Host "Reusing existing docker container"
}
else {
Write-Host "Creating docker container"
$Parameters += @{
"FilesOnly" = $filesOnly
}
if ($imageName) {
$Parameters += @{ "imageName" = $imageName }
# When an imageName is specified and test toolkit installation is requested, include
# the test toolkit in the image build so it does not have to be imported on every
# container creation. This matches the logic in Import-TestToolkitToBcContainer.
if ($installTestRunner -or $installTestFramework -or $installTestLibraries -or $installPerformanceToolkit) {
$Parameters += @{
"includeTestToolkit" = $true
"includeTestLibrariesOnly" = [bool]$installTestLibraries
"includeTestFrameworkOnly" = !$installTestLibraries -and [bool]($installTestFramework -or $installPerformanceToolkit)
"includePerformanceToolkit" = [bool]$installPerformanceToolkit
}
}
}
if ($memoryLimit) { $Parameters += @{ "memoryLimit" = $memoryLimit } }
$Parameters += @{
"accept_eula" = $true
"accept_insiderEula" = $accept_insiderEula
"containerName" = $containerName
"artifactUrl" = $artifactUrl
"useGenericImage" = $useGenericImage
"Credential" = $credential
"auth" = $auth
"vsixFile" = $vsixFile
"updateHosts" = !$IsInsideContainer
"licenseFile" = $licenseFile
"EnableTaskScheduler" = $enableTaskScheduler
"AssignPremiumPlan" = $assignPremiumPlan
"additionalParameters" = @("--volume ""$($baseFolder):c:\sources""")
}
if ($sharedFolder) {
$Parameters.additionalParameters += @("--volume ""$($sharedFolder):c:\shared""")
}
Invoke-Command -ScriptBlock $NewBcContainer -ArgumentList $Parameters
if ($createContainer -and -not $bcAuthContext) {
if ($keyVaultCertPfxFile -and $KeyVaultClientId -and $keyVaultCertPfxPassword) {
$Parameters = @{
"containerName" = $containerName
"pfxFile" = $keyVaultCertPfxFile
"pfxPassword" = $keyVaultCertPfxPassword
"clientId" = $keyVaultClientId
}
Invoke-Command -ScriptBlock $SetBcContainerKeyVaultAadAppAndCertificate -ArgumentList $Parameters
}
}
}
if ($tenant -ne 'default' -and -not (Get-BcContainerTenants -containerName $containerName | Where-Object { $_.id -eq $tenant })) {
$Parameters = @{
"containerName" = $containerName
"tenantId" = $tenant
}
New-BcContainerTenant @Parameters
$Parameters = @{
"containerName" = $containerName
"tenant" = $tenant
"credential" = $credential
"permissionsetId" = "SUPER"
"ChangePasswordAtNextLogOn" = $false
"assignPremiumPlan" = $assignPremiumPlan
}
New-BcContainerBcUser @Parameters
$tenantApps = Get-BcContainerAppInfo -containerName $containerName -tenant $tenant -tenantSpecificProperties -sort DependenciesFirst
Get-BcContainerAppInfo -containerName $containerName -tenant "default" -tenantSpecificProperties -sort DependenciesFirst | Where-Object { $_.IsInstalled } | ForEach-Object {
$name = $_.Name
$version = $_.Version
$tenantApp = $tenantApps | Where-Object { $_.Name -eq $name -and $_.Version -eq $version }
if ($tenantApp.SyncState -eq "NotSynced" -or $tenantApp.SyncState -eq 3) {
Sync-BcContainerApp -containerName $containerName -tenant $tenant -appName $Name -appVersion $Version -Mode ForceSync -Force
}
if (-not $tenantApp.IsInstalled) {
Install-BcContainerApp -containerName $containerName -tenant $tenant -appName $_.Name -appVersion $_.Version
}
}
}
if ($CopySymbolsFromContainer) {
$containerSymbolsFolder = Get-BcContainerPath -containerName $containerName -path $packagesFolder
if ("$containerSymbolsFolder" -eq "") {
throw "The appSymbolsFolder ($appSymbolsFolder) is not shared with the container."
}
CopySymbolsFromContainer -containerName $containerName -containerSymbolsFolder $containerSymbolsFolder
$CopySymbolsFromContainer = $false
}
} | ForEach-Object { Write-Host -ForegroundColor Yellow "`nCreating Container took $([int]$_.TotalSeconds) seconds" }
$script:existingContainerName = $containerName
return $script:existingContainerName
}
function RemoveBuildContainer {
if ($script:existingContainerName) {
$Parameters = @{
"containerName" = $script:existingContainerName
}
Invoke-Command -ScriptBlock $RemoveBcContainer -ArgumentList $Parameters
$script:existingContainerName = ''
}
}
# Create compilerFolder and return path
function GetCompilerFolder {
if (!$useCompilerFolder -or $script:existingCompilerFolder) {
# Either we are not using CompilerFolder (return blank)
# Or we have a compilerfolder (return existing)
return $script:existingCompilerFolder
}
Measure-Command {
Write-Host -ForegroundColor Yellow @'
_____ _ _ _____ _ _ ______ _ _
/ ____| | | (_) / ____| (_) | | ____| | | | |
| | _ __ ___ __ _| |_ _ _ __ __ _ | | ___ _ __ ___ _ __ _| | ___ _ __| |__ ___ | | __| | ___ _ __
| | | '__/ _ \/ _` | __| | '_ \ / _` | | | / _ \| '_ ` _ \| '_ \| | |/ _ \ '__| __/ _ \| |/ _` |/ _ \ '__|
| |____| | | __/ (_| | |_| | | | | (_| | | |___| (_) | | | | | | |_) | | | __/ | | | | (_) | | (_| | __/ |
\_____|_| \___|\__,_|\__|_|_| |_|\__, | \_____\___/|_| |_| |_| .__/|_|_|\___|_| |_| \___/|_|\__,_|\___|_|
__/ | | |
|___/ |_|
'@
Write-PSCallStack
Write-Host "Creating CompilerFolder '$artifactUrl'"
$parameters = @{
"artifactUrl" = $artifactUrl
"cacheFolder" = $artifactCachePath
"vsixFile" = $vsixFile
"containerName" = $containerName
}
$compilerFolder = Invoke-Command -ScriptBlock $NewBcCompilerFolder -ArgumentList $parameters
Write-Host "CompilerFolder $compilerFolder created"
} | ForEach-Object { Write-Host -ForegroundColor Yellow "`nCreating CompilerFolder took $([int]$_.TotalSeconds) seconds" }
$script:existingCompilerFolder = $compilerFolder
return $script:existingCompilerFolder
}
function RemoveCompilerFolder {
if ($script:existingCompilerFolder) {
$parameters = @{
"compilerFolder" = $script:existingCompilerFolder
}
Invoke-Command -ScriptBlock $RemoveBcCompilerFolder -ArgumentList $parameters
$script:existingCompilerFolder = ''
}
}
$telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @()
try {
if ($PipelineInitialize) {
Invoke-Command -ScriptBlock $PipelineInitialize
}
$warningsToShow = @()
if (!$baseFolder -or !(Test-Path $baseFolder -PathType Container)) {
throw "baseFolder must be an existing folder"
}
if ($sharedFolder -and !(Test-Path $sharedFolder -PathType Container)) {
throw "If sharedFolder is specified, it must be an existing folder"
}
if($keepContainer -and !$credential) {
# If keepContainer is specified, credentials must also be specified, as otherwise the container will be created with a random password and there will be no way to access it.
throw "If keepContainer is specified, you must also specify credentials"
}
if(!$credential) {
# Create a random password to use, as the container will not be kept after the pipeline finishes.
$password = GetRandomPassword
$credential= (New-Object pscredential 'admin', (ConvertTo-SecureString -String $password -AsPlainText -Force))
}
if ($memoryLimit -eq "") {
$memoryLimit = "8G"
}
if ($installApps -is [String]) { $installApps = @($installApps.Split(',').Trim() | Where-Object { $_ }) }
if ($installTestApps -is [String]) { $installTestApps = @($installTestApps.Split(',').Trim() | Where-Object { $_ }) }
if ($previousApps -is [String]) { $previousApps = @($previousApps.Split(',').Trim() | Where-Object { $_ }) }
if ($appFolders -is [String]) { $appFolders = @($appFolders.Split(',').Trim() | Where-Object { $_ }) }
if ($testFolders -is [String]) { $testFolders = @($testFolders.Split(',').Trim() | Where-Object { $_ }) }
if ($bcptTestFolders -is [String]) { $bcptTestFolders = @($bcptTestFolders.Split(',').Trim() | Where-Object { $_ }) }
if ($pageScriptingTests -is [String]) { $pageScriptingTests = @($pageScriptingTests.Split(',').Trim() | Where-Object { $_ }) }
if ($additionalCountries -is [String]) { $additionalCountries = @($additionalCountries.Split(',').Trim() | Where-Object { $_ }) }
if ($AppSourceCopMandatoryAffixes -is [String]) { $AppSourceCopMandatoryAffixes = @($AppSourceCopMandatoryAffixes.Split(',').Trim() | Where-Object { $_ }) }
if ($AppSourceCopSupportedCountries -is [String]) { $AppSourceCopSupportedCountries = @($AppSourceCopSupportedCountries.Split(',').Trim() | Where-Object { $_ }) }
if ($customCodeCops -is [String]) { $customCodeCops = @($customCodeCops.Split(',').Trim() | Where-Object { $_ }) }
if ($restoreDatabases -is [string]) { $restoreDatabases = @($restoreDatabases.Split(',').Trim() | Where-Object { $_ }) }
$appFolders = @($appFolders | ForEach-Object { CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $_ -name "appFolders" } | Where-Object { Test-Path $_ } )
$testFolders = @($testFolders | ForEach-Object { CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $_ -name "testFolders" } | Where-Object { Test-Path $_ } )
$bcptTestFolders = @($bcptTestFolders | ForEach-Object { CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $_ -name "bcptTestFolders" } | Where-Object { Test-Path $_ } )
$pageScriptingTests = @($pageScriptingTests | ForEach-Object { CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $_ -name "pageScriptingTests" } | Where-Object { Test-Path $_ } | ForEach-Object { if (Test-Path -Path $_ -PathType Container) { return (Join-Path $_ '*.yml') } else { return $_ } } )
$customCodeCops = @($customCodeCops | ForEach-Object { CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $_ -name "customCodeCops" } | Where-Object { $_ -like 'https://*' -or (Test-Path $_) } )
$buildOutputFile = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $buildOutputFile -name "buildOutputFile"
$containerEventLogFile = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $containerEventLogFile -name "containerEventLogFile"
$testResultsFile = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $testResultsFile -name "testResultsFile"
$bcptTestResultsFile = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $bcptTestResultsFile -name "bcptTestResultsFile"
$pageScriptingTestResultsFile = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $pageScriptingTestResultsFile -name "pageScriptingTestResultsFile"
$pageScriptingTestResultsFolder = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $pageScriptingTestResultsFolder -name "pageScriptingTestResultsFolder"
$rulesetFile = CheckRelativePath -baseFolder $baseFolder -sharedFolder $sharedFolder -path $rulesetFile -name "rulesetFile"
$restoreDatabases | ForEach-Object {
if ($_ -notin @("BeforeBcpTests", "BeforeEachTestApp", "BeforeEachBcptTestApp", "BeforeEachPageScriptingTest", "BeforePageScriptingTests")) {
throw "restoreDatabases must be one of the following values: BeforeBcpTests, BeforeEachTestApp, BeforeEachBcptTestApp, BeforePageScriptingTests, BeforeEachPageScriptingTest"
}
}
$containerEventLogFile,$buildOutputFile,$testResultsFile,$bcptTestResultsFile | ForEach-Object {
if ($_ -and (Test-Path $_)) {
Remove-Item -Path $_ -Force
}
}
if ($pageScriptingTestResultsFolder -and (Test-Path $pageScriptingTestResultsFolder)) {
Remove-Item -Path $pageScriptingTestResultsFolder -Recurse -Force
New-Item -ItemType Directory -Path $pageScriptingTestResultsFolder | Out-Null
}
if ($pageScriptingTestResultsFile -and (Test-Path $pageScriptingTestResultsFile)) {
Remove-Item -Path $pageScriptingTestResultsFile -Force
}
$addBcptTestSuites = $true
if ($bcptTestSuites) {
$addBcptTestSuites = $false
}
if ($bcptTestFolders) {
$bcptTestFolders | ForEach-Object {
if (-not (Test-Path (Join-Path $_ "bcptSuite.json"))) {
throw "no bcptsuite.json found in bcpt test folder $_"
}
if ($addBcptTestSuites) {
$bcptTestSuites += @((Join-Path $_ "bcptSuite.json"))
}
}
}
$artifactUrl = ""
$filesOnly = $false
$IsBcSaaSInfrastructure = $bcAuthContext -and $bcAuthContext -is [Hashtable] -and $bcAuthContext.ContainsKey('scopes') -and $bcAuthContext.scopes -like "https://projectmadeira.com/*"
if ($IsBcSaaSInfrastructure) {
Write-Host "Using BC SaaS Infrastructure. Test for feature compatibility."
if ("$environment" -eq "") {
throw "When specifying bcAuthContext, you also have to specify the name of the pre-setup online environment to use."
}
if ($additionalCountries) {
throw "You cannot specify additional countries when using an online environment."
}
if ($uninstallRemovedApps) {
Write-Host -ForegroundColor Yellow "Uninstalling removed apps from online environments are not supported"
$uninstallRemovedApps = $false
}
if (!$doNotRunBcptTests -and $bcptTestSuites) {
throw "BCPT Tests are not supported on cloud pipelines yet!"
}
if (!$doNotRunPageScriptingTests -and $pageScriptingTests -and $pageScriptingTestResultsFolder -and $pageScriptingTestResultsFile) {
throw "Page scripting Tests are not supported on cloud pipelines yet!"