diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..d1cfb376 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "TechTix Monorepo", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "runArgs": ["--network=host"], + "features": { + "ghcr.io/devcontainers/features/aws-cli:1.1.2": { + }, + "ghcr.io/devcontainers/features/node:1": { + "version": "20" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.11" + } + }, + "mounts": [ + "source=${localEnv:HOME}${localEnv:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind,consistency=cached" + ], + "postCreateCommand": "npm install --prefix frontend && cd backend && pip install uv && uv sync", + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "ms-python.python", + "ms-python.vscode-pylance" + ] + } + }, + "remoteUser": "root" +} diff --git a/.github/workflows/deploy_backend.yml b/.github/workflows/deploy_backend.yml index 7a32546c..2701d3b4 100644 --- a/.github/workflows/deploy_backend.yml +++ b/.github/workflows/deploy_backend.yml @@ -1,11 +1,9 @@ name: Deploy DurianPy Events Service on: - push: - paths: - - backend/** - branches: - - main - - stage + workflow_run: + workflows: ["Backend Formatting Check on Push"] + types: + - completed workflow_dispatch: permissions: @@ -14,17 +12,28 @@ permissions: jobs: deploy: + name: Deploy (${{ (github.event.workflow_run.head_branch == 'main' || github.ref_name == 'main') && 'prod' || 'staging' }}) + if: >- + (github.event_name == 'workflow_run' && + github.event.workflow_run.conclusion == 'success' && + (github.event.workflow_run.head_branch == 'main' || github.event.workflow_run.head_branch == 'stage')) || + (github.event_name == 'workflow_dispatch' && + (github.ref_name == 'main' || github.ref_name == 'stage')) runs-on: ubuntu-latest + environment: ${{ (github.event.workflow_run.head_branch == 'main' || github.ref_name == 'main') && 'prod' || 'staging' }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_sha || github.sha }} - name: Set ARN for Role to Assume run: | - if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then + BRANCH="${{ github.event.workflow_run.head_branch || github.ref_name }}" + if [[ "$BRANCH" == "main" ]]; then echo "AWS_OIDC_ROLE_ARN=${{ secrets.AWS_OIDC_ROLE_ARN_PROD }}" >> $GITHUB_ENV - elif [[ "${{ github.ref }}" == "refs/heads/stage" ]]; then + elif [[ "$BRANCH" == "stage" ]]; then echo "AWS_OIDC_ROLE_ARN=${{ secrets.AWS_OIDC_ROLE_ARN_STAGE }}" >> $GITHUB_ENV fi @@ -61,9 +70,16 @@ jobs: - name: Deploy with Serverless v3 run: | - if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - sls deploy --stage prod --verbose - elif [[ "${{ github.ref }}" == "refs/heads/stage" ]]; then - sls deploy --stage staging --verbose + BRANCH="${{ github.event.workflow_run.head_branch || github.ref_name }}" + if [[ "$BRANCH" == "main" ]]; then + echo "Deploying to production..." + if ! output=$(sls deploy --stage prod --conceal 2>&1); then + echo "Production deployment failed:" + echo "$output" + exit 1 + fi + echo "Production deployment completed successfully." + elif [[ "$BRANCH" == "stage" ]]; then + sls deploy --stage staging --conceal fi working-directory: backend diff --git a/.github/workflows/FE-deploy-stage-to-prod-pipeline.yml b/.github/workflows/fe_deploy_to_main.yml similarity index 79% rename from .github/workflows/FE-deploy-stage-to-prod-pipeline.yml rename to .github/workflows/fe_deploy_to_main.yml index d5194719..d089d854 100644 --- a/.github/workflows/FE-deploy-stage-to-prod-pipeline.yml +++ b/.github/workflows/fe_deploy_to_main.yml @@ -1,11 +1,11 @@ -name: Deploy stage to main +name: Deploy dev to main on: workflow_dispatch: jobs: - deploy-stage-to-main: - name: Deploy stage to main + deploy-dev-to-main: + name: Deploy dev to main runs-on: ubuntu-latest defaults: run: @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - ref: stage + ref: dev - uses: actions/setup-node@v4 with: node-version: "22.15" @@ -37,12 +37,12 @@ jobs: uses: trstringer/manual-approval@v1 with: secret: ${{ secrets.GITHUB_TOKEN }} - approvers: ArJSarmiento,ASPactores,seangaaab + approvers: kindadailybren, prcsmae, seangaaab minimum-approvals: 1 - issue-title: "Deploying stage to main" + issue-title: "Deploying dev to main" issue-body: "Please approve or deny the deployment." - - name: Sync stage to main + - name: Sync dev to main uses: connor-baer/action-sync-branch@main with: branch: main diff --git a/.github/workflows/FE-pr-and-deploy-to-stage-pipeline.yml b/.github/workflows/fe_pr_and_deploy.yml similarity index 76% rename from .github/workflows/FE-pr-and-deploy-to-stage-pipeline.yml rename to .github/workflows/fe_pr_and_deploy.yml index a056d770..307fd250 100644 --- a/.github/workflows/FE-pr-and-deploy-to-stage-pipeline.yml +++ b/.github/workflows/fe_pr_and_deploy.yml @@ -1,15 +1,15 @@ -name: Build and Deploy PR to stage +name: Build and Deploy PR to dev on: push: - branches: [stage] + branches: [dev] pull_request: - branches: [stage] + branches: [dev] workflow_dispatch: jobs: build-and-deploy: - name: Build and Deploy PR to stage + name: Build and Deploy PR to dev runs-on: ubuntu-latest defaults: run: @@ -32,6 +32,6 @@ jobs: run: npm run build:dummy - name: Deploy to Amplify - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'workflow_dispatch' run: | curl -X POST -d '{}' "${{ secrets.AMPLIFY_WEBHOOK_URL }}" -H "Content-Type:application/json" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1938d902 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing to the Project + +Thank you for your interest in contributing! To maintain a clean history and ensure a smooth development process, we follow a specific workflow. Please follow these guidelines to get started. + +--- + +## 1. Getting Started + +### Fork the Repository + +First, create your own copy of the repository by clicking the **Fork** button at the top of the page. Clone your fork to your local machine: + +```bash +git clone https://github.com/YOUR-USERNAME/repository-name.git +cd repository-name + +``` + +### Configure Upstream + +Add the original repository as a remote to keep your local fork in sync: + +```bash +git remote add upstream https://github.com/ORIGINAL-OWNER/repository-name.git + +``` + +--- + +## 2. Branching and Development + +### Create a Feature Branch + +Before starting any work, create a new branch. We use **Conventional Naming** based on the issue or ticket you are addressing: + +- `feat/issue-ID` (e.g., `feat/123-add- login-logic`) +- `fix/issue-ID` (e.g., `fix/456-patch-security-hole`) +- `docs/issue-ID` +- `refactor/issue-ID` + +```bash +git checkout -b feat/123-short-description +``` + +See [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#specification). + +### Stay in Sync (Rebase Flow) + +Our repository uses a **rebase flow** to keep the history linear. When pulling updates from the main repository, always use the `--rebase` (or `-r`) flag to avoid merge commits: + +```bash +git pull -r upstream main + +``` + +--- + +## 3. Commit Guidelines + +We follow the **Conventional Commits** specification. Commits should be structured as follows: + +`(): ` + +- **feat**: A new feature +- **fix**: A bug fix +- **docs**: Documentation only changes +- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc) +- **refactor**: A code change that neither fixes a bug nor adds a feature + +**Example:** +`feat(auth): add JWT validation to login endpoint` + +See [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#specification). + +--- + +## 4. Submitting a Pull Request + +Once you have completed your work and verified it locally: + +1. **Push to your fork:** + +```bash +git push origin your-branch-name +``` + +2. **Raise a PR:** Navigate to the original repository on GitHub. You will see a prompt to "Compare & pull request" from your fork. +3. **Target Branch:** Ensure your PR is targeting the `main` repository's `dev` or `main` branch as specified in the issue. +4. **Review:** Wait for the maintainers to review your code. We may request changes before merging. + +Thank you for helping us improve the project! diff --git a/README.md b/README.md index ff26295f..c2c7432d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SPARCS Events Platform +# DurianPy Events Platform ## Tech Stack - Serverless Framework diff --git a/backend/CONTRIBUITING.md b/backend/CONTRIBUITING.md new file mode 100644 index 00000000..cb313be0 --- /dev/null +++ b/backend/CONTRIBUITING.md @@ -0,0 +1,404 @@ +# Contributing to SPARCS TECHTIX API + +Thank you for your interest in contributing to the SPARCS Events API! This document provides guidelines and best practices for contributing to this project. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Coding Conventions](#coding-conventions) +- [Commit Guidelines](#commit-guidelines) +- [Pull Request Process](#pull-request-process) +- [Code Review](#code-review) + +## Code of Conduct + +Please be respectful and constructive in all interactions. We aim to maintain a welcoming and inclusive environment for all contributors. + +## Getting Started + +1. Follow the setup instructions in the [README.md](README.md) +2. Create a new branch for your feature or bugfix: + ```shell + git checkout -b feature/your-feature-name + ``` +3. Make your changes following the conventions below +4. Test your changes thoroughly +5. Submit a pull request + +## Coding Conventions + +### Python Style Guide + +We follow PEP 8 with some project-specific conventions: + +#### Naming Conventions + +- **snake_case** for variables, functions, methods, and module names: + ```python + def calculate_total_price(item_count, unit_price): + total_price = item_count * unit_price + return total_price + ``` + +- **PascalCase** for class names: + ```python + class UserRepository: + pass + + class EventRegistration: + pass + ``` + +- **UPPER_SNAKE_CASE** for constants: + ```python + MAX_RETRY_ATTEMPTS = 3 + DEFAULT_TIMEOUT_SECONDS = 30 + API_BASE_URL = "https://api.example.com" + ``` + +#### Tuple Deconstruction on Returns + +When returning multiple values, use tuple deconstruction for clarity: + +**βœ… Good:** +```python +def get_user_info(user_id: str) -> tuple[str, str, int]: + """ + Retrieves user information. + + :param user_id: The unique identifier for the user + :type user_id: str + + :return: A tuple containing (username, email, age) + :rtype: tuple[str, str, int] + """ + # ... implementation + return username, email, age + +# Usage with tuple deconstruction +username, email, age = get_user_info("123") +``` + +**❌ Bad:** +```python +def get_user_info(user_id: str) -> dict: + # ... implementation + return {"username": username, "email": email, "age": age} + +# Less clear usage +user_info = get_user_info("123") +username = user_info["username"] +``` + +For complex returns with many values, prefer using dataclasses or Pydantic models: + +```python +from dataclasses import dataclass + +@dataclass +class UserInfo: + username: str + email: str + age: int + created_at: datetime + is_active: bool + +def get_user_info(user_id: str) -> UserInfo: + """ + Retrieves detailed user information. + + :param user_id: The unique identifier for the user + :type user_id: str + + :return: User information object + :rtype: UserInfo + """ + # ... implementation + return UserInfo( + username=username, + email=email, + age=age, + created_at=created_at, + is_active=is_active + ) +``` + +#### Docstrings + +Use reStructuredText (reST) format as specified in PEP 287: + +```python +def process_payment(amount: float, currency: str, payment_method: str) -> tuple[bool, str]: + """ + Processes a payment transaction. + + :param amount: The payment amount + :type amount: float + + :param currency: The currency code (e.g., 'USD', 'EUR') + :type currency: str + + :param payment_method: The payment method identifier + :type payment_method: str + + :return: A tuple containing (success_status, transaction_id) + :rtype: tuple[bool, str] + + :raises ValueError: If amount is negative or currency is invalid + :raises PaymentProcessingError: If payment processing fails + """ + # ... implementation + return success, transaction_id +``` + +#### Type Hints + +Always use type hints for function parameters and return values: + +```python +from typing import Optional, List, Dict + +def find_events( + category: str, + start_date: Optional[datetime] = None, + limit: int = 10 +) -> List[Dict[str, any]]: + """Find events matching the criteria.""" + # ... implementation + return events +``` + +#### Clean Architecture Principles + +Follow the dependency rule: **dependencies point inward** + +```python +# ❌ Bad: Domain layer depending on infrastructure +# model/event.py +from repository.event_repository import EventRepository # Wrong! + +class Event: + def save(self): + repo = EventRepository() + repo.save(self) + +# βœ… Good: Infrastructure depends on domain +# repository/event_repository.py +from model.event import Event # Correct! + +class EventRepository: + def save(self, event: Event) -> bool: + # ... implementation + return success +``` + +Layer structure: +1. **Domain (`model/`)** - No dependencies on other layers +2. **Application (`usecase/`)** - Depends on domain only +3. **Infrastructure (`repository/`, `aws/`)** - Depends on domain and application +4. **Presentation (`controller/`)** - Depends on application and domain +5. **External (`external_gateway/`, `functions/`)** - Outermost layer + +## Commit Guidelines + +We follow [Conventional Commits](https://www.conventionalcommits.org/) specification. + +### Commit Message Format + +``` +(): + + + +