adding monkeytype
Some checks failed
Mark Stale PRs / stale (push) Has been cancelled

This commit is contained in:
Benjamin Falch
2026-04-23 13:53:44 +02:00
parent e214a2fd35
commit 2bc741fb78
1930 changed files with 7590652 additions and 0 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: monkeytype
open_collective: # Replace with a single Open Collective username
ko_fi: monkeytype
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ["https://www.monkeytype.store/"]

137
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,137 @@
name: Bug report
description: Create a report to help us improve
labels: [bug]
body:
- type: markdown
attributes:
value: |
# Welcome
```
Thanks for taking the time to fill out this bug! If you need real-time help, join us on Discord: discord.gg/monkeytype
```
- type: checkboxes
attributes:
label: Did you clear cache before opening an issue?
description: Sometimes your browser has old files cached and the bug you are experiencing might be already fixed, or is just a side effect of a new update. If you don't know how to do that, this website should help https://www.pcmag.com/how-to/how-to-clear-your-cache-on-any-browser.
options:
- label: I have cleared my cache
required: true
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please [search](https://github.com/monkeytypegame/monkeytype/issues?q=is%3Aissue) to see if an issue already exists for the bug you encountered.
options:
- label: I have [searched](https://github.com/monkeytypegame/monkeytype/issues?q=is%3Aissue) the existing open and closed issues
required: true
- type: markdown
attributes:
value: |
# Basic debugging
```
Below fields are very important to quickly track down the issue, so please take the time to carefully check when the issue happens and when it does not.
```
- type: dropdown
attributes:
label: Does the issue happen when logged in?
options: ["Yes", "No", "N/A"]
validations:
required: true
- type: dropdown
attributes:
label: Does the issue happen when logged out?
options: ["Yes", "No"]
validations:
required: true
- type: dropdown
attributes:
label: Does the issue happen in incognito mode when logged in?
options: ["Yes", "No", "N/A"]
validations:
required: true
- type: dropdown
attributes:
label: Does the issue happen in incognito mode when logged out?
options: ["Yes", "No"]
validations:
required: true
- type: textarea
attributes:
label: Account name
description: Your Monkeytype account name.
placeholder: |
Miodec
validations:
required: false
- type: textarea
attributes:
label: Account config
description: If your issue only happens when logged in, please provide your config. To export your config, go to the Settings page, scroll all the way down to `import/export settings` and click `export`.
placeholder: |
Miodec
{"theme":"cyberspace","showKeyTips":false,"showLiveWpm":false,"showTimerProgress":false, ... "smoothCaret":true}
validations:
required: false
- type: markdown
attributes:
value: |
# Issue details
```
Please provide a detailed description of what's happening, and most importantly - solid steps to reproduce the issue. This will help us find it quicker.
```
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
validations:
required: false
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: false
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: false
- type: textarea
attributes:
label: Environment
description: |
examples:
- **OS**: Windows 10
- **Browser**: Google Chrome
- **Browser Version**: 94.0.4606.71 (Official Build) (64-bit)
value: |
- OS:
- Browser:
- Browser Version:
validations:
required: false
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request / Idea
url: https://github.com/monkeytypegame/monkeytype/discussions
about: Please do not create issues for feature requests. Instead, use GitHub Discussions.

28
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,28 @@
# Monkeytype AI Coding Instructions
Make the responses extremely concise. Sacrifice grammar for the sake of concision.
## Architecture
**Monorepo**: pnpm + Turborepo with frontend (Vite + SolidJS), backend (Express + MongoDB + Redis), and shared packages.
## Commands
All commands support `-fe`, `-be`, `-pkg` suffixes for targeted execution:
```bash
pnpm run lint-fe # Frontend linting
pnpm run test-be # Backend + integration tests
pnpm run build-pkg # Packages only
pnpm run dev # All workspaces with hot reload
```
## SolidJS Migration
Frontend is partially migrated - new components use SolidJS (`.tsx`), legacy code remains vanilla JS.
## Debug Tips
- Type/lint errors: Run `pnpm run lint` (OXLint is source of truth, not tsc)
## Key Files
- `turbo.json`: Task deps and caching
- `frontend/src/ts/config-metadata.ts`: Config validation rules
- `packages/contracts/src/index.ts`: API contract structure
- `packages/funbox/src/list.ts`: All funbox definitions
- `backend/src/api/routes/index.ts`: ts-rest setup

7
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
versioning-strategy: increase
schedule:
interval: "weekly"

29
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
api:
- any: ["frontend/src/ts/ape/**/*", "backend/src/api/**/*"]
assets:
- any: ["frontend/static/**/*"]
all: ["!frontend/static/**/*.html"]
backend:
- any: ["backend/**/*"]
docs:
- any: ["**/*.md"]
frontend:
- any: ["frontend/**/*"]
packages:
- any: ["packages/**/*"]
local dev:
- any:
[
"**/turbo.json",
"**/tsconfig.json",
"**/knip.json",
"**/.prettierrc",
"**/.oxlintrc.json",
"**/.eslintrc.cjs",
]

55
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,55 @@
### Description
<!--
Please describe the change(s) made in your PR:
- explain the problem being solved
- for bug fixes without an open issue, include steps to reproduce the issue
- summarize the approach taken
Use your own words. Do not rely on AI-generated descriptions.
They do not demonstrate your understanding of the problem or the solution.
Writing the description yourself helps you verify the scope of your work and
helps us better understand your intent, reasoning and level of insight.
-->
### Checks
- [ ] Adding quotes?
- Make sure to follow the [quotes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/QUOTES.md)
- [ ] Make sure to include translations for the quotes in the description (or another comment) so we can verify their content.
- [ ] Adding a language?
- Make sure to follow the [languages documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LANGUAGES.md)
- [ ] Add language to `packages/schemas/src/languages.ts`
- [ ] Add language to exactly one group in `frontend/src/ts/constants/languages.ts`
- [ ] Add language json file to `frontend/static/languages`
- [ ] Adding a theme?
- Make sure to follow the [themes documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/THEMES.md)
- [ ] Add theme to `packages/schemas/src/themes.ts`
- [ ] Add theme to `frontend/src/ts/constants/themes.ts`
- [ ] (optional) Add theme css file to `frontend/static/themes`
- [ ] Add some screenshots of the theme, especially with different test settings (colorful, flip colors) to your pull request
- [ ] Adding a layout?
- [ ] Make sure to follow the [layouts documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/LAYOUTS.md)
- [ ] Add layout to `packages/schemas/src/layouts.ts`
- [ ] Add layout json file to `frontend/static/layouts`
- [ ] Adding a font?
- Make sure to follow the [fonts documentation](https://github.com/monkeytypegame/monkeytype/blob/master/docs/FONTS.md)
- [ ] Add font file to `frontend/static/webfonts`
- [ ] Add font to `packages/schemas/src/fonts.ts`
- [ ] Add font to `frontend/src/ts/constants/fonts.ts`
- [ ] Check if any open issues are related to this PR; if so, be sure to tag them below.
- [ ] Make sure the PR title follows the Conventional Commits standard. (https://www.conventionalcommits.org for more info)
- [ ] Make sure to include your GitHub username prefixed with @ inside parentheses at the end of the PR title.
<!-- label(optional scope): pull request title (@your_github_username) -->
<!-- I know I know they seem boring but please do them, they help us and you will find out it also helps you. -->
Closes #
<!-- The issue(s) your PR resolves if any (delete if that is not the case) -->
<!-- Please reference any issues and/or PRs related to your pull request -->
<!-- Pro tip: you can mention an issue, PR, or discussion on GitHub by referencing its hash number, e.g.: [#1234](https://github.com/monkeytypegame/monkeytype/pull/1234) -->
<!-- Pro tip: you can press . (dot or period) in the code tab of any GitHub repo to get access to GitHub's VS Code web editor. Enjoy! :) -->

61
.github/workflows/check-formatting.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Check formatting
env:
PNPM_VERSION: "10.28.1"
NODE_VERSION: "24.11.0"
on:
pull_request:
branches: [master]
types: [opened, reopened, synchronize, ready_for_review]
permissions:
contents: read
concurrency:
group: group-format-check-${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
check:
if: github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'force-ci') || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
runs-on: ubuntu-latest
steps:
- name: Full checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Install formatter
run: pnpm install -D -w oxfmt
- name: Get changed files
id: get-changed-files
uses: actions/github-script@v7
with:
script: |
const changedFiles = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
}
);
return changedFiles.filter(file=> file.status !== "removed").map(file => file.filename).join(' ');
- name: Check formatting (changed files)
run: |
CHANGED_FILES=$(echo ${{ steps.get-changed-files.outputs.result }})
if [ -n "$CHANGED_FILES" ]; then
pnpm oxfmt $CHANGED_FILES --check --no-error-on-unmatched-pattern
fi

23
.github/workflows/check-todo.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: PR Todo Checker
on:
pull_request_review_comment:
types: [edited, deleted]
pull_request:
types: [opened, synchronize, reopened]
jobs:
find_todos:
runs-on: ubuntu-latest
permissions:
pull-requests: write # to comment on PRs
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for Todos
uses: phntmxyz/pr_todo_checker@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,39 @@
name: Comment on PR for CI Failure
permissions:
pull-requests: write
on:
workflow_run:
workflows: [Monkey CI]
types: [completed]
jobs:
on-failure:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: Download workflow artifact
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Read the pr_num file
id: pr_num_reader
uses: juliangruber/read-file-action@v1
with:
path: ./pr_num/pr_num.txt
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ steps.pr_num_reader.outputs.content }}
body: |
Continuous integration check(s) failed. Please review the [failing check\'s logs](${{ github.event.workflow_run.html_url }}) and make the necessary changes.
- name: Apply label changes
uses: PauMAVA/add-remove-label-action@v1.0.3
with:
issue_number: ${{ steps.pr_num_reader.outputs.content }}
add: "waiting for update"
remove: "waiting for review"

View File

@@ -0,0 +1,40 @@
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs:
claude-review:
if: >-
contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options

51
.github/workflows/claude.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association)) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association))
)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
# claude_args: '--allowed-tools Bash(gh pr:*)'

