Skip to content

Run the scripts from Git Bash and MSYS2 on Windows - #141

Open
paolosalvatori wants to merge 9 commits into
mainfrom
smf-963-web-app-cosmosdb-nosql-api-sample-fails-on-windows-git-bash
Open

paolosalvatori wants to merge 9 commits into
mainfrom
smf-963-web-app-cosmosdb-nosql-api-sample-fails-on-windows-git-bash

Conversation

@paolosalvatori

@paolosalvatori paolosalvatori commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Running the web-app-cosmosdb-nosql-api sample from Git Bash on Windows fails at the Cosmos DB container step with Invalid partition key path found, and the script then carries on and deploys a web app whose container does not exist, as reported in SMF-963.
When Git Bash, or any other shell built on MSYS2, starts a Windows program such as the Azure CLI, it rewrites every argument that looks like an absolute POSIX path into a Windows path, so --partition-key-path /username reaches az as C:/Program Files/Git/username.
The same happens to the resource IDs the scripts pass to --scope or --ids and to the Front Door origin and probe paths, so the problem is not limited to one sample.
Our CI does not see it because it runs on Linux, where these arguments reach az unchanged.
Setting MSYS_NO_PATHCONV=1 turns the rewriting off, in Git Bash and in MSYS2 shells alike.
Git for Windows also cannot check out the repository at all, because of an empty file named ....

Fixes SMF-978

Changes

  • README.md gets a "Windows: Git Bash and MSYS2" section under "Local Testing": it asks Windows users of these shells to run export MSYS_NO_PATHCONV=1, set -o igncr and export SHELLOPTS before any script, explains why, and covers making the setting permanent, recognizing these shells with $MSYSTEM, and turning the conversion back on.
  • With the rewriting off, a Windows program no longer receives the Windows form of a local file, so every script that hands one to az, curl, jq, python or zip now passes it through a small native_path helper, which returns cygpath -m of the path in Git Bash and MSYS2 and the path unchanged everywhere else, so Linux, macOS and WSL behave exactly as before:
    • the temporary files read by curl and jq in the API Management, Front Door and URL shortener scripts;
    • the downloaded blob in function-app-managed-identity/python/scripts/test.sh;
    • the OpenAPI definition and policy in api-management-function-app/python/scripts/deploy.sh;
    • the Python checks the Event Hubs scripts run;
    • the function and dashboard archives the Event Hubs deployments build with zip, in the scripts, Bicep and Terraform variants (Git Bash ships no zip, and a zip.exe built for Windows needs the converted path too).
  • For the same reason a Windows curl no longer receives /dev/null as the NUL device, so the 23 curl calls that discard a response body, in 11 scripts, pass it -o "$(null_device)": NUL for a Windows curl in Git Bash and MSYS2, /dev/null everywhere else, MSYS2's own curl under /usr/bin included. Without it, a Windows curl exited with code 23 on any response with a body, and the MySQL deploy scripts never left their readiness loop.
  • Maven's mvn launcher is a shell script that hands its own paths to Java and relies on the conversion, so the three Service Bus deploy scripts run it as (unset MSYS_NO_PATHCONV; mvn ...); with the variable set, Java could not find Maven's launcher class.
  • The README's tips tell script authors about both helpers and about such launchers.
  • Windows programs such as the Azure CLI and jq end their lines with a carriage return, which Bash keeps in $(...) and read, so the README also asks for set -o igncr and export SHELLOPTS: the bash of Git Bash and MSYS2 then drops it. mapfile keeps the carriage return even then, so the App Configuration scripts, which read several fields at once with it, strip it with tr -d '\r'; without that, the sample failed on Windows with ResourceNotFound for a key vault whose ID carried the carriage return.
  • The empty file named ... in samples/web-app-cosmosdb-nosql-api/python/scripts/ is removed: Git for Windows refuses to check out a path whose last component ends with a period, so a clone on Windows stopped with error: invalid path and left no working tree.
    The file arrived by accident in 0f07d42 and nothing references it.
  • run-samples.sh registers samples/web-app-cosmosdb-nosql-api/python, the one sample variant with scripts that was missing from the list, next to its .NET counterpart, so CI also runs the variant this fix started from.

