Skip to content

Commit e0530e5

Browse files
committed
feat: load env vars from file or archive at startup
Adds LOAD_ENV_FROM_FILE and LOAD_ENV_FROM_ARCHIVE so a generic pack or artifact can ship its own container configuration (TYPE, VERSION, etc.) inside-out. Loaded values override anything passed via docker -e, and the load runs in start-configuration after proxy/Java setup but before TYPE is dispatched, so the loaded TYPE drives the deploy step. LOAD_ENV_FROM_ARCHIVE_ENTRY selects the file inside the archive (default: .env). EULA is intentionally NOT loadable this way since the EULA check runs earlier. Closes #4037
1 parent 14c8509 commit e0530e5

11 files changed

Lines changed: 162 additions & 0 deletions

File tree

docs/mods-and-plugins/index.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,46 @@ Disabling mods within docker compose files:
156156
mod2.jar
157157
```
158158
159+
### Loading container configuration from a pack
160+
161+
A pack can ship its own container configuration so that the server type, version,
162+
and other variables travel with the pack rather than being declared by the user.
163+
At startup, before `TYPE` is dispatched, the container can load environment
164+
variables from a file on disk, a URL, or a file inside an archive (URL or local).
165+
166+
- `LOAD_ENV_FROM_FILE`: container path or URL of a shell-style env file (one
167+
`KEY=VALUE` per line). Comments and blank lines are allowed.
168+
- `LOAD_ENV_FROM_ARCHIVE`: container path or URL of a zip/tar archive containing
169+
an env file. Each entry is sourced into the environment.
170+
- `LOAD_ENV_FROM_ARCHIVE_ENTRY`: relative path of the env file inside the archive.
171+
Defaults to `.env`.
172+
173+
Both `LOAD_ENV_FROM_FILE` and `LOAD_ENV_FROM_ARCHIVE` may be set at the same time;
174+
the file is loaded first, then the archive entry. Values loaded from these sources
175+
**override** anything passed via `docker run -e` (or compose `environment:`), so
176+
the pack's declared values win.
177+
178+
```shell
179+
docker run -d \
180+
-e EULA=TRUE \
181+
-e LOAD_ENV_FROM_ARCHIVE=https://cdn.example.org/my-pack.zip \
182+
itzg/minecraft-server
183+
```
184+
185+
Where `my-pack.zip` contains a `.env` at its root such as:
186+
187+
```env
188+
TYPE=FABRIC
189+
VERSION=1.21.1
190+
FABRIC_LOADER_VERSION=0.16.0
191+
GENERIC_PACK=https://cdn.example.org/my-pack.zip
192+
```
193+
194+
!!! warning
195+
The env file is sourced by `bash`, so any shell syntax it contains will be
196+
evaluated. Only point these variables at sources you trust. `EULA` cannot be
197+
set this way — it is checked before the env file is loaded.
198+
159199
## Mods/plugins list
160200

161201
You may also download or copy over individual mods/plugins using the `MODS` or `PLUGINS` environment variables. Both are a comma or newline delimited list of

scripts/start-configuration

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,23 @@ fi
152152

153153
cd /data || exit 1
154154

155+
##########################################
156+
# Optionally load environment variables from a file or archive entry,
157+
# allowing packs/artifacts to declare TYPE, VERSION and other settings
158+
# inside-out. Loaded values override anything passed in through docker -e.
159+
160+
if [[ ${LOAD_ENV_FROM_FILE:-} ]]; then
161+
if ! loadEnvFromFile "${LOAD_ENV_FROM_FILE}"; then
162+
exit 1
163+
fi
164+
fi
165+
166+
if [[ ${LOAD_ENV_FROM_ARCHIVE:-} ]]; then
167+
if ! loadEnvFromArchive "${LOAD_ENV_FROM_ARCHIVE}" "${LOAD_ENV_FROM_ARCHIVE_ENTRY:-.env}"; then
168+
exit 1
169+
fi
170+
fi
171+
155172
export DECLARED_TYPE=${TYPE^^}
156173
export DECLARED_VERSION="$VERSION"
157174

scripts/start-utils

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,71 @@ function applyResultsFile() {
2626
set +a
2727
}
2828

29+
function loadEnvFromFile() {
30+
local source=${1?Missing required source argument}
31+
local downloaded=
32+
33+
if isURL "$source"; then
34+
downloaded=$(mktemp)
35+
log "Downloading env file from $source"
36+
if ! get -o "$downloaded" "$source"; then
37+
logError "Failed to download env file from $source"
38+
rm -f "$downloaded"
39+
return 1
40+
fi
41+
log "Loading env vars from $source"
42+
applyResultsFile "$downloaded"
43+
rm -f "$downloaded"
44+
elif [ -f "$source" ]; then
45+
log "Loading env vars from $source"
46+
applyResultsFile "$source"
47+
else
48+
logError "Env file not found: $source"
49+
return 1
50+
fi
51+
}
52+
53+
function loadEnvFromArchive() {
54+
local source=${1?Missing required source argument}
55+
local entry=${2:-.env}
56+
local archive=
57+
local downloaded=
58+
local tmpdir
59+
local rc=0
60+
61+
if isURL "$source"; then
62+
downloaded=$(mktemp)
63+
log "Downloading archive from $source"
64+
if ! get -o "$downloaded" "$source"; then
65+
logError "Failed to download archive from $source"
66+
rm -f "$downloaded"
67+
return 1
68+
fi
69+
archive=$downloaded
70+
elif [ -f "$source" ]; then
71+
archive=$source
72+
else
73+
logError "Archive not found: $source"
74+
return 1
75+
fi
76+
77+
tmpdir=$(mktemp -d)
78+
if ! extract "$archive" "$tmpdir"; then
79+
logError "Failed to extract $source"
80+
rc=1
81+
elif [ ! -f "$tmpdir/$entry" ]; then
82+
logError "Entry '$entry' not found in archive $source"
83+
rc=1
84+
else
85+
log "Loading env vars from '$entry' in $source"
86+
applyResultsFile "$tmpdir/$entry"
87+
fi
88+
89+
rm -rf "$tmpdir"
90+
[[ -n "$downloaded" ]] && rm -f "$downloaded"
91+
return $rc
92+
}
93+
2994
function join_by() {
3095
local d=$1
3196
shift
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
services:
2+
mc:
3+
image: ${IMAGE_TO_TEST:-itzg/minecraft-server}
4+
environment:
5+
EULA: "true"
6+
SETUP_ONLY: "true"
7+
LOAD_ENV_FROM_ARCHIVE: /test/load-env.zip
8+
MOTD: from-compose
9+
LOG_TIMESTAMP: "true"
10+
DEBUG: "true"
11+
# the following are only used to speed up test execution
12+
TYPE: CUSTOM
13+
CUSTOM_SERVER: /servers/fake.jar
14+
VERSION: 1.18.1
15+
volumes:
16+
- ./data:/data
17+
- ./load-env.zip:/test/load-env.zip
18+
- ./fake.jar:/servers/fake.jar

tests/setuponlytests/load-env-from-archive/fake.jar

Whitespace-only changes.
221 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mc-image-helper assert propertyEquals --file=server.properties --property=motd --expect=from-archive
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
services:
2+
mc:
3+
image: ${IMAGE_TO_TEST:-itzg/minecraft-server}
4+
environment:
5+
EULA: "true"
6+
SETUP_ONLY: "true"
7+
LOAD_ENV_FROM_FILE: /test/load-env.env
8+
MOTD: from-compose
9+
LOG_TIMESTAMP: "true"
10+
DEBUG: "true"
11+
# the following are only used to speed up test execution
12+
TYPE: CUSTOM
13+
CUSTOM_SERVER: /servers/fake.jar
14+
VERSION: 1.18.1
15+
volumes:
16+
- ./data:/data
17+
- ./load-env.env:/test/load-env.env
18+
- ./fake.jar:/servers/fake.jar

tests/setuponlytests/load-env-from-file/fake.jar

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Loaded by LOAD_ENV_FROM_FILE during start-configuration
2+
MOTD=from-env-file

0 commit comments

Comments
 (0)