Skip to content

Commit

Permalink
Merge pull request #6 from monadius/5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kazk authored Apr 20, 2023
2 parents 8054aad + 3a492d0 commit 8545abf
Show file tree
Hide file tree
Showing 29 changed files with 355 additions and 51 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI
on:
push:
branches:
- main
pull_request:

jobs:
build:
runs-on: ubuntu-latest
if: ${{ github.repository == 'codewars/ocaml' }}
steps:
- uses: actions/checkout@v2
- uses: docker/setup-buildx-action@v2

- name: Build image
uses: docker/build-push-action@v3
with:
context: .
push: false
# Make the image available in next step
load: true
tags: ghcr.io/codewars/ocaml:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run Batteries Example
run: bin/run examples/batteries

- name: Run Base Example (with failing tests)
run: bin/run examples/base

- name: Run Domainslib Example
run: bin/run examples/domainslib

- name: Run Zarith Example
run: bin/run examples/zarith

- name: Report Image Size
run: |
echo "## Image Size" >> $GITHUB_STEP_SUMMARY
docker image inspect --format '{{.Size}}' ghcr.io/codewars/ocaml:latest | numfmt --to=si --suffix=B >> $GITHUB_STEP_SUMMARY
80 changes: 48 additions & 32 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,48 +1,64 @@
FROM buildpack-deps:bionic
FROM ubuntu:22.04 AS Builder

RUN set -ex; \
useradd --create-home codewarrior; \
# TODO Remove symlink in the next version
ln -s /home/codewarrior /workspace;

ENV OPAMROOT=/opt/opam \
OPAMCOLOR=never
ENV OPAMROOT=/opt/ocaml

RUN set -ex; \
mkdir -p $OPAMROOT; \
useradd --create-home codewarrior; \
chown codewarrior:codewarrior $OPAMROOT; \
apt-get update; \
apt-get install -y --no-install-recommends \
software-properties-common \
m4 \
rsync \
aspcud \
; \
# Needed for opam 2.0
add-apt-repository -y ppa:avsm/ppa; \
libgmp-dev \
opam \
;

USER codewarrior
ENV USER=codewarrior

RUN set -ex; \
opam init -y --shell-setup --disable-sandboxing --compiler=5.0.0;

RUN set -ex; \
opam install -y \
'batteries=3.6.0' \
'base=v0.15.1' \
'domainslib=0.5.0' \
'ocamlbuild=0.14.2' \
'ocamlfind=1.9.6' \
'ounit2=2.2.7' \
'zarith=1.12' \
;

FROM ubuntu:22.04

