Skip to content

Commit 537298f

Browse files
authored
Added in ci-cd deploy & discord bot tweaks (#11)
* Added in ci-cd deploy (untested) * Tweaked rfid scanner to support emulator & pcsc protocol * Whoops, rematch env secret names * Updated html description * Removed typo checking from pre-commits * Fixed LE decimals to hex checker on rfid * Fixed deploy server name * updated notification system embedded into session (stops potential double notifications * Updated naming for ci-cd
1 parent b405c83 commit 537298f

File tree

29 files changed

+889
-151
lines changed

29 files changed

+889
-151
lines changed

.github/workflows/ci.yml

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,16 @@ jobs:
119119
if: matrix.archive == 'tar.gz'
120120
run: |
121121
mkdir -p staging
122-
cp target/${{ matrix.target }}/release/main staging/timekeeper-server
123-
chmod +x staging/timekeeper-server
122+
cp target/${{ matrix.target }}/release/main staging/server
123+
chmod +x staging/server
124124
cd staging
125125
tar -czf ../${{ matrix.artifact }}.tar.gz .
126126
127127
- name: Prepare archive (zip)
128128
if: matrix.archive == 'zip'
129129
run: |
130130
mkdir -p staging
131-
cp target/${{ matrix.target }}/release/main.exe staging/timekeeper-server.exe
131+
cp target/${{ matrix.target }}/release/main.exe staging/server.exe
132132
cd staging
133133
zip -r ../${{ matrix.artifact }}.zip .
134134
@@ -245,6 +245,94 @@ jobs:
245245
path: ${{ matrix.artifact-path }}
246246
retention-days: 5
247247

248+
# ── Deploy to production server ──────────────────────────────────────
249+
deploy:
250+
name: Deploy
251+
needs: [build-server, build-web-client]
252+
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/ci-cd')
253+
runs-on: ubuntu-latest
254+
environment: production
255+
steps:
256+
- name: Download server artifact
257+
uses: actions/download-artifact@v4
258+
with:
259+
name: timekeeper-server-linux-x86_64
260+
261+
- name: Extract server binary
262+
run: |
263+
tar -xzf timekeeper-server-linux-x86_64.tar.gz
264+
ls -la
265+
266+
- name: Download web client
267+
uses: actions/download-artifact@v4
268+
with:
269+
name: web-client
270+
path: client/build/web
271+
272+
- name: Display downloaded files
273+
run: |
274+
ls -la
275+
ls -la client/build/web
276+
277+
# Join Tailscale network
278+
- name: Tailscale
279+
uses: tailscale/github-action@v4
280+
with:
281+
oauth-client-id: ${{ secrets.GRIM_TS_OAUTH_CLIENT_ID }}
282+
oauth-secret: ${{ secrets.GRIM_TS_OAUTH_SECRET }}
283+
tags: tag:ci
284+
285+
- name: Stop server service
286+
uses: appleboy/ssh-action@v1.0.3
287+
with:
288+
host: ${{ secrets.SSH_HOST }}
289+
username: ${{ secrets.SSH_USERNAME }}
290+
key: ${{ secrets.SSH_PRIVATE_KEY }}
291+
port: ${{ secrets.SSH_PORT }}
292+
script: |
293+
systemctl stop server
294+
295+
- name: Deploy to server
296+
uses: appleboy/scp-action@v1
297+
with:
298+
host: ${{ secrets.SSH_HOST }}
299+
username: ${{ secrets.SSH_USERNAME }}
300+
key: ${{ secrets.SSH_PRIVATE_KEY }}
301+
port: ${{ secrets.SSH_PORT }}
302+
source: "server,client"
303+
target: "/root/"
304+
overwrite: true
305+
306+
- name: Configure and restart server
307+
uses: appleboy/ssh-action@v1.0.3
308+
with:
309+
host: ${{ secrets.SSH_HOST }}
310+
username: ${{ secrets.SSH_USERNAME }}
311+
key: ${{ secrets.SSH_PRIVATE_KEY }}
312+
port: ${{ secrets.SSH_PORT }}
313+
script: |
314+
chmod +x /root/server
315+
cat > /etc/systemd/system/server.service << 'SVCEOF'
316+
[Unit]
317+
Description=TimeKeeper Server
318+
After=network.target
319+
320+
[Service]
321+
Type=simple
322+
WorkingDirectory=/root
323+
ExecStart=/root/server
324+
Restart=always
325+
RestartSec=10
326+
327+
Environment="RUST_LOG=info"
328+
329+
[Install]
330+
WantedBy=default.target
331+
SVCEOF
332+
systemctl daemon-reload
333+
systemctl restart server
334+
systemctl status server
335+
248336
# ── Create GitHub Release ───────────────────────────────────────────
249337
release:
250338
name: Create Release

.pre-commit-config.yaml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,3 @@ repos:
3636
# Git
3737
- id: check-symlinks
3838
- id: destroyed-symlinks
39-
- repo: https://github.com/crate-ci/typos
40-
rev: v1.40.0
41-
hooks:
42-
- id: typos
43-
args: []
44-
stages: [pre-commit]
45-
exclude: ^client/(ios|macos|android|windows|linux)/|client/lib/generated/.*\.(pb|pbjson)\.dart$
46-
- id: typos
47-
name: commit-msg-typos
48-
stages: [commit-msg]

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# TimeKeeper
2+
3+
A time tracking and sign-in management system built for FRC (FIRST Robotics Competition) teams. Track team member attendance across sessions and locations with support for RFID check-in/out, Discord integration, and multi-platform clients.
4+
5+
## Features
6+
7+
- **Session Management** - Create and manage timed sessions at configurable locations
8+
- **RFID Check-In/Out** - Supports both PCSC smart card readers and keyboard-emulating RFID readers
9+
- **Kiosk Mode** - Dedicated fullscreen mode for check-in stations
10+
- **Discord Bot** - Session reminders, attendance commands, leaderboards, and member name syncing
11+
- **Schedule Import** - Import sessions from CSV or ICS (iCalendar) files
12+
- **Multi-Platform Clients** - Desktop (Linux, Windows, macOS), Android, and Web
13+
- **Real-Time Sync** - gRPC streaming keeps all connected clients in sync

client/lib/generated/api/settings.pb.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,14 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
120120
$core.String? discordBotToken,
121121
$core.String? discordGuildId,
122122
$core.String? discordChannelId,
123+
@$core.Deprecated('This field is deprecated.')
123124
$fixnum.Int64? discordReminderMins,
124125
$core.bool? discordSelfLinkEnabled,
125126
$core.bool? discordNameSyncEnabled,
127+
$fixnum.Int64? discordStartReminderMins,
128+
$fixnum.Int64? discordEndReminderMins,
129+
$core.String? discordStartReminderMessage,
130+
$core.String? discordEndReminderMessage,
126131
}) {
127132
final result = create();
128133
if (nextSessionThresholdSecs != null)
@@ -136,6 +141,14 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
136141
result.discordSelfLinkEnabled = discordSelfLinkEnabled;
137142
if (discordNameSyncEnabled != null)
138143
result.discordNameSyncEnabled = discordNameSyncEnabled;
144+
if (discordStartReminderMins != null)
145+
result.discordStartReminderMins = discordStartReminderMins;
146+
if (discordEndReminderMins != null)
147+
result.discordEndReminderMins = discordEndReminderMins;
148+
if (discordStartReminderMessage != null)
149+
result.discordStartReminderMessage = discordStartReminderMessage;
150+
if (discordEndReminderMessage != null)
151+
result.discordEndReminderMessage = discordEndReminderMessage;
139152
return result;
140153
}
141154

