From 96ab89637d4840e96c1fd362137513254cf4356f Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Fri, 25 Jun 2021 11:28:24 +0200 Subject: [PATCH] Pins to remote branches (#754) * Accept "branch" in remote pins * Refactor pin loader for clarity * Load/Store branch in lockfile * Do use branches for pin checkouts/updates * Use pin branch for showing solution differences * New test for conflicting link pins * New test for detecting pin path changes * Document new branch pins * New test specific for branch pins * Fixes found during self-review * Fixes caught by testsuite on Windows * More fixes during self-review * Fixes requested during review --- doc/catalog-format-spec.md | 5 +- doc/user-changes.md | 13 ++ src/alire/alire-dependencies-states.ads | 27 +-- src/alire/alire-roots.adb | 19 +- src/alire/alire-solutions-diffs.adb | 4 +- src/alire/alire-solutions.adb | 7 +- src/alire/alire-user_pins.adb | 205 ++++++++++++------ src/alire/alire-user_pins.ads | 21 +- src/alire/alire-vcss-git.adb | 2 +- src/alire/alire.ads | 2 +- testsuite/drivers/alr.py | 12 +- testsuite/drivers/helpers.py | 38 ++++ testsuite/tests/pin/branch/test.py | 53 +++++ .../pin/{recursive_local => branch}/test.yaml | 0 testsuite/tests/pin/change-path/test.py | 46 ++++ .../test.yaml | 0 testsuite/tests/pin/change-type/test.py | 2 +- testsuite/tests/pin/conflicting-link/test.py | 45 ++++ .../tests/pin/conflicting-link/test.yaml | 3 + .../tests/pin/conflicting-remote/test.py | 102 +++++++++ .../tests/pin/conflicting-remote/test.yaml | 3 + testsuite/tests/pin/pin-dir/test.py | 2 +- .../test.py | 0 testsuite/tests/pin/recursive-local/test.yaml | 3 + .../test.py | 0 .../tests/pin/recursive-remote/test.yaml | 3 + 26 files changed, 514 insertions(+), 103 deletions(-) create mode 100644 testsuite/tests/pin/branch/test.py rename testsuite/tests/pin/{recursive_local => branch}/test.yaml (100%) create mode 100644 testsuite/tests/pin/change-path/test.py rename testsuite/tests/pin/{recursive_remote => change-path}/test.yaml (100%) create mode 100644 testsuite/tests/pin/conflicting-link/test.py create mode 100644 testsuite/tests/pin/conflicting-link/test.yaml create mode 100644 testsuite/tests/pin/conflicting-remote/test.py create mode 100644 testsuite/tests/pin/conflicting-remote/test.yaml rename testsuite/tests/pin/{recursive_local => recursive-local}/test.py (100%) create mode 100644 testsuite/tests/pin/recursive-local/test.yaml rename testsuite/tests/pin/{recursive_remote => recursive-remote}/test.py (100%) create mode 100644 testsuite/tests/pin/recursive-remote/test.yaml diff --git a/doc/catalog-format-spec.md b/doc/catalog-format-spec.md index 24d39c3d..f924b6f7 100644 --- a/doc/catalog-format-spec.md +++ b/doc/catalog-format-spec.md @@ -535,11 +535,12 @@ The specific pin kinds and their attributes are: For the common case of directories containing an Alire manifest, dependencies and pins will be included recursively in the build context. -* Pins to git repositories: the repository will be cloned locally and its directory will be used as in the previous case. Currently, this pin may optionally include a commit to fix the checkout to be used. Otherwise, the default branch will be used, and running `alr update` will refresh the checkout. +* Pins to git repositories: the repository will be cloned locally and its directory will be used as in the previous case. This pin may optionally include a commit to fix the checkout to be used, or a branch to track. Otherwise, the default branch will be used. Running `alr update` will refresh the checkout. * `url`: the URL of a git repository * `commit` (optional): a complete git commit hash. - * `crate_name = { url = "https://my/repo.git" } # Updatable pin` + * `crate_name = { url = "https://my/repo.git" } # Updatable pin to default branch` + * `crate_name = { url = "https://my/repo.git", branch="feature" } # Updatable pin` * `crate_name = { url = "https://my/repo.git", commit="abcdef..." } # Fixed pin` ### Using pins for crate testing diff --git a/doc/user-changes.md b/doc/user-changes.md index ef80bac3..2725c48f 100644 --- a/doc/user-changes.md +++ b/doc/user-changes.md @@ -6,6 +6,19 @@ stay on top of `alr` new features. ## Release `1.1` +### Pins to git branches + +PR [#754](https://github.com/alire-project/alire/pull/754) + +A new option for remote pins exist to track branches: + +``` +[[pins]] +wip = { url = "https://gitrepo.com/wip.git" branch="feature" } +``` + +Running `alr update` will pull any changes from the branch. + ### Pins stored in the manifest PR [#743](https://github.com/alire-project/alire/pull/743). diff --git a/src/alire/alire-dependencies-states.ads b/src/alire/alire-dependencies-states.ads index 16ce1ca2..a092b960 100644 --- a/src/alire/alire-dependencies-states.ads +++ b/src/alire/alire-dependencies-states.ads @@ -281,16 +281,16 @@ private else "") & Utils.To_Lower_Case (This.Fulfilled.Fulfillment'Img) & (if This.Fulfilled.Fulfillment = Linked - then ",pin=" & This.Fulfilled.Target.Element.Relative_Path - & (if not This.Fulfilled.Target.Element.Is_Broken - then "" - else ",broken") - & (if This.Fulfilled.Target.Element.Is_Remote - then ",url=" & This.Fulfilled.Target.Element.URL - else "") - & (if This.Has_Release - then ",release" - else "") + then This.Fulfilled.Target.Element.Image (User => True) + & (if not This.Fulfilled.Target.Element.Is_Broken + then "" + else ",broken") + & (if This.Fulfilled.Target.Element.Is_Remote + then ",url=" & This.Fulfilled.Target.Element.URL + else "") + & (if This.Has_Release + then ",release" + else "") else "") & (if This.Pinning.Pinned then ",pin=" & This.Pinning.Version.Image @@ -487,15 +487,10 @@ private when Hinted => TTY.Warn (This.Fulfilled.Fulfillment'Img), when others => This.Fulfilled.Fulfillment'Img) & (if This.Fulfilled.Fulfillment = Linked - then "," & TTY.Emph ("pin") & "=" - & TTY.URL (This.Fulfilled.Target.Element.Relative_Path) + then "," & This.Fulfilled.Target.Element.Image (User => True) & (if not This.Fulfilled.Target.Element.Is_Broken then "" else "," & TTY.Error ("broken")) - & (if This.Fulfilled.Target.Element.Is_Remote - then ",url=" & TTY.URL - (This.Fulfilled.Target.Element.URL) - else "") & (if This.Has_Release then "," & TTY.OK ("release") else "") diff --git a/src/alire/alire-roots.adb b/src/alire/alire-roots.adb index 65815975..6d80f16b 100644 --- a/src/alire/alire-roots.adb +++ b/src/alire/alire-roots.adb @@ -328,14 +328,15 @@ package body Alire.Roots is procedure Add_Link_Pin (Crate : Crate_Name; Pin : in out User_Pins.Pin) is + use type User_Pins.Pin; begin - -- Just in case this is a remote pin, deploy it + -- Just in case this is a remote pin, deploy it. Deploy is + -- conservative (unless Online), but it will detect local + -- inexpensive changes like a missing checkout, changed commit + -- or branch. - if Exhaustive - or else - (Allowed.Is_Empty or else Allowed.Contains (Crate)) - then + if Allowed.Is_Empty or else Allowed.Contains (Crate) then Pin.Deploy (Crate => Crate, Under => Pins_Dir, Online => Exhaustive); @@ -346,18 +347,16 @@ package body Alire.Roots is if Sol.Depends_On (Crate) and then Sol.State (Crate).Is_Linked - and then Sol.State (Crate).Link.Path /= Pin.Path + and then Sol.State (Crate).Link /= Pin then Raise_Checked_Error ("Conflicting pin links for crate " & TTY.Name (Crate) & ": Crate " & TTY.Name (Release (This).Name) - & " wants a link to " & TTY.URL (Pin.Path) + & " wants to link " & TTY.URL (Pin.Image (User => True)) & ", but a previous link exists to " - & TTY.URL (Sol.State (Crate).Link.Path)); + & TTY.URL (Sol.State (Crate).Link.Image (User => True))); end if; - -- TODO: test conflicting link detection for two pins - -- We have a new target root to load declare diff --git a/src/alire/alire-solutions-diffs.adb b/src/alire/alire-solutions-diffs.adb index bfb67db6..7db348c9 100644 --- a/src/alire/alire-solutions-diffs.adb +++ b/src/alire/alire-solutions-diffs.adb @@ -146,13 +146,13 @@ package body Alire.Solutions.Diffs is if Gains_State (Hinted) then Add_Change (Chg, Icon (Hinted), TTY.Warn ("external")); - -- Changed linked dir target + -- Changed linked dir elsif Has_Latter and then Latter.Is_Linked and then (not Has_Former or else not Former.Is_Linked or else Former.Link /= Latter.Link) then Add_Change (Chg, Icon (Pinned), - "pin=" & Latter.Link.Relative_Path); + Latter.Link.Image (User => True)); -- New unsolvable elsif Gains_State (Missed) then diff --git a/src/alire/alire-solutions.adb b/src/alire/alire-solutions.adb index 5e002e83..3551b773 100644 --- a/src/alire/alire-solutions.adb +++ b/src/alire/alire-solutions.adb @@ -423,7 +423,8 @@ package body Alire.Solutions is then Dep.Link.Relative_Path & (if Dep.Link.Is_Remote then " from " - & Dep.Link.TTY_URL_With_Commit + & Dep.Link.TTY_URL_With_Reference + (Detailed) else "") -- no remote else Utils.To_Lower_Case (Rel.Origin.Kind'Img)) & ")" -- origin completed @@ -456,7 +457,7 @@ package body Alire.Solutions is & Dep.Link.Relative_Path & (if Dep.Link.Is_Remote then " from " - & Dep.Link.TTY_URL_With_Commit + & Dep.Link.TTY_URL_With_Reference else "") -- no remote & ")" -- origin completed else ""), -- no details @@ -581,7 +582,7 @@ package body Alire.Solutions is .Append (TTY.Name (Dep.Crate)) .Append (TTY.URL ("file:") & Dep.Link.Relative_Path) .Append (if Dep.Link.Is_Remote - then Dep.Link.TTY_URL_With_Commit + then Dep.Link.TTY_URL_With_Reference (Detailed) else "") .New_Row; elsif Dep.Is_Pinned then diff --git a/src/alire/alire-user_pins.adb b/src/alire/alire-user_pins.adb index 9f1bf520..fbffa40d 100644 --- a/src/alire/alire-user_pins.adb +++ b/src/alire/alire-user_pins.adb @@ -15,6 +15,7 @@ package body Alire.User_Pins is package TTY renames Alire.Utils.TTY; package Keys is + Branch : constant String := "branch"; Commit : constant String := "commit"; Internal : constant String := "lockfiled"; Path : constant String := "path"; @@ -22,6 +23,24 @@ package body Alire.User_Pins is Version : constant String := "version"; end Keys; + ----------- + -- Image -- + ----------- + + function Image (This : Pin; User : Boolean) return String + is (case This.Kind is + when To_Version => "version=" & TTY.Version (This.Version.Image), + when To_Path => "path=" & TTY.URL (if User + then VFS.Attempt_Portable (+This.Path) + else +This.Path), + when To_Git => + (if Path (This) /= "" + then "path=" & TTY.URL ((if User + then VFS.Attempt_Portable (Path (This)) + else Path (This))) & "," + else "") + & ("url=" & This.TTY_URL_With_Reference)); + --------------- -- Is_Broken -- --------------- @@ -96,7 +115,7 @@ package body Alire.User_Pins is -- Update -- ------------ - procedure Update is + procedure Update (Branch : String) is begin Trace.Detail ("Checking out pin " & TTY.Name (Crate) & " at " & TTY.URL (Destination)); @@ -114,9 +133,10 @@ package body Alire.User_Pins is return; end if; - -- Finally update + -- Finally update. In case the branch has just been changed by the + -- user in the manifest, the following call wil also take care of it. - if not VCSs.Git.Handler.Update (Destination).Success then + if not VCSs.Git.Handler.Update (Destination, Branch).Success then Raise_Checked_Error ("Update of repository at " & TTY.URL (Destination) & " failed, re-run with -vv -d for details"); @@ -137,8 +157,12 @@ package body Alire.User_Pins is -- branch pin if Ada.Directories.Exists (Destination) + and then not Online and then - (This.Commit /= "" or else not Online) + (This.Commit /= "" -- Static checkout, no need to re-checkout + or else This.Branch = "" -- Default branch, same + or else VCSs.Git.Handler.Branch (Destination) = This.Branch) + -- Branch is explicit and matches the one on disk, same then Trace.Debug ("Skipping deployment of already existing pin at " & TTY.URL (Destination)); @@ -152,10 +176,12 @@ package body Alire.User_Pins is Checkout (Commit => +This.Commit); elsif Ada.Directories.Exists (Destination) then - Update; + Update (+This.Branch); + -- Branch may still be "" if none given else - Checkout; + Checkout (Branch => +This.Branch); + -- Branch may still be "" if none given end if; @@ -189,14 +215,20 @@ package body Alire.User_Pins is end Deploy; - ------------------------- - -- TTY_URL_With_Commit -- - ------------------------- + ---------------------------- + -- TTY_URL_With_Reference -- + ---------------------------- - function TTY_URL_With_Commit (This : Pin) return String + function TTY_URL_With_Reference (This : Pin; + Detailed : Boolean := False) + return String is (TTY.URL (URL (This)) & (if Commit (This).Has_Element - then "#" & TTY.Emph (Commit (This).Element.Ptr.all) + then "#" & TTY.Emph (if Detailed + then +This.Commit + else Origins.Short_Commit (+This.Commit)) + elsif Branch (This).Has_Element + then "#" & TTY.Emph (+This.Branch) else "")); ---------- @@ -264,6 +296,9 @@ package body Alire.User_Pins is "Boolean expected"); if This.Contains (Keys.URL) then + + -- A complete remote pin + return Result : Pin := (Kind => To_Git, others => <>) do Result.URL := +This.Checked_Pop (Keys.URL, @@ -276,9 +311,16 @@ package body Alire.User_Pins is if This.Contains (Keys.Commit) then Result.Commit := +This.Checked_Pop (Keys.Commit, TOML_String).As_String; + elsif This.Contains (Keys.Branch) then + Result.Branch := + +This.Checked_Pop (Keys.Branch, TOML_String).As_String; end if; end return; + else + + -- Just a local pin + return Result : Pin := (Kind => To_Path, others => <>) do Result.Path := +Utils.User_Input.To_Absolute_From_Portable @@ -287,6 +329,89 @@ package body Alire.User_Pins is end if; end From_Lockfile; + ------------------ + -- Load_To_Path -- + ------------------ + + function Load_To_Path return Pin is + Result : Pin := + (Kind => To_Path, + Path => <>); + User_Path : constant String := + This.Checked_Pop (Keys.Path, + TOML_String).As_String; + begin + This.Report_Extra_Keys; + + -- Check that the path was stored in portable format or as + -- absolute path. + + if not Check_Absolute_Path (User_Path) and then + not VFS.Is_Portable (User_Path) + then + This.Recoverable_Error + ("Pin relative paths must use forward slashes " + & "to be portable: " & Utils.TTY.URL (User_Path)); + end if; + + -- Make the path absolute if not already, and store it + + Result.Path := + +Utils.User_Input.To_Absolute_From_Portable + (User_Path => User_Path, + Error_When_Relative_Native => + "Pin relative paths must use forward slashes " & + " to be portable"); + + if not GNAT.OS_Lib.Is_Directory (+Result.Path) then + This.Checked_Error ("Pin path is not a valid directory: " + & (+Result.Path)); + end if; + + return Result; + end Load_To_Path; + + ----------------- + -- Load_Remote -- + ----------------- + + function Load_Remote return Pin is + Result : Pin := + (Kind => To_Git, + URL => +This.Checked_Pop (Keys.URL, + TOML_String).As_String, + Branch => <>, + Commit => <>, + Local_Path => <>); + begin + if This.Contains (Keys.Branch) + and then This.Contains (Keys.Commit) + then + This.Checked_Error + ("cannot specify both a branch and a commit"); + end if; + + -- TEST: simultaneous branch/commit + + if This.Contains (Keys.Commit) then + Result.Commit := + +This.Checked_Pop (Keys.Commit, TOML_String).As_String; + This.Assert (+Result.Commit in Origins.Git_Commit, + "invalid commit: " & (+Result.Commit)); + elsif This.Contains (Keys.Branch) then + Result.Branch := + +This.Checked_Pop (Keys.Branch, TOML_String).As_String; + This.Assert (+Result.Branch /= "", + "branch cannot be the empty string"); + end if; + + -- TEST: empty branch value + + This.Report_Extra_Keys; + + return Result; + end Load_Remote; + begin if This.Contains (Keys.Internal) then return Result : constant Pin := From_Lockfile do @@ -300,61 +425,10 @@ package body Alire.User_Pins is (This.Checked_Pop (Keys.Version, TOML_String).As_String)); elsif This.Contains (Keys.Path) then - return Result : Pin := - (Kind => To_Path, - Path => <>) - do - declare - User_Path : constant String := - This.Checked_Pop (Keys.Path, - TOML_String).As_String; - begin - This.Report_Extra_Keys; - - -- Check that the path was stored in portable format or as - -- absolute path. - - if not Check_Absolute_Path (User_Path) and then - not VFS.Is_Portable (User_Path) - then - This.Recoverable_Error - ("Pin relative paths must use forward slashes " - & "to be portable: " & Utils.TTY.URL (User_Path)); - end if; - - -- Make the path absolute if not already, and store it - - Result.Path := - +Utils.User_Input.To_Absolute_From_Portable - (User_Path => User_Path, - Error_When_Relative_Native => - "Pin relative paths must use forward slashes " & - " to be portable"); - - if not GNAT.OS_Lib.Is_Directory (+Result.Path) then - This.Checked_Error ("Pin path is not a valid directory: " - & (+Result.Path)); - end if; - end; - end return; + return Load_To_Path; elsif This.Contains (Keys.URL) then - return Result : Pin := - (Kind => To_Git, - URL => +This.Checked_Pop (Keys.URL, - TOML_String).As_String, - Commit => <>, - Local_Path => <>) - do - if This.Contains (Keys.Commit) then - Result.Commit := - +This.Checked_Pop (Keys.Commit, TOML_String).As_String; - This.Assert (+Result.Commit in Origins.Git_Commit, - "invalid commit: " & (+Result.Commit)); - end if; - - This.Report_Extra_Keys; - end return; + return Load_Remote; else Trace.Error ("Unexpected key in pin, got:"); @@ -407,6 +481,9 @@ package body Alire.User_Pins is if Commit (This).Has_Element then Table.Set (Keys.Commit, Create_String (Commit (This).Element.Ptr.all)); + elsif Branch (This).Has_Element then + Table.Set (Keys.Branch, + Create_String (Branch (This).Element.Ptr.all)); end if; end if; diff --git a/src/alire/alire-user_pins.ads b/src/alire/alire-user_pins.ads index d7903d82..0043a3a0 100644 --- a/src/alire/alire-user_pins.ads +++ b/src/alire/alire-user_pins.ads @@ -26,6 +26,9 @@ package Alire.User_Pins is type Pin (Kind : Kinds) is tagged private; + function Image (This : Pin; User : Boolean) return String; + -- Returns the internal information as-is or with relative paths, when User + function Is_Remote (This : Pin) return Boolean; -- A pin to a remote source such as git, source archives, etc @@ -55,11 +58,17 @@ package Alire.User_Pins is function URL (This : Pin) return Alire.URL with Pre => This.Is_Remote; + function Branch (This : Pin) return Optional.String + with Pre => This.Is_Remote; + function Commit (This : Pin) return Optional.String with Pre => This.Is_Remote; - function TTY_URL_With_Commit (This : Pin) return String + function TTY_URL_With_Reference (This : Pin; + Detailed : Boolean := False) + return String with Pre => This.Is_Remote; + -- returns https://blah[#commit|#branch], when existing procedure Deploy (This : in out Pin; Crate : Crate_Name; @@ -98,6 +107,7 @@ private case Kind is when To_Git => URL : UString; + Branch : UString; -- Optional Commit : UString; -- Optional Local_Path : Unbounded_Absolute_Path; -- Empty until the pin is locally deployed @@ -108,6 +118,15 @@ private end case; end record; + ------------ + -- Branch -- + ------------ + + function Branch (This : Pin) return Optional.String + is (if +This.Branch = "" + then Optional.Strings.Empty + else Optional.Strings.Unit (+This.Branch)); + ------------ -- Commit -- ------------ diff --git a/src/alire/alire-vcss-git.adb b/src/alire/alire-vcss-git.adb index d8bfd9d2..acf7b35d 100644 --- a/src/alire/alire-vcss-git.adb +++ b/src/alire/alire-vcss-git.adb @@ -411,7 +411,7 @@ package body Alire.VCSs.Git is with Unreferenced; Extra : constant String_Vector := (if Log_Level < Trace.Info - then Empty_Vector & "-q " + then Empty_Vector & "-q" else Empty_Vector & "--progress"); begin diff --git a/src/alire/alire.ads b/src/alire/alire.ads index 47ecb170..cd5d0c78 100644 --- a/src/alire/alire.ads +++ b/src/alire/alire.ads @@ -9,7 +9,7 @@ with Simple_Logging; package Alire with Preelaborate is - Version : constant String := "1.1.0-dev+recpins"; + Version : constant String := "1.1.0-dev+branchpins"; -- 1.1.0-dev: begin post-1.0 changes -- 1.0.0: no changes since rc3 -- 1.0.0-rc3: added help colors PR diff --git a/testsuite/drivers/alr.py b/testsuite/drivers/alr.py index 66375996..1ead09c0 100644 --- a/testsuite/drivers/alr.py +++ b/testsuite/drivers/alr.py @@ -212,6 +212,14 @@ def alr_manifest(): return "alire.toml" +def alr_touch_manifest(path="."): + """ + Make the lockfile older than the manifest, to ensure editions to the + manifest are detected. + """ + os.utime(os.path.join(path, "alire.lock"), (0, 0)) + + def alr_unpin(crate, manual=True, fail_if_missing=True, update=True): """ Unpin a crate, if pinned, or no-op otherwise. Will edit the manifest or use @@ -251,7 +259,7 @@ def alr_unpin(crate, manual=True, fail_if_missing=True, update=True): raise NotImplementedError("Unimplemented") -def alr_pin(crate, version="", path="", url="", commit="", +def alr_pin(crate, version="", path="", url="", commit="", branch="", manual=True, update=True): """ Pin a crate, either manually or using the command-line interface. Use only @@ -268,6 +276,8 @@ def alr_pin(crate, version="", path="", url="", commit="", pin_line = f"{crate} = {{ path = '{path}' }}" # literal so \ works elif url != "" and commit != "": pin_line = f"{crate} = {{ url = '{url}', commit = '{commit}' }}" + elif url != "" and branch != "": + pin_line = f"{crate} = {{ url = '{url}', branch = '{branch}' }}" elif url != "": pin_line = f"{crate} = {{ url = '{url}' }}" else: diff --git a/testsuite/drivers/helpers.py b/testsuite/drivers/helpers.py index 314c671a..7678c2e3 100644 --- a/testsuite/drivers/helpers.py +++ b/testsuite/drivers/helpers.py @@ -7,6 +7,8 @@ from zipfile import ZipFile import os import platform +import shutil +import stat # Return the entries (sorted) under a given folder, both folders and files @@ -74,6 +76,42 @@ def with_project(file, project): f.write('with "{}";'.format(project) + '\n' + content) +def git_branch(path="."): + """ + Return the branch name of the checkout + """ + start_cwd = os.getcwd() + os.chdir(path) + branch = run(["git", "branch"], + capture_output=True).stdout.split()[1] + os.chdir(start_cwd) + return branch.decode() + + +def git_head(path="."): + """ + Return the head commit in a git repo + """ + start_cwd = os.getcwd() + os.chdir(path) + head_commit = run(["git", "log", "-n1", "--no-abbrev", "--oneline"], + capture_output=True).stdout.split()[0] + os.chdir(start_cwd) + return head_commit.decode() + + +def git_blast(path): + """ + Change permissions prior to deletion, as otherwise Windows is uncapable + of removing git checkouts + """ + for dirpath, dirnames, filenames in os.walk(path): + os.chmod(dirpath, stat.S_IRWXU) + for filename in filenames: + os.chmod(os.path.join(dirpath, filename), stat.S_IRWXU) + shutil.rmtree(path) + + def init_git_repo(path): """ Initialize and commit everything inside a folder, returning the HEAD commit diff --git a/testsuite/tests/pin/branch/test.py b/testsuite/tests/pin/branch/test.py new file mode 100644 index 00000000..061db51b --- /dev/null +++ b/testsuite/tests/pin/branch/test.py @@ -0,0 +1,53 @@ +""" +Check pinning to a branch, and changing branches +""" + +from drivers.alr import run_alr, alr_pin, alr_unpin, init_local_crate +from drivers.asserts import assert_eq, assert_match +from drivers.helpers import git_branch, git_head, init_git_repo +from e3.os.fs import touch +from re import escape + +import re +import os +import shutil +import subprocess + +# "remote" is going to be the remote crate + +init_local_crate(name="remote", enter=False) +url = os.path.join(os.getcwd(), "remote") +head1 = init_git_repo("remote") +os.chdir("remote") +default_branch = git_branch() + +# Create a second branch and commit for testing +subprocess.run(["git", "checkout", "-b", "devel"]).check_returncode() +touch("telltale") +subprocess.run(["git", "add", "telltale"]).check_returncode() +subprocess.run(["git", "commit", "-m", "branching"]).check_returncode() +os.chdir("..") + +# Now pin to the branch, and verify the telltale file exists in the checkout +init_local_crate() +alr_pin("remote", url=url, branch="devel") +p = run_alr("pin") +assert_match("remote file:alire/cache/pins/remote " + + re.escape(url) + "#devel\n", # branch in the info matches + p.out) + +# Also verify the file exists +assert os.path.exists("alire/cache/pins/remote/telltale"), \ + "Missing file in checkout" + +# Edit pin to point to the default branch, and verify telltale is missing +alr_unpin("remote", update=False) +alr_pin("remote", url=url, branch=default_branch) +p = run_alr("pin") +assert_match("remote file:alire/cache/pins/remote " + + re.escape(url) + f"#{default_branch}\n", + p.out) +assert not os.path.exists("alire/cache/pins/remote/telltale"), \ + "Unexpected file in checkout" + +print('SUCCESS') diff --git a/testsuite/tests/pin/recursive_local/test.yaml b/testsuite/tests/pin/branch/test.yaml similarity index 100% rename from testsuite/tests/pin/recursive_local/test.yaml rename to testsuite/tests/pin/branch/test.yaml diff --git a/testsuite/tests/pin/change-path/test.py b/testsuite/tests/pin/change-path/test.py new file mode 100644 index 00000000..469db1a4 --- /dev/null +++ b/testsuite/tests/pin/change-path/test.py @@ -0,0 +1,46 @@ +""" +Verify that the path of a pin can be changed and this is detected automatically +""" + +from drivers.alr import run_alr, alr_pin, alr_unpin, init_local_crate +from drivers.asserts import assert_eq, assert_match +from drivers.helpers import on_windows + +import os + + +def create_dep(nest): + """ + Create the same dependency yyy under the given nest path + """ + os.mkdir(nest) + os.chdir(nest) + init_local_crate("yyy", enter=False) + os.chdir("..") + + +# Two versions of the same crate +create_dep("nest1") +create_dep("nest2") + +# Dependent main crate +init_local_crate() + +alr_pin("yyy", path="../nest1/yyy") + +# Edit the pin path by unpinning/repinning, without running alr in between, +# so this is equivalent to just editing the pin +alr_unpin("yyy", update=False) +alr_pin("yyy", path="../nest2/yyy", update=False) + +# Now detect changes and show output +p = run_alr("pin", quiet=False) +assert_eq("""Note: Synchronizing workspace... +Dependencies automatically updated as follows: + + · yyy 0.0.0 (path=../nest2/yyy) + +yyy file:../nest2/yyy \n""", + p.out) + +print('SUCCESS') diff --git a/testsuite/tests/pin/recursive_remote/test.yaml b/testsuite/tests/pin/change-path/test.yaml similarity index 100% rename from testsuite/tests/pin/recursive_remote/test.yaml rename to testsuite/tests/pin/change-path/test.yaml diff --git a/testsuite/tests/pin/change-type/test.py b/testsuite/tests/pin/change-type/test.py index 3ba44b28..b7cafe20 100644 --- a/testsuite/tests/pin/change-type/test.py +++ b/testsuite/tests/pin/change-type/test.py @@ -35,7 +35,7 @@ alr_pin('libhello', path='../crates/libhello_1.0.0') p = run_alr('show', '--solve') assert_match('.*Dependencies \(external\):.*' 'libhello\^1 \(direct,linked' - ',pin=../crates/libhello_1.0.0\).*', + ',path=../crates/libhello_1.0.0\).*', p.out, flags=re.S) # Repin to a version and check again diff --git a/testsuite/tests/pin/conflicting-link/test.py b/testsuite/tests/pin/conflicting-link/test.py new file mode 100644 index 00000000..4460a7e2 --- /dev/null +++ b/testsuite/tests/pin/conflicting-link/test.py @@ -0,0 +1,45 @@ +""" +Verify that two equal links for the same create are accepted but two different +ones are rejected +""" + +from drivers.alr import run_alr, alr_pin, alr_unpin, init_local_crate +from drivers.asserts import assert_eq, assert_match + +import os + +# We are going to setup xxx --> yyy --> zzz, xxx --> zzz to verify same link +# is accepted. + +init_local_crate(name="zzz", enter=False) + +os.mkdir("nest") # By nesting yyy, we force different relative paths that +os.chdir("nest") # should not be a problem as internally is the same abs path. +init_local_crate(name="yyy") +alr_pin("zzz", path="../../zzz") + +os.chdir("..") +os.chdir("..") +init_local_crate() # This places us at ./xxx +alr_pin("yyy", path="../nest/yyy") +alr_pin("zzz", path="../zzz") # This is the doubly-linked same crate + +# Should work properly +p = run_alr("pin") +assert_eq('yyy file:../nest/yyy \n' + 'zzz file:../zzz \n', + p.out) + +# Now we will pin a different zzz from xxx, +# so xxx --> ./xxx/zzz conflicts with ./nest/yyy --> ../../zzz + +alr_unpin("zzz") +init_local_crate(name="zzz", enter=False) # This one is at ./xxx/zzz +alr_pin("zzz", path="./zzz", update=False) + +# Should detect conflicting links +p = run_alr("pin", complain_on_error=False) +assert_match(".*Conflicting pin links for crate zzz:.*", + p.out) + +print('SUCCESS') diff --git a/testsuite/tests/pin/conflicting-link/test.yaml b/testsuite/tests/pin/conflicting-link/test.yaml new file mode 100644 index 00000000..872fc127 --- /dev/null +++ b/testsuite/tests/pin/conflicting-link/test.yaml @@ -0,0 +1,3 @@ +driver: python-script +indexes: + basic_index: {} diff --git a/testsuite/tests/pin/conflicting-remote/test.py b/testsuite/tests/pin/conflicting-remote/test.py new file mode 100644 index 00000000..0c60e14a --- /dev/null +++ b/testsuite/tests/pin/conflicting-remote/test.py @@ -0,0 +1,102 @@ +""" +Check conflict detection for remote pins for the same crate +""" + +from drivers.alr import run_alr, alr_pin, alr_unpin, init_local_crate +from drivers.asserts import assert_eq, assert_match +from drivers.helpers import git_blast, git_head, init_git_repo +from e3.os.fs import touch +from re import escape + +import re +import os +import shutil +import subprocess + +# "remote" is going to be the remote crate. Two other crates will depend on it +# with different branches/commits etc + +init_local_crate(name="zzz", enter=False) +url = os.path.join(os.getcwd(), "zzz") +head1 = init_git_repo("zzz") +os.chdir("zzz") + +# Create a second branch and commit for testing +subprocess.run(["git", "checkout", "-b", "devel"]).check_returncode() +touch("x") +subprocess.run(["git", "add", "x"]).check_returncode() +subprocess.run(["git", "commit", "-m", "branching"]).check_returncode() +head2 = git_head() +os.chdir("..") + + +# In this function we will test that two crates with the same remote works, +# for several combinations +def should_work(commit="", branch=""): + os.mkdir("nest") + os.chdir("nest") + + for crate in ["xxx", "yyy"]: + init_local_crate(crate) + alr_pin("zzz", url=url, commit=commit, branch=branch) + os.chdir("..") + + os.chdir("xxx") + alr_pin("yyy", path="../yyy") + + p = run_alr("pin") + assert_match(escape("yyy file:../yyy") + ".*\n" + + escape("zzz file:alire/cache/pins/zzz") + ".*" + + escape(url), + p.out) + + # Clean up for next trial + os.chdir("..") + os.chdir("..") + git_blast("nest") + + +# In this function we will test conflicts that should be detected +def should_not_work(commits=["", ""], branches=["", ""], match_error="FAIL"): + # Commits and branches must contain two values that go into each crate pin + + os.mkdir("nest") + os.chdir("nest") + crates = ["xxx", "yyy"] + + for i in [0, 1]: + init_local_crate(crates[i]) + alr_pin("zzz", url=url, commit=commits[i], branch=branches[i]) + os.chdir("..") + + os.chdir("xxx") + alr_pin("yyy", path="../yyy", update=False) + + p = run_alr("pin", complain_on_error=False) + assert_match(match_error, + p.out) + + # Clean up for next trial + os.chdir("..") + os.chdir("..") + shutil.rmtree("nest") + + +should_work() # Remote at default branch +should_work(commit=head1) # Remote at given commit +should_work(branch="devel") # Remote at given branch + +should_not_work(commits=[head1, head2], + match_error=".*" + + re.escape("Conflicting pin links for crate zzz: " + "Crate xxx wants to link ") + + ".*" + re.escape(", but a previous link exists to ") + + ".*zzz#[0-9a-f]+.*") # with commit +should_not_work(branches=["", "devel"], + match_error=".*" + + re.escape("Conflicting pin links for crate zzz: " + "Crate xxx wants to link ") + + ".*" + re.escape(", but a previous link exists to ") + + ".*zzz#devel\n") # with branch + +print('SUCCESS') diff --git a/testsuite/tests/pin/conflicting-remote/test.yaml b/testsuite/tests/pin/conflicting-remote/test.yaml new file mode 100644 index 00000000..872fc127 --- /dev/null +++ b/testsuite/tests/pin/conflicting-remote/test.yaml @@ -0,0 +1,3 @@ +driver: python-script +indexes: + basic_index: {} diff --git a/testsuite/tests/pin/pin-dir/test.py b/testsuite/tests/pin/pin-dir/test.py index 10fbb682..4d08ec26 100644 --- a/testsuite/tests/pin/pin-dir/test.py +++ b/testsuite/tests/pin/pin-dir/test.py @@ -31,7 +31,7 @@ p = run_alr('with', '--solve') # over some parts of the output assert_match('.*Dependencies \(external\):.*' 'libhello\^1.0.0 \(direct,linked' - ',pin=../crates/libhello_1.0.0\).*', # relative, always fwd slash + ',path=../crates/libhello_1.0.0\).*', # relative, always fwd slash p.out, flags=re.S) # Check that unpinning the dependency works and now the dependency is show diff --git a/testsuite/tests/pin/recursive_local/test.py b/testsuite/tests/pin/recursive-local/test.py similarity index 100% rename from testsuite/tests/pin/recursive_local/test.py rename to testsuite/tests/pin/recursive-local/test.py diff --git a/testsuite/tests/pin/recursive-local/test.yaml b/testsuite/tests/pin/recursive-local/test.yaml new file mode 100644 index 00000000..872fc127 --- /dev/null +++ b/testsuite/tests/pin/recursive-local/test.yaml @@ -0,0 +1,3 @@ +driver: python-script +indexes: + basic_index: {} diff --git a/testsuite/tests/pin/recursive_remote/test.py b/testsuite/tests/pin/recursive-remote/test.py similarity index 100% rename from testsuite/tests/pin/recursive_remote/test.py rename to testsuite/tests/pin/recursive-remote/test.py diff --git a/testsuite/tests/pin/recursive-remote/test.yaml b/testsuite/tests/pin/recursive-remote/test.yaml new file mode 100644 index 00000000..872fc127 --- /dev/null +++ b/testsuite/tests/pin/recursive-remote/test.yaml @@ -0,0 +1,3 @@ +driver: python-script +indexes: + basic_index: {} -- 2.39.5