Skip to content

Commit 69336e3

Browse files
committed
deployment to railway
1 parent 193f111 commit 69336e3

42 files changed

Lines changed: 307 additions & 355 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env

Lines changed: 0 additions & 2 deletions
This file was deleted.

.github/workflows/deploy.yml

Lines changed: 107 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -4,97 +4,77 @@ on:
44
push:
55
branches:
66
- main
7+
paths:
8+
- "**.rs"
9+
- "**.vue"
10+
- "**.ts"
11+
- ".github/workflows/deploy.yaml"
12+
- "**Dockerfile"
713
workflow_dispatch:
814

915
jobs:
10-
setup:
16+
deploy:
1117
runs-on: ubuntu-latest
1218

19+
permissions:
20+
contents: read
21+
id-token: write
22+
1323
steps:
1424
- name: Checkout code
1525
uses: actions/checkout@v4
1626

17-
- name: Set up Rust
18-
uses: dtolnay/rust-toolchain@master
27+
- name: Login to Google Cloud
28+
id: gcp-auth
29+
uses: google-github-actions/auth@v2
1930
with:
20-
toolchain: nightly-2025-11-15
21-
targets: wasm32-unknown-unknown
22-
23-
- name: Install wasm-pack
24-
uses: jetli/[email protected]
31+
token_format: "access_token"
32+
project_id: ${{ secrets.GCP_PROJECT_ID }}
33+
workload_identity_provider: ${{ secrets.GCP_WLIF_PROVIDER }}
34+
service_account: ${{ secrets.GCP_SA }}
35+
access_token_lifetime: 600s
2536

26-
- name: Install Bun
27-
uses: oven-sh/setup-bun@v2
37+
- name: Login to Docker Hub
38+
uses: docker/login-action@v2
39+
with:
40+
username: ${{ secrets.DOCKER_USERNAME }}
41+
password: ${{ secrets.DOCKER_PASSWORD }}
2842

29-
- name: Restore Cargo cache
43+
- name: Restore build cache
3044
uses: actions/cache/restore@v4
31-
id: cargo-cache
45+
id: build-cache
3246
with:
3347
path: |
34-
~/.cargo/registry
35-
~/.cargo/git
36-
~/.cargo/bin
3748
src-api/target
3849
src-crypto/target
39-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
40-
restore-keys: ${{ runner.os }}-cargo-
41-
42-
- name: Install dependencies and run postinstall
43-
run: bun i
44-
env:
45-
DATABASE_URL: ${{ secrets.DATABASE_URL }}
46-
47-
- name: Save Cargo cache
48-
if: always()
49-
uses: actions/cache/save@v4
50-
with:
51-
path: |
50+
src-macro/target
51+
~/.cargo/bin
5252
~/.cargo/registry
5353
~/.cargo/git
54-
~/.cargo/bin
55-
src-api/target
56-
src-crypto/target
57-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
58-
59-
- name: Upload workspace
60-
uses: actions/upload-artifact@v4
61-
with:
62-
name: workspace
63-
path: |
64-
.
65-
!.git
66-
retention-days: 1
67-
68-
api:
69-
needs: setup
70-
runs-on: ubuntu-latest
71-
72-
permissions:
73-
contents: read
74-
id-token: write
75-
76-
steps:
77-
- name: Download workspace
78-
uses: actions/download-artifact@v4
79-
with:
80-
name: workspace
54+
node_modules
55+
key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/package-lock.json') }}
56+
restore-keys: |
57+
${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }}
58+
${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}
59+
${{ runner.os }}-build-
8160
8261
- name: Set up Rust
62+
if: steps.build-cache.outputs.cache-hit != 'true'
8363
uses: dtolnay/rust-toolchain@master
8464
with:
8565
toolchain: nightly-2025-11-15
66+
targets: wasm32-unknown-unknown
67+
components: rustfmt
8668

87-
- name: Restore Cargo cache
88-
uses: actions/cache/restore@v4
89-
with:
90-
path: |
91-
~/.cargo/registry
92-
~/.cargo/git
93-
~/.cargo/bin
94-
src-api/target
95-
src-crypto/target
96-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
97-
restore-keys: ${{ runner.os }}-cargo-
69+
- name: Write to .env
70+
run: |
71+
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > src-api/.env
72+
73+
- name: Install wasm-pack
74+
uses: jetli/[email protected]
75+
76+
- name: Install Bun
77+
uses: oven-sh/setup-bun@v2
9878

9979
- name: Check if sqlx CLI is cached
10080
id: checkfile
@@ -113,92 +93,84 @@ jobs:
11393
env:
11494
DATABASE_URL: ${{ secrets.DATABASE_URL }}
11595

116-
- name: Build API (release)
117-
run: cargo build --release --locked
118-
working-directory: src-api
96+
- name: Install dependencies and run postinstall
97+
run: |
98+
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > src-api/.env
99+
bun i
119100
env:
120101
DATABASE_URL: ${{ secrets.DATABASE_URL }}
121102

