Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
854aa64
feat: allow late task submissions within 12-hour grace period and rai…
Khushal928 Jul 12, 2025
4c264ce
feat: update schemas as per praveshan requirements
Khushal928 Jul 14, 2025
30b5216
added the role
ganidande905 Jul 22, 2025
8dfe9b5
Merge pull request #32 from Khushal928/praveshan-develop
ganidande905 Jul 22, 2025
f01d6bc
Add alembic version for new migration
ganidande905 Jul 22, 2025
098f2a7
Merge pull request #33 from ganidande905/praveshan-develop-local
ganidande905 Jul 22, 2025
c450139
Merge pull request #35 from amfoss/praveshan-develop
ganidande905 Jul 22, 2025
b6ab34b
auto-sync users from Google Sheet into DB
ganidande905 Jul 22, 2025
38177a5
Merge pull request #36 from amfoss/praveshan-develop
ganidande905 Jul 22, 2025
36035a9
feat: add commit_hash into excel sheet upon task submission
Khushal928 Jul 24, 2025
3ca216e
Merge pull request #37 from Khushal928/praveshan-develop
ganidande905 Jul 24, 2025
16c7dc7
Merge pull request #38 from amfoss/praveshan-develop
ganidande905 Jul 24, 2025
586117f
deploy on digital ocean
ganidande905 Jul 30, 2025
ab62851
Merge pull request #39 from amfoss/praveshan-develop
ganidande905 Jul 30, 2025
749aa3e
change apple boy version
ganidande905 Jul 30, 2025
d25b78b
Merge pull request #40 from amfoss/praveshan-develop
ganidande905 Jul 30, 2025
be9d4df
remove deploy
ganidande905 Jul 30, 2025
0b6842a
Merge pull request #41 from amfoss/praveshan-develop
ganidande905 Jul 30, 2025
5b64c0b
update env variables
ganidande905 Jul 30, 2025
1d4b9e7
Merge pull request #42 from amfoss/praveshan-develop
ganidande905 Jul 30, 2025
251d388
deploy to droplet
ganidande905 Jul 31, 2025
beed079
Merge pull request #43 from amfoss/praveshan-develop
ganidande905 Jul 31, 2025
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
3 changes: 2 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
AMMENTOR_DB_URL=
PRAVESHAN_DB_URL=
SMTP_EMAIL=
SMTP_PASSWORD=
GOOGLE_SHEET_ID=
104 changes: 60 additions & 44 deletions .github/workflows/ghcr-deploy.yml
Original file line number Diff line number Diff line change
@@ -1,55 +1,71 @@
# Inspired from: https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images
name: Create and publish Docker image to GHCR
# Inspired the workflow from https://faun.pub/full-ci-cd-with-docker-github-actions-digitalocean-droplets-container-registry-db2938db8246
name: CI

# Configures this workflow to run every time a change is pushed to the branch called `release`.
# 1
# Controls when the workflow will run
on:
workflow_dispatch:
# Triggers the workflow on push events but only for the master branch
push:
branches: ['production']
branches: [ praveshan ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
inputs:
version:
description: 'Image version'
required: true
#2
env:
REGISTRY: "registry.digitalocean.com/praveshan"
IMAGE_NAME: "ammentor-backend-praveshan"

#3
jobs:
build-and-push-image:
build_and_push:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Checkout the repo
uses: actions/checkout@v2

- name: Build container image
run: docker build -t $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) .

# Uses the `docker/login-action` action to log in to the Github Container Registry
- name: Log in to the Container registry
uses: docker/login-action@v3
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

- name: Log in to DigitalOcean Container Registry with short-lived credentials
run: doctl registry login --expiry-seconds 600

- name: Remove all old images
run: if [ ! -z "$(doctl registry repository list | grep "$(echo $IMAGE_NAME)")" ]; then doctl registry repository delete-manifest $(echo $IMAGE_NAME) $(doctl registry repository list-tags $(echo $IMAGE_NAME) | grep -o "sha.*") --force; else echo "No repository"; fi

# This step uses `docker/metadata-action` to extract tags and labels that will be applied to the specified image.
# The `id` "meta" allows the output of this step to be referenced in a subsequent step.
# The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/amfoss/amMentor-backend
tags: |
# set latest tag for master branch
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'production') }},priority=2000
type=schedule,pattern={{date 'YYYYMMDD'}}
type=ref,event=tag
type=ref,event=pr
type=sha

# This step uses the `docker/build-push-action` action to build the image. If the build succeeds, it pushes the image to GitHub Packages.
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
- name: Push image to DigitalOcean Container Registry
run: docker push $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7)

deploy:
runs-on: ubuntu-latest
needs: build_and_push

steps:
- name: Deploy to Digital Ocean droplet via SSH action
uses: appleboy/ssh-action@master
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSHKEY }}
passphrase: ${{ secrets.PASSPHRASE }}
envs: IMAGE_NAME,REGISTRY,{{ secrets.DIGITALOCEAN_ACCESS_TOKEN }},GITHUB_SHA
script: |
# Login to registry
docker login -u ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} -p ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} registry.digitalocean.com
# Stop running container
docker stop $(echo $IMAGE_NAME)
# Remove old container
docker rm $(echo $IMAGE_NAME)
# Run a new container from a new image
docker run -d \
--restart always \
--name $(echo $IMAGE_NAME) \
$(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7)
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ dist/
*.bak
*.tmp
txt.txt
credentials.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Add start_date to submissions
"""nullable = true for group name

Revision ID: 398274d89756
Revises: e0d610d30201
Create Date: 2025-06-02 18:41:29.709189
Revision ID: 12599f674ee9
Revises: 1d2a6bece126
Create Date: 2025-07-23 01:16:53.822252

"""
from typing import Sequence, Union
Expand All @@ -12,21 +12,25 @@