@@ -159,6 +172,10 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
159172
..aInt64(5, _omitFieldNames ? '' : 'discordReminderMins')
160173
..aOB(6, _omitFieldNames ? '' : 'discordSelfLinkEnabled')
161174
..aOB(7, _omitFieldNames ? '' : 'discordNameSyncEnabled')
175+
..aInt64(8, _omitFieldNames ? '' : 'discordStartReminderMins')
176+
..aInt64(9, _omitFieldNames ? '' : 'discordEndReminderMins')
177+
..aOS(10, _omitFieldNames ? '' : 'discordStartReminderMessage')
178+
..aOS(11, _omitFieldNames ? '' : 'discordEndReminderMessage')
162179
..hasRequiredFields = false;
163180

164181
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -217,12 +234,16 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
217234
@$pb.TagNumber(4)
218235
void clearDiscordChannelId() => $_clearField(4);
219236

237+
@$core.Deprecated('This field is deprecated.')
220238
@$pb.TagNumber(5)
221239
$fixnum.Int64 get discordReminderMins => $_getI64(4);
240+
@$core.Deprecated('This field is deprecated.')
222241
@$pb.TagNumber(5)
223242
set discordReminderMins($fixnum.Int64 value) => $_setInt64(4, value);
243+
@$core.Deprecated('This field is deprecated.')
224244
@$pb.TagNumber(5)
225245
$core.bool hasDiscordReminderMins() => $_has(4);
246+
@$core.Deprecated('This field is deprecated.')
226247
@$pb.TagNumber(5)
227248
void clearDiscordReminderMins() => $_clearField(5);
228249

