Files
actions/factorio-mod-push/action.yml
Sebastian Seedorf de4c48d0bf Add factorio-mod-push action
Composite action that sets version in info.json, creates README.md
if missing, zips with {name}_{version}.zip structure, and uploads
to mods.factorio.com via init_upload + finish_upload API.

Fails on any API error (auth, unknown mod, invalid release).
2026-05-19 18:17:08 +02:00

147 lines
4.4 KiB
YAML

name: Factorio Mod Push
description: Set version, zip, and upload a Factorio mod to mods.factorio.com
inputs:
mod-folder:
description: Path to the Factorio mod root directory (containing info.json)
required: false
default: "."
version:
description: Semver version string to set in info.json, e.g. 1.0.1
required: true
factorio-token:
description: Factorio API key with ModPortal: Upload Mods permission
required: true
outputs:
zip-path:
description: Absolute path to the generated zip file
value: ${{ steps.package.outputs.zip-path }}
runs:
using: composite
steps:
- name: Validate inputs
shell: bash
run: |
MOD_FOLDER="${{ inputs.mod-folder }}"
VERSION="${{ inputs.version }}"
if [ ! -f "$MOD_FOLDER/info.json" ]; then
echo "ERROR: info.json not found in $MOD_FOLDER"
exit 1
fi
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "ERROR: version '$VERSION' does not match semver x.y.z"
exit 1
fi
- name: Set version in info.json
shell: bash
run: |
MOD_FOLDER="${{ inputs.mod-folder }}"
VERSION="${{ inputs.version }}"
sed -i 's/"version": *"[^"]*"/"version": "'"$VERSION"'"/' "$MOD_FOLDER/info.json"
- name: Create README.md if missing
shell: bash
run: |
MOD_FOLDER="${{ inputs.mod-folder }}"
if [ ! -f "$MOD_FOLDER/README.md" ]; then
TITLE=$(sed -n 's/.*"title": *"\([^"]*\)".*/\1/p' "$MOD_FOLDER/info.json")
DESC=$(sed -n 's/.*"description": *"\([^"]*\)".*/\1/p' "$MOD_FOLDER/info.json")
cat > "$MOD_FOLDER/README.md" <<- EOF
# $TITLE
$DESC
EOF
echo "Created $MOD_FOLDER/README.md"
fi
- name: Read mod name from info.json
id: modinfo
shell: bash
run: |
MOD_FOLDER="${{ inputs.mod-folder }}"
NAME=$(sed -n 's/.*"name": *"\([^"]*\)".*/\1/p' "$MOD_FOLDER/info.json")
echo "name=$NAME" >> $GITHUB_OUTPUT
- name: Package mod zip
id: package
shell: bash
run: |
MOD_FOLDER="${{ inputs.mod-folder }}"
NAME=${{ steps.modinfo.outputs.name }}
VERSION="${{ inputs.version }}"
ZIP_NAME="${NAME}_${VERSION}.zip"
ZIP_PATH="${GITHUB_WORKSPACE}/${ZIP_NAME}"
TMPDIR=$(mktemp -d)
cp -r "$MOD_FOLDER/." "$TMPDIR/$NAME/"
find "$TMPDIR/$NAME" -type d -name node_modules -exec rm -rf {} + 2>/dev/null || true
find "$TMPDIR/$NAME" -type d -name .git -exec rm -rf {} + 2>/dev/null || true
cd "$TMPDIR"
zip -r "$ZIP_PATH" "$NAME/"
rm -rf "$TMPDIR"
echo "zip-path=$ZIP_PATH" >> $GITHUB_OUTPUT
echo "Created $ZIP_PATH"
- name: Init upload to Factorio mod portal
id: init
shell: bash
run: |
NAME=${{ steps.modinfo.outputs.name }}
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST "https://mods.factorio.com/api/v2/mods/releases/init_upload" \
-H "Authorization: Bearer ${{ inputs.factorio-token }}" \
-F "mod=$NAME")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" -ne 200 ]; then
echo "ERROR: init_upload failed (HTTP $HTTP_CODE)"
echo "Response: $BODY"
exit 1
fi
UPLOAD_URL=$(echo "$BODY" | sed -n 's/.*"upload_url": *"\([^"]*\)".*/\1/p')
if [ -z "$UPLOAD_URL" ]; then
echo "ERROR: init_upload failed - no upload_url in response"
echo "Response: $BODY"
exit 1
fi
echo "upload_url=$UPLOAD_URL" >> $GITHUB_OUTPUT
- name: Upload zip to Factorio mod portal
shell: bash
run: |
ZIP_PATH=${{ steps.package.outputs.zip-path }}
UPLOAD_URL=${{ steps.init.outputs.upload_url }}
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST "$UPLOAD_URL" \
-F "file=@$ZIP_PATH")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" -ne 200 ]; then
echo "ERROR: upload failed (HTTP $HTTP_CODE)"
echo "Response: $BODY"
exit 1
fi
if ! echo "$BODY" | grep -q '"success": *true'; then
echo "ERROR: upload reported failure"
echo "Response: $BODY"
exit 1
fi
echo "Upload successful: ${{ steps.modinfo.outputs.name }} ${{ inputs.version }}"