Skip to content

Commit 94a6ff9

Browse files
committed
Squashed commit of the following:
commit 85df065 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 20:09:27 2025 -0400 feat: Version bump commit 1c432b9 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 19:20:19 2025 -0400 fix: Test skipping commit df5ca7d Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 19:14:22 2025 -0400 feat: Allow tests to be skipped commit 0789a5b Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 18:56:49 2025 -0400 Update linux.yml commit 72c2bd0 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 18:51:53 2025 -0400 Update linux.yml commit d2066d9 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 18:51:29 2025 -0400 Update linux.yml commit 122f586 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 18:48:37 2025 -0400 fix: Linux keyring on GitHub Actions commit 007f19c Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 18:05:48 2025 -0400 fix: Builds commit 8b123b1 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 18:00:56 2025 -0400 fix: Builds commit 5ebfec6 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 17:47:47 2025 -0400 fix: Builds commit b935090 Author: Nick Logozzo <nlogozzo225@gmail.com> Date: Sat Nov 1 17:36:22 2025 -0400 fix: macOS Keyring Storage
1 parent 3c330ae commit 94a6ff9

File tree

16 files changed

+218
-29
lines changed

16 files changed

+218
-29
lines changed

.github/workflows/linux.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ permissions:
1111
contents: read
1212
env:
1313
TEST_PROJECT_NAME: Nickvision.Desktop.Tests
14+
DISPLAY: :99.0
15+
CI: true
1416
jobs:
1517
build:
1618
name: "Build on Linux"
@@ -31,21 +33,20 @@ jobs:
3133
uses: actions/setup-dotnet@v5
3234
with:
3335
dotnet-version: '9.0.x'
36+
- name: "Unlock Keyring"
37+
uses: t1m0thyj/unlock-keyring@v1
3438
- name: "Setup Environment"
3539
run: |
36-
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
37-
sudo apt-get install firefox xdg-user-dirs xdg-user-dirs-gtk gettext tzdata locales libnotify-dev libsecret-tools notification-daemon -y
40+
sudo apt-get install xdg-user-dirs xdg-user-dirs-gtk gettext tzdata locales libnotify-dev libsecret-tools notification-daemon xvfb -y
3841
xdg-user-dirs-update
3942
xdg-user-dirs-gtk-update
4043
sudo locale-gen en_US.UTF-8
4144
sudo update-locale LANG=en_US.UTF-8
42-
/usr/lib/notification-daemon-1.0/notification-daemon --display=:0 -r &
43-
- name: "Unlock Keyring"
44-
uses: t1m0thyj/unlock-keyring@v1
45+
xvfb-run /usr/lib/notification-daemon/notification-daemon -r &
4546
- name: "Restore"
4647
run: dotnet restore
4748
- name: "Build"
4849
run: dotnet build -c Release
4950
- name: "Test"
5051
run: dotnet test -c Release ${{ env.TEST_PROJECT_NAME }}
51-
52+