@@ -243,6 +264,42 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
243264
$core.bool hasDiscordNameSyncEnabled() => $_has(6);
244265
@$pb.TagNumber(7)
245266
void clearDiscordNameSyncEnabled() => $_clearField(7);
267+
268+
@$pb.TagNumber(8)
269+
$fixnum.Int64 get discordStartReminderMins => $_getI64(7);
270+
@$pb.TagNumber(8)
271+
set discordStartReminderMins($fixnum.Int64 value) => $_setInt64(7, value);
272+
@$pb.TagNumber(8)
273+
$core.bool hasDiscordStartReminderMins() => $_has(7);
274+
@$pb.TagNumber(8)
275+
void clearDiscordStartReminderMins() => $_clearField(8);
276+
277+
@$pb.TagNumber(9)
278+
$fixnum.Int64 get discordEndReminderMins => $_getI64(8);
279+
@$pb.TagNumber(9)
280+
set discordEndReminderMins($fixnum.Int64 value) => $_setInt64(8, value);
281+
@$pb.TagNumber(9)
282+
$core.bool hasDiscordEndReminderMins() => $_has(8);
283+
@$pb.TagNumber(9)
284+
void clearDiscordEndReminderMins() => $_clearField(9);
285+
286+
@$pb.TagNumber(10)
287+
$core.String get discordStartReminderMessage => $_getSZ(9);
288+
@$pb.TagNumber(10)
289+
set discordStartReminderMessage($core.String value) => $_setString(9, value);
290+
@$pb.TagNumber(10)
291+
$core.bool hasDiscordStartReminderMessage() => $_has(9);
292+
@$pb.TagNumber(10)
293+
void clearDiscordStartReminderMessage() => $_clearField(10);
294+
295+
@$pb.TagNumber(11)
296+
$core.String get discordEndReminderMessage => $_getSZ(10);
297+
@$pb.TagNumber(11)
298+
set discordEndReminderMessage($core.String value) => $_setString(10, value);
299+
@$pb.TagNumber(11)
300+
$core.bool hasDiscordEndReminderMessage() => $_has(10);
301+
@$pb.TagNumber(11)
302+
void clearDiscordEndReminderMessage() => $_clearField(11);
246303
}
247304

248305
class UpdateSettingsResponse extends $pb.GeneratedMessage {

client/lib/generated/api/settings.pbjson.dart

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ const UpdateSettingsRequest$json = {
6969
'3': 5,
7070
'4': 1,
7171
'5': 3,
72-
'10': 'discordReminderMins'
72+
'8': {'3': true},
73+
'10': 'discordReminderMins',
7374
},
7475
{
7576
'1': 'discord_self_link_enabled',
@@ -85,6 +86,34 @@ const UpdateSettingsRequest$json = {
8586
'5': 8,
8687
'10': 'discordNameSyncEnabled'
8788
},
89+
{
90+
'1': 'discord_start_reminder_mins',
91+
'3': 8,
92+
'4': 1,
93+
'5': 3,
94+
'10': 'discordStartReminderMins'
95+
},
96+
{
97+
'1': 'discord_end_reminder_mins',
98+
'3': 9,
99+
'4': 1,
100+
'5': 3,
101+
'10': 'discordEndReminderMins'
102+
},
103+
{
104+
'1': 'discord_start_reminder_message',
105+
'3': 10,
106+
'4': 1,
107+
'5': 9,
108+
'10': 'discordStartReminderMessage'
109+
},
110+
{
111+
'1': 'discord_end_reminder_message',
112+
'3': 11,
113+
'4': 1,
114+
'5': 9,
115+
'10': 'discordEndReminderMessage'
116+
},
88117
],
89118
};
90119