RUN set -ex; \
apt-get update; \
apt-get install -y --no-install-recommends \
opam \
gcc \
libc6-dev \
libgmp-dev \
; \
rm -rf /var/lib/apt/lists/*;

USER codewarrior
ENV USER=codewarrior \
HOME=/home/codewarrior
COPY --from=builder \
/opt/ocaml/5.0.0/bin/ocamlc.opt \
/opt/ocaml/5.0.0/bin/ocamlopt.opt \
/opt/ocaml/5.0.0/bin/ocamldep.opt \
/opt/ocaml/5.0.0/bin/ocamlbuild \
/opt/ocaml/5.0.0/bin/ocamlfind \
/opt/ocaml/5.0.0/bin/
COPY --from=builder \
/opt/ocaml/5.0.0/lib/ /opt/ocaml/5.0.0/lib/

# --disable-sandboxing is needed to do this in a container witout `--privileged`
RUN opam init -y --compiler=4.07.1 --disable-sandboxing

ENV OPAM_SWITCH_PREFIX=/opt/opam/4.07.1 \
CAML_LD_LIBRARY_PATH=/opt/opam/4.07.1/lib/stublibs \
OCAML_TOPLEVEL_PATH=/opt/opam/4.07.1/lib/toplevel \
PATH=/opt/opam/4.07.1/bin:$PATH
RUN set -ex; \
useradd --create-home codewarrior; \
mkdir -p /workspace; \
chown -R codewarrior:codewarrior /workspace;

RUN opam install -y \
'ounit=2.0.8' \
'batteries=2.9.0' \
'core=v0.11.3' \
;
USER codewarrior
ENV USER=codewarrior \
PATH=/opt/ocaml/5.0.0/bin:$PATH

COPY workspace/test.ml /workspace/test.ml
COPY workspace/_tags /workspace/_tags
COPY --chown=codewarrior:codewarrior workspace/. /workspace/
WORKDIR /workspace
63 changes: 63 additions & 0 deletions DockerfileAlpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
FROM alpine:3.17 AS builder

ENV OPAMROOT=/opt/ocaml

RUN set -ex; \
mkdir -p $OPAMROOT; \
adduser -D codewarrior; \
chown -R codewarrior:codewarrior /opt/ocaml; \
apk update; \
apk add --virtual .build-deps \
build-base \
ocaml-compiler-libs \
gmp-dev \
opam \
;

USER codewarrior
ENV USER=codewarrior

RUN set -ex; \
opam init -y --shell-setup --disable-sandboxing --compiler=5.0.0;

RUN set -ex; \
opam install -y \
'batteries=3.6.0' \
'base=v0.15.1' \
'domainslib=0.5.0' \
'ocamlbuild=0.14.2' \
'ocamlfind=1.9.6' \
'ounit2=2.2.7' \
'zarith=1.12' \
;

FROM alpine:3.17

RUN set -ex; \
apk add --no-cache \
gcc \
gmp-dev \
musl-dev \
;

COPY --from=builder \
/opt/ocaml/5.0.0/bin/ocamlc.opt \
/opt/ocaml/5.0.0/bin/ocamlopt.opt \
/opt/ocaml/5.0.0/bin/ocamldep.opt \
/opt/ocaml/5.0.0/bin/ocamlbuild \
/opt/ocaml/5.0.0/bin/ocamlfind \
/opt/ocaml/5.0.0/bin/
COPY --from=builder \
/opt/ocaml/5.0.0/lib/ /opt/ocaml/5.0.0/lib/

RUN set -ex; \
adduser -D codewarrior; \
mkdir /workspace; \
chown -R codewarrior:codewarrior /workspace;

USER codewarrior
ENV USER=codewarrior \
PATH=/opt/ocaml/5.0.0/bin:$PATH

COPY --chown=codewarrior:codewarrior workspace/. /workspace/
WORKDIR /workspace
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ Container image for OCaml used by CodeRunner.
## Usage

```bash
W=/workspace/
W=/workspace
# Create container
C=$(docker container create --rm -w $W ghcr.io/codewars/ocaml:latest \
sh -c 'ocamlbuild -quiet -use-ocamlfind test.native && exec ./test.native')
BUILD="ocamlbuild -quiet -use-ocamlfind cwtest.native"
C=$(docker container create --rm -w $W ghcr.io/codewars/ocaml:latest sh -c "$BUILD && exec ./cwtest.native")

# Write solution and tests in workspace/fixture.ml
# Then copy it inside a container
docker container cp workspace/fixture.ml $C:$W/fixture.ml
# Copy solution and test files
docker container cp ${1:-examples/basic}/. $C:$W

# Run tests
# Start
docker container start --attach $C
```

Expand Down
1 change: 1 addition & 0 deletions bin/alpine_build
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
docker build -f DockerfileAlpine -t ocaml:alpine .
2 changes: 2 additions & 0 deletions bin/alpine_run
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export IMAGE=ocaml:alpine
exec "$(dirname "$0")/run" "$@"
1 change: 1 addition & 0 deletions bin/build
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
docker build -t ghcr.io/codewars/ocaml:latest .
16 changes: 16 additions & 0 deletions bin/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
set -eu

if [ -z "${IMAGE:+x}" ]; then
IMAGE=ghcr.io/codewars/ocaml:latest
fi

W=/workspace
# Create container
BUILD="ocamlbuild -quiet -use-ocamlfind cwtest.native"
C=$(docker container create --rm -w $W $IMAGE sh -c "$BUILD && exec ./cwtest.native")

# Copy files
docker container cp ${1:-examples/basic}/. $C:$W

# Start
docker container start --attach $C
3 changes: 3 additions & 0 deletions examples/base/preloaded.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
open Base

let print_int_list = Fn.compose Sexp.to_string (sexp_of_list sexp_of_int)
3 changes: 3 additions & 0 deletions examples/base/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
open Base

let cubes = List.map ~f:Int.(fun x -> x ** 3)
15 changes: 15 additions & 0 deletions examples/base/tests.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
open Base
open OUnit
open Preloaded

let suite = [
"cubes tests (passing)" >:::
[
"Testing [1; 2; 3]" >:: (fun _ -> assert_equal [1; 8; 27] (Solution.cubes (List.range 1 4)) ~printer:print_int_list);
"Testing [5]" >:: (fun _ -> assert_equal [125] (Solution.cubes [5]) ~printer:print_int_list);
];
"cubes tests (failing)" >:::
[
"Testing [1; 2; 3]" >:: (fun _ -> assert_equal [1; 8; 27] (Solution.cubes (List.range 1 5)) ~printer:print_int_list);
];
]
Empty file added examples/basic/preloaded.ml
Empty file.
1 change: 1 addition & 0 deletions examples/basic/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let is_even n = n mod 2 = 0
44 changes: 44 additions & 0 deletions examples/basic/tests.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
open Solution
open OUnit

let suite = [
"Top level test case" >:: (fun _ -> assert_equal false (is_even 1024));
"Test Odd" >:::
[
"Should return false for 1" >:: (fun _ -> assert_equal false (is_even 1));
"Should return false for 7" >:: (fun _ -> assert_equal false (is_even 7))
];
"Test even" >:::
[
"Should return true for 100" >:: (fun _ -> assert_equal true (is_even 100));
"Should return true for 42" >:: (fun _ -> assert_equal true (is_even 42))
];
"Test edge cases" >:::
[
"Test zero" >:::
[
"Should return true for 0" >:: (fun _ -> assert_equal true (is_even 0))
];
"Test -1" >:::
[
"Should return false for -1" >:: (fun _ -> assert_equal false (is_even (-1)))
]
];
"Unlabeled tests" >:::
[
TestCase (fun _ -> assert_equal false (is_even 100) ~msg:"Incorrect answer for n=100");
TestCase (fun _ -> assert_equal false (is_even 100) ~msg:"Incorrect answer for n=100");
TestList [
TestCase (fun _ -> assert_equal false (is_even 100) ~msg:"Incorrect answer for n=100");
TestCase (fun _ -> assert_equal false (is_even 100) ~msg:"Incorrect answer for n=100");
]

];
"Nested labels" >::: [
"Outer label" >: ("Inner label" >: ("Tests with nested labels" >::: [
TestCase (fun _ -> assert_equal false (is_even 100) ~msg:"Incorrect answer for n=100" ~printer: string_of_bool);
TestCase (fun _ -> assert_equal false (is_even 100) ~msg:"Incorrect answer for n=100" ~printer: string_of_bool);

]))
]
]
4 changes: 4 additions & 0 deletions examples/batteries/preloaded.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
open Batteries

let print_int = IO.to_string Int.print
let print_int_list = IO.to_string (List.print Int.print)
8 changes: 8 additions & 0 deletions examples/batteries/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
open Batteries

let sum n =
(1 -- n)
|> Enum.map (fun i -> Num.num_of_int i |> Num.int_of_num)
|> Enum.sum

let cubes = List.map Int.(fun x -> x ** 3)
16 changes: 16 additions & 0 deletions examples/batteries/tests.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
open Batteries
open OUnit
open Preloaded

let suite = [
"sum tests" >:::
[
"Testing 1" >:: (fun _ -> assert_equal 1 (Solution.sum 1) ~printer:print_int);
"Testing 9" >:: (fun _ -> assert_equal 45 (Solution.sum 9) ~printer:print_int);
];
"cubes tests" >:::
[
"Testing [1; 2; 3]" >:: (fun _ -> assert_equal [1; 8; 27] (Solution.cubes ((1 -- 3) |> List.of_enum)) ~printer:print_int_list);
"Testing [5]" >:: (fun _ -> assert_equal [125] (Solution.cubes [5]) ~printer:print_int_list);
];
]
5 changes: 5 additions & 0 deletions examples/compile-error/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let sum n =
if n = 0 then
0
else
n + sum (n - 1)
9 changes: 9 additions & 0 deletions examples/compile-error/tests.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
open OUnit

let suite = [
"sum tests" >:::
[
"Testing 1" >:: (fun _ -> assert_equal 1 (Solution.sum 1) ~printer:string_of_int);
"Testing 9" >:: (fun _ -> assert_equal 45 (Solution.sum 9) ~printer:string_of_int);
];
]
Loading

0 comments on commit 8545abf

Please sign in to comment.