.github/workflows/macos.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ jobs:
3737
security create-keychain -p ${{ env.KEYRING_PASSWORD }} build.keychain
3838
security default-keychain -s build.keychain
3939
security unlock-keychain -p ${{ env.KEYRING_PASSWORD }} build.keychain
40+
security set-keychain-settings -t 3600 -l build.keychain
4041
- name: "Restore"
4142
run: dotnet restore
4243
- name: "Build"
4344
run: dotnet build -c Release
4445
- name: "Test"
4546
run: dotnet test -c Release ${{ env.TEST_PROJECT_NAME }}
47+
- name: Cleanup Keychain
48+
if: always()
49+
run: security delete-keychain build.keychain

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 Nick Logozzo
3+
Copyright (c) 2025 Nickvision
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Nickvision.Desktop.Tests/DatabaseKeyringServiceTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public sealed class DatabaseKeyringServiceTests
1717
[TestMethod]
1818
public void Case001_Init()
1919
{
20+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
21+
{
22+
Assert.Inconclusive("Dialogs are not supported in CI environments");
23+
}
2024
#pragma warning disable CA1416
2125
_keyringService = new DatabaseKeyringService(new AppInfo("org.nickvision.desktop.test", "Nickvision.Desktop.Test", "Test"), new SystemSecretService());
2226
#pragma warning restore CA1416
@@ -27,13 +31,21 @@ public void Case001_Init()
2731
[TestMethod]
2832
public void Case002_Check()
2933
{
34+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
35+
{
36+
Assert.Inconclusive("Dialogs are not supported in CI environments");
37+
}
3038
Assert.IsNotNull(_keyringService);
3139
Assert.IsTrue(_keyringService.IsSavingToDisk);
3240
}
3341

3442
[TestMethod]
3543
public async Task Case003_Add()
3644
{
45+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
46+
{
47+
Assert.Inconclusive("Dialogs are not supported in CI environments");
48+
}
3749
Assert.IsNotNull(_keyringService);
3850
Assert.IsTrue(await _keyringService.AddCredentialAsync(new Credential("YouTube", "abc", "123", new Uri("https://www.youtube.com"))));
3951
Assert.IsNotNull(_keyringService.Credentials.FirstOrDefault(c => c.Name == "YouTube"));
@@ -43,6 +55,10 @@ public async Task Case003_Add()
4355
[TestMethod]
4456
public async Task Case004_Update()
4557
{
58+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
59+
{
60+
Assert.Inconclusive("Dialogs are not supported in CI environments");
61+
}
4662
Assert.IsNotNull(_keyringService);
4763
Assert.IsTrue(await _keyringService.AddCredentialAsync(new Credential("Google", "x@gmail.com", "asdfgh123!", new Uri("https://www.google.com"))));
4864
var cred = _keyringService.Credentials.FirstOrDefault(c => c.Name == "Google");
@@ -57,6 +73,10 @@ public async Task Case004_Update()
5773
[TestMethod]
5874
public async Task Case005_Remove()
5975
{
76+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
77+
{
78+
Assert.Inconclusive("Dialogs are not supported in CI environments");
79+
}
6080
Assert.IsNotNull(_keyringService);
6181
Assert.IsTrue(await _keyringService.AddCredentialAsync(new Credential("Example", "user1", "pass1", new Uri("https://www.example.com"))));
6282
var cred = _keyringService.Credentials.FirstOrDefault(c => c.Name == "Example");
@@ -68,6 +88,10 @@ public async Task Case005_Remove()
6888
[TestMethod]
6989
public async Task Case006_Cleanup()
7090
{
91+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
92+
{
93+
Assert.Inconclusive("Dialogs are not supported in CI environments");
94+
}
7195
Assert.IsNotNull(_keyringService);
7296
Assert.IsTrue(await _keyringService.DestroyAsync());
7397
Assert.IsFalse(_keyringService.Credentials.Any());

Nickvision.Desktop.Tests/Nickvision.Desktop.Tests.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
</PropertyGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
23-
<PackageReference Include="MSTest" Version="3.6.4"/>
22+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
23+
<PackageReference Include="MSTest" Version="3.11.0"/>
24+
<PackageReference Include="MSTest.TestAdapter" Version="3.11.0"/>
25+
<PackageReference Include="MSTest.TestFramework" Version="3.11.0"/>
2426
</ItemGroup>
2527

2628
<ItemGroup>

Nickvision.Desktop.Tests/SystemSecretServiceTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@ public sealed class SystemSecretServiceTests
1515
[TestMethod]
1616
public void Case001_Initialize()
1717
{
18+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
19+
{
20+
Assert.Inconclusive("Dialogs are not supported in CI environments");
21+
}
1822
_secretService = new SystemSecretService();
1923
Assert.IsNotNull(_secretService);
2024
}
2125

2226
[TestMethod]
2327
public async Task Case002_Add()
2428
{
29+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
30+
{
31+
Assert.Inconclusive("Dialogs are not supported in CI environments");
32+
}
2533
Assert.IsNotNull(_secretService);
2634
var secret = new Secret("Nickvision.Desktop.Test", "abc");
2735
Assert.IsTrue(await _secretService.AddAsync(secret));
@@ -31,6 +39,10 @@ public async Task Case002_Add()
3139
[TestMethod]
3240
public async Task Case003_Create()
3341
{
42+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
43+
{
44+
Assert.Inconclusive("Dialogs are not supported in CI environments");
45+
}
3446
Assert.IsNotNull(_secretService);
3547
var service = await _secretService.CreateAsync("Nickvision.Desktop.Test2");
3648
Assert.IsFalse(service is null);
@@ -40,6 +52,10 @@ public async Task Case003_Create()
4052
[TestMethod]
4153
public void Case004_Create()
4254
{
55+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
56+
{
57+
Assert.Inconclusive("Dialogs are not supported in CI environments");
58+
}
4359
Assert.IsNotNull(_secretService);
4460
var service = _secretService.Create("Nickvision.Desktop.Test3");
4561
Assert.IsFalse(service is null);
@@ -49,6 +65,10 @@ public void Case004_Create()
4965
[TestMethod]
5066
public async Task Case005_Get()
5167
{
68+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
69+
{
70+
Assert.Inconclusive("Dialogs are not supported in CI environments");
71+
}
5272
Assert.IsNotNull(_secretService);
5373
Assert.IsTrue(await _secretService.AddAsync(new Secret("Nickvision.Desktop.Test4", "abc")));
5474
var secret = _secretService.Get("Nickvision.Desktop.Test4");
@@ -60,6 +80,10 @@ public async Task Case005_Get()
6080
[TestMethod]
6181
public async Task Case006_Get()
6282
{
83+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
84+
{
85+
Assert.Inconclusive("Dialogs are not supported in CI environments");
86+
}
6387
Assert.IsNotNull(_secretService);
6488
var secret = await _secretService.CreateAsync("Nickvision.Desktop.Test5");
6589
Assert.IsFalse(secret is null);
@@ -73,6 +97,10 @@ public async Task Case006_Get()
7397
[TestMethod]
7498
public async Task Case007_Update()
7599
{
100+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
101+
{
102+
Assert.Inconclusive("Dialogs are not supported in CI environments");
103+
}
76104
Assert.IsNotNull(_secretService);
77105
Assert.IsTrue(_secretService.Add(new Secret("Nickvision.Desktop.Test6", "abc123")));
78106
var secret = await _secretService.GetAsync("Nickvision.Desktop.Test6");
@@ -89,6 +117,10 @@ public async Task Case007_Update()
89117
[TestMethod]
90118
public async Task Case008_Update()
91119
{
120+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
121+
{
122+
Assert.Inconclusive("Dialogs are not supported in CI environments");
123+
}
92124
Assert.IsNotNull(_secretService);
93125
Assert.IsTrue(await _secretService.AddAsync(new Secret("Nickvision.Desktop.Test7", "abc123")));
94126
var secret = _secretService.Get("Nickvision.Desktop.Test7");
@@ -105,6 +137,10 @@ public async Task Case008_Update()
105137
[TestMethod]
106138
public async Task Case009_Delete()
107139
{
140+
if (global::System.Environment.GetEnvironmentVariable("CI") == "true")
141+
{
142+
Assert.Inconclusive("Dialogs are not supported in CI environments");
143+
}
108144
Assert.IsNotNull(_secretService);
109145
foreach (var cred in new[]
110146
{

Nickvision.Desktop/Keyring/PasswordGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ static PasswordGenerator()
3232
];
3333
SpecialChars =
3434
[
35-
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
36-
'@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'
35+
'!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[',
36+
'\\', ']', '^', '_', '`', '{', '|', '}', '~'
3737
];
3838
}
3939

Nickvision.Desktop/Nickvision.Desktop.csproj

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88
<Nullable>enable</Nullable>
99
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1010
<PackageId>Nickvision.Desktop</PackageId>
11-
<Version>2025.10.0</Version>
11+
<Version>2025.11.0</Version>
1212
<Company>Nickvision</Company>
1313
<Authors>Nickvision</Authors>
14-
<Description>A cross-platform base for Nickvision desktop applications</Description>
14+
<Description>A cross-platform base for Nickvision desktop applications.</Description>
1515
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1616
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
1717
<Copyright>(c) Nickvision 2021-2025</Copyright>
1818
<PackageProjectUrl>https://nickvision.org</PackageProjectUrl>
1919
<RepositoryUrl>https://github.com/NickvisionApps/Desktop</RepositoryUrl>
20+
<Title>Nickvision.Desktop</Title>
21+
<PackageIcon>icon.png</PackageIcon>
2022
</PropertyGroup>
2123

2224
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
@@ -42,4 +44,12 @@
4244
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.2.1" Condition="$([MSBuild]::IsOSPlatform('Windows'))"/>
4345
</ItemGroup>
4446

47+
<ItemGroup>
48+
<None Include="..\resources\icon.png">
49+
<Pack>True</Pack>
50+
<PackagePath></PackagePath>
51+
<Link>icon.png</Link>
52+
</None>
53+
</ItemGroup>
54+
4555
</Project>

Nickvision.Desktop/System/SystemSecretService.cs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public bool Add(Secret secret)
6060
StartInfo = new ProcessStartInfo()
6161
{
6262
FileName = "security",
63-
Arguments = $"add-generic-password -a default -s '{secret.Name}' -w '{secret.Value}'",
63+
Arguments = $"add-generic-password -a default -s {secret.Name} -w \"{secret.Value}\"",
6464
RedirectStandardOutput = true,
6565
RedirectStandardError = true,
6666
UseShellExecute = false,
@@ -134,7 +134,7 @@ public async Task<bool> AddAsync(Secret secret)
134134
StartInfo = new ProcessStartInfo()
135135
{
136136
FileName = "security",
137-
Arguments = $"add-generic-password -a default -s '{secret.Name}' -w '{secret.Value}'",
137+
Arguments = $"add-generic-password -a default -s {secret.Name} -w \"{secret.Value}\"",
138138
RedirectStandardOutput = true,
139139
RedirectStandardError = true,
140140
UseShellExecute = false,
@@ -228,7 +228,7 @@ public bool Delete(string name)
228228
StartInfo = new ProcessStartInfo()
229229
{
230230
FileName = "security",
231-
Arguments = $"delete-generic-password -a default -s '{name}'",
231+
Arguments = $"delete-generic-password -a default -s {name}",
232232
RedirectStandardOutput = true,
233233
RedirectStandardError = true,
234234
UseShellExecute = false,
@@ -280,7 +280,7 @@ public async Task<bool> DeleteAsync(string name)
280280
StartInfo = new ProcessStartInfo()
281281
{
282282
FileName = "security",
283-
Arguments = $"delete-generic-password -a default -s '{name}'",
283+
Arguments = $"delete-generic-password -a default -s {name}",
284284
RedirectStandardOutput = true,
285285
RedirectStandardError = true,
286286
UseShellExecute = false,
@@ -336,7 +336,7 @@ public async Task<bool> DeleteAsync(string name)
336336
StartInfo = new ProcessStartInfo()
337337
{
338338
FileName = "security",
339-
Arguments = $"find-generic-password -a default -w -s '{name}'",
339+
Arguments = $"find-generic-password -a default -s {name} -w",
340340
RedirectStandardOutput = true,
341341
RedirectStandardError = true,
342342
UseShellExecute = false,
@@ -350,7 +350,7 @@ public async Task<bool> DeleteAsync(string name)
350350
return null;
351351
}
352352
var stdout = process.StandardOutput.ReadToEnd();
353-
return new Secret(name, stdout.Split('\n')[^1]);
353+
return new Secret(name, stdout.Split('\n')[0]);
354354
#elif OS_LINUX
355355
using var process = new Process()
356356
{
@@ -370,8 +370,7 @@ public async Task<bool> DeleteAsync(string name)
370370
{
371371
return null;
372372
}
373-
var stdout = process.StandardOutput.ReadToEnd();
374-
return new Secret(name, stdout.Split('\n')[^1]);
373+
return new Secret(name, process.StandardOutput.ReadToEnd());
375374
#else
376375
return null;
377376
#endif
@@ -406,7 +405,7 @@ public async Task<bool> DeleteAsync(string name)
406405
StartInfo = new ProcessStartInfo()
407406
{
408407
FileName = "security",
409-
Arguments = $"find-generic-password -a default -w -s '{name}'",
408+
Arguments = $"find-generic-password -a default -s {name} -w",
410409
RedirectStandardOutput = true,
411410
RedirectStandardError = true,
412411
UseShellExecute = false,
@@ -419,8 +418,8 @@ public async Task<bool> DeleteAsync(string name)
419418
{
420419
return null;
421420
}
422-
var stdout = await process.StandardOutput.ReadToEndAsync();
423-
return new Secret(name, stdout.Split('\n')[^1]);
421+
var stdout = process.StandardOutput.ReadToEnd();
422+
return new Secret(name, stdout.Split('\n')[0]);
424423
#elif OS_LINUX
425424
using var process = new Process()
426425
{
@@ -440,8 +439,7 @@ public async Task<bool> DeleteAsync(string name)
440439
{
441440
return null;
442441
}
443-
var stdout = await process.StandardOutput.ReadToEndAsync();
444-
return new Secret(name, stdout.Split('\n')[^1]);
442+
return new Secret(name, await process.StandardOutput.ReadToEndAsync());
445443
#else
446444
return null;
447445
#endif
@@ -486,7 +484,7 @@ public bool Update(Secret secret)
486484
StartInfo = new ProcessStartInfo()
487485
{
488486
FileName = "security",
489-
Arguments = $"add-generic-password -a default -s '{secret.Name}' -w '{secret.Value}' -U",
487+
Arguments = $"add-generic-password -a default -s {secret.Name} -w \"{secret.Value}\" -U",
490488
RedirectStandardOutput = true,
491489
RedirectStandardError = true,
492490
UseShellExecute = false,
@@ -560,7 +558,7 @@ public async Task<bool> UpdateAsync(Secret secret)
560558
StartInfo = new ProcessStartInfo()
561559
{
562560
FileName = "security",
563-
Arguments = $"add-generic-password -a default -s '{secret.Name}' -w '{secret.Value}' -U",
561+
Arguments = $"add-generic-password -a default -s {secret.Name} -w \"{secret.Value}\" -U",
564562
RedirectStandardOutput = true,
565563
RedirectStandardError = true,
566564
UseShellExecute = false,

0 commit comments

Comments
 (0)