Your First Godot Export VDF LF Line Ending Proof for Linux steamcmd in One Evening - 2026
Your Godot export succeeded on Windows. Steamworks partner site saved the depot layout. GitHub Actions on ubuntu-latest runs steamcmd and dies with ERROR! App build file before upload even queues. You open the same app_build_demo.vdf in Notepad—it looks fine.
December 2026 micro-studios ship Godot 4.5 demos through Linux CI while founders still edit VDF snippets on Windows laptops. CRLF line endings are invisible in many editors but fatal to steamcmd parse on Linux. 12 setlive preflight checks cover partner identity and branch discipline; SteamPipe wall-clock trend teaches queued ≠ failed—this Tutorials & Beginner-First guide closes the VDF LF gap in one evening: prove LF with file + hexdump, pin .gitattributes, run a dry-run log, file vdf_line_ending_receipt_v1.json, and set vdf_lf_ok on BUILD_RECEIPT before fest promotion.
Non-repetition note: depot and branch discipline owns beta branch naming; setlive checklist owns twelve partner checks; Help #31 forward owns traceback-first CRLF parse fix; VDF LF preflight (Guide) owns the ninety-second gate; Lesson 269 forward owns the BUILD_RECEIPT milestone. This URL is the beginner evening path for teams who never proved LF before Linux steamcmd.
Why this matters now (December 2026)
- Linux CI is default — GitHub Actions
ubuntu-latest+steamcmdis cheaper than maintaining a Windows runner for upload-only jobs. - Windows Notepad lies — CRLF looks normal;
steamcmdon Linux rejects the file before queue telemetry even starts. - Holiday ship crunch — Teams paste VDF hotfixes on laptops Friday; CI breaks Saturday while founders are offline.
- False queue confusion — Parse failures get mis-tagged as "Steam is down" when wall-clock trend actually describes validation delay after a successful parse.
- Receipt rows multiplied — Thursday row review needs
vdf_lf_okbesideupload_duration_min—this evening wires it.
Direct answer: Run file and hexdump -C on app_build_*.vdf, enforce LF via .gitattributes or dos2unix, capture a Linux steamcmd dry-run log without parse errors, file vdf_line_ending_receipt_v1.json with V1–V6 pass, set vdf_lf_ok: true on BUILD_RECEIPT before promotion.
Who this is for and what you get
| Audience | You will be able to… |
|---|---|
| Beginner with first Godot Steam export | Run file + dos2unix on one VDF |
| Solo engineer | Stop Windows edits from breaking Linux CI |
| Producer | Require LF receipt before upload week |
| Working dev | Wire .gitattributes, CI proof step, jq gate |
Time: ~90–120 minutes first evening (includes one dry-run); 10 minutes per VDF edit after template exists.
Prerequisites: Godot 4.5 export preset producing app_build_*.vdf; steamcmd available on Linux CI or WSL; Steam partner credentials in CI secrets (or local dry-run with guardrails).
What you will have after one evening
.gitattributesrow for*.vdf→text eol=lf- CI step logging
file+hexdumphead on VDF paths vdf_line_ending_receipt_v1.jsonpassing V1–V6- BUILD_RECEIPT column
vdf_lf_ok release-evidence/steam/vdf-lf/README.mdwith proof commandsbuild_app_dry_run_<date>.logwithout parse error lines
Beginner checkbox (before YAML depth)
| # | Bad habit | Fix tonight |
|---|---|---|
| 1 | Edit VDF in Notepad on Windows | Use VS Code with LF visible or run dos2unix in CI |
| 2 | Commit CRLF because Git default on Windows | Add .gitattributes (V3) |
| 3 | Assume Steamworks UI validates LF | UI checks fields—not line endings on disk |
| 4 | Skip dry-run on Linux | V4 log is the proof |
| 5 | Promote without receipt | File V6 before merge |
Success check: file app_build_demo.vdf reports ASCII text without CRLF on the Linux runner that runs steamcmd.
Evening overview (four blocks)
| Block | Minutes | Output |
|---|---|---|
| A — Locate VDF | 15–20 | Path + Godot export preset note |
| B — Prove LF | 25–35 | file + hexdump log |
| C — Pin repo policy | 20–30 | .gitattributes PR |
| D — Dry-run + receipt | 30–45 | Log + JSON + BUILD_RECEIPT |
Run dry-run on Linux (CI runner or WSL)—Windows steamcmd may accept CRLF and hide the bug.
Gates V1–V6 (promotion discipline)
Align with VDF LF preflight (Guide). Blog = evening path; guide = ninety-second gate.
| Gate | Check | Fail when |
|---|---|---|
| V1 | app_build_*.vdf exists at documented path |
Missing after Godot export step |
| V2 | file shows LF-only (no CRLF) |
CRLF line terminators in output |
| V3 | .gitattributes contains *.vdf text eol=lf |
Missing or overridden locally |
| V4 | Linux steamcmd dry-run log |
ERROR! App build file parse line |
| V5 | Fail-closed promotion jq | Merge without vdf_lf_ok |
| V6 | vdf_line_ending_receipt_v1.json filed |
Missing on promotion |
Promotion rule: steam_upload_promotion_allowed is true only when V1–V6 pass and Wednesday smoke S4–S6 are GREEN on the same build_label.
Block A — Locate Godot export VDF paths
Godot Steam export templates vary by team layout. Common pattern after export:
build/steam/app_build_<appid>.vdf
build/steam/depot_build_<depotid>.vdf
Pin one canonical path in release-evidence/steam/vdf-lf/README.md:
# VDF LF proof (Godot 4.5)
- App build VDF: `build/steam/app_build_1234560.vdf`
- Generated by: Godot export preset `Steam/Linux CI`
- Linux steamcmd job: `.github/workflows/godot-steam-upload.yml`
Cross-read depot and branch discipline so depot IDs in VDF match Steamworks—not a line-ending issue, but parse errors mask ID typos.
Red flag: Multiple app_build_*.vdf copies (Desktop vs repo)—receipt must reference the CI path only (V1).
Block B — Prove LF with file and hexdump
On the same OS image as CI (ubuntu-latest):
VDF="build/steam/app_build_1234560.vdf"
file "$VDF"
hexdump -C "$VDF" | head -n 3
Pass (V2):
app_build_1234560.vdf: ASCII text
Fail:
app_build_1234560.vdf: ASCII text, with CRLF line terminators
CRLF smoking gun in hexdump: bytes 0d 0a at line ends instead of 0a alone.
Fix CRLF once (manual)
dos2unix "$VDF"
file "$VDF"
Windows PowerShell (local pre-commit):
# Requires WSL or Git Bash
wsl dos2unix build/steam/app_build_1234560.vdf
Re-run file until CRLF disappears before filing receipt.
CI proof step (copy into workflow)
- name: VDF LF proof (V2)
shell: bash
run: |
VDF="build/steam/app_build_1234560.vdf"
test -f "$VDF"
file "$VDF" | tee vdf_file_proof.txt
if file "$VDF" | grep -q CRLF; then
echo "V2 fail: CRLF detected"
exit 1
fi
hexdump -C "$VDF" | head -n 5 | tee vdf_hex_head.txt
Upload vdf_file_proof.txt as artifact for producer audit.
Block C — .gitattributes policy (V3)
Add to repo root .gitattributes:
# Steam VDF must be LF for Linux steamcmd (Godot 4.5 CI)
*.vdf text eol=lf
Renormalize after adding:
git add --renormalize .
git status
Commit message template: chore(steam): enforce LF on VDF for Linux steamcmd.
Pair 15-free GitHub Actions CI recipes for Godot + Steam workflow graphs—this post owns line-ending material, not full export authoring.
Editor hygiene table
| Editor | Setting |
|---|---|
| VS Code | Status bar LF; "files.eol": "\n" for *.vdf |
| JetBrains | File Properties → Line separator → LF |
| Notepad++ | Edit → EOL Conversion → Unix (LF) |
| Notepad (Win11) | Avoid for VDF—no LF default |
Block D — Linux steamcmd dry-run (V4)
Run before setlive week—cousin to 12 setlive checks check 8:
steamcmd +login "$STEAM_PARTNER_USER" "$STEAM_PARTNER_PASS" \
+run_app_build build/steam/app_build_1234560.vdf \
+quit 2>&1 | tee release-evidence/steam/vdf-lf/build_app_dry_run_2026-12-08.log
V4 pass: log contains upload/queue lines; no ERROR! App build file.
V4 fail: parse error—return to V2/V3; do not blame Steam queue (wall-clock trend routing table).
Auth vs parse vs queue (quick route)
| Log signal | Lane |
|---|---|
Invalid Password |
Partner login / SteamGuard |
ERROR! App build file |
This tutorial (LF/parse) |
OK then long idle |
Queue / validation (wall-clock trend) |
DepotOwnedByOtherAccount |
GameMaker partner sanity cousin |
vdf_line_ending_receipt_v1.json
Pin under release-evidence/steam/vdf-lf/VDF_LINE_ENDING_RECEIPT.json:
{
"schema": "vdf_line_ending_receipt_v1",
"build_label": "december-fest-2026-rc2",
"engine": "godot_4.5",
"app_build_vdf_path": "build/steam/app_build_1234560.vdf",
"depot_build_vdf_paths": [
"build/steam/depot_build_1234561.vdf"
],
"file_proof_command": "file build/steam/app_build_1234560.vdf",
"file_proof_output": "ASCII text",
"hex_head_artifact": "release-evidence/steam/vdf-lf/vdf_hex_head.txt",
"gitattributes_path": ".gitattributes",
"gitattributes_vdf_rule": "*.vdf text eol=lf",
"dry_run_log": "release-evidence/steam/vdf-lf/build_app_dry_run_2026-12-08.log",
"workflow_path": ".github/workflows/godot-steam-upload.yml",
"cousin_receipts": {
"depot_owner": "release-evidence/steam/depot_owner_receipt_v1.json",
"setlive_dry_run_forward": "release-evidence/steam/gm_steam_setlive_dry_run_receipt_v1.json"
},
"gates": {
"V1_vdf_exists": "pass",
"V2_lf_only": "pass",
"V3_gitattributes": "pass",
"V4_dry_run_parse": "pass",
"V5_fail_closed": "pass",
"V6_receipt": "pass"
},
"vdf_lf_ok": true,
"steam_upload_promotion_allowed": true
}
BUILD_RECEIPT columns
| Column | Example |
|---|---|
vdf_lf_ok |
true |
vdf_line_ending_receipt_path |
release-evidence/steam/vdf-lf/VDF_LINE_ENDING_RECEIPT.json |
app_build_vdf_sha256 |
optional fingerprint for row diff |
Lesson 269 forward wires promotion gates; Resource #29 refresh adds dos2unix / file tool rows beside SteamPipe observability list.
V5 — Fail-closed jq (CI tail)
jq -e '.vdf_lf_ok == true' release-evidence/steam/vdf-lf/VDF_LINE_ENDING_RECEIPT.json
Block merge to fest branch when receipt missing or false—pair Thursday row review column vdf_lf_ok.
Developer path — upload week order
| Step | Artifact | Owner |
|---|---|---|
| 1 | Godot export preset produces VDF | Engineering |
| 2 | V2 file proof on Linux |
CI |
| 3 | V3 .gitattributes merged |
Engineering |
| 4 | V4 dry-run log | CI / release |
| 5 | V6 receipt + BUILD_RECEIPT | Release owner |
| 6 | Wednesday smoke | Producer |
| 7 | Thursday row diff | Release owner |
Do not run setlive when V4 parse failed—fix LF first (12 setlive checks).
Relationship to checklist vs guide vs help
| Artifact | Role |
|---|---|
| This tutorial | One-evening LF proof + receipt |
| 12 setlive checks | Partner + branch checklist |
| VDF LF preflight (Guide) | Ninety-second V1–V6 |
| Help #31 forward | CRLF parse failure fix lane |
| Wall-clock trend | Queue vs auth vs parse routing |
| Blog #5 forward | Concurrency patterns list — Unity cousin, not Godot VDF |
Common mistakes
- Testing only on Windows —
steamcmdon Windows may parse CRLF; Linux CI fails. - Fixing VDF in CI without
.gitattributes— next Windows edit reintroduces CRLF. - Confusing parse fail with queue delay — no upload telemetry until parse succeeds.
- Multiple VDF copies — receipt points at wrong path (V1 fail).
- Skipping hexdump —
filemissing on minimal CI images; installfilepackage. - Promoting with
vdf_lf_okfalse — V5 fail-closed exists for a reason. - Editing depot IDs and line endings in one rushed commit — separate commits for easier row review.
Troubleshooting
| Symptom | Lane |
|---|---|
ERROR! App build file on Linux only |
V2 CRLF → dos2unix + V3 |
file: command not found |
sudo apt-get install -y file on runner |
| Dry-run auth fail | Partner / SteamGuard—not LF |
| Parse OK, queue idle hours | Wall-clock trend |
| Godot re-exports CRLF | Fix export script template or post-export dos2unix step |
.gitattributes ignored |
Run git add --renormalize |
Worked example — Friday laptop edit to Monday CI
Scenario: Founder edits app_build_1234560.vdf in Notepad Friday 5 p.m. Monday 9 a.m. CI fails parse.
| Step | What you do | Expected result |
|---|---|---|
| 1 | Open CI log | ERROR! App build file |
| 2 | Re-run job with V2 step | CRLF line terminators |
| 3 | dos2unix + commit |
file shows ASCII text only |
| 4 | Merge .gitattributes |
V3 pass on next PR |
| 5 | Dry-run on Linux | V4 log without parse error |
| 6 | File receipt | vdf_lf_ok: true |
| 7 | Thursday row review | Column matches receipt |
Lesson learned: Steamworks UI never saw the line endings—only Linux steamcmd did.
Discord stand-up one-liner (copy/paste)
VDF CRLF killed Linux steamcmd—
dos2unix+.gitattributesmerged, dry-run clean,vdf_lf_ok=true, upload queued not failed.
Proof table (producer audit)
| Row | Evidence | Pass |
|---|---|---|
| VDF path | V1 README | Matches CI |
file output |
V2 artifact | No CRLF |
.gitattributes |
V3 diff | *.vdf text eol=lf |
| Dry-run log | V4 file | No parse error |
| jq gate | V5 CI | Exit 0 |
| Receipt | V6 path | vdf_lf_ok: true |
Seven scenarios (A–G)
| ID | Situation | Correct response |
|---|---|---|
| A | First Linux CI upload | Implement V2–V4 before real upload |
| B | Windows-only team until now | Add WSL proof step tonight |
| C | Parse fail + "Steam down" rumor | Route to V2—not queue trend |
| D | Parse OK, long queue | Wall-clock trend |
| E | Contractor emailed VDF | dos2unix before commit—never trust attachment |
| F | Receipt true, upload auth fail | Partner login lane—not LF |
| G | Skipped receipt, promoted | Roll back; file V6 before retry |
Folder layout
release-evidence/steam/vdf-lf/
README.md
VDF_LINE_ENDING_RECEIPT.json
build_app_dry_run_2026-12-08.log
vdf_file_proof.txt
vdf_hex_head.txt
.gitattributes # *.vdf text eol=lf
build/steam/
app_build_1234560.vdf
Godot export preset notes (4.5)
- Document which preset writes VDF—custom export plugins may emit CRLF on Windows.
- If preset cannot force LF, add post-export CI step:
find build/steam -name '*.vdf' -exec dos2unix {} +. - Keep one
build_labelin VDF comments aligned with visible build label policy.
Pair Godot WASM ceiling blog for HTML5 lane—orthogonal to Steam VDF LF.
Tools and sibling reads
- 18-free Steamworks resources
- 15-free GitHub Actions CI recipes
- BUILD_RECEIPT beginner pipeline
- Wednesday demo smoke
- February 2027 prep calendar — lock LF policy before holiday freeze
- Official: Steamworks depot documentation, Godot export docs
Key takeaways
- Linux
steamcmdrequires LF VDF—Windows editors hide CRLF until CI fails. - One evening wires
fileproof,.gitattributes, dry-run log, receipt, and BUILD_RECEIPT column. ERROR! App build fileis parse lane—not queue delay (wall-clock trend)..gitattributesprevents the next laptop edit from re-breaking CI.- Fail-closed
vdf_lf_okbefore fest branch promotion. - 12 setlive checks check 6 pairs Godot/Linux CI—run this tutorial first.
- Guide preflight holds ninety-second V1–V6; this post holds beginner evening blocks.
- Wednesday smoke still required—LF receipt ≠ binary proof.
- Help #31 forward owns fix steps when you are already RED in CI.
- Forward blog #2 Audacity loudness when Whisper batch is next in cluster.
FAQ
Does Steamworks validate line endings?
No—the partner UI validates app/depot fields, not on-disk newline bytes.
Can we convert CRLF in CI only without .gitattributes?
Temporary fix—V3 prevents recurrence; without it, the next Windows edit breaks CI again.
Unity or GameMaker VDF?
Same LF rule on Linux steamcmd; Godot export paths differ—receipt schema still applies.
What if we use a Windows self-hosted runner?
You may hide CRLF bugs—keep V2 on ubuntu-latest even if upload runs elsewhere.
How does this relate to Lesson 269?
Lesson 269 promotes vdf_line_ending_receipt into BUILD_RECEIPT gates—run this evening tutorial first.
Is parse failure the same as DepotOwnedByOtherAccount?
No—partner/depot ownership is setlive checklist / GameMaker partner lane.
Conclusion
December Godot Steam uploads fail quietly on Windows and loudly on Linux when VDF stays CRLF. One evening: prove LF, pin .gitattributes, capture dry-run, file the receipt, set vdf_lf_ok—then interpret queue telemetry honestly.
Next reads: VDF LF preflight (Guide), 12 setlive checks, SteamPipe wall-clock trend, Lesson 269 forward, BUILD_RECEIPT beginner.
Pin file + dos2unix beside your Godot export preset before holiday upload week—not after the first ERROR! App build file in CI.