Skip to content

Commit

Permalink
New On_Demand action type (#847)
Browse files Browse the repository at this point in the history
* New On_Demand action for future-proofing

* Test for on-demand actions
  • Loading branch information
mosteo authored Sep 15, 2021
1 parent b923985 commit ff02df6
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 30 deletions.
65 changes: 51 additions & 14 deletions src/alire/alire-properties-actions-runners.adb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
with AAA.Enum_Tools;

package body Alire.Properties.Actions.Runners is

-------------
Expand All @@ -21,6 +23,9 @@ package body Alire.Properties.Actions.Runners is
if This.Working_Folder /= "" then
Table.Set (TOML_Keys.Action_Folder, +This.Working_Folder);
end if;
if This.Name /= "" then
Table.Set (TOML_Keys.Name, +This.Name);
end if;
Arr.Append (Table);
return Arr;
end To_TOML;
Expand All @@ -41,29 +46,58 @@ package body Alire.Properties.Actions.Runners is
-- Create_One --
----------------

function Create_One (Raw : TOML.TOML_Value)
function Create_One (Raw : TOML.TOML_Value; Index : Positive)
return Conditional.Properties
is
From : constant TOML_Adapters.Key_Queue :=
From_TOML.From.Descend (Raw, "action");
Kind : TOML_Value;
Command : TOML_Value;
Path : TOML_Value;
Used : Boolean;
From_TOML.From.Descend (Raw, "action #"
& Utils.Trim (Index'Image));
Kind : TOML_Value;
Command : TOML_Value;
Name : TOML_Value;
Has_Name : Boolean;
Path : TOML_Value;
Has_Path : Boolean;
Moment : Moments;

function Is_Valid is new AAA.Enum_Tools.Is_Valid (Moments);
begin
if not From.Pop (TOML_Keys.Action_Type, Kind) then
From.Checked_Error ("action type missing");
elsif not From.Pop (TOML_Keys.Action_Command, Command) then
From.Checked_Error ("action command missing");
end if;

Used := From.Pop (TOML_Keys.Action_Folder, Path);
-- The path key for an action is optional.
Has_Path := From.Pop (TOML_Keys.Action_Folder, Path);
-- The path key for an action is optional

Has_Name := From.Pop (TOML_Keys.Name, Name);
-- Name is optional but for custom actions

if Kind.Kind /= TOML_String
or else (Used and then Path.Kind /= TOML_String)
or else (Has_Path and then Path.Kind /= TOML_String)
then
From.Checked_Error ("actions type, and folder must be strings");
end if;

if not Is_Valid (TOML_Adapters.Adafy (Kind.As_String)) then
From.Checked_Error ("action type is invalid: " & Kind.As_String);
else
Moment := Moments'Value (TOML_Adapters.Adafy (Kind.As_String));
end if;

if Moment = On_Demand and then not Has_Name then
From.Checked_Error ("on-demand actions require a name");
end if;

if Has_Name and then
(Name.Kind /= TOML_String or else Name.As_String not in Action_Name)
then
From.Checked_Error ("actions type and folder must be strings");
From.Checked_Error
("action name must be a string made of "
& "'a' .. 'z', '0' .. '9', '-', starting with a letter and not "
& "ending with a dash nor containing consecutive dashes"
& ASCII.LF & "Offending name is: " & Name.As_String);
end if;

if Command.Kind /= TOML_Array
Expand All @@ -81,20 +115,23 @@ package body Alire.Properties.Actions.Runners is
return New_Value
(New_Run
(Moment =>
Moments'Value (TOML_Adapters.Adafy (Kind.As_String)),
Moment,

Name =>
(if Has_Name then Name.As_String else ""),

Relative_Command_Line =>
TOML_Adapters.To_Vector (TOML_Adapters.To_Array (Command)),

Working_Folder =>
(if Used then Path.As_String else ".")));
(if Has_Path then Path.As_String else ".")));
end Create_One;

Raw : constant TOML_Value := From.Pop;

begin
if Raw.Kind = TOML_Table then
return Create_One (Raw);
return Create_One (Raw, 1);
end if;

-- It should be an array that we we'll load one by one:
Expand All @@ -110,7 +147,7 @@ package body Alire.Properties.Actions.Runners is

return Props : Conditional.Properties do
for I in 1 .. Raw.Length loop
Props := Props and Create_One (Raw.Item (I));
Props := Props and Create_One (Raw.Item (I), I);
end loop;
end return;
end From_TOML;
Expand Down
48 changes: 33 additions & 15 deletions src/alire/alire-properties-actions-runners.ads
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ package Alire.Properties.Actions.Runners with Preelaborate is
type Run (<>) is new Action with private;
-- Encapsulates the execution of an external command

function New_Run (Moment : Moments;
Relative_Command_Line : Utils.String_Vector;
Working_Folder : Any_Path)
return Run;
-- Working folder will be entered for execution
-- Relative command-line must consider being in working folder

function Command_Line (This : Run) return Utils.String_Vector;
function Working_Folder (This : Run) return String;

Expand All @@ -25,15 +18,34 @@ package Alire.Properties.Actions.Runners with Preelaborate is

private

type Run (Moment : Moments; Folder_Len : Natural)
subtype Action_Name is String with Dynamic_Predicate =>
(for all Char of Action_Name =>
Char in 'a' .. 'z' | '0' .. '9' | '-')
and then Action_Name'Length > 1
and then Action_Name (Action_Name'First) in 'a' .. 'z'
and then Action_Name (Action_Name'Last) /= '-'
and then (for all I in Action_Name'First .. Action_Name'Last - 1 =>
(if Action_Name (I) = '-'
then Action_Name (I + 1) /= '-'));

type Run (Moment : Moments; Folder_Len : Natural; Name_Len : Natural)
is new Action (Moment) with record
Name : String (1 .. Name_Len);
-- Optional, except for custom actions, which require a name.

Relative_Command_Line : Utils.String_Vector;
Working_Folder : Any_Path (1 .. Folder_Len);
end record;
end record
with Type_Invariant =>
(Name = "" or else Name in Action_Name)
and then
(if Moment = On_Demand then Name /= "");

overriding
function Image (This : Run) return String
is (Utils.To_Mixed_Case (This.Moment'Img) & " run: <project>" &
is (Utils.To_Mixed_Case (This.Moment'Img)
& (if This.Name /= "" then " (" & This.Name & ")" else "")
& " run: ${CRATE_DIR}" &
(if This.Working_Folder /= "" then "/" else "") &
This.Working_Folder & "/" & This.Relative_Command_Line.Flatten);

Expand All @@ -44,14 +56,20 @@ private
This.Working_Folder & "/" & This.Relative_Command_Line.Flatten);

function New_Run (Moment : Moments;
Name : String;
Relative_Command_Line : Utils.String_Vector;
Working_Folder : Any_Path)
return Run
return Run'Class
-- Working folder will be entered for execution
-- Relative command-line must consider being in working folder
is
(Moment,
Working_Folder'Length,
Relative_Command_Line,
Utils.To_Native (Working_Folder));
(Run'
(Moment,
Working_Folder'Length,
Name'Length,
Name,
Relative_Command_Line,
Utils.To_Native (Working_Folder)));

function Command_Line (This : Run) return Utils.String_Vector
is (This.Relative_Command_Line);
Expand Down
3 changes: 2 additions & 1 deletion src/alire/alire-properties-actions.ads
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ package Alire.Properties.Actions with Preelaborate is
Post_Fetch, -- After being downloaded
Pre_Build, -- Before being compiled as the working release
Post_Build, -- After being compiled as the working release
Test -- On demand for testing of releases
Test, -- On demand for testing of releases
On_Demand -- On demand from command-line
);

type Action (<>) is abstract new Properties.Property with private;
Expand Down
17 changes: 17 additions & 0 deletions testsuite/drivers/alr.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,20 @@ def alr_with(dep="", path="", url="", commit="", branch="",
args += ["--branch", f"{branch}"]

return run_alr(*args, force=force)


def add_action(type, command, name=""):
"""
Add an action to the manifest in the current directory.
:param str type: "pre-build", etc
:param list command: array/list of strings that make up the command
"""
if not os.path.isfile(alr_manifest()):
raise RuntimeError("Manifest not found")

with open(alr_manifest(), "a") as manifest:
manifest.write("[[actions]]\n")
manifest.write(f"type = '{type}'\n")
manifest.write(f"command = {command}\n")
if name != "":
manifest.write(f"name = '{name}'\n")
54 changes: 54 additions & 0 deletions testsuite/tests/index/custom-action/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
Check loading of custom actions and related changes
"""

from drivers.alr import run_alr, init_local_crate, alr_manifest, add_action
from drivers.asserts import assert_eq, assert_match
from os import chdir
from shutil import rmtree

import re


def bad_action_check(type, command, name, error_re):
# Test in new crate as the manifest is going to be broken
init_local_crate("abc")
add_action(type=type, command=command, name=name)
p = run_alr("show", complain_on_error=False)
assert p.status != 0, "Unexpected success"
assert_match(error_re, p.out)
chdir("..")
rmtree("abc")


init_local_crate()

# Add a proper custom action and verify its loading
add_action(type="on-demand", name="my-action", command=["ls"])
p = run_alr("show")
assert_match(".*" + re.escape("On_Demand (my-action) run: ${CRATE_DIR}/./ls"),
p.out)

# Verify that regular action can also have a name
add_action(type="post-fetch", name="action-2", command=["ls"])
p = run_alr("show")
assert_match(".*" + re.escape("Post_Fetch (action-2) run: ${CRATE_DIR}/./ls"),
p.out)

# Add an on-demand action without name and see it fails
bad_action_check(type="on-demand", command=["ls"], name="",
error_re=".*on-demand actions require a name")

# Bad names
bad_action_check(type="on-demand", command=["ls"], name="2nd-action",
error_re=".*Offending name is")
bad_action_check(type="on-demand", command=["ls"], name="bad--action",
error_re=".*Offending name is")
bad_action_check(type="on-demand", command=["ls"], name="-action",
error_re=".*Offending name is")
bad_action_check(type="on-demand", command=["ls"], name="action-",
error_re=".*Offending name is")
bad_action_check(type="on-demand", command=["ls"], name="x",
error_re=".*Offending name is")

print('SUCCESS')
3 changes: 3 additions & 0 deletions testsuite/tests/index/custom-action/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
driver: python-script
indexes:
basic_index: {}

0 comments on commit ff02df6

Please sign in to comment.