@@ -93,11 +122,15 @@ final $typed_data.Uint8List updateSettingsRequestDescriptor = $convert.base64Dec
93122
'ChVVcGRhdGVTZXR0aW5nc1JlcXVlc3QSPQobbmV4dF9zZXNzaW9uX3RocmVzaG9sZF9zZWNzGA'
94123
'EgASgDUhhuZXh0U2Vzc2lvblRocmVzaG9sZFNlY3MSKgoRZGlzY29yZF9ib3RfdG9rZW4YAiAB'
95124
'KAlSD2Rpc2NvcmRCb3RUb2tlbhIoChBkaXNjb3JkX2d1aWxkX2lkGAMgASgJUg5kaXNjb3JkR3'
96-
'VpbGRJZBIsChJkaXNjb3JkX2NoYW5uZWxfaWQYBCABKAlSEGRpc2NvcmRDaGFubmVsSWQSMgoV'
97-
'ZGlzY29yZF9yZW1pbmRlcl9taW5zGAUgASgDUhNkaXNjb3JkUmVtaW5kZXJNaW5zEjkKGWRpc2'
98-
'NvcmRfc2VsZl9saW5rX2VuYWJsZWQYBiABKAhSFmRpc2NvcmRTZWxmTGlua0VuYWJsZWQSOQoZ'
99-
'ZGlzY29yZF9uYW1lX3N5bmNfZW5hYmxlZBgHIAEoCFIWZGlzY29yZE5hbWVTeW5jRW5hYmxlZA'
100-
'==');
125+
'VpbGRJZBIsChJkaXNjb3JkX2NoYW5uZWxfaWQYBCABKAlSEGRpc2NvcmRDaGFubmVsSWQSNgoV'
126+
'ZGlzY29yZF9yZW1pbmRlcl9taW5zGAUgASgDQgIYAVITZGlzY29yZFJlbWluZGVyTWlucxI5Ch'
127+
'lkaXNjb3JkX3NlbGZfbGlua19lbmFibGVkGAYgASgIUhZkaXNjb3JkU2VsZkxpbmtFbmFibGVk'
128+
'EjkKGWRpc2NvcmRfbmFtZV9zeW5jX2VuYWJsZWQYByABKAhSFmRpc2NvcmROYW1lU3luY0VuYW'
129+
'JsZWQSPQobZGlzY29yZF9zdGFydF9yZW1pbmRlcl9taW5zGAggASgDUhhkaXNjb3JkU3RhcnRS'
130+
'ZW1pbmRlck1pbnMSOQoZZGlzY29yZF9lbmRfcmVtaW5kZXJfbWlucxgJIAEoA1IWZGlzY29yZE'
131+
'VuZFJlbWluZGVyTWlucxJDCh5kaXNjb3JkX3N0YXJ0X3JlbWluZGVyX21lc3NhZ2UYCiABKAlS'
132+
'G2Rpc2NvcmRTdGFydFJlbWluZGVyTWVzc2FnZRI/ChxkaXNjb3JkX2VuZF9yZW1pbmRlcl9tZX'
133+
'NzYWdlGAsgASgJUhlkaXNjb3JkRW5kUmVtaW5kZXJNZXNzYWdl');
101134

102135
@$core.Deprecated('Use updateSettingsResponseDescriptor instead')
103136
const UpdateSettingsResponse$json = {

0 commit comments

Comments
 (0)