-
Notifications
You must be signed in to change notification settings - Fork 3
293 lines (272 loc) · 13.3 KB
/
Copy pathci.yml
File metadata and controls
293 lines (272 loc) · 13.3 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
name: CI
on:
push:
pull_request:
jobs:
test-offline:
# The full suite - including init, seed-pack install, selftest, store,
# search, shred, lock, unlock - runs with the runtime offline guard
# active: any attempt to create a network socket aborts the process.
# This is the executable proof of the zero-network claim.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ["3.11", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install (network allowed for dependency download only)
run: pip install -e .[dev]
- name: Test suite with offline guard active
env:
COMPARTMENT_ASSERT_OFFLINE: "1"
run: pytest tests/ -q --cov=compartment --cov-report=xml --cov-append
- name: Dashboard tests (loopback socket; offline guard intentionally off)
# `compartment dash` binds 127.0.0.1 by design and is never run under the
# offline guard, so its tests run here with the guard off.
run: pytest tests/test_dash.py -q --cov=compartment --cov-report=xml --cov-append
- name: Coverage to Codecov
# Never fail the build on a reporting service: the offline guard and the
# test suite are the gate here, not the upload.
uses: codecov/codecov-action@v5
continue-on-error: true
with:
files: ./coverage.xml
flags: ${{ matrix.os }}-py${{ matrix.python }}
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
linux-panel:
# Linux has no notification area worth relying on, so it draws the same
# panel as an ordinary window. "It opens and stays open" is the failure
# unit tests cannot see, and this is a real Linux box, so it is checked
# here on a virtual display.
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: The Tk package, which pip cannot install
run: sudo apt-get update && sudo apt-get install -y python3-tk xvfb
- name: Install
run: pip install -e .[dev]
- name: A vault to look at
run: |
compartment init --passphrase ci-only-not-a-secret --no-app
compartment store --source "from chat" "Deploys go out Tuesday and Thursday mornings."
compartment store --source "from chat" "Staging runs Postgres 16 behind pgbouncer."
- name: The panel, without a display (what it would say)
run: compartment panel --self-check
- name: It opens and stays open
run: |
xvfb-run -a --server-args="-screen 0 1280x1024x24" \
compartment panel --show >/dev/null 2>&1 &
PID=$!
sleep 18
if ! kill -0 $PID 2>/dev/null; then
echo "the panel exited on its own"; exit 1
fi
echo "still running after 18s"
kill $PID 2>/dev/null || true
pkill -f "compartment.*panel" || true
sleep 2
- name: A second copy stands down instead of opening a second window
run: |
# Detached from the step's stdout, or the runner waits on it.
xvfb-run -a --server-args="-screen 0 1280x1024x24" \
compartment panel --show >/dev/null 2>&1 &
sleep 12
set +e
out=$(timeout 30 xvfb-run -a compartment panel --show 2>&1)
rc=$?
set -e
echo "second copy rc=$rc said: $out"
pkill -f "compartment.*panel" || true
if [ "$rc" -ne 0 ]; then
echo "the second copy did not exit; it opened a window of its own"
exit 1
fi
echo "$out" | grep -q "already open" || {
echo "the second copy exited without saying why"; exit 1; }
- name: It puts itself in the applications menu
run: |
compartment panel --login on
test -f "$HOME/.local/share/applications/compartment.desktop" \
|| { echo "no desktop entry"; exit 1; }
cat "$HOME/.local/share/applications/compartment.desktop"
grep -q "^Type=Application" "$HOME/.local/share/applications/compartment.desktop"
grep -q "^Exec=" "$HOME/.local/share/applications/compartment.desktop"
desktop-file-validate "$HOME/.local/share/applications/compartment.desktop" \
|| echo "note: desktop-file-validate not installed or reported style warnings"
compartment panel --login off
test ! -f "$HOME/.local/share/applications/compartment.desktop"
windows-tray:
# The tray app has to actually start on Windows, and "it starts" is the
# failure the unit tests cannot see: the macOS app once launched, ran, and
# showed nothing at all. The runner has a desktop session, so the app can
# really be drawn here - the screenshot is uploaded so a human can look at
# the panel without owning a Windows machine.
runs-on: windows-latest
# Nothing here should take twenty minutes. Without a cap, one command
# waiting on a passphrase prompt that no one can answer held a runner
# for 2h40m: init crashed on a non-ASCII character, left the vault
# locked, and the next command sat asking for input.
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install with the tray extra
run: pip install -e .[tray]
- name: A vault to look at
# Five ordinary-looking memories, so the screenshot shows the panel
# doing its job instead of an empty list.
run: |
compartment init --passphrase ci-only-not-a-secret
compartment store --source "from chat" "Deploys go out Tuesday and Thursday mornings, never on a Friday."
compartment store --source "from chat" "Approved (answered OK): send the release notes to the team now?"
compartment store --source "from chat" "Staging runs Postgres 16 behind pgbouncer on port 6432."
compartment store --source "from chat" "Prefers spaces over tabs everywhere except Go."
compartment store --source "from chat" "This laptop has 16 GB, so keep parallel jobs at 4."
python -c "from compartment import menubar as m; m.set_setting(m.default_vault(), 'capture_hook', True)"
- name: The panel, without a window (what it would say)
run: compartment tray --self-check
- name: Changing the passphrase has to work here too
# The apps grew a Change password button, and Windows is the side
# with no one sitting in front of it. Prove the whole round trip:
# the new passphrase opens the vault and the old one does not.
shell: pwsh
run: |
"Locksmith-ci-only" | compartment rekey --new-passphrase-stdin
if ($LASTEXITCODE -ne 0) { Write-Error "rekey failed"; exit 1 }
compartment lock
$old = "ci-only-not-a-secret" | compartment unlock --passphrase-stdin 2>&1
if ($LASTEXITCODE -eq 0) { Write-Error "the OLD passphrase still opens it"; exit 1 }
Write-Host "old passphrase correctly rejected"
"Locksmith-ci-only" | compartment unlock --passphrase-stdin
if ($LASTEXITCODE -ne 0) { Write-Error "the NEW passphrase does not open it"; exit 1 }
Write-Host "new passphrase accepted"
- name: A bigger screen than the 1024x768 default
shell: pwsh
continue-on-error: true
run: |
Set-DisplayResolution -Width 1920 -Height 1080 -Force
Start-Sleep -Seconds 3
- name: A second copy stands down instead of adding another icon
# `compartment init` above already started the tray app, which is the
# whole point of it. So a launch now must hand off and exit 0, and
# the copy that was already there must still be running afterwards.
shell: pwsh
run: |
$before = @(Get-Process compartment -ErrorAction SilentlyContinue).Count
if ($before -lt 1) { Write-Error "init did not start the tray app"; exit 1 }
compartment tray --show
if ($LASTEXITCODE -ne 0) { Write-Error "the second copy failed instead of standing down"; exit 1 }
$after = @(Get-Process compartment -ErrorAction SilentlyContinue).Count
if ($after -ne $before) { Write-Error "second copy changed the count: $before -> $after"; exit 1 }
Write-Host "one copy before, one copy after"
Get-Process compartment -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2
- name: Start the tray app and prove it is still alive
# At the DEFAULT scale, which is what nearly everyone runs. The
# high-DPI path below must not be the only one exercised.
shell: pwsh
run: |
$p = Start-Process -PassThru -FilePath compartment -ArgumentList 'tray','--show'
Start-Sleep -Seconds 25
if ($p.HasExited) {
Write-Error "the tray app exited on its own with $($p.ExitCode)"
exit 1
}
Write-Host "still running after 25s (pid $($p.Id))"
Stop-Process -Id $p.Id -Force
Start-Sleep -Seconds 2
- name: Start it again at 2x, the way a high-DPI laptop draws it
# The runner's display is 96 DPI, so this is the only way to see the
# scaled path render here - and it is what produces a screenshot
# sharp enough to sit beside the Retina macOS one.
shell: pwsh
env:
COMPARTMENT_UI_SCALE: "2"
run: |
$p = Start-Process -PassThru -FilePath compartment -ArgumentList 'tray','--show'
Start-Sleep -Seconds 25
if ($p.HasExited) {
Write-Error "the tray app exited at 2x scale with $($p.ExitCode)"
exit 1
}
Write-Host "still running at 2x after 25s (pid $($p.Id))"
- name: Screenshot the panel, cropped by Windows itself
shell: pwsh
run: |
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class NativeWin {
[DllImport("user32.dll", CharSet=CharSet.Unicode)]
public static extern IntPtr FindWindow(string cls, string name);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr h);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left, Top, Right, Bottom; }
[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(
IntPtr h, int attr, out RECT r, int size);
}
"@
# The whole desktop first, so this step always produces something
# even if the precise capture below cannot find the window.
$b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
Write-Host "desktop: $($b.Width)x$($b.Height)"
$full = New-Object System.Drawing.Bitmap $b.Width, $b.Height
$g2 = [System.Drawing.Graphics]::FromImage($full)
$g2.CopyFromScreen($b.Location, [System.Drawing.Point]::Empty, $b.Size)
$full.Save("$PWD\tray.png", [System.Drawing.Imaging.ImageFormat]::Png)
# Ask the process for its own window rather than FindWindow: passing
# $null for a string parameter through P/Invoke marshals as "" in
# PowerShell, which then matches a window class literally named
# empty and finds nothing.
Write-Host "windows with a title:"
Get-Process | Where-Object { $_.MainWindowHandle -ne 0 } |
ForEach-Object { Write-Host " $($_.ProcessName): '$($_.MainWindowTitle)'" }
$proc = Get-Process |
Where-Object { $_.MainWindowHandle -ne 0 -and
($_.ProcessName -like "*compartment*" -or
$_.MainWindowTitle -eq "Compartment") } |
Select-Object -First 1
if (-not $proc) {
Write-Warning "no Compartment window found; desktop shot only"
exit 0
}
$h = $proc.MainWindowHandle
[void][NativeWin]::SetForegroundWindow($h)
Start-Sleep -Milliseconds 900
$r = New-Object NativeWin+RECT
# 9 = DWMWA_EXTENDED_FRAME_BOUNDS: the real frame, without the
# invisible resize border a plain GetWindowRect includes.
[void][NativeWin]::DwmGetWindowAttribute($h, 9, [ref]$r, 16)
$w = $r.Right - $r.Left; $ht = $r.Bottom - $r.Top
if ($w -lt 50 -or $ht -lt 50) {
Write-Warning "implausible window rect ${w}x${ht}; desktop shot only"
exit 0
}
$bmp = New-Object System.Drawing.Bitmap $w, $ht
$g = [System.Drawing.Graphics]::FromImage($bmp)
$g.CopyFromScreen($r.Left, $r.Top, 0, 0,
(New-Object System.Drawing.Size $w, $ht))
$bmp.Save("$PWD\compartment-windows-panel.png",
[System.Drawing.Imaging.ImageFormat]::Png)
Write-Host "panel: ${w}x${ht}"
- uses: actions/upload-artifact@v4
with:
name: windows-tray-screenshot
path: |
compartment-windows-panel.png
tray.png