# revision identifiers, used by Alembic.
revision: str = '398274d89756'
down_revision: Union[str, None] = 'e0d610d30201'
revision: str = '12599f674ee9'
down_revision: Union[str, None] = '1d2a6bece126'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('submissions', sa.Column('start_date', sa.DateTime(), nullable=False))
op.alter_column('users', 'group_name',
existing_type=sa.VARCHAR(),
nullable=True)
# ### end Alembic commands ###


def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('submissions', 'start_date')
op.alter_column('users', 'group_name',
existing_type=sa.VARCHAR(),
nullable=False)
# ### end Alembic commands ###
123 changes: 123 additions & 0 deletions alembic/versions/1d2a6bece126_initial_schema_for_new_branch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""Initial schema for new branch

Revision ID: 1d2a6bece126
Revises:
Create Date: 2025-07-23 00:20:39.735170

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '1d2a6bece126'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('otp',
sa.Column('email', sa.String(), nullable=False),
sa.Column('otp', sa.String(), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('email')
)
op.create_index(op.f('ix_otp_email'), 'otp', ['email'], unique=False)
op.create_table('tracks',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('title', sa.String(), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('title')
)
op.create_index(op.f('ix_tracks_id'), 'tracks', ['id'], unique=False)
op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('role', sa.String(), nullable=False),
sa.Column('group_name', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
op.create_table('leaderboard',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('track_id', sa.Integer(), nullable=True),
sa.Column('mentee_id', sa.Integer(), nullable=True),
sa.Column('total_points', sa.Integer(), nullable=True),
sa.Column('tasks_completed', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['mentee_id'], ['users.id'], ),
sa.ForeignKeyConstraint(['track_id'], ['tracks.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_leaderboard_id'), 'leaderboard', ['id'], unique=False)
op.create_table('mentor_mentee_map',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('mentor_id', sa.Integer(), nullable=False),
sa.Column('mentee_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['mentee_id'], ['users.id'], ),
sa.ForeignKeyConstraint(['mentor_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('mentor_id', 'mentee_id', name='unique_mentor_mentee')
)
op.create_index(op.f('ix_mentor_mentee_map_id'), 'mentor_mentee_map', ['id'], unique=False)
op.create_table('tasks',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('track_id', sa.Integer(), nullable=False),
sa.Column('task_no', sa.Integer(), nullable=False),
sa.Column('title', sa.String(), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('points', sa.Integer(), nullable=True),
sa.Column('deadline_days', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['track_id'], ['tracks.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('track_id', 'task_no', name='unique_track_task')
)
op.create_index(op.f('ix_tasks_id'), 'tasks', ['id'], unique=False)
op.create_table('submissions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('mentee_id', sa.Integer(), nullable=False),
sa.Column('task_id', sa.Integer(), nullable=False),
sa.Column('task_name', sa.String(), nullable=False),
sa.Column('task_no', sa.Integer(), nullable=False),
sa.Column('reference_link', sa.Text(), nullable=False),
sa.Column('status', sa.String(), nullable=True),
sa.Column('submitted_at', sa.DateTime(), nullable=True),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('approved_at', sa.DateTime(), nullable=True),
sa.Column('mentor_feedback', sa.Text(), nullable=True),
sa.Column('submitted_late', sa.Boolean(), nullable=True),
sa.Column('commit_hash', sa.String(), nullable=False),
sa.ForeignKeyConstraint(['mentee_id'], ['users.id'], ),
sa.ForeignKeyConstraint(['task_id'], ['tasks.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_submissions_id'), 'submissions', ['id'], unique=False)
# ### end Alembic commands ###


def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_submissions_id'), table_name='submissions')
op.drop_table('submissions')
op.drop_index(op.f('ix_tasks_id'), table_name='tasks')
op.drop_table('tasks')
op.drop_index(op.f('ix_mentor_mentee_map_id'), table_name='mentor_mentee_map')
op.drop_table('mentor_mentee_map')
op.drop_index(op.f('ix_leaderboard_id'), table_name='leaderboard')
op.drop_table('leaderboard')
op.drop_index(op.f('ix_users_id'), table_name='users')
op.drop_index(op.f('ix_users_email'), table_name='users')
op.drop_table('users')
op.drop_index(op.f('ix_tracks_id'), table_name='tracks')
op.drop_table('tracks')
op.drop_index(op.f('ix_otp_email'), table_name='otp')
op.drop_table('otp')
# ### end Alembic commands ###
32 changes: 0 additions & 32 deletions alembic/versions/71c51601b989_add_testing_column_to_tracks.py

This file was deleted.

This file was deleted.

32 changes: 0 additions & 32 deletions alembic/versions/e0d610d30201_removed_testing_column_to_tracks.py

This file was deleted.

Loading