122-
- name: Login to Google Cloud
123-
id: gcp-auth
124-
uses: google-github-actions/auth@v2
125-
with:
126-
token_format: "access_token"
127-
project_id: ${{ secrets.GCP_PROJECT_ID }}
128-
workload_identity_provider: ${{ secrets.GCP_WLIF_PROVIDER }}
129-
service_account: ${{ secrets.GCP_SA }}
130-
access_token_lifetime: 600s
103+
- name: Build API
104+
working-directory: src-api
105+
run: cargo build --release --locked --no-default-features
106+
env:
107+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
131108

132-
- name: Login to Docker Hub
133-
uses: docker/login-action@v2
134-
with:
135-
username: ${{ secrets.DOCKER_USERNAME }}
136-
password: ${{ secrets.DOCKER_PASSWORD }}
109+
- name: Build API image
110+
run: docker build . -t onlycs/attendance:api -f Dockerfile.api
137111

138-
- name: Login to Artifact Registry
139-
uses: docker/login-action@v2
140-
with:
141-
registry: us-central1-docker.pkg.dev
142-
username: oauth2accesstoken
143-
password: ${{ steps.gcp-auth.outputs.access_token }}
112+
- name: Generate static site
113+
run: bun generate
144114

145-
- name: Build Docker image
115+
- name: Build API (include static assets)
146116
working-directory: src-api
147-
run: |
148-
docker build . -t onlycs/attendance:latest -t us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/attendance/attendance:latest
117+
run: cargo build --release --locked
118+
env:
119+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
120+
121+
- name: Build standard image
122+
run: docker build . -t onlycs/attendance:latest
149123

150-
- name: Push Docker image
124+
- name: Push Docker images
151125
run: |
152-
docker push us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/attendance/attendance:latest
126+
docker push onlycs/attendance:api
153127
docker push onlycs/attendance:latest
154128
155-
- name: Deploy to Cloud Run
129+
- name: Regenerate frontend for ourselves
156130
run: |
157-
gcloud run deploy attendance \
158-
--image=us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/attendance/attendance:latest \
159-
--platform=managed \
160-
--region=us-central1 \
161-
--allow-unauthenticated
162-
163-
static:
164-
needs: setup
165-
runs-on: ubuntu-latest
166-
167-
permissions:
168-
contents: read
169-
id-token: write
170-
171-
steps:
172-
- name: Download workspace
173-
uses: actions/download-artifact@v4
174-
with:
175-
name: workspace
176-
177-
- name: Install Bun
178-
uses: oven-sh/setup-bun@v2
179-
180-
- name: Install dependencies
181-
run: bun i --frozen-lockfile
182-
env:
183-
DATABASE_URL: ${{ secrets.DATABASE_URL }}
184-
185-
- name: Generate static site
186-
run: bun generate
131+
rm -rf .output
132+
echo "NUXT_PUBLIC_API_URL=${{ secrets.API_URL }}" > .env
133+
bun generate
187134
env:
188135
API_URL: ${{ secrets.API_URL }}
189-
DATABASE_URL: ${{ secrets.DATABASE_URL }}
190-
191-
- name: Login to Google Cloud
192-
uses: google-github-actions/auth@v2
193-
with:
194-
project_id: ${{ secrets.GCP_PROJECT_ID }}
195-
workload_identity_provider: ${{ secrets.GCP_WLIF_PROVIDER }}
196-
service_account: ${{ secrets.GCP_SA }}
197136

