Skip to content

Commit 894d672

Browse files
committed
add new post
1 parent 6b56b5f commit 894d672

11 files changed

+558
-0
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
title: "The Case of the Cursed Release"
3+
date: "2025-08-07"
4+
redirect_from : /the-case-of-the-cursed-release
5+
coverImage: \assets\images\2025\cursedrelease.png
6+
categories:
7+
- "programming"
8+
tags:
9+
- "PowerShell"
10+
- "DevOps"
11+
- "AzurePipelines"
12+
excerpt: "Everything looked fine—until our Linux container refused to touch our build artifacts. Turns out PowerShell v5 zipped up our files with backslashes in their *names*. This post dives into a cursed bug with a surprisingly simple fix."
13+
fileName: '2025-08-07-the-case-of-the-cursed-release.md'
14+
---
15+
16+
Recently, I ran into one of those DevOps nightmares that only shows up when all the stars are aligned just wrong. The kind where you stare at the screen and say, "This should work," and then it **doesn’t**. And worse **PowerShell lies to you**. I thought PowerShell and I had something special...how could it treat me of all people like that.
17+
18+
This is the story of how an innocent ZIP file became haunted, how PowerShell 5 doomed a build, and how I barely escaped with my sanity (and deployment pipeline) intact.
19+
20+
![A cursed release is born](<../assets/images/2025/cursedrelease.png>)
21+
22+
---
23+
24+
## Observed behavior: things looked... fine?
25+
26+
We use YAML Pipelines in ADO which not surprisingly I find that I really enjoy. This is like the evolution of using System Center (or Opalis) Orchestrator to achieve complex automation.
27+
28+
And if you are surprised that I enjoy this, then you must be new here!
29+
30+
**Well**, Recently I needed to add a new folder into a zip we produce in our build process. I told management that this would be trivially easy, barely an inconvenience, only three days at max.
31+
32+
Previous to this point, all of our artifacts in our build would be single binary files, like .exe and .dll files and things. We'd take all the built bits, put them into our OUT dir and make a zip file out of the whole mess. Then our Release would grab the output and push it out to our regions and bam, it all just worked.
33+
34+
But we never added a folder to our build before.
35+
36+
How hard could it be?
37+
38+
## Step 1: This should be easy, barely an inconvenience
39+
40+
I added a simple step to my `PowerShell@2` task, copying recursively an entire directory and preserving its output structure
41+
42+
Then I ran my release script, which was super simple, just launching a task on one of my Linux agents to run the task of grabbing my content and pushing it out to the expected location on this storage account.
43+
44+
So I ran my release process and saw that it looks like we see the files, but uploading them to a storage account failed with `Cannot find the specified file`
45+
46+
## What do you mean you can't find the files? But it's right there!!
47+
48+
![Somehow enumerating files but we can't find them](<../assets/images/2025/theCaseSafe1.png>)
49+
50+
I made sure that my output appeared under 'Artifacts\Published'.
51+
52+
53+
![Somehow enumerating files but we can't find them](<../assets/images/2025/myCoolReleasezip.png>)
54+
55+
Ok, let me open the zip file too just to be safe.
56+
![Somehow enumerating files but we can't find them](<../assets/images/2025/sanityCheck.png>)
57+
58+
## Step 2: Add logs like a panicked developer
59+
60+
I cracked open the logs and saw the files were *definitely* there. PowerShell said so!
61+
And yet... `Set-AzStorage` would just give me a shrug emoji and die.
62+
63+
64+
At this point I began adding all sorts of logging, `Get-ChildItem`, `ls`, `dir` and even `[System.IO.FileSearcher]` dumps, even ASCII art.
65+
66+
The files seemed to be visible, **but** any operation trying to *touch* them would explode:
67+
68+
- `Get-Item`? Nope.
69+
- `Upload-Item`? Also broken.
70+
- Even using `-Include` or `-Filter`? Instant failure.
71+
72+
Check this out for proof! Even a `Get-ChildItem`, the holiest and most chosen of cmdlets would explode.
73+
74+
### *Look at this, I cannot even GCI in here*
75+
76+
![Somehow enumerating files but we can't find them](<../assets/images/2025/cantevenGCI.png>)
77+
78+
Have you ever seen that? `Get-ChildItem` throwing!?
79+
80+
## Step 3: this has *got* to be related to the host environment
81+
82+
At this point I began questioning a lot of things. My Career choices, political affiliation, even my preference for coffee over tea and bourbon over tequila.
83+
84+
What could this possibly be?
85+
86+
I decided to remote out to one of our Release env boxes, which is in ADO so its really just a container running some remote os to run a PowerShell script and do stuff.
87+
88+
I noticed these are `Linux` containers...so I used SSH. I found here that doing directory commands on the new files were really odd... like I couldn't `CD` into the `myNewFolder` folder at all.
89+
90+
But I *could* `cat` or `grep` on them using bash commands. However using PowerShell commands would fail. Now why would that be?
91+
92+
### Fine, I guess I will try reading
93+
94+
`>ls`
95+
96+
![](../assets/images/2025/fineIGuessIllRead.png)
97+
98+
Wait a minute...
99+
## The Slashes Were Wrong
100+
101+
The paths weren’t just inconsistent. They were... **interleaved**.
102+
103+
How the hell do you even end up with Slashes in this direction on a Linux machine?!
104+
105+
At this point I was just impressed with the degree of my mess up. I deleted the directory, pushed the zip file back over again and opened it in front of my own eyes.
106+
107+
![](../assets/images/2025/bustedZip.png)
108+
109+
>zip appears to use backslashes as path separators
110+
111+
## How did I manage to get a Zip file wrong?
112+
113+
Honestly I was kind of proud of myself. This was a whole new genre of spectacular disaster. I was charting new territory here.
114+
115+
I had succeeded in making files exist on a linux box was the full *Amuse-bouche* of possible characters in their names:
116+
* some with backslashes
117+
* some with forward slashes.
118+
* some with **both**.
119+
120+
I wouldn't have been surprised to find that some had a cream filling!
121+
122+
Here's the crux of the issue then, for some files, the slash wasn’t a *path separator* - it was part of the **literal file name**.
123+
124+
In Linux, a slash facing the wrong way doesn’t mean that the file is safely nestled in a folder like that guy inside of the Bantha in Star Wars, it means a file **named** `"Controllers\HomeController.cs"`.
125+
126+
Like, no folder at all. Just one cursed file.
127+
128+
129+
## Root cause: PowerShell v5 Zips Wrong™
130+
131+
The ZIP file had been created using PowerShell 5 in a Windows container. And here’s where the real goblin comes in:
132+
133+
> PowerShell v5’s `Compress-Archive` writes entries with **Windows-style backslashes**.
134+
> Not as path separators. As actual **filename characters**.
135+
136+
When you unzip that on Linux? Congratulations, you've just summoned a cursed archive where:
137+
138+
- `Controllers\HomeController.cs` is a file, not a folder + file
139+
- Nothing works because **no tools expect this**
140+
- PowerShell is confused, .NET is confused, *I'm* confused
141+
142+
[Yes](https://superuser.com/questions/1382839/zip-files-expand-with-backslashes-on-linux-no-subdirectories?utm_source=chatgpt.com), [this](https://stackoverflow.com/questions/27289115/system-io-compression-zipfile-net-4-5-output-zip-in-not-suitable-for-linux-mac?utm_source=chatgpt.com) [has](https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4975?utm_source=chatgpt.com) [been](https://mikebridge.github.io/post/windows-tar-gzip-for-linux/?utm_source=chatgpt.com) [a](https://qa.fmod.com/t/bug-fmod-engine-download-uses-backslashes-in-archives-which-do-not-expand-correctly-on-linux/21977?utm_source=chatgpt.com) [known](https://stackoverflow.com/questions/27289115/system-io-compression-zipfile-net-4-5-output-zip-in-not-suitable-for-linux-mac?utm_source=chatgpt.com) [bug](https://mikebridge.github.io/post/windows-tar-gzip-for-linux/?utm_source=chatgpt.com) [for](https://superuser.com/questions/1382839/zip-files-expand-with-backslashes-on-linux-no-subdirectories?utm_source=chatgpt.com) [*years*](https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4975?utm_source=chatgpt.com).
143+
144+
But PowerShell v5 is built into Windows and I do not think this particular thing is getting fixed because you never know, some company out there making critical life saving widgets might have built their entire production line workflow depending on this behavior of zip files.
145+
146+
I don't know, not my department.
147+
148+
## Wrapping it all up
149+
150+
- `Get-ChildItem` could see the file just fine, since PowerShell is slash agnostic
151+
- But `$_ .FullName` or `.ToString()` rewrote the slashes to forward ones
152+
- So `Upload` failed because it passed the wrong path
153+
- `-Include` and `-Filter` (which hit the provider layer) failed completely due to the implicit conversion of slash direction
154+
- Meanwhile piping into `Where-Object` *worked*, because it stayed object-based and so PowerShell could navigate around that
155+
156+
157+
This led to the haunting bug where **I could see a file, but not act on it**.
158+
Even `Copy-Item` and `Test-Path` failed.
159+
160+
And yes you are right, I could have fixed this by using `-LiteralPath` but I really wanted to fix the underlying issue. You deserve a cookie for thinking about this as a fix, treat yo' self.
161+
162+
## The Fix: PowerShell Core (v7+) to the Rescue
163+
164+
Once I figured this out (after many wasted hours and caffeine), the fix was simple:
165+
166+
**Run the ZIP creation step in PowerShell Core**.
167+
168+
PowerShell 7 uses `System.IO.Compression.ZipArchive` in a way that correctly encodes folders using **forward slashes** as expected. And not as part of the file name. Wild, right?
169+
170+
Suddenly:
171+
172+
- Files extracted into real folders
173+
- Uploads worked
174+
- Slashes stayed in their lane
175+
- I slept better
176+
177+
It was literally as simple as adding `pwsh: true` to my YAML pipeline file.
178+
179+
180+
## Why the eff is this even a difference in Windows and Linux?
181+
182+
This is really interesting. I'd heard a lawsuit was involved in some distant point in the path but maybe not, maybe it was all just as simple as 'This is how IBM did it'. In any case, [you can read for yourself here](https://www.os2museum.com/wp/why-does-windows-really-use-backslash-as-path-separator/)
183+
184+
## Final Thoughts: Don't Let This Happen to You
185+
186+
This was a perfect example of a cross-platform edge case that *seemed* fine—until it hit a different environment. PowerShell did what it was told… it just told no one what it actually did.
187+
188+
If you're:
189+
190+
- Using **Windows-based build agents**
191+
- Creating ZIPs in **PowerShell v5**
192+
- Deploying those ZIPs on **Linux containers**
193+
194+
> **Switch to PowerShell Core now.** Save yourself. Save your pipeline. Save your sanity.
195+
196+
This bug was never fixed in Windows PowerShell.
197+
The fix is easy—just **use the modern shell**.
198+
199+
But if you're like me, you can still use PowerShell ISE just to annoy your coworkers.

assets/images/2025/bustedZip.png

32.4 KB
Loading

assets/images/2025/cantevenGCI.png

32.4 KB
Loading
1.68 MB
Loading
17.3 KB
Loading
15 KB
Loading

assets/images/2025/sanityCheck.png

16.2 KB
Loading
112 KB
Loading

0 commit comments

Comments
 (0)