Repository: msys2/msys2-autobuild

Issue:
arm64 runner improvements
- [x] #93
- [x] Switch to new ARM64 Git for Windows
  - It's still beta, but would probably work ok, and we can get it some exercise. 
  - Done in jeremyd2019/winautoconfig@f7b8399376544f8cd43270b8fe96348bbb8eef25
- [ ] Switch to ARM64 Python
  - just remove the `architecture: x64`.  I tested, but there are not yet binary wheels for win-arm64, so installing requirements fails for lack of visual studio.  I'm not interested in installing visual studio just for that. 
- [ ] Update to Windows 11 24H2
  - I did some preliminary work (and random commentary) on that in https://github.com/jeremyd2019/winautoconfig/issues/5, but I haven't been offered the update yet on any system (though I didn't opt in for latest updates).  I'd like to wait until the host gets the update at least.  Based on my previous feelings on 'major' Windows upgrades, I'd normally wait a few months for the bugs to be shaken out of the new version.

Hints from issue comments:
@lazka: >     * [ ]  Switch to ARM64 Python

None of the packages affected provide arm64 binary wheels. They are likely all waiting for GitHub hosted runners (for example https://github.com/pyca/cryptography/issues/11199) as Python packaging does not support cross compiling (except hackily with setuptools which is on the way out). So I don't expect much progress there. The timeline for those runners was recently moved/adjusted to Q1 2025 (https://github.com/github/roadmap/issues/970) @jeremyd2019: I've added [pre and post job scripts/hooks](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job) on my ARM64 runners: https://github.com/jeremyd2019/winautoconfig/compare/202e943d2bf03ca0e4f00b6a733e3e956d23ed6b...2b5e6a0931f0cecabd242132e70428995ccc84b3@lazka: > I've added [pre and post job scripts/hooks](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job) on my ARM64 runners: [jeremyd2019/winautoconfig@202e943...3419a78](https://github.com/jeremyd2019/winautoconfig/compare/202e943d2bf03ca0e4f00b6a733e3e956d23ed6b...3419a788ac86dc73ed55776f13916cde148d3be7)

Does that mean we can cancel stuck jobs now?

edit: I tried it, seems to have worked (or it was in a good state): https://github.com/msys2-arm/msys2-autobuild/actions/runs/12702640749/job/35409213279

oh, now it's stuck again in make_tree_writable@jeremyd2019: Yes, it now should be safer to cancel jobs.  Not completely safe, but safer.  The fact that it was not building a package means it probably would have been relatively safe before.

> oh, now it's stuck again in make_tree_writable

That's https://github.com/git-for-windows/git/issues/5320.  Local `rmdir /s C:\M\B` unstuck it@jeremyd2019: maybe make_tree_writable needs to detect symlinks/directory junctions and not follow them also?  Just thinking out loud@lazka: huh, good point, https://github.com/python/cpython/issues/67596@lazka: I've created #107@jeremyd2019: I'm installing the arm64 git-for-windows on my runners with this month's windows updates.@jeremyd2019: Ugh, the windows 11 23h2 update KB5051989 is breaking remote desktop (at least on arm64 systems and local accounts) for me, fails with a quick flash of a device attached to the system is not functioning.  Might have to skip this one (or wait if MS comes up with a patch for the patch)@jeremyd2019: I ~lied~ misspoke, I updated the runner images with the February updates, but the host was downgraded to January due to not being able to use remote desktop (I don't want to have to dig out the USB-C-to-HDMI dongle and use it locally).@jeremyd2019: March's update has remote desktop working properly again.

Solution patch:
diff --git a/msys2_autobuild/build.py b/msys2_autobuild/build.py
index 2195a11..c648456 100644
--- a/msys2_autobuild/build.py
+++ b/msys2_autobuild/build.py
@@ -122,6 +122,29 @@ def run_cmd(msys2_root: PathLike, args: Sequence[PathLike], **kwargs: Any) -> No
     check_call([executable, '-lc'] + [shlex_join([str(a) for a in args])], env=env, **kwargs)
 
 
+def make_tree_writable(topdir: PathLike) -> None:
+    # Ensure all files and directories under topdir are writable
+    # (and readable) by owner.
+    # Taken from meson, and adjusted
+
+    def chmod(p: PathLike) -> None:
+        print(p)
+        os.chmod(p, os.stat(p).st_mode | stat.S_IWRITE | stat.S_IREAD)
+
+    chmod(topdir)
+    for root, dirs, files in os.walk(topdir):
+        for d in dirs:
+            chmod(os.path.join(root, d))
+        # Work around Python bug following junctions
+        # https://github.com/python/cpython/issues/67596#issuecomment-1918112817
+        if hasattr(os.path, 'isjunction'):  # Python 3.12 only
+            dirs[:] = [d for d in dirs if not os.path.isjunction(os.path.join(root, d))]
+        for fname in files:
+            fpath = os.path.join(root, fname)
+            if os.path.isfile(fpath):
+                chmod(fpath)
+
+
 def reset_git_repo(path: PathLike):
 
     def clean():
@@ -129,17 +152,6 @@ def reset_git_repo(path: PathLike):
         check_call(["git", "clean", "-xfdf"], cwd=path)
         check_call(["git", "reset", "--hard", "HEAD"], cwd=path)
 
-    def make_tree_writable(topdir: PathLike) -> None:
-        # Ensure all files and directories under topdir are writable
-        # (and readable) by owner.
-        # Taken from meson
-        for d, _, files in os.walk(topdir):
-            os.chmod(d, os.stat(d).st_mode | stat.S_IWRITE | stat.S_IREAD)
-            for fname in files:
-                fpath = os.path.join(d, fname)
-                if os.path.isfile(fpath):
-                    os.chmod(fpath, os.stat(fpath).st_mode | stat.S_IWRITE | stat.S_IREAD)
-
     made_writable = False
     for i in range(10):
         try:


Test patch:
diff --git a/tests/main_test.py b/tests/main_test.py
index 7b24583..7bab414 100644
--- a/tests/main_test.py
+++ b/tests/main_test.py
@@ -1,7 +1,40 @@
 # type: ignore
 
+import os
+import stat
+import tempfile
+from pathlib import Path
+
 from msys2_autobuild.utils import parse_optional_deps
 from msys2_autobuild.queue import parse_buildqueue, get_cycles
+from msys2_autobuild.build import make_tree_writable
+
+
+def test_make_tree_writable():
+    with tempfile.TemporaryDirectory() as tempdir:
+        nested_dir = Path(tempdir) / "nested"
+        nested_junction = nested_dir / "junction"
+        nested_dir.mkdir()
+        file_path = nested_dir / "test_file.txt"
+        file_path.write_text("content")
+
+        # Create a junction loop if possible, to make sure we ignore it
+        if hasattr(os.path, 'isjunction') and os.name == 'nt':
+            import _winapi
+            _winapi.CreateJunction(str(nested_dir), str(nested_junction))
+        else:
+            nested_junction.mkdir()
+
+        # Remove permissions
+        for p in [tempdir, nested_dir, file_path, nested_junction]:
+            os.chmod(p, os.stat(p).st_mode & ~stat.S_IWRITE & ~stat.S_IREAD)
+
+        make_tree_writable(tempdir)
+
+        assert os.access(tempdir, os.W_OK) and os.access(tempdir, os.R_OK)
+        assert os.access(nested_dir, os.W_OK) and os.access(nested_dir, os.R_OK)
+        assert os.access(file_path, os.W_OK) and os.access(file_path, os.R_OK)
+        assert os.access(nested_junction, os.W_OK) and os.access(nested_junction, os.R_OK)
 
 
 def test_parse_optional_deps():