diff --git a/APLSource/APLTreeUtils2.aplc b/APLSource/APLTreeUtils2.aplc index b406d92b9..107e6b01f 100644 --- a/APLSource/APLTreeUtils2.aplc +++ b/APLSource/APLTreeUtils2.aplc @@ -3,13 +3,13 @@ ⍝ While `APLTreeUtils` was a namespace scipt designed to be included into pretty much every member of the ⍝ APLTree library, `APLTreeUtils2` is a class with shared methods. You are supposed to call those methods. ⍝ This has some major advantageous over the old approach: -⍝ * It's possible to add new functions to `APLTreeUtils`. With the old approach there was always the possibility +⍝ * It's possible to add new functions to `APLTreeUtils2`. With the old approach there was always the possibility ⍝ of a name clash, so adding new function was practically impossible. ⍝ * The sequence of fixing does not matter (though with lazy fixing that should not be an issue anymore anyway, ⍝ but at the time of writing it still is). ⍝ * Over the years we have seen rare `⎕IO` and `⎕ML` issues with `:Include`. We just avoid the possibility now.\\ ⍝ For a list with the precise differences between `APLTreeUtils` and `APLTreeUtils2` see the project ReadMe on -⍝ GitHub. Note that there are many. Most importantly, `APLTreeUtils2` requires Dyalog 18.0. +⍝ GitHub. Note that there are many. Most importantly, `APLTreeUtils2` requires at least Dyalog 18.0. ⍝ Kai Jaeger\\ ⍝ Homepage: @@ -17,11 +17,17 @@ ∇ r←Version :Access Public Shared - r←'APLTreeUtils2' '1.2.1+62' '2023-10-05' + r←'APLTreeUtils2' '1.3.0+62' '2023-11-05' ∇ ∇ History - ⍝ * 1.2.0 from 2023-10-05 + ⍝ * 1.3.0 from 2023-11-05 + ⍝ * `ToNum` now accepts a left argument which is returned in case the right argument is an empty vector. + ⍝ * New methods: `BitsToInt` and `IntToBits` + ⍝ * 1.2.2 from 2023-10-09 + ⍝ * Minor fix in CreateUUID + ⍝ * License corrected + ⍝ * 1.2.1 from 2023-10-05 ⍝ * Bug fix in `Create_UUID` ⍝ * 1.2.0 from 2023-05-08 ⍝ * Bug fix: version number corrected @@ -67,10 +73,16 @@ r←0 2∊⍨10|⎕DR y ∇ - ∇ r←ToNum y - ⍝ Transforms `y` into number(s) + ∇ r←{default}ToNum y + ⍝ Transforms `y` into number(s).\\ + ⍝ In case `y` is empty `default` is returned which defaults to ⍬ (empty numeric vector). :Access Public Shared - r←⊃(//)⎕VFI y + :If 0=≢y + :AndIf 0<⎕NC'default' + r←default + :Else + r←⊃(//)⎕VFI y + :EndIf ∇ ∇ r←IsScripted y @@ -302,7 +314,7 @@ ∇ r←Create_UUID ⍝ Produces a UUID :Access Public Shared - r←'-'@(4+5×⍳4)⊢(⎕D,⎕C ⎕A)[4{9+|⍵}@20⊢5@15?36⍴16] + r←'-'@(4+5×⍳4)⊢(⎕D,⎕C ⎕A)[4(9+|)@20⊢5@15?36⍴16] ∇ ∇ r←{type}Base64 txt;charset @@ -420,4 +432,18 @@ cats''∘four¨24 part 8 bits ⍵ } + ∇ r←BitsToInt y + ⍝ Unsigned + :Access Public Shared + r←(32⍴2)⊥⌽32↑y + ∇ + + + ∇ r←IntToBits y + ⍝ Unsigned + :Access Public Shared + r←⌽(32⍴2)⊤y + ∇ + + :EndClass diff --git a/APLSource/Admin/CopyScriptsFromPackagesIn.aplf b/APLSource/Admin/CopyScriptsFromPackagesIn.aplf new file mode 100644 index 000000000..9b855eb55 --- /dev/null +++ b/APLSource/Admin/CopyScriptsFromPackagesIn.aplf @@ -0,0 +1,24 @@ + {r}←{targetFolder}CopyScriptsFromPackagesIn path2Packages;packages;i;package;packageID;packageName;fullName;F +⍝ Copy scripts installed in `path2Packages` over to `targetFolder` which defaults to APLSource/ +⍝ Note that packages that are not just scripts are NOT handled. CommTools and FilesAndDirs are examples. +⍝ This function is called by `CheckForLaterVersion`. + r←0 + F←##.FilesAndDirs + targetFolder←{0<⎕NC ⍵:⍎⍵ ⋄ './APLSource/'}'targetFolder' + packages←{⍵.packageID}##.Registry.APLfromJSON⊃⎕NGET path2Packages,'/apl-buildlist.json' + :For i package :InEach (⍳≢packages)packages + packageID←⊃,/1↓⎕NPARTS package + packageName←{{⍵↑⍨¯1+⍵⍳'-'}⍵↓⍨⍵⍳'-'}packageID + fullName←⊃F.Dir targetFolder,'/',packageName,'.*' + :If 0=≢fullName + :If F.IsDir path2Packages,'/',packageID,'/APLSource' + r∨←(targetFolder,'/')(⎕NCOPY⍠('IfExists' 'ReplaceIfNewer')('Wildcard' 1)F.ExecNfunction)path2Packages,'/',packageID,'/APLSource/*' + :Else + r∨←(targetFolder,'/')(⎕NCOPY⍠('IfExists' 'ReplaceIfNewer')('Wildcard' 1)F.ExecNfunction)path2Packages,'/',packageID,'/',packageName,'*' + :EndIf + :Else ⍝ Simple files + fullName←⊃F.Dir path2Packages,'/',package,'/',packageName,'.*' + r∨←(targetFolder,'/')(⎕NCOPY⍠('IfExists' 'ReplaceIfNewer')F.ExecNfunction)fullName + :EndIf + :EndFor +⍝Done diff --git a/APLSource/Admin/HandleRunFunction.aplf b/APLSource/Admin/HandleRunFunction.aplf new file mode 100644 index 000000000..b582f2cff --- /dev/null +++ b/APLSource/Admin/HandleRunFunction.aplf @@ -0,0 +1,9 @@ + {r}←HandleRunFunction dummy;myZip;zipFilename +⍝ Copies the "Run.aplf" function and also creates a ZIP file for it + r←0 + './Dist/'⎕NCOPY⍠('IfExists' 'Replace')⊣'Run.aplf' + zipFilename←'./Dist/Run.zip' + myZip←⎕NEW ##.ZipArchive(,⊂zipFilename) + F.PWD myZip.Add'Run.aplf' + myZip.Dispose +⍝Done diff --git a/APLSource/Admin/Make.aplf b/APLSource/Admin/Make.aplf index 13385db78..9acf19dc8 100644 --- a/APLSource/Admin/Make.aplf +++ b/APLSource/Admin/Make.aplf @@ -26,7 +26,7 @@ :EndIf :EndIf - path←(2 ⎕NQ #'GetEnvironment' 'USERPROFILE'),'\Documents\Dyalog APL Files\StartupSession\CiderTatin\Tatin' + path←(2 ⎕NQ #'GetEnvironment' 'USERPROFILE'),'\Documents\Dyalog APL Files\SessionExtensions\CiderTatin\Tatin' :If F.IsDir path ⍝ Does a folder Tatin/ live in the version-agnostic folder? :AndIf F.IsFile path,'/Tatin.dyalog' ⍝ Does the Tatin user command script live there? file1←path,'/Tatin.dyalog' @@ -89,6 +89,7 @@ 0 0⍴{⎕←⍵}⍣(~batchFlag)⊣' Process started for "making" the Tatin Server...' {⍵.HasExited:shy←0 ⋄ _←⎕DL 0.2 ⋄ ∇ ⍵}P_ 'The process exited with an error code'Assert 0=P_.Proc.ExitCode + HandleRunFunction ⍬ UpdateTestServerAssets ⍬ {}⎕SE.Link.Refresh'#.Tatin' :If CopyTatinClientToUserDocuments batchFlag diff --git a/APLSource/Client/Add2Config.aplf b/APLSource/Client/Add2Config.aplf index b12d99b74..884141637 100644 --- a/APLSource/Client/Add2Config.aplf +++ b/APLSource/Client/Add2Config.aplf @@ -4,7 +4,7 @@ cfg←ReadPackageConfigFile path ⍎'cfg.',name,'←value' :Trap Reg.ErrorNo - WritePackageConfigFile path cfg + 0 WritePackageConfigFile path cfg :Else qdmx←⎕DMX msg←qdmx.EM diff --git a/APLSource/Client/AddTatinVersionNumber.aplf b/APLSource/Client/AddTatinVersionNumber.aplf new file mode 100644 index 000000000..000d435e6 --- /dev/null +++ b/APLSource/Client/AddTatinVersionNumber.aplf @@ -0,0 +1,5 @@ + pkgCfg←AddTatinVersionNumber pkgCfg +⍝ Inject or update the tatin version number the package configuration was created / changed by. +⍝ `pkgCfg` is a namespace with values + pkgCfg.tatin_version←{⍵↑⍨¯1+⍵⍳'+'}2⊃Reg.Version +⍝Done diff --git a/APLSource/Client/BuildPackage.aplf b/APLSource/Client/BuildPackage.aplf index f8d148baf..47dd4aaa5 100644 --- a/APLSource/Client/BuildPackage.aplf +++ b/APLSource/Client/BuildPackage.aplf @@ -16,6 +16,9 @@ ⍝ If no build number is specified the original build number is taken and bumped. ⍝ Otherwise the given version and build number rule (the build number is not bumped). ⍝ The package config file is updated as a side effect. + :If 'Win'≡##.APLTreeUtils2.GetOperatingSystem ⍬ + 'On Windows, Tatin requires .NET Framework to be available for building packages'Assert 0=##.APLTreeUtils2.ToNum 2 ⎕NQ #'GetEnvironment' 'DYALOG_NETCORE' + :EndIf parms.projectPath←RemoveFileProtocol parms.projectPath parms.projectPath←⊃1 ⎕NPARTS ##.FilesAndDirs.AddTrailingSep parms.projectPath cfg_←ReadPackageConfigFile parms.projectPath @@ -43,7 +46,7 @@ :If 0<≢cfg.assets cfg.assets←{1=≢⍵:⊃⍵ ⋄ 1↓⊃,/',',¨⍵}ProcessAssetsProp parms.projectPath cfg.assets :EndIf - ⍝ CreateAPIfromCFG cfg ⍝TODO⍝ Should we implement this?! + ⍝ CreateAPIfromCFG cfg ⍝TODO⍝ Should we implement this?! Yes we should! :If ≢/{⎕JSON⍠('Dialect' 'JSON5')⊣⍵}¨cfg cfg_ WritePackageConfigFile parms.projectPath cfg :EndIf diff --git a/APLSource/Client/CheckBuildList.aplf b/APLSource/Client/CheckBuildList.aplf index 88c0fbbdc..7cefa487b 100644 --- a/APLSource/Client/CheckBuildList.aplf +++ b/APLSource/Client/CheckBuildList.aplf @@ -14,12 +14,12 @@ filename1←folder,Reg.BuildListFilename filename2←folder,Reg.DependenciesFilename :If ~force - ⍝ Tatin always writes the dependency file first and the build list next to file, - ⍝ yet the timestamps in the milliseconds sometimes pretend it to be the other way round. - ⍝ Therefore we add one second to make sure that we only re-build the build list when the - ⍝ dependency file has really been modified by a user with an editor. - ts1←1 GetFileTime filename1 - ts2←GetFileTime filename2 + ⍝ Tatin always writes first the dependency file and only then the build list to the disk, + ⍝ yet the timestamps in the milliseconds sometimes pretend it to be the other way round (Windows). + ⍝ Therefore we add one second for the build list to make sure that we only re-build the build list + ⍝ when the dependency file has really been modified by a user with an editor. + ts1←GetFileTime filename1 + ts2←1 GetFileTime filename2 :EndIf tree←ReadBuildList filename1 deps←ReadDependencyList filename2 diff --git a/APLSource/Client/CreateAPIfromCFG.aplf b/APLSource/Client/CreateAPIfromCFG.aplf index 5a7ec162d..671c3b142 100644 --- a/APLSource/Client/CreateAPIfromCFG.aplf +++ b/APLSource/Client/CreateAPIfromCFG.aplf @@ -6,7 +6,7 @@ ⍝ We need to establish a way that allows indentifying the namespace within a project that is going to be the package, if any! ⍝ In my (Kai's) case it is by convention: A project Foo always has a namepsace Foo.Foo that is going to be the package. ⍝ However, other people may have different ideas, including one where the project is actually identical with the package. - ∘∘∘ + ∘∘∘ ⍝TODO⍝ r←⍬ api←'##.',cfg.name,'.',cfg.api :If ~(⎕NC⊂api)∊9.4 9.5 ⍝ Not for classes and interfaces diff --git a/APLSource/Client/InitPackageConfig.aplf b/APLSource/Client/InitPackageConfig.aplf index d819affb8..2f1b3fdc8 100644 --- a/APLSource/Client/InitPackageConfig.aplf +++ b/APLSource/Client/InitPackageConfig.aplf @@ -61,6 +61,7 @@ {}cfg.⎕EX'wx' ⍝ Not anymore (since 0.61.0) list←(' '~¨⍨↓cfg.⎕NL 2)~' '~¨⍨↓'_'cfg.⎕NL 2 ⍝ user properties start with "_" by convention bool←list∊varsList,('uri' 'date') ⍝ These are injected by the server + bool∨←list∊'tatin_version' '' :If ∨/~bool msg←'Invalid propert',((1+1<+/~bool)⊃'y' 'ies'),' in config namespace: ',⊃{⍺,',',⍵}/((~bool)/list) msg Assert 0 diff --git a/APLSource/Client/InitialisePackage.aplf b/APLSource/Client/InitialisePackage.aplf index 3bfe36cd5..f7cbadc35 100644 --- a/APLSource/Client/InitialisePackage.aplf +++ b/APLSource/Client/InitialisePackage.aplf @@ -10,7 +10,7 @@ folder←⍵ _←F.MkDir folder ('file "',CFG_Name,'" already exists')Assert 0=F.Exists folder,'/',CFG_Name:1 - cfg←1 WritePackageConfigFile folder cfg + cfg←0 WritePackageConfigFile folder cfg fn←folder,'/',cfg.source _←{F.MkDir ⍵}⍣(0=F.Exists fn)⊣fn 1:cfg diff --git a/APLSource/Client/LoadDependencies.aplf b/APLSource/Client/LoadDependencies.aplf index 8caf67d49..f8ee6d8fa 100644 --- a/APLSource/Client/LoadDependencies.aplf +++ b/APLSource/Client/LoadDependencies.aplf @@ -21,7 +21,7 @@ :If isUC←'[myucmds]'{⍺≡⎕C(≢⍺)↑⍵}installFolder installFolder←GetMyUCMDsFolder(≢'[myucmds]')↓installFolder :EndIf - ('Folder does not exist:',⍕installFolder)Assert ⎕NEXISTS installFolder + ('Folder does not exist: ',⍕installFolder)Assert ⎕NEXISTS installFolder :If 0=≢targetSpace targetSpace←(1+isUC)⊃'#' '⎕SE' :EndIf diff --git a/APLSource/Client/LoadPackages.aplf b/APLSource/Client/LoadPackages.aplf index 6bd966df5..d562c473d 100644 --- a/APLSource/Client/LoadPackages.aplf +++ b/APLSource/Client/LoadPackages.aplf @@ -49,7 +49,7 @@ :EndTrap →(0=≢paths)/0 :Trap Reg.ErrorNo - loaded←LoadDependencies tempFolder targetSpace + r←≢loaded←LoadDependencies tempFolder targetSpace :Else qdmx←⎕DMX ⎕DL 0.01 @@ -58,7 +58,6 @@ (⊃{0=≢⍺:⍵ ⋄ ⍺}/qdmx.(Message EM))⎕SIGNAL{⍺=0:⍵ ⋄ ⍺}/qdmx.(ENX EN) :EndTrap ⎕DL 0.01 - r←≢F.ListDirs tempFolder,'/' :If ~MyUserSettings.caching list←F.ListDirs tempFolder :If 0=≢list ⍝ Anything? At all?! diff --git a/APLSource/Client/WritePackageConfigFile.aplf b/APLSource/Client/WritePackageConfigFile.aplf index 128941304..a16a3a632 100644 --- a/APLSource/Client/WritePackageConfigFile.aplf +++ b/APLSource/Client/WritePackageConfigFile.aplf @@ -1,34 +1,34 @@ - {cfg}←{initializeFlag}WritePackageConfigFile(path cfg);qdmx;cfg_file;tmp -⍝ Write package config file to disk. -⍝ By default several properties are checked and an error is thrown in case something is not quite right, -⍝ This can be changed by passing a 1 as ⍺. In this case two checks are NOT perfortmed: -⍝ * "source" might be empty -⍝ * "assets" is ignored, and therefore might not exist - initializeFlag←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'initializeFlag' +{cfg}←{check}WritePackageConfigFile(path cfg);qdmx;cfg_file;tmp +⍝ Write package config file to disk.\\ +⍝ By default several properties are checked and an error is thrown in case something is not quite right.\\ +⍝ This can be changed by passing a 0 as ⍺. In this case the checks are **not** performed. + check←{0<⎕NC ⍵:⍎⍵ ⋄ 1}'check' cfg←InitPackageConfig cfg - :If 0=initializeFlag + :If check '"source" must not be empty'Assert 0<≢cfg.source :EndIf '"source" must be simple'Assert 1=|≡cfg.source '"source" must be character'Assert' '=1↑0⍴cfg.source path←⊃1 ⎕NPARTS path,'/' cfg_file←path,CFG_Name - {}ValidateVersion cfg.version - cfg←ValidatePackageFiles cfg - cfg←initializeFlag ValidateOSprops cfg - cfg←ValidateTags cfg - cfg←ValidateAPI cfg - cfg←ValidateDocumentation cfg - :If 0=≢cfg.license - cfg.license←MyUserSettings.license + :If check + {}ValidateVersion cfg.version + cfg←ValidatePackageFiles cfg + cfg←check ValidateOSprops cfg + cfg←ValidateTags cfg + cfg←ValidateAPI cfg + cfg←ValidateDocumentation cfg + :If 0=≢cfg.license + cfg.license←MyUserSettings.license + :EndIf + cfg←ValidateMaintainer cfg + cfg←path ValidateFiles cfg + ⍝ Note that we cannot validate the license, that's up to the server. + 'Invalid: "assets" - not a path'Assert~','∊cfg.assets + cfg.assets←path PolishAssetsPaths cfg.assets + path ValidateAssets cfg.assets + {}path CheckAssetsExist cfg.assets :EndIf - cfg←ValidateMaintainer cfg - cfg←path ValidateFiles cfg - ⍝ Note that we cannot validate the license, that's up to the server. - 'Invalid: "assets" - not a path'Assert~','∊cfg.assets - cfg.assets←path PolishAssetsPaths cfg.assets - path ValidateAssets cfg.assets - {}path CheckAssetsExist cfg.assets 'Invalid package name'Assert 0=(⎕NS''){0=≢⍵:0 ⋄ ⍺.⎕NC ⍵}cfg.name tmp←cfg_file,'.tmp' :Trap 0 diff --git a/APLSource/CodeCoverage.aplc b/APLSource/CodeCoverage.aplc index 986812b94..3bc13c802 100644 --- a/APLSource/CodeCoverage.aplc +++ b/APLSource/CodeCoverage.aplc @@ -54,11 +54,13 @@ ∇ r←Version :Access Public Shared - r←'CodeCoverage' '0.10.2+52' '2023-04-09' + r←'CodeCoverage' '0.10.3+52' '2023-06-23' ∇ ∇ History :Access Public Shared + ⍝ * 0.10.3 from 2023-04-23 + ⍝ * Bug fix: Instance names could not be assigned to "ignore" ⍝ * 0.10.2 from 2023-04-09 ⍝ * On non-Windows platforms the file access matrix needs setting to avoid problems ⍝ * 0.10.1 from 2023-04-08 @@ -137,7 +139,7 @@ ∇ r←Get r←','(≠⊆⊢),_ignore ∇ - ∇ set value;v;b;v2;noOf + ∇ set value;v;noOf :If 0=noOf←+/∧\1=≡¨value.NewValue v←∊value.NewValue :Else @@ -145,10 +147,6 @@ :EndIf 'Must be character vector'⎕SIGNAL 11/⍨' '≠1↑0⍴v ('Has invalid characters',b/v)⎕SIGNAL 11/⍨∨/b←~v∊APL_Chars,',⎕#.' - v2←','(≠⊆⊢),v - :If ∨/~b←0<⎕NC⊃v2 - 11 ⎕SIGNAL⍨'Unknown names: ',⊃{⍺,',',⍵}/(~b)/v2 - :EndIf _ignore←v ∇ :EndProperty diff --git a/APLSource/CommTools/ADOC_Doc.apla b/APLSource/CommTools/ADOC_Doc.apla new file mode 100644 index 000000000..daace2093 --- /dev/null +++ b/APLSource/CommTools/ADOC_Doc.apla @@ -0,0 +1,364 @@ +( + '' + '## Overview' + '' + 'This package comes with functions and operators that allow you to interrogate the user, and make her...' + '' + '* answer simple questions like "Are you sure?" or "Do you really want to delete this file?" (`YesOrNo`)' + '* select an item (or several items) from a list of items, or quit (`Select`)' + '* pause and read a message before continuing (`Pause`)' + '* enter a character vector in response to something like "Enter your name:" (`AskForText`)' + '* enter a number in response to something like "How many copies? " (`AskForNumber`)' + ' ' + 'This is useful for interacting with the user in the session, something that user commands do' + 'quite often.' + '' + 'Note that although `AskForNumber` and `AskForText` are operators rather than functions, this document' + 'will refer to them as functions except when they themselves are discussed.' + '' + '## Interrupting the functions' + '' + 'When a user is prompted for input, due to a long-standing bug in Dyalog you cannot interrupt with a weak ' + 'or a strong interrupt. Answering the question or selecting an item and then trying to interrupt the ' + 'code does not work well on modern machines because they are just too fast.' + '' + 'That''s why all functions allow you to enter `∘∘∘`, which makes the functions run on a stop vector that ' + 'is dynamically set for just this purpose. ' + '' + '## Automated responses' + '' + 'Test cases quite often require certain decisions and choices to be made. In such cases the presence ' + 'of a human in front of the screen is required. The automation feature is designed to allow such tests ' + 'to run without a human.' + '' + 'All functions can be integrated into tests so that no user is required to answer any questions or' + 'select items. This is achieved by providing data on global variables that do not normally exist.' + '' + '#### Dynamically generated questions and captions' + ' ' + 'Sometimes you need to create a question (`YesOrNo`) or a caption (`Select`) dynamically such as ' + '"Select action for file /foo/boo/this-will-change.txt"' + '' + 'In such cases, for automation, just specify only the leading part of the question or caption which won''t be ' + 'generated dynamically. In the example just specify "Select action for file". If a complete match cannot ' + 'be found, `Select` or `YesAndNo` will check if the _beginning_ of the caption/question matches ' + 'the given text, so it would still find a match.' + '' + 'However, you may find it more convenient to use aliases.' + '' + '### Using aliases' + '' + 'Aliases were introduced in version 1.2. By default, an alias is represented by the `@` character.' + 'As of version 1.3.0 you can choose a different one by assigning it to the global variable `AliasChar`.' + 'The only reason for doing this is when you need to have the `@` glyph in a question or caption. Even then' + 'more often than not you don''t need to worry about this because aliases can only be defined until the first' + 'space character of the first Line Feed character.' + '' + 'Anything up to and including the `@` will be taken as alias as long as it does not contain LC, CR or a ' + 'space, meaning that this part will not be displayed as a question or message, but can be used as a matching ' + 'string, again including the (trailing) `AliasChar` character. ' + '' + 'For example:' + '' + '```' + '''MyAlias@My Question''' + '```' + '' + 'This defines an alias. This on the other hand doesn''t:' + '' + '```' + '''The @ character is used...''' + '```' + '' + 'Note that while aliases may seem like a perfect solution, they can also reduce readability. If you create a ' + 'number of responses in advance at the start of your application/user command this can become a bit confusing.' + '' + 'There are two ways to avoid this:' + '' + '1. Use a meaningful expression as an alias rather than, for example, just `1@`' + '' + '2. You can add something _after_ the `@` glyph: This will be ignored and can therefore be used as a comment' + '' + ' It could be a hint or the name of the function that asks the question, define context etc.' + ' ' + '### Cleaning up' + '' + 'Of course there is nothing wrong with deleting the global variables yourself like this:' + '' + '```' + '⎕EX ''CommTools.Select_Choices''' + ' ⍝ ... etc''' + '```' + '' + 'However, you can also just call the `Cleanup` function which will do the job for you.' + '' + 'This has some advantages:' + '' + '* If `CommTools` is extended in the future, `Cleanup` may do more than that, so calling it is safer than trying ' + ' to do the job yourself. ' + '* You don''t need to know the names of the global variables, and therefore you can''t get them wrong either ' + '' + 'The downside of `Cleanup` is that it deletes _all_ global variables used for automation, and there ' + 'may be scenarios where you do not want this. Then you have to delete what you don''t need anymore.' + ' ' + '## The functions and operators' + '' + '' + '### The function "YesOrNo"' + '' + 'Imagine asking this question:' + ' ' + '```' + 'CommTools.YesOrNo ''Are you sure you want to delete this file?''' + '```' + '' + 'To answer this question automatically create a global variable `YesOrNo_Answers` as a matrix' + 'with two columns:' + '' + '| `[;1]` | Contains the question to be answered automatically.' + '| `[;2]` | The answer to be given, usually either "y" or "n".' + '' + 'So in our case we specify:' + '' + '```' + 'CommTools.YesOrNo_Answers←1 2⍴''Are you sure you want to delete this file?'' ''y''' + '``` ' + '' + 'When the question is asked:' + ' ' + '```' + 'CommTools.YesOrNo ''Are you sure you want to delete this file?''' + '```' + '' + '`YesOrNo` would do the following:' + '' + '1. Check if there is a global variable `YesOrNo_Answers` around.' + '2. If there is, it would try to match the question to any row in the first column of `YesOrNo_Answers` ' + '3. If there is a match, it would return the value in the second column of the identified row' + '' + 'Notes:' + '' + '* Leading spaces in the question are removed' + '* If multiple entries in `YesOrNo_Answers` match the question an error is thrown' + '* If the second column is empty `YesOrNo` would return the default answer given by the' + ' left argument: 1="Y", 0="N"' + ' ' + ' If there is no left argument provided in such a case `YesOrNo` would throw an error' + '* If no row in `YesOrNo_Answers` matches the question it would ask the question as usual' + '' + '#### Dynamic questions' + ' ' + 'Sometimes you need to ask dynamically created questions like "Are you sure you want to delete the file /foo/boo/this-will-change.txt?"' + '' + 'In this case just specify "Are you sure you want to delete the file" as the text to match: if there is no complete match, `YesOrNo` ' + 'will start searching for questions that _begin_ with the given text, so it will still find a match. ' + '' + '#### Multiple line questions ' + '' + '`YesNorNo` accepts nested vectors as questions as well as a character vector with `⎕UCS 10` in it. (Note that a nested vector' + 'is internally converted to a simple character vector with `⎕UCS 10` (Line Feed) in it)' + '' + 'If the two searches outlined above do not produce a match `YesNorNo` would isolate the last bit of the question ' + 'and check it for a complete match.' + '' + 'This is best explained with an example:' + '' + '```' + ' q←⊂''file1''' + ' q,←⊂''file1''' + ' q,←⊂''Sure you wanna delete these?''' + ' CommTools.YesOrNo_Answers←1 2⍴ (3⊃q) ''y''' + ' ⎕←CommTools.YesOrNo q' + ('y'⋄) + '```' + '' + '### The "Select" function ' + '' + 'Imagine you want to present a list with three items, and you want the second one to be selected.' + '' + 'This can be achieved by creating a global variable `Select_Choices` as a matrix with two' + 'columns:' + '' + '| `[;1]` | Contains the caption of the list.' + '| `[;2]` | The answer to be given, so usually either an integer pointing to the item selected,<
>or a vector of integers (in case "a" was entered) or `⍬` (in case "q" was entered).' + '' + '`Select` would do the following:' + '' + '1. Check whether there is a global variable `Select_Choices` around' + '2. If so it would try to match the caption to any row in the first column of `Select_Choices` ' + '3. If there is a match it would return the value in the second column of the identified row' + '' + 'Notes:' + '' + '* Leading spaces in choices are removed' + '* If multiple entries in `Select_Choices` match the caption an error is thrown' + '* If no row in `Select_Choices` matches the caption it would present the list for selection ' + '' + 'So in our case we specify:' + '' + '```' + ' l←⊂''First''' + ' l,←⊂''Second''' + ' l,←⊂''Third''' + ' caption←''Please select hot item:''' + ' CommTools.Select_Choices←1 2⍴caption 2' + ' ⎕←caption CommTools.Select l ' + ('2'⋄) + '``` ' + '' + 'For all items:' + '' + '``` ' + ' CommTools.Select_Choices←1 2⍴caption ''a''' + ' ⎕←caption CommTools.Select l ' + '1 2 3' + '``` ' + '' + 'For no items ("quit"):' + '' + '``` ' + ' CommTools.Select_Choices←1 2⍴caption ''q''' + ' ⎕←⍬≡caption CommTools.Select l ' + ('1'⋄) + '``` ' + '' + 'In case the list items are not only created dynamically but they are also complex and a table it might be difficult or even ' + 'impossible to construct a matching line, for example because you don''t know upfront the number of spaces between columns.' + '' + 'In such a case use the `*` glyph as a wildcard character: it tells `Select` to match only the beginning of a line.' + '' + '#### Specifying a selection' + ' ' + 'If you need a specific selection to be returned but you don''t know the item number in advance because the list is created ' + 'dynamically, you can specify the selection itself instead of an integer.' + '' + 'For example:' + '' + '```' + ' CommTools.Select_Choices←1 2⍴caption ''Second''' + ' ⎕←caption CommTools.Select l' + '2 ' + '```' + '' + '### The "Pause" function ' + '' + 'The purpose of this function is to print a message to `⎕SE`, possibly with line feed characters (`⎕UCS 10`) in them, ' + 'and tell the user to press to continue.' + '' + 'As with the other functions you can interrupt `Pause` by typing `∘∘∘`.' + '' + 'If you don''t want the function to pause at all, create a global variable `NoPause` and assign a 1 to it.' + '' + 'If you want to pause in some but not all instances you can assign one or more `msg` to `NoPause`. ' + 'The function will try to match the string(s) on `NoPause` with the message, and if it''s a full or partial match it won''t pause, otherwise it will.' + '' + '### Ask for a number' + '' + 'Imagine you ask this question:' + ' ' + '```' + '({1}CommTools.AskForNumber) ''Number of copies to print''' + '```' + '' + 'Note that the operator `AskForNumber` requires a check function to be passed as left operand. In the example ' + 'the check function simply returns a 1. Instead you could perform a real check like this:' + '' + '```' + 'CheckFn←{' + ' ⍵∊1 2 3 4 5:1 ' + ' ⎕←''Please enter a number between 1 and 5'' ' + ' 0' + ('}'⋄) + '(CheckFn CommTools.AskForNumber) ''Number of copies to print:''' + '``` ' + '' + 'If the user does not enter a number but just presses , an empty vector is returned. You can force the user to enter' + 'a number by passing a 1 as left argument (`enforce`).' + '' + 'To answer this question automatically create a global variable `AskForNumber_Answers` as a matrix' + 'with two columns:' + '' + '| `[;1]` | Contains the question.' + '| `[;2]` | The answer to be given, either as a character vector or as a number.' + '' + 'So in our case we specify:' + '' + '```' + 'CommTools.AskForNumber_Answers←1 2⍴''Number of copies to print'' 1' + '``` ' + '' + 'When the question is asked:' + ' ' + '```' + '({1}CommTools.AskForNumber) ''Number of copies to print''' + '```' + '' + '`AskForNumber` would do the following:' + '' + '1. Check whether there is a global variable `AskForNumber_Answers` around' + '2. If there is it would try to match the question to any row in the first column of `AskForNumber_Answers` ' + '3. If there is a match it would return the value in the second column of the identified row and convert it to a number' + ' if it is text' + '' + 'Notes:' + '' + '* Leading spaces in the question are removed, ": " is appended' + '* If multiple entries in `AskForNumber_Answers` match the question, an error will be thrown' + '* If no row in `AskForNumber_Answers` matches the question, the question is asked as usual' + '' + '### Asking for a character vector' + '' + 'Imagine you are asking this question:' + ' ' + '```' + '({1}CommTools.AskForText) ''Enter a month (three characters)''' + '```' + '' + 'Note that the operator `AskForText` requires a check function to be passed as left operand. In the example ' + 'the check function simply returns a 1. Instead you could perform a real check like this:' + '' + '```' + 'CheckFn←{' + ' (⊂⍵)∊↓12 3⍴''janfebmaraprmaijunjulaugsepoctnovdec'':1 ' + ' ⎕←''Please enter just three lowercase characters'' ' + ' 0' + ('}'⋄) + '(CheckFn CommTools.AskForText) ''Enter a month (just three character)''' + '``` ' + '' + 'If the user does not enter anything but just hits an empty vector will be returned. ' + 'You can force the user to enter a number by passing a 1 as left argument (`enforce`).' + '' + 'Instead of a Boolean you can also specify a character vector as `⍺`. In that case, when the user just presses ``' + 'without entering anything, the default (`⍺`) is returned.' + '' + 'To answer this question automatically create a global variable `AskForText_Answers` as a matrix' + 'with two columns:' + '' + '| `[;1]` | Contains the question.' + '| `[;2]` | The answer to be given.' + '' + 'So in our case we specify:' + '' + '```' + 'CommTools.AskForText_Answers←1 2⍴''Enter a month (three character)'' ''mar''' + '``` ' + '' + 'When the question is asked:' + ' ' + '```' + '({1}CommTools.AskForText) ''Enter a month (three character)''' + '```' + '' + '`AskForText` would do the following:' + '' + '1. Check whether there is a global variable `AskForText_Answers`' + '2. If so it would try to match the question to any row in the first column of `AskForText_Answers` ' + '3. If there is a match it would return the value in the second column of the identified row' + '' + 'Notes:' + '' + '* Leading spaces in the question are removed, ": " is appended' + '* If multiple entries in `AskForText_Answers` match the question, an error will be thrown' + '* If no row in `AskForText_Answers` matches the question, the question will be asked as usual' +) diff --git a/APLSource/CommTools/AliasChar.apla b/APLSource/CommTools/AliasChar.apla new file mode 100644 index 000000000..a2495a227 --- /dev/null +++ b/APLSource/CommTools/AliasChar.apla @@ -0,0 +1 @@ +'@' diff --git a/APLSource/CommTools/AskForNumber.aplo b/APLSource/CommTools/AskForNumber.aplo new file mode 100644 index 000000000..0bc4d4237 --- /dev/null +++ b/APLSource/CommTools/AskForNumber.aplo @@ -0,0 +1,65 @@ + value←{enforce}(CheckFn AskForNumber)question;flag;input;valid;alias;bool;buff;success;firstPart;ind +⍝ Operator asking a question and allowing the user to enter a number.\\ +⍝ By entering "`∘∘∘`" the user may interrupt `AskForNumber`: this activates a stop.\\ +⍝ `CheckFn` is supposed to be a function that gets the input as `⍵` and must return a Boolean +⍝ with a 1 indicating that the input is okay and 0 that it is not. If you don't need/want +⍝ a check function pass `{1}` as left operand.\\ +⍝ If the user refuses to enter a number an empty vector is returned. +⍝ However, you might prevent this from happening by specifying a 1 as `⍺` (`enforce`), meaning that +⍝ the user must enter a number in order to continue. + enforce←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'enforce' + value←⍬ + success←flag←0 + :If ~(≡question)∊0 1 + question←1↓⊃,/LF,¨question + :EndIf + question←ReplaceCRbyLF question + question←{⍵↓⍨+/∧\' '=⍵}question + :Repeat + firstPart←{⍵↑⍨⍵⍳LF}question + :If 0<⎕NC'AskForNumber_Answers' + AskForNumber_Answers←(0<≢¨' '~⍨¨AskForNumber_Answers[;1])⌿AskForNumber_Answers + AskForNumber_Answers[;1]←{⍵↓⍨+/∧\' '=⍵}¨AskForNumber_Answers[;1] + :AndIf 0<≢AskForNumber_Answers + :If AliasChar∊firstPart + ind←firstPart⍳AliasChar + :AndIf ∧/~(LF,' ')∊ind↑question + (alias question)←ind{(⍺↑⍵)(⍺↓⍵)}firstPart + flag←0<+/bool←({⍵↑⍨⍵⍳AliasChar}¨AskForNumber_Answers[;1])≡¨⊂alias + :ElseIf ~flag←0<+/bool←AskForNumber_Answers[;1]≡¨⊂firstPart + flag←0<+/bool←firstPart∘{⍵≡(≢⍵)↑⍺}¨AskForNumber_Answers[;1] + :EndIf + :If flag + 'Multiple pre-prepared answers qualify?!'Assert 1=+/bool + input←⍕2⊃AskForNumber_Answers[bool⍳1;] + :EndIf + :Else + :If (⌊/firstPart⍳' ',LF)>firstPart⍳AliasChar + question←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}question + :EndIf + :EndIf + :If ~flag + input←⍞,0/⍞←question,': ' + :If '∘∘∘'≡¯3↑input + (1+⊃⎕LC)⎕STOP⊃⎕SI + ∘∘∘ ⍝ Deliberate stop caused by user input + :EndIf + input←{⍵↑⍨1+-(⌽⍵)⍳':'}input + :EndIf + :If 0=≢input~' ' + :AndIf ~enforce + value←⍬ + :Return + :Else + (valid value)←⎕VFI input + :If 1≠≢valid + ⎕←'Please enter precisely one number' + :ElseIf valid + success←CheckFn value + value←⊃value + :Else + ⎕←'You did not enter a valid number, please retry!' + :EndIf + :EndIf + :Until success +⍝Done diff --git a/APLSource/CommTools/AskForText.aplo b/APLSource/CommTools/AskForText.aplo new file mode 100644 index 000000000..05495f629 --- /dev/null +++ b/APLSource/CommTools/AskForText.aplo @@ -0,0 +1,74 @@ + text←{x}(CheckFn AskForText)question;flag;alias;bool;buff;success;firstPart;enforce;default +⍝ Operator asking a question and allowing the user to enter text.\\ +⍝ By entering "`∘∘∘`" the user may interrupt `AskForText`: this activates a stop.\\ +⍝ `CheckFn` is supposed to be a function that gets the input as `⍵` and must return a Boolean +⍝ with a 1 indicating that the input is okay and 0 that it is not. If you don't need/want +⍝ a check function pass `{1}` as left operand.\\ +⍝ If the user refuses to enter anything an empty vector is returned. +⍝ However, you might prevent this from happening by specifying a 1 as `⍺` (`enforce`), meaning that +⍝ the user must enter something in order to continue. +⍝ You can also specify a default as `⍺` by passing a character vector instead of a Boolean. +⍝ In case the user does not enter something that default is returned; that's why the Boolean would +⍝ not make a difference anyway. + x←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'x' + :If ' '=1↑0⍴x + default←x + enforce←0 + :Else + enforce←x + default←'' + :EndIf + text←'' + success←flag←0 + :If ~(≡question)∊0 1 + question←1↓⊃,/LF,¨question + :EndIf + question←ReplaceCRbyLF question + question←{⍵↓⍨+/∧\' '=⍵}question + :Repeat + firstPart←{⍵↑⍨⍵⍳LF}question + :If 0<⎕NC'AskForText_Answers' + AskForText_Answers←(0<≢¨' '~⍨¨AskForText_Answers[;1])⌿AskForText_Answers + AskForText_Answers[;1]←{⍵↓⍨+/∧\' '=⍵}¨AskForText_Answers[;1] + :AndIf 0<≢AskForText_Answers + :If AliasChar∊question + (alias question)←{l←⍵⍳AliasChar ⋄ (l↑⍵)(l↓⍵)}question + flag←0<+/bool←({⍵↑⍨⍵⍳AliasChar}¨AskForText_Answers[;1])≡¨⊂alias + :ElseIf ~flag←0<+/bool←AskForText_Answers[;1]≡¨⊂question + flag←0<+/bool←question∘{⍵≡(≢⍵)↑⍺}¨AskForText_Answers[;1] + :EndIf + :If flag + 'Multiple pre-prepared answers qualify?!'Assert 1=+/bool + text←2⊃AskForText_Answers[bool⍳1;] + :EndIf + :Else + :If (⌊/firstPart⍳' ',LF)>firstPart⍳AliasChar + question←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}question + :EndIf + :EndIf + :If 0<≢default + question,←' [',default,']' + :EndIf + :If ~flag + text←⍞,0/⍞←question,': ' + :If '∘∘∘'≡¯3↑text + (1+⊃⎕LC)⎕STOP⊃⎕SI + ∘∘∘ ⍝ Deliberate stop caused by user input + :EndIf + text←(≢{⍵↑⍨-¯1+(⌽⍵)⍳LF}question,': ')↓text + :If 0=≢text + :AndIf 0<≢default + text←default + :EndIf + :EndIf + :If 0=≢text + :If enforce + ⎕←'You must enter something!' + :Else + :Return + :EndIf + :Else + success←CheckFn text + :EndIf + :Until success +⍝Done diff --git a/APLSource/CommTools/Assert.aplf b/APLSource/CommTools/Assert.aplf new file mode 100644 index 000000000..33ea0e578 --- /dev/null +++ b/APLSource/CommTools/Assert.aplf @@ -0,0 +1 @@ + Assert←{⍺←'' ⋄ (,1)≡,⍵:r←1 ⋄ ⎕ML←1 ⋄ ⍺ ⎕SIGNAL 1↓(⊃∊⍵),11} diff --git a/APLSource/CommTools/Cleanup.aplf b/APLSource/CommTools/Cleanup.aplf new file mode 100644 index 000000000..b0ff31e10 --- /dev/null +++ b/APLSource/CommTools/Cleanup.aplf @@ -0,0 +1,6 @@ + Cleanup + ⎕EX'Select_Choices' + ⎕EX'YesOrNo_Answers' + ⎕EX'NoPause' + ⎕EX'AskForNumber_Answers' + ⎕EX'AskForText_Answers' diff --git a/APLSource/CommTools/Copyright.aplf b/APLSource/CommTools/Copyright.aplf new file mode 100644 index 000000000..8f9f9fbca --- /dev/null +++ b/APLSource/CommTools/Copyright.aplf @@ -0,0 +1,2 @@ + r←Copyright + r←'Copyright by Kai Jaeger ⋄ https://kai-jaeger.de ⋄ kai@aplteam.com' diff --git a/APLSource/CommTools/ErrNo.aplf b/APLSource/CommTools/ErrNo.aplf new file mode 100644 index 000000000..c64b1e759 --- /dev/null +++ b/APLSource/CommTools/ErrNo.aplf @@ -0,0 +1,2 @@ + r←ErrNo + r←811 diff --git a/APLSource/CommTools/Help.aplf b/APLSource/CommTools/Help.aplf new file mode 100644 index 000000000..81cda173d --- /dev/null +++ b/APLSource/CommTools/Help.aplf @@ -0,0 +1,2 @@ + Help + ⎕SE.UCMD'ADOC ',⍕⎕THIS diff --git a/APLSource/CommTools/History.apla b/APLSource/CommTools/History.apla new file mode 100644 index 000000000..48e7356a7 --- /dev/null +++ b/APLSource/CommTools/History.apla @@ -0,0 +1,47 @@ +( + '* 1.7.0 from 2023-09-15' + ' * The `AskForText` function now allows to define a default via `⍺`' + '* 1.6.1 from 2023-08-09' + ' * In case a non-existent item in a list was specified on `Select_Choices` the `Select` function' + ' did not return an empty vector but an index one greater than the number of items in the list' + '* 1.6.0 from 2023-08-07' + ' * `Select` now support a wildcard character (`*`) for `Select_Choices`' + '* 1.5.0 from 2023-06-13' + ' * A question may now contain LF in `AskForNumber`, `AskForText`, `YesOrNo` and `Pause`' + ' * Documentation improved' + ' * Bug fixes' + ' * Not all main functions used `AliasChar` rather than "@"' + '* 1.4.1 from 2023-04-10' + ' * Bug fix in `YesOrNo`, `AskForNumber` and `AskForText`: an alias was not removed from the question' + '* 1.4.0 from 2023-04-03' + ' * AskForText added' + ' * AskForNumber added ' + ' * Documentation polished' + '* 1.3.0 from 2023-03-15' + ' * `Pause` added' + ' * The result of the `Version` function has changed' + '* 1.2.3 from 2023-02-19' + ' * The package wrongly believed to depend on `Tester2` and `CodeCoverage`' + '* 1.2.2 from 2023-01-15' + ' * An alias in a caption was only ignored when `Select_Choices` was defined' + '* 1.2.1 from 2021-12-13' + ' * `Select` had a problem when Select_Choices did exist but had zero rows ' + ' * `YesOrNo` had a problem when YesOrNo_Answers did exist but had zero rows' + '* 1.2.0 from 2021-12-12' + ' * `Select` and `YesOrNow` now support aliases.' + '* 1.1.0 from 2021-10-10' + ' * `YesAndNo` now checks the global `YesOrNo_Answers`: if it is not empty it is used to answer the question' + ' automatically' + ' * `Select` now checks the global `Select_Choices`: if it is not empty it is used to select an item' + ' automatically ' + ' * `Cleanup` added as a means to get rid of all global variables used for automation' + ' * `Public` added in order to identify the public interface' + ' * `Help` added with extensive documentation including details regarding automation' + ' * Both `YesOrNo` and `Select` can now be interrupted by entering "∘∘∘"' + ' * Bug fixes' + ' * `Select` produced an error message when an item of ⍵ was a scalar' + '* 1.0.1 from 2021-10-12' + ' * Bug fix: API decleration was wrong' + '* 1.0.0 from 2021-10-11' + ' * First release' +) diff --git a/APLSource/CommTools/LF.aplf b/APLSource/CommTools/LF.aplf new file mode 100644 index 000000000..f154cde2c --- /dev/null +++ b/APLSource/CommTools/LF.aplf @@ -0,0 +1,2 @@ + r←LF + r←⎕UCS 10 diff --git a/APLSource/CommTools/Pause.aplf b/APLSource/CommTools/Pause.aplf new file mode 100644 index 000000000..de96913ce --- /dev/null +++ b/APLSource/CommTools/Pause.aplf @@ -0,0 +1,42 @@ + {flag}←{lineFlag}Pause msg;input;alias;ind + ⍝ Prints `msg` to he session and tells the user that she must press if she wants to continue. + ⍝ By entering "`∘∘∘`" the user may interrupt `Pause`: this activates a stop.\\ + ⍝ The optional left argument `lineFlag` defaults to 0. If it's 1 then a line is printed. The length + ⍝ is defined by `⎕PW-1`.\\ + ⍝ You can prevent `Pause` from displaying the message and requiring the user to press , + ⍝ refer to the documentation with ]ADoc CommTools\\ + ⍝ The function will return 1 in case it did present the message and stopped, and 0 otherwise. + lineFlag←{0=⎕NC ⍵:0 ⋄ ⍎⍵}'lineFlag' + msg←⊃LF{⍺,⍺⍺,⍵}/⊆msg + msg←ReplaceCRbyLF msg + flag←1 + :If AliasChar∊msg + :If 0<⎕NC'NoPause' + (alias msg)←{l←⍵⍳AliasChar ⋄ (l↑⍵)(l↓⍵)}msg + flag←~∨/({⍵↑⍨⍵⍳AliasChar}¨⊆NoPause)≡¨⊂alias + :EndIf + :Else + :If 0<⎕NC'NoPause' + :If NoPause≡1 + :OrIf ∨/(⊆NoPause)∊⊆msg + :OrIf ∨/(NoPause/⍨~AliasChar∊¨NoPause){⍺≡¨(≢¨⍺)↑¨(≢⍺)⍴⊂⍵}{⍵↑⍨¯1+⍵⍳LF}msg + flag←0 + :EndIf + :EndIf + :EndIf + :If flag + :If lineFlag + ⍞←((⎕PW-1)⍴'─'),LF + :EndIf + :If AliasChar∊msg + ind←msg⍳AliasChar + :AndIf ∧/~(ind↑msg)∊' ',LF + msg←ind↓msg + :EndIf + input←⍞,0/⍞←({0=≢⍵:⍵ ⋄ ⍵,LF}msg),'In order to continue press ' + :If '∘∘∘'≡¯3↑input + (1+⊃⎕LC)⎕STOP⊃⎕SI + ∘∘∘ ⍝ Deliberate stop caused by user input + :EndIf + :EndIf +⍝Done diff --git a/APLSource/CommTools/Public.aplf b/APLSource/CommTools/Public.aplf new file mode 100644 index 000000000..7bab46702 --- /dev/null +++ b/APLSource/CommTools/Public.aplf @@ -0,0 +1,9 @@ + r←Public + r←'' + r,←⊂'AskForString' + r,←⊂'AskForNumber' + r,←⊂'Select' + r,←⊂'YesOrNo' + r,←⊂'Pause' + r,←⊂'Cleanup' + r,←⊂'Help' diff --git a/APLSource/CommTools/ReplaceCRbyLF.aplf b/APLSource/CommTools/ReplaceCRbyLF.aplf new file mode 100644 index 000000000..e295c7ccf --- /dev/null +++ b/APLSource/CommTools/ReplaceCRbyLF.aplf @@ -0,0 +1 @@ +ReplaceCRbyLF←{LF@(⍸⍵=⎕UCS 13)⊣⍵} diff --git a/APLSource/CommTools/Select.aplf b/APLSource/CommTools/Select.aplf new file mode 100644 index 000000000..957fbf865 --- /dev/null +++ b/APLSource/CommTools/Select.aplf @@ -0,0 +1,108 @@ + index←{x}Select choices;flag;answer;question;value;bool;⎕ML;⎕IO;manyFlag;mustFlag;caption;buff;flag2;alias;blankFlag;firstPart +⍝ Presents `choices` as a numbered list and allows the user to select either exactly one or multiple ones. +⍝ Selecting just one is the default.\\ +⍝ The optional left argument allows you to specify multiple (positional) choices: +⍝ * `caption` is shown above the choices; must be a simple character vector +⍝ * `manyFlag` defaults to 0 (meaning just one item might be selected) or 1, in which case multiple items can be selected +⍝ * `mustFlag` forces the user to select at least one option\\ +⍝ Notes: +⍝ * `choices` must not have more than 999 items\\ +⍝ * By entering "`∘∘∘`" the user may interrupt `Select` by running onto a stop vector, overcoming the bug that ⍞ cannot be interrupted +⍝ * If the user aborts by entering "q" (for "quit") `⍬` will be returned +⍝ You can make `Select` select none, one or several choices automatically, refer to the documentation +⍝ with ]ADoc CommTools\\ + x←{0<⎕NC ⍵:⊆⍎⍵ ⋄ ''}'x' + (caption manyFlag mustFlag)←x,(⍴,x)↓'' 0 0 + '"caption" must be a simple character vector'Assert 1=≡caption←,caption + '"caption" must not contain line feeds (⎕UCS 10)'Assert~LF∊caption + '"caption" must not contain carriage returns (⎕UCS 13)'Assert~(⎕UCS 13)∊caption + ⎕IO←1 ⋄ ⎕ML←1 + 'Invalid right argument; must be a vector of text vectors.'⎕SIGNAL ErrNo/⍨2≠|≡choices + 'Right argument has more than 999 items'⎕SIGNAL ErrNo/⍨999<≢choices + flag←0 + firstPart←{⍵↑⍨⍵⍳LF}caption + :If 0<⎕NC'Select_Choices' + Select_Choices←(0<≢¨Select_Choices[;1])⌿Select_Choices + :AndIf 0<≢Select_Choices + :If AliasChar∊caption + (alias caption)←{l←⍵⍳AliasChar ⋄ (l↑⍵)(l↓⍵)}caption + flag←0<+/bool←({⍵↑⍨(,⍵)⍳AliasChar}¨Select_Choices[;1])≡¨⊂alias + :Else + :If 0=+/bool←Select_Choices[;1]≡¨⊂caption + bool←caption∘{⍵≡⍺↑⍨≢⍵}¨Select_Choices[;1] + :EndIf + flag←0<+/bool + :EndIf + :If flag + 'Multiple choices qualify?!'Assert 1=+/bool + index←2⊃Select_Choices[bool⍳1;] + :If (⊂index)∊0 ⍬ + index←⍬ + :ElseIf ' '=1↑0⍴∊index ⍝ Text?! + :If '*'=¯1↑index + index←((¯1+≢index)↑¨{⍵↓⍨+/∧\' '=⍵}¨choices)⍳⊂(¯1↓index) + 'Invalid selection'Assert index∊⍳≢choices + :ElseIf (,'a')≡,index + index←⍳≢choices + :ElseIf (,'q')≡,index + index←⍬ + :ElseIf 0<≢buff←⍸index∘≡¨choices + index←buff + 'Invalid selection'Assert index∊⍳≢choices + :ElseIf 1=≢index←⍸index∘≡¨(≢index)↑¨{⍵↓⍨+/∧\' '=⍵}¨choices + index←⊃index + 'Invalid selection'Assert index∊⍳≢choices + :Else + 'Invalid selection'Assert 0 + :EndIf + :Else + 'Invalid selection'Assert∧/index∊⍳≢choices + :EndIf + :EndIf + :EndIf + :If ~flag + flag2←0 + :Repeat + blankFlag←0≠≢caption + :If AliasChar∊caption + :If (firstPart⍳' ')>firstPart⍳AliasChar + caption←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}caption + :EndIf + :EndIf + ⎕←{⍵↑'---',(blankFlag/' '),caption,(blankFlag/' '),⍵⍴'-'}⎕PW-1 + ⎕←⍪{((⊂'. '),¨⍨(⊂3 0)⍕¨⍳≢⍵),¨⍵}choices + ⎕←'' + question←'Select one ',(manyFlag/'or more '),'item',((manyFlag)/'s'),' ' + question,←((manyFlag∨~mustFlag)/'('),((~mustFlag)/'q=quit'),((manyFlag∧~mustFlag)/', '),(manyFlag/'a=all'),((manyFlag∨~mustFlag)/')'),' :' + :If 0<≢answer←⍞,0/⍞←question + answer←(⍴question)↓answer + :If '∘∘∘'≡¯3↑answer + (1+⊃⎕LC)⎕STOP⊃⎕SI + ∘∘∘ ⍝ Deliberate stop caused by user input + :EndIf + :If 1=≢answer + :AndIf answer∊'Qq',manyFlag/'Aa' + :If answer∊'Qq' + :If 0=mustFlag + index←⍬ + flag2←1 + :EndIf + :Else + index←⍳≢choices + flag2←1 + :EndIf + :Else + (bool value)←⎕VFI answer + :If ∧/bool + :AndIf manyFlag∨1=+/bool + value←bool/value + :AndIf ∧/value∊⍳⍴choices + index←value + flag2←0≠≢index + :EndIf + :EndIf + :EndIf + :Until flag2 + index←{1<≢⍵:⍵ ⋄ ⊃⍵}⍣(⍬≢index)⊣index + :EndIf +⍝Done diff --git a/APLSource/CommTools/Version.aplf b/APLSource/CommTools/Version.aplf new file mode 100644 index 000000000..fc5f4298b --- /dev/null +++ b/APLSource/CommTools/Version.aplf @@ -0,0 +1,3 @@ + r←Version +⍝ See also `History` + r←'CommTools' '1.7.0' '2023-09-15' diff --git a/APLSource/CommTools/YesOrNo.aplf b/APLSource/CommTools/YesOrNo.aplf new file mode 100644 index 000000000..4e12c1b64 --- /dev/null +++ b/APLSource/CommTools/YesOrNo.aplf @@ -0,0 +1,100 @@ + yesOrNo←{default}YesOrNo question;isOkay;answer;add;dtb;answer2;bool;flag;buff;alias;firstPart;ind + ⍝ Asks a simple question and allows just "Y" (or "y") or "N" (or "n") as answers.\\ + ⍝ The question may be a simple character vector, possibly with `⎕UCS 10` in between, + ⍝ or a vector of simple character vectors.\\ + ⍝ You may specify a default via the optional left argument which when specified + ⍝ rules what happens when the user just presses . + ⍝ `default` must be either 1 (yes) or 0 (no).\\ + ⍝ By entering "`∘∘∘`" the user may interrupt `YesOrNo`: this activates a stop.\\ + ⍝ You can make `YesOrNo` answer the question automatically, refer to the documentation + ⍝ with ]ADoc CommTools\\ + ⍝ Note that this function does **not** work as intended when traced! + isOkay←0 + default←{0<⎕NC ⍵:⍎⍵ ⋄ ''}'default' + isOkay←0 + :If ~(≡question)∊0 1 + question←1↓⊃,/LF,¨question + :EndIf + question←ReplaceCRbyLF question + question←{⍵↓⍨+/∧\' '=⍵}question + :If 0≠≢default + 'Left argument must be a scalar'⎕SIGNAL 11/⍨1≠≢default + :AndIf ~default∊0 1 + 'The left argument. if specified, must be a Boolean or empty'⎕SIGNAL 11 + :EndIf + flag←1 + firstPart←{⍵↑⍨⍵⍳LF}question + :If 0<⎕NC'YesOrNo_Answers' + YesOrNo_Answers←(0<≢¨' '~⍨¨YesOrNo_Answers[;1])⌿YesOrNo_Answers + :AndIf 0<≢YesOrNo_Answers + :If AliasChar∊firstPart + ind←firstPart⍳AliasChar + (alias question)←ind{(⍺↑⍵)(⍺↓⍵)}firstPart + flag←0<+/bool←({⍵↑⍨⍵⍳AliasChar}¨YesOrNo_Answers[;1])≡¨⊂alias + :ElseIf ~flag←0<+/bool←YesOrNo_Answers[;1]≡¨⊂question + :AndIf ~flag←0<+/bool←question∘{⍵≡(≢⍵)↑⍺}¨YesOrNo_Answers[;1] + :AndIf LF∊question + buff←{⍵↓⍨+/∧\' '=⍵}⊃¯1↑LF(≠⊆⊢)question + flag←0<+/bool←YesOrNo_Answers[;1]≡¨⊂buff + :EndIf + :If flag + 'Multiple pre-prepared answers qualify?!'Assert 1=+/bool + answer←2⊃YesOrNo_Answers[bool⍳1;] + :If 0=≢answer + yesOrNo←default + :Else + ('Invalid answer: ',answer)Assert answer∊'YyNn' + yesOrNo←answer∊'Yy' + :EndIf + :EndIf + :Else + flag←0 + :EndIf + :If ~flag + :If 0=≢default + add←' (y/n) ' + :Else + :If default + add←' (Y/n) ' + :Else + add←' (y/N) ' + :EndIf + :EndIf + :If 1<≡question + question←1↓⊃,/LF,¨question + :EndIf + :If (⌊/firstPart⍳' ',LF)>firstPart⍳AliasChar + question←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}question,add + :Else + question,←add + :EndIf + :Repeat + ⎕←'' + ⍞←question + answer←⍞ + :If answer≡question ⍝ Did ... (since version 18.0 trailing blanks are not removed anymore) + :OrIf (≢answer)=¯1+≢question ⍝ ... the ... + :OrIf 0=≢answer ⍝ ... user ... + :OrIf question≡(-≢question)↑answer ⍝ ... just ... + dtb←{⍵↓⍨-+/∧\' '=⌽⍵} + answer2←dtb answer + :OrIf answer2≡((-≢answer2)↑LF{~⍺∊⍵:⍵ ⋄ ' ',dtb ⍺{⌽⍵↑⍨1+⍵⍳⍺}⌽⍵}question) ⍝ ... press ... + :OrIf answer≡{1↓⊃¯1↑(⍵=LF)⊂⍵}LF,question ⍝ ... ? + :If 0≠≢default + yesOrNo←default + isOkay←1 + :EndIf + :Else + :If '∘∘∘'≡¯3↑answer + (1+⊃⎕LC)⎕STOP⊃⎕SI + ∘∘∘ ⍝ Deliberate stop caused by user input + :EndIf + answer←¯1↑{⍵↓⍨-+/∧\' '=⌽⍵}answer + :If answer∊'YyNn' + isOkay←1 + yesOrNo←answer∊'Yy' + :EndIf + :EndIf + :Until isOkay + :EndIf +⍝Done diff --git a/APLSource/HandleError.aplc b/APLSource/HandleError.aplc index 733d18240..f2718dfdc 100644 --- a/APLSource/HandleError.aplc +++ b/APLSource/HandleError.aplc @@ -87,7 +87,7 @@ ∇ ∇ History - :Access Public Shared + :Access Public Shared ⍝ * 4.1.4 ⋄ 2023-04-18 ⍝ * Bug fix: the structure of the project was wrong, resulting in test-related stuff being including ⍝ in the package: that caused the naming problem. diff --git a/APLSource/HashPasswords/ADOC_Doc.apla b/APLSource/HashPasswords/ADOC_Doc.apla new file mode 100644 index 000000000..1c02217dc --- /dev/null +++ b/APLSource/HashPasswords/ADOC_Doc.apla @@ -0,0 +1,49 @@ +( + '# HashPasswords' + '' + '## Overview' + '' + 'This package gives you the tools you need to generate the hash for a salted password.' + '' + 'Note that there are several reasons for salting a password:' + '' + '* Ensure that passwords that are too short to be secure by normal standards are secure anyway,' + ' at least as far as the length is concerned' + '* Prevent an attacker from realising when users are sharing the same password' + '' + 'By salting a password with a randomly generated salt, we increase security.' + '' + 'Note that the salt must be stored as clear text. And no, this is not a security risk!' + '' + 'For more information see ' + '' + '## Conga DLLs' + '' + 'On Windows, .NET is used for solving the task.' + '' + 'On other platforms `HashPasswords` needs the Conga DLL `libconga??ssl64.*` when `??` stands for the ' + 'version number like "34".' + '' + 'If the global variable `HashPasswords.PathToCongaDLLs` is empty (that''s the default) then ' + '`HashPasswords` tries to use the Conga DLL the currently running APL is coming with.' + '' + 'If this is not suitable (for example in an application with a runtime EXE and a limited set of' + 'Dyalog files while no Dyalog is installed) then make sure that `PathToCongaDLLs` point to a ' + 'folder where suitable DLLs can be found.' + '' + '## Examples' + '' + '```' + ' ≢salt1←HashPasswords.CreateSalt 10' + '10' + ' hash1←HashPasswords.Hash salt1,''123456''' + ' salt2←HashPasswords.CreateSalt 10' + ' ⎕←hash2←HashPasswords.Hash salt2,''123456''' + ' hash1≢hash2' + ('1'⋄) + ' hash1≡HashPasswords.Hash salt1,''123456''' + ('1'⋄) + ' hash2≡HashPasswords.Hash salt2,''123456''' + ('1'⋄) + '```' +) diff --git a/APLSource/HashPasswords/API/CreateSalt.aplf b/APLSource/HashPasswords/API/CreateSalt.aplf new file mode 100644 index 000000000..100f49f51 --- /dev/null +++ b/APLSource/HashPasswords/API/CreateSalt.aplf @@ -0,0 +1 @@ + CreateSalt←{##.CreateSalt ⍵} diff --git a/APLSource/HashPasswords/API/Dispose.aplf b/APLSource/HashPasswords/API/Dispose.aplf new file mode 100644 index 000000000..9a52d1c3f --- /dev/null +++ b/APLSource/HashPasswords/API/Dispose.aplf @@ -0,0 +1,2 @@ + Dispose + ##.Dispose diff --git a/APLSource/HashPasswords/API/Hash.aplf b/APLSource/HashPasswords/API/Hash.aplf new file mode 100644 index 000000000..150b2e6fa --- /dev/null +++ b/APLSource/HashPasswords/API/Hash.aplf @@ -0,0 +1 @@ + Hash←{##.Hash ⍵} diff --git a/APLSource/HashPasswords/API/Init_SHA256.aplf b/APLSource/HashPasswords/API/Init_SHA256.aplf new file mode 100644 index 000000000..80594f0a6 --- /dev/null +++ b/APLSource/HashPasswords/API/Init_SHA256.aplf @@ -0,0 +1 @@ + Init_SHA256←{⍺←⊢ ⋄ 1:shy←⍺ ##.Init_SHA256 ⍵} diff --git a/APLSource/HashPasswords/Assert.aplf b/APLSource/HashPasswords/Assert.aplf new file mode 100644 index 000000000..33ea0e578 --- /dev/null +++ b/APLSource/HashPasswords/Assert.aplf @@ -0,0 +1 @@ + Assert←{⍺←'' ⋄ (,1)≡,⍵:r←1 ⋄ ⎕ML←1 ⋄ ⍺ ⎕SIGNAL 1↓(⊃∊⍵),11} diff --git a/APLSource/HashPasswords/CreateSalt.aplf b/APLSource/HashPasswords/CreateSalt.aplf new file mode 100644 index 000000000..1913f756b --- /dev/null +++ b/APLSource/HashPasswords/CreateSalt.aplf @@ -0,0 +1,6 @@ + salt←CreateSalt length;⎕RL;alp +⍝ Creates (pseudo)-randomly a salt with `length` characters. Only ASCII chars and digits are used as input. + ⎕DL 0.0001×?100 + ⎕RL←+/⎕TS + alp←(⎕C ⎕A),⎕A,⎕D + salt←alp[?length⍴≢alp] diff --git a/APLSource/HashPasswords/Dispose.aplf b/APLSource/HashPasswords/Dispose.aplf new file mode 100644 index 000000000..852f94369 --- /dev/null +++ b/APLSource/HashPasswords/Dispose.aplf @@ -0,0 +1,3 @@ + Dispose +⍝ Clean up + ⎕EX'SHA256' diff --git a/APLSource/HashPasswords/Hash.aplf b/APLSource/HashPasswords/Hash.aplf new file mode 100644 index 000000000..a590b6dd0 --- /dev/null +++ b/APLSource/HashPasswords/Hash.aplf @@ -0,0 +1,8 @@ + hash←Hash password +⍝ Returns the hash for `password`.\\ +⍝ In case `HashPasswords` has not yet been initialized, `Hash` takes care of that. + :If 0=⎕NC'SHA256' + Init_SHA256 ⍬ + :EndIf + hash←,Hex SHA256.ComputeHash⊂'UTF-8'⎕UCS password + ⍝Done diff --git a/APLSource/HashPasswords/Hex.aplf b/APLSource/HashPasswords/Hex.aplf new file mode 100644 index 000000000..c5832b61d --- /dev/null +++ b/APLSource/HashPasswords/Hex.aplf @@ -0,0 +1,10 @@ + Hex←{ ⍝ Transform integers to hex and vice versa\\ +⍝ `⍵` simple dec- or hex-array\\ +⍝ `←` simple hex- or dec-array\\ +⍝ Accepts hex as uppercase or lowercase but always return lowercase.\\ + ⎕IO←0 + t←0∊⊃⍬⍴0⍴⊂⍵ + a←⎕D,'abcdef',⎕D,'ABCDEF' + t:⍉a⌷⍨⊂16⊥⍣¯1⊢⍉⍵ + ⍉16⊥16|a⍳⍉⍵ + } diff --git a/APLSource/HashPasswords/History.apla b/APLSource/HashPasswords/History.apla new file mode 100644 index 000000000..527cc4fe9 --- /dev/null +++ b/APLSource/HashPasswords/History.apla @@ -0,0 +1,6 @@ +( + '* 1.0.1 ⋄ 2023-07-16' + ' * Did not work with 32-bit-version of Dyalog' + '* 1.0.0' + ' * First version' +) diff --git a/APLSource/HashPasswords/Init_SHA256.aplf b/APLSource/HashPasswords/Init_SHA256.aplf new file mode 100644 index 000000000..bb0635d11 --- /dev/null +++ b/APLSource/HashPasswords/Init_SHA256.aplf @@ -0,0 +1,24 @@ + {r}←{force}Init_SHA256 dummy;extn;⎕USING;filename;path;OS +⍝ Make the necessary preparations for hashing a password.\\ +⍝ Once initiallized, the function does not run its code again, except when a 1 is passed as `⍺`. + force←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'force' + '⍺ must be a Boolean'Assert(⊂force)∊0 1 + :If 9≠⎕NC'SHA256' + :OrIf force + OS←APLTreeUtils2.GetOperatingSystem ⍬ + :If 'Win'≡OS + ⎕USING←',System.Security.Cryptography.Algorithms',('W'=⊃⊃'.'⎕WG'APLVersion')/'.dll' + SHA256←⎕NEW System.Security.Cryptography.SHA256Managed + :Else + 'SHA256'⎕NS'' + :If 0=≢path←PathToCongaDLLs + path←(2 ⎕NQ #'GetEnvironment' 'Dyalog'),'/lib/' + :EndIf + filename←⊃FilesAndDirs.ListFiles path,'libconga??ssl',((1+Is64Bit)⊃'32' '64'),'.',(1+'Mac'≡OS)⊃'so' 'dylib' + 'Conga DLL not found'Assert 0<≢filename + 'Init'SHA256.⎕NA filename,'|nettle_sha256_init >I1[112]' + 'Update'SHA256.⎕NA filename,'|nettle_sha256_update =I1[112] U8 U1[32]' + SHA256.⎕FX'digest←ComputeHash data;ctx' 'data←⎕UCS⊃data' 'ctx←Init 112' 'ctx←Update ctx(⍴data)data' '(ctx digest)←Digest ctx 32 32' + :EndIf + :EndIf diff --git a/APLSource/HashPasswords/Is64Bit.aplf b/APLSource/HashPasswords/Is64Bit.aplf new file mode 100644 index 000000000..d4c1cb280 --- /dev/null +++ b/APLSource/HashPasswords/Is64Bit.aplf @@ -0,0 +1,2 @@ + r←Is64Bit + r←∨/'-64'⍷1⊃'#'⎕WG'APLVersion' diff --git a/APLSource/HashPasswords/PathToCongaDLLs.apla b/APLSource/HashPasswords/PathToCongaDLLs.apla new file mode 100644 index 000000000..a614936fa --- /dev/null +++ b/APLSource/HashPasswords/PathToCongaDLLs.apla @@ -0,0 +1 @@ +'' diff --git a/APLSource/HashPasswords/Public.aplf b/APLSource/HashPasswords/Public.aplf new file mode 100644 index 000000000..3222eba0a --- /dev/null +++ b/APLSource/HashPasswords/Public.aplf @@ -0,0 +1,2 @@ + r←Public + r←'Init_SHA256' 'CreateSalt' 'Hash' 'Dispose' diff --git a/APLSource/HashPasswords/Version.aplf b/APLSource/HashPasswords/Version.aplf new file mode 100644 index 000000000..e876e4e27 --- /dev/null +++ b/APLSource/HashPasswords/Version.aplf @@ -0,0 +1,3 @@ + r←Version + ⍝ See also `History` + r←'HashPasswords' '1.0.1' '2023-07-16' diff --git a/APLSource/MarkAPL.aplc b/APLSource/MarkAPL.aplc index f9e14d0fe..6a7e5bb69 100644 --- a/APLSource/MarkAPL.aplc +++ b/APLSource/MarkAPL.aplc @@ -49,7 +49,7 @@ ⍝ If you want to create a full-blown HTML page without any files being involved you ⍝ can also set the `createFullHtmlPage` parameter to 1. ⍝ ## Misc -⍝ |Homepage: | | +⍝ |Homepage: | | ⍝ |Cheat sheet: | #.MarkAPL.Help 0 | ⍝ |Reference: | #.MarkAPL.Reference 0 | ⍝ Kai Jaeger @@ -59,11 +59,20 @@ ∇ r←Version :Access Public Shared ⍝ See `History` - r←'MarkAPL' '11.0.4+246' '2022-10-26' + r←'MarkAPL' '11.1.0+246' '2022-08-13' ∇ ∇ History :Access Public Shared + ⍝ * 11.1.0 ⋄ 2023-08-13 + ⍝ * Layout `MarkAPL_screen` improved (TOC and inline code) + ⍝ * Layout `BlackOnWhite_screen` improved (TOC and inline code) + ⍝ * Documentation improved / updated + ⍝ * `LICENSE` updated + ⍝ * Packages updated + ⍝ * `Make` updated + ⍝ * 11.0.5 ⋄ 2022-06-15 + ⍝ * Bug fix: `Matrix2MarkdownList` complained wrongly that ⍵ should have 2 columns: 3 is correct ⍝ * 11.0.4 ⋄ 2022-10-26 ⍝ * Bug fixes ⍝ * `ShowHtml` did not cope when the right argument pointed to the current directory @@ -523,9 +532,9 @@ ⍝ 2. Nesting level. May start with either 0 or 1. ⍝ 3. Text vector of vector of text vector. 'Invalid right argument - not a matrix'⎕SIGNAL 11/⍨2≠⍴⍴mat - 'Invalid right argument - must have 2 columns'⎕SIGNAL 11/⍨3≠2⊃⍴mat + 'Invalid right argument - must have 3 columns'⎕SIGNAL 11/⍨3≠2⊃⍴mat 'First line''s level must be either 0 or 1'⎕SIGNAL 11/⍨~(⊃mat)∊0 1 - 'Invalid right argument - must have 2 columns'⎕SIGNAL 11/⍨~∧/mat[;1]∊0,⍳999 + 'Invalid right argument - must have 3 columns'⎕SIGNAL 11/⍨~∧/mat[;1]∊0,⍳999 mat[;2]←{⍵-1}⍣(1=⊃mat[;2])⊣mat[;2] ⍝ Ensure 0 is first level md←mat[;2]⍴¨' ' type←0=mat[;1] diff --git a/APLSource/Registry/GetGroupData.aplf b/APLSource/Registry/GetGroupData.aplf index b3d963dd0..5fa02c4c4 100644 --- a/APLSource/Registry/GetGroupData.aplf +++ b/APLSource/Registry/GetGroupData.aplf @@ -1,4 +1,4 @@ - r←{what}GetGroupData(path groupName);dcfFilename;dcfTie;markdown;email;folder + r←{what}GetGroupData(path groupName);dcfFilename;dcfTie;markdown;email;folder;cred;ind ⍝ Returns the data saved for a group as a matrix: ⍝ [;1] The name ⍝ [;2] The corresponding data\\ @@ -16,6 +16,12 @@ dcfTie←dcfFilename ⎕FSTIE 0 markdown←⎕FREAD dcfTie 3 email←⎕FREAD dcfTie 4 + :If 0=≢email + cred←##.Server.ReadCredentialFile ##.Server.G.RegistryPath,'/Credentials.csv' + ind←cred[;1]⍳⊂groupName + :AndIf ind≤≢cred + email←4⊃cred[ind;],'' '' ⍝ Paranoia because of format change in 2023-11 + :EndIf r←2 2⍴'markdown'markdown'email'email ⎕FUNTIE dcfTie :If 0<⎕NC'what' diff --git a/APLSource/Registry/History.apla b/APLSource/Registry/History.apla index e448175ea..7d11f6501 100644 --- a/APLSource/Registry/History.apla +++ b/APLSource/Registry/History.apla @@ -1,4 +1,14 @@ ( + '* 0.103.0 ⋄ 2023-11-03' + ' * You must specify now an email address for your group before you can publish a package ' + ' * Bug fixes' + ' * `BuildPackages` checks whether the .NET Framework is available and throws an error if not' + ' * `ListVersions` works on local un-managed Registries' + ' * `UpdateTatin` did not define its explicit result in case of success, causing a VALUE ERROR' + ' * Documentation for how to add a new API key was flawed.' + ' * `GetRegistryIndex` did not return anything useful in case of a local but unmanaged Registry' + '* 0.102.3 ⋄ 2023-10-13' + ' * Bug fix regarding the `files` property: if that pointed to a file in a sub-folder it was not handled correctly' '* 0.102.2 ⋄ 2023-10-09' ' * Documentation corrected regarding installation' '* 0.102.1 ⋄ 2023-10-07' diff --git a/APLSource/Registry/Index/GetRegistryIndex.aplf b/APLSource/Registry/Index/GetRegistryIndex.aplf index 1c01bbdef..e55ba91a9 100644 --- a/APLSource/Registry/Index/GetRegistryIndex.aplf +++ b/APLSource/Registry/Index/GetRegistryIndex.aplf @@ -1,6 +1,6 @@ r←{nested}GetRegistryIndex path;filename;list ⍝ In a managed Tatin Registry there must be a file "tatin_index.txt". -⍝ In a local un-managed Registry there is none, so we list all directories +⍝ In a local un-managed Registry there is none. ⍝ By default a simple character vector (⎕UCS 10-separated) is returned. ⍝ You may change this by specifying a 1 as `⍺`. nested←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'nested' @@ -8,5 +8,6 @@ :If 0<##.F.IsFile filename r←⊃##.F.NGET filename nested :Else - r←(1+≢path)↓¨##.F.ListDirs path + r←(1+≢path)↓¨##.F.ListDirs path ⍝ It's a local Registry, and it is not managed, se we go for directories :EndIf +⍝Done diff --git a/APLSource/Registry/Version.aplf b/APLSource/Registry/Version.aplf index 3c5e9e585..b50a31aa3 100644 --- a/APLSource/Registry/Version.aplf +++ b/APLSource/Registry/Version.aplf @@ -1,3 +1,3 @@ r←Version ⍝ See also `History` - r←'Tatin' '0.102.2+1680' '2023-10-09' + r←'Tatin' '0.103.0+1702' '2023-11-07' diff --git a/APLSource/Server/AcceptCredential.aplf b/APLSource/Server/AcceptCredential.aplf index f1fa8d337..7d0e98f38 100644 --- a/APLSource/Server/AcceptCredential.aplf +++ b/APLSource/Server/AcceptCredential.aplf @@ -1,9 +1,11 @@ - okayFlag←credentials AcceptCredential(apiKey groupName);credentials;ind;salt;hash;apiKeys;groupNames + okayFlag←credentials AcceptCredential(apiKey groupName);credentials;ind;salt;hash;apiKeys;groupNames ⍝ Takes an API key and a group name and checks whether that is found in the array `credentials`. ⍝ Note that `G.Credentials` is usually provided as `⍺` except test cases etc.\\ ⍝ This function is called when the user attempts to perform a PUT (read: "Publish") or ⍝ a DELETE (if ever) or any other operation that requires authentication.\\ ⍝ apiKey must be clear text. +⍝ Note that with version this function also checks whether for a group name that is not * and +⍝ requires an API key there is an email address defined. okayFlag←1 :If 0<≢credentials ⍝ Do we have any credentials saved at all? :If 2=⍴⍴credentials diff --git a/APLSource/Server/ConvertCredentials.aplf b/APLSource/Server/ConvertCredentials.aplf index 0ddd5064c..47343e940 100644 --- a/APLSource/Server/ConvertCredentials.aplf +++ b/APLSource/Server/ConvertCredentials.aplf @@ -1,10 +1,9 @@ -credentials←credentials ConvertCredentials newCredentials;i;group;apiKey;salt;hash + credentials←credentials ConvertCredentials newCredentials;i;group;apiKey;salt;hash ⍝ Converts API-keys in `newCredentials` into hashes and adds a Salt along the way. ⍝ Eventually merges `credentials` and `newCredentials`. :If 0<≢newCredentials - newCredentials,←⊂'' :For i :In ⍳≢newCredentials - (group apiKey)←2↑' '~⍨¨newCredentials[i;] + (group apiKey salt)←3↑' '~⍨¨newCredentials[i;] :If 0=≢apiKey newCredentials[i;]←group'' '' :Else diff --git a/APLSource/Server/EditGroupHomepage_.aplf b/APLSource/Server/EditGroupHomepage_.aplf index 43b48fba1..50e0a5038 100644 --- a/APLSource/Server/EditGroupHomepage_.aplf +++ b/APLSource/Server/EditGroupHomepage_.aplf @@ -13,7 +13,7 @@ html,←⊂H.BR html,←⊂H.BR - html,←⊂'email'H.Label'Email address:' + html,←⊂'email'H.Label'Single email address:' html,←⊂H.BR parms←⎕NS'' parms.(id name type placeholder value)←'email' 'email' 'email' 'your.name@email.address'email @@ -32,7 +32,7 @@ html,←⊂H.Input parms html,←⊂H.BR - ⍝ This is used to tell the server what this is all about. that allows the server to determine that + ⍝ This is used to tell the server what this is all about. That allows the server to determine that ⍝ the content must be massaged in a specific way. parms←⎕NS'' parms.(id name type value style readonly)←'sgh' 'sgh' 'text' 'save-group-homepage' 'display:none;' 'readonly' diff --git a/APLSource/Server/Handle_PUT_And_POST.aplf b/APLSource/Server/Handle_PUT_And_POST.aplf index 5f1676d27..c0ae18d6f 100644 --- a/APLSource/Server/Handle_PUT_And_POST.aplf +++ b/APLSource/Server/Handle_PUT_And_POST.aplf @@ -10,13 +10,18 @@ groupName←request R.GetQueryParameter'group' :EndIf :If G.Credentials AcceptCredential apiKey groupName - :If Reg.IsValidPackageID_Complete request.RequestTarget - response←SavePackage request - :ElseIf request IsREST_v1 request.RequestTarget - response←request Handle_PUT_And_POST_REST_Version1 request.RequestTarget + :If 0=≢'email'Reg.GetGroupData G.RegistryPath groupName + :AndIf 0 G.INI.Get 'CONFIG:EnforceEmailAddress' + response←request RespondWithHTML 400 'No email address defined' 'You cannot publish without defining an email address for your group.' :Else - response←request Respond 400 ⍝ 400 ←→ Bad Request - response.Content←'Neither valid package ID nor proper REST request' + :If Reg.IsValidPackageID_Complete request.RequestTarget + response←SavePackage request + :ElseIf request IsREST_v1 request.RequestTarget + response←request Handle_PUT_And_POST_REST_Version1 request.RequestTarget + :Else + response←request Respond 400 ⍝ 400 ←→ Bad Request + response.Content←'Neither valid package ID nor proper REST request' + :EndIf :EndIf :Else response←request RespondWithHTML 401 'Unauthorized request' ⍝ 401 ←→ Unauthorized diff --git a/APLSource/Server/OnRequest.aplf b/APLSource/Server/OnRequest.aplf index 9c3f96944..5b6f0523d 100644 --- a/APLSource/Server/OnRequest.aplf +++ b/APLSource/Server/OnRequest.aplf @@ -31,7 +31,7 @@ :EndIf :If 200≠response.StatusCode ⍝ Add potential error message in case something went wrong: - logMsg,←(326≠⎕DR response.Content)/'; ',response.Content + logMsg,←((~∨/''⍷∊response.Content)∧326≠⎕DR response.Content)/'; ',response.Content :EndIf :EndIf P.AppLog logMsg diff --git a/APLSource/Server/ProcessCredentials.aplf b/APLSource/Server/ProcessCredentials.aplf index 8f8409f30..3b8d444bf 100644 --- a/APLSource/Server/ProcessCredentials.aplf +++ b/APLSource/Server/ProcessCredentials.aplf @@ -1,7 +1,6 @@ - G←ProcessCredentials G;filename;credentials;body;oldFilename;newCredentials + G←ProcessCredentials G;filename;credentials;body;newCredentialsFilename;newCredentials ⍝ Returns a matrix with {group} - Salt - API-key-hash combinations used for authentication. -⍝ For the time being, if the file is still lacking an email address, no conversion is done. - oldFilename←G.RegistryPath,'/Credentials.txt' + newCredentialsFilename←G.RegistryPath,'/Credentials.txt' filename←G.RegistryPath,'/Credentials.csv' :If 0=F.Exists filename ''F.NPUT filename @@ -13,10 +12,10 @@ :Else G.Credentials←credentials :EndIf - :If 0<≢newCredentials←ReadCredentialFile oldFilename + :If 0<≢newCredentials←ReadCredentialFile newCredentialsFilename G.Credentials←G.Credentials ConvertCredentials newCredentials (⊂⊃¨{⍺,',',⍵}/¨↓G.Credentials)F.NPUT filename 1 - F.DeleteFile oldFilename + F.DeleteFile newCredentialsFilename :EndIf G.CredentialsTimestamp←Reg.Timestamp2Float⊃⊃('type' 3)F.Dir filename ⍝Done diff --git a/APLSource/Server/ReadCredentialFile.aplf b/APLSource/Server/ReadCredentialFile.aplf index 75a6e9fe2..c2133480d 100644 --- a/APLSource/Server/ReadCredentialFile.aplf +++ b/APLSource/Server/ReadCredentialFile.aplf @@ -1,11 +1,15 @@ r←ReadCredentialFile filename;buff ⍝ Read the credential file (either Credentials.txt or Credentials.csv), and remove any comments -⍝ as well as empty lines. - r←0 2⍴⊂'' +⍝ (= lines that start with a semicolon) as well as empty lines. +⍝ Returns a matrix with 4 columns: +⍝ [1] Group name +⍝ [2] API key +⍝ [3] SALT (empty in case of Credentials.txt) + r←0 4⍴⊂'' :If F.IsFile filename :AndIf 0<≢buff←⊃F.NGET filename 1 :AndIf 0<≢buff←(';'≠⊃¨buff)/buff :AndIf 0<≢buff←(0<≢¨buff)/buff buff←{','@(⍸'='=⍵)⊣⍵}¨buff ⍝ Exchange the deprecated "=" against "," - r←↑','A.Split¨buff + r←3↑⍤1⊢(↑','A.Split¨buff),⊂'' :EndIf diff --git a/APLSource/Server/RespondWithHTML.aplf b/APLSource/Server/RespondWithHTML.aplf index 0879a5ee2..5d4fadb5a 100644 --- a/APLSource/Server/RespondWithHTML.aplf +++ b/APLSource/Server/RespondWithHTML.aplf @@ -1,13 +1,17 @@ - response←request RespondWithHTML y;html;msg;statusCode -⍝ Compiles an HTML response with "msg" becoming an

tag - (statusCode msg)←2↑(⊆y),'' '' - :If 0=≢msg - msg←GetReasonPhraseFor statusCode + response←request RespondWithHTML y;html;msg;statusCode;caption +⍝ Compiles an HTML response with "caption" becoming an

tag and +⍝ the (optional) "msg" one to many

tags. + (statusCode caption msg)←3↑(⊆y),'' '' '' + :If 0=≢caption + caption←GetReasonPhraseFor statusCode :EndIf response←request Respond statusCode html←HtmlHeader'' - html,←⊂'

',msg,' (',(⍕statusCode),')

' + html,←⊂'

',caption,' (',(⍕statusCode),')

' html,←AddSpecialMessage ⍬ + :If 0<≢msg + html,←{'

',⍵,'

'}¨⊆msg + :EndIf html,←'' '' response.Content←MassageHTML html ⍝Done diff --git a/APLSource/Server/ReturnGroupHomepage.aplf b/APLSource/Server/ReturnGroupHomepage.aplf index 7085f0527..f694efcf6 100644 --- a/APLSource/Server/ReturnGroupHomepage.aplf +++ b/APLSource/Server/ReturnGroupHomepage.aplf @@ -10,7 +10,7 @@ :If F.IsFile htmlFilename html,←⊂⊃⎕NGET htmlFilename html,←⊂'
' - html,←⊂'email'H.Label'Email address:' + html,←⊂'email'H.Label'Single email address:' html,←⊂H.BR email←'email'Reg.GetGroupData G.RegistryPath groupName parms←⎕NS'' @@ -34,7 +34,7 @@ data←GetPackagesOfGroup groupName :If 0<≢data html,←⊂'

Packages owned by "',groupName,'"

' - colTitles←'Package name' 'Description' 'OS' 'Tags' + colTitles←'Package name' 'Description' 'OS' 'UC*' html,←⊂H.OpenTable colTitles CalcTableWidth data html,←⊂'' html,←⊂'' @@ -50,6 +50,7 @@ html,←⊂'' html,←⊂'' html,←⊂'' + html,←⊂'

* UC means "User Command"' diff --git a/APLSource/Tatin.dyalog b/APLSource/Tatin.dyalog index 6e5585436..1e6cf042f 100644 --- a/APLSource/Tatin.dyalog +++ b/APLSource/Tatin.dyalog @@ -1,6 +1,6 @@ -:Namespace Tatin +:Namespace Tatin ⍝ The ]Tatin user commands for managing packages.\\ -⍝ * 0.74.0 - 2023-10-07 +⍝ * 0.76.0 - 2023-11-03 ⎕IO←1 ⋄ ⎕ML←1 @@ -455,7 +455,7 @@ :If 1 TC.YesOrNo msg version←TC.UpdateClient tag folder :If 0<≢version - ⎕CMD '"',folder,'/Assets/docs/ReleaseNotes.html"' + ⎕CMD'"',folder,'/Assets/docs/ReleaseNotes.html"' r←'Tatin updated on disk to ',version r,←CR,'The current WS has NOT been updated, please restart a fresh session.' :EndIf @@ -467,6 +467,7 @@ :AndIf 1 TC.C.YesOrNo q {}TC.F.RmDirByForce folder2 :EndIf + r←'Tatin successfully updated to version ',(1↓tag),' in ',folder2 :EndIf :EndIf ∇ @@ -840,6 +841,9 @@ ∇ ∇ zipFilename←BuildPackage Arg;filename;sourcePath;targetPath;prompt;msg;dependencies;version;openCiderProjects;ind;cfg;parms + :If 'Win'≡TC.##.APLTreeUtils2.GetOperatingSystem ⍬ + 'On Windows, Tatin requires .NET Framework to be available for building packages'Assert 0=0 TC.##.APLTreeUtils2.ToNum 2 ⎕NQ #'GetEnvironment' 'DYALOG_NETCORE' + :EndIf (sourcePath targetPath)←Arg.(_1 _2) prompt←0 zipFilename←'' @@ -1246,7 +1250,7 @@ :EndIf :Trap 0 noOf←Arg.nobetas TC.LoadPackages identifier targetSpace - r←(⍕noOf),' package',((1≠noOf)/'s'),' (including dependencies) loaded into ',targetSpace + r←(⍕noOf),' package',((1≠noOf)/'s'),' loaded into ',targetSpace :Else ⍝ We must make sure that all connections get closed before passing on the error qdmx←⎕DMX @@ -2624,4 +2628,4 @@ CalledFrom←{⊃{⍵↓⍨+/∧\'⎕'=⊃¨⍵}⍵} -:EndNamespace \ No newline at end of file +:EndNamespace diff --git a/APLSource/TestCases/Test_API_100.aplf b/APLSource/TestCases/Test_API_100.aplf index 4b2201017..c7429d8a0 100644 --- a/APLSource/TestCases/Test_API_100.aplf +++ b/APLSource/TestCases/Test_API_100.aplf @@ -1,5 +1,5 @@ r←Test_API_100(stopFlag batchFlag);⎕TRAP;rc;msg -⍝ Exercise ⎕SE.Tatin.DeprecatePackage on an unknwn package +⍝ Exercise ⎕SE.Tatin.DeprecatePackage on an unknown package ⎕TRAP←(999 'C' '. ⍝ Deliberate error')(0 'N') r←T._Failed diff --git a/APLSource/TestCases/Test_InstallAndLoad_051.aplf b/APLSource/TestCases/Test_InstallAndLoad_051.aplf index 060c639ba..b4329dded 100644 --- a/APLSource/TestCases/Test_InstallAndLoad_051.aplf +++ b/APLSource/TestCases/Test_InstallAndLoad_051.aplf @@ -1,4 +1,4 @@ - r←Test_InstallAndLoad_051(stopFlag batchFlag);⎕TRAP;targetPath + r←Test_InstallAndLoad_051(stopFlag batchFlag);⎕TRAP;targetPath;msg;rc;en;res1;res2;res3 ⍝ Install and load two packages of which the second brings in a better version of the first one as dependency (was a bug) ⎕TRAP←(999 'C' '. ⍝ Deliberate error')(0 'N') r←T._Failed @@ -8,15 +8,20 @@ targetPath←∆GetTempDir ⍬ - {}TC.InstallPackages'[localhost]aplteam-APLProcess-0.2.10'targetPath - {}TC.InstallPackages'[localhost]example-Boo-1.0.0'targetPath + res1←TC.InstallPackages'[localhost]aplteam-APLProcess-0.2.10'targetPath + →T.GoToTidyUp(,⊆'aplteam-APLProcess-0.2.10')≢res1 + res2←TC.InstallPackages'[localhost]example-Boo-1.0.0'targetPath + →T.GoToTidyUp(,⊆'example-Boo-1.0.0')≢res2 + + res3←TC.LoadDependencies targetPath #.TEMP + →T.GoToTidyUp'#._tatin.example_Boo_1_0_0' '#._tatin.aplteam_APLProcess_0_3_0'≢⍕¨res3 - {}TC.LoadDependencies targetPath #.TEMP →T.GoToTidyUp'0.3.0'≢{⍵/⍨3>+\'.'=⍵}2⊃#.TEMP.APLProcess.Version →T.GoToTidyUp'0.3.0'≢{⍵/⍨3>+\'.'=⍵}2⊃#.TEMP.Boo.Version r←T._OK ∆TidyUp: - Assert 0=⊃F.RmDir targetPath + (rc en msg)←F.RmDir targetPath + msg Assert 0=rc ⍝Done diff --git a/APLSource/TestCases/Test_Load_021.aplf b/APLSource/TestCases/Test_Load_021.aplf index d3341ed7d..9f3e5b7ae 100644 --- a/APLSource/TestCases/Test_Load_021.aplf +++ b/APLSource/TestCases/Test_Load_021.aplf @@ -11,7 +11,7 @@ res←1 TC.LoadPackages pkgs'#.TEMP' - →T.PassesIf 11=res + →T.PassesIf 2=res →T.PassesIf 2=≢#.TEMP.⎕NL⍳16 expected←'#._tatin.aplteam_APLTreeUtils_6_0_10.APLTreeUtils' '#._tatin.aplteam_CompareSimple_3_0_2.CompareSimple' →T.PassesIf expected≡⍕¨#.TEMP.{⍎¨⎕NL ⍵}¯9 diff --git a/APLSource/TestCases/Test_Load_086.aplf b/APLSource/TestCases/Test_Load_086.aplf index c27807659..da18a7762 100644 --- a/APLSource/TestCases/Test_Load_086.aplf +++ b/APLSource/TestCases/Test_Load_086.aplf @@ -14,7 +14,7 @@ →T.GoToTidyUp 1 :Else qdmx←⎕DMX - expected←'refers to a package that is not installed:' + expected←'Invalid entry: aplteam-Tester2-3.5.0 ; re-install!' →T.GoToTidyUp~∨/expected⍷qdmx.EM :EndTrap diff --git a/APLSource/TestCases/Test_Load_088.aplf b/APLSource/TestCases/Test_Load_088.aplf index f9ed48e5a..9915ef286 100644 --- a/APLSource/TestCases/Test_Load_088.aplf +++ b/APLSource/TestCases/Test_Load_088.aplf @@ -17,7 +17,7 @@ →T.GoToTidyUp 1 :Else qdmx←⎕DMX - expected←'refers to a package that is not installed:' + expected←'Invalid entry: aplteam-Tester2-3.5.0 ; re-install!' →T.GoToTidyUp~∨/expected⍷qdmx.EM :EndTrap diff --git a/APLSource/TestCases/Test_Misc_003.aplf b/APLSource/TestCases/Test_Misc_003.aplf index 066d13253..2b92ff2de 100644 --- a/APLSource/TestCases/Test_Misc_003.aplf +++ b/APLSource/TestCases/Test_Misc_003.aplf @@ -10,7 +10,7 @@ cfg.group←'Example' cfg.name←'MyPackage' cfg.version←'0.1.0' - 1 TC.WritePackageConfigFile dir cfg + 0 TC.WritePackageConfigFile dir cfg cfgFilename←dir,'/',TC.CFG_Name :Trap ##.Registry.ErrorNo diff --git a/APLSource/TestCases/Test_Misc_018.aplf b/APLSource/TestCases/Test_Misc_018.aplf index 682adab90..a198c511c 100644 --- a/APLSource/TestCases/Test_Misc_018.aplf +++ b/APLSource/TestCases/Test_Misc_018.aplf @@ -37,9 +37,9 @@ tree2←TC.GetDependencyTree'file://',path →T.GoToTidyUp~∧/(tree[;2]~⊂'minimalversion-C-1.1.2')∊tree2[;2] ⍝ Because C-1.1.2 is neither requested nor needed + →T.GoToTidyUp∧/(tree[;2]~⊂'minimalversion-C-1.1.3')∊tree2[;2] ⍝ But C-1.1.3 isrequested! ⍝ We compromise here: rather rebuilding everything in a second and third test case we use what we have right now - ⎕DL 0.2 path TC.Dependencies.Add'DoesNotExist-APLTreeUtils-6.0.1' ⍝ Modify it: that does not exist #.⎕SHADOW'TEMP' @@ -59,4 +59,3 @@ F.RmDir ∆BuildDir ⎕EX'∆BuildDir' F.DeleteFile zipFiles -⍝Done diff --git a/APLSource/TestCases/Test_Publish_060.aplf b/APLSource/TestCases/Test_Publish_060.aplf index ca78fda70..3fa7e61eb 100644 --- a/APLSource/TestCases/Test_Publish_060.aplf +++ b/APLSource/TestCases/Test_Publish_060.aplf @@ -19,7 +19,7 @@ ⎕DL 0.1 (rc msg)←F.RmDirByForce tempPath msg Assert rc=0 - (rc msg)←F.RmDirByForce ∆GetServerRegistryFolder,'aplteam-Seven7Zip-9.0.1' + (rc msg)←F.RmDirByForce ∆TEMP_REGISTRY_FOLDER,'/aplteam-Seven7Zip-9.0.4' msg Assert rc=0 ∆RecompileIndexOnServer ⍝Done diff --git a/APLSource/TestCases/Test_Publish_061.aplf b/APLSource/TestCases/Test_Publish_061.aplf index e19b54b40..920c12e3e 100644 --- a/APLSource/TestCases/Test_Publish_061.aplf +++ b/APLSource/TestCases/Test_Publish_061.aplf @@ -1,4 +1,4 @@ - r←Test_Publish_061(stopFlag batchFlag);⎕TRAP;path;zipFileName;statusCode;errMsg;list + r←Test_Publish_061(stopFlag batchFlag);⎕TRAP;path;zipFileName;statusCode;errMsg;list;msg;rc ⍝ Attempt to publish a package that is not a Cider project and has dependencies in the default folder ⎕TRAP←(999 'C' '. ⍝ Deliberate error')(0 'N') r←T._Failed @@ -13,5 +13,6 @@ ∆TidyUp: F.DeleteFile zipFileName - {}F.RmDirByForce ∆TEMP_REGISTRY_FOLDER,'/aplteam-Seven7Zip-9.0.1' + (rc msg)←F.RmDirByForce ∆TEMP_REGISTRY_FOLDER,'/aplteam-Seven7Zip-9.0.1' + msg Assert 0=rc ⍝Don diff --git a/APLSource/TestCases/Test_Publish_062.aplf b/APLSource/TestCases/Test_Publish_062.aplf index 6e62a6734..a677b2dbd 100644 --- a/APLSource/TestCases/Test_Publish_062.aplf +++ b/APLSource/TestCases/Test_Publish_062.aplf @@ -1,4 +1,4 @@ - r←Test_Publish_062(stopFlag batchFlag);⎕TRAP;path;zipFileName;statusCode;errMsg;list + r←Test_Publish_062(stopFlag batchFlag);⎕TRAP;path;zipFileName;statusCode;errMsg;list;rc;msg ⍝ Attempts to publish a package that is a Cider project but we specify a different folder (takes precedence) ⎕TRAP←(999 'C' '. ⍝ Deliberate error')(0 'N') r←T._Failed @@ -13,6 +13,7 @@ ∆TidyUp: F.DeleteFile zipFileName - Assert 0=⊃F.RmDirByForce ∆GetServerRegistryFolder,'aplteam-Seven7Zip-9.0.1' + (rc msg)←F.RmDirByForce ∆TEMP_REGISTRY_FOLDER,'/aplteam-Seven7Zip-9.0.1' + msg Assert 0=rc ∆RecompileIndexOnServer ⍝Don diff --git a/APLSource/TestCases/Test_Server_304.aplf b/APLSource/TestCases/Test_Server_304.aplf index 14fcc5718..86ce55dcf 100644 --- a/APLSource/TestCases/Test_Server_304.aplf +++ b/APLSource/TestCases/Test_Server_304.aplf @@ -8,7 +8,7 @@ #.⎕SHADOW'TEMP' ⋄ 'TEMP'#.⎕NS'' noOf←TC.LoadPackages((TC.ReplaceRegistryAlias '[localhost]'),'aplteam-APLProcess-0')#.TEMP - →T.GoToTidyUp 3≠noOf + →T.GoToTidyUp 1≠noOf →T.GoToTidyUp'0.5.1'≢∆RemoveBuildFromVersion 2⊃#.TEMP.APLProcess.Version r←T._OK diff --git a/APLSource/TestCases/Test_Server_350.aplf b/APLSource/TestCases/Test_Server_350.aplf index b1649eb0a..fb97791e1 100644 --- a/APLSource/TestCases/Test_Server_350.aplf +++ b/APLSource/TestCases/Test_Server_350.aplf @@ -9,7 +9,7 @@ 'TEMP'#.⎕NS'' res←TC.LoadPackages((TC.ReplaceRegistryAlias '[localhost]'),'aplteam-APLProcess-0.2.1')#.TEMP - →T.PassesIf 2=res + →T.PassesIf 1=res →T.PassesIf 3=≢#.TEMP.APLProcess.Version r←T._OK diff --git a/APLSource/TestCases/Test_UserCommands_083.aplf b/APLSource/TestCases/Test_UserCommands_083.aplf index b170b472f..dedef3efe 100644 --- a/APLSource/TestCases/Test_UserCommands_083.aplf +++ b/APLSource/TestCases/Test_UserCommands_083.aplf @@ -8,7 +8,7 @@ r←Test_UserCommands_083(stopFlag batchFlag);⎕TRAP;res;msg;rc;stop stop←0 (rc msg res)←stop ∆UCMD'LoadPackages [local]Laguntza #.TEMP -nobetas' - →T.GoToTidyUp '0 packages (including dependencies) loaded into #.TEMP'≢res + →T.GoToTidyUp '0 packages loaded into #.TEMP'≢res r←T._OK diff --git a/APLSource/TestCases/Test_UserCommands_084.aplf b/APLSource/TestCases/Test_UserCommands_084.aplf index 100cb12d1..7124a36e3 100644 --- a/APLSource/TestCases/Test_UserCommands_084.aplf +++ b/APLSource/TestCases/Test_UserCommands_084.aplf @@ -9,7 +9,7 @@ stop←0 (rc msg res)←stop ∆UCMD'LoadPackages [local]APLTreeUtils2,Does-NotExist,[tatin]Does-AlsoNotExist #.TEMP -nobetas' Assert 0=rc - →T.GoToTidyUp'1 package (including dependencies) loaded into #.TEMP'≢res + →T.GoToTidyUp'1 package loaded into #.TEMP'≢res r←T._OK diff --git a/APLSource/TestCases/Test_UserCommands_085.aplf b/APLSource/TestCases/Test_UserCommands_085.aplf index 36b578232..5cd9702ee 100644 --- a/APLSource/TestCases/Test_UserCommands_085.aplf +++ b/APLSource/TestCases/Test_UserCommands_085.aplf @@ -10,7 +10,7 @@ ∆res←⎕SE.UCMD'Tatin.LoadPackages [tatin]APLTreeUtils2' :EndWith →T.GoToTidyUp 9≠#.Foo.⎕NC'APLTreeUtils2' - →T.GoToTidyUp'1 package (including dependencies) loaded into #.Foo'≢#.Foo.∆res + →T.GoToTidyUp'1 package loaded into #.Foo'≢#.Foo.∆res R←T._OK diff --git a/APLSource/TestCases/Test_UserCommands_087.aplf b/APLSource/TestCases/Test_UserCommands_087.aplf index 42cb5f449..29f20de95 100644 --- a/APLSource/TestCases/Test_UserCommands_087.aplf +++ b/APLSource/TestCases/Test_UserCommands_087.aplf @@ -5,7 +5,8 @@ stop←0 (rc msg)←2↑stop ∆UCMD'LoadDependencies [MyUCMDs]foo' - →T.PassesIf rc=911 + →T.PassesIf 0≠rc + →T.PassesIf∨/'Folder does not exist:'⍷msg →T.PassesIf'/MyUCMDs/foo'{≡/⎕C ⍺((-≢⍺)↑⍵)}F.EnforceSlash msg R←T._OK diff --git a/APLSource/TestCases/Test_UserCommands_123.aplf b/APLSource/TestCases/Test_UserCommands_123.aplf index 7a6181b8f..b461af8bc 100644 --- a/APLSource/TestCases/Test_UserCommands_123.aplf +++ b/APLSource/TestCases/Test_UserCommands_123.aplf @@ -9,7 +9,7 @@ stop←0 (rc msg)←2↑stop ∆UCMD'InstallPackages "',zip1,',',zip2,'" [myucmds]Foo' - →T.PassesIf rc=911 + →T.PassesIf 0≠rc →T.PassesIf∨/'You must not specify a name after [MyUCMDs] when installing more than one package'⍷msg r←T._OK diff --git a/APLSource/ZipArchive.aplc b/APLSource/ZipArchive.aplc index 53e9b7944..818aa3181 100644 --- a/APLSource/ZipArchive.aplc +++ b/APLSource/ZipArchive.aplc @@ -26,13 +26,13 @@ ∇ r←Version :Access Public Shared - r←'ZipArchive' '1.1.0-beta-1+16' '2023-05-21' + r←'ZipArchive' '1.1.0+16' '2023-06-12' ∇ ∇ History :Access Public Shared - ⍝ * 1.1.0 from 2023-05-21 - ⍝ * Under Linux, the `Add` method keeps failing every now and then for no apparent reason. Doe retry now. + ⍝ * 1.1.0 from 2023-06-12 + ⍝ * On Linux, the `Add` method keeps failing every now and then for no apparent reason. Retries now. ⍝ * Many errors signalled by the `Assert` function did not incorporate a message when they should and could ⍝ * 1.0.2 from 2023-04-09 ⍝ * Bug fix in `UnzipTo`: issues with spaces in filenames on non-Windows platforms diff --git a/Assets/docs/InstallingAndUpdatingTheTatinClient.html b/Assets/docs/InstallingAndUpdatingTheTatinClient.html index bae6c80c8..2d8a72177 100644 --- a/Assets/docs/InstallingAndUpdatingTheTatinClient.html +++ b/Assets/docs/InstallingAndUpdatingTheTatinClient.html @@ -76,7 +76,7 @@

