Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
27619f5
chore(docker): add development Docker environment
brunoschumacherf Sep 10, 2026
5b6f9b6
chore(rails): initialize Rails 8 application with PostgreSQL
brunoschumacherf Sep 10, 2026
9425d76
feat(app): setup authentication, Inertia, React, Vite and Tailwind
brunoschumacherf Sep 10, 2026
43f2905
feat(db): add user model, imports and database seeds
brunoschumacherf Sep 10, 2026
94da9f7
feat(auth): add Rails authentication and sessions
brunoschumacherf Sep 10, 2026
1db658c
feat(frontend): configure Inertia React and Vite
brunoschumacherf Sep 10, 2026
13d068b
feat(frontend): configure Tailwind CSS and assets
brunoschumacherf Sep 10, 2026
f69bd45
feat(auth): add profile and registration controllers
brunoschumacherf Sep 10, 2026
0620952
chore: configure frontend dependencies and development tooling
brunoschumacherf Sep 10, 2026
5c94c0b
feat(frontend): integrate Inertia with Rails application
brunoschumacherf Sep 10, 2026
527865c
feat(auth): add login page
brunoschumacherf Sep 10, 2026
1a56d42
feat(admin): add admin dashboard
brunoschumacherf Sep 10, 2026
9952b13
feat(imports): add asynchronous user import job
brunoschumacherf Sep 10, 2026
77f2259
chore(db): update database schema
brunoschumacherf Sep 10, 2026
1834007
chore(infra): configure Docker and Kamal deployment
brunoschumacherf Sep 10, 2026
a5d2233
chore(rails): configure CI and application infrastructure
brunoschumacherf Sep 10, 2026
abe7b56
feat(frontend): configure Inertia React and Vite
brunoschumacherf Sep 10, 2026
1d44d6c
feat(auth): implement authentication and user registration
brunoschumacherf Sep 10, 2026
4dd02a9
feat(admin): implement user management dashboard
brunoschumacherf Sep 10, 2026
0ebb7c0
feat(imports): implement asynchronous user import
brunoschumacherf Sep 10, 2026
c22bb81
feat(db): update user model and database schema
brunoschumacherf Sep 10, 2026
78443fc
test: add application and security test coverage
brunoschumacherf Sep 10, 2026
cb9446c
docs(i18n): add Portuguese translations and documentation
brunoschumacherf Sep 10, 2026
fe46cfc
refactor(inertia): remove legacy Inertia example files
brunoschumacherf Sep 10, 2026
b21849d
chore: configure application channels and security
brunoschumacherf Sep 10, 2026
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
49 changes: 49 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.

# Ignore git directory.
/.git/
/.gitignore

# Ignore bundler config.
/.bundle

# Ignore all environment files.
/.env*