198-
- name: Deploy to Firebase Hosting
137+
- name: Deploy API to GCE
138+
run: |
139+
gcloud compute ssh attendance-vm \
140+
--zone=us-central1-a \
141+
--tunnel-through-iap \
142+
--command="
143+
sudo docker pull onlycs/attendance:api &&
144+
sudo docker stop attendance || true &&
145+
sudo docker rm attendance || true &&
146+
sudo docker run -d \
147+
--name attendance \
148+
--restart always \
149+
--network=attendance-net \
150+
-e DATABASE_URL='${{ secrets.DATABASE_URL }}' \
151+
-e JWT_SECRET='${{ secrets.JWT_SECRET }}' \
152+
-p 8080:8080 \
153+
onlycs/attendance:api
154+
"
155+
156+
- name: Deploy Static to Firebase Hosting
199157
uses: FirebaseExtended/action-hosting-deploy@v0
200158
with:
201159
repoToken: ${{ secrets.GITHUB_TOKEN }}
202-
firebaseServiceAccount: ${{ secrets.GCP_SA }}
160+
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_RIVER_WAVE_445602_N7 }}
203161
channelId: live
204162
projectId: ${{ secrets.GCP_PROJECT_ID }}
163+
164+
- name: Save Build cache
165+
if: always()
166+
uses: actions/cache/save@v4
167+
with:
168+
path: |
169+
src-api/target
170+
src-crypto/target
171+
src-macro/target
172+
~/.cargo/bin
173+
~/.cargo/registry
174+
~/.cargo/git
175+
node_modules
176+
key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/package-lock.json') }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ app/wasm/*
3030
app/utils/api/hey/*
3131
app/utils/api/client.ts
3232
src-api/openapi.yml
33+
34+
.firebaserc
35+
.firebase

Dockerfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM rustlang/rust:nightly
2+
3+
WORKDIR /var/www/attendance
4+
COPY ./src-api/target/release/attendance-api .
5+
COPY start.sh .
6+
COPY ./.output/public ./static
7+
RUN chmod +x start.sh
8+
9+
CMD ["./start.sh"]

Dockerfile.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM rustlang/rust:nightly
2+
3+
WORKDIR /var/www/attendance
4+
COPY ./start.sh .
5+
COPY ./src-api/target/release/attendance-api .
6+
RUN chmod +x start.sh
7+
8+
CMD ["./start.sh"]

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ wasm:
88
@echo "=== Building WASM package"
99
cd src-crypto && rm -rf pkg && wasm-pack build --target web --release
1010
@echo "=== Copying package files"
11-
rm -rf app/wasm
12-
cp -r src-crypto/pkg app/wasm
11+
rm -rf public/wasm
12+
cp -r src-crypto/pkg public/wasm
1313
@echo "=== Patching workerHelpers.js files"
14-
sed -i 's|\.\./\.\./\.\.|../../../attendance_crypto.js|g' app/wasm/snippets/*/src/workerHelpers.js
14+
sed -i 's|\.\./\.\./\.\.|../../../attendance_crypto.js|g' public/wasm/snippets/*/src/workerHelpers.js
1515

1616
openapi:
1717
@echo "=== Generating OpenAPI spec"

README.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
11
# Attendance
22

3-
This was such a big update, readme incoming soon!
3+
An FRC-focused attendance tracker.
4+
5+
## Features
6+
7+
- **QR Code Logins**: Students can scan a QR code to sign in
8+
- **Fully encrypted**: All student information is fully encrypted, complying with NYS-ED laws (or so I'm told)
9+
- **Admin Dashboard and Editor**: Admins can view and edit attendance records, student information, and more.
10+
- **Hour Types**: Record build season, learning days, offseason, or outreach hours separately.
11+
- **API**: Includes a REST API with OpenAPI documentation at `/api/docs` or `/api/openapi.yml`
12+
- **Mobile Friendly**: Includes a seperate mobile interface for both students and admins
13+
14+
## Deployment
15+
16+
1. Deploy the docker container. Railway is by far the easiest way to do this.
17+
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/wWtiqs?referralCode=Y3VMtD&utm_medium=integration&utm_source=template&utm_campaign=generic)
18+
1. To set the timezone (`TZ`), use the [database timezone format](https://wikipedia.org/wiki/List_of_tz_database_time_zones).
19+
2. Set the `JWT_SECRET` to a large (>32 character), random password. You do not need to remember this.
20+
21+
2. On your app's dashboard, select `onlycs/attendance`, and then `Settings` > `Networking`. If your team has a domain
22+
(e.g. `attendance.team2791.org`), add a `Custom Domain`. Otherwise, generate a domain.
23+
24+
<img src="https://github.com/onlycs/attendance/blob/main/assets/deploy-railway-url.png?raw=true" alt="URL Settings" width="69%">
25+
26+
3. Visit `https://<your-domain>/onboard`, and click `Get a new setup token`. To view this, in Railway, select `onlycs/attendance`, and then `Deployments` > `View Logs`.
27+
28+
<p align="center">
29+
<img src="https://github.com/onlycs/attendance/blob/main/assets/deploy-railway-logs.png?raw=true" alt="Logs" width="69%">
30+
<img src="https://github.com/onlycs/attendance/blob/main/assets/deploy-railway-token.png?raw=true" alt="Setup Token" width="69%">
31+
</p>
32+
33+
4. Use the setup token, enter the initial admin username and password, and you're all set!
34+
35+
## Further configuration
36+
37+
- **Inviting Admins**: `Settings` > `Invitation`
38+
- **Student ID Format**: `Settings` > `User Management` > `Student ID Configuration`
39+
- **Hour Goals or Requirements**: `Settings` > `Attendance`
40+
- **Change Username or Password**: `Settings` > `My Account`
41+
42+
## Screenshots
43+
44+
![Attendance UI](https://github.com/onlycs/attendance/blob/main/assets/sc-attendance.png?raw=true)
45+
![Admin Dashboard](https://github.com/onlycs/attendance/blob/main/assets/sc-dashboard.png?raw=true)
46+
![Admin Settings UI](https://github.com/onlycs/attendance/blob/main/assets/sc-settings.png?raw=true)
47+
![Hours Editor](https://github.com/onlycs/attendance/blob/main/assets/sc-editor-censored.png?raw=true)
48+
49+
<p align="center">
50+
<img src="https://github.com/onlycs/attendance/blob/main/assets/sc-mobile.png?raw=true" alt="Student Mobile UI" width="29%">
51+
<img src="https://github.com/onlycs/attendance/blob/main/assets/sc-sign-in.png?raw=true" alt="Login Page" width="69%">
52+
</p>

0 commit comments

Comments
 (0)