4. Updating Tatin in 18.0 and 18.2You've installed Tatin before

In that case you need to perform some action, depending on the version of Tatin you've have installed

Tatin version 0.100.1 and earlier

-

0.100.1 and ealier version were installed into the MyUCMDs/ folder.

+

0.100.1 and earlier versions were installed into the MyUCMDs/ folder.

That has changed: it's now installed into a different folder. Please delete it from MyUCMDs/.

Any Tatin version 0.101.0 and 0.100.1

These versions told you to install Tatin into

diff --git a/Assets/docs/InstallingTheTatinServer.html b/Assets/docs/InstallingTheTatinServer.html index bb504ab9f..bfe8e946b 100644 --- a/Assets/docs/InstallingTheTatinServer.html +++ b/Assets/docs/InstallingTheTatinServer.html @@ -386,7 +386,7 @@

10. Misc

11. Updating the Server

-

Before updating the server you must read the release notes!. The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance.

+

Before updating the server you must read the release notes! The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance.

Entries in the INI file might have been added or removed. Whether that is the case will be revealed by the release notes. If something has changed follow the release notes. Do not replace the INI file!

-

The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: quite several settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell.

+

The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: a number of settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell.

13. Assets

diff --git a/Assets/docs/PackageConfiguration.html b/Assets/docs/PackageConfiguration.html index 22679a123..aea19dcb4 100644 --- a/Assets/docs/PackageConfiguration.html +++ b/Assets/docs/PackageConfiguration.html @@ -45,10 +45,11 @@
  • 2.3.18. project_url
  • 2.3.19. source
  • 2.3.20. tags
  • -
  • 2.3.21. userCommandScript
  • -
  • 2.3.22. version
  • -
  • 2.3.23. APL System variables: ⎕IO and ⎕ML
  • -
  • 2.3.24. Injected values +
  • 2.3.21. tatin_version
  • +
  • 2.3.22. userCommandScript
  • +
  • 2.3.23. version
  • +
  • 2.3.24. APL System variables: ⎕IO and ⎕ML
  • +
  • 2.3.25. Injected values
  • @@ -77,6 +78,7 @@

    1. Overview

    ml: 1, name: "DotNetZip", source: "DotNetZip.aplc", + tatin_version: "0.103.0", tags: "zip-tools", version: "0.5.4", } @@ -231,9 +233,8 @@

    2.3.2. assets

  • When the package configuration file is written to disk the existence of “assets” is checked. If it's not a folder an error is thrown.

    See also the GetFullPath2AssetsFolder function.

  • The assets\ folder must be considered read-only. Never write to it from a package!
  • -
  • Watch out for the convention regarding a file “LICENSE” in the root of a project; refer to the “Server-TipsAndTricks” document for details
  • Check the “files” property for files like “ReadMe.txt” and the like
  • -
  • A file named “LICENSE” in the root of a project will always be copied to the root of a package
  • +
  • A file named “LICENSE” in the root of a project will always be copied to the root of a package by convention when a package is build.
  • Accessing assets from a class instance

    @@ -296,15 +297,22 @@

    2.3.7. files

    You might want to get one or more specific files into the root of the package although they are not really assets.

    Typical examples are “LICENSE” and “ReadMe.[md|txt|html]”. Strictly speaking they are not assets because the package will still function perfectly well without them. Also, you want them to go to the root of the project, not into a sub-folder, so that they stand out.

    -

    That can be achieved by adding them to the “files” property. This can be one of:

    +

    That can be achieved by adding them to the “files” property. files can be one of:

    • Absend
    • Empty
    • A single file
    • A comma-separated list of files
    -

    Note that if it specifies a sub-folder BuildPackage will copy the file from that project-specific sub-folder to the root of the package.

    -

    See als “assets”.

    +

    Note that if it specifies a sub-folder, BuildPackage will copy the folder from that project-specific sub-folder to the root of the package.

    +

    See also “assets”.

    +
    +Information +
    +

    Note that a file LICENSE in the root of a project is by convention copied to the root of a package when a package is build.

    +
    +
    +

    2.3.8. GetFullPath2AssetsFolder

    @@ -470,8 +478,15 @@

    2.3.20. tags

    There is also no point in adding tags like “dyalog” or “apl” to a package: Tatin is a Dyalog APL package manager…

    Note that people in charge of the principal Tatin server will have an eye on the tags, and might silently correct them to keep them consistent and meaningful.

    +

    The Tatin version number without any build ID the config data was created / changed by.

    +

    You should not edit this because it is overwritten before saving the data anyway.

    +

    If a package is a user command then this must contain the path to the user command script relative to the projects root. InstallPackages uses this to identify a user command script and to move it from the source folder (if any) to the root of the install folder.

    @@ -479,7 +494,7 @@

    2.3.21. userCommandScript

    Note that for a package to be a user command the package must be constructed in a particular way. This is discussed in the “Publishing Packages” document.

    The version[2] part of the package ID[1]

    @@ -492,19 +507,19 @@

    2.3.22. version

    For details see the Tatin and Semantic Versioning document.

    By default the package configuration file carries the values of the two Dyalog parameters (environment variables) Default_IO and Default_ML for the system variables ⎕IO and ⎕ML, or, if these are not defined, ⎕IO←1 and ⎕ML←1 which are the built-in defaults.

    Tatin uses these values for setting the system variables accordingly in any namespace that is created by either the LoadPackages or the LoadDependencies function before any code is loaded into them. This is important because that makes any sub-namespace created later on inherit those values.

    The user must not specify “date”, but when a package is published the server will inject “date” as a timestamp in the format yyyymmdd.hhmmss.

    @@ -520,7 +535,7 @@
    2.3.24.1. date

    Since packages, once published, cannot be altered, it is safe to assume that the publishing date determines the correct order. However, as long as the version consists of just digits and dots, and possibly a build number, date does not play a role in determining precedence.

      diff --git a/Assets/docs/PublishingPackages.html b/Assets/docs/PublishingPackages.html index 84b08c5d1..c7652eb81 100644 --- a/Assets/docs/PublishingPackages.html +++ b/Assets/docs/PublishingPackages.html @@ -186,33 +186,39 @@
      3.1.3.1. Client
      3.1.3.2. Server
    -

    Credentials are saved in the file “Credentials.csv” in the Registry\ folder in the server's home folder.

    +

    Credentials are saved in the file “Credentials.csv” in the Registry's home folder.

    If you run your own Tatin Server we suggest that you create a UUID and use that as an API key.

    +
    +

    Email address is required

    +

    Note that having a valid API key is not enough for publishing a package: you must also define an email address for your group.

    +

    For that click “Groups > {your-group-name} > Create” and add at least an email address.

    +
    +

    For an API key to be accepted by a Tatin Server, it must be added to a file Credentials.txt in the Registry's root directory.

    Make sure that you specify it as either

    <group-name>,<api-key>

    or

    *,<api-key>
    -

    Instead of the “,” you can also use “=” as separator but that is deprecated.

    If the server finds such a file it will perform the following actions:

    • Take the data and convert it to a different format
    • Delete rows from Credential.csv that share a group name with Credentials.txt
    • -
    • Add the data to Credentials.cvs
    • -
    • Delete the file Credentials.txt.
    • +
    • Create a Salt for every API key in Credentials.txt
    • +
    • Convert every API-key and its Salt into a hash and add them together with the according group name to Credentials.csv
    • +
    • Delete the file Credentials.txt
    -

    The format of the file Credentials.csv is this:

    -
    <group-name<,<api-key-hash>,<salt>
    +

    The format of the file Credentials.csv is:

    +
    <group-name>,<api-key-hash>,<salt>
     *,<api-key-hash>,<salt>
     *
      -
    • In the first case, anybody who provides the API key the hash was produced from, can publish packages for that group.
    • -
    • In the second case, the password the hash was created from, is a kind of master password: it allows the creation of packages with any group name.
    • +
    • In the first case, anybody who provides the API key the hash was produced from can publish packages for that group.
    • +
    • In the second case, the password the hash was created from is a kind of master password: it allows the creation of packages with any group name.
    • The third case means that no API key is required for any (remaining) group(s).

    The different scenarios can be mixed:

    @@ -226,7 +232,7 @@

    3.1.4. Credentials for your *

    This is interpreted as “require API keys for the groups <group1> and <group2> but allow anything else without an API key”.

    Finally, you can allow anybody to publish packages under a particular group name without providing an API key:

    -
    <group1>,<hash1>
    +
    <group1>,<hash1>,<email-address>
     <group2>,
     *,<hash3>

    This means:

    @@ -240,9 +246,15 @@

    3.1.4. Credentials for your

    3.1.4.1. Editing the file “Credentials.csv”
    -

    There is only one reason why you might need to change the file Credentials.csv: when a group name must be deleted for some reason.

    +

    There is one reasons why you might need to change the file Credentials.csv: when a group name must be deleted for some reason.

    If a new group needs to be added, or a new API key needs to be assigned to an existing group, you must create a file Credentials.txt, see above.

    +

    The files Credentials.txt as well as Credentials.csv both allow comment lines: any line that has a ; as the very first character is regarded a comment.

    +

    4. Publishing

    diff --git a/Assets/docs/ReleaseNotes.html b/Assets/docs/ReleaseNotes.html index 270d39419..49b81d010 100644 --- a/Assets/docs/ReleaseNotes.html +++ b/Assets/docs/ReleaseNotes.html @@ -20,6 +20,9 @@
    • Release Notes
        +
      • Version 0.103.0 from 2023-11-04
      • +
      • Version 0.102.3 from 2023-10-13
      • +
      • Version 0.102.2 from 2023-10-09
      • Version 0.102.1 from 2023-10-07
      • Version 0.102.0 from 2023-10-04
      • Version 0.101.2 from 2023-09-30
      • @@ -49,6 +52,30 @@

        Release Notes

        Tatin release notes contain information regarding actions that need to be executed before a new version can be used, or oustandingly important pieces of information.

        This document does not come with a complete list of fixes, added features etc. Consult Tatin on GitHub for that.

        +
          +
        • No action required but note that the result of the API function LoadPackages has changed.
        • +
        + +
          +
        • No action required
        • +
        + +
          +
        • No action required
        • +
        +

        Version 0.102.1 from 2023-10-07

        diff --git a/Assets/docs/Server-TipsAndTricks.html b/Assets/docs/Server-TipsAndTricks.html index 1d7414b57..5a6e82b07 100644 --- a/Assets/docs/Server-TipsAndTricks.html +++ b/Assets/docs/Server-TipsAndTricks.html @@ -132,7 +132,7 @@

        2.2. Version

        2.3. Developing with a running server

        -

        You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the coder is doing, and at the same time, any changes and additions would be added to the project by Link.

        +

        You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the code is doing, and at the same time, any changes and additions would be added to the project by Link.

        Let's assume that you want to run the Tatin server that is part of the Tatin project. When the Tatin test cases are executed then Tatin would ask you whether you want to start the server automatically – that is the server we talking about, no https://test.tatin.dev

        To achieve this execute the following steps:

          @@ -163,7 +163,7 @@

          2.3. Developing with a running se

          2.3.1. Error trapping

    -

    Keep in mind that error trapping is active, so when you change a function and inject a typo it will trigger it once your code gets executed.

    +

    Keep in mind that error trapping is active, so when you change a function and inject a typo this will trigger error trapping once your code gets executed.

    If there is any danger of you locking horns with error trapping consider putting this into OnRequest:

    ⎕TRAP←0 'S' ⍝TODO⍝

    Also, make ⎕TRAP a local variable in OnRequest.

    @@ -174,10 +174,8 @@

    2.4. Developing wit

    When the test cases are executed the user is asked whether she wants the Tatin server required by the Tatin test cases to be started automatically.

    -
      -
    • If the user answers this with a “yes” and instance
    • -
    -

    You must know exactly what you are doing, otherwise, you are likely to loose code.

    +

    If the user answers this with a “yes” an instance of the server is

    +

    You must know exactly what you are doing, otherwise you are likely to loose code.

    diff --git a/Assets/docs/SyntaxReference.html b/Assets/docs/SyntaxReference.html index b286a0352..69d5a0626 100644 --- a/Assets/docs/SyntaxReference.html +++ b/Assets/docs/SyntaxReference.html @@ -743,7 +743,7 @@

    2.24. LoadPackages

    Loads the package(s) into (#|⎕SE)._tatin.{packageName} and establishes a reference for every one of them in targetSpace

    Loads all dependencies, if any, as well into (#|⎕SE)._tatin but does not create references for them in targetSpace.

    By default beta versions are considered in case the package ID is incomplete, but you can suppress them by passing 1 as .

    -

    Returns the number of packages installed, including dependencies.

    +

    Returns the number of principal packages installed.

    Note that the package ID(s) might use any case, meaning that if the package's name is foo-Goo-1.2.3 then you might as well spell it foo-GOO-1.2.3 or FOO-goo-1.2.3: it would not make a difference

    diff --git a/Assets/docs/TatinForContributors.html b/Assets/docs/TatinForContributors.html index 021bab020..b0f6a8fbe 100644 --- a/Assets/docs/TatinForContributors.html +++ b/Assets/docs/TatinForContributors.html @@ -71,8 +71,9 @@

    2. Requirements

    • Windows
    • Linux
    • +
    • Mac OS
    -

    AIX is not supported.

    +

    AIX is not supported. The PI is not officially supported but it will probably work anyway.

    You also need to have Git installed.

    Note that Tatin is managed by the Cider project management tool. If you are not familiar with Cider you are advised to spend some time playing with it before using it for serious work. 30 minutes should suffice.

    Though it is possible making changes or adding code to Tatin without Cider, using Cider makes it significantly easier. Also, the build process requires Cider.

    +

    Having said this, you don't neccessarily need to build a new version for creating a pull request, so you might get away without Cider, but using Cider is certainly recommended.

    4. How to work on Tatin

    @@ -133,11 +135,11 @@

    8. Executing the test cases

          ]Cider.RunTests
     #.Tatin.#.Tatin.TestCases.RunTests ⍝ Execute this for running the test suite

    Executing RunTests means that on Windows you will be asked whether a test server should be started. Usually, you will answer with a “Y”, and that lets the test suite start another instance of Dyalog, and run a test server in that instance.

    -

    On non-Windows platforms you will be be asked to start the server yourself and press <enter> once that is done.

    +

    On non-Windows platforms you will be be asked to start the server yourself and press <enter> once that has been done.

    Developers might also run into one of two common scenarios:

    1. During development you might want to execute all tests or just a specific group (or groups) of tests. -

      You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit windows etc.

      +

      You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit window etc.

      When a test fails then the test framework stops, and the developer can investigate on the spot.

    2. You might want to execute the test suite automatically (batch mode), meaning that the tests would not attempt to interact with a user. (This implies that test cases that depend on a user won't be executed. Tatin has only a few test cases that fall into this category; more than 95% of the tests are batchable)

      This might be required by an automated build process.

      @@ -184,7 +186,7 @@

      Starting a Tatin test server “manually”

      -

      You may specify 'view' as the left argument to force the result of that function into a read-only edit window.

      +

      You may specify 'view' as the left argument to force the result of that function into a read-only ⎕ED window.

      Let's assume that you want to execute all tests of the group API:

      T.RunThese 'API'

      Or just the tests 8 and 20 of the group API:

      @@ -293,14 +295,14 @@

      8.2.1. Running the test suite from
    3. By default all errors are trapped.

      If you need to track down a bug then you don't want this: in that case, pass a 1 as the left argument: this is treated as “debug” flag.

    4. -

      Of course RunBatchTests does not execute any test cases that require a human in front of the monitor, but the number of such tests is very small anyway.

      +

      Of course RunBatchTests does not execute any test cases that require a human in front of the monitor, but the number of such tests is small anyway.

      RunBatchTests checks the command line:

      • In case OFF2=1 was specified on the command line then ⎕OFF is executed after the last test case got executed.

        If one or more test cases failed then ⎕OFF 123 is executed. That allows the calling environment to check whether the test suite was executed successfully in its entirety or not.

      • If OFF2=1 was not specified a message is printed to the session, indicating success or failure.
      -

      Note that OFF=1 would kind of also work, but it would make Plodder ⎕OFF, the underlying HTTP server uses by Tatin. That is for some things too early. For example you cannot get a code coverage report then.

      +

      Note that OFF=1 would kind of also work, but it would make Plodder ⎕OFF, the underlying HTTP server used by Tatin. That might be too early. For example, you cannot get a code coverage report then.

      8.2.2. Running the test suite from a console or terminal

      diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbe49a66e..0bd2eebcd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,7 @@ And if you like the project, but just don't have time to contribute, that's fine - [Reporting Bugs](#reporting-bugs) - [Suggesting Enhancements](#suggesting-enhancements) + ## Code of Conduct We don't have one, and we don't need one. APLers are a friendly species. @@ -48,6 +49,7 @@ When contributing to this project, you must agree that you have authored 100% of If you want to contribute then looking at the document ["Tatin for contributors"](https://tatin.dev/Assets/docs/TatinForContributors.html) is a must. + ### Reporting Bugs @@ -70,7 +72,6 @@ A good bug report shouldn't leave others needing to chase you up for more inform > You must never report security-related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to , probably encrypted. - We use GitHub issues to track bugs and errors. If you run into an issue with the project: * Open an [Issue](/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) @@ -88,8 +89,6 @@ Once it's filed: - If the team is able to reproduce the issue, it will be labelled as a `bug`, as well as possibly other labels (such as `critical`), and the issue will be left to be implemented by someone (it could be your first code contribution?!). - - ### Suggesting Enhancements This section guides you through submitting an enhancement suggestion for Tatin, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. @@ -115,4 +114,5 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/apltea ## Attribution + This guide is based on the https://contributing.md/ diff --git a/Run.aplf b/Run.aplf index c87abf925..033cb8f45 100644 --- a/Run.aplf +++ b/Run.aplf @@ -2,7 +2,7 @@ ⍝ Brings Cider's and Tatin's APIs into ⎕SE and makes sure that the associated user commands will be found. ⍝ This is for version 19.0 ⍝ The function is part of the Tatin project. -⍝ Version 0.1.3 from 2023-10-07 ⋄ Kai Jaeger +⍝ Version 0.1.5 from 2023-11-01 ⋄ Kai Jaeger path←⊃,/1 ⎕NPARTS∊path_,'/../../SessionExtensions/CiderTatin' :If 19≤⊃(//)⎕VFI{⍵↑⍨¯1+⍵⍳'.'}2⊃# ⎕WG'APLVersion' :AndIf 0=⎕SE.⎕NC'_Tatin' @@ -20,35 +20,17 @@ :EndTrap DLB←{⍵↓⍨+/∧\' '=⍵} DTB←{⍵↓⍨-+/∧\' '=⌽⍵} - :Trap 0 - :If 'Win'≡3↑1⊃# ⎕WG'APLVersion' - res←⎕SE.SALT.Settings'cmddir' - sep←';' - paths←sep(≠⊆⊢)res + :If ⎕NEXISTS path,'/Cider' ⍝ People might not have activated Cider + :Trap 0 + res←⎕SE.Tatin.LoadDependencies(path,'/Cider')⎕SE + :If ~∨/'_Cider_'⍷∊⍕res + ⎕←'Attempt to load the "Cider" package failed' ⋄ →0 + :EndIf :Else - res←⎕SE.SALT.Settings'cmddir' - sep←':' - paths←sep(≠⊆⊢)res - :EndIf - paths←{DTB DLB ⍵}¨paths - paths←(⎕NEXISTS¨paths)/paths - paths←∪{⊃,/1 ⎕NPARTS ⍵}¨paths - :If ~(⊂path)∊paths ⍝ Already known? - {}⎕SE.SALT.Settings'cmddir ',path,(⊃,/';',¨paths),' -permanent' - :EndIf - :Else - qdmx←⎕DMX - ⎕←'Attempt to add <',path,' to the search paths for user commands failed (',qdmx.EM,', RC=',(⍕qdmx.EN),')' ⋄ →0 - :EndTrap - :Trap 0 - res←⎕SE.Tatin.LoadDependencies(path,'/Cider')⎕SE - :If ~∨/'_Cider_'⍷∊⍕res - ⎕←'Attempt to load the "Cider" package failed' ⋄ →0 - :EndIf - :Else - qdmx←⎕DMX - ⎕←'Attempt to load the "Cider" package failed (',qdmx.EM,', RC=',(⍕qdmx.EN),')' ⋄ →0 - :EndTrap + qdmx←⎕DMX + ⎕←'Attempt to load the "Cider" package failed (',qdmx.EM,', RC=',(⍕qdmx.EN),')' ⋄ →0 + :EndTrap + :EndIf :EndIf ⎕SE.⎕EX'CiderTatin' ⍝Done \ No newline at end of file diff --git a/TestData/Aliase2/apl-buildlist.json b/TestData/Aliase2/apl-buildlist.json index 9bf002974..fede77c1d 100644 --- a/TestData/Aliase2/apl-buildlist.json +++ b/TestData/Aliase2/apl-buildlist.json @@ -1 +1,44 @@ -{ packageID: [ "aplteam-WinSys-5.0.1", "aplteam-WinReg-5.0.2", "aplteam-ShowChmHelp-3.0.1", "aplteam-OS-3.0.1", "aplteam-FilesAndDirs-5.0.3", "aplteam-Execute-3.0.2", "aplteam-DotNetZip-1.0.2", "aplteam-CompareSimple-5.0.1", "aplteam-APLTreeUtils2-1.1.1", "aplteam-APLGit-0.24.0", "F@aplteam-FilesAndDirs-5.0.3", "A@aplteam-APLTreeUtils2-1.1.1", ], principal: [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, ], url: [ "https://tatin.dev/aplteam-WinSys-5.0.1", "https://tatin.dev/aplteam-WinReg-5.0.2", "https://tatin.dev/aplteam-ShowChmHelp-3.0.1", "https://tatin.dev/aplteam-OS-3.0.1", "https://tatin.dev/aplteam-FilesAndDirs-5.0.3", "https://tatin.dev/aplteam-Execute-3.0.2", "https://tatin.dev/aplteam-DotNetZip-1.0.2", "https://tatin.dev/aplteam-CompareSimple-5.0.1", "https://tatin.dev/aplteam-APLTreeUtils2-1.1.1", "./TestData/ZIP-file/aplteam-APLGit-0.24.0.zip", "https://tatin.dev/aplteam-FilesAndDirs-5.0.3", "https://tatin.dev/aplteam-APLTreeUtils2-1.1.1", ], } +{ + packageID: [ + "aplteam-WinSys-5.0.1", + "aplteam-WinReg-5.0.2", + "aplteam-ShowChmHelp-3.0.1", + "aplteam-OS-3.0.1", + "aplteam-FilesAndDirs-5.0.3", + "aplteam-Execute-3.0.2", + "aplteam-DotNetZip-1.0.2", + "aplteam-CompareSimple-5.0.1", + "aplteam-APLTreeUtils2-1.1.1", + "aplteam-APLGit-0.24.0", + "F@aplteam-FilesAndDirs-5.0.3", + "A@aplteam-APLTreeUtils2-1.1.1", + ], + principal: [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + ], + url: [ + "https://tatin.dev/aplteam-WinSys-5.0.1", + "https://tatin.dev/aplteam-WinReg-5.0.2", + "https://tatin.dev/aplteam-ShowChmHelp-3.0.1", + "https://tatin.dev/aplteam-OS-3.0.1", + "https://tatin.dev/aplteam-FilesAndDirs-5.0.3", + "https://tatin.dev/aplteam-Execute-3.0.2", + "https://tatin.dev/aplteam-DotNetZip-1.0.2", + "https://tatin.dev/aplteam-CompareSimple-5.0.1", + "https://tatin.dev/aplteam-APLTreeUtils2-1.1.1", + "./TestData/ZIP-file/aplteam-APLGit-0.24.0.zip", + "https://tatin.dev/aplteam-FilesAndDirs-5.0.3", + "https://tatin.dev/aplteam-APLTreeUtils2-1.1.1", + ], +} diff --git a/TestData/Build/CiderProject/apl-package.json b/TestData/Build/CiderProject/apl-package.json index fa39af327..db2e1165a 100644 --- a/TestData/Build/CiderProject/apl-package.json +++ b/TestData/Build/CiderProject/apl-package.json @@ -19,5 +19,5 @@ source: "APLSource/Seven7Zip.aplc", tags: "zip-tools", userCommandScript: "", - version: "9.0.133+161", + version: "9.0.5+170", } diff --git a/TestData/Build/CiderProject/aplteam-Seven7Zip-9.0.30.zip b/TestData/Build/CiderProject/aplteam-Seven7Zip-9.0.30.zip deleted file mode 100644 index e69de29bb..000000000 diff --git a/TestData/Build/Default/apl-package.json b/TestData/Build/Default/apl-package.json index a781a6b39..04a6812e5 100644 --- a/TestData/Build/Default/apl-package.json +++ b/TestData/Build/Default/apl-package.json @@ -19,5 +19,5 @@ source: "APLSource/Seven7Zip.aplc", tags: "zip-tools", userCommandScript: "", - version: "9.0.1+252", + version: "9.0.1+271", } diff --git a/TestData/Build/Special/apl-package.json b/TestData/Build/Special/apl-package.json index c308be886..c1382a8d9 100644 --- a/TestData/Build/Special/apl-package.json +++ b/TestData/Build/Special/apl-package.json @@ -19,5 +19,5 @@ source: "APLSource/Seven7Zip.aplc", tags: "zip-tools", userCommandScript: "", - version: "9.0.1+341", + version: "9.0.1+378", } diff --git a/TestData/Build/UserCommand/apl-package.json b/TestData/Build/UserCommand/apl-package.json index da753bdfc..df45c5a5b 100644 --- a/TestData/Build/UserCommand/apl-package.json +++ b/TestData/Build/UserCommand/apl-package.json @@ -19,5 +19,5 @@ source: "APLSource/APLGit2ForTests", tags: "apl-git-interface", userCommandScript: "APLSource/APLGit2ForTests_UC.dyalog", - version: "0.9.0+326", + version: "0.9.0+335", } diff --git a/TestData/DifferentCase/apl-package.json b/TestData/DifferentCase/apl-package.json index 50627a588..19413f50e 100644 --- a/TestData/DifferentCase/apl-package.json +++ b/TestData/DifferentCase/apl-package.json @@ -19,5 +19,5 @@ source: "APLprocess.aplc", tags: "process", userCommandScript: "", - version: "20.99.99+2695", + version: "20.99.99+2704", } diff --git a/TestData/InstallFromList/apl-buildlist.json b/TestData/InstallFromList/apl-buildlist.json index abea5af5b..78c094614 100644 --- a/TestData/InstallFromList/apl-buildlist.json +++ b/TestData/InstallFromList/apl-buildlist.json @@ -1 +1,26 @@ -{ packageID: [ "aplteam-Tester2-3.0.1", "aplteam-CodeCoverage-0.7.1", "aplteam-OS-3.0.0", "aplteam-IniFiles-5.0.1", "aplteam-FilesAndDirs-5.0.0", "aplteam-APLTreeUtils2-1.1.0", ], principal: [ 1, 1, 0, 0, 0, 0, ], url: [ "https://tatin.dev/aplteam-Tester2-3.0.1", "https://tatin.dev/aplteam-CodeCoverage-0.7.1", "https://tatin.dev/aplteam-OS-3.0.0", "https://tatin.dev/aplteam-IniFiles-5.0.1", "https://tatin.dev/aplteam-FilesAndDirs-5.0.0", "https://tatin.dev/aplteam-APLTreeUtils2-1.1.0", ], } +{ + packageID: [ + "aplteam-Tester2-3.0.1", + "aplteam-CodeCoverage-0.7.1", + "aplteam-OS-3.0.0", + "aplteam-IniFiles-5.0.1", + "aplteam-FilesAndDirs-5.0.0", + "aplteam-APLTreeUtils2-1.1.0", + ], + principal: [ + 1, + 1, + 0, + 0, + 0, + 0, + ], + url: [ + "https://tatin.dev/aplteam-Tester2-3.0.1", + "https://tatin.dev/aplteam-CodeCoverage-0.7.1", + "https://tatin.dev/aplteam-OS-3.0.0", + "https://tatin.dev/aplteam-IniFiles-5.0.1", + "https://tatin.dev/aplteam-FilesAndDirs-5.0.0", + "https://tatin.dev/aplteam-APLTreeUtils2-1.1.0", + ], +} diff --git a/TestData/Source/APLTree/apl-package.json b/TestData/Source/APLTree/apl-package.json index 713375a1e..bf47839af 100644 --- a/TestData/Source/APLTree/apl-package.json +++ b/TestData/Source/APLTree/apl-package.json @@ -19,5 +19,5 @@ source: "APLTreeUtils.apln", tags: "", userCommandScript: "", - version: "6.0.0+113", + version: "6.0.0+122", } diff --git a/TestData/Source/F-D/apl-package.json b/TestData/Source/F-D/apl-package.json index f67a2533e..6a94f5bbf 100644 --- a/TestData/Source/F-D/apl-package.json +++ b/TestData/Source/F-D/apl-package.json @@ -19,5 +19,5 @@ source: "FilesAndDirs.aplc", tags: "", userCommandScript: "", - version: "3.0.0+113", + version: "3.0.0+122", } diff --git a/TestData/Source/OS/apl-package.json b/TestData/Source/OS/apl-package.json index 8de8eaaa4..716b8adde 100644 --- a/TestData/Source/OS/apl-package.json +++ b/TestData/Source/OS/apl-package.json @@ -19,5 +19,5 @@ source: "OS.aplc", tags: "foo,boo", userCommandScript: "", - version: "2.0.0+353", + version: "2.0.0+381", } diff --git a/TestData/tatin-client.json b/TestData/tatin-client.json index d72960d30..f0df5a55c 100644 --- a/TestData/tatin-client.json +++ b/TestData/tatin-client.json @@ -1 +1 @@ -{ registries: [ { alias: "local", api_key: "", port: 0, priority: 1000, uri: "C:\\Users\\kai\\AppData\\Local\\Temp\\Tatin-Test-Registry_121432420_0", }, { alias: "tatin", api_key: "", port: 0, priority: 100, uri: "https://tatin.dev/", }, { alias: "localhost", api_key: "Test-API-Key", port: 5001, priority: 90, uri: "https://localhost/", }, { alias: "tatin-test", api_key: "Tatin-Test-API-Key", port: 0, priority: 0, uri: "https://test.tatin.dev/", }, ], source: "", } +{ registries: [ { alias: "local", api_key: "", port: 0, priority: 1000, uri: "C:\\Users\\kai\\AppData\\Local\\Temp\\Tatin-Test-Registry_165445822_0", }, { alias: "tatin", api_key: "", port: 0, priority: 100, uri: "https://tatin.dev/", }, { alias: "localhost", api_key: "Test-API-Key", port: 5001, priority: 90, uri: "https://localhost/", }, { alias: "tatin-test", api_key: "Tatin-Test-API-Key", port: 0, priority: 0, uri: "https://test.tatin.dev/", }, ], source: "", } diff --git a/TestServer/Groups/aplteam.dcf b/TestServer/Groups/aplteam.dcf index fa63d5d25..14e760c52 100644 Binary files a/TestServer/Groups/aplteam.dcf and b/TestServer/Groups/aplteam.dcf differ diff --git a/TestServer/Registry/tatin_descriptions.json b/TestServer/Registry/tatin_descriptions.json index aa6abe776..808b6a803 100644 --- a/TestServer/Registry/tatin_descriptions.json +++ b/TestServer/Registry/tatin_descriptions.json @@ -1 +1 @@ -Git interface from Dyalog APL via Git Bash Git interface from Dyalog APL via Git Bash Git interface from Dyalog APL via Git Bash Git interface from Dyalog APL via Git Bash Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL process from within Dyalog APL Start an APL session from within APL platform independently General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities required by most members of the APLTree library General utilities required by most members of the APLTree library General utilities required by most members of the APLTree library General utilities required by most members of the APLTree library Allows comparing and merging objects in the workspace with a file or a file with another file Allows comparing objects in the workspace with a file or a file with another file Allows comparing objects in the workspace with a file or a file with another file Allows comparing objects in the workspace with a file or a file with another file Zipping and unzipping with.NET Core on all major platforms Zipping and unzipping with.NET Core on all major platforms Zipping and unzipping with.NET Core on all major platforms Zipping and unzipping with.NET Core on all major platforms Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Start a process from within APL Start a process from within APL Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Test Allows instantiating good old INI files in APL; comes with extend syntax supporting APL-like data structures Allows instantiating good old INI files in APL; comes with extend syntax supporting APL-like data structures Allows instantiating good old INI files in APL; comes with extend syntax supporting APL-like data structures Managing and displaying help pages based on markdown files Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 OS-related tools for all platofrm OS-related tools for all platofrm OS-related tools for all platofrm OS-related tools for all platofrm OS-related tools for all major platforms Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Tool for zipping/unzipping under program control Tool for zipping/unzipping under program control Zip files with the Open Source tool 7Zip Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Test framework for Dyalog APL Test framework for Dyalog APL Test framework for Dyalog APL Test framework for Dyalog APL Test framework for Dyalog APL Tools to read from and write to the Windows Event Log Tools to read from and write to the Windows Event Log Tools to read from and write to the Windows Event Log Tools to read from and write to the Windows Event Log Tools for dealing with the Windows Registry Tools for dealing with the Windows Registry Tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Interface for many of the Windows operating system tools Interface for many of the Windows operating system tools Start an APL session from within APL platform independently Has two depedencies, one one a different server Has one dependencies Has NO depedencies and NO file apl-dependencies.txt This and that Modified by maintenance file Has NO depedencies but a file apl-dependencies.txt (empty) Has NO depedencies but a file apl-dependencies.txt (empty) This and that This and that Has NO depedencies and NO file apl-dependencies.txt Has one depedencies Pretends to be a user command Pretends to be a user command This and that This and that This and that This and that This and that This and that This and that This and that Has NO depedencies and NO file apl-dependencies.txt \ No newline at end of file +Git interface from Dyalog APL via Git Bash Git interface from Dyalog APL via Git Bash Git interface from Dyalog APL via Git Bash Git interface from Dyalog APL via Git Bash Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL session from within APL platform independently Start an APL process from within Dyalog APL Start an APL session from within APL platform independently General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities :Included by most members of the APLTree library General utilities required by most members of the APLTree library General utilities required by most members of the APLTree library General utilities required by most members of the APLTree library General utilities required by most members of the APLTree library Allows comparing and merging objects in the workspace with a file or a file with another file Allows comparing objects in the workspace with a file or a file with another file Allows comparing objects in the workspace with a file or a file with another file Allows comparing objects in the workspace with a file or a file with another file Zipping and unzipping with.NET Core on all major platforms Zipping and unzipping with.NET Core on all major platforms Zipping and unzipping with.NET Core on all major platforms Zipping and unzipping with.NET Core on all major platforms Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Constants with meaningful names for Dyalog error codes Start a process from within APL Start a process from within APL Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Utilities for doing gymnastics with files and directories Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Allows to catch errors on an application level; saves information that allow analyzing the error Test Allows instantiating good old INI files in APL; comes with extend syntax supporting APL-like data structures Allows instantiating good old INI files in APL; comes with extend syntax supporting APL-like data structures Allows instantiating good old INI files in APL; comes with extend syntax supporting APL-like data structures Managing and displaying help pages based on markdown files Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 Converts Markdown to HTML5 OS-related tools for all platofrm OS-related tools for all platofrm OS-related tools for all platofrm OS-related tools for all platofrm OS-related tools for all major platforms Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Controlling an APL session remotely via the Ride protocol Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Interface between Windows' SCM (Service Control Manager) and applications running as a service Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Zip files with the Open Source tool 7Zip Tool for zipping/unzipping under program control Tool for zipping/unzipping under program control Zip files with the Open Source tool 7Zip Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Display CHM files under program control (Windows only) Test framework for Dyalog APL Test framework for Dyalog APL Test framework for Dyalog APL Test framework for Dyalog APL Test framework for Dyalog APL Tools to read from and write to the Windows Event Log Tools to read from and write to the Windows Event Log Tools to read from and write to the Windows Event Log Tools to read from and write to the Windows Event Log Tools for dealing with the Windows Registry Tools for dealing with the Windows Registry Tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Limited set of tools for dealing with the Windows Registry Interface for many of the Windows operating system tools Interface for many of the Windows operating system tools Start an APL session from within APL platform independently Has two depedencies, one one a different server Has one dependencies Has NO depedencies and NO file apl-dependencies.txt This and that Modified by maintenance file Has NO depedencies but a file apl-dependencies.txt (empty) Has NO depedencies but a file apl-dependencies.txt (empty) This and that This and that Has NO depedencies and NO file apl-dependencies.txt Has one depedencies Pretends to be a user command Pretends to be a user command This and that This and that This and that This and that This and that This and that This and that This and that Has NO depedencies and NO file apl-dependencies.txt \ No newline at end of file diff --git a/TestServer/Registry/tatin_index.txt b/TestServer/Registry/tatin_index.txt index faee4c37c..ad2ea7e06 100644 --- a/TestServer/Registry/tatin_index.txt +++ b/TestServer/Registry/tatin_index.txt @@ -1 +1 @@ -aplteam-APLGit-0.24.0 aplteam-APLGit-0.24.2 aplteam-APLGit-0.24.4 aplteam-APLGit-0.24.5 aplteam-APLProcess-0.2.0 aplteam-APLProcess-0.2.1 aplteam-APLProcess-0.2.2 aplteam-APLProcess-0.2.7 aplteam-APLProcess-0.2.9 aplteam-APLProcess-0.2.10 aplteam-APLProcess-0.2.11 aplteam-APLProcess-0.2.12 aplteam-APLProcess-0.3.0 aplteam-APLProcess-0.5.1 aplteam-APLProcess-1.0.0 aplteam-APLTreeUtils-6.0.0 aplteam-APLTreeUtils-6.0.1 aplteam-APLTreeUtils-6.0.9 aplteam-APLTreeUtils-6.0.10 aplteam-APLTreeUtils-6.0.11 aplteam-APLTreeUtils-7.0.0 aplteam-APLTreeUtils2-1.1.1 aplteam-APLTreeUtils2-1.1.2 aplteam-APLTreeUtils2-1.1.3 aplteam-APLTreeUtils2-1.2.0 aplteam-Compare-5.0.0 aplteam-CompareSimple-3.0.2 aplteam-CompareSimple-3.0.5 aplteam-CompareSimple-3.0.6 aplteam-DotNetZip-0.5.1 aplteam-DotNetZip-0.5.2 aplteam-DotNetZip-0.5.3 aplteam-DotNetZip-0.5.4 aplteam-EventCodes-2.0.6 aplteam-EventCodes-2.0.10 aplteam-EventCodes-2.1.0 aplteam-EventCodes-2.1.1 aplteam-EventCodes-2.1.2 aplteam-Execute-2.0.0 aplteam-Execute-3.0.1 aplteam-FilesAndDirs-3.2.1 aplteam-FilesAndDirs-3.2.7 aplteam-FilesAndDirs-3.2.8 aplteam-FilesAndDirs-3.2.9 aplteam-FilesAndDirs-3.2.15 aplteam-FilesAndDirs-3.2.16 aplteam-HandleError-3.0.0 aplteam-HandleError-3.0.1 aplteam-HandleError-3.0.2 aplteam-HandleError-3.0.8 aplteam-HandleError-3.0.9 aplteam-HandleError-3.0.10 aplteam-Hello-0.1.0 aplteam-IniFiles-4.0.0 aplteam-IniFiles-4.0.1 aplteam-IniFiles-4.0.4 aplteam-Laguntza-5.0.0-beta-1 aplteam-MarkAPL-9.1.0 aplteam-MarkAPL-9.1.1 aplteam-MarkAPL-9.1.9 aplteam-MarkAPL-9.1.13 aplteam-MarkAPL-10.0.0 aplteam-MarkAPL-11.0.3 aplteam-OS-2.0.1 aplteam-OS-2.0.2 aplteam-OS-2.0.3 aplteam-OS-2.0.4 aplteam-OS-3.1.1 aplteam-RideClient-0.6.0 aplteam-RideClient-0.6.1 aplteam-RideClient-0.6.2 aplteam-RideClient-0.6.3 aplteam-RideClient-0.6.4 aplteam-RideClient-0.6.10 aplteam-RideClient-0.7.0 aplteam-RideClient-0.7.1 aplteam-ServiceState-2.0.0 aplteam-ServiceState-2.0.1 aplteam-ServiceState-2.0.2 aplteam-ServiceState-2.0.8 aplteam-ServiceState-2.0.9 aplteam-ServiceState-2.0.10 aplteam-ServiceState-2.0.11 aplteam-Seven7Zip-9.0.1 aplteam-Seven7Zip-9.0.4 aplteam-Seven7Zip-9.0.7 aplteam-Seven7Zip-9.0.8 aplteam-Seven7Zip-9.0.9 aplteam-Seven7Zip-9.0.10 aplteam-Seven7Zip-9.0.69 aplteam-Seven7Zip-9.0.100 aplteam-Seven7Zip-9.0.101 aplteam-Seven7Zip-9.0.122 aplteam-SevenZip-3.0.0 aplteam-SevenZip-3.0.1 aplteam-SevenZip-4.0.4 aplteam-ShowChmHelp-2.0.0 aplteam-ShowChmHelp-2.0.1 aplteam-ShowChmHelp-2.0.2 aplteam-ShowChmHelp-2.0.3 aplteam-ShowChmHelp-2.0.4 aplteam-Tester2-2.2.2 aplteam-Tester2-2.2.3 aplteam-Tester2-2.2.4 aplteam-Tester2-2.2.5 aplteam-Tester2-2.2.21 aplteam-WindowsEventLog-2.0.0 aplteam-WindowsEventLog-2.0.1 aplteam-WindowsEventLog-2.0.2 aplteam-WindowsEventLog-2.0.3 aplteam-WinReg-4.0.0 aplteam-WinReg-4.0.1 aplteam-WinReg-4.0.2 aplteam-WinRegSimple-2.0.0 aplteam-WinRegSimple-2.0.1 aplteam-WinRegSimple-2.0.2 aplteam-WinRegSimple-2.0.3 aplteam-WinRegSimple-2.0.4 aplteam-WinSys-4.0.0 aplteam-WinSys-4.0.1 dyalog-APLProcess-1.0.0 elsewhere-Boo-3.1.2 elsewhere-strange-1.0.0 example-Boo-1.0.0 example-deprecated-1.0.0 example-deprecated-1.1.0 example-Foo-1.0.0 example-Goo-2.0.0 example-Goo-2.1.0 example-Joo-1.0.0 example-JustOneBeta-0.1.0-beta-1 example-Moo-1.0.1 example-Too-1.0.0 example-UC-1.0.0 example-UC_2-1.0.0 example-versions-0.1.0 example-versions-1.0.0-beta-1 example-versions-1.0.0 example-versions-1.0.1-This-fix example-versions-1.0.1-That-fix example-versions-1.0.12-zzz example-versions-1.0.12-That-fix example-versions-2.0.0 example-Zoo-3.0.0 group1-Foo-1.0.0 group2-Foo-1.1.0 \ No newline at end of file +aplteam-APLGit-0.24.0 aplteam-APLGit-0.24.2 aplteam-APLGit-0.24.4 aplteam-APLGit-0.24.5 aplteam-APLProcess-0.2.0 aplteam-APLProcess-0.2.1 aplteam-APLProcess-0.2.2 aplteam-APLProcess-0.2.7 aplteam-APLProcess-0.2.9 aplteam-APLProcess-0.2.10 aplteam-APLProcess-0.2.11 aplteam-APLProcess-0.2.12 aplteam-APLProcess-0.3.0 aplteam-APLProcess-0.5.1 aplteam-APLProcess-1.0.0 aplteam-APLTreeUtils-6.0.0 aplteam-APLTreeUtils-6.0.1 aplteam-APLTreeUtils-6.0.9 aplteam-APLTreeUtils-6.0.10 aplteam-APLTreeUtils-6.0.11 aplteam-APLTreeUtils-7.0.0 aplteam-APLTreeUtils2-1.1.1 aplteam-APLTreeUtils2-1.1.2 aplteam-APLTreeUtils2-1.1.3 aplteam-APLTreeUtils2-1.2.0 aplteam-Compare-5.0.0 aplteam-CompareSimple-3.0.2 aplteam-CompareSimple-3.0.5 aplteam-CompareSimple-3.0.6 aplteam-DotNetZip-0.5.1 aplteam-DotNetZip-0.5.2 aplteam-DotNetZip-0.5.3 aplteam-DotNetZip-0.5.4 aplteam-EventCodes-2.0.6 aplteam-EventCodes-2.0.10 aplteam-EventCodes-2.1.0 aplteam-EventCodes-2.1.1 aplteam-EventCodes-2.1.2 aplteam-Execute-2.0.0 aplteam-Execute-3.0.1 aplteam-FilesAndDirs-3.2.1 aplteam-FilesAndDirs-3.2.7 aplteam-FilesAndDirs-3.2.8 aplteam-FilesAndDirs-3.2.9 aplteam-FilesAndDirs-3.2.15 aplteam-FilesAndDirs-3.2.16 aplteam-HandleError-3.0.0 aplteam-HandleError-3.0.1 aplteam-HandleError-3.0.2 aplteam-HandleError-3.0.8 aplteam-HandleError-3.0.9 aplteam-HandleError-3.0.10 aplteam-Hello-0.1.0 aplteam-IniFiles-4.0.0 aplteam-IniFiles-4.0.1 aplteam-IniFiles-4.0.4 aplteam-Laguntza-6.0.0-beta-1 aplteam-MarkAPL-9.1.0 aplteam-MarkAPL-9.1.1 aplteam-MarkAPL-9.1.9 aplteam-MarkAPL-9.1.13 aplteam-MarkAPL-10.0.0 aplteam-MarkAPL-11.0.3 aplteam-OS-2.0.1 aplteam-OS-2.0.2 aplteam-OS-2.0.3 aplteam-OS-2.0.4 aplteam-OS-3.1.1 aplteam-RideClient-0.6.0 aplteam-RideClient-0.6.1 aplteam-RideClient-0.6.2 aplteam-RideClient-0.6.3 aplteam-RideClient-0.6.4 aplteam-RideClient-0.6.10 aplteam-RideClient-0.7.0 aplteam-RideClient-0.7.1 aplteam-ServiceState-2.0.0 aplteam-ServiceState-2.0.1 aplteam-ServiceState-2.0.2 aplteam-ServiceState-2.0.8 aplteam-ServiceState-2.0.9 aplteam-ServiceState-2.0.10 aplteam-ServiceState-2.0.11 aplteam-Seven7Zip-9.0.4 aplteam-Seven7Zip-9.0.7 aplteam-Seven7Zip-9.0.8 aplteam-Seven7Zip-9.0.9 aplteam-Seven7Zip-9.0.10 aplteam-SevenZip-3.0.0 aplteam-SevenZip-3.0.1 aplteam-SevenZip-4.0.4 aplteam-ShowChmHelp-2.0.0 aplteam-ShowChmHelp-2.0.1 aplteam-ShowChmHelp-2.0.2 aplteam-ShowChmHelp-2.0.3 aplteam-ShowChmHelp-2.0.4 aplteam-Tester2-2.2.2 aplteam-Tester2-2.2.3 aplteam-Tester2-2.2.4 aplteam-Tester2-2.2.5 aplteam-Tester2-2.2.21 aplteam-WindowsEventLog-2.0.0 aplteam-WindowsEventLog-2.0.1 aplteam-WindowsEventLog-2.0.2 aplteam-WindowsEventLog-2.0.3 aplteam-WinReg-4.0.0 aplteam-WinReg-4.0.1 aplteam-WinReg-4.0.2 aplteam-WinRegSimple-2.0.0 aplteam-WinRegSimple-2.0.1 aplteam-WinRegSimple-2.0.2 aplteam-WinRegSimple-2.0.3 aplteam-WinRegSimple-2.0.4 aplteam-WinSys-4.0.0 aplteam-WinSys-4.0.1 dyalog-APLProcess-1.0.0 elsewhere-Boo-3.1.2 elsewhere-strange-1.0.0 example-Boo-1.0.0 example-deprecated-1.0.0 example-deprecated-1.1.0 example-Foo-1.0.0 example-Goo-2.0.0 example-Goo-2.1.0 example-Joo-1.0.0 example-JustOneBeta-0.1.0-beta-1 example-Moo-1.0.1 example-Too-1.0.0 example-UC-1.0.0 example-UC_2-1.0.0 example-versions-0.1.0 example-versions-1.0.0-beta-1 example-versions-1.0.0 example-versions-1.0.1-This-fix example-versions-1.0.1-That-fix example-versions-1.0.12-zzz example-versions-1.0.12-That-fix example-versions-2.0.0 example-Zoo-3.0.0 group1-Foo-1.0.0 group2-Foo-1.1.0 \ No newline at end of file diff --git a/TestServer/Registry/tatin_os.txt b/TestServer/Registry/tatin_os.txt index 774620c81..127b14c07 100644 --- a/TestServer/Registry/tatin_os.txt +++ b/TestServer/Registry/tatin_os.txt @@ -54,7 +54,7 @@ aplteam-Hello-0.1.0 0 0 1 aplteam-IniFiles-4.0.0 0 0 1 aplteam-IniFiles-4.0.1 0 0 1 aplteam-IniFiles-4.0.4 0 0 1 -aplteam-Laguntza-5.0.0-beta-1 0 0 1 +aplteam-Laguntza-6.0.0-beta-1 0 0 1 aplteam-MarkAPL-9.1.0 0 0 1 aplteam-MarkAPL-9.1.1 0 0 1 aplteam-MarkAPL-9.1.9 0 0 1 @@ -81,16 +81,11 @@ aplteam-ServiceState-2.0.8 0 0 1 aplteam-ServiceState-2.0.9 0 0 1 aplteam-ServiceState-2.0.10 0 0 1 aplteam-ServiceState-2.0.11 0 0 1 -aplteam-Seven7Zip-9.0.1 0 0 1 aplteam-Seven7Zip-9.0.4 1 1 1 aplteam-Seven7Zip-9.0.7 1 1 1 aplteam-Seven7Zip-9.0.8 1 1 1 aplteam-Seven7Zip-9.0.9 1 1 1 aplteam-Seven7Zip-9.0.10 1 1 1 -aplteam-Seven7Zip-9.0.69 1 1 1 -aplteam-Seven7Zip-9.0.100 1 1 1 -aplteam-Seven7Zip-9.0.101 1 1 1 -aplteam-Seven7Zip-9.0.122 1 1 1 aplteam-SevenZip-3.0.0 1 1 1 aplteam-SevenZip-3.0.1 1 1 1 aplteam-SevenZip-4.0.4 1 1 1 diff --git a/TestServer/Registry/tatin_tags.txt b/TestServer/Registry/tatin_tags.txt index 08952fc82..1b0a67385 100644 --- a/TestServer/Registry/tatin_tags.txt +++ b/TestServer/Registry/tatin_tags.txt @@ -110,22 +110,17 @@ 89 zip-tools 90 zip-tools 91 zip-tools -92 zip-tools -93 zip-tools -94 zip-tools -95 zip-tools -96 zip-tools -102 test-framework -102 unit-tests -103 test-framework -103 unit-tests -104 test-framework -104 unit-tests -105 test-framework -105 unit-tests -106 test-framework -106 unit-tests -111 windows-registry -112 windows-registry -113 windows-registry -121 processes +97 test-framework +97 unit-tests +98 test-framework +98 unit-tests +99 test-framework +99 unit-tests +100 test-framework +100 unit-tests +101 test-framework +101 unit-tests +106 windows-registry +107 windows-registry +108 windows-registry +116 processes diff --git a/TestServer/Registry/tatin_uc.txt b/TestServer/Registry/tatin_uc.txt index 102ff2737..3b53860cf 100644 --- a/TestServer/Registry/tatin_uc.txt +++ b/TestServer/Registry/tatin_uc.txt @@ -112,11 +112,6 @@ - - - - - diff --git a/TestServer/Server/Assets/docs/InstallingAndUpdatingTheTatinClient.html b/TestServer/Server/Assets/docs/InstallingAndUpdatingTheTatinClient.html index bae6c80c8..2d8a72177 100644 --- a/TestServer/Server/Assets/docs/InstallingAndUpdatingTheTatinClient.html +++ b/TestServer/Server/Assets/docs/InstallingAndUpdatingTheTatinClient.html @@ -76,7 +76,7 @@

      4. Updating Tatin in 18.0 and 18.2You've installed Tatin before

      In that case you need to perform some action, depending on the version of Tatin you've have installed

      Tatin version 0.100.1 and earlier

      -

      0.100.1 and ealier version were installed into the MyUCMDs/ folder.

      +

      0.100.1 and earlier versions were installed into the MyUCMDs/ folder.

      That has changed: it's now installed into a different folder. Please delete it from MyUCMDs/.

      Any Tatin version 0.101.0 and 0.100.1

      These versions told you to install Tatin into

      diff --git a/TestServer/Server/Assets/docs/InstallingTheTatinServer.html b/TestServer/Server/Assets/docs/InstallingTheTatinServer.html index bb504ab9f..bfe8e946b 100644 --- a/TestServer/Server/Assets/docs/InstallingTheTatinServer.html +++ b/TestServer/Server/Assets/docs/InstallingTheTatinServer.html @@ -386,7 +386,7 @@

      10. Misc

      11. Updating the Server

      -

      Before updating the server you must read the release notes!. The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance.

      +

      Before updating the server you must read the release notes! The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance.

      Entries in the INI file might have been added or removed. Whether that is the case will be revealed by the release notes. If something has changed follow the release notes. Do not replace the INI file!

      -

      The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: quite several settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell.

      +

      The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: a number of settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell.

    5. @@ -77,6 +78,7 @@

      1. Overview

      ml: 1, name: "DotNetZip", source: "DotNetZip.aplc", + tatin_version: "0.103.0", tags: "zip-tools", version: "0.5.4", }
      @@ -231,9 +233,8 @@

      2.3.2. assets

    6. When the package configuration file is written to disk the existence of “assets” is checked. If it's not a folder an error is thrown.

      See also the GetFullPath2AssetsFolder function.

    7. The assets\ folder must be considered read-only. Never write to it from a package!
    8. -
    9. Watch out for the convention regarding a file “LICENSE” in the root of a project; refer to the “Server-TipsAndTricks” document for details
    10. Check the “files” property for files like “ReadMe.txt” and the like
    11. -
    12. A file named “LICENSE” in the root of a project will always be copied to the root of a package
    13. +
    14. A file named “LICENSE” in the root of a project will always be copied to the root of a package by convention when a package is build.
    15. Accessing assets from a class instance

      @@ -296,15 +297,22 @@

      2.3.7. files

      You might want to get one or more specific files into the root of the package although they are not really assets.

      Typical examples are “LICENSE” and “ReadMe.[md|txt|html]”. Strictly speaking they are not assets because the package will still function perfectly well without them. Also, you want them to go to the root of the project, not into a sub-folder, so that they stand out.

      -

      That can be achieved by adding them to the “files” property. This can be one of:

      +

      That can be achieved by adding them to the “files” property. files can be one of:

      • Absend
      • Empty
      • A single file
      • A comma-separated list of files
      -

      Note that if it specifies a sub-folder BuildPackage will copy the file from that project-specific sub-folder to the root of the package.

      -

      See als “assets”.

      +

      Note that if it specifies a sub-folder, BuildPackage will copy the folder from that project-specific sub-folder to the root of the package.

      +

      See also “assets”.

      +
      +Information +
      +

      Note that a file LICENSE in the root of a project is by convention copied to the root of a package when a package is build.

      +
      +
      +

      2.3.8. GetFullPath2AssetsFolder

      @@ -470,8 +478,15 @@

      2.3.20. tags

      There is also no point in adding tags like “dyalog” or “apl” to a package: Tatin is a Dyalog APL package manager…

      Note that people in charge of the principal Tatin server will have an eye on the tags, and might silently correct them to keep them consistent and meaningful.

      +

      The Tatin version number without any build ID the config data was created / changed by.

      +

      You should not edit this because it is overwritten before saving the data anyway.

      +

      If a package is a user command then this must contain the path to the user command script relative to the projects root. InstallPackages uses this to identify a user command script and to move it from the source folder (if any) to the root of the install folder.

      @@ -479,7 +494,7 @@

      2.3.21. userCommandScript

      Note that for a package to be a user command the package must be constructed in a particular way. This is discussed in the “Publishing Packages” document.

      The version[2] part of the package ID[1]

      @@ -492,19 +507,19 @@

      2.3.22. version

      For details see the Tatin and Semantic Versioning document.

      By default the package configuration file carries the values of the two Dyalog parameters (environment variables) Default_IO and Default_ML for the system variables ⎕IO and ⎕ML, or, if these are not defined, ⎕IO←1 and ⎕ML←1 which are the built-in defaults.

      Tatin uses these values for setting the system variables accordingly in any namespace that is created by either the LoadPackages or the LoadDependencies function before any code is loaded into them. This is important because that makes any sub-namespace created later on inherit those values.

      The user must not specify “date”, but when a package is published the server will inject “date” as a timestamp in the format yyyymmdd.hhmmss.

      @@ -520,7 +535,7 @@
      2.3.24.1. date

      Since packages, once published, cannot be altered, it is safe to assume that the publishing date determines the correct order. However, as long as the version consists of just digits and dots, and possibly a build number, date does not play a role in determining precedence.

        diff --git a/TestServer/Server/Assets/docs/PublishingPackages.html b/TestServer/Server/Assets/docs/PublishingPackages.html index 84b08c5d1..c7652eb81 100644 --- a/TestServer/Server/Assets/docs/PublishingPackages.html +++ b/TestServer/Server/Assets/docs/PublishingPackages.html @@ -186,33 +186,39 @@
        3.1.3.1. Client
        3.1.3.2. Server
      -

      Credentials are saved in the file “Credentials.csv” in the Registry\ folder in the server's home folder.

      +

      Credentials are saved in the file “Credentials.csv” in the Registry's home folder.

      If you run your own Tatin Server we suggest that you create a UUID and use that as an API key.

      +
      +

      Email address is required

      +

      Note that having a valid API key is not enough for publishing a package: you must also define an email address for your group.

      +

      For that click “Groups > {your-group-name} > Create” and add at least an email address.

      +
      +

      For an API key to be accepted by a Tatin Server, it must be added to a file Credentials.txt in the Registry's root directory.

      Make sure that you specify it as either

      <group-name>,<api-key>

      or

      *,<api-key>
      -

      Instead of the “,” you can also use “=” as separator but that is deprecated.

      If the server finds such a file it will perform the following actions:

      • Take the data and convert it to a different format
      • Delete rows from Credential.csv that share a group name with Credentials.txt
      • -
      • Add the data to Credentials.cvs
      • -
      • Delete the file Credentials.txt.
      • +
      • Create a Salt for every API key in Credentials.txt
      • +
      • Convert every API-key and its Salt into a hash and add them together with the according group name to Credentials.csv
      • +
      • Delete the file Credentials.txt
      -

      The format of the file Credentials.csv is this:

      -
      <group-name<,<api-key-hash>,<salt>
      +

      The format of the file Credentials.csv is:

      +
      <group-name>,<api-key-hash>,<salt>
       *,<api-key-hash>,<salt>
       *
        -
      • In the first case, anybody who provides the API key the hash was produced from, can publish packages for that group.
      • -
      • In the second case, the password the hash was created from, is a kind of master password: it allows the creation of packages with any group name.
      • +
      • In the first case, anybody who provides the API key the hash was produced from can publish packages for that group.
      • +
      • In the second case, the password the hash was created from is a kind of master password: it allows the creation of packages with any group name.
      • The third case means that no API key is required for any (remaining) group(s).

      The different scenarios can be mixed:

      @@ -226,7 +232,7 @@

      3.1.4. Credentials for your *

      This is interpreted as “require API keys for the groups <group1> and <group2> but allow anything else without an API key”.

      Finally, you can allow anybody to publish packages under a particular group name without providing an API key:

      -
      <group1>,<hash1>
      +
      <group1>,<hash1>,<email-address>
       <group2>,
       *,<hash3>

      This means:

      @@ -240,9 +246,15 @@

      3.1.4. Credentials for your

      3.1.4.1. Editing the file “Credentials.csv”
    -

    There is only one reason why you might need to change the file Credentials.csv: when a group name must be deleted for some reason.

    +

    There is one reasons why you might need to change the file Credentials.csv: when a group name must be deleted for some reason.

    If a new group needs to be added, or a new API key needs to be assigned to an existing group, you must create a file Credentials.txt, see above.

    +

    The files Credentials.txt as well as Credentials.csv both allow comment lines: any line that has a ; as the very first character is regarded a comment.

    +

    4. Publishing

    diff --git a/TestServer/Server/Assets/docs/ReleaseNotes.html b/TestServer/Server/Assets/docs/ReleaseNotes.html index 270d39419..49b81d010 100644 --- a/TestServer/Server/Assets/docs/ReleaseNotes.html +++ b/TestServer/Server/Assets/docs/ReleaseNotes.html @@ -20,6 +20,9 @@
    • Release Notes
        +
      • Version 0.103.0 from 2023-11-04
      • +
      • Version 0.102.3 from 2023-10-13
      • +
      • Version 0.102.2 from 2023-10-09
      • Version 0.102.1 from 2023-10-07
      • Version 0.102.0 from 2023-10-04
      • Version 0.101.2 from 2023-09-30
      • @@ -49,6 +52,30 @@

        Release Notes

        Tatin release notes contain information regarding actions that need to be executed before a new version can be used, or oustandingly important pieces of information.

        This document does not come with a complete list of fixes, added features etc. Consult Tatin on GitHub for that.

        +
          +
        • No action required but note that the result of the API function LoadPackages has changed.
        • +
        + +
          +
        • No action required
        • +
        + +
          +
        • No action required
        • +
        +

        Version 0.102.1 from 2023-10-07

        diff --git a/TestServer/Server/Assets/docs/Server-TipsAndTricks.html b/TestServer/Server/Assets/docs/Server-TipsAndTricks.html index 1d7414b57..5a6e82b07 100644 --- a/TestServer/Server/Assets/docs/Server-TipsAndTricks.html +++ b/TestServer/Server/Assets/docs/Server-TipsAndTricks.html @@ -132,7 +132,7 @@

        2.2. Version

        2.3. Developing with a running server

        -

        You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the coder is doing, and at the same time, any changes and additions would be added to the project by Link.

        +

        You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the code is doing, and at the same time, any changes and additions would be added to the project by Link.

        Let's assume that you want to run the Tatin server that is part of the Tatin project. When the Tatin test cases are executed then Tatin would ask you whether you want to start the server automatically – that is the server we talking about, no https://test.tatin.dev

        To achieve this execute the following steps:

          @@ -163,7 +163,7 @@

          2.3. Developing with a running se

          2.3.1. Error trapping

    -

    Keep in mind that error trapping is active, so when you change a function and inject a typo it will trigger it once your code gets executed.

    +

    Keep in mind that error trapping is active, so when you change a function and inject a typo this will trigger error trapping once your code gets executed.

    If there is any danger of you locking horns with error trapping consider putting this into OnRequest:

    ⎕TRAP←0 'S' ⍝TODO⍝

    Also, make ⎕TRAP a local variable in OnRequest.

    @@ -174,10 +174,8 @@

    2.4. Developing wit

    When the test cases are executed the user is asked whether she wants the Tatin server required by the Tatin test cases to be started automatically.

    -
      -
    • If the user answers this with a “yes” and instance
    • -
    -

    You must know exactly what you are doing, otherwise, you are likely to loose code.

    +

    If the user answers this with a “yes” an instance of the server is

    +

    You must know exactly what you are doing, otherwise you are likely to loose code.

    diff --git a/TestServer/Server/Assets/docs/SyntaxReference.html b/TestServer/Server/Assets/docs/SyntaxReference.html index b286a0352..69d5a0626 100644 --- a/TestServer/Server/Assets/docs/SyntaxReference.html +++ b/TestServer/Server/Assets/docs/SyntaxReference.html @@ -743,7 +743,7 @@

    2.24. LoadPackages

    Loads the package(s) into (#|⎕SE)._tatin.{packageName} and establishes a reference for every one of them in targetSpace

    Loads all dependencies, if any, as well into (#|⎕SE)._tatin but does not create references for them in targetSpace.

    By default beta versions are considered in case the package ID is incomplete, but you can suppress them by passing 1 as .

    -

    Returns the number of packages installed, including dependencies.

    +

    Returns the number of principal packages installed.

    Note that the package ID(s) might use any case, meaning that if the package's name is foo-Goo-1.2.3 then you might as well spell it foo-GOO-1.2.3 or FOO-goo-1.2.3: it would not make a difference

    diff --git a/TestServer/Server/Assets/docs/TatinForContributors.html b/TestServer/Server/Assets/docs/TatinForContributors.html index 021bab020..b0f6a8fbe 100644 --- a/TestServer/Server/Assets/docs/TatinForContributors.html +++ b/TestServer/Server/Assets/docs/TatinForContributors.html @@ -71,8 +71,9 @@

    2. Requirements

    • Windows
    • Linux
    • +
    • Mac OS
    -

    AIX is not supported.

    +

    AIX is not supported. The PI is not officially supported but it will probably work anyway.

    You also need to have Git installed.

    Note that Tatin is managed by the Cider project management tool. If you are not familiar with Cider you are advised to spend some time playing with it before using it for serious work. 30 minutes should suffice.

    Though it is possible making changes or adding code to Tatin without Cider, using Cider makes it significantly easier. Also, the build process requires Cider.

    +

    Having said this, you don't neccessarily need to build a new version for creating a pull request, so you might get away without Cider, but using Cider is certainly recommended.

    4. How to work on Tatin

    @@ -133,11 +135,11 @@

    8. Executing the test cases

          ]Cider.RunTests
     #.Tatin.#.Tatin.TestCases.RunTests ⍝ Execute this for running the test suite

    Executing RunTests means that on Windows you will be asked whether a test server should be started. Usually, you will answer with a “Y”, and that lets the test suite start another instance of Dyalog, and run a test server in that instance.

    -

    On non-Windows platforms you will be be asked to start the server yourself and press <enter> once that is done.

    +

    On non-Windows platforms you will be be asked to start the server yourself and press <enter> once that has been done.

    Developers might also run into one of two common scenarios:

    1. During development you might want to execute all tests or just a specific group (or groups) of tests. -

      You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit windows etc.

      +

      You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit window etc.

      When a test fails then the test framework stops, and the developer can investigate on the spot.

    2. You might want to execute the test suite automatically (batch mode), meaning that the tests would not attempt to interact with a user. (This implies that test cases that depend on a user won't be executed. Tatin has only a few test cases that fall into this category; more than 95% of the tests are batchable)

      This might be required by an automated build process.

      @@ -184,7 +186,7 @@

      Starting a Tatin test server “manually”

      -

      You may specify 'view' as the left argument to force the result of that function into a read-only edit window.

      +

      You may specify 'view' as the left argument to force the result of that function into a read-only ⎕ED window.

      Let's assume that you want to execute all tests of the group API:

      T.RunThese 'API'

      Or just the tests 8 and 20 of the group API:

      @@ -293,14 +295,14 @@

      8.2.1. Running the test suite from
    3. By default all errors are trapped.

      If you need to track down a bug then you don't want this: in that case, pass a 1 as the left argument: this is treated as “debug” flag.

    4. -

      Of course RunBatchTests does not execute any test cases that require a human in front of the monitor, but the number of such tests is very small anyway.

      +

      Of course RunBatchTests does not execute any test cases that require a human in front of the monitor, but the number of such tests is small anyway.

      RunBatchTests checks the command line:

      • In case OFF2=1 was specified on the command line then ⎕OFF is executed after the last test case got executed.

        If one or more test cases failed then ⎕OFF 123 is executed. That allows the calling environment to check whether the test suite was executed successfully in its entirety or not.

      • If OFF2=1 was not specified a message is printed to the session, indicating success or failure.
      -

      Note that OFF=1 would kind of also work, but it would make Plodder ⎕OFF, the underlying HTTP server uses by Tatin. That is for some things too early. For example you cannot get a code coverage report then.

      +

      Note that OFF=1 would kind of also work, but it would make Plodder ⎕OFF, the underlying HTTP server used by Tatin. That might be too early. For example, you cannot get a code coverage report then.

      8.2.2. Running the test suite from a console or terminal

      diff --git a/docs/InstallingAndUpdatingTheTatinClient.md b/docs/InstallingAndUpdatingTheTatinClient.md index cd1ae7a89..0cc98d48b 100644 --- a/docs/InstallingAndUpdatingTheTatinClient.md +++ b/docs/InstallingAndUpdatingTheTatinClient.md @@ -43,7 +43,7 @@ A> In that case you need to perform some action, depending on the version of Tat A> A> #### Tatin version 0.100.1 and earlier A> -A> 0.100.1 and ealier version were installed into the `MyUCMDs/` folder. +A> 0.100.1 and earlier versions were installed into the `MyUCMDs/` folder. A> A> That has changed: it's now installed into a different folder. Please delete it from `MyUCMDs/`. A> @@ -310,3 +310,4 @@ Notes: + diff --git a/docs/InstallingTheTatinServer.md b/docs/InstallingTheTatinServer.md index 8037bf857..fd8828e9c 100644 --- a/docs/InstallingTheTatinServer.md +++ b/docs/InstallingTheTatinServer.md @@ -289,7 +289,7 @@ For details see: ## Updating the Server -Before updating the server **you must read the release notes!**. The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance. +Before updating the server **you must read the release notes!** The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance. ### The Workspace @@ -304,7 +304,7 @@ Naturally, the server will produce an error message for a short period during th Entries in the INI file might have been added or removed. Whether that is the case will be revealed by the release notes. If something has changed follow the release notes. _Do not replace the INI file!_ -The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: quite several settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell. +The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: a number of settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell. ## Assets @@ -315,3 +315,4 @@ What kind of actions need to be taken, if any, is revealed by the release notes. First of all, never replace the folder `maintenance/`: its content documents what changes have been carried out towards the packages in the past, and you don't want to lose this. If the new one is not empty then copy the content over. Maintenance files can be used to carry out changes on all or some of the packages managed by that server, like adding a new property to the package config files of all packages. + diff --git a/docs/PackageConfiguration.md b/docs/PackageConfiguration.md index e543679da..2e36ff4a1 100644 --- a/docs/PackageConfiguration.md +++ b/docs/PackageConfiguration.md @@ -25,6 +25,7 @@ This is an example: ml: 1, name: "DotNetZip", source: "DotNetZip.aplc", + tatin_version: "0.103.0", tags: "zip-tools", version: "0.5.4", } @@ -199,11 +200,9 @@ Notes: * The `assets\` folder must be considered read-only. Never write to it from a package! -* Watch out for the convention regarding a file "LICENSE" in the root of a project; refer to the "Server-TipsAndTricks" document for details - * Check the "[files](#)" property for files like "ReadMe.txt" and the like -* A file named "LICENSE" in the root of a project will always be copied to the root of a package +* A file named "LICENSE" in the root of a project will always be copied to the root of a package by convention when a package is build. A> ### Accessing assets from a class instance A> @@ -280,16 +279,18 @@ You might want to get one or more specific files into the root of the package al Typical examples are "LICENSE" and "ReadMe.[md\|txt\|html]". Strictly speaking they are not assets because the package will still function perfectly well without them. Also, you want them to go to the root of the project, not into a sub-folder, so that they stand out. -That can be achieved by adding them to the "files" property. This can be one of: +That can be achieved by adding them to the "files" property. `files` can be one of: * Absend * Empty * A single file * A comma-separated list of files -Note that if it specifies a sub-folder `BuildPackage` will copy the file from that project-specific sub-folder to the root of the package. +Note that if it specifies a sub-folder, `BuildPackage` will copy the folder from that project-specific sub-folder to the root of the package. + +See also "[assets](#)". -See als "[assets](#)". +I> Note that a file `LICENSE` in the root of a project is by convention copied to the root of a package when a package is build. #### GetFullPath2AssetsFolder @@ -487,6 +488,11 @@ There is also no point in adding tags like "dyalog" or "apl" to a package: Tatin Note that people in charge of the principal Tatin server will have an eye on the tags, and might silently correct them to keep them consistent and meaningful. +#### tatin_version + +The Tatin version number without any build ID the config data was created / changed by. + +You should not edit this because it is overwritten before saving the data anyway. #### userCommandScript @@ -566,3 +572,5 @@ Since packages, once published, cannot be altered, it is safe to assume that the [^TatinVars]: The Tatin package variables are discussed in detail in the document `FirstStepsWithTatin.html` + + diff --git a/docs/PublishingPackages.md b/docs/PublishingPackages.md index 04a691f45..f798bd668 100644 --- a/docs/PublishingPackages.md +++ b/docs/PublishingPackages.md @@ -116,12 +116,18 @@ API keys are saved in the user settings file. The quickest and yet safe way to e ##### Server -Credentials are saved in the file "Credentials.csv" in the `Registry\` folder in the server's home folder. +Credentials are saved in the file "Credentials.csv" in the Registry's home folder. #### Credentials for your own Tatin Server If you run your own Tatin Server we suggest that you create a UUID and use that as an API key. +A> ### Email address is required +A> +A> Note that having a valid API key is not enough for publishing a package: you must also define an email address for your group. +A> +A> For that click "Groups > {your-group-name} > Create" and add at least an email address. + For an API key to be accepted by a Tatin Server, it must be added to a file `Credentials.txt` in the Registry's root directory. Make sure that you specify it as either @@ -136,25 +142,24 @@ or *, ``` -Instead of the "`,`" you can also use "`=`" as separator but that is deprecated. - If the server finds such a file it will perform the following actions: * Take the data and convert it to a different format * Delete rows from `Credential.csv` that share a group name with `Credentials.txt` -* Add the data to `Credentials.cvs` -* Delete the file `Credentials.txt`. +* Create a Salt for every API key in `Credentials.txt` +* Convert every API-key and its Salt into a hash and add them together with the according group name to `Credentials.csv` +* Delete the file `Credentials.txt` -The format of the file `Credentials.csv` is this: +The format of the file `Credentials.csv` is: ``` -, +,, *,, * ``` -* In the first case, anybody who provides the API key the hash was produced from, can publish packages for that group. -* In the second case, the password the hash was created from, is a kind of master password: it allows the creation of packages with _any_ group name. +* In the first case, anybody who provides the API key the hash was produced from can publish packages for that group. +* In the second case, the password the hash was created from is a kind of master password: it allows the creation of packages with _any_ group name. * The third case means that no API key is required for any (remaining) group(s). The different scenarios can be mixed: @@ -180,7 +185,7 @@ This is interpreted as "require API keys for the groups and bu Finally, you can allow anybody to publish packages under a particular group name without providing an API key: ``` -, +,, , *, ``` @@ -196,10 +201,14 @@ This means: ##### Editing the file "Credentials.csv" -There is only one reason why you might need to change the file `Credentials.csv`: when a group name must be deleted for some reason. +There is one reasons why you might need to change the file `Credentials.csv`: when a group name must be deleted for some reason. If a new group needs to be added, or a new API key needs to be assigned to an existing group, you must create a file `Credentials.txt`, see above. +##### Comments + +The files `Credentials.txt` as well as `Credentials.csv` both allow comment lines: any line that has a `;` as the very first character is regarded a comment. + ## Publishing @@ -385,4 +394,10 @@ You can now develop a package `Foo` and publish it on `[my]`, probably several t You would then publish it on `[my-team]`. At the same time, you would either delete the package from `[my]` or, if you want the Registry `[my]` to be ignored altogether, set its priority to zero. -When all is good the beta is promoted to an official release and published to the Tatin company server. At the same time, the package will most likely be deleted from the Team server. \ No newline at end of file +When all is good the beta is promoted to an official release and published to the Tatin company server. At the same time, the package will most likely be deleted from the Team server. + + + + + + diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index a31e6abbc..d3af4b974 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -12,6 +12,18 @@ Tatin release notes contain information regarding actions that need to be execut This document does not come with a complete list of fixes, added features etc. Consult [Tatin on GitHub](https://github.com/aplteam/Tatin) for that. +## Version 0.103.0 from 2023-11-04 + +* No action required but note that the result of the API function `LoadPackages` has changed. + +## Version 0.102.3 from 2023-10-13 + +* No action required + +## Version 0.102.2 from 2023-10-09 + +* No action required + ## Version 0.102.1 from 2023-10-07 * Most importantly, a bug fix in `]Tatin.CiderUpdate` @@ -147,6 +159,11 @@ No breaking changes, no user actions required. + + + + + diff --git a/docs/Server-TipsAndTricks.md b/docs/Server-TipsAndTricks.md index 7b4983ea0..af57cbe55 100644 --- a/docs/Server-TipsAndTricks.md +++ b/docs/Server-TipsAndTricks.md @@ -95,7 +95,7 @@ Note that by design new versions always comprehend both the server and the clien ### Developing with a running server -You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the coder is doing, and at the same time, any changes and additions would be added to the project by Link. +You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the code is doing, and at the same time, any changes and additions would be added to the project by Link. Let's assume that you want to run the Tatin server that is part of the Tatin project. When the Tatin test cases are executed then Tatin would ask you whether you want to start the server automatically -- that is the server we talking about, **no** https://test.tatin.dev @@ -128,7 +128,7 @@ These will call the Tatin functions that perform the real actions. #### Error trapping -Keep in mind that error trapping is active, so when you change a function and inject a typo it will trigger it once your code gets executed. +Keep in mind that error trapping is active, so when you change a function and inject a typo this will trigger error trapping once your code gets executed. If there is any danger of you locking horns with error trapping consider putting this into `OnRequest`: @@ -139,14 +139,13 @@ Also, make `⎕TRAP` a local variable in `OnRequest`. `⍝TODO⍝` is a reminder that won't go unnoticed: there is a test case that will detect these markers and report them, so it's pretty hard to forget them. - ### Developing with two sessions, server and client When the test cases are executed the user is asked whether she wants the Tatin server required by the Tatin test cases to be started automatically. -* If the user answers this with a "yes" and instance +If the user answers this with a "yes" an instance of the server is -You must know exactly what you are doing, otherwise, you are likely to loose code. +You must know exactly what you are doing, otherwise you are likely to loose code. | **To be enhanced** | @@ -195,4 +194,5 @@ The text shown on the web page is defined in the document "Licencing.html" in th #### A "LICENSE" file -By convention a file named "LICENSE" when placed in the root of the project will be copied automatically to the root of a package when build by `BuildPackage`. This convention is independent from the INI file. \ No newline at end of file +By convention a file named "LICENSE" when placed in the root of the project will be copied automatically to the root of a package when build by `BuildPackage`. This convention is independent from the INI file. + diff --git a/docs/SyntaxReference.md b/docs/SyntaxReference.md index ee3be27e9..a40b51a75 100644 --- a/docs/SyntaxReference.md +++ b/docs/SyntaxReference.md @@ -697,7 +697,7 @@ Loads all dependencies, if any, as well into `(#|⎕SE)._tatin` but does _not_ c By default beta versions are considered in case the package ID is incomplete, but you can suppress them by passing 1 as `⍺`. -Returns the number of packages installed, including dependencies. +Returns the number of principal packages installed. Note that the package ID(s) might use any case, meaning that if the package's name is `foo-Goo-1.2.3` then you might as well spell it `foo-GOO-1.2.3` or `FOO-goo-1.2.3`: it would not make a difference @@ -853,3 +853,4 @@ r←Version Returns "name", "version" and "date". + diff --git a/docs/TatinForContributors.md b/docs/TatinForContributors.md index f5f9aefc3..cb94c0407 100644 --- a/docs/TatinForContributors.md +++ b/docs/TatinForContributors.md @@ -27,8 +27,9 @@ You need one of: * Windows * Linux +* Mac OS -AIX is not supported. +AIX is not supported. The PI is not officially supported but it will probably work anyway. You also need to have Git installed. @@ -40,6 +41,8 @@ If you are not familiar with Cider you are advised to spend some time playing wi Though it is possible making changes or adding code to Tatin without Cider, using Cider makes it significantly easier. Also, the build process requires Cider. +Having said this, you don't neccessarily need to build a new version for creating a pull request, so you might get away without Cider, but using Cider is certainly recommended. + ## How to work on Tatin @@ -111,13 +114,13 @@ If you just want to execute all test cases before pushing your changes to GitHub Executing `RunTests` means that on Windows you will be asked whether a test server should be started. Usually, you will answer with a "Y", and that lets the test suite start another instance of Dyalog, and run a test server in that instance. -On non-Windows platforms you will be be asked to start the server yourself and press once that is done. +On non-Windows platforms you will be be asked to start the server yourself and press `` once that has been done. Developers might also run into one of two common scenarios: 1. During development you might want to execute all tests or just a specific group (or groups) of tests. - You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit windows etc. + You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit window etc. When a test fails then the test framework stops, and the developer can investigate on the spot. @@ -175,7 +178,7 @@ The `RunTests` function performs these tasks: * Change the current directory * Establish all required references -* Instantiate the `Tester2` class under the name `T` +* Instantiate the `Tester2` class as `T` * Start a local Tatin test server if the user confirms this * Call the `T.Run` function @@ -217,9 +220,9 @@ Before executing any test case call this function: #.Tatin.TestCases.Prepare ``` -This changes the current directory, establishes all required references and instantiates the `Tester2` class under the name `T`. +This changes the current directory, establishes all required references and instantiates the `Tester2` class as `T`. -You can now list all groups of tests: +You can now list the groups: ``` T.ListGroups @@ -268,7 +271,7 @@ Notes: * The wildcard character (`*`) can be used to identify a group * The first comment line after any header lines is displayed -You may specify `'view'` as the left argument to force the result of that function into a read-only edit window. +You may specify `'view'` as the left argument to force the result of that function into a read-only `⎕ED` window. Let's assume that you want to execute all tests of the group `API`: @@ -327,7 +330,7 @@ When running the test suite with the batch flag set, as `RunBatchTests` does, is If you need to track down a bug then you don't want this: in that case, pass a `1` as the left argument: this is treated as "debug" flag. -Of course `RunBatchTests` does not execute any test cases that require a human in front of the monitor, but the number of such tests is very small anyway. +Of course `RunBatchTests` does not execute any test cases that require a human in front of the monitor, but the number of such tests is small anyway. `RunBatchTests` checks the command line: @@ -337,7 +340,7 @@ Of course `RunBatchTests` does not execute any test cases that require a human i * If `OFF2=1` was not specified a message is printed to the session, indicating success or failure. -Note that `OFF=1` would kind of also work, but it would make Plodder `⎕OFF`, the underlying HTTP server uses by Tatin. That is for some things too early. For example you cannot get a code coverage report then. +Note that `OFF=1` would kind of also work, but it would make Plodder `⎕OFF`, the underlying HTTP server used by Tatin. That might be too early. For example, you cannot get a code coverage report then. #### Running the test suite from a console or terminal @@ -413,3 +416,4 @@ There is a function that creates a single HTML file from all the Tatin markdown Now open that file with the word processor of your choice and use its spell-checking capabilities. + diff --git a/html/InstallingAndUpdatingTheTatinClient.html b/html/InstallingAndUpdatingTheTatinClient.html index bae6c80c8..2d8a72177 100644 --- a/html/InstallingAndUpdatingTheTatinClient.html +++ b/html/InstallingAndUpdatingTheTatinClient.html @@ -76,7 +76,7 @@

      4. Updating Tatin in 18.0 and 18.2You've installed Tatin before

      In that case you need to perform some action, depending on the version of Tatin you've have installed

      Tatin version 0.100.1 and earlier

      -

      0.100.1 and ealier version were installed into the MyUCMDs/ folder.

      +

      0.100.1 and earlier versions were installed into the MyUCMDs/ folder.

      That has changed: it's now installed into a different folder. Please delete it from MyUCMDs/.

      Any Tatin version 0.101.0 and 0.100.1

      These versions told you to install Tatin into

      diff --git a/html/InstallingTheTatinServer.html b/html/InstallingTheTatinServer.html index bb504ab9f..bfe8e946b 100644 --- a/html/InstallingTheTatinServer.html +++ b/html/InstallingTheTatinServer.html @@ -386,7 +386,7 @@

      10. Misc

      11. Updating the Server

      -

      Before updating the server you must read the release notes!. The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance.

      +

      Before updating the server you must read the release notes! The reason is that an update might be as easy as copying the workspace over while the server is running but as complex as requiring several actions while the server is down for maintenance.

      Entries in the INI file might have been added or removed. Whether that is the case will be revealed by the release notes. If something has changed follow the release notes. Do not replace the INI file!

      -

      The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: quite several settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell.

      +

      The server checks the INI file for having been changed, and if that is the case re-initialize the INI file. However, whether that works or not depends on the kind of change: a number of settings are required at a very early stage, and cannot be simply changed later on. Again the release notes will tell.

    5. @@ -77,6 +78,7 @@

      1. Overview

      ml: 1, name: "DotNetZip", source: "DotNetZip.aplc", + tatin_version: "0.103.0", tags: "zip-tools", version: "0.5.4", }
      @@ -231,9 +233,8 @@

      2.3.2. assets

    6. When the package configuration file is written to disk the existence of “assets” is checked. If it's not a folder an error is thrown.

      See also the GetFullPath2AssetsFolder function.

    7. The assets\ folder must be considered read-only. Never write to it from a package!
    8. -
    9. Watch out for the convention regarding a file “LICENSE” in the root of a project; refer to the “Server-TipsAndTricks” document for details
    10. Check the “files” property for files like “ReadMe.txt” and the like
    11. -
    12. A file named “LICENSE” in the root of a project will always be copied to the root of a package
    13. +
    14. A file named “LICENSE” in the root of a project will always be copied to the root of a package by convention when a package is build.
    15. Accessing assets from a class instance

      @@ -296,15 +297,22 @@

      2.3.7. files

      You might want to get one or more specific files into the root of the package although they are not really assets.

      Typical examples are “LICENSE” and “ReadMe.[md|txt|html]”. Strictly speaking they are not assets because the package will still function perfectly well without them. Also, you want them to go to the root of the project, not into a sub-folder, so that they stand out.

      -

      That can be achieved by adding them to the “files” property. This can be one of:

      +

      That can be achieved by adding them to the “files” property. files can be one of:

      • Absend
      • Empty
      • A single file
      • A comma-separated list of files
      -

      Note that if it specifies a sub-folder BuildPackage will copy the file from that project-specific sub-folder to the root of the package.

      -

      See als “assets”.

      +

      Note that if it specifies a sub-folder, BuildPackage will copy the folder from that project-specific sub-folder to the root of the package.

      +

      See also “assets”.

      +
      +Information +
      +

      Note that a file LICENSE in the root of a project is by convention copied to the root of a package when a package is build.

      +
      +
      +

      2.3.8. GetFullPath2AssetsFolder

      @@ -470,8 +478,15 @@

      2.3.20. tags

      There is also no point in adding tags like “dyalog” or “apl” to a package: Tatin is a Dyalog APL package manager…

      Note that people in charge of the principal Tatin server will have an eye on the tags, and might silently correct them to keep them consistent and meaningful.

      +

      The Tatin version number without any build ID the config data was created / changed by.

      +

      You should not edit this because it is overwritten before saving the data anyway.

      +

      If a package is a user command then this must contain the path to the user command script relative to the projects root. InstallPackages uses this to identify a user command script and to move it from the source folder (if any) to the root of the install folder.

      @@ -479,7 +494,7 @@

      2.3.21. userCommandScript

      Note that for a package to be a user command the package must be constructed in a particular way. This is discussed in the “Publishing Packages” document.

      The version[2] part of the package ID[1]

      @@ -492,19 +507,19 @@

      2.3.22. version

      For details see the Tatin and Semantic Versioning document.

      By default the package configuration file carries the values of the two Dyalog parameters (environment variables) Default_IO and Default_ML for the system variables ⎕IO and ⎕ML, or, if these are not defined, ⎕IO←1 and ⎕ML←1 which are the built-in defaults.

      Tatin uses these values for setting the system variables accordingly in any namespace that is created by either the LoadPackages or the LoadDependencies function before any code is loaded into them. This is important because that makes any sub-namespace created later on inherit those values.

      The user must not specify “date”, but when a package is published the server will inject “date” as a timestamp in the format yyyymmdd.hhmmss.

      @@ -520,7 +535,7 @@
      2.3.24.1. date

      Since packages, once published, cannot be altered, it is safe to assume that the publishing date determines the correct order. However, as long as the version consists of just digits and dots, and possibly a build number, date does not play a role in determining precedence.

        diff --git a/html/PublishingPackages.html b/html/PublishingPackages.html index 84b08c5d1..c7652eb81 100644 --- a/html/PublishingPackages.html +++ b/html/PublishingPackages.html @@ -186,33 +186,39 @@
        3.1.3.1. Client
        3.1.3.2. Server
      -

      Credentials are saved in the file “Credentials.csv” in the Registry\ folder in the server's home folder.

      +

      Credentials are saved in the file “Credentials.csv” in the Registry's home folder.

      If you run your own Tatin Server we suggest that you create a UUID and use that as an API key.

      +
      +

      Email address is required

      +

      Note that having a valid API key is not enough for publishing a package: you must also define an email address for your group.

      +

      For that click “Groups > {your-group-name} > Create” and add at least an email address.

      +
      +

      For an API key to be accepted by a Tatin Server, it must be added to a file Credentials.txt in the Registry's root directory.

      Make sure that you specify it as either

      <group-name>,<api-key>

      or

      *,<api-key>
      -

      Instead of the “,” you can also use “=” as separator but that is deprecated.

      If the server finds such a file it will perform the following actions:

      • Take the data and convert it to a different format
      • Delete rows from Credential.csv that share a group name with Credentials.txt
      • -
      • Add the data to Credentials.cvs
      • -
      • Delete the file Credentials.txt.
      • +
      • Create a Salt for every API key in Credentials.txt
      • +
      • Convert every API-key and its Salt into a hash and add them together with the according group name to Credentials.csv
      • +
      • Delete the file Credentials.txt
      -

      The format of the file Credentials.csv is this:

      -
      <group-name<,<api-key-hash>,<salt>
      +

      The format of the file Credentials.csv is:

      +
      <group-name>,<api-key-hash>,<salt>
       *,<api-key-hash>,<salt>
       *
        -
      • In the first case, anybody who provides the API key the hash was produced from, can publish packages for that group.
      • -
      • In the second case, the password the hash was created from, is a kind of master password: it allows the creation of packages with any group name.
      • +
      • In the first case, anybody who provides the API key the hash was produced from can publish packages for that group.
      • +
      • In the second case, the password the hash was created from is a kind of master password: it allows the creation of packages with any group name.
      • The third case means that no API key is required for any (remaining) group(s).

      The different scenarios can be mixed:

      @@ -226,7 +232,7 @@

      3.1.4. Credentials for your *

      This is interpreted as “require API keys for the groups <group1> and <group2> but allow anything else without an API key”.

      Finally, you can allow anybody to publish packages under a particular group name without providing an API key:

      -
      <group1>,<hash1>
      +
      <group1>,<hash1>,<email-address>
       <group2>,
       *,<hash3>

      This means:

      @@ -240,9 +246,15 @@

      3.1.4. Credentials for your

      3.1.4.1. Editing the file “Credentials.csv”
    -

    There is only one reason why you might need to change the file Credentials.csv: when a group name must be deleted for some reason.

    +

    There is one reasons why you might need to change the file Credentials.csv: when a group name must be deleted for some reason.

    If a new group needs to be added, or a new API key needs to be assigned to an existing group, you must create a file Credentials.txt, see above.

    +

    The files Credentials.txt as well as Credentials.csv both allow comment lines: any line that has a ; as the very first character is regarded a comment.

    +

    4. Publishing

    diff --git a/html/ReleaseNotes.html b/html/ReleaseNotes.html index 270d39419..49b81d010 100644 --- a/html/ReleaseNotes.html +++ b/html/ReleaseNotes.html @@ -20,6 +20,9 @@
    • Release Notes
        +
      • Version 0.103.0 from 2023-11-04
      • +
      • Version 0.102.3 from 2023-10-13
      • +
      • Version 0.102.2 from 2023-10-09
      • Version 0.102.1 from 2023-10-07
      • Version 0.102.0 from 2023-10-04
      • Version 0.101.2 from 2023-09-30
      • @@ -49,6 +52,30 @@

        Release Notes

        Tatin release notes contain information regarding actions that need to be executed before a new version can be used, or oustandingly important pieces of information.

        This document does not come with a complete list of fixes, added features etc. Consult Tatin on GitHub for that.

        +
          +
        • No action required but note that the result of the API function LoadPackages has changed.
        • +
        + +
          +
        • No action required
        • +
        + +
          +
        • No action required
        • +
        +

        Version 0.102.1 from 2023-10-07

        diff --git a/html/Server-TipsAndTricks.html b/html/Server-TipsAndTricks.html index 1d7414b57..5a6e82b07 100644 --- a/html/Server-TipsAndTricks.html +++ b/html/Server-TipsAndTricks.html @@ -132,7 +132,7 @@

        2.2. Version

        2.3. Developing with a running server

        -

        You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the coder is doing, and at the same time, any changes and additions would be added to the project by Link.

        +

        You might want to run a server while Tatin is an open Cider project. The running server allows you to investigate what the code is doing, and at the same time, any changes and additions would be added to the project by Link.

        Let's assume that you want to run the Tatin server that is part of the Tatin project. When the Tatin test cases are executed then Tatin would ask you whether you want to start the server automatically – that is the server we talking about, no https://test.tatin.dev

        To achieve this execute the following steps:

          @@ -163,7 +163,7 @@

          2.3. Developing with a running se

          2.3.1. Error trapping

    -

    Keep in mind that error trapping is active, so when you change a function and inject a typo it will trigger it once your code gets executed.

    +

    Keep in mind that error trapping is active, so when you change a function and inject a typo this will trigger error trapping once your code gets executed.

    If there is any danger of you locking horns with error trapping consider putting this into OnRequest:

    ⎕TRAP←0 'S' ⍝TODO⍝

    Also, make ⎕TRAP a local variable in OnRequest.

    @@ -174,10 +174,8 @@

    2.4. Developing wit

    When the test cases are executed the user is asked whether she wants the Tatin server required by the Tatin test cases to be started automatically.

    -
      -
    • If the user answers this with a “yes” and instance
    • -
    -

    You must know exactly what you are doing, otherwise, you are likely to loose code.

    +

    If the user answers this with a “yes” an instance of the server is

    +

    You must know exactly what you are doing, otherwise you are likely to loose code.

    diff --git a/html/SyntaxReference.html b/html/SyntaxReference.html index b286a0352..69d5a0626 100644 --- a/html/SyntaxReference.html +++ b/html/SyntaxReference.html @@ -743,7 +743,7 @@

    2.24. LoadPackages

    Loads the package(s) into (#|⎕SE)._tatin.{packageName} and establishes a reference for every one of them in targetSpace

    Loads all dependencies, if any, as well into (#|⎕SE)._tatin but does not create references for them in targetSpace.

    By default beta versions are considered in case the package ID is incomplete, but you can suppress them by passing 1 as .

    -

    Returns the number of packages installed, including dependencies.

    +

    Returns the number of principal packages installed.

    Note that the package ID(s) might use any case, meaning that if the package's name is foo-Goo-1.2.3 then you might as well spell it foo-GOO-1.2.3 or FOO-goo-1.2.3: it would not make a difference

    diff --git a/html/TatinForContributors.html b/html/TatinForContributors.html index 021bab020..b0f6a8fbe 100644 --- a/html/TatinForContributors.html +++ b/html/TatinForContributors.html @@ -71,8 +71,9 @@

    2. Requirements

    • Windows
    • Linux
    • +
    • Mac OS
    -

    AIX is not supported.

    +

    AIX is not supported. The PI is not officially supported but it will probably work anyway.

    You also need to have Git installed.

    Note that Tatin is managed by the Cider project management tool. If you are not familiar with Cider you are advised to spend some time playing with it before using it for serious work. 30 minutes should suffice.

    Though it is possible making changes or adding code to Tatin without Cider, using Cider makes it significantly easier. Also, the build process requires Cider.

    +

    Having said this, you don't neccessarily need to build a new version for creating a pull request, so you might get away without Cider, but using Cider is certainly recommended.

    4. How to work on Tatin

    @@ -133,11 +135,11 @@

    8. Executing the test cases

          ]Cider.RunTests
     #.Tatin.#.Tatin.TestCases.RunTests ⍝ Execute this for running the test suite

    Executing RunTests means that on Windows you will be asked whether a test server should be started. Usually, you will answer with a “Y”, and that lets the test suite start another instance of Dyalog, and run a test server in that instance.

    -

    On non-Windows platforms you will be be asked to start the server yourself and press <enter> once that is done.

    +

    On non-Windows platforms you will be be asked to start the server yourself and press <enter> once that has been done.

    Developers might also run into one of two common scenarios:

    1. During development you might want to execute all tests or just a specific group (or groups) of tests. -

      You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit windows etc.

      +

      You are sitting in front of the monitor, and therefore tests can ask you to perform certain tasks, like closing an edit window etc.

      When a test fails then the test framework stops, and the developer can investigate on the spot.

    2. You might want to execute the test suite automatically (batch mode), meaning that the tests would not attempt to interact with a user. (This implies that test cases that depend on a user won't be executed. Tatin has only a few test cases that fall into this category; more than 95% of the tests are batchable)

      This might be required by an automated build process.

      @@ -184,7 +186,7 @@

      Starting a Tatin test server “manually”

      -

      You may specify 'view' as the left argument to force the result of that function into a read-only edit window.

      +

      You may specify 'view' as the left argument to force the result of that function into a read-only ⎕ED window.

      Let's assume that you want to execute all tests of the group API:

      T.RunThese 'API'

      Or just the tests 8 and 20 of the group API:

      @@ -293,14 +295,14 @@

      8.2.1. Running the test suite from
    3. By default all errors are trapped.

      If you need to track down a bug then you don't want this: in that case, pass a 1 as the left argument: this is treated as “debug” flag.

    4. -

      Of course RunBatchTests does not execute any test cases that require a human in front of the monitor, but the number of such tests is very small anyway.

      +

      Of course RunBatchTests does not execute any test cases that require a human in front of the monitor, but the number of such tests is small anyway.

      RunBatchTests checks the command line:

      • In case OFF2=1 was specified on the command line then ⎕OFF is executed after the last test case got executed.

        If one or more test cases failed then ⎕OFF 123 is executed. That allows the calling environment to check whether the test suite was executed successfully in its entirety or not.

      • If OFF2=1 was not specified a message is printed to the session, indicating success or failure.
      -

      Note that OFF=1 would kind of also work, but it would make Plodder ⎕OFF, the underlying HTTP server uses by Tatin. That is for some things too early. For example you cannot get a code coverage report then.

      +

      Note that OFF=1 would kind of also work, but it would make Plodder ⎕OFF, the underlying HTTP server used by Tatin. That might be too early. For example, you cannot get a code coverage report then.

      8.2.2. Running the test suite from a console or terminal

      diff --git a/packages/apl-buildlist.json b/packages/apl-buildlist.json index 8c45286df..664697f4e 100644 --- a/packages/apl-buildlist.json +++ b/packages/apl-buildlist.json @@ -14,7 +14,7 @@ "aplteam-DotNetZip-2.1.0", "aplteam-CommTools-1.7.0", "aplteam-CodeCoverage-0.10.3", - "aplteam-APLTreeUtils2-1.2.1", + "aplteam-APLTreeUtils2-1.3.0", "aplteam-APLProcess-0.7.0", "dyalog-HttpCommand-5.1.14", ], diff --git a/packages/apl-dependencies.txt b/packages/apl-dependencies.txt index 4c4a7e505..1b3f50112 100644 --- a/packages/apl-dependencies.txt +++ b/packages/apl-dependencies.txt @@ -13,4 +13,4 @@ aplteam-HashPasswords-1.0.1 aplteam-GitHubAPIv3-1.0.1 aplteam-CommTools-1.7.0 aplteam-APLProcess-0.7.0 -aplteam-APLTreeUtils2-1.2.1 +aplteam-APLTreeUtils2-1.3.0 diff --git a/server.ini.RemoveMe b/server.ini.RemoveMe index 4ff62d999..6290cc787 100644 --- a/server.ini.RemoveMe +++ b/server.ini.RemoveMe @@ -29,6 +29,7 @@ MenuItemBefore = '' ; Prefix for some of the menu items MaintenancePath = '' ; Code to be loaded from in order to execute it once and then rename the file EmailAddress = 'some@email.com' ; The email address listed in the footer etc (NOT what's used for sending crash reports!) Subject = 'Regarding https://your.server' ; Used in the subject line of ordinary email (NOT crash reports!) +EnforceEmailAddress = 1 ; 1=an email address must be defined (home page) for publishing packages if an API key is required [HTMLBACKGROUND]