Skip to content

Commit e0e501f

Browse files
Start: Demo application
0 parents  commit e0e501f

33 files changed

+1556
-0
lines changed

.gitignore

Lines changed: 428 additions & 0 deletions
Large diffs are not rendered by default.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Martin Zimmermann
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# DevOpsDays Graz 2025 Sample Application
2+
This is the companion repository for the workshop "Modern Delivery for Not-so-modern Software" on the DevOpsDays Graz 2025.
3+
4+
## Repository Structure
5+
The `main` branch contains the start of the workshop on which the participant should work while following along the workshop.
6+
7+
The `workshop-steps` branch will contain the results of the steps in the workshop as separate git commits and can be used as a reference.
8+
9+
## Sample Application
10+
The sample application is a simple ToDo List application. However, there are a few things that that make it hard to build: copying files, manually entering version numbers, manual signing, etc.
11+
12+
In the workshop we will take the application and modernize its delivery step by step.
13+
14+
## Folder Structure
15+
* `data/`: contains the file `default-tasks.xml`, which is a dev fallback for the template engine
16+
* `scripts/`: contains the signing script (only works on windows)
17+
* `src/`: source code of the application
18+
* `DevOpsDaysTasks.Core/`: models, services and database access of the app
19+
* `DevOpsDaysTasks.Core.Tests/`: tests for models and services
20+
* `DevOpsDaysTasks.IntegrationTests/`: tests that actually query a database
21+
* `DevOpsDaysTasks.UI/`: UI and main application
22+
* `workshop_material/`: additional material for participants, e.g., the file that should be copied with a release
23+
* `release_steps.md`: instruction on how to perform a manual release
24+
25+
## How to Start the Application
26+
⚠️ This application requires .NET 8 SDK or later
27+
28+
Run `dotnet run --project .\src\DevOpsDaysTasks.UI\`.
29+
30+
### Switching between MSSQL and SQLite
31+
By default the application uses localDB. For cross-platform compatibility also a SQLite adapter is implemented. Replace `DbKind.SqlServer` with `DbKind.Sqlite` in `MainWindow.axaml.cs` and `MSSQLFixture.cs` to use SQLite.

data/templates/default-tasks.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<Tasks>
2+
<Task Title="Prepare Demo Application" Done="false" />
3+
<Task Title="Prepare slides" Done="false" />
4+
</Tasks>

scripts/sign.ps1

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
param(
2+
[Parameter(Mandatory)][String]$ExeFile,
3+
[Parameter(Mandatory)][String]$PfxFile,
4+
[Parameter(Mandatory)][String]$PfxPassword
5+
)
6+
$signtool = "C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe"
7+
8+
# Sign the .exe
9+
$exe = Get-ChildItem $ExeFile | Select-Object -First 1
10+
& $signtool sign `
11+
/fd SHA256 `
12+
/f $PfxFile `
13+
/p $PfxPassword `
14+
/tr http://timestamp.digicert.com `
15+
/td SHA256 `
16+
$exe.FullName
17+
18+
# Verify
19+
Get-AuthenticodeSignature $exe.FullName | Format-List
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="coverlet.collector" Version="6.0.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
15+
<PackageReference Include="xunit" Version="2.5.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<Using Include="Xunit" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<ProjectReference Include="..\DevOpsDaysTasks.Core\DevOpsDaysTasks.Core.csproj" />
25+
</ItemGroup>
26+
27+
</Project>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using DevOpsDaysTasks.Core.Services;
2+
using System.Text;
3+
4+
namespace DevOpsDaysTasks.Core.Tests;
5+
6+
public class TemplateLoaderTests
7+
{
8+
[Fact]
9+
public void LoadDefaultTasks_Parses_Title_And_Done()
10+
{
11+
// Arrange
12+
var xml = """
13+
<Tasks>
14+
<Task Title=" Buy groceries " Done="true"/>
15+
<Task Title="Prepare slides" Done="0"/>
16+
</Tasks>
17+
""";
18+
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
19+
20+
// Act
21+
var tasks = TemplateLoader.LoadDefaultTasks(stream);
22+
23+
// Assert
24+
Assert.Equal(2, tasks.Count);
25+
Assert.Equal("Buy groceries", tasks[0].Title); // trimmed
26+
Assert.True(tasks[0].IsDone); // "true" -> true
27+
Assert.False(tasks[1].IsDone); // "0" -> false
28+
}
29+
30+
[Fact]
31+
public void LoadDefaultTasks_Skips_Empty_Titles()
32+
{
33+
var xml = """
34+
<Tasks>
35+
<Task Title=" " Done="true"/>
36+
<Task Done="true"><Title></Title></Task>
37+
<Task Title="Valid" Done="false"/>
38+
</Tasks>
39+
""";
40+
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
41+
42+
// Act
43+
var tasks = TemplateLoader.LoadDefaultTasks(stream);
44+
45+
Assert.Single(tasks); // Tasks without Title or an empty Tilte are skipped
46+
Assert.Equal("Valid", tasks[0].Title);
47+
}
48+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace DevOpsDaysTasks.Core.Attributes
4+
{
5+
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
6+
public sealed class VersionNameAttribute : Attribute
7+
{
8+
public string VersionName { get; }
9+
public VersionNameAttribute(string versionName)
10+
{
11+
VersionName = versionName;
12+
}
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.1" />
11+
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.*" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.ComponentModel;
2+
using System.Runtime.CompilerServices;
3+
4+
namespace DevOpsDaysTasks.Core.Models;
5+
6+
public class TaskItem : INotifyPropertyChanged
7+
{
8+
9+
private string _title = string.Empty;
10+
private bool _isDone;
11+
12+
public int Id { get; set; }
13+
14+
public string Title
15+
{
16+
get => _title;
17+
set { if (_title != value) { _title = value; OnPropertyChanged(); } }
18+
}
19+
20+
public bool IsDone
21+
{
22+
get => _isDone;
23+
set { if (_isDone != value) { _isDone = value; OnPropertyChanged(); } }
24+
}
25+
26+
public event PropertyChangedEventHandler? PropertyChanged;
27+
private void OnPropertyChanged([CallerMemberName] string? prop = null)
28+
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
29+
}

0 commit comments

Comments
 (0)