70
.github/workflows/fix-formatting.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
name: Fix formatting
env:
PNPM_VERSION: "10.28.1"
NODE_VERSION: "24.11.0"
permissions:
contents: write
pull-requests: write
on:
pull_request_target:
types: [labeled]
jobs:
format:
runs-on: ubuntu-latest
if: github.event.label.name == 'format'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name}}
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Install formatter
run: pnpm install -D -w oxfmt --ignore-scripts
- name: Get changed files
id: get-changed-files
uses: actions/github-script@v7
with:
script: |
const changedFiles = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
}
);
return changedFiles.filter(file=> file.status !== "removed").map(file => file.filename).join(' ');
- name: Fix formatting
env:
CHANGED_FILES: ${{ steps.get-changed-files.outputs.result }}
run: |
if [ -n "$CHANGED_FILES" ]; then
echo "$CHANGED_FILES" | tr ' ' '\n' | xargs pnpm oxfmt --no-error-on-unmatched-pattern
fi
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "fix formatting"
- name: Remove label
uses: actions-ecosystem/action-remove-labels@v1
with:
labels: format

14
.github/workflows/labeler.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.API_TOKEN }}"

346
.github/workflows/monkey-ci.yml vendored Normal file
View File

@@ -0,0 +1,346 @@
name: Monkey CI
env:
PNPM_VERSION: "10.28.1"
NODE_VERSION: "24.11.0"
RECAPTCHA_SITE_KEY: "6Lc-V8McAAAAAJ7s6LGNe7MBZnRiwbsbiWts87aj"
permissions:
contents: read
on:
pull_request:
branches: [master]
types: [opened, reopened, synchronize, ready_for_review]
push:
branches: [master]
concurrency:
group: group-${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
pre-ci:
if: github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'force-ci') || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
name: pre-ci
runs-on: ubuntu-latest
outputs:
should-build-be: ${{ steps.export-changes.outputs.should-build-be }}
should-build-fe: ${{ steps.export-changes.outputs.should-build-fe }}
should-build-pkg: ${{ steps.export-changes.outputs.should-build-pkg }}
assets-json: ${{ steps.export-changes.outputs.assets-json }}
steps:
- name: Full checkout
uses: actions/checkout@v4
# paths filter doesn't need checkout on pr
if: github.event_name != 'pull_request'
- name: Detect changes
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
json:
- 'frontend/static/**/*'
be-src:
- 'backend/**/*.{ts,js,json,lua,css,html}'
- 'backend/package.json'
fe-src:
- 'frontend/**/*.{ts,scss,html}'
- 'frontend/package.json'
pkg-src:
- 'packages/**/*'
anti-cheat:
- 'backend/**/anticheat/**'
- name: Check Anti-cheat
if: steps.filter.outputs.anti-cheat == 'true' && !contains(github.event.pull_request.labels.*.name, 'force-ci') && !contains(github.event.pull_request.labels.*.name, 'force-full-ci')
run: exit 1
- name: Export changes
id: export-changes
run: |
echo "should-build-pkg=${{ steps.filter.outputs.pkg-src }}" >> $GITHUB_OUTPUT
echo "should-build-be=${{ steps.filter.outputs.be-src }}" >> $GITHUB_OUTPUT
echo "should-build-fe=${{ steps.filter.outputs.fe-src }}" >> $GITHUB_OUTPUT
echo "assets-json=${{ steps.filter.outputs.json }}" >> $GITHUB_OUTPUT
prime-cache:
name: prime-cache
runs-on: ubuntu-latest
needs: [pre-ci]
if: needs.pre-ci.outputs.should-build-be == 'true' || needs.pre-ci.outputs.should-build-fe == 'true' || needs.pre-ci.outputs.should-build-pkg == 'true' || needs.pre-ci.outputs.assets-json == 'true' || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
steps:
- name: Checkout pnpm-lock
uses: actions/checkout@v4
with:
sparse-checkout: |
pnpm-lock.yaml
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache node modules
id: cache-pnpm
uses: actions/cache@v4
env:
cache-name: node-modules
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-build-${{ env.cache-name }}-${{ hashFiles('pnpm-lock.yaml') }}
lookup-only: true
- if: ${{ steps.cache-pnpm.outputs.cache-hit != 'true' }}
name: Full checkout
uses: actions/checkout@v4
- if: ${{ steps.cache-pnpm.outputs.cache-hit != 'true' }}
name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- if: ${{ steps.cache-pnpm.outputs.cache-hit != 'true' }}
name: Install dependencies
run: pnpm install
ci-be:
name: ci-be
needs: [pre-ci, prime-cache]
runs-on: ubuntu-latest
if: needs.pre-ci.outputs.should-build-be == 'true' || needs.pre-ci.outputs.should-build-pkg == 'true' || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
backend
packages
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache node modules
id: cache-pnpm
uses: actions/cache@v4
env:
cache-name: node-modules
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-build-${{ env.cache-name }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install
- name: Check lint
run: npm run lint-fast-be && npm run lint-be
- name: Build
run: npm run build-be
- name: Test
run: npm run test-be
ci-fe:
name: ci-fe
needs: [pre-ci, prime-cache]
runs-on: ubuntu-latest
if: needs.pre-ci.outputs.should-build-fe == 'true' || needs.pre-ci.outputs.should-build-pkg == 'true' || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
frontend
packages
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Create stub firebase config
working-directory: ./frontend/src/ts/constants
run: mv ./firebase-config-example.ts ./firebase-config.ts && cp ./firebase-config.ts ./firebase-config-live.ts
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache node modules
id: cache-pnpm
uses: actions/cache@v4
env:
cache-name: node-modules
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-build-${{ env.cache-name }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install
- name: Check lint
run: npm run lint-fast-fe && npm run lint-fe
- name: Build
run: npm run build-fe
- name: Test
run: npm run test-fe
ci-assets:
name: ci-assets
needs: [pre-ci, prime-cache]
runs-on: ubuntu-latest
if: needs.pre-ci.outputs.assets-json == 'true' || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
frontend
packages
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
languages:
- 'frontend/static/languages/**'
quotes:
- 'frontend/static/quotes/**'
others:
- 'frontend/static/layouts/**'
- 'frontend/static/themes/**'
- 'frontend/static/webfonts/**'
- 'frontend/static/challenges/**'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache node modules
id: cache-pnpm
uses: actions/cache@v4
env:
cache-name: node-modules
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-build-${{ env.cache-name }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install
- name: Lint JSON
run: npm run lint-json-assets
- name: Validate language assets
if: steps.filter.outputs.languages == 'true'
run: npm run check-assets-languages
- name: Validate quote assets
if: steps.filter.outputs.quotes == 'true'
run: npm run check-assets-quotes
- name: Validate other assets
if: steps.filter.outputs.others == 'true'
run: npm run check-assets-others
ci-pkg:
name: ci-pkg
needs: [pre-ci, prime-cache]
runs-on: ubuntu-latest
if: needs.pre-ci.outputs.should-build-pkg == 'true' || contains(github.event.pull_request.labels.*.name, 'force-full-ci')
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
packages
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache node modules
id: cache-pnpm
uses: actions/cache@v4
env:
cache-name: node-modules
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-build-${{ env.cache-name }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install
- name: Check lint
run: npm run lint-fast-pkg && npm run lint-pkg
- name: Build
run: npm run build-pkg
- name: Test
run: npm run test-pkg
on-failure:
name: on-failure
runs-on: ubuntu-latest
needs: [ci-be, ci-fe, ci-assets, ci-pkg]
if: ${{ always() && contains(needs.*.result, 'failure') && github.ref != 'refs/heads/master' }}
steps:
- name: Save the PR number in an artifact
shell: bash
env:
PR_NUM: ${{ github.event.number }}
run: echo $PR_NUM > pr_num.txt
- name: Upload the PR number
uses: actions/upload-artifact@v4
with:
name: pr_num
path: ./pr_num.txt

View File

@@ -0,0 +1,87 @@
name: Publish Docker image
permissions:
contents: read
on:
release:
types: [published]
workflow_dispatch:
jobs:
push_to_registry:
env:
BE_REPO: monkeytype/monkeytype-backend
FE_REPO: monkeytype/monkeytype-frontend
PLATFORMS: linux/amd64,linux/arm64
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb
- name: Log in to Docker Hub
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Backend extract metadata (tags, labels)
id: bemeta
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
with:
images: ${{ env.BE_REPO }}
tags: |
type=semver,pattern={{version}}
- name: Backend build and push Docker image
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0
with:
context: .
platforms: ${{ env.PLATFORMS }}
file: ./docker/backend/Dockerfile
push: true
tags: ${{ env.BE_REPO }}:latest,${{ steps.bemeta.outputs.tags }}
labels: ${{ steps.bemeta.outputs.labels }}
build-args: |
server_version: ${{ github.event.release.tag_name }}
- name: Backend publish description
uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
repository: ${{ env.BE_REPO }}
short-description: Official backend server for monkeytype.com
readme-filepath: ./docs/SELF_HOSTING.md
- name: Frontend extract metadata (tags, labels)
id: femeta
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81
with:
images: ${{ env.FE_REPO }}
tags: |
type=semver,pattern={{version}}
- name: Frontend build and push Docker image
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0
with:
context: .
platforms: ${{ env.PLATFORMS }}
file: ./docker/frontend/Dockerfile
push: true
tags: ${{ env.FE_REPO }}:latest,${{ steps.femeta.outputs.tags }}
labels: ${{ steps.femeta.outputs.labels }}
- name: Frontend publish description
uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
repository: ${{ env.FE_REPO }}
short-description: Official frontend server for monkeytype.com
readme-filepath: ./docs/SELF_HOSTING.md

73
.github/workflows/semantic-pr-title.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: "Semantic PR Title"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- reopened
permissions:
pull-requests: write
jobs:
main:
name: check
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
steps:
- name: Lint and verify PR title
uses: amannn/action-semantic-pull-request@v5
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
build
chore
ci
docs
feat
impr
fix
perf
refactor
revert
style
test
requireScope: false
subjectPattern: ^.+ \(@[^ ,]+(, @[^ ,]+)*\)$
subjectPatternError: |
Title "{title}"
didn't match the configured pattern. Please ensure that the title
contains your name so that you can be credited in our changelog.
- uses: marocchino/sticky-pull-request-comment@v2
# When the previous steps fails, the workflow would stop. By adding this
# condition you can continue the execution with the populated error message.
if: always() && (steps.lint_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
message: |
Hey there and thank you for opening this pull request! 👋🏼
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and also include the author name at the end inside parenthesis. It looks like your proposed title needs to be adjusted.
Details:
```
${{ steps.lint_pr_title.outputs.error_message }}
```
A correct version would look something like:
feat: add new feature (@github_username)
impr(quotes): add english quotes (@username)
fix(leaderboard): show user rank correctly (@user1, @user2, @user3)
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: pr-title-lint-error
delete: true

16
.github/workflows/stale-pr.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: "Mark Stale PRs"
on:
schedule:
- cron: "30 20 * * *"
permissions:
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
with:
stale-pr-message: "This PR is stale. Please trigger a re-run of the PR check action."
days-before-stale: 7

100
.github/workflows/update-labels.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: Check labels to update
permissions:
actions: read
pull-requests: read
on:
pull_request_target:
types:
[
review_requested,
ready_for_review,
review_request_removed,
converted_to_draft,
synchronize,
edited,
]
pull_request_review:
types: [submitted, edited, dismissed]
pull_request_review_comment:
types: [created, edited]
issue_comment:
types: [created, edited]
jobs:
update-labels:
runs-on: ubuntu-latest
env:
PR_NUM: ${{ github.event.pull_request.number || github.event.issue.number }}
steps:
- name: Set up varibles
run: |
echo "REVIEW=0" >> $GITHUB_ENV
echo "UPDATE=0" >> $GITHUB_ENV
- name: Add 'waiting for review' label
# when a review is requested or if the PR is converted from a draft
if: |
github.event_name == 'pull_request_target' &&
contains(fromJSON('["review_requested", "ready_for_review"]'), github.event.action)
run: echo "REVIEW=1" >> $GITHUB_ENV
- name: Remove 'waiting for review' label
# when a review request is removed or if the PR is converted to a draft
# or when the PR is reviewed by the owner, a member or a collaborator
if: |
(
github.event_name == 'pull_request_target' &&
contains(fromJSON('["review_request_removed", "converted_to_draft"]'), github.event.action)
) ||
(
github.event_name == 'pull_request_review' &&
contains(fromJSON('["submitted", "edited"]'), github.event.action) &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association)
)
run: echo "REVIEW=-1" >> $GITHUB_ENV
- name: Add 'waiting for update' label
# when a review by one of {owner, member, collaborator} requests changes
if: |
github.event_name == 'pull_request_review' &&
github.event.review.state == 'changes_requested' &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association)
run: echo "UPDATE=1" >> $GITHUB_ENV
- name: Remove 'waiting for update' label from PR/issue
# when PR is commited to or if the PR is edited or if a review is requested or dismissed
# or when a comment is added by the author to the review or to the main PR thread
if: |
(
github.event_name == 'pull_request_target' &&
contains(fromJSON('["synchronize", "edited", "review_requested"]'), github.event.action)
) ||
(
github.event_name == 'pull_request_review' &&
github.event.action == 'dismissed'
) ||
(
github.event_name == 'pull_request_review_comment' &&
contains(fromJSON('["created", "edited"]'), github.event.action) &&
github.event.comment.user.id == github.event.pull_request.user.id
) ||
(
github.event_name == 'issue_comment' &&
contains(fromJSON('["created", "edited"]'), github.event.action) &&
github.event.comment.user.id == github.event.issue.user.id
)
run: echo "UPDATE=-1" >> $GITHUB_ENV
- name: Save result in a JSON file
env:
LABELS_JSON: ${{ format('{{"waiting_for_review"{0} "{1}", "waiting_for_update"{0} "{2}", "pr_num"{0} "{3}"}}', ':', env.REVIEW, env.UPDATE, env.PR_NUM) }}
run: echo $LABELS_JSON > write-labels.json
- name: Upload the JSON file
uses: actions/upload-artifact@v4
with:
name: labels
path: ./write-labels.json

52
.github/workflows/write-labels.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Write label on PR/issue
permissions:
pull-requests: write
issues: write
on:
workflow_run:
workflows: [Check labels to update]
types: [completed]
jobs:
write-labels:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Download workflow artifact
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Read json file
id: json_reader
uses: juliangruber/read-file-action@v1
with:
path: ./labels/write-labels.json
- name: Add `waiting for review` label
if: fromJSON(steps.json_reader.outputs.content).waiting_for_review == 1
run: echo "ADD_LABELS=${ADD_LABELS}waiting for review," >> $GITHUB_ENV
- name: Remove `waiting for review` label
if: fromJSON(steps.json_reader.outputs.content).waiting_for_review == -1
run: echo "REMOVE_LABELS=${REMOVE_LABELS}waiting for review," >> $GITHUB_ENV
- name: Add `waiting for update` label
if: fromJSON(steps.json_reader.outputs.content).waiting_for_update == 1
run: echo "ADD_LABELS=${ADD_LABELS}waiting for update," >> $GITHUB_ENV
- name: Remove `waiting for update` label
if: fromJSON(steps.json_reader.outputs.content).waiting_for_update == -1
run: echo "REMOVE_LABELS=${REMOVE_LABELS}waiting for update," >> $GITHUB_ENV
- name: Apply label changes
if: env.ADD_LABELS || env.REMOVE_LABELS
uses: PauMAVA/add-remove-label-action@v1.0.3
with:
issue_number: ${{ fromJSON(steps.json_reader.outputs.content).pr_num }}
add: ${{ env.ADD_LABELS }}
remove: ${{ env.REMOVE_LABELS }}