# Ignore all default key files.
/config/master.key
/config/credentials/*.key

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/.keep

# Ignore storage (uploaded files in development and any SQLite databases).
/storage/*
!/storage/.keep
/tmp/storage/*
!/tmp/storage/.keep

# Ignore assets.
/node_modules/
/app/assets/builds/*
!/app/assets/builds/.keep
/public/assets

/coverage
/.kamal/secrets

# Ignore CI service files.
/.github

# Ignore development files
/.devcontainer

# Ignore Docker metadata (the image build context still uses Dockerfile / Dockerfile.dev)
/.dockerignore
9 changes: 9 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# See https://git-scm.com/docs/gitattributes for more about git attribute files.

# Mark the database schema as having been generated.
db/schema.rb linguist-generated

# Mark any vendored files as having been vendored.
vendor/* linguist-vendored
config/credentials/*.yml.enc diff=rails_credentials
config/credentials.yml.enc diff=rails_credentials
48 changes: 48 additions & 0 deletions .github/agents/modern-fullstack-developer.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
name: Modern Fullstack Developer
description: "Use for the Rails 8+ full-stack developer test: user administration, Rails authentication, ActiveStorage avatars, React/Inertia or Hotwire UI, Solid Queue imports, Solid Cable dashboard updates, Docker/Kamal delivery, testing, security, and README compliance."
tools: [read, edit, search, execute, todo]
reasoning-effort: high
argument-hint: "Describe the user-management feature, bug, test, or delivery requirement to implement."
user-invocable: true
---

You are the senior full-stack engineer responsible for completing the Umanni Modern Fullstack Developer Test in this repository.

## Mission

Deliver a production-minded user-management application for Rails 8+ and Ruby 4+, using the repository's existing architecture wherever it is sound. The application must support:

- Admin and normal-user authorization with Rails' built-in authentication generator, not Devise.
- User CRUD for admins, role toggling, profile self-service, strict validation, and secure access boundaries.
- Avatar uploads or remote avatar URLs through the existing ActiveStorage setup.
- Asynchronous CSV/XLSX imports through Solid Queue, with live progress and status updates through Solid Cable and frontend state or streams.
- A responsive, accessible frontend using the stack already selected by the repository, with a modern CSS framework and useful validation feedback.
- Production-minded PostgreSQL, MySQL, or SQLite configuration, including WAL mode where SQLite is used.
- Multi-stage Docker, Thruster/Kamal-ready defaults, credentials-based configuration, and clean deployment documentation.

## Working Rules

- Before changing code, inspect the owning implementation, nearby tests, routes, and configuration. State one local hypothesis and one focused validation check internally, then make the smallest coherent edit.
- If the repository already chooses React/Inertia, Hotwire, Vite, or a CSS framework, follow that choice. Do not introduce a competing frontend architecture without a concrete requirement.
- Prefer Rails conventions, service objects already present in the repository, policy/authorization boundaries, strong parameters, database constraints, and transactional writes over ad hoc controller logic.
- Keep imports idempotent where practical, validate rows before persistence, report failures clearly, and never expose another user's data through progress or profile endpoints.
- Treat authentication, authorization, file handling, XSS, CSRF, SQL injection, mass assignment, and unsafe spreadsheet input as first-class concerns.
- Add or update focused model, service, controller/integration, job, channel, and system tests. Use parallel testing consistently with the existing test setup and preserve a credible path to 90% coverage.
- Keep tests deterministic and avoid Redis. Use Solid Queue and Solid Cable as required by the brief.
- Preserve unrelated user changes. Do not reset, checkout, or rewrite history. Do not commit unless explicitly asked.
- At the beginning of implementation work, check the current branch and working tree. Create or use a dedicated task branch only when requested or when the repository workflow requires it; never silently discard existing work.
- Update the English README whenever setup, seed data, test commands, deployment, architecture, or user-facing behavior changes. Include a clearly labeled AI disclosure naming the model used: GitHub Copilot.

## Delivery Loop

1. Inspect the relevant code path and existing conventions.
2. Make a narrow implementation or test change.
3. Run the cheapest behavior-focused validation immediately.
4. Repair local failures before expanding scope.
5. Run the relevant full test, lint, security, and build checks when the slice is complete.
6. Review the diff for authorization gaps, regressions, missing documentation, and accidental metadata or generated-file churn.

## Completion Criteria

Do not call a task complete until the affected behavior is implemented, tested, documented when relevant, and validated with executable checks. Report commands run, relevant failures that predate the change, and any remaining risk. Keep the final response concise and link changed workspace files.
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: bundler
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
174 changes: 174 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
name: CI

on:
pull_request:
push:
branches: [ main ]

jobs:
scan_ruby:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true

- name: Scan for common Rails security vulnerabilities using static analysis
run: bin/brakeman --no-pager

- name: Scan for known security vulnerabilities in gems used
run: bin/bundler-audit

scan_js:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install JavaScript dependencies
run: npm ci

- name: Scan for security vulnerabilities in JavaScript dependencies
run: npm audit --audit-level=high

lint:
runs-on: ubuntu-latest
env:
RUBOCOP_CACHE_ROOT: tmp/rubocop
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true

- name: Prepare RuboCop cache
uses: actions/cache@v4
env:
DEPENDENCIES_HASH: ${{ hashFiles('.ruby-version', '**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }}
with:
path: ${{ env.RUBOCOP_CACHE_ROOT }}
key: rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
restore-keys: |
rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-

- name: Lint code for consistent style
run: bin/rubocop -f github

test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3

# redis:
# image: valkey/valkey:8
# ports:
# - 6379:6379
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5

steps:
- name: Install packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libpq-dev libsqlite3-dev libvips

- name: Checkout code
uses: actions/checkout@v6

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install JavaScript dependencies
run: npm ci

- name: Run tests
env:
RAILS_ENV: test
DATABASE_URL: postgres://postgres:postgres@localhost:5432
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
run: bin/rails db:test:prepare test

system-test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3

# redis:
# image: valkey/valkey:8
# ports:
# - 6379:6379
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5

steps:
- name: Install packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libpq-dev libsqlite3-dev libvips

- name: Checkout code
uses: actions/checkout@v6

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install JavaScript dependencies
run: npm ci

- name: Run System Tests
env:
RAILS_ENV: test
DATABASE_URL: postgres://postgres:postgres@localhost:5432
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
run: bin/rails db:test:prepare test:system

- name: Keep screenshots from failed system tests
uses: actions/upload-artifact@v4
if: failure()
with:
name: screenshots
path: ${{ github.workspace }}/tmp/screenshots
if-no-files-found: ignore
48 changes: 48 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# Temporary files generated by your text editor or operating system
# belong in git's global ignore instead:
# `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`

# Ignore bundler config.
/.bundle

# Ignore all environment files.
/.env*

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep

# Ignore storage (uploaded files in development and any SQLite databases).
/storage/*
!/storage/.keep
/tmp/storage/*
!/tmp/storage/
!/tmp/storage/.keep

/public/assets
/coverage
/public/vite-test
/public/vite-ssr

# Ignore key files for decrypting credentials and more.
/config/*.key
/config/master.key
/.kamal/secrets


# Vite Ruby
/public/vite*
node_modules
# Vite uses dotenv and suggests to ignore local-only env files. See
# https://vitejs.dev/guide/env-and-mode.html#env-files
*.local

4 changes: 4 additions & 0 deletions .kamal/secrets.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$(cat config/master.key)
DATABASE_URL=$DATABASE_URL
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
23 changes: 23 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
inherit_gem:
rubocop-rails-omakase: rubocop.yml

AllCops:
NewCops: enable
TargetRubyVersion: 4.0
Exclude:
- "bin/**/*"
- "db/schema.rb"
- "db/*_schema.rb"
- "vendor/**/*"
- "node_modules/**/*"
- "storage/**/*"
- "tmp/**/*"

Layout/LineLength:
Max: 120

Style/Documentation:
Enabled: false

Style/StringLiterals:
EnforcedStyle: double_quotes
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby-4.0.6
Loading