Skip to content

Commit

Permalink
Merge branch 'release/0.0.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
pipisebastian committed Nov 13, 2024
2 parents 9d67be6 + 5bed8b7 commit 8823830
Show file tree
Hide file tree
Showing 125 changed files with 11,684 additions and 2,730 deletions.
6 changes: 1 addition & 5 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
## ๐Ÿ“‹ ๊ฐœ์š”

- ์ด๋ฒˆ PR์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ๋˜๋Š” ๊ตฌํ˜„ํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์„ค๋ช…์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

## ๐Ÿ“ ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์š”์•ฝ
## ๐Ÿ“ ๋ณ€๊ฒฝ ์‚ฌํ•ญ

- ๋ณ€๊ฒฝ๋œ ์ฃผ์š” ์‚ฌํ•ญ์„ bullet point๋กœ ์ •๋ฆฌํ•˜์—ฌ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

Expand Down
77 changes: 77 additions & 0 deletions .github/workflows/auto_merge_approved_pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: "Auto Merge Approved PRs"

on:
pull_request_review:
types: [submitted]

jobs:
auto_merge:
runs-on: ubuntu-latest
steps:
- name: "Check Approvals"
id: check
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const pull_number = context.payload.pull_request.number;
const reviews = await github.rest.pulls.listReviews({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pull_number,
});
const approvals = reviews.data.filter(review => review.state === 'APPROVED');
const approvalCount = approvals.length;
core.info(`PR #${pull_number} has ${approvalCount} approval(s).`);
return approvalCount >= 2;
- name: "Merge PR"
if: ${{ steps.check.outputs.result == 'true' }}
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const pull_number = context.payload.pull_request.number;
// PR ์ •๋ณด ๋ฐ ๋ณ‘ํ•ฉ ๊ฐ€๋Šฅ ์ƒํƒœ ํ™•์ธ
let pr;
for (let i = 0; i < 5; i++) { // ์ตœ๋Œ€ 5ํšŒ ์žฌ์‹œ๋„
const { data } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pull_number,
});
pr = data;
if (pr.state !== 'open') {
core.info(`PR #${pull_number} is not open (state: ${pr.state}).`);
return;
}
if (pr.mergeable === true) {
break; // ๋ณ‘ํ•ฉ ๊ฐ€๋Šฅํ•˜๋ฉด ๋ฃจํ”„ ์ข…๋ฃŒ
} else if (pr.mergeable === false) {
core.info(`PR #${pull_number} cannot be merged due to conflicts or other issues.`);
return;
} else {
// mergeable์ด null์ธ ๊ฒฝ์šฐ (๊ณ„์‚ฐ ์ค‘), ์ž ์‹œ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„
await new Promise(resolve => setTimeout(resolve, 2000)); // 2์ดˆ ๋Œ€๊ธฐ
}
}
if (pr.mergeable !== true) {
core.info(`PR #${pull_number} mergeable status is unknown after retries.`);
return;
}
// PR ๋ณ‘ํ•ฉ ์‹œ๋„
try {
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pull_number,
});
core.info(`PR #${pull_number} has been merged successfully.`);
} catch (error) {
core.info(`Failed to merge PR #${pull_number}: ${error.message}`);
}
27 changes: 27 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Deploy on Server

on:
push:
branches:
- main

jobs:
deploy:
runs-on: [self-hosted, boost-was] # ๋ผ๋ฒจ์— ํ•ด๋‹นํ•˜๋Š” runner๋กœ ์‹คํ–‰

steps:
# 1. ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํด๋ก 
- name: Checkout Repository
uses: actions/checkout@v4

# 2. Docker Compose๋กœ ์„œ๋น„์Šค ๋นŒ๋“œ ๋ฐ ์žฌ์‹œ์ž‘
- name: Build and Deploy Docker Images
env:
NODE_ENV: production
MONGODB_URI: ${{ secrets.MONGODB_URI }}
run: |
docker-compose up -d --build
# 3. Clean up Old Images
- name: Remove Dangling Images
run: docker image prune -f
43 changes: 43 additions & 0 deletions .github/workflows/lint_and_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Lint and Test

