Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
TZ=America/Edmonton

# For homelab server, change the following to your server ip ex: http://192.168.x.x:3000
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_URL=http://localhost:3737

# AUTH_SECRET is auto-generated by deploy.sh and docker-entrypoint.sh
# To set manually, uncomment and provide your own value:
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: CI

on:
pull_request:
branches: [dev, main]

jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"

- run: npm ci

- run: npx prisma generate

- run: npm run lint

- run: npm test
56 changes: 56 additions & 0 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Docker Release

on:
push:
tags:
- "v*.*.*"

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

permissions:
contents: read
packages: write

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
23 changes: 23 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: release-please

on:
push:
branches:
- main

permissions:
contents: write
pull-requests: write

jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
release-type: node
target-branch: main
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ dev.db-journal
data/files/resumes
CLAUDE.md
/docs
.github
.github/*
!.github/workflows
.claude
.specify
/specs
Expand Down
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Changelog

## 1.0.0 (2026-02-28)


### Features

* add release automation ([6fd8247](https://github.com/Gsync/jobsync/commit/6fd8247f836208d61eddae935c4cbd63fac36cde))


### Bug Fixes

* Add job draft date in job details ([f6c2bb6](https://github.com/Gsync/jobsync/commit/f6c2bb65f14364f1292ecccf66c4f2999ba5cfc6))
* Admin tab swich ([8c57052](https://github.com/Gsync/jobsync/commit/8c5705297c643a13c9d00da34a45d7d85f785f23))
* bullet and order styling of editor content ([423b0f4](https://github.com/Gsync/jobsync/commit/423b0f43d0cfff76e1522864bd1b5177773692fb))
* button hydration error ([d7e97a0](https://github.com/Gsync/jobsync/commit/d7e97a014e2d41ccdb1cd77d6baa0b6975576f4b))
* Combobox filter issue ([1ab477e](https://github.com/Gsync/jobsync/commit/1ab477eb6e64f0aab7da360fcc936897217583e5))
* Combobox undefined error ([fdaa9fe](https://github.com/Gsync/jobsync/commit/fdaa9fe72c35695136871a8e92fb5311af52a476))
* configure release-please to target dev branch ([9ca7db0](https://github.com/Gsync/jobsync/commit/9ca7db003a5fb2d0ef4484a223aa7511eb84c08b))
* Create company bug when adding experience ([c992077](https://github.com/Gsync/jobsync/commit/c99207744f8f038ad490d10dba581dba8c13d960))
* DatePicker bug in Safari browser ([0f24106](https://github.com/Gsync/jobsync/commit/0f24106ebe5fabbd65336e2de128a623d3406099))
* Dialog scroll ([93f8e7d](https://github.com/Gsync/jobsync/commit/93f8e7dbec477b14c924ea0f819283c9a1f142f0))
* Edit company ([d7a15e2](https://github.com/Gsync/jobsync/commit/d7a15e293345e8097a43e9e4128b1e5a07ff024b))
* Error accessing ollama api endpoint in docker ([83aa24a](https://github.com/Gsync/jobsync/commit/83aa24a5ec8f503c1f2c758e4fb5ec5d2506bcc4))
* Failing Tasks playwright tests ([4c2cecf](https://github.com/Gsync/jobsync/commit/4c2cecf95c77106b7f6fafd2ded8ca4c16822d9c))
* hydration error, minor refactor ([6d2db31](https://github.com/Gsync/jobsync/commit/6d2db31ebde9ee6afc426f8ece397145becfe731))
* job status undefined issue ([91d3097](https://github.com/Gsync/jobsync/commit/91d309762d87f863ffd481b6c720b91ee8e21c5a))
* jobsApplied based on applied field ([d0ad166](https://github.com/Gsync/jobsync/commit/d0ad166a291477bd53663d59165a40bc6af203cb))
* login error validation ([7df090a](https://github.com/Gsync/jobsync/commit/7df090a6b899b89394d29722f7f247730b2b8713))
* minor layout issues ([55e1e42](https://github.com/Gsync/jobsync/commit/55e1e42d38e26c74ff675f0a761cabe40cde7cb2))
* no matching decryption secret ([b8f3919](https://github.com/Gsync/jobsync/commit/b8f3919cc5fa39d241b034639c73684c3284e34d))
* openssl not found ([290a1a7](https://github.com/Gsync/jobsync/commit/290a1a7b6ba54968ba19ebd0c41a378bbd8b1fa0))
* resume undefined issue ([dbe01a9](https://github.com/Gsync/jobsync/commit/dbe01a91ede0a378dd9678c44ac73823339e4546))
* Revalidate company list in addjob when adding company ([785c49b](https://github.com/Gsync/jobsync/commit/785c49b92ef6459fdb9d77045795108d78d26c65))
* route path ([4234c08](https://github.com/Gsync/jobsync/commit/4234c0808d83871bffb1d2d54a2205a244133771))
* session based conditional rendering ([b008e1b](https://github.com/Gsync/jobsync/commit/b008e1b7efa0912db5512b33561295b4c59b0c4d))
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ cd jobsync
docker compose up
```

Open [http://localhost:3000](http://localhost:3000) and create your account. That's it!
Open [http://localhost:3737](http://localhost:3737) and create your account. That's it!

API keys for AI providers can be configured in **Settings** after signing in.

Expand Down
4 changes: 2 additions & 2 deletions __tests__/AddCompany.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe("AddCompany Component", () => {
/>,
);
expect(
screen.getByRole("button", { name: /add company/i }),
screen.getByRole("button", { name: /new company/i }),
).toBeInTheDocument();
});

Expand All @@ -41,7 +41,7 @@ describe("AddCompany Component", () => {
/>,
);
const addCompanyButton = screen.getByRole("button", {
name: /add company/i,
name: /new company/i,
});
fireEvent.click(addCompanyButton);

Expand Down
6 changes: 3 additions & 3 deletions __tests__/ProfileContainer.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jest.mock("@/actions/profile.actions", () => ({
total: 0,
success: true,
message: "",
})
}),
),
}));

Expand All @@ -31,15 +31,15 @@ describe("ProfileContainer Component", () => {

it("should open the create resume dialog upon clicking create resume button", async () => {
const createResumeButton = screen.getByRole("button", {
name: /create resume/i,
name: /new resume/i,
});
await act(async () => {
fireEvent.click(createResumeButton);
});

expect(screen.getByRole("dialog")).toBeInTheDocument();
expect(
screen.getByRole("heading", { level: 2, name: /create resume/i })
screen.getByRole("heading", { level: 2, name: /create resume/i }),
).toBeInTheDocument();
});
});
33 changes: 17 additions & 16 deletions __tests__/dashboard.actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getJobsActivityForPeriod,
getActivityCalendarData,
} from "@/actions/dashboard.actions";
import { APP_CONSTANTS } from "@/lib/constants";
import { getCurrentUser } from "@/utils/user.utils";
import { PrismaClient } from "@prisma/client";

Expand Down Expand Up @@ -52,7 +53,7 @@ describe("Dashboard Actions", () => {
(getCurrentUser as jest.Mock).mockResolvedValue(null);

await expect(getJobsAppliedForPeriod(7)).rejects.toThrow(
"Not authenticated"
"Not authenticated",
);
});

Expand All @@ -68,11 +69,11 @@ describe("Dashboard Actions", () => {
it("should handle database errors", async () => {
(getCurrentUser as jest.Mock).mockResolvedValue(mockUser);
(prisma.$transaction as jest.Mock).mockRejectedValue(
new Error("Database error")
new Error("Database error"),
);

await expect(getJobsAppliedForPeriod(7)).rejects.toThrow(
"Failed to calculate job count"
"Failed to calculate job count",
);
});
});
Expand Down Expand Up @@ -114,26 +115,26 @@ describe("Dashboard Actions", () => {
orderBy: {
appliedDate: "desc",
},
take: 6,
take: APP_CONSTANTS.RECENT_NUM_JOBS_ACTIVITIES,
});
});

it("should throw error when user is not authenticated", async () => {
(getCurrentUser as jest.Mock).mockResolvedValue(null);

await expect(getRecentJobs()).rejects.toThrow(
"Failed to fetch jobs list. "
"Failed to fetch jobs list. ",
);
});

it("should handle database errors", async () => {
(getCurrentUser as jest.Mock).mockResolvedValue(mockUser);
(prisma.job.findMany as jest.Mock).mockRejectedValue(
new Error("Database error")
new Error("Database error"),
);

await expect(getRecentJobs()).rejects.toThrow(
"Failed to fetch jobs list. "
"Failed to fetch jobs list. ",
);
});

Expand Down Expand Up @@ -176,18 +177,18 @@ describe("Dashboard Actions", () => {
(getCurrentUser as jest.Mock).mockResolvedValue(null);

await expect(getActivityDataForPeriod()).rejects.toThrow(
"Failed to fetch activities data."
"Failed to fetch activities data.",
);
});

it("should handle database errors", async () => {
(getCurrentUser as jest.Mock).mockResolvedValue(mockUser);
(prisma.activity.findMany as jest.Mock).mockRejectedValue(
new Error("Database error")
new Error("Database error"),
);

await expect(getActivityDataForPeriod()).rejects.toThrow(
"Failed to fetch activities data."
"Failed to fetch activities data.",
);
});

Expand Down Expand Up @@ -251,18 +252,18 @@ describe("Dashboard Actions", () => {
(getCurrentUser as jest.Mock).mockResolvedValue(null);

await expect(getJobsActivityForPeriod()).rejects.toThrow(
"Failed to fetch jobs list. "
"Failed to fetch jobs list. ",
);
});

it("should handle database errors", async () => {
(getCurrentUser as jest.Mock).mockResolvedValue(mockUser);
(prisma.job.groupBy as jest.Mock).mockRejectedValue(
new Error("Database error")
new Error("Database error"),
);

await expect(getJobsActivityForPeriod()).rejects.toThrow(
"Failed to fetch jobs list. "
"Failed to fetch jobs list. ",
);
});

Expand Down Expand Up @@ -308,18 +309,18 @@ describe("Dashboard Actions", () => {
(getCurrentUser as jest.Mock).mockResolvedValue(null);

await expect(getActivityCalendarData()).rejects.toThrow(
"Failed to fetch jobs list. "
"Failed to fetch jobs list. ",
);
});

it("should handle database errors", async () => {
(getCurrentUser as jest.Mock).mockResolvedValue(mockUser);
(prisma.job.groupBy as jest.Mock).mockRejectedValue(
new Error("Database error")
new Error("Database error"),
);

await expect(getActivityCalendarData()).rejects.toThrow(
"Failed to fetch jobs list. "
"Failed to fetch jobs list. ",
);
});

Expand Down
16 changes: 10 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
services:
app:
image: ghcr.io/gsync/jobsync:latest
container_name: jobsync_app
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
- "3737:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=file:/data/dev.db
- AUTH_SECRET=${AUTH_SECRET:-}
- ENCRYPTION_KEY=you-encryption-key-here
- NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000}
- ENCRYPTION_KEY=${ENCRYPTION_KEY:-you-encryption-key-here}
- NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3737}
- AUTH_TRUST_HOST=${AUTH_TRUST_HOST:-true}
- TZ=${TZ:-America/Edmonton}
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
Expand All @@ -21,3 +19,9 @@ services:
volumes:
- ./jobsyncdb/data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3737"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
Loading