Compare commits

..

7 Commits

Author SHA1 Message Date
708e9526ef docs: Consolidate rootless documentation and mark as experimental
- Remove separate README-ROOTLESS.md file
- Integrate rootless documentation into main README.md
- Mark rootless support as experimental
- Add clear documentation about limitations and use cases
- Include warning about experimental nature

This consolidates all documentation in one place and makes it clear
that rootless support is still experimental.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 13:44:28 +09:00
44e651a3a6 refactor: Replace build system with unified solution
- Remove old build.py and build-rootless.py wrapper scripts
- Rename build-unified.py to build.py as the main build script
- Delete BUILD_MIGRATION.md (no longer needed)
- Update CI workflow to use new build.py syntax
- Update documentation in CLAUDE.md and README-ROOTLESS.md

The new build system provides all functionality in a single script:
- Default: builds regular images
- --rootless: builds only rootless images
- --both: builds both regular and rootless images
- --multiarch and --push-tags: work as before

This creates a cleaner, more maintainable build system.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 13:41:45 +09:00
9a401315f5 chore: Add Python cache to .gitignore and remove from repo
- Add __pycache__/ and Python compiled files to .gitignore
- Remove accidentally committed __pycache__ directory
- Prevent future Python cache files from being tracked

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 13:17:22 +09:00
f86d9ef67f refactor: Unify build system for regular and rootless images
- Create build-unified.py that handles both regular and rootless builds
- Convert build.py and build-rootless.py to wrapper scripts for backwards compatibility
- Update CI workflow to use unified build command
- Add BUILD_MIGRATION.md documentation
- Eliminate code duplication between build scripts
- Support flexible build options: --rootless, --both, --only-stable-latest

This maintains all existing functionality while providing a cleaner, more maintainable build system.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 13:12:50 +09:00
4460fb51e6 feat: Add rootless image building to CI pipeline
- Update docker-build.yml workflow to build rootless variants
- Rootless images are built after regular images with -rootless suffix
- Both use the same multi-architecture build process
- Triggered automatically when buildinfo.json changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 13:02:56 +09:00
445a3291cc fix: Address linting issues in rootless Docker implementation
- Add --no-install-recommends to apt-get install in Dockerfile
- Consolidate consecutive RUN instructions in Dockerfile
- Fix shellcheck warnings: quote variables and use -n instead of \! -z
- These changes improve best practices without affecting functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-06 19:35:51 +09:00
7e59315e3d feat: Add rootless Docker support
Implements #547 - Add support for rootless Docker images to avoid permission issues.

Key changes:
- Add Dockerfile.rootless that runs as UID 1000 by default
- Create simplified entrypoint script without chown operations
- Add build-rootless.py to build rootless variants with -rootless suffix
- Document rootless usage in README-ROOTLESS.md
- Update main README with rootless section

The rootless images eliminate common permission problems by:
- Running as non-root from the start (USER 1000:1000)
- Avoiding recursive chown operations that can cause race conditions
- Using open permissions (777) on directories during build
- Not supporting PUID/PGID environment variables

This provides a cleaner solution for rootless Docker users and those
experiencing permission issues with volumes.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-03 20:07:42 +09:00
6 changed files with 388 additions and 491 deletions

View File