Tests

  • bash -n on every edited script, and shellcheck reports the same findings as on main for each of them.
  • The helper returns the path unchanged on Linux and the C:/... form when cygpath is on the PATH.
  • null_device returns /dev/null without cygpath, NUL with cygpath and a native curl first on the PATH, and /dev/null with cygpath and /usr/bin/curl.
  • git ls-tree finds no other path that Windows refuses: no name that ends with a period or a space, no reserved device name, no character such as :.
  • The Samples CI of this pull request runs the changed samples on Linux.
  • Windows is not covered by CI yet: Run all samples on a Windows runner from Git Bash #142 adds a manual Windows job built on this branch; its runs found the file named ... and the Maven launcher.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh

paolosalvatori and others added 3 commits October 23, 2026 15:51
run-samples.sh registered 26 of the 27 sample variants that ship scripts; the Python variant of web-app-cosmosdb-nosql-api was the one missing, so CI only ever ran its .NET counterpart.
It is registered next to the .NET entry with the same deploy, validate and call steps, and, like it, is not added to ARM64_SAMPLE_DIRS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
Git Bash and MSYS2 shells rewrite every argument that looks like an absolute POSIX path before starting a Windows program, so Azure values such as the Cosmos DB partition key path /username, resource IDs and the Front Door origin path reached az as Windows paths (SMF-963).
The README now asks Windows users of these shells to export MSYS_NO_PATHCONV=1, which turns the rewriting off, and explains why.
With the rewriting off, the local files the scripts hand to Windows programs need their Windows form: the temporary files read by curl and jq, the Python checks of the Event Hubs samples, the API Management definition and policy, the downloaded blob and the QR code.
A native_path helper in each of these scripts returns that form through cygpath when it exists and the path unchanged otherwise, so Linux, macOS and WSL behave exactly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
The Event Hubs and Event Hubs with Event Grid deployments write their function and dashboard archives to an absolute path under the scripts folder.
Git Bash ships no zip, and a zip.exe built for Windows cannot open a /c/... path once MSYS_NO_PATHCONV=1 is set, so the archive path now goes through native_path as well; a zip from MSYS2 accepts the C:/... form too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
@paolosalvatori
paolosalvatori requested a review from a team as a code owner October 23, 2026 14:17
Copilot AI balanced review requested due to automatic review settings October 23, 2026 14:17
Git for Windows refuses to check out a path whose last component ends with a period, so a clone of this repository on Windows stops with "error: invalid path 'samples/web-app-cosmosdb-nosql-api/python/scripts/...'" and leaves no working tree.
The file named "..." is empty, nothing references it, and it arrived by accident in 0f07d42.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The upstream MSYS2 setup and remaining native Windows curl null-output paths leave Windows compatibility issues unresolved.

2 open issues
What changed in this PR

This PR improves Windows shell compatibility for the Azure samples by documenting path-conversion settings and explicitly converting local file paths passed to external tools.

Changes:

  • Adds Git Bash and MSYS2 setup guidance.
  • Converts temporary files, API definitions, Python script paths and archive destinations with native_path.
  • Registers the Python Cosmos DB NoSQL sample in the shared sample runner.
