diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index f0ad4f90..545659f0 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -4,7 +4,6 @@ on:
branches:
- main
pull_request:
- pull_request_target:
jobs:
reviewdog:
name: reviewdog
diff --git a/.gitignore b/.gitignore
index e6297c16..fb8e26da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ sandbox/
*.log
/frontend/.next
/frontend/.cache
+/frontend/cache
/frontend/public/sw.*
/frontend/public/workbox-*
/frontend/storybook-static
diff --git a/.reviewdog.yml b/.reviewdog.yml
index 3d101a76..7933b36f 100644
--- a/.reviewdog.yml
+++ b/.reviewdog.yml
@@ -2,6 +2,9 @@ runner:
eslint:
cmd: cd frontend && yarn -s run eslint src --ext .js,.jsx,.ts,.tsx -f=rdjson
format: rdjson
+ stylelint:
+ cmd: cd frontend && yarn -s run stylelint 'src/**/*.css' 'src/**/*.scss'
+ format: stylelint
tsc:
cmd: cd frontend && yarn -s run tsc
format: tsc
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000..fc043d5f
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ "EditorConfig.EditorConfig",
+ "dbaeumer.vscode-eslint",
+ "stylelint.vscode-stylelint",
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index fd79c9e5..763ca543 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -14,4 +14,19 @@
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true,
},
+
+ // stylelint
+ "[css]": {
+ "editor.defaultFormatter": "stylelint.vscode-stylelint",
+ "editor.formatOnSave": true,
+ },
+ "[scss]": {
+ "editor.defaultFormatter": "stylelint.vscode-stylelint",
+ "editor.formatOnSave": true,
+ },
+
+ "[json]": {
+ "editor.defaultFormatter": "vscode.json-language-features",
+ "editor.formatOnSave": true,
+ },
}
diff --git a/backend/Dockerfile b/backend/Dockerfile
index 31503f10..88e3f4a7 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -36,6 +36,13 @@ RUN python3 -m pip install -r /backend/requirements.txt --no-cache-dir
COPY --from=nsjail /bin/nsjail /bin/nsjail
+# nsjail wants to mount this so ensure it exists
+RUN mkdir -p /lib32
+
+COPY compilers/download.sh /compilers/
+
+RUN bash /compilers/download.sh
+
WORKDIR /backend
ENTRYPOINT ["/backend/docker_entrypoint.sh"]
diff --git a/backend/compilers/.gitignore b/backend/compilers/.gitignore
index 802cd1f7..b0d0e99b 100644
--- a/backend/compilers/.gitignore
+++ b/backend/compilers/.gitignore
@@ -1,4 +1,4 @@
*
-!gcc2.8.1/
-!ido5.3/
-!ido7.1/
+!gcc2.8.1
+!ido5.3
+!ido7.1
diff --git a/backend/compilers/download.sh b/backend/compilers/download.sh
index c0b4cd94..e0cf0465 100755
--- a/backend/compilers/download.sh
+++ b/backend/compilers/download.sh
@@ -13,11 +13,14 @@ else
fi
# gcc2.8.1
+mkdir -p "$compiler_dir/gcc2.8.1"
curl -L "https://github.com/pmret/gcc-papermario/releases/download/master/$os.tar.gz" | tar zx -C "$compiler_dir/gcc2.8.1"
curl -L "https://github.com/pmret/binutils-papermario/releases/download/master/$os.tar.gz" | tar zx -C "$compiler_dir/gcc2.8.1"
# ido5.3
-curl -L "https://github.com/ethteck/ido-static-recomp/releases/download/master/ido-5.3-recomp-$ido_os-latest.tar.gz" | tar zx -C "$compiler_dir/ido5.3/"
+mkdir -p "$compiler_dir/ido5.3"
+curl -L "https://github.com/ethteck/ido-static-recomp/releases/download/master/ido-5.3-recomp-$ido_os-latest.tar.gz" | tar zx -C "$compiler_dir/ido5.3"
# ido7.1
-curl -L "https://github.com/ethteck/ido-static-recomp/releases/download/master/ido-7.1-recomp-$ido_os-latest.tar.gz" | tar zx -C "$compiler_dir/ido7.1/"
+mkdir -p "$compiler_dir/ido7.1"
+curl -L "https://github.com/ethteck/ido-static-recomp/releases/download/master/ido-7.1-recomp-$ido_os-latest.tar.gz" | tar zx -C "$compiler_dir/ido7.1"
diff --git a/backend/coreapp/compiler_wrapper.py b/backend/coreapp/compiler_wrapper.py
index 2e16e1c2..90aa7ea8 100644
--- a/backend/coreapp/compiler_wrapper.py
+++ b/backend/coreapp/compiler_wrapper.py
@@ -1,4 +1,5 @@
from typing import Dict, List, Optional, Set, Tuple
+from collections import OrderedDict
from coreapp.models import Asm, Assembly, Compilation
from coreapp import util
from coreapp.sandbox import Sandbox
@@ -38,9 +39,10 @@ else:
def load_compilers() -> Dict[str, Dict[str, str]]:
ret = {}
- compiler_dirs = next(os.walk(settings.COMPILER_BASE_PATH))
+ compilers_base = settings.BASE_DIR / "compilers"
+ compiler_dirs = next(os.walk(compilers_base))
for compiler_id in compiler_dirs[1]:
- config_path = Path(settings.COMPILER_BASE_PATH / compiler_id / "config.json")
+ config_path = Path(compilers_base / compiler_id / "config.json")
if config_path.exists():
with open(config_path) as f:
ret[compiler_id] = json.load(f)
@@ -51,6 +53,7 @@ def load_compilers() -> Dict[str, Dict[str, str]]:
@dataclass
class Arch:
name: str
+ description: str
assemble_cmd: Optional[str] = None
objdump_cmd: Optional[str] = None
nm_cmd: Optional[str] = None
@@ -59,13 +62,15 @@ class Arch:
def load_arches() -> Dict[str, Arch]:
return {
"mips": Arch(
- "MIPS (Nintendo 64)",
+ "Nintendo 64",
+ "MIPS (big-endian)",
assemble_cmd='mips-linux-gnu-as -march=vr4300 -mabi=32 -o "$OUTPUT" "$INPUT"',
objdump_cmd="mips-linux-gnu-objdump",
nm_cmd="mips-linux-gnu-nm",
),
"mipsel": Arch(
- "MIPS (LE)",
+ "PlayStation 2",
+ "MIPS (little-endian)",
assemble_cmd='mips-linux-gnu-as -march=mips64 -mabi=64 -o "$OUTPUT" "$INPUT"',
objdump_cmd="mips-linux-gnu-objdump",
nm_cmd="mips-linux-gnu-nm",
@@ -120,17 +125,18 @@ class CompilerWrapper:
return {k: {"arch": CompilerWrapper.arch_from_compiler(k)} for k in CompilerWrapper.available_compiler_ids()}
@staticmethod
- def available_arches() -> List[Tuple[str, str]]:
+ def available_arches() -> OrderedDict[str, Dict[str, str]]:
a_set: Set[str] = set()
- ret = []
+ ret = OrderedDict()
for id in CompilerWrapper.available_compiler_ids():
a_set.add(_compilers[id]["arch"])
- for a in a_set:
- ret.append((a, _arches[a].name))
-
- ret.sort(key=lambda x: x[0])
+ for a in sorted(a_set):
+ ret[a] = {
+ "name": _arches[a].name,
+ "description": _arches[a].description,
+ }
return ret
diff --git a/backend/coreapp/sandbox.py b/backend/coreapp/sandbox.py
index 01629ad2..504f9f46 100644
--- a/backend/coreapp/sandbox.py
+++ b/backend/coreapp/sandbox.py
@@ -55,6 +55,7 @@ class Sandbox(contextlib.AbstractContextManager["Sandbox"]):
"--bindmount_ro", "/lib32",
"--bindmount_ro", "/lib64",
"--bindmount_ro", "/usr",
+ "--bindmount_ro", str(settings.COMPILER_BASE_PATH),
"--env", "PATH=/usr/bin:/bin",
"--disable_proc", # Needed for running inside Docker
"--time_limit", "30", # seconds
diff --git a/backend/decompme/settings.py b/backend/decompme/settings.py
index 41a365bd..a62a82c6 100644
--- a/backend/decompme/settings.py
+++ b/backend/decompme/settings.py
@@ -25,6 +25,7 @@ env = environ.Env(
SESSION_COOKIE_SECURE=(bool, True),
GITHUB_CLIENT_ID=(str, ""),
GITHUB_CLIENT_SECRET=(str, ""),
+ COMPILER_BASE_PATH=(str, BASE_DIR / "compilers")
)
for stem in [".env.local", ".env"]:
@@ -150,7 +151,7 @@ if DEBUG:
else:
SESSION_COOKIE_SAMESITE = "Lax"
-COMPILER_BASE_PATH = BASE_DIR / "compilers"
+COMPILER_BASE_PATH = Path(env("COMPILER_BASE_PATH"))
LOCAL_FILE_DIR = BASE_DIR / "local_files"
USE_SANDBOX_JAIL = env("USE_SANDBOX_JAIL")
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 0e2c5924..2e33381e 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -21,8 +21,9 @@ services:
DATABASE_URL: psql://decompme:decompme@postgres:5432/decompme
SECRET_KEY: "django-insecure-nm#!8%z$$hc0wwi#m_*l9l)=m*6gs4&o_^-e5b5vj*k05&yaqc1"
DEBUG: "on"
- ALLOWED_HOSTS: "localhost,127.0.0.1"
+ ALLOWED_HOSTS: "backend,localhost,127.0.0.1"
USE_SANDBOX_JAIL: "on"
+ COMPILER_BASE_PATH: /compilers
ports:
- "8000:8000"
security_opt:
@@ -38,6 +39,8 @@ services:
size: 64M
frontend:
build: frontend
+ environment:
+ INTERNAL_API_BASE: http://backend:8000/api
ports:
- "8080:8080"
volumes:
diff --git a/frontend/.dockerignore b/frontend/.dockerignore
new file mode 100644
index 00000000..cc48fabc
--- /dev/null
+++ b/frontend/.dockerignore
@@ -0,0 +1,3 @@
+.next/
+cache/
+node_modules/
diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json
index 0e0181da..b1fee051 100644
--- a/frontend/.eslintrc.json
+++ b/frontend/.eslintrc.json
@@ -1,18 +1,55 @@
{
- "extends": "next/core-web-vitals",
+ "parser": "@typescript-eslint/parser",
+ "plugins": [
+ "@typescript-eslint",
+ "css-modules"
+ ],
+ "extends": [
+ "eslint:recommended",
+ "next/core-web-vitals",
+ "plugin:@typescript-eslint/recommended"
+ ],
"rules": {
"semi": ["error", "never", { "beforeStatementContinuationChars": "always" }],
"indent": ["error", 4],
"quotes": ["error", "double"],
"quote-props": ["error", "consistent"],
- "brace-style": "error",
+ "brace-style": "off",
+ "@typescript-eslint/brace-style": ["error"],
"object-curly-spacing": ["error", "always"],
"array-bracket-spacing": ["error", "never"],
"no-else-return": "off",
"no-trailing-spaces": "error",
+ "no-multi-spaces": "error",
+ "no-multiple-empty-lines": "error",
+ "comma-dangle": "off",
+ "@typescript-eslint/comma-dangle": ["error", "always-multiline"],
+ "comma-spacing": "off",
+ "@typescript-eslint/comma-spacing": ["error"],
"prefer-const": ["warn", { "destructuring": "all" }],
"arrow-parens": ["error", "as-needed"],
"no-confusing-arrow": ["error", { "allowParens": true }],
+ "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
+ "no-extra-semi": "off",
+ "@typescript-eslint/no-extra-semi": ["error"],
+ "no-empty-function": "off",
+ "@typescript-eslint/no-empty-function": "off",
+ "no-unused-expressions": "off",
+ "@typescript-eslint/no-unused-expressions": ["warn"],
+ "@typescript-eslint/ban-ts-comment": "off",
+ "keyword-spacing": "off",
+ "@typescript-eslint/keyword-spacing": ["error"],
+ "@typescript-eslint/member-delimiter-style": ["error", {
+ "multiline": {
+ "delimiter": "none",
+ "requireLast": true
+ },
+ "singleline": {
+ "delimiter": "comma",
+ "requireLast": false
+ },
+ "multilineDetection": "brackets"
+ }],
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/first": "error",
@@ -42,7 +79,14 @@
"pathGroupsExcludedImportTypes": ["react"],
"alphabetize": { "order": "asc", "caseInsensitive": true }
}
- ]
+ ],
+ "import/no-unresolved": "error",
+ "import/export": "warn",
+ "import/no-named-as-default": "warn",
+ "import/no-named-as-default-member": "warn",
+ "import/no-unused-modules": "warn",
+ "css-modules/no-unused-class": "warn",
+ "css-modules/no-undef-class": "warn"
},
"overrides": [
{
diff --git a/frontend/.stylelintrc.json b/frontend/.stylelintrc.json
new file mode 100644
index 00000000..7c261d11
--- /dev/null
+++ b/frontend/.stylelintrc.json
@@ -0,0 +1,12 @@
+{
+ "extends": [
+ "stylelint-config-standard",
+ "stylelint-config-css-modules"
+ ],
+ "rules": {
+ "indentation": 4,
+ "declaration-empty-line-before": null,
+ "at-rule-no-unknown": null,
+ "no-descending-specificity": null
+ }
+}
diff --git a/frontend/next.config.js b/frontend/next.config.js
index 6fe38d23..592d3b9f 100644
--- a/frontend/next.config.js
+++ b/frontend/next.config.js
@@ -12,7 +12,14 @@ for (const envFile of [".env.local", ".env"]) {
process.env.NEXT_PUBLIC_API_BASE = process.env.API_BASE
process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID
-process.env.NEXT_PUBLIC_COMMIT_HASH = execSync("git rev-parse HEAD").toString().trim()
+let git_hash
+try {
+ git_hash = execSync("git rev-parse HEAD").toString().trim()
+} catch (error) {
+ console.log("Unable to get git hash, assume running inside Docker")
+ git_hash = "abc123"
+}
+process.env.NEXT_PUBLIC_COMMIT_HASH = git_hash
const withPWA = require('next-pwa')
const runtimeCaching = require('next-pwa/cache')
@@ -22,9 +29,14 @@ module.exports = removeImports(withPWA({
return [
{
source: "/",
- destination: "/scratch",
+ destination: "/scratch/new",
permanent: false,
},
+ {
+ source: "/scratch",
+ destination: "/scratch/new",
+ permanent: true,
+ },
]
},
async rewrites() {
diff --git a/frontend/package.json b/frontend/package.json
index c5236d66..44faa57d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -4,7 +4,7 @@
"dev": "next dev --port 8080",
"build": "next build",
"start": "next start --port 8080",
- "lint": "next lint",
+ "lint": "next lint && yarn stylelint 'src/**/*.css' 'src/**/*.scss'",
"postinstall": "next telemetry disable > /dev/null",
"storybook": "start-storybook -p 6006 --ci",
"build-storybook": "build-storybook"
@@ -43,6 +43,8 @@
"@storybook/preset-scss": "^1.0.3",
"@storybook/react": "^6.3.10",
"@svgr/webpack": "^5.5.0",
+ "@typescript-eslint/eslint-plugin": "^5.0.0",
+ "@typescript-eslint/parser": "^5.0.0",
"autoprefixer": "^10.3.1",
"babel-loader": "^8.2.2",
"css-loader": "^6.4.0",
@@ -51,6 +53,7 @@
"eslint": "7",
"eslint-config-next": "11.1.2",
"eslint-formatter-rdjson": "^1.0.5",
+ "eslint-plugin-css-modules": "^2.11.0",
"eslint-plugin-react": "^7.26.1",
"eslint-plugin-react-hooks": "^4.2.0",
"monaco-editor-webpack-plugin": "^5.0.0",
@@ -61,6 +64,9 @@
"sass-loader": "^12.1.0",
"storybook-dark-mode": "^1.0.8",
"style-loader": "^3.3.0",
+ "stylelint": "^13.13.1",
+ "stylelint-config-css-modules": "^2.2.0",
+ "stylelint-config-standard": "^22.0.0",
"typescript": "^4.4.2",
"webpack": "5"
}
diff --git a/frontend/src/components/ArchSelect/ArchSelect.module.scss b/frontend/src/components/ArchSelect/ArchSelect.module.scss
new file mode 100644
index 00000000..cd0a7038
--- /dev/null
+++ b/frontend/src/components/ArchSelect/ArchSelect.module.scss
@@ -0,0 +1,68 @@
+.container {
+ list-style: none;
+
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.arch {
+ flex: 1;
+
+ display: flex;
+ align-items: center;
+ gap: 16px;
+
+ padding: 16px;
+
+ border: 1px solid transparent;
+ border-radius: 6px;
+ background: var(--g300);
+ color: var(--g1200);
+
+ cursor: pointer;
+ user-select: none;
+
+ white-space: nowrap;
+
+ transition: border-color 0.1s, background 0.15s, color 0.2s;
+
+ svg {
+ width: 40px;
+ height: 40px;
+
+ will-change: filter;
+ filter: grayscale(100%);
+ transition: filter 0.2s;
+ }
+
+ &:hover,
+ &.selected {
+ border-color: var(--g400);
+ color: var(--g2000);
+
+ svg {
+ filter: grayscale(0%);
+ }
+ }
+
+ &.selected {
+ background: var(--g400);
+ }
+}
+
+.labelContainer {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.consoleName {
+ font-weight: 500;
+}
+
+.archName {
+ font-size: 0.8em;
+ opacity: 0.6;
+}
diff --git a/frontend/src/components/ArchSelect/ArchSelect.tsx b/frontend/src/components/ArchSelect/ArchSelect.tsx
new file mode 100644
index 00000000..7940e8bf
--- /dev/null
+++ b/frontend/src/components/ArchSelect/ArchSelect.tsx
@@ -0,0 +1,42 @@
+import classNames from "classnames"
+
+import styles from "./ArchSelect.module.scss"
+import LogoN64 from "./n64.svg"
+import LogoPS2 from "./ps2.svg"
+
+const ICONS = {
+ "mips":
Owner
+Fork of
+Description
+