diff --git a/.github/linting.sh b/.github/linting.sh
new file mode 100755
index 00000000..65ae7b8a
--- /dev/null
+++ b/.github/linting.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/bash
+
+set -euxo pipefail
+
+# shellcheck disable=SC2044
+for book in $(find ./ -maxdepth 1 -name "*.yml"); do
+ if [ "$book" != "./handlers.yml" ]; then
+ ansible-lint "$book" -x braces,line-length
+ fi
+done
+# shellcheck disable=SC2044
+for dir in $(find ./roles -maxdepth 1 -type d); do
+ ansible-lint "$dir" -x braces,line-length
+done
diff --git a/.github/workflows/ansible-linting.yml b/.github/workflows/ansible-linting.yml
new file mode 100644
index 00000000..2bc0eeeb
--- /dev/null
+++ b/.github/workflows/ansible-linting.yml
@@ -0,0 +1,22 @@
+name: Test contest deployment (ansible scripts)
+
+on: [push,pull_request]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v2
+ - name: Install ansible lint tools
+ run: sudo apt update; sudo pip install ansible-lint
+ - name: Lint the different scripts
+ run: |
+ set -eux
+ ansible-lint --version
+ ansible-lint .
+ working-directory: provision-contest/ansible
+ - name: Lint the different scripts (Via the script)
+ run: ../../.github/linting.sh
+ working-directory: provision-contest/ansible
+
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
index b917f770..91187d0a 100644
--- a/.github/workflows/shellcheck.yml
+++ b/.github/workflows/shellcheck.yml
@@ -11,4 +11,4 @@ jobs:
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
- SHELLCHECK_OPTS: -e SC1090
+ SHELLCHECK_OPTS: -e SC1090 -e SC2086 -e SC2046
diff --git a/README.md b/README.md
index bc6ac73a..993fff96 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,6 @@ developers, but might be of use for others too.
The following subdirectories contain:
* `website` the HTML code for www.domjudge.org
-* `icpc-wf` scripts used at the ICPC World Finals
+* `provision-contest` scripts used at ICPC style contests, such as the World Finals
* `contest-api` a script to validate an implementation of the
[ICPC contest API](https://ccs-specs.icpc.io/contest_api)
diff --git a/contest-api/check-api.sh b/contest-api/check-api.sh
index eacfe95e..7e611d74 100755
--- a/contest-api/check-api.sh
+++ b/contest-api/check-api.sh
@@ -14,7 +14,9 @@ languages
problems
groups
organizations
+persons
team-members
+accounts
teams
state
submissions
@@ -30,6 +32,8 @@ scoreboard
ENDPOINTS_OPTIONAL='
team-members
+persons
+accounts
awards
commentary
'
diff --git a/contest-api/json-schema/account.json b/contest-api/json-schema/account.json
new file mode 100644
index 00000000..af093e37
--- /dev/null
+++ b/contest-api/json-schema/account.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CLICS Contest API - account",
+ "description": "Definition of a single account object",
+
+ "type": "object",
+ "properties": {
+ "id": { "$ref": "common.json#/identifier" },
+ "username": { "type": "string" },
+ "password": { "type": ["string", "null"] },
+ "type": {
+ "oneOf": [
+ { "enum": [ "team", "judge", "admin", "analyst", "staff" ] },
+ { "type": "null" }
+ ]
+ },
+ "ip": { "type": ["string", "null"] },
+ "team_id": { "$ref": "common.json#/identifierornull" },
+ "person_id": { "$ref": "common.json#/identifierornull" }
+ },
+ "required": ["id", "username", "type"],
+ "$ref": "common.json#/strictproperties"
+}
diff --git a/contest-api/json-schema/accounts.json b/contest-api/json-schema/accounts.json
new file mode 100644
index 00000000..29cb973a
--- /dev/null
+++ b/contest-api/json-schema/accounts.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CLICS Contest API: accounts",
+ "description": "JSON response of this API call",
+
+ "type": "array",
+ "uniqueItems": true,
+ "$ref": "common.json#/nonemptyarray",
+ "items": {
+ "$ref": "account.json#"
+ }
+}
diff --git a/contest-api/json-schema/common.json b/contest-api/json-schema/common.json
index 9250b539..f7d0811e 100644
--- a/contest-api/json-schema/common.json
+++ b/contest-api/json-schema/common.json
@@ -11,7 +11,9 @@
"problems",
"groups",
"organizations",
+ "persons",
"team-members",
+ "accounts",
"teams",
"state",
"submissions",
diff --git a/contest-api/json-schema/event-feed.json b/contest-api/json-schema/event-feed.json
index 43c4abaa..656f5e05 100644
--- a/contest-api/json-schema/event-feed.json
+++ b/contest-api/json-schema/event-feed.json
@@ -22,7 +22,9 @@
{ "$ref": "group.json#" },
{ "$ref": "organization.json#" },
{ "$ref": "team.json#" },
+ { "$ref": "person.json#" },
{ "$ref": "team-member.json#" },
+ { "$ref": "account.json#" },
{ "$ref": "state.json#" },
{ "$ref": "submission.json#" },
{ "$ref": "judgement.json#" },
diff --git a/contest-api/json-schema/person.json b/contest-api/json-schema/person.json
new file mode 100644
index 00000000..5ea2cdc4
--- /dev/null
+++ b/contest-api/json-schema/person.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CLICS Contest API - person",
+ "description": "Definition of a single person object",
+
+ "type": "object",
+ "properties": {
+ "id": { "$ref": "common.json#/identifier" },
+ "team_id": { "$ref": "common.json#/identifier" },
+ "icpc_id": { "type": [ "string", "null" ] },
+ "name": { "type": "string" },
+ "title": { "type": [ "string", "null" ] },
+ "email": { "type": [ "string", "null" ] },
+ "sex": {
+ "oneOf": [
+ { "enum": [ "male", "female" ] },
+ { "type": "null" }
+ ]
+ },
+ "role": { "enum": [ "contestant", "coach", "staff" ] },
+ "photo": { "$ref": "common.json#/imagerefsornull" }
+ },
+ "required": ["id", "team_id", "name", "role"],
+ "$ref": "common.json#/strictproperties"
+}
diff --git a/contest-api/json-schema/persons.json b/contest-api/json-schema/persons.json
new file mode 100644
index 00000000..0c24e937
--- /dev/null
+++ b/contest-api/json-schema/persons.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CLICS Contest API: persons",
+ "description": "JSON response of this API call",
+
+ "type": "array",
+ "uniqueItems": true,
+ "$ref": "common.json#/nonemptyarray",
+ "items": {
+ "$ref": "person.json#"
+ }
+}
diff --git a/domlogo/domlogo.py b/domlogo/domlogo.py
index ce60a337..f8bd7285 100755
--- a/domlogo/domlogo.py
+++ b/domlogo/domlogo.py
@@ -5,6 +5,7 @@
import os
import requests
import re
+import time
font = ('Roboto', 14)
team_image = sg.Image(filename='domlogo-files/photos/idle.png')
@@ -41,7 +42,7 @@
api_url = f'{api_url}/contests/{cid}'
print(f'Contest is {cid}.')
-latest_logfile = max(glob.glob('output/log/judge.*-0.log'), key=os.path.getctime)
+latest_logfile = max(glob.glob('output/log/judge.*-2.log'), key=os.path.getctime)
print(f'Checking logfile {latest_logfile}')
with open(latest_logfile, 'r') as logfile:
# Seeks to the end of the file.
@@ -49,10 +50,13 @@
results = []
last_seen, needs_update = (None, None)
while True:
- event, values = window.read(timeout=10)
+ event, values = window.read(timeout=30)
if event == sg.WIN_CLOSED:
break
line = logfile.readline()
+ # Sleep here for a tiny amount of time to avoid using too much CPU.
+ if len(line) == 0:
+ time.sleep(0.01)
if 'Working directory:' in line:
token = line.strip().split('/')
judging_id = token[-1]
@@ -65,10 +69,9 @@
team_id = submission_data['team_id']
last_seen = (submission_id, judging_id, team_id)
new_filename = f'domlogo-files/photos/{team_id}.png'
- if team_id>=120:
- team_image.update(filename=new_filename)
- f'domlogo-files/photos/{team_id}.png')
- team_image.update(filename=f'domlogo-files/photos/{team_id}.png')
+ if (int)(team_id) >= 120:
+ new_filename = f'domlogo-files/photos/crew.png'
+ team_image.update(filename=new_filename)
metadata_text.update(f's{submission_id} / {submission_data["problem_id"]} / {submission_data["language_id"]}')
results_text.update('Busy compiling.')
elif 'No submissions in queue' in line:
@@ -96,6 +99,8 @@
color = 'DeepSkyBlue'
for i in range(len(cache)-1):
cache[i] = cache[i+1]
+ if (int)(tid) >= 120:
+ tid = 'DOMjudge'
cache[-1] = (f'domlogo-files/logos/{tid}.png', f's{sid}/j{jid}\n{verdict}', color, jid)
for i in range(len(cache)):
previous_column[i][0].update(filename=cache[i][0])
diff --git a/icpc-wf/ansible/group_vars/all/secret.yml.example b/icpc-wf/ansible/group_vars/all/secret.yml.example
deleted file mode 100644
index 74a24831..00000000
--- a/icpc-wf/ansible/group_vars/all/secret.yml.example
+++ /dev/null
@@ -1,19 +0,0 @@
-# Password for the MySQL replication user.
-# Set this to enable master-master replication between two domservers.
-REPLICATION_PASSWORD: some-replication-password
-
-# Database user password.
-DB_PASSWORD: some-database-password
-
-# Credentials for the REST API.
-API_USER: judgehost
-API_PASSWORD: some-judgehost-password
-
-# Username and password to be used in .netrc files on admin machines
-ADMIN_USER: admin
-ADMIN_PASSWORD: some-admin-password
-
-# Password for domjudge shell user
-# Set this to enable a password on the 'domjudge' shell accounts
-# created on the domserver and judgehosts.
-#DJ_SHELL_USER_PW: some-hashed-password
diff --git a/icpc-wf/ansible/roles/base_packages/tasks/main.yml b/icpc-wf/ansible/roles/base_packages/tasks/main.yml
deleted file mode 100644
index caed77dc..00000000
--- a/icpc-wf/ansible/roles/base_packages/tasks/main.yml
+++ /dev/null
@@ -1,100 +0,0 @@
----
-# This task configures packaging and installs various system utilities
-
-- name: replace pc2.ecs.baylor.edu by packages in apt sources
- replace:
- dest: "/etc/apt/{{ item }}"
- regexp: 'pc2\.ecs\.baylor\.edu'
- replace: 'packages'
- with_items:
- - sources.list
- - sources.list.d/microsoft.list
- - sources.list.d/mono.list
- - sources.list.d/vscode.list
- - sources.list.d/pypy-ubuntu-ppa-buster.list
- notify: run apt update
- when: WF_RESTRICTED_NETWORK
-
-- name: add packages to hosts file
- lineinfile:
- dest: /etc/hosts
- regexp: '^10\.3\.3\.209'
- line: "10.3.3.209 packages"
- notify: run apt update
- when: WF_RESTRICTED_NETWORK
-
-- name: check for dpkg architecture i386
- command: dpkg --print-foreign-architectures
- register: dpkg_architectures
- changed_when: false
-
-- name: remove unused dpkg architecture i386
- command: dpkg --remove-architecture i386
- notify: run apt update
- when: dpkg_architectures.stdout.find('i386') != -1
-
-- name: remove pycharm repo
- replace:
- dest: /etc/apt/sources.list
- regexp: '^([^#].*pycharm.*)$'
- replace: '# \1'
- notify: run apt update
-
-- name: flush handlers
- meta: flush_handlers
-
-- name: remove some packages
- apt:
- state: absent
- pkg:
- - apport
- - ntp
-
-- name: install common required/useful packages
- apt:
- state: present
- pkg:
- - ack
- - git
- - htop
- - httpie
- - ncdu
- - pv
- - screen
- - autoconf
- - automake
- - efibootmgr
- - curl
- - gcc
- - g++
- - default-jdk-headless
- - make
- - zip
- - unzip
- - php-cli
- - php-gd
- - php-curl
- - php-mysql
- - php-json
- - php-xml
- - php-zip
- - php-mbstring
- - php-intl
- - bsdmainutils
- - libcgroup-dev
- - libcurl4-gnutls-dev
- - libjsoncpp-dev
- - libmagic-dev
- - composer
- - debootstrap
- - texlive-latex-recommended
- - texlive-latex-extra
- - apache2-utils
- - tig
- - bat
- - jq
-
-- name: install local DEB packages
- include: install-local-package.yml
- with_fileglob:
- - install-{{host_type}}/*.deb
diff --git a/icpc-wf/ansible/roles/domjudge_checkout/tasks/main.yml b/icpc-wf/ansible/roles/domjudge_checkout/tasks/main.yml
deleted file mode 100644
index 1cfdfe16..00000000
--- a/icpc-wf/ansible/roles/domjudge_checkout/tasks/main.yml
+++ /dev/null
@@ -1,35 +0,0 @@
----
-# These tasks create a checkout of the DOMjudge repo
-
-- name: create working copy directory
- file:
- path: "{{DJ_DIR}}"
- state: directory
- owner: domjudge
- group: domjudge
-
-- name: create working copy of the domjudge repo
- become: yes
- become_user: domjudge
- git: repo={{DJ_GIT_REPO}} dest={{DJ_DIR}} version={{DJ_BRANCH}} accept_hostkey=yes update=yes
- register: git_working_copy
-
-- name: Check composer dependencies present
- become: no
- local_action: stat path=files/lib/vendor
- register: libvendor
-
-- name: Copy in composer dependencies (if they exist locally)
- synchronize:
- src: files/lib/vendor/
- dest: "{{DJ_DIR}}/lib/vendor/"
- owner: no
- use_ssh_args: true
- when: libvendor.stat.exists
-
-- name: fix ownership of lib/vendor
- file:
- path: "{{DJ_DIR}}/lib/vendor"
- recurse: yes
- owner: domjudge
- group: domjudge
diff --git a/icpc-wf/ansible/roles/domlogo/files/domlogo.py b/icpc-wf/ansible/roles/domlogo/files/domlogo.py
deleted file mode 100755
index 11d07a23..00000000
--- a/icpc-wf/ansible/roles/domlogo/files/domlogo.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/python3
-
-import PySimpleGUI as sg
-import glob
-import os
-import requests
-import re
-import time
-
-font = ('Roboto', 14)
-team_image = sg.Image(filename='domlogo-files/photos/idle.png')
-metadata_text = sg.Text('No submissions in queue.', font=font)
-results_text = sg.Text('', font=font)
-current_column = [
- [team_image],
- [metadata_text],
- [results_text],
-]
-cache = [('domlogo-files/logos/DOMjudge.png', ' \n ', None, None) for _ in range(10)]
-previous_column = [
- [sg.Image(filename=c[0]), sg.Text(c[1], font=font), sg.Canvas(size=(10,50))] for c in cache
-]
-layout = [
- [sg.Column(current_column), sg.VerticalSeparator(), sg.Column(previous_column)],
-]
-window = sg.Window('DOMlogo', layout, location=(1000,0), keep_on_top=True)
-
-with open('etc/restapi.secret', 'r') as secrets:
- while True:
- line = secrets.readline()
- if not line:
- break
- if line.startswith('#'):
- continue
- id, api_url, user, passwd = line.strip().split()
- break
-print(f'Using {api_url} as endpoint.')
-
-contests = requests.get(f'{api_url}/contests', auth=(user,passwd)).json()
-latest_contest = sorted(contests, key=lambda c: c['end_time'])[-1]
-cid = latest_contest['id']
-api_url = f'{api_url}/contests/{cid}'
-print(f'Contest is {cid}.')
-
-latest_logfile = max(glob.glob('output/log/judge.*-0.log'), key=os.path.getctime)
-print(f'Checking logfile {latest_logfile}')
-with open(latest_logfile, 'r') as logfile:
- # Seeks to the end of the file.
- logfile.seek(0, 2)
- results = []
- last_seen, needs_update = (None, None)
- while True:
- event, values = window.read(timeout=30)
- if event == sg.WIN_CLOSED:
- break
- line = logfile.readline()
- if len(line) == 0:
- time.sleep(0.01)
- if 'Working directory:' in line:
- token = line.strip().split('/')
- judging_id = token[-1]
- submission_id = token[-2]
- if not last_seen or last_seen[1] != judging_id:
- print(f'new submission, line was {line}')
- needs_update = last_seen
- results = []
- submission_data = requests.get(f'{api_url}/submissions/{submission_id}', auth=(user,passwd)).json()
- team_id = submission_data['team_id']
- last_seen = (submission_id, judging_id, team_id)
- new_filename = f'domlogo-files/photos/{team_id}.png'
- if (int)(team_id) >= 120:
- new_filename = f'domlogo-files/photos/crew.png'
- team_image.update(filename=new_filename)
- metadata_text.update(f's{submission_id} / {submission_data["problem_id"]} / {submission_data["language_id"]}')
- results_text.update('Busy compiling.')
- elif 'No submissions in queue' in line:
- needs_update = last_seen
- last_seen = None
- team_image.update(filename=f'domlogo-files/photos/idle.png')
- metadata_text.update('No submissions in queue.')
- results_text.update('')
- elif ' Compilation: ' in line:
- results_text.update(line.split('💻')[1:])
- elif ', result: ' in line:
- result = line.split(', result: ')[-1].strip()
- results.append('✔' if result == 'correct' else '✘')
- results_text.update('\n'.join(re.findall(
- '.{1,78}', ' '.join(results))))
- if needs_update:
- sid, jid, tid = needs_update
- needs_update = None
- judging_data = requests.get(f'{api_url}/judgements/{jid}', auth=(user,passwd)).json()
- verdict = judging_data['judgement_type_id'] or 'pending'
- color = 'firebrick1'
- if verdict == 'AC':
- color = 'LightGreen'
- elif verdict == 'pending':
- color = 'DeepSkyBlue'
- for i in range(len(cache)-1):
- cache[i] = cache[i+1]
- if (int)(tid) >= 120:
- tid = 'DOMjudge'
- cache[-1] = (f'domlogo-files/logos/{tid}.png', f's{sid}/j{jid}\n{verdict}', color, jid)
- for i in range(len(cache)):
- previous_column[i][0].update(filename=cache[i][0])
- previous_column[i][1].update(cache[i][1])
- previous_column[i][2].TKCanvas.config(bg=cache[i][2])
-
-window.close()
diff --git a/icpc-wf/ansible/roles/domlogo/templates/domjudgelogo.desktop.j2 b/icpc-wf/ansible/roles/domlogo/templates/domjudgelogo.desktop.j2
deleted file mode 100644
index b905e534..00000000
--- a/icpc-wf/ansible/roles/domlogo/templates/domjudgelogo.desktop.j2
+++ /dev/null
@@ -1,4 +0,0 @@
-[Desktop Entry]
-Name=domjudgelogo
-Type=Application
-Exec=bash -c "sleep 10 && cd {{DJ_DIR}} && python3 ./domlogo.py 1>/home/domjudge/logo.out 2>/home/domjudge/logo.err"
diff --git a/icpc-wf/ansible/roles/domserver/handlers/main.yml b/icpc-wf/ansible/roles/domserver/handlers/main.yml
deleted file mode 100644
index 6ebc67dd..00000000
--- a/icpc-wf/ansible/roles/domserver/handlers/main.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-# Define here handlers associated to this role.
-
-- name: restart nginx
- service: name=nginx enabled=yes state=restarted
-
-- name: restart PHP FPM
- service: name=php7.4-fpm enabled=yes state=restarted
diff --git a/icpc-wf/ansible/roles/grafana/files/datasources.yml b/icpc-wf/ansible/roles/grafana/files/datasources.yml
deleted file mode 100644
index d6f596e4..00000000
--- a/icpc-wf/ansible/roles/grafana/files/datasources.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-# config file version
-apiVersion: 1
-
-# list of datasources that should be deleted from the database
-deleteDatasources:
- - name: Prometheus
- orgId: 1
-
-# list of datasources to insert/update depending
-# what's available in the database
-datasources:
- # name of the datasource. Required
-- name: Prometheus
- # datasource type. Required
- type: prometheus
- # access mode. proxy or direct (Server or Browser in the UI). Required
- access: proxy
- # org id. will default to orgId 1 if not specified
- orgId: 1
- # url
- url: http://localhost:9090
- # database password, if used
- # password:
- # database user, if used
- # user:
- # database name, if used
- # database:
- # enable/disable basic auth
- # basicAuth:
- # basic auth username
- # basicAuthUser:
- # basic auth password
- # basicAuthPassword:
- # enable/disable with credentials headers
- # withCredentials:
- # mark as default datasource. Max one per org
- isDefault: true
- #
Authors and contact
The main developers are Jaap Eldering, Nicky Gerritsen, Keith
-Johnson, Thijs Kinkhorst, and Tobias Werth, with contributions from
-many other people.
+Johnson, Thijs Kinkhorst, Mart Pluijmaekers, Michael Vasseur and Tobias Werth,
+with contributions from many other people.
Anyone interested is welcome to contribute to DOMjudge.
The project is licensed under the GNU General Public Licence. This gives
@@ -76,7 +76,7 @@ you the freedom to use it for any purpose, without cost, make changes
that you require and share those with the community.
You can reach us through the development mailinglist
-DOMjudge-devel.
+DOMjudge-devel.
(You need to be subscribed to the list before you can post, to counter spam.)
This is also the place to get help with your questions about installing
or using DOMjudge.
diff --git a/website/demo.shtml b/website/demo.shtml
index 4dd4cb67..27125619 100644
--- a/website/demo.shtml
+++ b/website/demo.shtml
@@ -45,4 +45,8 @@ contest, NWERC 2018, with some sensitive things removed.
If there's something not working, or you've got questions, please contact us
at admin@domjudge.org.
+Logging
+
+We send exceptions encountered on the demo instance to Sentry. This should not contain personal information but we prefer to inform you of this. These errors make it easier to detect and debug bugs in our current development version, so you already help us by encountering these bugs!
+
diff --git a/website/development.shtml b/website/development.shtml
index 35213d2d..e15ae649 100644
--- a/website/development.shtml
+++ b/website/development.shtml
@@ -22,13 +22,13 @@ for how to run DOMjudge from git sources.
our Github project.
You may report a new issue there.
If you have a question or you are unsure whether what you're seeing is
-a bug, please discuss it on the development mailinglist or
+a bug, please discuss it on the development mailinglist or
chat beforehand (see below).
Mailinglist and chat
You are welcome to subscribe to our
-development mailinglist
+development mailinglist
for discussions on development, but also for asking general
questions on using and installing DOMjudge (you need to subscribe
to the list before you can post).
diff --git a/website/docker-compose.yml b/website/docker-compose.yml
new file mode 100644
index 00000000..dc3975da
--- /dev/null
+++ b/website/docker-compose.yml
@@ -0,0 +1,27 @@
+version: '3'
+
+services:
+ mariadb:
+ image: docker.io/mariadb
+ hostname: mariadb
+ environment:
+ - MYSQL_ROOT_PASSWORD=domjudge
+ - MYSQL_USER=domjudge
+ - MYSQL_PASSWORD=djpw
+ - MYSQL_DATABASE=domjudge
+ ports:
+ - 13306:3306
+ command: --max-connections=1000 --max-allowed-packet=512M
+ volumes:
+ - /var/lib/mysql
+ domjudge:
+ image: docker.io/domjudge/domserver
+ hostname: domserver
+ environment:
+ - MYSQL_ROOT_PASSWORD=domjudge
+ - MYSQL_USER=domjudge
+ - MYSQL_PASSWORD=djpw
+ - MYSQL_DATABASE=domjudge
+ - MYSQL_HOST=mariadb
+ ports:
+ - 8080:80
diff --git a/website/documentation.shtml b/website/documentation.shtml
index 4cc75ec5..f37341eb 100644
--- a/website/documentation.shtml
+++ b/website/documentation.shtml
@@ -18,7 +18,7 @@ state as well as perform certain actions, such as submit solutions
and control contest state (with admin role).
The DOMjudge API is an implementation of the ICPC
-Contest API
+Contest API
(except that it does not (yet) implement the optional team-members
endpoint). It also has some extensions, see the
internal API documentation.
diff --git a/website/download.shtml b/website/download.shtml
index 569e8dc1..d207a358 100644
--- a/website/download.shtml
+++ b/website/download.shtml
@@ -35,14 +35,13 @@ dated .
To get notifications of new releases, subscribe to the low volume
-announcements
+announcements
mailing list.
DOMjudge docker images
Official Docker images for both the domserver and judgehost are also available.
-See the Docker Hub
-repository for the DOMserver to get started.
+See the Docker Hub repository for the DOMserver to get started or use the example docker-compose setup.
Debian Packages
diff --git a/website/header.shtml b/website/header.shtml
index 8ae61899..a77563e2 100644
--- a/website/header.shtml
+++ b/website/header.shtml
@@ -1,7 +1,7 @@
-
diff --git a/website/tools.shtml b/website/tools.shtml
index c94ed8bc..c81c97ea 100644
--- a/website/tools.shtml
+++ b/website/tools.shtml
@@ -62,4 +62,11 @@
can connect to.
+Autologin for team machines
+
+ LightDM CCS autologin makes
+ it possible to automatically log in to team machines when the contest starts in DOMjudge. This
+ is useful to keep problem data secret and to make sharing credentials easier on the organizer.
+
+