@ -1,408 +0,0 @@
# Factorio Docker Permission Issues - Solutions and Workarounds
This document provides comprehensive solutions and workarounds for permission-related issues in the Factorio Docker container, based on detailed analysis of issues #558, #556, #555, #549, #496, #501, #492, and #420.
## Table of Contents
- [Root Cause Analysis](#root-cause-analysis)
- [Critical Prerequisites](#critical-prerequisites)
- [General Solutions](#general-solutions)
- [Platform-Specific Issues](#platform-specific-issues)
- [Docker System Requirements](#docker-system-requirements)
- [Advanced Troubleshooting](#advanced-troubleshooting)
- [Known Issues and Limitations](#known-issues-and-limitations)
## Root Cause Analysis
Based on detailed investigation by maintainer @Fank and community reports, the permission issues stem from:
1. **Container Architecture Issues**:
- No `USER` directive in Dockerfile despite creating a factorio user
- Container starts as root and performs recursive `chown` on every start
- The recursive `chown -R factorio:factorio /factorio` can be interrupted, leaving inconsistent permissions
- Dynamic UID/GID mapping using PUID/PGID environment variables adds complexity
2. **Rootless Docker Complications**:
- UID namespace remapping (e.g., container UID 845 → host UID 100844)
- Rootless Docker daemons cannot change ownership of bind-mounted volumes
- Different rootless implementations use different UID mappings
3. **Host System Dependencies**:
- Older Docker versions (especially pre-20.x) have permission handling bugs
- Some kernel versions have issues with user namespace operations
- SELinux and AppArmor can interfere with volume permissions
## Critical Prerequisites
### Update Your System First!
Many permission issues are caused by outdated system components:
```bash
# For Ubuntu/Debian
sudo apt-get update
sudo apt-get upgrade
# Specifically update Docker to 27.x or newer
# Follow: https://docs.docker.com/engine/install/ubuntu/#install-docker-engine
```
**Important**: Multiple users reported that updating Docker resolved their "Operation not permitted" errors.
## General Solutions
### Solution A: Pre-create Directories with Correct Permissions
```bash
# Create the directory structure
sudo mkdir -p /opt/factorio/{saves,mods,config,scenarios,script-output}
# Set ownership to factorio user (845:845)
sudo chown -R 845:845 /opt/factorio
# Set appropriate permissions (note the 'u+rwx' for write access)
sudo chmod -R u+rwx /opt/factorio
```
### Solution B: Use the Rootless Docker Image (Recommended)
The project now provides a rootless variant that runs as UID 1000, which avoids most permission issues:
```bash
docker run -d \
-p 34197:34197/udp \
-p 27015:27015/tcp \
-v /opt/factorio:/factorio \
--name factorio \
factoriotools/factorio:latest-rootless
```
**Benefits of rootless images**:
- No `chown` operations on startup
- No need to pre-create directories with specific permissions
- Works seamlessly with rootless Docker installations
- Avoids the recursive permission changes that can be interrupted
**Available rootless tags**:
- `latest-rootless`
- `stable-rootless`
- `2.0.55-rootless` (or any specific version with `-rootless` suffix)
## Platform-Specific Issues and Solutions
### NixOS with Rootless Docker
**Problem**: Permission denied errors when creating directories, even after setting ownership to 845:845. Files show ownership by UID 100844 instead of 845.
**Solutions**:
1. **Find and use your actual rootless Docker user ID**:
```bash
# Method 1: Check your user ID
id -u
# Method 2: Check existing Docker volumes for the UID Docker is using
ls -lan /path/to/other/docker/volumes
# Common rootless Docker UIDs:
# - 100999 (NixOS default)
# - 100844 (as reported in issue #558)
# - 1000 (some configurations)
# Apply the correct ownership
sudo chown -R 100999:100999 ./factorio
```
2. **Configure NixOS Docker properly**:
```nix
# In configuration.nix
virtualisation.docker.rootless = {
enable = true;
setSocketVariable = true;
};
```
3. **Port Mapping Issues**: Rootless Docker on NixOS has issues with userland-proxy that can cause random port assignments. Consider using host networking if possible.
### macOS with Colima
**Problem**: `copy_file` permission denied errors, even with correct ownership. Permission errors when running docker-dlc.sh.
**Solutions**:
1. **Set broader permissions before mounting**:
```bash
# Create directory structure
mkdir -p ./factorio-server/{saves,mods,config,scenarios}
# Set ownership AND permissions
sudo chown -R 845:845 ./factorio-server
sudo chmod -R 775 ./factorio-server
```
2. **Use Docker Desktop instead of Colima** if the issues persist, as it has better macOS integration
3. **Specify PUID/PGID explicitly**:
```yaml
environment:
- PUID=502 # Common macOS user ID
- PGID=20 # Common macOS staff group
```
### Windows
**Problem**: Cannot remove temporary locale files due to Windows-Linux permission translation. Errors like "Permission denied trying to remove /factorio/temp/currently-playing/locale/de".
**Solutions**:
1. **Use WSL2 backend** for Docker Desktop (required for proper Linux filesystem semantics)
2. **Store volumes in WSL2 filesystem** instead of Windows filesystem:
```bash
# Inside WSL2 terminal
mkdir -p ~/factorio
chmod -R 777 ~/factorio
```
```yaml
# docker-compose.yml - use WSL2 path
volumes:
- ~/factorio:/factorio
```
3. **Avoid Windows drive mounts** (like `W:\docker\factorio`) as they have inherent permission translation issues
4. **Add :Z flag for SELinux context** (some Windows Docker setups benefit from this):
```yaml
volumes:
- ~/factorio:/factorio:Z
```
### Synology NAS
**Problem**: Permission denied when accessing mounted volumes. Error: "filesystem error: status: Permission denied [/factorio/saves]".
**Solutions**:
1. **Create and set permissions via SSH**:
```bash
# SSH into Synology
sudo mkdir -p /volume1/docker/factorio
sudo chown -R 845:845 /volume1/docker/factorio
sudo chmod -R u+rwx /volume1/docker/factorio # Important: u+rwx for write access
```
2. **Use the correct volume path in your container**:
```bash
docker run -d \
-p 34197:34197/udp \
-p 27015:27015/tcp \
-v /volume1/docker/factorio:/factorio \
--name factorio \
--restart=always \
factoriotools/factorio
```
3. **Check DSM Docker permissions** - ensure the Docker package has proper permissions to the shared folder
## Docker System Requirements
### Minimum Docker Version
Based on community reports, these Docker versions are known to work:
- **Docker 27.4.1** - Confirmed working
- **Docker 20.x+** - Generally stable
- **Docker 19.x and below** - Known permission issues
**Check your Docker version**:
```bash
docker --version
# If below 20.x, update immediately!
```
### "Operation not permitted" at Util.cpp:81
This specific error is often caused by:
1. **Outdated Docker version** - Update Docker first!
2. **Outdated kernel** - Run system updates
3. **Missing kernel capabilities** - Check Docker daemon configuration
## Docker Compose Best Practices
### Basic Configuration
```yaml
version: '3'
services:
factorio:
image: factoriotools/factorio:stable
container_name: factorio
ports:
- "34197:34197/udp"
- "27015:27015/tcp"
volumes:
- ./factorio:/factorio
restart: unless-stopped
stdin_open: true # For interactive console
tty: true
```
### Advanced Configuration for Permission Issues
```yaml
version: '3'
services:
factorio:
image: factoriotools/factorio:stable
container_name: factorio
ports:
- "34197:34197/udp"
- "27015:27015/tcp"
volumes:
- ./factorio:/factorio:Z # :Z for SELinux systems
restart: unless-stopped
# user: "845:845" # WARNING: This might break the entrypoint script
environment:
- PUID=845
- PGID=845
- UPDATE_MODS_ON_START=false # Disable if having permission issues
```
### Rootless Docker Configuration
```yaml
version: '3'
services:
factorio:
image: factoriotools/factorio:latest-rootless
container_name: factorio
ports:
- "34197:34197/udp"
- "27015:27015/tcp"
volumes:
- ./factorio:/factorio
restart: unless-stopped
environment:
- PUID=1000 # Rootless default
- PGID=1000
```
## Advanced Troubleshooting
### Step-by-Step Diagnosis
1. **Check Current Ownership**:
```bash
ls -lan ./factorio
# Look for UIDs like 845, 1000, 100844, 100999
```
2. **Verify Docker User Mapping**:
```bash
# Check what user the container is running as
docker exec factorio id
# Check file ownership inside container
docker exec factorio ls -lan /factorio
```
3. **Test Without Volume Mount** (isolates host permission issues):
```bash
docker run --rm -it factoriotools/factorio:stable
# If this works, the issue is with your host volume permissions
```
4. **Check Security Modules**:
```bash
# SELinux (Fedora, RHEL, CentOS)
getenforce
# If "Enforcing", try adding :Z to volume mount
# AppArmor (Ubuntu, Debian)
sudo apparmor_status | grep docker
```
5. **Debug the Entrypoint Script**:
```bash
# Run with debug output
docker run --rm -it \
-e DEBUG=true \
-v ./factorio:/factorio \
factoriotools/factorio:stable
```
### Common Error Messages and Solutions
| Error | Cause | Solution |
|-------|-------|----------|
| `Util.cpp:81: Operation not permitted` | Outdated Docker/kernel | Update Docker and system packages |
| `chown: Operation not permitted` | Rootless Docker | Use rootless Docker UID for ownership |
| `Permission denied [/factorio/saves]` | Wrong directory permissions | `chmod -R u+rwx` on host directory |
| `Couldn't create lock file /factorio/.lock` | Container can't write to volume | Check volume mount and permissions |
| `Map version X cannot be loaded` | Version mismatch | Use correct Docker image version |
## Known Issues and Limitations
### Interrupted chown Operations
The container performs `chown -R factorio:factorio /factorio` on every start. If the container is killed during this operation:
- Files will have inconsistent ownership
- Some files owned by 845, others by different UIDs
- Solution: Let the container complete startup before stopping
### Rootless Docker Port Mapping
**Issue #496**: Rootless Docker with userland-proxy causes random port assignments instead of the configured 34197.
- **Workaround**: Use host networking mode if possible
- **Note**: This is a Docker limitation, not specific to this image
### Map Version Compatibility
**Problem**: "Map version 2.0.23-0 cannot be loaded because it is higher than the game version".
**Solution**:
```bash
# Use a version that matches or exceeds your save
docker pull factoriotools/factorio:2.0.23
# Or always use latest for newest features
docker pull factoriotools/factorio:latest
```
## Recommended Approach
### For New Installations
1. **Update your system first** - Many issues are caused by old Docker versions
2. **Try the rootless image first** - It avoids most permission issues entirely
3. **Pre-create directories** with correct permissions if using the standard image
4. **Test without volumes** first to ensure the image works
### For Existing Installations with Issues
1. **Stop the container** and let it shut down cleanly
2. **Backup your data** before making changes
3. **Check Docker version** - update if below 20.x
4. **Fix permissions** using the platform-specific solution
5. **Consider rootless variant** for easier permission management
### Best Practices
- **Let the container start fully** before stopping (avoid interrupted chown)
- **Use named volumes** instead of bind mounts when possible
- **Monitor first startup** to ensure permissions are set correctly
- **Keep Docker updated** to avoid known bugs
## Community Solutions
### Proposed Improvements (from @Fank)
1. **Add USER directive** in Dockerfile after creating directories
2. **Optimize chown logic** to only run when ownership is wrong
3. **Implement fixuid** for better UID/GID mapping
4. **Add health checks** to ensure permissions are correct before starting
### Alternative Images
Some users have tried other Factorio Docker images (e.g., goofball222/factorio) but report the same Util.cpp:81 errors, suggesting this is a broader ecosystem issue related to Docker versions and system configurations.
## Quick Reference
| Platform | Common UID | Recommended Approach |
|----------|-----------|---------------------|
| Standard Docker | 845 | Update Docker, use `chown 845:845` |
| Rootless Docker (NixOS) | 100999, 100844 | Find actual UID, chown to that |
| macOS (Docker Desktop) | 502 (user), 20 (staff) | Use PUID/PGID env vars |
| Windows | N/A | Use WSL2 filesystem |
| Synology NAS | varies | Check DSM user, ensure Docker has folder access |
## Getting Help
If these solutions don't work:
1. **Update everything first** (Docker, kernel, system packages)
2. **Provide full details** when reporting issues:
- Docker version (`docker --version`)
- OS and version
- Full error messages
- Output of `ls -lan` on your volume
3. **Try the rootless image** as an alternative
4. **Check issue #558** for ongoing discussions
Remember: The vast majority of permission issues are resolved by updating Docker to version 20.x or newer!

View File

@ -6,17 +6,8 @@
[中文](./README_zh_CN.md)
<!-- start autogeneration tags -->
* `latest, 2.0.60`
* `latest, 2.0.58`
* `2, 2.0, 2.0.55, stable, stable-2.0.55`
* `2.0.59`
* `stable-1.1.110, 1, 1.1, 1.1.110`
* `1.0.0, 1.0`
* `0.17.79, 0.17`
* `0.16.51, 0.16`
* `0.15.40, 0.15`
* `0.14.23, 0.14`
* `0.13.20, 0.13`
* `0.12.35, 0.12`
<!-- end autogeneration tags -->
## Tag descriptions
@ -184,8 +175,6 @@ Copy mods into the mods folder and restart the server.
As of 0.17 a new environment variable was added ``UPDATE_MODS_ON_START`` which if set to ``true`` will cause the mods get to updated on server start. If set a valid [Factorio Username and Token](https://www.factorio.com/profile) must be supplied or else the server will not start. They can either be set as docker secrets, environment variables, or pulled from the server-settings.json file.
**Note:** When using the Space Age DLC, the built-in mods (`elevated-rails`, `quality`, and `space-age`) are automatically skipped during mod updates to prevent conflicts. These mods are included with the DLC and should not be downloaded separately.
### Scenarios
If you want to launch a scenario from a clean start (not from a saved map) you'll need to start the docker image from an alternate entrypoint. To do this, use the example entrypoint file stored in the /factorio/entrypoints directory in the volume, and launch the image with the following syntax. Note that this is the normal syntax with the addition of the --entrypoint setting AND the additional argument at the end, which is the name of the Scenario in the Scenarios folder.
@ -505,19 +494,6 @@ Consider using rootless images if you:
## Troubleshooting
### Permission Issues
If you're experiencing permission errors such as:
- `chown: Operation not permitted`
- `Permission denied [/factorio/saves]`
- `Util.cpp:81: Operation not permitted`
- Files owned by unexpected UIDs (like 100844 instead of 845)
Please refer to our comprehensive [Permission Issues Guide](./PERMISSION_ISSUES_GUIDE.md) for detailed solutions. Common fixes include:
- **Updating Docker** to version 20.x or newer (this resolves many issues)
- **Using the rootless image** variants (e.g., `factoriotools/factorio:stable-rootless`)
- **Setting correct ownership** for your specific Docker configuration
### My server is listed in the server browser, but nobody can connect
Check the logs. If there is the line `Own address is RIGHT IP:WRONG PORT`, then this could be caused by the Docker proxy. If the the IP and port is correct it's probably a port forwarding or firewall issue instead.

View File

@ -1,19 +1,363 @@
{
"0.12.35": {
"sha256": "ab9cf01a56dde3073aaaa5152c628bbf9a5bb85638b87dc3d7fdb77fb169aedd",
"tags": [
"0.12.35",
"0.12"
]
},
"0.13.20": {
"sha256": "cbf5481e4b7e0efcc07c7b6a1fc3ff1404ad5597f3c9d37914a52ffb58d7c159",
"tags": [
"0.13.20",
"0.13"
]
},
"0.14.23": {
"sha256": "96c3e7acd4e0f066a499baba01823cac7c1caf0e50dbddcea5793f57bd60dc8c",
"tags": [
"0.14.23",
"0.14"
]
},
"0.15.40": {
"sha256": "1041ef61ea4aecd1f425e6030a909f0c349a9c01d1b3324d84a61b1cfef5ba6c",
"tags": [
"0.15.40",
"0.15"
]
},
"0.16.51": {
"sha256": "6cb09f5ac87f16f8d5b43cef26c0ae26cc46a57a0382e253dfda032dc5bb367f",
"tags": [
"0.16.51",
"0.16"
]
},
"0.17.79": {
"sha256": "9ace12fa986df028dc1851bf4de2cb038044d743e98823bc1c48ba21aa4d23df",
"tags": [
"0.17.79",
"0.17"
]
},
"1.0.0": {
"sha256": "81d9e1aa94435aeec4131c8869fa6e9331726bea1ea31db750b65ba42dbd1464",
"tags": [
"1.0.0",
"1.0"
]
},
"1.1.110": {
"sha256": "485fe6db36e5decd7dd0d70e7c97e61f818100fa3e48d87884b287027c7a646a",
"tags": [
"stable-1.1.110",
"1",
"1.1",
"1.1.110"
]
},
"2.0.13": {
"sha256": "27b36901a39e593adf28418c0286142c6c7a9f83d156963c7369bd405a25c7d1",
"tags": [
"stable-2.0.13",
"2.0",
"2.0.13"
]
},
"2.0.14": {
"sha256": "5a4bc4c3b2a97ed1fc58eb796321e848dcc64435bd91013dd9c78a14a8ce8815",
"tags": [
"stable-2.0.14",
"2.0",
"2.0.14"
]
},
"2.0.15": {
"sha256": "70b441cb807811a60586c01107248c1d8d7ae043bd1f23675fc924fbaaa538d8",
"tags": [
"stable-2.0.15",
"2.0",
"2.0.15"
]
},
"2.0.16": {
"sha256": "f2069b4b746500d945eeb67ef7eda5e7aebe7fd0294c2af4e117af22a3bbaea3",
"tags": [
"2.0.16"
]
},
"2.0.17": {
"sha256": "183407f2fb21e05152442ffb5f15ffb283994339ca6a51b3559a257c30505e5e",
"tags": [
"2.0.17"
]
},
"2.0.18": {
"sha256": "f378a1dc8a545c13d8ca616cbe72d245aa3ce93e3f219d8d60d3c06c7df82dc0",
"tags": [
"2.0.18"
]
},
"2.0.19": {
"sha256": "2e27aca3a7f65b50916d14a62203b6861cbe657e8d2dbd8f813e0a606efce9c7",
"tags": [
"2.0.19"
]
},
"2.0.20": {
"sha256": "c4a901f2f1dbedbb41654560db4c6fab683a30c20334e805d4ef740c0416515a",
"tags": [
"stable-2.0.20",
"2.0",
"2.0.20"
]
},
"2.0.21": {
"sha256": "1d6d2785006d6a8d9d5fdcdaa7097a189ec35ba95f3521025dc4e046f7a1398e",
"tags": [
"stable-2.0.21",
"2.0",
"2.0.21"
]
},
"2.0.22": {
"sha256": "14c3eea7600fbe7f35bca52fe4c277e8f5e23b34c35ebebaa46c6752c750cb85",
"tags": [
"2.0.22"
]
},
"2.0.23": {
"sha256": "e819fc9ad6df061bf9d4bffc91988dd18d0e3982c8b1c22c0525d78bda3ef216",
"tags": [
"stable-2.0.23",
"2.0",
"2.0.23"
]
},
"2.0.24": {
"sha256": "4644acc4195391fe19a7468c546d10a494ce1a188964c79f20cb0fa050b67120",
"tags": [
"2.0.24"
]
},
"2.0.25": {
"sha256": "0d1698f1f29759ff27faa6a5d9c3804377cb1767f2692003a8e9d4c294845e5a",
"tags": [
"2.0.25"
]
},
"2.0.26": {
"sha256": "a401024039372a53b9a29b7deb4ac279cd9a34abe69426a109a13a9a1c599f1f",
"tags": [
"2.0.26"
]
},
"2.0.27": {
"sha256": "63c75ce74cd9d1e4b65ae9f98e9865abdbe3d600fb3259dcda5ea69a512b2993",
"tags": [
"2.0.27"
]
},
"2.0.28": {
"sha256": "ea9937b6adc7a18e17a4e1e64992ec389407497b36e68280bb14fcdd4c884dd3",
"tags": [
"stable-2.0.28",
"2.0",
"2.0.28"
]
},
"2.0.29": {
"sha256": "54088c9cacfddbce2e7bf90604fea095ff723e70d9bb056e1fb469b900a50f09",
"tags": [
"2.0.29"
]
},
"2.0.30": {
"sha256": "4137824a20e1f3298410432c85e62d0eb46b0dab1a8411c233699f890d4c1668",
"tags": [
"stable-2.0.30",
"2.0",
"2.0.30"
]
},
"2.0.31": {
"sha256": "0ee39ff6181ef41b606b7ba1ab5c04d8f81579ef56ec4947e4d74ce5d192b5d5",
"tags": [
"2.0.31"
]
},
"2.0.32": {
"sha256": "2a6102ae42dcc5e8fe923bd68bcd326a569e35912acde121301e5d4d2d856417",
"tags": [
"stable-2.0.32",
"2.0",
"2.0.32"
]
},
"2.0.33": {
"sha256": "9365a34d1724e5c9f592cc9da511485e2fa7da1c12df08029bce478586ba4b7b",
"tags": [
"2.0.33"
]
},
"2.0.34": {
"sha256": "9511462203ebb2763f9f8623bb17f3070041ae3cbd7d80284c1e9bb38c09fc40",
"tags": [
"2.0.34"
]
},
"2.0.35": {
"sha256": "31cd58eaf4b06cc0dc5d82640f7adf2366aa9da64133d2c228f1308f1060a990",
"tags": [
"2.0.35"
]
},
"2.0.36": {
"sha256": "e94567b986654f1f7c3ec5c8bd151e3768b4ab9ab9cc389f6b9fd8e0dab32ce2",
"tags": [
"2.0.36"
]
},
"2.0.37": {
"sha256": "5f105131fe4f48d47fd813f57b6bd275840a47b21e39b30d22bf5da30075a786",
"tags": [
"2.0.37"
]
},
"2.0.38": {
"sha256": "ad9650f7456aecc8adb5369eedb418507c7643bede0da60fc1a239878d4902de",
"tags": [
"2.0.38"
]
},
"2.0.39": {
"sha256": "0f8a3d0e43797b5ff4d8b85d7c334b095a3f07d9aa7f80b1e87f94939a93df34",
"tags": [
"stable-2.0.39",
"2.0",
"2.0.39"
]
},
"2.0.40": {
"sha256": "eac1f24afb68acbfcf1d72d2ad142e8584d77f2d100a3af743f106e50ac176d3",
"tags": [
"2.0.40"
]
},
"2.0.41": {
"sha256": "77ebccae8167fc1a9fc4da8c11e8410f6017b92b1a0913eb58ac5285c9eec399",
"tags": [
"stable-2.0.41",
"2.0",
"2.0.41"
]
},
"2.0.42": {
"sha256": "b5b8b8bdc915e67dbc1710cd3d6aa6802d397b7c0f47db07da8acf39d5bd6376",
"tags": [
"stable-2.0.42",
"2.0",
"2.0.42"
]
},
"2.0.43": {
"sha256": "bde6e167330c4439ce7df3ac519ea445120258ef676f1f6ad31d0c2816d3aee3",
"tags": [
"stable-2.0.43",
"2.0",
"2.0.43"
]
},
"2.0.44": {
"sha256": "9468c5e07080c01eb7a734036160bf806d62cafc11465a23150cfbd210e1036d",
"tags": [
"2.0.44"
]
},
"2.0.45": {
"sha256": "4fd7e04bb3ea7d12da8e1c3befc6b53b3c0064775c960a5a9db6a943f2259fc2",
"tags": [
"2.0.45"
]
},
"2.0.46": {
"sha256": "fc611b6d4078b5d9448284c2890f7e0b6b1f203d52f622c655d3600982489c3e",
"tags": [
"2.0.46"
]
},
"2.0.47": {
"sha256": "f0f320c77616a4794227eb637a70b557108f3141a4633276593220a768f49a26",
"tags": [
"stable-2.0.47",
"2.0",
"2.0.47"
]
},
"2.0.48": {
"sha256": "f0038835e96bbacc19d52d22d47469882d9ebe41a4e5213c0471020647a1ee2d",
"tags": [
"2.0.48"
]
},
"2.0.49": {
"sha256": "ef0648ca1ba44c145a3a3e4c174ccd276eb4a335155a20df1ae0e47156fa34ff",
"tags": [
"2.0.49"
]
},
"2.0.50": {
"sha256": "81d4aec735473c5bd2c87f09abcd793c31cb9a07d9fdf3c3d7275c78ebe4bc18",
"tags": [
"2.0.50"
]
},
"2.0.51": {
"sha256": "fc940dea67d25d3fd403531520e8afda2779ff1fa8050f535ac1351b7873a070",
"tags": [
"2.0.51"
]
},
"2.0.52": {
"sha256": "be8d6216890089890693d6d94f141f745d35c53e52c6b942f6c944f5c00c8c26",
"tags": [
"2.0.52"
]
},
"2.0.53": {
"sha256": "40a57076f80dbee0238dab62f16585def06f7d7e5b41f6b677be41b4d2cae811",
"tags": [
"2.0.53"
]
},
"2.0.54": {
"sha256": "ad47c541b70763552bcf597202ee84aaac727d0ba158873134dc163a3a0506f0",
"tags": [
"2.0.54"
]
},
"2.0.55": {
"sha256": "ef12a54d1556ae1f84ff99edc23706d13b7ad41f1c02d74ca1dfadf9448fcbae",
"tags": [
"stable",
"stable-2.0.55",
"2",
"2.0",
"2.0.55"
"2.0.55",
"stable"
]
},
"2.0.60": {
"sha256": "69b5be1a867fd99524f9914dfee900a1ac386cf4e74c4a63768c05dc4d2b2b0b",
"2.0.57": {
"sha256": "22b232afb77067c68a3afe087be6a0ee760479262598a12a709e1b03ea9508a6",
"tags": [
"2.0.57"
]
},
"2.0.58": {
"sha256": "be82e1aeba4169420e1b00c12a3e00ec2309a41327f9d6c335feec27bbc885e6",
"tags": [
"latest",
"2.0.60"
"2.0.58"
]
}
}

View File

@ -43,7 +43,7 @@ ENV PORT=34197 \
SHELL ["/bin/bash", "-eo", "pipefail", "-c"]
RUN apt-get -q update \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils procps gettext-base file --no-install-recommends \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils procps gettext-base --no-install-recommends \
&& if [[ "$(uname -m)" == "aarch64" ]]; then \
echo "installing ARM compatability layer" \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install unzip --no-install-recommends \

View File

@ -23,33 +23,24 @@ print_failure()
echo "$1"
}
# Checks if the current game version satisfies the mod's minimum required version.
# Returns 1 if the game version is compatible with the mod, 0 if not
# Checks game version vs version in mod.
# Returns 0 if major version differs or mod minor version is less than game version, 1 if ok
check_game_version() {
local mod_required_version="$1" # The minimum Factorio version required by the mod
local current_game_version="$2" # The current Factorio version
local game_version="$1"
local mod_version="$2"
local mod_major mod_minor game_major game_minor
mod_major=$(echo "$mod_required_version" | cut -d '.' -f1)
mod_minor=$(echo "$mod_required_version" | cut -d '.' -f2)
game_major=$(echo "$current_game_version" | cut -d '.' -f1)
game_minor=$(echo "$current_game_version" | cut -d '.' -f2)
local game_major mod_major game_minor mod_minor
game_major=$(echo "$game_version" | cut -d '.' -f1)
game_minor=$(echo "$game_version" | cut -d '.' -f2)
mod_major=$(echo "$mod_version" | cut -d '.' -f1)
mod_minor=$(echo "$mod_version" | cut -d '.' -f2)
# If game major version is greater than mod's required major version, it's compatible
if [[ "$game_major" -gt "$mod_major" ]]; then
echo 1
return
fi
# If game major version is less than mod's required major version, it's not compatible
if [[ "$game_major" -lt "$mod_major" ]]; then
if [[ "$game_major" -ne "$mod_major" ]]; then
echo 0
return
fi
# Major versions are equal, check minor versions
# Game minor version must be >= mod's required minor version
if [[ "$game_minor" -ge "$mod_minor" ]]; then
if [[ "$mod_minor" -ge "$game_minor" ]]; then
echo 1
else
echo 0
@ -88,7 +79,7 @@ check_dependency_version()
fi
;;
">")
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | head -n1)" == "$required_version" && "$required_version" != "$mod_version" ]]; then
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | head -n1)" == "$required_version" && "$required_version" != "$FACTORIO_VERSION" ]]; then
echo 1
else
echo 0
@ -102,7 +93,7 @@ check_dependency_version()
fi
;;
"<")
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | tail -n1)" == "$required_version" && "$required_version" != "$mod_version" ]]; then
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | tail -n1)" == "$required_version" && "$required_version" != "$FACTORIO_VERSION" ]]; then
echo 1
else
echo 0
@ -125,15 +116,11 @@ get_mod_info()
{
local mod_info_json="$1"
# Process mod releases from newest to oldest, looking for a compatible version
while IFS= read -r mod_release_info; do
local mod_version mod_factorio_version
mod_version=$(echo "$mod_release_info" | jq -r ".version")
mod_factorio_version=$(echo "$mod_release_info" | jq -r ".info_json.factorio_version")
# Check if this mod version is compatible with our Factorio version
# This prevents downloading mods that require a newer Factorio version (fixes #468)
# and ensures backward compatibility (e.g., Factorio 2.0 can use 1.x mods) (fixes #517)
if [[ $(check_game_version "$mod_factorio_version" "$FACTORIO_VERSION") == 0 ]]; then
echo " Skipping mod version $mod_version because of factorio version mismatch" >&2
continue
@ -227,12 +214,9 @@ update_mod()
return 0
}
# Process all enabled mods from mod-list.json, but skip built-in mods
# The Space Age DLC includes built-in mods (elevated-rails, quality, space-age) that should not be downloaded
if [[ -f $MOD_DIR/mod-list.json ]]; then
jq -r ".mods|map(select(.enabled))|.[].name" "$MOD_DIR/mod-list.json" | while read -r mod; do
# Skip base mod and DLC built-in mods
if [[ $mod != base ]] && [[ $mod != elevated-rails ]] && [[ $mod != quality ]] && [[ $mod != space-age ]]; then
if [[ $mod != base ]]; then
update_mod "$mod" || true
fi
done

View File

@ -59,40 +59,41 @@ latestCurrentVersionShort=$latestCurrentVersionMajor.$latestCurrentVersionMinor
echo "stableOnlineVersionShort=${stableOnlineVersionShort} experimentalOnlineVersionShort=${experimentalOnlineVersionShort}"
echo "stableCurrentVersionShort=${stableCurrentVersionShort} latestCurrentVersionShort=${latestCurrentVersionShort}"
# Create new buildinfo.json with only current versions
tmpfile=$(mktemp)
# Start with empty JSON object
echo '{}' > "$tmpfile"
# Remove stable tag
cp buildinfo.json "$tmpfile"
jq --arg stable_current_version "$stable_current_version" 'with_entries(if .key == $stable_current_version then .value.tags |= . - ["stable"] else . end)' "$tmpfile" > buildinfo.json
rm -f -- "$tmpfile"
# Add stable version
if [[ "$stable_online_version" == "$experimental_online_version" ]]; then
# Stable and experimental are the same version
jq --arg stable_online_version "$stable_online_version" --arg sha256 "$stable_sha256" --arg stableOnlineVersionShort "$stableOnlineVersionShort" --arg stableOnlineVersionMajor "$stableOnlineVersionMajor" \
'. + {($stable_online_version): {sha256: $sha256, tags: ["latest", "stable", ("stable-" + $stable_online_version), $stableOnlineVersionMajor, $stableOnlineVersionShort, $stable_online_version]}}' "$tmpfile" > buildinfo.json
# Remove latest tag
cp buildinfo.json "$tmpfile"
jq --arg latest_current_version "$latest_current_version" 'with_entries(if .key == $latest_current_version then .value.tags |= . - ["latest"] else . end)' "$tmpfile" > buildinfo.json
rm -f -- "$tmpfile"
# Update tag by stable
cp buildinfo.json "$tmpfile"
if [[ "$stable_online_version" == "$stable_current_version" ]]; then
jq --arg stable_current_version "$stable_current_version" --arg stable_online_version "$stable_online_version" --arg sha256 "$stable_sha256" 'with_entries(if .key == $stable_current_version then .key |= $stable_online_version | .value.sha256 |= $sha256 | .value.tags |= . - [$stable_current_version] + [$stable_online_version, "stable"] else . end)' "$tmpfile" > buildinfo.json
else
# Different stable and experimental versions
# First add stable
jq --arg stable_online_version "$stable_online_version" --arg sha256 "$stable_sha256" --arg stableOnlineVersionShort "$stableOnlineVersionShort" --arg stableOnlineVersionMajor "$stableOnlineVersionMajor" \
'. + {($stable_online_version): {sha256: $sha256, tags: ["stable", ("stable-" + $stable_online_version), $stableOnlineVersionMajor, $stableOnlineVersionShort, $stable_online_version]}}' "$tmpfile" > buildinfo.json.tmp
mv buildinfo.json.tmp "$tmpfile"
jq --arg stable_current_version "$stable_current_version" --arg stable_online_version "$stable_online_version" --arg sha256 "$stable_sha256" --arg stableOnlineVersionShort "$stableOnlineVersionShort" --arg stableOnlineVersionMajor "$stableOnlineVersionMajor" 'with_entries(if .key == $stable_current_version then .value.tags |= . - ["latest","stable",$stableOnlineVersionMajor] else . end) | to_entries | . + [{ key: $stable_online_version, value: { sha256: $sha256, tags: ["latest","stable",("stable-" + $stable_online_version),$stableOnlineVersionMajor,$stableOnlineVersionShort,$stable_online_version]}}] | from_entries' "$tmpfile" > buildinfo.json
fi
rm -f -- "$tmpfile"
# Then add experimental
# Update tag by latest
cp buildinfo.json "$tmpfile"
if [[ $experimental_online_version != "$stable_online_version" ]]; then
if [[ $stableOnlineVersionShort == "$experimentalOnlineVersionShort" ]]; then
jq --arg experimental_online_version "$experimental_online_version" --arg sha256 "$experimental_sha256" \
'. + {($experimental_online_version): {sha256: $sha256, tags: ["latest", $experimental_online_version]}}' "$tmpfile" > buildinfo.json
jq --arg experimental_online_version "$experimental_online_version" --arg stable_online_version "$stable_online_version" --arg sha256 "$experimental_sha256" 'with_entries(if .key == $stable_online_version then .value.tags |= . - ["latest"] else . end) | to_entries | . + [{ key: $experimental_online_version, value: { sha256: $sha256, tags: ["latest", $experimental_online_version]}}] | from_entries' "$tmpfile" > buildinfo.json
else
jq --arg experimental_online_version "$experimental_online_version" --arg sha256 "$experimental_sha256" --arg experimentalOnlineVersionShort "$experimentalOnlineVersionShort" --arg experimentalOnlineVersionMajor "$experimentalOnlineVersionMajor" \
'. + {($experimental_online_version): {sha256: $sha256, tags: ["latest", $experimentalOnlineVersionMajor, $experimentalOnlineVersionShort, $experimental_online_version]}}' "$tmpfile" > buildinfo.json
jq --arg experimental_online_version "$experimental_online_version" --arg stable_online_version "$stable_online_version" --arg sha256 "$experimental_sha256" --arg experimentalOnlineVersionShort "$experimentalOnlineVersionShort" --arg experimentalOnlineVersionMajor "$experimentalOnlineVersionMajor" 'with_entries(if .key == $stable_online_version then .value.tags |= . - ["latest"] else . end) | to_entries | . + [{ key: $experimental_online_version, value: { sha256: $sha256, tags: ["latest",$experimentalOnlineVersionMajor,$experimentalOnlineVersionShort,$experimental_online_version]}}] | from_entries' "$tmpfile" > buildinfo.json
fi
fi
rm -f -- "$tmpfile"
# Generate README tags with logical sorting and de-duplication
# First, collect all unique tags with their versions
# Use regular arrays for bash compatibility
declare tag_versions
declare -A tag_versions
while IFS= read -r version; do
while IFS= read -r tag; do
# If this tag is already seen, compare versions to keep the latest