File Description
samples/​url-shortener/​python/​scripts/​validate.sh Converts the downloaded QR file path.
samples/​function-app-managed-identity/​python/​scripts/​test.sh Converts the blob download destination.
samples/​function-app-front-door/​python/​scripts/​validate.sh Converts response temporary-file paths.
samples/​function-app-front-door/​python/​scripts/​call-front-door.sh Converts response temporary-file paths.
samples/​eventhubs/​python/​terraform/​deploy.sh Converts function and dashboard archive destinations.
samples/​eventhubs/​python/​scripts/​validate.sh Converts Python check paths.
samples/​eventhubs/​python/​scripts/​run-pipeline.sh Converts Python paths and the function archive destination.
samples/​eventhubs/​python/​scripts/​deploy.sh Converts function and dashboard archive destinations.
samples/​eventhubs/​python/​bicep/​deploy.sh Converts function and dashboard archive destinations.
samples/​eventhubs-eventgrid/​python/​terraform/​deploy.sh Converts the processor archive destination.
samples/​eventhubs-eventgrid/​python/​scripts/​validate.sh Converts the Python check path.
samples/​eventhubs-eventgrid/​python/​scripts/​run-pipeline.sh Converts Python reader paths.
samples/​eventhubs-eventgrid/​python/​scripts/​deploy.sh Converts the processor archive destination.
samples/​eventhubs-eventgrid/​python/​bicep/​deploy.sh Converts the processor archive destination.
samples/​api-management-function-app/​python/​scripts/​validate.sh Converts response temporary-file paths.
samples/​api-management-function-app/​python/​scripts/​deploy.sh Converts the API definition and policy directory path.
samples/​api-management-function-app/​python/​scripts/​call-api.sh Converts response temporary-file paths.
run-samples.sh Registers the Python Cosmos DB NoSQL sample.
README.md Documents Windows shell setup and path handling.

Review effort: Balanced

Comment thread README.md
Comment thread README.md Outdated
paolosalvatori and others added 5 commits October 23, 2026 17:05
With MSYS_NO_PATHCONV=1 these shells no longer turn a /dev/null argument into nul for Windows programs, so a Windows curl given -o /dev/null cannot open its output and exits with code 23 as soon as a response has a body.
The MySQL readiness loops would then read HTTP_CODE as 200000, the status followed by their || echo "000" fallback, and never finish.
A null_device helper returns NUL for a Windows curl in these shells and /dev/null everywhere else, MSYS2's own curl included, and the 23 curl calls that discard a body in 11 scripts use it.
The README says so, and tells script authors to do the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
Maven's mvn launcher is a shell script that hands its own paths, such as its classpath, to Java, a Windows program on Windows, and relies on Git Bash and MSYS2 converting them.
With MSYS_NO_PATHCONV=1 Java received the POSIX paths and stopped with "Could not find or load main class org.codehaus.plexus.classworlds.launcher.Launcher", as the Windows job showed.
The three deploy scripts of the Service Bus sample run mvn in a subshell that unsets the variable, and the README tells script authors to do the same for such launchers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
… programs

Windows programs such as the Azure CLI and jq end every line with a carriage return, which Bash keeps in $(...) and read, so a name or an ID read from az --output tsv carries a trailing \r.
In the Windows CI run, the App Configuration sample's private endpoint failed twice with ResourceNotFound for a key vault that existed, its name ending in that \r.
MSYS2's bash, which Git Bash ships, drops carriage returns from command substitution and read when its igncr option is on, and an exported SHELLOPTS passes the option to the scripts, so the README asks for set -o igncr and export SHELLOPTS next to MSYS_NO_PATHCONV=1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
On Windows the Azure CLI ends every line with a carriage return, and bash's igncr option drops it from $(...) and read but not from mapfile.
The App Configuration samples read several fields at once with mapfile, so in the Windows CI the key vault ID kept its \r and the private endpoint failed with ResourceNotFound, and the AKS sample read the cluster's OIDC and workload identity flags as "true\r".
Every mapfile that reads az output, in those samples and in the AKS cluster scripts, now reads it through tr -d '\r', which changes nothing on Linux and macOS, and the README says igncr does not cover mapfile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh
…eachable

call-web-app.sh compares the certificate the web app loaded from Key Vault with the one it serves on port 8443, and reaches that port through the container's IP address.
The host can reach that address only when Docker runs natively on Linux; with Docker Desktop on Windows and macOS, and in the Windows CI, the .NET script timed out and failed a deployment that worked.
Both scripts now record whether the earlier call through the container IP succeeded, and when it did not, they say so and skip the certificate check. On Linux the check runs as before, the .NET one failing when HTTPS on port 8443 is broken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhZ2ojsEXd2SGMo7s9uiZh

This branch was previously deployed

1 inactive deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants