Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7fac836
feat: update test cases with improved script formatting and memory us…
Abhinav-Prajapati Jun 16, 2025
531fc0b
feat: add memory usage validation to test cases with tolerance checks
Abhinav-Prajapati Jun 16, 2025
90f3f17
feat: add memory monitoring during code execution
Abhinav-Prajapati Jun 16, 2025
7e4c212
feat: enhance memory reporting for Alpine Linux environments
Abhinav-Prajapati Jun 16, 2025
253d796
feat: enhance memory usage validation messages in test cases
Abhinav-Prajapati Jun 16, 2025
28ea058
fix: change warning to error for memory report read failure
Abhinav-Prajapati Jun 16, 2025
098d65e
feat: add support for additional programming languages and adjust mem…
Abhinav-Prajapati Jun 16, 2025
a9d21f6
feat: add Dockerfile for Alpine Linux with multiple programming langu…
Abhinav-Prajapati Jun 16, 2025
41c9f69
feat: update test cases for C, Node.js, and Python with improved form…
Abhinav-Prajapati Jun 16, 2025
bb4fd89
feat: add support for additional programming languages in supportedLa…
Abhinav-Prajapati Jun 16, 2025
1ff15de
Implement code changes to enhance functionality and improve performance
Abhinav-Prajapati Jun 16, 2025
313b690
add support for C#
Abhinav-Prajapati Jun 16, 2025
81696a1
add simlink for php
Abhinav-Prajapati Jun 16, 2025
dc34a60
feat: add test case for golang, php, csharp, kotlin, ts
Abhinav-Prajapati Jun 16, 2025
96ef1f4
refactor
Abhinav-Prajapati Jun 16, 2025
378ea98
fix formating
Abhinav-Prajapati Jun 18, 2025
ab5b53c
fix formating
Abhinav-Prajapati Jun 18, 2025
feceb2e
fix: correct syntax in symbolic link creation for pip
Abhinav-Prajapati Jun 18, 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
93 changes: 93 additions & 0 deletions Dockerfile.alplne
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
FROM alpine:latest

ENV PYTHONUNBUFFERED=1

# Switch to edge repository for latest packages
RUN echo "https://dl-cdn.alpinelinux.org/alpine/edge/main" > /etc/apk/repositories && \
echo "https://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \
echo "https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories

# Install basic tools and build dependencies in smaller chunks
RUN set -ex && \
apk add --no-cache gcc g++ make musl-dev curl wget tar gzip

RUN set -ex && \
apk add --no-cache libffi-dev openssl-dev zlib-dev bzip2-dev xz-dev readline-dev sqlite-dev

RUN set -ex && \
apk add --no-cache autoconf bison yaml-dev gdbm-dev ncurses-dev git bash ca-certificates

RUN set -ex && \
apk add --no-cache chromium lsof iptables ip6tables

# Install available packages from Alpine Edge
RUN set -ex && \
apk add --no-cache \
openjdk21 \
ruby \
php82 php82-cli php82-common php82-phar php82-json php82-openssl php82-curl php82-dev \
go mono

# Install Kotlin manually
ARG KOTLIN_VERSION=2.1.0
RUN cd /opt && \
wget https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip && \
unzip kotlin-compiler-${KOTLIN_VERSION}.zip && \
mv kotlinc /opt/kotlin && \
ln -s /opt/kotlin/bin/kotlin /usr/local/bin/kotlin && \
ln -s /opt/kotlin/bin/kotlinc /usr/local/bin/kotlinc && \
rm kotlin-compiler-${KOTLIN_VERSION}.zip


RUN apk add --no-cache nodejs npm
ENV PATH="/opt/node/bin:$PATH"
RUN npm install -g typescript ts-node

RUN apk add --no-cache python3

# Create symbolic links and fix paths
RUN ln -sf python3 /usr/bin/python && \
ln -sf pip3 /usr/bin/pip && \
ln -s /usr/bin/php82 /usr/bin/php

# Remove unneeded GCC internals to save space
RUN set -ex && \
find /usr/libexec/gcc -name "cc1obj" -delete 2>/dev/null || true && \
find /usr/libexec/gcc -name "lto1" -delete 2>/dev/null || true && \
find /usr/libexec/gcc -name "lto-wrapper" -delete 2>/dev/null || true && \
rm -f /usr/bin/x86_64-alpine-linux-musl-gcj 2>/dev/null || true

# Copy application files
COPY . /usr/bin/
COPY start.sh /usr/bin/

# Install npm dependencies
RUN npm --prefix /usr/bin/ install

# Create non-root user for security
RUN addgroup -S -g 2000 runner && \
adduser -S -D -u 2000 -s /sbin/nologin -h /tmp -G runner runner

# Set up environment variables for all languages
ENV PATH="/opt/node/bin:/opt/swift/usr/bin:$PATH"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ENV PATH includes /opt/swift/usr/bin. However, the PR description explicitly states that Swift is not supported due to Alpine's incompatibility with glibc. This path entry appears to be a leftover or mistake and should be removed to avoid confusion and keep the environment clean.

ENV PATH="/opt/node/bin:$PATH"

ENV JAVA_HOME="/usr/lib/jvm/java-21-openjdk"
ENV GOPATH="/tmp/go"
ENV GOCACHE="/tmp/go-cache"

EXPOSE 8080

# Health check to verify installations
RUN echo "=== Language Versions ===" && \
echo "Node: $(node --version)" && \
echo "Python: $(python --version)" && \
echo "Java: $(java --version | head -1)" && \
echo "Ruby: $(ruby --version)" && \
echo "Go: $(go version)" && \
echo "PHP: $(php --version | head -1)" && \
echo "TypeScript: $(tsc --version)" && \
echo "C#: $(mono --version) | head -1" && \
echo "Kotlin: $(kotlin -version)" && \
echo "========================"

# USER runner
CMD sh /usr/bin/start.sh
38 changes: 37 additions & 1 deletion configs/language.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { CPP, C, PYTHON, JAVA, NODEJS, RUBY, PROMPTV1, PROMPTV2, PROMPTV3 } = require('../enums/supportedLanguages')
const { CPP, C, PYTHON, JAVA, NODEJS, RUBY, GO, PHP, TYPESCRIPT, CSHARP, KOTLIN, PROMPTV1, PROMPTV2, PROMPTV3 } = require('../enums/supportedLanguages')
const ONE_MB = 1024 // ulimit uses Kilobyte as base unit
const ALLOWED_RAM = process.env.ALLOWED_RAM || 512

Expand Down Expand Up @@ -45,6 +45,42 @@ const LANGUAGES_CONFIG = {
filename: 'solution.rb',
memory: ALLOWED_RAM * ONE_MB,
},
[GO]: {
compile: 'go build -o solution solution.go',
run: './solution',
timeout: 4,
filename: 'solution.go',
memory: 1024 * ONE_MB, // Go programs can be memory-intensive, so we allocate 1GB
},
[PHP]: {
compile: 'php -l solution.php',
run: 'php solution.php',
timeout: 8,
filename: 'solution.php',
memory: ALLOWED_RAM * ONE_MB,
},
[TYPESCRIPT]: {
compile: 'tsc solution.ts --outDir .',
run: 'node solution.js',
timeout: 10,
filename: 'solution.ts',
memory: 786432, // Similar to Node.js since it compiles to JS
},
[CSHARP]: {
compile: 'mcs -out:solution.exe solution.cs',
run: 'mono solution.exe',
timeout: 6,
filename: 'solution.cs',
memory: ALLOWED_RAM * ONE_MB * 1.5,
requiresProjectFile: false, // Not needed for Mono
},
[KOTLIN]: {
compile: 'kotlinc solution.kt -include-runtime -d solution.jar',
run: 'java -jar solution.jar',
timeout: 6,
filename: 'solution.kt',
memory: 1024 * ONE_MB //Kotlin/JVM needs slightly more memory
},
[PROMPTV1]: {
model: 'gpt-4-1106-preview',
},
Expand Down
5 changes: 5 additions & 0 deletions enums/supportedLanguages.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ module.exports = {
JAVA: 'java',
NODEJS: 'nodejs',
RUBY: 'ruby',
GO: 'go',
PHP: 'php',
TYPESCRIPT: 'typescript',
CSHARP: 'csharp',
KOTLIN: 'kotlin',
PROMPTV1: 'promptv1',
PROMPTV2: 'promptv2',
PROMPTV3: 'promptv3',
Expand Down
39 changes: 31 additions & 8 deletions services/code.service.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

/* globals gc */
const util = require('util')
const exec = util.promisify(require('child_process').exec)
Expand Down Expand Up @@ -228,11 +229,12 @@ const _executeCode = async (req, res, response) => {
// Check if there is no compilation error
if (response.compileMessage === '') {
let command
if (language === 'java') {
if (language === 'java' || language === 'kotlin') {
// Remove ulimit as a temp fix
command = `cd /tmp/ && timeout ${langConfig.timeout}s ${langConfig.run}`
command = `cd /tmp/ && /usr/bin/time -f "%M" -o /tmp/memory_report.txt timeout ${langConfig.timeout}s ${langConfig.run}`
} else {
command = `cd /tmp/ && ulimit -v ${langConfig.memory} && ulimit -m ${langConfig.memory} && timeout ${langConfig.timeout}s ${langConfig.run}`
// Execute command with memory limits and resource monitoring for non-Java languages
command = `cd /tmp/ && ulimit -v ${langConfig.memory} && ulimit -m ${langConfig.memory} && /usr/bin/time -f "%M" -o /tmp/memory_report.txt timeout ${langConfig.timeout}s ${langConfig.run}`
}

// Check if there is any input that is to be provided to code execution
Expand All @@ -244,14 +246,35 @@ const _executeCode = async (req, res, response) => {
}

const outputLog = await _runScript(command, res, true)

let memoryKB = null
try {
const path = '/tmp/memory_report.txt'
await fs.promises.access(path, fs.constants.F_OK)
const memoryReport = await fs.promises.readFile(path, 'utf8')
memoryKB = parseInt(memoryReport.trim(), 10)
} catch (err) {
console.error(`Memory report not found or failed to read: ${err.message}`)
}

if (memoryKB) {
response.memory = memoryKB
} else {
response.memory = null;
}

console.log('Memory used:', response.memory);

response.output =
outputLog.error !== undefined
? _prepareErrorMessage(outputLog, language, command)
: outputLog.result.stdout

if (outputLog.error) {
response.error = 1
}
} else {
}
else {
response.error = 1
}
} catch (e) {
Expand Down Expand Up @@ -399,7 +422,7 @@ const _getAiScore = async (langConfig, question, response, points, userAnswer, r

const _executeStatement = (db, sql) => {
return new Promise((resolve, reject) => {
db.all(sql, function(err, rows) {
db.all(sql, function (err, rows) {
if (err) {
reject(err);
} else {
Expand Down Expand Up @@ -887,14 +910,14 @@ const _postCleanUp = async (type, staticServerInstance = undefined, jasmineServe
await _cleanUpDir(appConfig.multifile.workingDir, appConfig.multifile.submissionFileDownloadPath)
switch (type) {
case FRONTEND_STATIC_JASMINE:
if(staticServerInstance) {
if (staticServerInstance) {
staticServerInstance.close(() => {
logger.info('Exiting static server in post cleanup')
})
}
break
case FRONTEND_REACT_JASMINE:
if(jasmineServer) {
if (jasmineServer) {
logger.info('Exiting react setup server in post cleanup')
process.kill(-jasmineServer.pid)
}
Expand All @@ -917,7 +940,7 @@ const _executeMultiFile = async (req, res, response) => {
let result
if (req?.non_editable_files) {
const isValidSubmission = await _checkIntegrity(req.non_editable_files)
if(!isValidSubmission) throw new Error(`A non editable file has been modified, exiting...`)
if (!isValidSubmission) throw new Error(`A non editable file has been modified, exiting...`)
}
switch (req.type) {
case FRONTEND_STATIC_JASMINE:
Expand Down
Loading