on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev

jobs:
lint_and_test:
name: Lint and Test
runs-on: ubuntu-latest

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

# Install pnpm
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true

# Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"

# Run lint
- name: Run lint
run: pnpm eslint .

# Run tests
- name: Run tests
run: pnpm test
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/node_modules
**/node_modules

/dist
*/dist
/build
.DS_Store
.env
16 changes: 9 additions & 7 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"arrowParens": "always",
"printWidth": 100,
"tabWidth": 2,
"arrowParens": "always",
"endOfLine": "auto",
"bracketSpacing": true
}
"jsxSingleQuote": false,
"singleQuote": false,

"plugins": ["@pandabox/prettier-plugin"],
"pandaFirstProps": ["as", "className", "layerStyle", "textStyle"],
"pandaStylePropsFirst": true,
"pandaSortOtherProps": true
}
15 changes: 15 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.workingDirectories": [
{
"pattern": "./packages/*/"
}
],
"eslint.useFlatConfig": true,
"eslint.validate": ["javascript", "typescript", "javascriptreact", "html", "typescriptreact"],
"eslint.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
}
}
129 changes: 129 additions & 0 deletions @noctaCrdt/Crdt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { LinkedList } from "./LinkedList";
import { NodeId, Node } from "./Node";
import { RemoteInsertOperation, RemoteDeleteOperation, SerializedProps } from "./Interfaces";

