Compare commits

..

19 Commits

Author SHA1 Message Date
Florian Kinder
17ec12cb7f Fix shellcheck warnings in entrypoint scripts
- Add shellcheck disable=SC1091 for dynamic source paths
- Quote INSTALLED_DIRECTORY variables to prevent word splitting
- Resolves all reported shellcheck issues in PR review

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 22:31:45 +09:00
Florian Kinder
b8ad6e06ab Fix ARM64 emulation support in all entrypoints
- Add shared setup-exec.sh script to handle ARM64 emulation logic
- Update scenario.sh and scenario2map.sh to use box64 emulation on ARM64
- Refactor docker-entrypoint.sh and docker-entrypoint-rootless.sh to use shared script
- Ensures all factorio binary calls work correctly on ARM64 platforms

Fixes #585

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 22:21:22 +09:00
github-actions[bot]
89183f5271 Auto Update Factorio to stable version: 2.0.60 experimental version: 2.0.65 2025-08-22 12:50:33 +00:00
Florian Kinder
4e0c2d0fe9 Fix missing addgroup/adduser commands in Debian slim image (#584)
Replace addgroup/adduser with groupadd/useradd which are available
in the debian:stable-slim base image. The addgroup and adduser commands
are part of the adduser package which is not installed in the slim image.

This fixes the build failure:
/bin/bash: line 1: addgroup: command not found

Continues fix for #582
2025-08-13 22:38:47 +09:00
Florian Kinder
19ca8d8ceb Fix RCON build error by adding _GNU_SOURCE for inet_aton (#583)
The build was failing with 'implicit declaration of function inet_aton'
error in newer GCC versions. Adding _GNU_SOURCE feature test macro
makes inet_aton available. Also fixed unused result warning for fread.

Fixes #582
2025-08-13 22:28:12 +09:00
github-actions[bot]
15bc6ef848 Auto Update Factorio to stable version: 2.0.60 experimental version: 2.0.64 2025-08-12 12:54:36 +00:00
dependabot[bot]
bca045c3d1 Bump actions/checkout from 4 to 5 (#581)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 14:30:52 +09:00
github-actions[bot]
b0c8562021 Auto Update Factorio to stable version: 2.0.60 experimental version: 2.0.63 2025-08-04 17:29:02 +00:00
github-actions[bot]
2fe6a0b3c7 Auto Update Factorio to stable version: 2.0.60 experimental version: 2.0.62 2025-07-31 12:57:17 +00:00
github-actions[bot]
b8cd293fdd Auto Update Factorio to stable version: 2.0.60 experimental version: 2.0.61 2025-07-30 17:26:48 +00:00
github-actions[bot]
bd774f0ad8 Auto Update Factorio to stable version: 2.0.60 experimental version: 2.0.60 2025-07-15 08:35:47 +00:00
Florian Kinder
b3099102cf Add comprehensive permission issues guide (#580)
- Created PERMISSION_ISSUES_GUIDE.md with detailed solutions for permission problems
- Based on thorough analysis of issues #558, #556, #555, #549, #496, #501, #492, #420
- Includes platform-specific solutions (NixOS, macOS, Windows, Synology)
- Documents critical Docker version requirements (20.x+ recommended)
- Highlights rootless image as recommended solution for permission issues
- Added link to guide in README.md troubleshooting section

Key findings documented:
- Most "Operation not permitted" errors resolved by updating Docker
- Rootless Docker requires using actual Docker UID (e.g., 100999) not 845
- Interrupted chown operations cause inconsistent file ownership
- Rootless images avoid most permission issues entirely

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-12 14:28:20 +09:00
Florian Kinder
e8adbf55c1 feat: Add rootless Docker support (#574)
* 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>

* 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>

* 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>

* 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>

* 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>

* 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>

* 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>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-12 13:46:33 +09:00
Florian Kinder
533789470f Fix buildinfo.json pollution by only keeping latest versions (#579)
Previously, update.sh would accumulate all historical versions in buildinfo.json,
leading to an ever-growing file. This change modifies the script to rebuild
buildinfo.json from scratch with only the current stable and experimental versions.

Changes:
- Replace incremental update logic with complete rebuild
- Create fresh buildinfo.json containing only latest versions
- Preserve all proper tags (stable, latest, version numbers, etc.)
- Fix bash compatibility issue with associative arrays

This ensures buildinfo.json remains clean and only contains the versions
currently being built and published.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-12 12:35:59 +09:00
Florian Kinder
5b6e0cde8b Add file package to Docker image for better debugging (#578)
The Docker builds have been failing since June 2025 with the error:
'/bin/bash: line 1: file: command not found'

This error occurs when the SHA256 checksum verification fails and the
Dockerfile attempts to run 'file' command to help debug what type of
file was downloaded. However, the 'file' package is not installed in
the debian:stable-slim base image.

This commit adds the 'file' package to the apt-get install list so that
when SHA256 verification fails, we can get better debugging information
about what was actually downloaded (e.g., whether it's an HTML error
page instead of the expected tar.xz file).

While this doesn't fix the root cause of the SHA256 failures, it will
provide better diagnostics to help identify the actual issue.

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

Co-Authored-By: Claude <noreply@anthropic.com>

Took 55 minutes
2025-07-12 12:21:42 +09:00
github-actions[bot]
00038b5184 Auto Update Factorio to stable version: 2.0.55 experimental version: 2.0.60 2025-07-10 12:54:47 +00:00
Florian Kinder
72c3590cd6 Fix duplicate mod error with Space Age DLC mods (#576) (#577)
Skip downloading built-in DLC mods (elevated-rails, quality, space-age) when UPDATE_MODS_ON_START is enabled. These mods are included with the Space Age DLC and attempting to download them separately causes "Duplicate mod" errors.

- Modified update-mods.sh to skip DLC built-in mods during updates
- Added documentation note explaining the automatic handling of DLC mods

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-09 21:50:59 +09:00
github-actions[bot]
23942e3117 Auto Update Factorio to stable version: 2.0.55 experimental version: 2.0.59 2025-07-09 10:29:10 +00:00
Florian Kinder
15d38ea739 Fix mod version compatibility checking for major version differences (#575)
This fixes issue #517 where the auto mod updater was downloading the wrong
mod versions after updating to Space Age (Factorio 2.0). The problem was that
the version checking logic was incorrectly rejecting compatible mod versions.

Changes:
- Fixed check_game_version() to properly handle major version differences
- Game versions can now run mods designed for older major versions (backward compatibility)
- Game versions correctly reject mods requiring newer versions
- Fixed variable references in check_dependency_version()
- Added clarifying comments about the version checking behavior

This also addresses issue #468 by ensuring mods requiring newer Factorio
versions than currently installed are properly skipped.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-06 19:01:05 +09:00
17 changed files with 537 additions and 415 deletions

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4.0.2

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: shellcheck
uses: reviewdog/action-shellcheck@v1
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: hadolint
uses: reviewdog/action-hadolint@v1

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ secrets.REPO_TOKEN }}

408
PERMISSION_ISSUES_GUIDE.md Normal file
View File

@@ -0,0 +1,408 @@
# 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,8 +6,8 @@
[中文](./README_zh_CN.md)
<!-- start autogeneration tags -->
* `latest, 2.0.58`
* `2, 2.0, 2.0.55, stable, stable-2.0.55`
* `latest, 2.0.65`
* `2, 2.0, 2.0.60, stable, stable-2.0.60`
<!-- end autogeneration tags -->
## Tag descriptions
@@ -175,6 +175,8 @@ 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.
@@ -494,6 +496,19 @@ 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,363 +1,19 @@
{
"0.12.35": {
"sha256": "ab9cf01a56dde3073aaaa5152c628bbf9a5bb85638b87dc3d7fdb77fb169aedd",
"2.0.60": {
"sha256": "69b5be1a867fd99524f9914dfee900a1ac386cf4e74c4a63768c05dc4d2b2b0b",
"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-2.0.55",
"stable",
"stable-2.0.60",
"2",
"2.0",
"2.0.55",
"stable"
"2.0.60"
]
},
"2.0.57": {
"sha256": "22b232afb77067c68a3afe087be6a0ee760479262598a12a709e1b03ea9508a6",
"tags": [
"2.0.57"
]
},
"2.0.58": {
"sha256": "be82e1aeba4169420e1b00c12a3e00ec2309a41327f9d6c335feec27bbc885e6",
"2.0.65": {
"sha256": "6b80f0fbeb43753ed7654695ffa427f442496068f2f72129de76cea246a0c63e",
"tags": [
"latest",
"2.0.58"
"2.0.65"
]
}
}

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 --no-install-recommends \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils procps gettext-base file --no-install-recommends \
&& if [[ "$(uname -m)" == "aarch64" ]]; then \
echo "installing ARM compatability layer" \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install unzip --no-install-recommends \
@@ -54,8 +54,8 @@ RUN apt-get -q update \
fi \
&& rm -rf /var/lib/apt/lists/*
RUN addgroup --system --gid "$PGID" "$GROUP" \
&& adduser --system --uid "$PUID" --gid "$PGID" --no-create-home --disabled-password --shell /bin/sh "$USER"
RUN groupadd --system --gid "$PGID" "$GROUP" \
&& useradd --system --uid "$PUID" --gid "$PGID" --no-create-home --shell /bin/sh "$USER"
# version checksum of the archive to download
ARG VERSION

View File

@@ -6,8 +6,8 @@ services:
args:
# Check buildinfo.json for supported versions and SHAs
# https://github.com/factoriotools/factorio-docker/blob/master/buildinfo.json
- VERSION=2.0.55
- SHA256=ef12a54d1556ae1f84ff99edc23706d13b7ad41f1c02d74ca1dfadf9448fcbae
- VERSION=2.0.60
- SHA256=69b5be1a867fd99524f9914dfee900a1ac386cf4e74c4a63768c05dc4d2b2b0b
ports:
- "34197:34197/udp"
- "27015:27015/tcp"

View File

@@ -53,10 +53,9 @@ fi
# In rootless mode, we don't need to handle user switching or chown
# The container runs as the specified user from the start
EXEC=""
if [[ -f /bin/box64 ]]; then
# Use emulator for ARM hosts
EXEC="/bin/box64"
fi
# Setup ARM64 emulation support
# shellcheck disable=SC1091
source "${INSTALLED_DIRECTORY}/setup-exec.sh"
# Update config path
sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini

View File

@@ -41,10 +41,10 @@ if [[ $NRTMPSAVES -gt 0 ]]; then
fi
if [[ ${UPDATE_MODS_ON_START:-} == "true" ]]; then
${INSTALLED_DIRECTORY}/docker-update-mods.sh
"${INSTALLED_DIRECTORY}"/docker-update-mods.sh
fi
${INSTALLED_DIRECTORY}/docker-dlc.sh
"${INSTALLED_DIRECTORY}"/docker-dlc.sh
EXEC=""
if [[ $(id -u) == 0 ]]; then
@@ -56,11 +56,10 @@ if [[ $(id -u) == 0 ]]; then
# Drop to the factorio user
EXEC="runuser -u factorio -g factorio --"
fi
if [[ -f /bin/box64 ]]; then
# Use an emulator to run on ARM hosts
# this only gets installed when the target docker platform is linux/arm64
EXEC="$EXEC /bin/box64"
fi
# Setup ARM64 emulation support
# shellcheck disable=SC1091
source "${INSTALLED_DIRECTORY}/setup-exec.sh"
sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini

View File

@@ -1,5 +1,6 @@
#!/bin/bash
set -eoux pipefail
INSTALLED_DIRECTORY=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
if [[ -z ${1:-} ]]; then
echo "No argument supplied"
@@ -31,7 +32,12 @@ if [[ ! -f $CONFIG/map-settings.json ]]; then
cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json"
fi
exec /opt/factorio/bin/x64/factorio \
# Setup ARM64 emulation support
EXEC=""
# shellcheck disable=SC1091
source "${INSTALLED_DIRECTORY}/setup-exec.sh"
exec $EXEC /opt/factorio/bin/x64/factorio \
--port "$PORT" \
--start-server-load-scenario "$SERVER_SCENARIO" \
--preset "$PRESET" \

View File

@@ -1,5 +1,6 @@
#!/bin/bash
set -eoux pipefail
INSTALLED_DIRECTORY=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
if [[ -z ${1:-} ]]; then
echo "No argument supplied"
@@ -23,5 +24,10 @@ if [[ ! -f $CONFIG/map-settings.json ]]; then
cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json"
fi
exec /opt/factorio/bin/x64/factorio \
# Setup ARM64 emulation support
EXEC=""
# shellcheck disable=SC1091
source "${INSTALLED_DIRECTORY}/setup-exec.sh"
exec $EXEC /opt/factorio/bin/x64/factorio \
--scenario2map "$SERVER_SCENARIO"

View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Setup EXEC variable for running Factorio with ARM64 emulation support
# This script handles ARM64 emulation and can be combined with user switching as needed
# If EXEC is not already set, initialize it
if [[ -z "${EXEC:-}" ]]; then
EXEC=""
fi
if [[ -f /bin/box64 ]]; then
# Use an emulator to run on ARM hosts
# this only gets installed when the target docker platform is linux/arm64
EXEC="$EXEC /bin/box64"
fi
export EXEC

View File

@@ -23,24 +23,33 @@ print_failure()
echo "$1"
}
# 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
# 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
check_game_version() {
local game_version="$1"
local mod_version="$2"
local mod_required_version="$1" # The minimum Factorio version required by the mod
local current_game_version="$2" # The current Factorio version
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)
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)
if [[ "$game_major" -ne "$mod_major" ]]; then
# 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
echo 0
return
fi
if [[ "$mod_minor" -ge "$game_minor" ]]; then
# Major versions are equal, check minor versions
# Game minor version must be >= mod's required minor version
if [[ "$game_minor" -ge "$mod_minor" ]]; then
echo 1
else
echo 0
@@ -79,7 +88,7 @@ check_dependency_version()
fi
;;
">")
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | head -n1)" == "$required_version" && "$required_version" != "$FACTORIO_VERSION" ]]; then
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | head -n1)" == "$required_version" && "$required_version" != "$mod_version" ]]; then
echo 1
else
echo 0
@@ -93,7 +102,7 @@ check_dependency_version()
fi
;;
"<")
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | tail -n1)" == "$required_version" && "$required_version" != "$FACTORIO_VERSION" ]]; then
if [[ "$(printf '%s\n%s\n' "$required_version" "$mod_version" | sort -V | tail -n1)" == "$required_version" && "$required_version" != "$mod_version" ]]; then
echo 1
else
echo 0
@@ -116,11 +125,15 @@ 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
@@ -214,9 +227,12 @@ 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
if [[ $mod != base ]]; then
# Skip base mod and DLC built-in mods
if [[ $mod != base ]] && [[ $mod != elevated-rails ]] && [[ $mod != quality ]] && [[ $mod != space-age ]]; then
update_mod "$mod" || true
fi
done

View File

@@ -1,3 +1,4 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
@@ -107,7 +108,8 @@ char* read_password(const char* conf_dir) {
fseek(fptr, 0, SEEK_SET); /* same as rewind(f); */
char *password = malloc(fsize + 1);
fread(password, fsize, 1, fptr);
size_t bytes_read = fread(password, fsize, 1, fptr);
(void)bytes_read; // Suppress unused warning
fclose(fptr);
password[fsize] = 0;

View File

@@ -59,41 +59,40 @@ latestCurrentVersionShort=$latestCurrentVersionMajor.$latestCurrentVersionMinor
echo "stableOnlineVersionShort=${stableOnlineVersionShort} experimentalOnlineVersionShort=${experimentalOnlineVersionShort}"
echo "stableCurrentVersionShort=${stableCurrentVersionShort} latestCurrentVersionShort=${latestCurrentVersionShort}"
# Create new buildinfo.json with only current versions
tmpfile=$(mktemp)
# 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"
# Start with empty JSON object
echo '{}' > "$tmpfile"
# 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
# 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
else
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"
# Update tag by latest
cp buildinfo.json "$tmpfile"
if [[ $experimental_online_version != "$stable_online_version" ]]; then
# 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"
# Then add experimental
if [[ $stableOnlineVersionShort == "$experimentalOnlineVersionShort" ]]; then
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
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
else
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
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
fi
fi
rm -f -- "$tmpfile"
# Generate README tags with logical sorting and de-duplication
# First, collect all unique tags with their versions
declare -A tag_versions
# Use regular arrays for bash compatibility
declare 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