Skip to content
This repository was archived by the owner on Mar 19, 2025. It is now read-only.

Commit c179a3e

Browse files
Merge pull request #3 from SubPointSolutions/dev
Dev
2 parents 59c9113 + 619b7b2 commit c179a3e

File tree

3 files changed

+266
-21
lines changed

3 files changed

+266
-21
lines changed

Build/build.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
{
4444
"Target": "scripts",
4545
"TargetFiles": [
46-
"bin/debug/scripts/SubPointSolutions.CakeBuild.Core.cake"
46+
"bin/debug/scripts/SubPointSolutions.CakeBuild.Core.cake",
47+
"bin/debug/scripts/build-template.json"
4748
]
4849
}
4950
]

README.md

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,182 @@ Common cake build tools for SubPoint Solution projects.
66
| ------------- | ------------- |
77
| dev | [![Build status](https://ci.appveyor.com/api/projects/status/54i4q9hrktdhj0je/branch/dev?svg=true)](https://ci.appveyor.com/project/SubPointSupport/cakebuildtools/branch/dev) |
88
| beta | [![Build status](https://ci.appveyor.com/api/projects/status/54i4q9hrktdhj0je/branch/beta?svg=true)](https://ci.appveyor.com/project/SubPointSupport/cakebuildtools/branch/beta) |
9-
| master| [![Build status](https://ci.appveyor.com/api/projects/status/54i4q9hrktdhj0je/branch/master?svg=true)](https://ci.appveyor.com/project/SubPointSupport/cakebuildtools/branch/master) |
9+
| master| [![Build status](https://ci.appveyor.com/api/projects/status/54i4q9hrktdhj0je/branch/master?svg=true)](https://ci.appveyor.com/project/SubPointSupport/cakebuildtools/branch/master) |
10+
11+
### SubPointSolutions.CakeBuildTools in details
12+
CakeBuildTools is a high level abstraction over [Cakebuild](http://cakebuild.net) aiming to provide a highly repeatable and reusable build workflow.
13+
It is used to build [SPMeta2](https://github.com/SubPointSolutions/SPMeta2), [SPMeta2 Reverse](https://github.com/SubPointSolutions/spmeta2-reverse), [SPMeta2 VS Extensions](https://github.com/SubPointSolutions/spmeta2-vsixextensions), [SPMeta2-Spec](https://github.com/SubPointSolutions/spmeta2-spec), [MetaPack](https://github.com/SubPointSolutions/MetaPack), [DefinitelyPacked](https://github.com/SubPointSolutions/DefinitelyPacked) and [some other projects](https://github.com/SubPointSolutions) in a highly standartized way.
14+
15+
Implementation is done via cake build script which is packaged and then reused across all the builds.
16+
The aim is to hide all the complexity of the build and drive the whole build workflow via name conventions and json build configuration.
17+
18+
Current build handles:
19+
* Checking presense of environment variables
20+
* Cleaning folders
21+
* Building *.sln files
22+
* Building set of *.csproj files
23+
* Running unit tests (files and groups)
24+
* NuGet packaging and publishing
25+
* Chocolatey packaging and publishing
26+
* ZIP packaging (with checksums, part of Chocolatey packaging)
27+
28+
The following 'rules' and name conventions are encofced in order to keep the build simple:
29+
30+
#### Rule 1 - same build config for all solutions
31+
Every solution must have "Build" project housing the following files:
32+
* build.cake
33+
* build.ps1
34+
* build.json
35+
* tools/nuget.config
36+
* tools/nuget.exe
37+
* tools/packages.config
38+
* tools/packages.config.md5sum
39+
40+
nuget.config must have configuration to load up the main NuGet gallery plus both SubPoint Solution Staging and CI galleries:
41+
```xml
42+
<?xml version="1.0" encoding="utf-8"?>
43+
<configuration>
44+
<packageSources>
45+
<add key="nuget" value="https://www.nuget.org/api/v2/" />
46+
<add key="SubPointSolutions Staging" value="https://www.myget.org/F/subpointsolutions-staging/api/v2" />
47+
<add key="SubPointSolutions Appeyor CI - cakebuildtools " value="https://ci.appveyor.com/nuget/subpointsolutions-cakebuildtools" />
48+
</packageSources>
49+
</configuration>
50+
```
51+
packages.config must have at least two packages. That's how common build infrastructure gets delivered to Cake.
52+
53+
```xml
54+
<?xml version="1.0" encoding="utf-8"?>
55+
<packages>
56+
<package id="Cake" version="0.17.0" />
57+
<package id="SubPointSolutions.CakeBuildTools" version="0.1.0-alpha170521456" />
58+
</packages>
59+
```
60+
61+
#### Rule 2 - keep the build simple
62+
Build script must delegate all work to 'SubPointSolutions.CakeBuild.Core.cake'.
63+
Use 'load' directive to load up the core build script.
64+
```
65+
// load up common tools
66+
#load tools/SubPointSolutions.CakeBuildTools/scripts/SubPointSolutions.CakeBuild.Core.cake
67+
68+
// default targets
69+
RunTarget(target);
70+
```
71+
#### Rule 3 - Default and CI builds must always pass
72+
The following builds must always pass. Use [cmder](cmder.net) in order to execute the following builds:
73+
* powershell .\build.ps1
74+
* powershell .\build.ps1 -Target Default-CI
75+
76+
#### Rule 4 - Leverage built-in tasks
77+
Common build script provides cake tasks with the following name convention:
78+
79+
* Default-XXX is a target to be used in build.ps1
80+
* Action-XXX is a self-container build task to be chained with "Default-XXX" tasks
81+
82+
83+
| Target | Actions |
84+
| ------------- | ------------- |
85+
| | Action-Docs-Publishing |
86+
| | Action-Validate-Environment |
87+
| | Action-Restore-NuGet-Packages |
88+
| Default | Action-Clean <br/> Action-Build <br/> Action-Run-UnitTests |
89+
| Default-Build | Action-Clean <br/> Action-Build |
90+
| Default-Clean | Action-Clean |
91+
| Default-Run-UnitTests | Action-Clean<br/>Action-Build<br/> Action-Run-UnitTests |
92+
| Default-API-NuGet-Packaging | Action-Clean<br/>Action-Build<br/>Action-Run-UnitTests<br/>Action-NuGet-Packaging |
93+
| Default-API-NuGet-Publishing | Action-Clean<br/>Action-Build<br/>Action-Run-UnitTests<br/>Action-API-NuGet-Packaging<br/>Action-API-NuGet-Publishing<br/> |
94+
| Default-CLI | Action-Clean<br/>Action-Build<br/>Action-Run-UnitTests<br/>Action-API-NuGet-Packaging<br/>Action-CLI-Zip-Packaging<br/>Action-CLI-Chocolatey-Packaging |
95+
| Default-CLI-Publishing | Action-Clean<br/>Action-Build<br/>Action-Run-UnitTests<br/>Action-API-NuGet-Packaging<br/>Action-CLI-Zip-Packaging<br/>Action-CLI-Chocolatey-Packaging<br/>Action-CLI-Zip-Publishing<br/>Action-CLI-Chocolatey-Publishing |
96+
| Default-Desktop | TBD |
97+
| Default-CI | Action-Clean<br/>Action-Build<br/>Action-Run-UnitTests<br/>Action-API-NuGet-Packaging<br/>Action-CLI-Zip-Packaging<br/>Action-CLI-Chocolatey-Publishing |
98+
99+
#### Rule 5 - simple build is done with build.json config
100+
All build configiration is to be driven from the build.json config.
101+
102+
The simpliest example can bve found below, and more complicated can be found in project repos at github (such as SPMeta2 and other projects).
103+
```json
104+
{
105+
"defaultSolutionDirectory": "./../",
106+
"defaultSolutionFilePath": "./../YourSolution.sln",
107+
"defaultNuGetPackagesDirectory": "./build-artifact-nuget-packages",
108+
"defaultChocolateyPackagesDirectory": "./build-artifact-cli-packages",
109+
"defaultNuspecVersion": "0.1.0",
110+
111+
"defaultTestCategories": [
112+
"CI.Core"
113+
],
114+
"customNuspecs": [
115+
116+
],
117+
"customChocolateySpecs": [
118+
119+
],
120+
"defaultTestAssemblyPaths": [
121+
122+
],
123+
"defaultBuildDirs": [ ],
124+
"defaultEnvironmentVariables": [ ]
125+
}
126+
```
127+
128+
#### Rule 6 - build customizations go with replacing default tasks
129+
Core script exposes all default/action cake tasks as global C# variables.
130+
That allows to inject custom build tasks redefining the whole build workflow.
131+
Use the following example to get started:
132+
133+
```cs
134+
135+
// load up common tools
136+
#tool nuget:https://www.myget.org/F/subpointsolutions-staging/api/v2?package=SubPointSolutions.CakeBuildTools&prerelease
137+
#load tools\SubPointSolutions.CakeBuildTools\scripts\SubPointSolutions.CakeBuild.Core.cake
138+
139+
// redefining default build task
140+
// cleaning up existing actions, adding our custom one
141+
142+
// in that case we follow all the avialable 'Default' build profiles from the core build script
143+
defaultActionBuild.Task.Actions.Clear();
144+
defaultActionBuild
145+
.Does(() =>
146+
{
147+
Information(string.Format("Building VSIX for solution:[{0}]", defaultSolutionFilePath));
148+
149+
MSBuild(defaultSolutionFilePath, settings => {
150+
151+
settings.Verbosity = Verbosity.Quiet;
152+
153+
// Building with MSBuild 12.0 fails #97
154+
// CRAZY!! to avoid the following error
155+
// error MSB4018: The "ValidateVsixManifest" task failed unexpectedly
156+
settings.ToolPath = @"C:\Program Files (x86)\MSBuild\12.0\bin\MSBuild.exe";
157+
});
158+
});
159+
160+
// filling up default build task with custom build
161+
//defaultActionBuild.Does(actionCustomBuild);
162+
163+
// default targets
164+
RunTarget(target);
165+
166+
```
167+
168+
#### Rule 7 - light appveyor.yml config
169+
appveyor.yml config must be light. Avoid adding heavy logic into it keeping it as following:
170+
171+
```
172+
test: off
173+
174+
clone_folder: c:\prj
175+
176+
build_script:
177+
- ps: c:\prj\Build\build.ps1 -Target "Default-CI" -Verbosity Minimal
178+
179+
artifacts:
180+
- path: '**\packages\*.nupkg'
181+
```
182+
183+
#### Rule 8 - find examples in other projects
184+
Check dev branches of other SubPoint Solution projects to get more understanding on how common build works and can be configured.
185+
186+
### Known issues
187+
* Appveyor build may somehow chacnge MD5 checksum of packages.config. Disable it in build.ps1

SubPointSolutions.CakeBuildTools/Scripts/SubPointSolutions.CakeBuild.Core.cake

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// common tooling
1+
// common tooling
22
// always version to avoid breaking change with new releases
33
#addin nuget:https://www.nuget.org/api/v2/?package=Cake.Powershell&Version=0.2.9
44
#addin nuget:https://www.nuget.org/api/v2/?package=newtonsoft.json&Version=9.0.1
@@ -189,13 +189,16 @@ string ResolveVersionForPackage(string id) {
189189
// is it choco spec?
190190
specs = jsonConfig["customChocolateySpecs"];
191191

192-
foreach(var spec in specs) {
193-
var specId = (string)spec["Id"];
192+
if(specs != null)
193+
{
194+
foreach(var spec in specs) {
195+
var specId = (string)spec["Id"];
194196

195-
if(specId == id) {
196-
return (string)spec["Version"];
197-
}
198-
}
197+
if(specId == id) {
198+
return (string)spec["Version"];
199+
}
200+
}
201+
}
199202

200203
throw new Exception(String.Format("Cannot resolve version for package:[{0}]. Neither customNuspecs nor customChocolateySpecs has it", id));
201204
}
@@ -341,6 +344,7 @@ NuGetPackSettings[] ResolveNuGetPackSettings() {
341344

342345
packSettings.Dependencies = ResolveDependenciesForPackage(packSettings.Id);
343346

347+
Verbose("Adding Authors/Owners...");
344348
if(spec["Authors"] == null)
345349
packSettings.Authors = new [] { "SubPoint Solutions" };
346350
else
@@ -351,28 +355,40 @@ NuGetPackSettings[] ResolveNuGetPackSettings() {
351355
else
352356
packSettings.Owners = spec["Owners"].Select(t => (string)t).ToArray();
353357

358+
Verbose("Adding License/ProjectUrl/IconUrl...");
354359
packSettings.LicenseUrl = new System.Uri((string)spec["LicenseUrl"]);
355360
packSettings.ProjectUrl = new System.Uri((string)spec["ProjectUrl"]);
356361
packSettings.IconUrl = new System.Uri((string)spec["IconUrl"]);
357362

363+
Verbose("Adding Description/Copyright...");
358364
packSettings.Description = (string)spec["Description"];
359365
packSettings.Copyright = (string)spec["Copyright"];
360366

367+
Verbose("Adding tags...");
361368
packSettings.Tags = spec["Tags"].Select(t => (string)t).ToArray();
362369

363370
packSettings.RequireLicenseAcceptance = false;
364371
packSettings.Symbols = false;
365372
packSettings.NoPackageAnalysis = false;
366373

367374
// files
368-
var packageFiles = spec["Files"].ToArray();
375+
var packageFilesObject = spec["Files"];
376+
377+
Verbose("Adding files...");
369378

370379
// default files
371-
if(packageFiles.Count() == 0)
380+
if(packageFilesObject == null || packageFilesObject.Select(t => t).Count() == 0)
372381
{
373-
Verbose("Adding default files - *.dll/*.xml from bin/debug");
382+
var packageFiles = packageFilesObject;
374383

384+
Verbose("Adding default files - *.dll/*.xml from bin/debug");
385+
375386
var projectPath = System.IO.Path.Combine(defaultSolutionDirectory, packSettings.Id);
387+
388+
var customProjectFolder = (string)spec["CustomProjectFolder"];
389+
if(!String.IsNullOrEmpty(customProjectFolder))
390+
projectPath = System.IO.Path.Combine(defaultSolutionDirectory, customProjectFolder);
391+
376392
var projectBinPath = System.IO.Path.Combine(projectPath, "bin/debug");
377393

378394
packSettings.BasePath = projectBinPath;
@@ -389,11 +405,24 @@ NuGetPackSettings[] ResolveNuGetPackSettings() {
389405
};
390406
}
391407
else{
408+
409+
var packageFiles = packageFilesObject;
410+
392411
Verbose("Adding custom files...");
393412

394413
var nuSpecContentFiles = new List<NuSpecContent>();
395414

396-
var projectPath = System.IO.Path.Combine(defaultSolutionDirectory, packSettings.Id);
415+
var projectPath = System.IO.Path.Combine(defaultSolutionDirectory, packSettings.Id);
416+
417+
var customProjectFolder = (string)spec["CustomProjectFolder"];
418+
419+
if(!String.IsNullOrEmpty(customProjectFolder))
420+
projectPath = System.IO.Path.Combine(defaultSolutionDirectory, customProjectFolder);
421+
422+
Verbose("Project path: " + projectPath);
423+
424+
var projectBinPath = System.IO.Path.Combine(projectPath, "bin/debug");
425+
397426
packSettings.BasePath = projectPath;
398427

399428
foreach(var packageFile in packageFiles){
@@ -462,12 +491,15 @@ NuSpecDependency[] ResolveDependenciesForPackage(string id) {
462491

463492
// CI related environment
464493
// * dev / beta / master versioning and publishing
465-
var ciBranch = GetGlobalEnvironmentVariable("ci.activebranch") ?? "dev";
494+
var ciBranch = GetGlobalEnvironmentVariable("ci.activebranch") ?? "local";
466495

467496
// override under CI run
468497
var ciBranchOverride = GetGlobalEnvironmentVariable("APPVEYOR_REPO_BRANCH");
469498
if(!String.IsNullOrEmpty(ciBranchOverride))
499+
{
500+
Information(String.Format("Detected APPVEYOR build. Reverting to APPVEYOR_REPO_BRANCH varibale:[{0}]", ciBranchOverride));
470501
ciBranch = ciBranchOverride;
502+
}
471503

472504
var ciNuGetSource = GetGlobalEnvironmentVariable("ci.nuget.source") ?? String.Empty;
473505
var ciNuGetKey = GetGlobalEnvironmentVariable("ci.nuget.key") ?? String.Empty;
@@ -601,22 +633,52 @@ var defaultActionAPINuGetPackaging =Task("Action-API-NuGet-Packaging")
601633
}
602634
});
603635

636+
bool ShouldPublishAPINuGet(string branch) {
637+
638+
// always publish dev branch
639+
// the rest comes from 'ciNuGetShouldPublish' -> 'ci.nuget.shouldpublish' environment variable
640+
if(branch == "dev")
641+
return true;
642+
643+
return ciNuGetShouldPublish;
644+
}
645+
604646
var defaultActionAPINuGetPublishing = Task("Action-API-NuGet-Publishing")
605647
// all packaged should be compiled by NuGet-Packaging task into 'defaultNuGetPackagesDirectory' folder
606648
.Does(() =>
607649
{
608-
if(!ciNuGetShouldPublish) {
609-
Information("Skipping NuGet publishing as ciNuGetShouldPublish is false.");
650+
Information(String.Format("API NuGet publishing enabled? branch:[{0}]", ciBranch));
651+
var shouldPublish = ShouldPublishAPINuGet(ciBranch);
652+
653+
var nugetSource = String.Empty;
654+
var nugetKey = String.Empty;
655+
656+
if(!shouldPublish) {
657+
Information("Skipping NuGet publishing.");
610658
return;
659+
} else {
660+
Information("Fetching NuGet feed creds.");
661+
662+
var feedSourceVariableName = String.Format("ci.nuget.{0}-source", ciBranch);
663+
var feedKeyVariableName = String.Format("ci.nuget.{0}-key", ciBranch);
664+
665+
var feedSourceValue = GetGlobalEnvironmentVariable(feedSourceVariableName);
666+
var feedKeyValue = GetGlobalEnvironmentVariable(feedKeyVariableName);
667+
668+
if(String.IsNullOrEmpty(feedSourceValue))
669+
throw new Exception(String.Format("environment variable is null or empty:[{0}]", feedSourceVariableName));
670+
671+
if(String.IsNullOrEmpty(feedKeyValue))
672+
throw new Exception(String.Format("environment variable is null or empty:[{0}]", feedKeyVariableName));
673+
674+
nugetSource = feedSourceValue;
675+
nugetKey = feedKeyValue;
611676
}
612677

613678
Information("Publishing NuGet packages to repository: [{0}]", new []{
614-
ciNuGetSource
679+
nugetSource
615680
});
616681

617-
var nugetSource = ciNuGetSource;
618-
var nugetKey = ciNuGetKey;
619-
620682
var nuGetPackages = System.IO.Directory.GetFiles(defaultNuGetPackagesDirectory, "*.nupkg");
621683

622684
foreach(var packageFilePath in nuGetPackages)
@@ -968,4 +1030,8 @@ var taskDefaultCI = Task("Default-CI")
9681030
.IsDependentOn("Action-API-NuGet-Packaging")
9691031
.IsDependentOn("Action-CLI-Zip-Packaging")
9701032
.IsDependentOn("Action-CLI-Chocolatey-Packaging")
971-
.IsDependentOn("Action-Docs-Merge");
1033+
// always 'push'
1034+
// the action checks if the current branch has to be published (dev always, the rest goes via 'ci.nuget.shouldpublish')
1035+
.IsDependentOn("Action-API-NuGet-Publishing")
1036+
1037+
.IsDependentOn("Action-Docs-Merge");

0 commit comments

Comments
 (0)