export class CRDT {
clock: number;
client: number;
textLinkedList: LinkedList;

constructor(client: number) {
this.clock = 0; // ์ด CRDT์˜ ๋…ผ๋ฆฌ์  ์‹œ๊ฐ„ ์„ค์ •
this.client = client;
this.textLinkedList = new LinkedList();
}

/**
* ๋กœ์ปฌ์—์„œ ์‚ฝ์ž… ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์›๊ฒฉ์— ์ „ํŒŒํ•  ์—ฐ์‚ฐ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
* @param index ์‚ฝ์ž…ํ•  ์ธ๋ฑ์Šค
* @param value ์‚ฝ์ž…ํ•  ๊ฐ’
* @returns ์›๊ฒฉ์— ์ „ํŒŒํ•  ์‚ฝ์ž… ์—ฐ์‚ฐ ๊ฐ์ฒด
*/
localInsert(index: number, value: string): RemoteInsertOperation {
const id = new NodeId((this.clock += 1), this.client);
const remoteInsertion = this.textLinkedList.insertAtIndex(index, value, id);
return { node: remoteInsertion.node };
}

/**
* ๋กœ์ปฌ์—์„œ ์‚ญ์ œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์›๊ฒฉ์— ์ „ํŒŒํ•  ์—ฐ์‚ฐ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
* @param index ์‚ญ์ œํ•  ์ธ๋ฑ์Šค
* @returns ์›๊ฒฉ์— ์ „ํŒŒํ•  ์‚ญ์ œ ์—ฐ์‚ฐ ๊ฐ์ฒด
*/
localDelete(index: number): RemoteDeleteOperation {
// ์œ ํšจํ•œ ์ธ๋ฑ์Šค์ธ์ง€ ํ™•์ธ
if (index < 0 || index >= this.textLinkedList.spread().length) {
throw new Error(`์œ ํšจํ•˜์ง€ ์•Š์€ ์ธ๋ฑ์Šค์ž…๋‹ˆ๋‹ค: ${index}`);
}

// ์‚ญ์ œํ•  ๋…ธ๋“œ ์ฐพ๊ธฐ
const nodeToDelete = this.textLinkedList.findByIndex(index);
if (!nodeToDelete) {
throw new Error(`์‚ญ์ œํ•  ๋…ธ๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ธ๋ฑ์Šค: ${index}`);
}

// ์‚ญ์ œ ์—ฐ์‚ฐ ๊ฐ์ฒด ์ƒ์„ฑ
const operation: RemoteDeleteOperation = {
targetId: nodeToDelete.id,
clock: this.clock + 1,
};

// ๋กœ์ปฌ ์‚ญ์ œ ์ˆ˜ํ–‰
this.textLinkedList.deleteNode(nodeToDelete.id);

// ํด๋ก ์—…๋ฐ์ดํŠธ
this.clock += 1;

return operation;
}

/**
* ์›๊ฒฉ์—์„œ ์‚ฝ์ž… ์—ฐ์‚ฐ์„ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
* @param operation ์›๊ฒฉ ์‚ฝ์ž… ์—ฐ์‚ฐ ๊ฐ์ฒด
*/
remoteInsert(operation: RemoteInsertOperation): void {
const newNodeId = new NodeId(operation.node.id.clock, operation.node.id.client);
const newNode = new Node(operation.node.value, newNodeId);
newNode.next = operation.node.next;
newNode.prev = operation.node.prev;
this.textLinkedList.insertById(newNode);
// ๋™๊ธฐํ™” ๋…ผ๋ฆฌ์  ์‹œ๊ฐ„
if (this.clock <= newNode.id.clock) {
this.clock = newNode.id.clock + 1;
}
}

/**
* ์›๊ฒฉ์—์„œ ์‚ญ์ œ ์—ฐ์‚ฐ์„ ์ˆ˜์‹ ํ–ˆ์„๋•Œ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
* @param operation ์›๊ฒฉ ์‚ญ์ œ ์—ฐ์‚ฐ ๊ฐ์ฒด
*/
remoteDelete(operation: RemoteDeleteOperation): void {
const { targetId, clock } = operation;
if (targetId) {
this.textLinkedList.deleteNode(targetId);
}
// ๋™๊ธฐํ™” ๋…ผ๋ฆฌ์  ์‹œ๊ฐ„
if (this.clock <= clock) {
this.clock = clock + 1;
}
}

/**
* ํ˜„์žฌ ํ…์ŠคํŠธ๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
* @returns ํ˜„์žฌ ํ…์ŠคํŠธ
*/
read(): string {
return this.textLinkedList.stringify();
}

/**
* ํ˜„์žฌ ํ…์ŠคํŠธ๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
* @returns ํ˜„์žฌ ํ…์ŠคํŠธ ๋ฐฐ์—ด
*/
spread(): string[] {
return this.textLinkedList.spread();
}

/**
* textLinkedList๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” getter ๋ฉ”์„œ๋“œ
* @returns LinkedList ์ธ์Šคํ„ด์Šค
*/
public getTextLinkedList(): LinkedList {
return this.textLinkedList;
}

/**
* CRDT์˜ ์ƒํƒœ๋ฅผ ์ง๋ ฌํ™” ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
* @returns ์ง๋ ฌํ™” ๊ฐ€๋Šฅํ•œ CRDT ์ƒํƒœ
*/
serialize(): SerializedProps {
return {
clock: this.clock,
client: this.client,
textLinkedList: {
head: this.textLinkedList.head,
nodeMap: this.textLinkedList.nodeMap,
},
};
}
}
32 changes: 32 additions & 0 deletions @noctaCrdt/Interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { NodeId, Node } from "./Node";

export interface InsertOperation {
node: Node;
}

export interface DeleteOperation {
targetId: NodeId | null;
clock: number;
}
export interface RemoteInsertOperation {
node: Node;
}

export interface RemoteDeleteOperation {
targetId: NodeId | null;
clock: number;
}

export interface CursorPosition {
clientId: number;
position: number;
}

export interface SerializedProps {
clock: number;
client: number;
textLinkedList: {
head: NodeId | null;
nodeMap: { [key: string]: Node };
};
}
Loading

0 comments on commit 8823830

Please sign in to comment.