├── Images ├── Icon.png ├── Banner.png ├── HeroImage.png └── PacletIcon.png ├── Resources ├── DefaultIcon.wxf ├── GitHubIcon.wxf ├── TerminalIcon.wxf ├── PublisherTokenIcon.wxf └── GitHubEnvironmentData.wl ├── .github ├── images │ ├── readme-1.png │ ├── readme-2.png │ ├── readme-3.png │ ├── readme-create-workflow-1.png │ ├── dir.svg │ └── nb.svg └── ISSUE_TEMPLATE │ └── bug_report.md ├── Examples ├── Sample.wl ├── FewIssues.wl ├── MoreIssues.wl ├── AdvancedSample.wl └── CompiledLibrary.wl ├── Scripts ├── RunScript.sh ├── TestPaclet.wls ├── BuildPaclet.wls ├── CheckPaclet.wls ├── .githooks │ └── pre-commit ├── SubmitPaclet.wls ├── SetReleaseParameters.wls ├── BuildPacletMX.wls └── FormatNotebooks.wls ├── Kernel ├── DeployPaclet.wl ├── Config.wl ├── Symbols.wl ├── PacletCICD.wl ├── Compilation.wl ├── PacletInformation.wl ├── GitHubPacletInstall.wl ├── FormatNotebooks.wl ├── Loading.wl ├── Units.wl ├── Console.wl ├── SubmitPaclet.wl ├── ErrorHandling.wl ├── BuildPaclet.wl ├── CheckPaclet.wl └── ScriptConfirmation.wl ├── LICENSE ├── .gitmodules ├── CONTRIBUTING.md ├── Tools ├── GenerateHeroImage.wl ├── GenerateLogo.wl └── GenerateBannerImage.wl ├── Tests ├── Utilities.wlt ├── WorkflowExport.wlt ├── Units.wlt ├── MXBuild.wlt ├── TestPaclet.wlt ├── ExampleDirectory.wlt ├── ErrorHandling.wlt └── BuildPaclet.wlt ├── README.md ├── PacletInfo.wl └── Documentation └── English ├── Tutorials └── GitHubActions.nb └── Guides └── CustomCICDWorkflows.nb /Images/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Images/Icon.png -------------------------------------------------------------------------------- /Images/Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Images/Banner.png -------------------------------------------------------------------------------- /Images/HeroImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Images/HeroImage.png -------------------------------------------------------------------------------- /Images/PacletIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Images/PacletIcon.png -------------------------------------------------------------------------------- /Resources/DefaultIcon.wxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Resources/DefaultIcon.wxf -------------------------------------------------------------------------------- /Resources/GitHubIcon.wxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Resources/GitHubIcon.wxf -------------------------------------------------------------------------------- /.github/images/readme-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/.github/images/readme-1.png -------------------------------------------------------------------------------- /.github/images/readme-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/.github/images/readme-2.png -------------------------------------------------------------------------------- /.github/images/readme-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/.github/images/readme-3.png -------------------------------------------------------------------------------- /Resources/TerminalIcon.wxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Resources/TerminalIcon.wxf -------------------------------------------------------------------------------- /Resources/PublisherTokenIcon.wxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/Resources/PublisherTokenIcon.wxf -------------------------------------------------------------------------------- /.github/images/readme-create-workflow-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WolframResearch/PacletCICD/main/.github/images/readme-create-workflow-1.png -------------------------------------------------------------------------------- /Examples/Sample.wl: -------------------------------------------------------------------------------- 1 | <| 2 | "Name" -> "Sample", 3 | "Version" -> "1.7.0", 4 | "URL" -> "https://github.com/WolframResearch/PacletCICD-Examples-Sample/archive/refs/tags/v1.7.0.zip" 5 | |> -------------------------------------------------------------------------------- /Examples/FewIssues.wl: -------------------------------------------------------------------------------- 1 | <| 2 | "Name" -> "FewIssues", 3 | "Version" -> "1.7.0", 4 | "URL" -> "https://github.com/WolframResearch/PacletCICD-Examples-FewIssues/archive/refs/tags/v1.7.0.zip" 5 | |> -------------------------------------------------------------------------------- /Examples/MoreIssues.wl: -------------------------------------------------------------------------------- 1 | <| 2 | "Name" -> "MoreIssues", 3 | "Version" -> "1.7.0", 4 | "URL" -> "https://github.com/WolframResearch/PacletCICD-Examples-MoreIssues/archive/refs/tags/v1.7.0.zip" 5 | |> -------------------------------------------------------------------------------- /Examples/AdvancedSample.wl: -------------------------------------------------------------------------------- 1 | <| 2 | "Name" -> "AdvancedSample", 3 | "Version" -> "1.7.0", 4 | "URL" -> "https://github.com/WolframResearch/PacletCICD-Examples-AdvancedSample/archive/refs/tags/v1.7.0.zip" 5 | |> -------------------------------------------------------------------------------- /Examples/CompiledLibrary.wl: -------------------------------------------------------------------------------- 1 | <| 2 | "Name" -> "CompiledLibrary", 3 | "Version" -> "1.7.0", 4 | "URL" -> "https://github.com/WolframResearch/PacletCICD-Examples-CompiledLibrary/archive/refs/tags/v1.7.0.zip" 5 | |> -------------------------------------------------------------------------------- /Scripts/RunScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wolframscript -file "Scripts/${1}" 4 | WS_EXIT_CODE=$(echo $?) 5 | 6 | if [ $WS_EXIT_CODE -eq 139 ] 7 | then 8 | echo "::warning::Warning: wolframscript did not exit cleanly" 9 | exit 0 10 | fi 11 | exit $WS_EXIT_CODE -------------------------------------------------------------------------------- /Scripts/TestPaclet.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | 3 | BeginPackage[ "Wolfram`PacletCICD`Scripts`" ]; 4 | 5 | Get @ FileNameJoin @ { DirectoryName @ $InputFileName, "Common.wl" }; 6 | checkResult @ Wolfram`PacletCICD`TestPaclet @ FileNameJoin @ { $pacDir, "Tests" }; 7 | 8 | EndPackage[ ]; 9 | -------------------------------------------------------------------------------- /Scripts/BuildPaclet.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | 3 | BeginPackage[ "Wolfram`PacletCICD`Scripts`" ]; 4 | 5 | Get @ FileNameJoin @ { DirectoryName @ $InputFileName, "Common.wl" }; 6 | checkResult @ Wolfram`PacletCICD`BuildPaclet[ 7 | $defNB, 8 | "Check" -> False, 9 | "ExitOnFail" -> True, 10 | "Target" -> "Submit" 11 | ]; 12 | 13 | EndPackage[ ]; 14 | -------------------------------------------------------------------------------- /Scripts/CheckPaclet.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | 3 | BeginPackage[ "Wolfram`PacletCICD`Scripts`" ]; 4 | 5 | Get @ FileNameJoin @ { DirectoryName @ $InputFileName, "Common.wl" }; 6 | checkResult @ Wolfram`PacletCICD`CheckPaclet[ 7 | $defNB, 8 | "Target" -> "Submit", 9 | "FailureCondition" -> { "Warning", "Error" } 10 | ]; 11 | 12 | EndPackage[ ]; 13 | -------------------------------------------------------------------------------- /Scripts/.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | filesToFormat="$(git --no-pager diff --name-status --no-color --cached | awk '$1 != "D" && $2 ~ /\.nb/ { print $2}')" 5 | 6 | if [ "$filesToFormat" != "" ]; then 7 | wolframscript Scripts/FormatNotebooks.wls --notebooks="$filesToFormat" 8 | 9 | for sourceFilePath in $filesToFormat 10 | do 11 | git add $sourceFilePath 12 | done; 13 | 14 | fi 15 | -------------------------------------------------------------------------------- /Scripts/SubmitPaclet.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | 3 | BeginPackage[ "Wolfram`PacletCICD`Scripts`" ]; 4 | 5 | Get @ FileNameJoin @ { DirectoryName @ $InputFileName, "Common.wl" }; 6 | 7 | Needs[ "PacletResource`" -> None ]; 8 | SetOptions[ PacletResource`Notebooks`ProcessNotebookForEmbedding, "EmbeddedHTMLImages" -> True ]; 9 | 10 | checkResult @ Wolfram`PacletCICD`SubmitPaclet[ $defNB, "ExitOnFail" -> True ]; 11 | 12 | EndPackage[ ]; 13 | -------------------------------------------------------------------------------- /Kernel/DeployPaclet.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | DeployPaclet // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | (* ::**********************************************************************:: *) 11 | (* ::Section::Closed:: *) 12 | (*DeployPaclet*) 13 | DeployPaclet[ ___ ] := Failure[ "NotImplemented", <| |> ]; 14 | 15 | (* ::**********************************************************************:: *) 16 | (* ::Section::Closed:: *) 17 | (*Package Footer*) 18 | End[ ]; 19 | 20 | EndPackage[ ]; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: rhennigan 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | - OS: [e.g. MacOSX] 28 | - Kernel Version: [e.g. 13.0] 29 | - PacletCICD Version: [e.g. 0.22.0] 30 | - Docker Image (If applicable): [e.g. wolframresearch/wolframengine:13.0.1] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /Kernel/Config.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | Begin[ "`Private`" ]; 7 | 8 | (* ::**********************************************************************:: *) 9 | (* ::Section::Closed:: *) 10 | (*Config*) 11 | $thisRepository := "WolframResearch/PacletCICD"; 12 | $thisPacletName := "Wolfram/PacletCICD"; 13 | $thisPaclet := $thisPaclet = PacletObject[ $thisPacletName ]; 14 | $thisPacletDir := $thisPacletDir = $thisPaclet[ "Location" ]; 15 | $thisPacletVersion := $thisPacletVersion = $thisPaclet[ "Version" ]; 16 | 17 | $tokenVersion = 1; 18 | 19 | (* ::**********************************************************************:: *) 20 | (* ::Subsection::Closed:: *) 21 | (*Debug*) 22 | Wolfram`PacletCICD`$Debug = False; 23 | 24 | (* ::**********************************************************************:: *) 25 | (* ::Section::Closed:: *) 26 | (*Package Footer*) 27 | End[ ]; 28 | 29 | EndPackage[ ]; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Wolfram Research 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Actions/build-paclet"] 2 | path = Actions/build-paclet 3 | url = https://github.com/WolframResearch/build-paclet.git 4 | 5 | [submodule "Actions/check-paclet"] 6 | path = Actions/check-paclet 7 | url = https://github.com/WolframResearch/check-paclet.git 8 | 9 | [submodule "Actions/submit-paclet"] 10 | path = Actions/submit-paclet 11 | url = https://github.com/WolframResearch/submit-paclet.git 12 | 13 | [submodule "Actions/test-paclet"] 14 | path = Actions/test-paclet 15 | url = https://github.com/WolframResearch/test-paclet.git 16 | 17 | [submodule "Examples/AdvancedSample"] 18 | path = PacletCICD-Examples/PacletCICD-Examples-AdvancedSample 19 | url = https://github.com/WolframResearch/PacletCICD-Examples-AdvancedSample.git 20 | 21 | [submodule "Examples/CompiledLibrary"] 22 | path = PacletCICD-Examples/PacletCICD-Examples-CompiledLibrary 23 | url = https://github.com/WolframResearch/PacletCICD-Examples-CompiledLibrary.git 24 | 25 | [submodule "Examples/FewIssues"] 26 | path = PacletCICD-Examples/PacletCICD-Examples-FewIssues 27 | url = https://github.com/WolframResearch/PacletCICD-Examples-FewIssues.git 28 | 29 | [submodule "Examples/MoreIssues"] 30 | path = PacletCICD-Examples/PacletCICD-Examples-MoreIssues 31 | url = https://github.com/WolframResearch/PacletCICD-Examples-MoreIssues.git 32 | 33 | [submodule "Examples/Sample"] 34 | path = PacletCICD-Examples/PacletCICD-Examples-Sample 35 | url = https://github.com/WolframResearch/PacletCICD-Examples-Sample.git -------------------------------------------------------------------------------- /.github/images/dir.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Wolfram® 2 | 3 | Thank you for taking the time to contribute to the [Wolfram Research](https://github.com/wolframresearch) repos on GitHub. 4 | 5 | ## Licensing of Contributions 6 | 7 | By contributing to Wolfram, you agree and affirm that: 8 | 9 | > Wolfram may release your contribution under the terms of the [MIT license](https://opensource.org/licenses/MIT); and 10 | 11 | > You have read and agreed to the [Developer Certificate of Origin](http://developercertificate.org/), version 1.1 or later. 12 | 13 | Please see [LICENSE](LICENSE) for licensing conditions pertaining 14 | to individual repositories. 15 | 16 | 17 | ## Bug reports 18 | 19 | ### Security Bugs 20 | 21 | Please **DO NOT** file a public issue regarding a security issue. 22 | Rather, send your report privately to security@wolfram.com. Security 23 | reports are appreciated and we will credit you for it. We do not offer 24 | a security bounty, but the forecast in your neighborhood will be cloudy 25 | with a chance of Wolfram schwag! 26 | 27 | ### General Bugs 28 | 29 | Please use the repository issues page to submit general bug issues. 30 | 31 | Please do not duplicate issues. 32 | 33 | Please do send a complete and well-written report to us. Note: **the 34 | thoroughness of your report will positively correlate to our willingness 35 | and ability to address it**. 36 | 37 | When reporting issues, always include: 38 | 39 | * Your version of *Mathematica*® or the Wolfram Language. 40 | * Your operating system. -------------------------------------------------------------------------------- /Tools/GenerateHeroImage.wl: -------------------------------------------------------------------------------- 1 | $pacDir = DirectoryName[ $InputFileName, 2 ]; 2 | 3 | $icon = BinaryDeserialize @ ByteArray[ "\ 4 | OEM6eJxLYynmcC9KLMjITC5O4ypm8cksLkljLOYMAQpk56UWFxcJgsFE+zQmuCyHW2Jyqlt+US5cjLm\ 5 | YI8jdyTk/J7+oiAEMPtgXzZ4FArfsi+TAYJ09UCu7f0FicmZJJVwV0Ahut8ycnNQU59KistSDDMyMLM\ 6 | wMTAwMjFB0UJmZkZUJolzAAUgccF9o54DEd+C67gqiE3bM0wPRD6qmOCHzG2ZKWjig6x9w3zhwmYNdz\ 7 | RDTLwNxlaI9mM9x/bE9Er+BK1zbAVn9EgMTB3T9A+4bhXXHwa7pOSTqBXa1SJ43mH590c0BSb5BpM8Z\ 8 | 6lsHbPJg/UT7Rj9Igzs+/r190fczkxhMTZ/aF8mc+5PFJ3CBSimNYckHXZSUI8dqAw77HfdsUOIGyof\ 9 | FHYr+weAbWH7xA+eLtGUuIPqLxh5wXD1YXGCAHDcKMf0iyHGDon8w+MYHOW4WzIwEu7LB/aEC2DeBEW\ 10 | A+ohT45w3JL5BSAkU/6fkGg0GZbxpeTzQEp6wzZ8wckPgXzrxxRE5ZD6pCbFFS2jp3Awd0/QPuG1hKK\ 11 | bDVckNOaQlpz5xQUtI6d3DZ5mC82QibPFj/gPsGVsLC4gZeQkPDHsaHxw1UPTzukPWDXB5UmpNazO1Y\ 12 | XJCaXBKUWJKZX8zpWFqSnwtkJsPlOT1zE9NTgzOrUg8qM0ID1c8BRiOUBeTkAw3JSwcqY2KCqoMBmHo\ 13 | EHwDQG+1Z" ]; 14 | 15 | $heroImage = 16 | Rasterize[ 17 | Framed[ 18 | Grid[ 19 | { 20 | { 21 | $icon, 22 | Style[ 23 | Column[ { "Paclet", "CI/CD" }, Spacings -> 0 ], 24 | FontColor -> White, 25 | FontFamily -> "Source Sans Pro", 26 | FontWeight -> "Heavy", 27 | FontSize -> 30 28 | ] 29 | } 30 | }, 31 | Alignment -> { Center, Center } 32 | ], 33 | Background -> RGBColor[ "#ab3626" ], 34 | FrameStyle -> None, 35 | RoundingRadius -> 8, 36 | FrameMargins -> 12 37 | ], 38 | ImageResolution -> 144, 39 | RasterSize -> { Automatic, 400 }, 40 | Background -> None 41 | ]; 42 | 43 | Export[ 44 | FileNameJoin @ { $pacDir, "Images", "HeroImage.png" }, 45 | $heroImage 46 | ] -------------------------------------------------------------------------------- /Tests/Utilities.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/Utilities.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/Utilities.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize@@Tests/Utilities.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Section::Closed:: *) 35 | (*relativePath*) 36 | VerificationTest[ 37 | Wolfram`PacletCICD`Private`relativePath[ 38 | "path/to/file", 39 | "path/another/file" 40 | ], 41 | "../../another/file", 42 | TestID -> "relativePath@@Tests/Utilities.wlt:36,1-43,2" 43 | ] 44 | 45 | VerificationTest[ 46 | Wolfram`PacletCICD`Private`relativePath[ "path/to/file", "path/to/file" ], 47 | ".", 48 | TestID -> "relativePath@@Tests/Utilities.wlt:45,1-49,2" 49 | ] 50 | 51 | VerificationTest[ 52 | Wolfram`PacletCICD`Private`relativePath[ "path/to/file" ][ 53 | "path/another/file" 54 | ], 55 | "../../another/file", 56 | TestID -> "relativePath@@Tests/Utilities.wlt:51,1-57,2" 57 | ] 58 | -------------------------------------------------------------------------------- /Kernel/Symbols.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Declare Fully Qualified Symbol Names*) 4 | Wolfram`PacletCICD`$Debug; 5 | Wolfram`PacletCICD`$ExamplesLocation; 6 | Wolfram`PacletCICD`$WorkflowValueScope; 7 | Wolfram`PacletCICD`AnnotateTestIDs; 8 | Wolfram`PacletCICD`ASTCondition; 9 | Wolfram`PacletCICD`ASTConditionValue; 10 | Wolfram`PacletCICD`ASTPattern; 11 | Wolfram`PacletCICD`ASTPatternTest; 12 | Wolfram`PacletCICD`BuildPaclet; 13 | Wolfram`PacletCICD`BytesToQuantity; 14 | Wolfram`PacletCICD`CheckDependencies; 15 | Wolfram`PacletCICD`CheckPaclet; 16 | Wolfram`PacletCICD`CompileLibraryResources; 17 | Wolfram`PacletCICD`ConsoleDebug; 18 | Wolfram`PacletCICD`ConsoleError; 19 | Wolfram`PacletCICD`ConsoleLog; 20 | Wolfram`PacletCICD`ConsoleNotice; 21 | Wolfram`PacletCICD`ConsoleType; 22 | Wolfram`PacletCICD`ConsoleWarning; 23 | Wolfram`PacletCICD`CreatePublisherToken; 24 | Wolfram`PacletCICD`DeletePublisherToken; 25 | Wolfram`PacletCICD`DeployPaclet; 26 | Wolfram`PacletCICD`EquivalentNodeQ; 27 | Wolfram`PacletCICD`ExampleDirectory; 28 | Wolfram`PacletCICD`FormatNotebooks; 29 | Wolfram`PacletCICD`FormattingHelper; 30 | Wolfram`PacletCICD`FromAST; 31 | Wolfram`PacletCICD`GitHubPacletInstall; 32 | Wolfram`PacletCICD`GitHubSecret; 33 | Wolfram`PacletCICD`InitializeWorkflowValues; 34 | Wolfram`PacletCICD`LoadSubPackage; 35 | Wolfram`PacletCICD`MessageFailure; 36 | Wolfram`PacletCICD`PacletCICD; 37 | Wolfram`PacletCICD`PublisherTokenObject; 38 | Wolfram`PacletCICD`PublisherTokens; 39 | Wolfram`PacletCICD`ReadableForm; 40 | Wolfram`PacletCICD`ReplacePacletInfo; 41 | Wolfram`PacletCICD`ResetExampleDirectory; 42 | Wolfram`PacletCICD`ScriptConfirm; 43 | Wolfram`PacletCICD`ScriptConfirmAssert; 44 | Wolfram`PacletCICD`ScriptConfirmBy; 45 | Wolfram`PacletCICD`ScriptConfirmMatch; 46 | Wolfram`PacletCICD`SecondsToQuantity; 47 | Wolfram`PacletCICD`SetContextLoad; 48 | Wolfram`PacletCICD`SetPacletInfo; 49 | Wolfram`PacletCICD`SubmitPaclet; 50 | Wolfram`PacletCICD`TestPaclet; 51 | Wolfram`PacletCICD`ToMarkdownString; 52 | Wolfram`PacletCICD`Workflow; 53 | Wolfram`PacletCICD`WorkflowEvaluate; 54 | Wolfram`PacletCICD`WorkflowExport; 55 | Wolfram`PacletCICD`WorkflowJob; 56 | Wolfram`PacletCICD`WorkflowJobQ; 57 | Wolfram`PacletCICD`WorkflowQ; 58 | Wolfram`PacletCICD`WorkflowStep; 59 | Wolfram`PacletCICD`WorkflowStepQ; 60 | Wolfram`PacletCICD`WorkflowValue; -------------------------------------------------------------------------------- /Scripts/SetReleaseParameters.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | 3 | (* :!CodeAnalysis::BeginBlock:: *) 4 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 5 | 6 | (* ::**********************************************************************:: *) 7 | (* ::Section::Closed:: *) 8 | (*Initialization*) 9 | $pacDir = DirectoryName[ $InputFileName, 2 ]; 10 | PacletDirectoryLoad @ $pacDir; 11 | Get[ "Wolfram`PacletCICD`" ]; 12 | 13 | (* ::**********************************************************************:: *) 14 | (* ::Subsection::Closed:: *) 15 | (*Get Release Directory*) 16 | $releaseDir = FileNameJoin @ { $pacDir, "Release" }; 17 | 18 | If[ ! DirectoryQ @ $releaseDir, 19 | Print[ "::error::Release directory ", $releaseDir, " does not exist!" ]; 20 | Exit[ 1 ], 21 | Print @ StringJoin[ 22 | "Release Files: \n\t", 23 | StringRiffle[ FileNames[ All, $releaseDir, Infinity ], "\n\t" ] 24 | ] 25 | ]; 26 | 27 | (* ::**********************************************************************:: *) 28 | (* ::Subsection::Closed:: *) 29 | (*Get Paclet Archive*) 30 | $releasePaclet = First[ FileNames[ "*.paclet", $releaseDir ], $Failed ]; 31 | 32 | If[ ! FileExistsQ @ $releasePaclet, 33 | Print[ "::error::Release paclet file not found!" ]; 34 | Exit[ 1 ] 35 | ]; 36 | 37 | $releaseFile = FileNameTake @ $releasePaclet; 38 | 39 | (* ::**********************************************************************:: *) 40 | (* ::Subsection::Closed:: *) 41 | (*Get Release Version*) 42 | $paclet = PacletObject @ File @ $pacDir; 43 | If[ ! PacletObjectQ @ $paclet, 44 | Print[ "::error::Paclet directory failure: " <> ToString @ $pacDir ]; 45 | Exit[ 1 ] 46 | ]; 47 | 48 | $version = $paclet[ "Version" ]; 49 | 50 | If[ ! StringQ @ $version && ! StringMatchQ[ $version, (DigitCharacter|".").. ], 51 | Print[ "::error::Paclet version failure: " <> ToString @ $version ]; 52 | Exit[ 1 ] 53 | ]; 54 | 55 | (* ::**********************************************************************:: *) 56 | (* ::Section::Closed:: *) 57 | (*Set Parameters*) 58 | Wolfram`PacletCICD`Private`setOutput[ "PACLET_BUILD_DIR" , $releaseDir ]; 59 | Wolfram`PacletCICD`Private`setOutput[ "PACLET_PATH" , $releasePaclet ]; 60 | Wolfram`PacletCICD`Private`setOutput[ "PACLET_FILE" , $releaseFile ]; 61 | Wolfram`PacletCICD`Private`setOutput[ "PACLET_RELEASE_TAG", "v"<>$version ]; 62 | 63 | (* :!CodeAnalysis::EndBlock:: *) 64 | -------------------------------------------------------------------------------- /Kernel/PacletCICD.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | EndPackage[ ]; 6 | 7 | (* ::**********************************************************************:: *) 8 | (* ::Section::Closed:: *) 9 | (*Install Dependencies*) 10 | If[ StringQ @ Environment[ "GITHUB_WORKFLOW" ] && $VersionNumber < 14, 11 | Quiet[ 12 | PacletInstall[ "https://github.com/WolframResearch/PacletCICD/releases/download/DefinitionNotebookClient-1.18.0/DefinitionNotebookClient-1.18.0.paclet" ]; 13 | PacletInstall[ "https://github.com/WolframResearch/PacletCICD/releases/download/PacletResource-1.6.0/PacletResource-1.6.0.paclet" ]; 14 | , 15 | PacletInstall::samevers 16 | ] 17 | ]; 18 | 19 | (* ::**********************************************************************:: *) 20 | (* ::Section::Closed:: *) 21 | (*Load Package*) 22 | Wolfram`PacletCICD`Internal`$MXFile = 23 | FileNameJoin @ { 24 | DirectoryName @ $InputFileName, 25 | ToString @ $SystemWordLength <> "Bit", 26 | "PacletCICD.mx" 27 | }; 28 | 29 | If[ FileExistsQ[ Wolfram`PacletCICD`Internal`$MXFile ] 30 | , 31 | Wolfram`PacletCICD`Internal`$MX = True; 32 | Get[ Wolfram`PacletCICD`Internal`$MXFile ] 33 | , 34 | Wolfram`PacletCICD`Internal`$MX = False; 35 | Quiet[ 36 | Block[ { $ContextPath }, 37 | Get[ "Wolfram`PacletCICD`Symbols`" ]; 38 | Get[ "Wolfram`PacletCICD`Config`" ]; 39 | Get[ "Wolfram`PacletCICD`ErrorHandling`" ]; 40 | Get[ "Wolfram`PacletCICD`Loading`" ]; 41 | Get[ "Wolfram`PacletCICD`Utilities`" ]; 42 | ], 43 | General::shdw 44 | ] 45 | ]; 46 | 47 | (* ::**********************************************************************:: *) 48 | (* ::Section::Closed:: *) 49 | (*Initialize Workflow Values*) 50 | If[ StringQ @ Environment[ "GITHUB_WORKFLOW" ], 51 | Wolfram`PacletCICD`InitializeWorkflowValues[ ] 52 | ]; 53 | 54 | (* ::**********************************************************************:: *) 55 | (* ::Section::Closed:: *) 56 | (*Check Dependencies*) 57 | If[ $VersionNumber < 13.2, 58 | Wolfram`PacletCICD`CheckDependencies[ 59 | PacletObject @ File @ DirectoryName[ $InputFileName, 2 ], 60 | Message -> True 61 | ] 62 | ]; 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Continuous integration and deployment for Wolfram Language Paclets 2 | 3 | ******************************************************************************** 4 | 5 | [![Check Paclet](https://github.com/WolframResearch/PacletCICD/actions/workflows/check-paclet.yml/badge.svg)](https://github.com/WolframResearch/PacletCICD/actions/workflows/check-paclet.yml) [![Release Paclet](https://github.com/WolframResearch/PacletCICD/actions/workflows/release.yml/badge.svg)](https://github.com/WolframResearch/PacletCICD/actions/workflows/release.yml) 6 | 7 | ![Notebook Icon](.github/images/nb.svg) [View Notebooks](Notebooks.md) 8 | 9 | # Installing PacletCICD 10 | 11 | 12 | ## From the [Wolfram Paclet Repository](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD) 13 | 14 | Using Wolfram Language version 13.0 or later: 15 | 16 | ```Mathematica 17 | PacletInstall[ResourceObject["Wolfram/PacletCICD"]] 18 | ``` 19 | 20 | ## Using [GitHubInstall](https://resources.wolframcloud.com/FunctionRepository/resources/GitHubInstall/) 21 | 22 | Using Wolfram Language version 12.0 or later: 23 | 24 | ```Mathematica 25 | ResourceFunction["GitHubInstall"]["WolframResearch", "PacletCICD"] 26 | ``` 27 | 28 | ## From Github 29 | The PacletCICD release comes in the form of a `.paclet` file, which contains the entire package and its documentation. Download the latest release from the [GitHub repo's releases page](https://github.com/WolframResearch/PacletCICD/releases). To install, run the following command in the Wolfram Language: 30 | 31 | ```Mathematica 32 | PacletInstall["/full/path/to/PacletCICD.paclet"] 33 | ``` 34 | 35 | This will permanently install the PacletCICD paclet. The Wolfram Language will always use the latest installed version of PacletCICD. Installed versions can be enumerated using the command: 36 | 37 | ```Mathematica 38 | PacletFind["Wolfram/PacletCICD"] 39 | ``` 40 | 41 | And all versions can be uninstalled using the command: 42 | 43 | ```Mathematica 44 | PacletUninstall["Wolfram/PacletCICD"] 45 | ``` 46 | 47 | # Features 48 | 49 | Automatically check Paclets for potential issues in a GitHub CI/CD workflow: 50 | 51 | 52 | 53 | Generate code annotations automatically whenever code is pushed to a branch: 54 | 55 | 56 | 57 | 58 | 59 | 60 | ## GitHub Actions 61 | 62 | PacletCICD functions can be used indirectly via the following GitHub Marketplace actions: 63 | * [build-paclet](https://github.com/marketplace/actions/build-paclet) 64 | * [check-paclet](https://github.com/marketplace/actions/check-paclet) 65 | 66 | These actions can also be automatically configured for your Paclet by using WorkflowExport: 67 | 68 | 69 | 70 | # License 71 | 72 | This project is licensed under the terms of the MIT license. See the LICENSE file in the root directory of this source tree for details. 73 | -------------------------------------------------------------------------------- /Tools/GenerateLogo.wl: -------------------------------------------------------------------------------- 1 | $pacDir = DirectoryName[ $InputFileName, 2 ]; 2 | 3 | $icon = BinaryDeserialize @ ByteArray[ "\ 4 | OEM6eJxLYynmcC9KLMjITC5O4ypm8cksLkljLOYMAQpk56UWFxcJgsFE+zQmuCyHW2Jyqlt+US5cjLm\ 5 | YI8jdyTk/J7+oiAEMPtgXzZ4FArfsi+TAYJ09UCu7f0FicmZJJVwV0Ahut8ycnNQU59KistSDDMyMLM\ 6 | wMTAwMjFB0UJmZkZUJolzAAUgccF9o54DEd+C67gqiE3bM0wPRD6qmOCHzG2ZKWjig6x9w3zhwmYNdz\ 7 | RDTLwNxlaI9mM9x/bE9Er+BK1zbAVn9EgMTB3T9A+4bhXXHwa7pOSTqBXa1SJ43mH590c0BSb5BpM8Z\ 8 | 6lsHbPJg/UT7Rj9Igzs+/r190fczkxhMTZ/aF8mc+5PFJ3CBSimNYckHXZSUI8dqAw77HfdsUOIGyof\ 9 | FHYr+weAbWH7xA+eLtGUuIPqLxh5wXD1YXGCAHDcKMf0iyHGDon8w+MYHOW4WzIwEu7LB/aEC2DeBEW\ 10 | A+ohT45w3JL5BSAkU/6fkGg0GZbxpeTzQEp6wzZ8wckPgXzrxxRE5ZD6pCbFFS2jp3Awd0/QPuG1hKK\ 11 | bDVckNOaQlpz5xQUtI6d3DZ5mC82QibPFj/gPsGVsLC4gZeQkPDHsaHxw1UPTzukPWDXB5UmpNazO1Y\ 12 | XJCaXBKUWJKZX8zpWFqSnwtkJsPlOT1zE9NTgzOrUg8qM0ID1c8BRiOUBeTkAw3JSwcqY2KCqoMBmHo\ 13 | EHwDQG+1Z" ]; 14 | 15 | $textCurves = 16 | First @ First @ ImportString[ 17 | ExportString[ 18 | Style[ 19 | "CI/CD", 20 | FontColor -> White, 21 | FontFamily -> "Source Sans Pro", 22 | FontWeight -> "Heavy", 23 | FontSize -> 22 24 | ], 25 | "PDF" 26 | ], 27 | { "PDF", "PageGraphics" }, 28 | "TextOutlines" -> True 29 | ]; 30 | 31 | $logoGFX = 32 | Framed[ 33 | Show[ 34 | $icon, 35 | Epilog -> 36 | Inset[ 37 | Graphics[ 38 | { 39 | EdgeForm @ { 40 | Thickness[ 0.05 ], 41 | RGBColor[ "#ab3626" ] 42 | }, 43 | $textCurves, 44 | EdgeForm @ None, 45 | $textCurves 46 | }, 47 | PlotRangePadding -> Scaled[ 0.1 ], 48 | ImageSize -> 65 49 | ], 50 | Scaled @ { 0.5, 0.2 } 51 | ], 52 | PlotRangePadding -> { 53 | { Automatic , Automatic }, 54 | { Scaled[ 0.125 ], Automatic } 55 | } 56 | ], 57 | Background -> RGBColor[ "#ab3626" ], 58 | FrameStyle -> None, 59 | RoundingRadius -> 8, 60 | FrameMargins -> { { 4, 4 }, { 4, 4 } } 61 | ]; 62 | 63 | $pacletLogo = 64 | Rasterize[ 65 | $logoGFX, 66 | ImageResolution -> 144, 67 | ImageSize -> 50, 68 | Background -> None 69 | ]; 70 | 71 | $logo = 72 | Rasterize[ 73 | $logoGFX, 74 | ImageResolution -> 144, 75 | RasterSize -> 128, 76 | Background -> None 77 | ]; 78 | 79 | Export[ 80 | FileNameJoin @ { $pacDir, "Images", "Icon.png" }, 81 | $logo 82 | ]; 83 | 84 | Export[ 85 | FileNameJoin @ { $pacDir, "Images", "PacletIcon.png" }, 86 | $pacletLogo, 87 | "PNG", 88 | ImageSize -> 50 89 | ] 90 | 91 | -------------------------------------------------------------------------------- /PacletInfo.wl: -------------------------------------------------------------------------------- 1 | PacletObject[ <| 2 | "Name" -> "Wolfram/PacletCICD", 3 | "Description" -> "Continuous integration and deployment for Wolfram Language paclets", 4 | "Creator" -> "Richard Hennigan ", 5 | "URL" -> "https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD", 6 | "SourceControlURL" -> "https://github.com/WolframResearch/PacletCICD", 7 | "License" -> "MIT", 8 | "PublisherID" -> "Wolfram", 9 | "Version" -> "0.36.2", 10 | "WolframVersion" -> "13.0+", 11 | "ReleaseID" -> "$RELEASE_ID$", 12 | "ReleaseDate" -> "$RELEASE_DATE$", 13 | "ReleaseURL" -> "$RELEASE_URL$", 14 | "ActionURL" -> "$ACTION_URL$", 15 | "Icon" -> "Images/PacletIcon.png", 16 | "Dependencies" -> { 17 | "DefinitionNotebookClient" -> ">=1.18.0", 18 | "PacletResource" -> ">=1.6.0", 19 | "ResourceSystemClient" -> ">=1.24.1" 20 | }, 21 | "Extensions" -> { 22 | { 23 | "Kernel", 24 | "Root" -> "Kernel", 25 | "Context" -> { "Wolfram`PacletCICD`" }, 26 | "Symbols" -> { 27 | "Wolfram`PacletCICD`$ExamplesLocation", 28 | "Wolfram`PacletCICD`AnnotateTestIDs", 29 | "Wolfram`PacletCICD`BuildPaclet", 30 | "Wolfram`PacletCICD`CheckDependencies", 31 | "Wolfram`PacletCICD`CheckPaclet", 32 | "Wolfram`PacletCICD`CreatePublisherToken", 33 | "Wolfram`PacletCICD`DeployPaclet", 34 | "Wolfram`PacletCICD`ExampleDirectory", 35 | "Wolfram`PacletCICD`GitHubPacletInstall", 36 | "Wolfram`PacletCICD`GitHubSecret", 37 | "Wolfram`PacletCICD`PacletCICD", 38 | "Wolfram`PacletCICD`PublisherTokenObject", 39 | "Wolfram`PacletCICD`SubmitPaclet", 40 | "Wolfram`PacletCICD`TestPaclet", 41 | "Wolfram`PacletCICD`Workflow", 42 | "Wolfram`PacletCICD`WorkflowEvaluate", 43 | "Wolfram`PacletCICD`WorkflowExport", 44 | "Wolfram`PacletCICD`WorkflowJob", 45 | "Wolfram`PacletCICD`WorkflowJobQ", 46 | "Wolfram`PacletCICD`WorkflowQ", 47 | "Wolfram`PacletCICD`WorkflowStep", 48 | "Wolfram`PacletCICD`WorkflowStepQ", 49 | "Wolfram`PacletCICD`WorkflowValue" 50 | } 51 | }, 52 | { 53 | "Documentation", 54 | "Root" -> "Documentation", 55 | "Language" -> "English" 56 | }, 57 | { 58 | "Asset", 59 | "Assets" -> { 60 | { "License" , "./LICENSE" }, 61 | { "ReadMe" , "./README.md" }, 62 | { "Images" , "./Images" }, 63 | { "Examples" , "./Examples" }, 64 | { "Resources", "./Resources" } 65 | } 66 | } 67 | } 68 | |> ] 69 | -------------------------------------------------------------------------------- /Kernel/Compilation.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | CompileLibraryResources // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | (* ::**********************************************************************:: *) 11 | (* ::Section::Closed:: *) 12 | (*CompileLibraryResources*) 13 | CompileLibraryResources // Options = { 14 | "SourceDirectory" -> Automatic, 15 | "BuildDirectory" -> Automatic, 16 | "LibraryName" -> Automatic, 17 | "EntryPoint" -> None (* TODO *) 18 | }; 19 | 20 | $$paclet = _PacletObject? PacletObjectQ; 21 | 22 | CompileLibraryResources[ pac: $$paclet, opts: OptionsPattern[ ] ] := 23 | catchTop @ Module[ { dir }, 24 | dir = pac[ "Location" ]; 25 | compileLibraryResources[ 26 | pac, 27 | toSourceDirectory[ OptionValue[ "SourceDirectory" ], dir ], 28 | toBuildDirectory[ OptionValue[ "BuildDirectory" ], dir ], 29 | toLibraryName[ OptionValue[ "LibraryName" ], pac ] 30 | ] 31 | ]; 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*toSourceDirectory*) 36 | toSourceDirectory[ dir_? DirectoryQ, _ ] := dir; 37 | 38 | toSourceDirectory[ Automatic, pacDir_ ] := 39 | SelectFirst[ Select[ FileNames[ All, pacDir ], DirectoryQ ], 40 | FileNames[ "*.c", # ] =!= { } & 41 | ]; 42 | 43 | toSourceDirectory // catchUndefined; 44 | 45 | (* ::**********************************************************************:: *) 46 | (* ::Subsection::Closed:: *) 47 | (*toBuildDirectory*) 48 | toBuildDirectory[ dir_? DirectoryQ, _ ] := dir; 49 | 50 | toBuildDirectory[ dir_File, _ ] := dir; 51 | 52 | toBuildDirectory[ Automatic, pacDir_ ] := (* TODO: check extension root *) 53 | FileNameJoin @ { pacDir, "LibraryResources", $SystemID }; 54 | 55 | toBuildDirectory // catchUndefined; 56 | 57 | (* ::**********************************************************************:: *) 58 | (* ::Subsection::Closed:: *) 59 | (*toLibraryName*) 60 | toLibraryName[ name_String, _ ] := name; 61 | 62 | toLibraryName[ Automatic, pac: $$paclet ] := 63 | StringDelete[ pac[ "Name" ], StartOfString~~___~~"/" ]; 64 | 65 | toLibraryName // catchUndefined; 66 | 67 | (* ::**********************************************************************:: *) 68 | (* ::Subsection::Closed:: *) 69 | (*compileLibraryResources*) 70 | compileLibraryResources[ pac_, src_, tgt_, name_ ] := 71 | Module[ { files }, 72 | needs[ "CCompilerDriver`" -> None ]; 73 | files = FileNames[ "*.c", src ]; 74 | CCompilerDriver`CreateLibrary[ 75 | files, 76 | name, 77 | "TargetDirectory" -> tgt, 78 | "CleanIntermediate" -> True 79 | ] 80 | ]; 81 | 82 | compileLibraryResources // catchUndefined; 83 | 84 | (* ::**********************************************************************:: *) 85 | (* ::Section::Closed:: *) 86 | (*Package Footer*) 87 | End[ ]; 88 | EndPackage[ ]; -------------------------------------------------------------------------------- /Scripts/BuildPacletMX.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | 3 | (* :!CodeAnalysis::BeginBlock:: *) 4 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 5 | 6 | (* ::**********************************************************************:: *) 7 | (* ::Section::Closed:: *) 8 | (*Initialization*) 9 | $pacDir = DirectoryName[ $InputFileName, 2 ]; 10 | $mxDir = FileNameJoin @ { $pacDir, "Kernel", ToString @ $SystemWordLength <> "Bit" }; 11 | If[ DirectoryQ @ $mxDir, DeleteDirectory[ $mxDir, DeleteContents -> True ] ]; 12 | 13 | $resDir = FileNameJoin @ { $pacDir, "Resources" }; 14 | PacletDirectoryLoad @ $pacDir; 15 | Block[ { Wolfram`PacletCICD`Internal`$BuildingMX = True }, 16 | Get[ "Wolfram`PacletCICD`" ] 17 | ]; 18 | 19 | (* ::**********************************************************************:: *) 20 | (* ::Section::Closed:: *) 21 | (*Pre-cache Values*) 22 | 23 | (* TODO: move this to package code conditioned on $BuildingMX *) 24 | 25 | (* ::**********************************************************************:: *) 26 | (* ::Subsection::Closed:: *) 27 | (*normalizeActionName*) 28 | LoadSubPackage[ "Workflows" ]; 29 | 30 | Print[ "Caching values for normalizeActionName:" ]; 31 | Scan[ 32 | Wolfram`PacletCICD`Private`normalizeActionName, 33 | { 34 | "WolframResearch/build-paclet@latest", 35 | "WolframResearch/check-paclet@latest", 36 | "WolframResearch/submit-paclet@latest", 37 | "WolframResearch/test-paclet@latest" 38 | } 39 | ]; 40 | 41 | (* ::**********************************************************************:: *) 42 | (* ::Subsection::Closed:: *) 43 | (*wlResource*) 44 | Print[ "Caching values for wlResource:" ]; 45 | Block[ { Wolfram`PacletCICD`Private`$resourceDirectory = $resDir }, 46 | Scan[ Wolfram`PacletCICD`Private`wlResource @* FileBaseName , 47 | FileNames[ "*.wl", $resDir ] 48 | ] 49 | ]; 50 | 51 | (* ::**********************************************************************:: *) 52 | (* ::Subsection::Closed:: *) 53 | (*wxfResource*) 54 | Print[ "Caching values for wxfResource:" ]; 55 | Block[ { Wolfram`PacletCICD`Private`$resourceDirectory = $resDir }, 56 | Scan[ Wolfram`PacletCICD`Private`wxfResource @* FileBaseName , 57 | FileNames[ "*.wxf", $resDir ] 58 | ] 59 | ]; 60 | 61 | (* ::**********************************************************************:: *) 62 | (* ::Subsection::Closed:: *) 63 | (*actionIcon*) 64 | LoadSubPackage[ "Formatting" ]; 65 | 66 | Print[ "Caching values for actionIcon:" ]; 67 | Scan[ 68 | Wolfram`PacletCICD`Private`actionIcon, 69 | { 70 | "actions/cache", 71 | "actions/checkout", 72 | "actions/create-release", 73 | "actions/download-artifact", 74 | "actions/upload-artifact", 75 | "actions/upload-release-asset", 76 | "WolframResearch/build-paclet", 77 | "WolframResearch/check-paclet", 78 | "WolframResearch/submit-paclet", 79 | "WolframResearch/test-paclet" 80 | } 81 | ]; 82 | 83 | (* ::**********************************************************************:: *) 84 | (* ::Section::Closed:: *) 85 | (*Build*) 86 | Print @ Wolfram`PacletCICD`Internal`BuildMX[ ]; 87 | (* :!CodeAnalysis::EndBlock:: *) 88 | -------------------------------------------------------------------------------- /Kernel/PacletInformation.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | ClearAll[ ReplacePacletInfo, SetPacletInfo ]; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | $ContextAliases[ "cp`" ] = "CodeParser`"; 11 | 12 | (* ::**********************************************************************:: *) 13 | (* ::Section::Closed:: *) 14 | (*ReplacePacletInfo*) 15 | ReplacePacletInfo[ ___ ] := Failure[ "NotImplemented", <| |> ]; 16 | 17 | (* ::**********************************************************************:: *) 18 | (* ::Section::Closed:: *) 19 | (*SetPacletInfo*) 20 | SetPacletInfo[ ___ ] := Failure[ "NotImplemented", <| |> ]; 21 | 22 | (* ::**********************************************************************:: *) 23 | (* ::Section::Closed:: *) 24 | (*Utilities*) 25 | 26 | (* ::**********************************************************************:: *) 27 | (* ::Subsection::Closed:: *) 28 | (*insertPacletInfo*) 29 | insertPacletInfo[ id_, key_ -> val_ ] := Enclose[ 30 | needs[ "CodeParser`" -> None ]; 31 | With[ { file = ConfirmBy[ pacletInfoFile @ id, FileExistsQ ] }, 32 | insertPacletInfo[ 33 | file, 34 | cp`CodeParse[ file, "SourceConvention" -> "SourceCharacterIndex" ], 35 | key -> val 36 | ] 37 | ], 38 | throwError[ "Cannot find PacletInfo file for `1`.", id ] & 39 | ]; 40 | 41 | insertPacletInfo[ file_, ast_, key_ -> val_ ] := Enclose[ 42 | Module[ { pos, string, new }, 43 | pos = FirstCase[ 44 | ast, 45 | ASTPattern @ HoldPattern[ PacletObject ][ Association[ 46 | ___, 47 | key -> ASTPattern[ v_, KeyValuePattern[ cp`Source -> src_ ] ], 48 | ___ 49 | ] ] :> src, 50 | Throw[ appendPacletInfo[ file, ast, key -> val ], $tag ], 51 | Infinity 52 | ]; 53 | string = ReadString @ file; 54 | new = StringReplacePart[ string, ToString[ val, InputForm ], pos ]; 55 | WithCleanup[ 56 | BinaryWrite[ file, StringReplace[ new, "\r\n" -> "\n" ] ], 57 | Close @ file 58 | ] 59 | ] ~Catch~ $tag 60 | ]; 61 | 62 | (* ::**********************************************************************:: *) 63 | (* ::Subsection::Closed:: *) 64 | (*appendPacletInfo*) 65 | appendPacletInfo 66 | 67 | (* ::**********************************************************************:: *) 68 | (* ::Subsection::Closed:: *) 69 | (*pacletInfoFile*) 70 | pacletInfoFile[ pac_PacletObject? PacletObjectQ ] := Enclose[ 71 | Module[ { dir, files, full }, 72 | dir = ConfirmBy[ pac[ "Location" ], DirectoryQ ]; 73 | files = FileNames[ "PacletInfo."~~("wl"|"m"), dir, IgnoreCase -> True ]; 74 | full = File @* ExpandFileName /@ files; 75 | Quiet @ Confirm @ SelectFirst[ full, PacletObjectQ @* PacletObject ] 76 | ], 77 | throwError[ "Cannot find PacletInfo file for `1`.", pac ] & 78 | ]; 79 | 80 | pacletInfoFile[ file_File ] := Enclose[ 81 | pacletInfoFile @ ConfirmBy[ PacletObject @ Flatten @ file, PacletObjectQ ], 82 | throwError[ "`1` does not correspond to a valid paclet.", file ] & 83 | ]; 84 | 85 | pacletInfoFile[ spec_ ] := Enclose[ 86 | pacletInfoFile @ ConfirmBy[ PacletObject @ spec, PacletObjectQ ], 87 | throwError[ "`1` is not a valid paclet specification.", spec ] & 88 | ]; 89 | 90 | (* ::**********************************************************************:: *) 91 | (* ::Section::Closed:: *) 92 | (*Package Footer*) 93 | End[ ]; 94 | EndPackage[ ]; -------------------------------------------------------------------------------- /Tests/WorkflowExport.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/WorkflowExport.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/WorkflowExport.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize@@Tests/WorkflowExport.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*Context Check*) 36 | VerificationTest[ 37 | Context @ WorkflowExport, 38 | "Wolfram`PacletCICD`", 39 | TestID -> "WorkflowExport-Context@@Tests/WorkflowExport.wlt:36,1-40,2" 40 | ] 41 | 42 | VerificationTest[ 43 | Context @ ExampleDirectory, 44 | "Wolfram`PacletCICD`", 45 | TestID -> "ExampleDirectory-Context@@Tests/WorkflowExport.wlt:42,1-46,2" 46 | ] 47 | 48 | (* ::**********************************************************************:: *) 49 | (* ::Section::Closed:: *) 50 | (*WorkflowExport*) 51 | VerificationTest[ 52 | Quiet[ 53 | check = WorkflowExport[ ExampleDirectory[ "FewIssues" ], "Check" ], 54 | WorkflowExport::entitlement 55 | ], 56 | _File? FileExistsQ, 57 | SameTest -> MatchQ, 58 | TestID -> "WorkflowExport-Check@@Tests/WorkflowExport.wlt:51,1-59,2" 59 | ] 60 | 61 | VerificationTest[ 62 | build = WorkflowExport[ ExampleDirectory[ "FewIssues" ], "Build" ], 63 | _File? FileExistsQ, 64 | SameTest -> MatchQ, 65 | TestID -> "WorkflowExport-Build@@Tests/WorkflowExport.wlt:61,1-66,2" 66 | ] 67 | 68 | VerificationTest[ 69 | release = WorkflowExport[ ExampleDirectory[ "FewIssues" ], "Release" ], 70 | _File? FileExistsQ, 71 | SameTest -> MatchQ, 72 | TestID -> "WorkflowExport-Release@@Tests/WorkflowExport.wlt:68,1-73,2" 73 | ] 74 | 75 | VerificationTest[ 76 | compile = WorkflowExport[ ExampleDirectory[ "FewIssues" ], "Compile" ], 77 | _File? FileExistsQ, 78 | SameTest -> MatchQ, 79 | TestID -> "WorkflowExport-Compile@@Tests/WorkflowExport.wlt:75,1-80,2" 80 | ] 81 | 82 | VerificationTest[ 83 | AllTrue[ Gather @ { check, build, release, compile }, Length[ # ] === 1 & ], 84 | True, 85 | TestID -> "WorkflowExport-Uniqueness@@Tests/WorkflowExport.wlt:82,1-86,2" 86 | ] 87 | 88 | VerificationTest[ 89 | StringReplace[ 90 | ReadString @ WorkflowExport[ ExampleDirectory[ "Sample" ], "Submit" ], 91 | "\r\n" -> "\n" 92 | ], 93 | Workflow[ "Submit" ][ "YAML" ], 94 | TestID -> "WorkflowExport-YAML-Equivalence@@Tests/WorkflowExport.wlt:88,1-95,2" 95 | ] 96 | 97 | VerificationTest[ 98 | ResetExampleDirectory[ "Sample" ], 99 | _Success, 100 | SameTest -> MatchQ, 101 | TestID -> "WorkflowExport-YAML-Equivalence-Cleanup@@Tests/WorkflowExport.wlt:97,1-102,2" 102 | ] -------------------------------------------------------------------------------- /Documentation/English/Tutorials/GitHubActions.nb: -------------------------------------------------------------------------------- 1 | Notebook[ 2 | { 3 | Cell[ 4 | TextData[ 5 | { 6 | "New in: ", 7 | Cell["", "HistoryData", CellTags -> "New"], 8 | " | Modified in: ", 9 | Cell[" ", "HistoryData", CellTags -> "Modified"], 10 | " | Obsolete in: ", 11 | Cell[" ", "HistoryData", CellTags -> "Obsolete"], 12 | " | Excised in: ", 13 | Cell[" ", "HistoryData", CellTags -> "Excised"] 14 | } 15 | ], 16 | "History", 17 | CellID -> 90967298 18 | ], 19 | Cell[ 20 | CellGroupData[ 21 | { 22 | Cell[ 23 | "Categorization", 24 | "CategorizationSection", 25 | CellID -> 512345580 26 | ], 27 | Cell[ 28 | "Tech Note", 29 | "Categorization", 30 | CellLabel -> "Entity Type", 31 | CellID -> 300688128 32 | ], 33 | Cell[ 34 | "Wolfram/PacletCICD", 35 | "Categorization", 36 | CellLabel -> "Paclet Name", 37 | CellID -> 49669172 38 | ], 39 | Cell[ 40 | "Wolfram`PacletCICD`", 41 | "Categorization", 42 | CellLabel -> "Context", 43 | CellID -> 418630995 44 | ], 45 | Cell[ 46 | "Wolfram/PacletCICD/tutorial/GitHubActions", 47 | "Categorization", 48 | CellLabel -> "URI", 49 | CellID -> 241695231 50 | ] 51 | }, 52 | Open 53 | ] 54 | ], 55 | Cell[ 56 | CellGroupData[ 57 | { 58 | Cell["Keywords", "KeywordsSection", CellID -> 156050553], 59 | Cell["XXXX", "Keywords", CellID -> 396122957] 60 | }, 61 | Closed 62 | ] 63 | ], 64 | Cell[ 65 | CellGroupData[ 66 | { 67 | Cell["GitHub Actions", "Title", CellID -> 59990617], 68 | Cell["XXXX", "Text", CellID -> 274407236], 69 | Cell[ 70 | BoxData[ 71 | GridBox[ 72 | { 73 | {"XXXX", Cell["XXXX", "TableText"]}, 74 | {"XXXX", Cell["XXXX", "TableText"]}, 75 | {"XXXX", Cell["XXXX", "TableText"]} 76 | } 77 | ] 78 | ], 79 | "DefinitionBox", 80 | CellID -> 256842662 81 | ], 82 | Cell["XXXX.", "Caption", CellID -> 833383687], 83 | Cell[ 84 | CellGroupData[ 85 | { 86 | Cell["XXXX", "MathCaption", CellID -> 87427267], 87 | Cell[ 88 | CellGroupData[ 89 | { 90 | Cell[ 91 | BoxData["XXXX"], 92 | "Input", 93 | CellLabel -> "In[1]:=", 94 | CellID -> 111952537 95 | ], 96 | Cell[ 97 | BoxData["XXXX"], 98 | "Output", 99 | CellLabel -> "Out[1]=", 100 | CellID -> 866100275 101 | ] 102 | }, 103 | Open 104 | ] 105 | ] 106 | }, 107 | Open 108 | ] 109 | ], 110 | Cell[ 111 | CellGroupData[ 112 | { 113 | Cell[ 114 | "Related Guides", 115 | "TutorialMoreAboutSection", 116 | CellID -> 115947552 117 | ], 118 | Cell["XXXX", "TutorialMoreAbout", CellID -> 273349287] 119 | }, 120 | Open 121 | ] 122 | ], 123 | Cell[ 124 | CellGroupData[ 125 | { 126 | Cell[ 127 | "Related Tech Notes", 128 | "RelatedTutorialsSection", 129 | CellID -> 51378728 130 | ], 131 | Cell["XXXX", "RelatedTutorials", CellID -> 724396369] 132 | }, 133 | Open 134 | ] 135 | ] 136 | }, 137 | Open 138 | ] 139 | ] 140 | }, 141 | ScreenStyleEnvironment -> "FutureObject", 142 | FrontEndVersion -> "13.0 for Microsoft Windows (64-bit) (December 2, 2021)", 143 | StyleDefinitions -> FrontEnd`FileName[ 144 | {"Wolfram"}, 145 | "TechNotePageStylesExt.nb", 146 | CharacterEncoding -> "UTF-8" 147 | ], 148 | ExpressionUUID -> "780fd82d-3c34-4369-8798-a405aaf1f349" 149 | ] -------------------------------------------------------------------------------- /.github/images/nb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/Units.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/Units.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/Units.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize-Paclet-Needs@@Tests/Units.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*Context Check*) 36 | VerificationTest[ 37 | Context @ BytesToQuantity, 38 | "Wolfram`PacletCICD`", 39 | TestID -> "BytesToQuantity-Context@@Tests/Units.wlt:36,1-40,2" 40 | ] 41 | 42 | VerificationTest[ 43 | Context @ SecondsToQuantity, 44 | "Wolfram`PacletCICD`", 45 | TestID -> "SecondsToQuantity-Context@@Tests/Units.wlt:42,1-46,2" 46 | ] 47 | 48 | (* ::**********************************************************************:: *) 49 | (* ::Section::Closed:: *) 50 | (*SecondsToQuantity*) 51 | VerificationTest[ 52 | SecondsToQuantity[ 12345 ], 53 | Quantity[ 54 | MixedMagnitude @ { 3, 25, 45 }, 55 | MixedUnit @ { "Hours", "Minutes", "Seconds" } 56 | ], 57 | TestID -> "SecondsToQuantity-1@@Tests/Units.wlt:51,1-58,2" 58 | ] 59 | 60 | VerificationTest[ 61 | q = Quantity[ 1.2346*^8, "Seconds" ], 62 | Quantity[ 1.2346*^8, "Seconds" ], 63 | TestID -> "SecondsToQuantity-2@@Tests/Units.wlt:60,1-64,2" 64 | ] 65 | 66 | VerificationTest[ 67 | SecondsToQuantity @ q, 68 | Quantity[ 69 | MixedMagnitude @ { 3, 10, 29.768518518518494 }, 70 | MixedUnit @ { "Years", "Months", "Days" } 71 | ], 72 | TestID -> "SecondsToQuantity-3@@Tests/Units.wlt:66,1-73,2" 73 | ] 74 | 75 | VerificationTest[ 76 | SecondsToQuantity @ f @ x, 77 | Quantity[ f @ x, "Seconds" ], 78 | TestID -> "SecondsToQuantity-4@@Tests/Units.wlt:75,1-79,2" 79 | ] 80 | 81 | VerificationTest[ 82 | SecondsToQuantity @ Quantity[ f @ x, "Minutes" ], 83 | Quantity[ 60 * f @ x, "Seconds" ], 84 | TestID -> "SecondsToQuantity-5@@Tests/Units.wlt:81,1-85,2" 85 | ] 86 | 87 | VerificationTest[ 88 | SecondsToQuantity[ 12346.0 ], 89 | Quantity[ 90 | MixedMagnitude @ { 3, 25, 46.00000000000037 }, 91 | MixedUnit @ { "Hours", "Minutes", "Seconds" } 92 | ], 93 | TestID -> "SecondsToQuantity-6@@Tests/Units.wlt:87,1-94,2" 94 | ] 95 | 96 | VerificationTest[ 97 | SecondsToQuantity[ 12346.0, "MixedUnits" -> False ], 98 | Quantity[ 3.4294444444444445, "Hours" ], 99 | TestID -> "SecondsToQuantity-7@@Tests/Units.wlt:96,1-100,2" 100 | ] 101 | 102 | VerificationTest[ 103 | SecondsToQuantity[ 6052.4 ], 104 | Quantity[ 105 | MixedMagnitude @ { 1, 40, 52.399999999999665 }, 106 | MixedUnit @ { "Hours", "Minutes", "Seconds" } 107 | ], 108 | TestID -> "SecondsToQuantity-8@@Tests/Units.wlt:102,1-109,2" 109 | ] 110 | 111 | VerificationTest[ 112 | SecondsToQuantity[ 12346.0, "MaxMixedUnits" -> 2 ], 113 | Quantity[ 114 | MixedMagnitude @ { 3, 25.76666666666667 }, 115 | MixedUnit @ { "Hours", "Minutes" } 116 | ], 117 | TestID -> "SecondsToQuantity-9@@Tests/Units.wlt:111,1-118,2" 118 | ] 119 | 120 | VerificationTest[ 121 | SecondsToQuantity[ 12346.0, "MaxMixedUnits" -> 1 ], 122 | SecondsToQuantity[ 12346.0, "MixedUnits" -> False ], 123 | TestID -> "SecondsToQuantity-10@@Tests/Units.wlt:120,1-124,2" 124 | ] 125 | 126 | VerificationTest[ 127 | SecondsToQuantity[ 12346.0, "MaxMixedUnits" -> invalid ], 128 | _, 129 | { SecondsToQuantity::mmu }, 130 | SameTest -> MatchQ, 131 | TestID -> "SecondsToQuantity-11@@Tests/Units.wlt:126,1-132,2" 132 | ] -------------------------------------------------------------------------------- /Tests/MXBuild.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/MXBuild.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/MXBuild.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize-Paclet-Needs@@Tests/MXBuild.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Section::Closed:: *) 35 | (*Setup*) 36 | VerificationTest[ 37 | BooleanQ @ Wolfram`PacletCICD`Internal`$MX, 38 | True, 39 | TestID -> "MX-Boolean@@Tests/MXBuild.wlt:36,1-40,2" 40 | ] 41 | 42 | VerificationTest[ 43 | IfMX[ f_ ][ actual_, expected_ ] := 44 | If[ TrueQ @ Wolfram`PacletCICD`Internal`$MX, 45 | f[ actual, expected ], 46 | True 47 | ], 48 | Null, 49 | TestID -> "Define-IfMX@@Tests/MXBuild.wlt:42,1-50,2" 50 | ] 51 | 52 | VerificationTest[ 53 | DirectoryQ @ Wolfram`PacletCICD`Private`$thisPacletDir, 54 | True, 55 | TestID -> "ThisPacletDirectory@@Tests/MXBuild.wlt:52,1-56,2" 56 | ] 57 | 58 | (* ::**********************************************************************:: *) 59 | (* ::Section::Closed:: *) 60 | (*LatestActionName*) 61 | VerificationTest[ 62 | ReplaceAll[ 63 | HoldComplete[ 64 | Wolfram`PacletCICD`Private`latestActionName[ 65 | "WolframResearch/build-paclet@latest", 66 | "WolframResearch", 67 | "build-paclet" 68 | ], 69 | Wolfram`PacletCICD`Private`latestActionName[ 70 | "WolframResearch/check-paclet@latest", 71 | "WolframResearch", 72 | "check-paclet" 73 | ], 74 | Wolfram`PacletCICD`Private`latestActionName[ 75 | "WolframResearch/submit-paclet@latest", 76 | "WolframResearch", 77 | "submit-paclet" 78 | ], 79 | Wolfram`PacletCICD`Private`latestActionName[ 80 | "WolframResearch/test-paclet@latest", 81 | "WolframResearch", 82 | "test-paclet" 83 | ] 84 | ], 85 | DownValues @ Wolfram`PacletCICD`Private`latestActionName 86 | ], 87 | HoldComplete[ _String, _String, _String, _String ], 88 | SameTest -> IfMX @ MatchQ, 89 | TestID -> "LatestActionName@@Tests/MXBuild.wlt:61,1-90,2" 90 | ] 91 | 92 | (* ::**********************************************************************:: *) 93 | (* ::Section::Closed:: *) 94 | (*WXFResource*) 95 | VerificationTest[ 96 | ReplaceAll[ 97 | HoldComplete[ 98 | Wolfram`PacletCICD`Private`wxfResource[ "DefaultIcon" ], 99 | Wolfram`PacletCICD`Private`wxfResource[ "GitHubIcon" ], 100 | Wolfram`PacletCICD`Private`wxfResource[ "PublisherTokenIcon" ], 101 | Wolfram`PacletCICD`Private`wxfResource[ "TerminalIcon" ] 102 | ], 103 | DownValues @ Wolfram`PacletCICD`Private`wxfResource 104 | ], 105 | HoldComplete[ _Graphics, _Graphics, _Graphics, _Graphics ], 106 | SameTest -> IfMX @ MatchQ, 107 | TestID -> "WXFResource@@Tests/MXBuild.wlt:95,1-108,2" 108 | ] 109 | 110 | (* ::**********************************************************************:: *) 111 | (* ::Section::Closed:: *) 112 | (*WLResource*) 113 | VerificationTest[ 114 | ReplaceAll[ 115 | HoldComplete @ Wolfram`PacletCICD`Private`wlResource[ 116 | "GitHubEnvironmentData" 117 | ], 118 | DownValues @ Wolfram`PacletCICD`Private`wlResource 119 | ], 120 | HoldComplete @ { { _String, _String }.. }, 121 | SameTest -> IfMX @ MatchQ, 122 | TestID -> "WLResource@@Tests/MXBuild.wlt:113,1-123,2" 123 | ] 124 | 125 | (* ::**********************************************************************:: *) 126 | (* ::Section::Closed:: *) 127 | (*ActionIcon*) 128 | 129 | (* TODO *) -------------------------------------------------------------------------------- /Kernel/GitHubPacletInstall.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | GitHubPacletInstall // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | (* ::**********************************************************************:: *) 11 | (* ::Section::Closed:: *) 12 | (*Messages*) 13 | GitHubPacletInstall::spec = 14 | "`1` is not a valid GitHub Paclet specification."; 15 | 16 | GitHubPacletInstall::ref = 17 | "Cannot determine a release for `1` using the ref `2`."; 18 | 19 | GitHubPacletInstall::unimpl = 20 | "Installing by `1` is not yet supported."; 21 | 22 | GitHubPacletInstall::versp = 23 | "`1` is not a valid version specification."; 24 | 25 | GitHubPacletInstall::tagsp = 26 | "`1` is not a valid tag specification."; 27 | 28 | GitHubPacletInstall::brsp = 29 | "`1` is not a valid branch specification."; 30 | 31 | GitHubPacletInstall::unknown = 32 | "An unexpected error occurred."; 33 | 34 | (* ::**********************************************************************:: *) 35 | (* ::Section::Closed:: *) 36 | (*Options*) 37 | GitHubPacletInstall // Options = { }; 38 | 39 | (* ::**********************************************************************:: *) 40 | (* ::Section::Closed:: *) 41 | (*Main definition*) 42 | GitHubPacletInstall[ spec_String? StringQ, opts: OptionsPattern[ ] ] := 43 | catchTop @ Module[ { assoc }, 44 | assoc = fromGitHubPacletSpec @ spec; 45 | If[ AssociationQ @ assoc, 46 | GitHubPacletInstall[ assoc, opts ], 47 | throwMessageFailure[ GitHubPacletInstall::spec, spec ] 48 | ] 49 | ]; 50 | 51 | GitHubPacletInstall[ as_Association? AssociationQ, opts: OptionsPattern[ ] ] := 52 | catchTop @ githubPacletInstall[ 53 | Lookup[ as, "User" ], 54 | Lookup[ as, "Repository" ], 55 | Lookup[ as, "Version" ], 56 | Lookup[ as, "Tag" ], 57 | Lookup[ as, "Branch" ] 58 | ]; 59 | 60 | (* ::**********************************************************************:: *) 61 | (* ::Section::Closed:: *) 62 | (*Error cases*) 63 | (* TODO *) 64 | 65 | (* ::**********************************************************************:: *) 66 | (* ::Section::Closed:: *) 67 | (*Dependencies*) 68 | 69 | $$maybeString = _String? StringQ | _Missing; 70 | 71 | (* ::**********************************************************************:: *) 72 | (* ::Subsection::Closed:: *) 73 | (*githubPacletInstall*) 74 | githubPacletInstall[ owner_String, repo_String, "Latest", tag_, branch_ ] := 75 | ghInstallLatest[ owner, repo, tag, branch ]; 76 | 77 | githubPacletInstall[ owner_String, repo_String, ver_String, tag_, branch_ ] := 78 | ghInstallVersion[ owner, repo, ver, tag, branch ]; 79 | 80 | (* Errors: *) 81 | githubPacletInstall[ owner_, repo_, ver_Missing, tag_String, branch_ ] := 82 | throwMessageFailure[ GitHubPacletInstall::unimpl, "tag" ]; 83 | 84 | githubPacletInstall[ owner_, repo_, ver_Missing, tag_Missing, branch_String ] := 85 | throwMessageFailure[ GitHubPacletInstall::unimpl, "branch" ]; 86 | 87 | githubPacletInstall[ owner_, repo_, ver: Except[ $$maybeString ], _, _ ] := 88 | throwMessageFailure[ GitHubPacletInstall::versp, ver ]; 89 | 90 | githubPacletInstall[ owner_, repo_, ver_, tag: Except[ $$maybeString ], _ ] := 91 | throwMessageFailure[ GitHubPacletInstall::tagsp, ver ]; 92 | 93 | githubPacletInstall[ owner_, repo_, ver_, tag_, br: Except[ $$maybeString ] ] := 94 | throwMessageFailure[ GitHubPacletInstall::brsp, br ]; 95 | 96 | (* ::**********************************************************************:: *) 97 | (* ::Subsection::Closed:: *) 98 | (*ghInstallLatest*) 99 | (* TODO *) 100 | ghInstallLatest[ ___ ] := throwMessageFailure[ GitHubPacletInstall::unknown ]; 101 | 102 | (* Use this URL: 103 | URLBuild @ <| 104 | "Scheme" -> "https", 105 | "Domain" -> "api.github.com", 106 | "Path" -> { "", "repos", owner, repo, "releases", "latest" } 107 | |> 108 | *) 109 | 110 | (* ::**********************************************************************:: *) 111 | (* ::Subsection::Closed:: *) 112 | (*ghInstallVersion*) 113 | (* TODO *) 114 | ghInstallVersion[ ___ ] := throwMessageFailure[ GitHubPacletInstall::unknown ]; 115 | 116 | (* ::**********************************************************************:: *) 117 | (* ::Subsection::Closed:: *) 118 | (*fromGitHubPacletSpec*) 119 | fromGitHubPacletSpec[ spec_String? StringQ ] := 120 | fromGitHubPacletSpec @ StringSplit[ spec, c: "/" | "@" :> c ]; 121 | 122 | fromGitHubPacletSpec[ { owner_, "/", repo_, "@", ref_ } ] := Enclose[ 123 | Module[ { assoc }, 124 | assoc = ConfirmBy[ makeSpecifierData @ ref, AssociationQ ]; 125 | Join[ <| "User" -> owner, "Repository" -> repo |>, assoc ] 126 | ], 127 | throwMessageFailure[ 128 | GitHubPacletInstall::ref, 129 | owner <> "/" <> repo, 130 | ref 131 | ] & 132 | ]; 133 | 134 | fromGitHubPacletSpec[ { owner_, "/", repo_ } ] := <| 135 | "User" -> owner, 136 | "Repository" -> repo, 137 | "Version" -> "Latest" 138 | |>; 139 | 140 | (* ::**********************************************************************:: *) 141 | (* ::Section::Closed:: *) 142 | (*Package Footer*) 143 | End[ ]; 144 | 145 | EndPackage[ ]; -------------------------------------------------------------------------------- /Kernel/FormatNotebooks.wl: -------------------------------------------------------------------------------- 1 | (* ::**************************************************************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | FormatNotebooks // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | (* ::**************************************************************************************************************:: *) 11 | (* ::Section::Closed:: *) 12 | (*FormatNotebooks*) 13 | FormatNotebooks[ dir_? DirectoryQ, opts: OptionsPattern[ ] ] := 14 | catchTop @ FormatNotebooks[ FileNames[ "*.nb", dir, Infinity ], opts ]; 15 | 16 | FormatNotebooks[ files_List, opts: OptionsPattern[ ] ] := 17 | catchTop @ Map[ FormatNotebooks[ #, opts ] &, files ]; 18 | 19 | FormatNotebooks[ file_? notebookFileQ, opts: OptionsPattern[ ] ] := 20 | catchTop @ makeReadable[ file, { opts } ]; 21 | 22 | (* ::**************************************************************************************************************:: *) 23 | (* ::Subsection::Closed:: *) 24 | (*notebookFileQ*) 25 | notebookFileQ[ file_? FileExistsQ ] := 26 | TrueQ @ Quiet @ Or[ 27 | ToLowerCase @ FileExtension @ file === "nb", 28 | ToLowerCase @ FileFormat @ file === "nb" 29 | ]; 30 | 31 | notebookFileQ[ ___ ] := False; 32 | 33 | (* ::**************************************************************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*makeReadable*) 36 | makeReadable[ file_, opts: { OptionsPattern[ ] } ] := makeReadable[ file, Hash @ ReadByteArray @ file, opts ]; 37 | 38 | makeReadable[ file_, hash_Integer, opts_ ] /; skipFormattingQ[ file, hash ] := 39 | Missing[ "Skipped", file ]; 40 | 41 | makeReadable[ file_, hash_Integer, opts: { OptionsPattern[ ] } ] := Enclose[ 42 | Module[ { res }, 43 | res = ConfirmBy[ makeReadable[ file, hash, $overrideFormats, opts ], 44 | FileExistsQ 45 | ]; 46 | saveHash @ file; 47 | res 48 | ], 49 | StringForm[ "Failed to format notebook: `1`", file ] 50 | ]; 51 | 52 | (* :!CodeAnalysis::BeginBlock:: *) 53 | (* :!CodeAnalysis::Disable::VariableError::Block:: *) 54 | makeReadable[ file_, hash_, HoldComplete[ overrides___ ], { opts: OptionsPattern[ ] } ] := 55 | Internal`InheritedBlock[ { RawArray, NumericArray, overrides }, 56 | Unprotect[ RawArray, NumericArray, overrides ]; 57 | ReleaseHold[ overrideFormat /@ HoldComplete @ overrides ]; 58 | 59 | Format[ x_RawArray? rawArrayQ, InputForm ] := 60 | OutputForm[ "CompressedData[\"" <> Compress @ x <> "\"]" ]; 61 | 62 | Format[ x_NumericArray? numericArrayQ, InputForm ] := 63 | OutputForm[ "CompressedData[\"" <> Compress @ x <> "\"]" ]; 64 | 65 | ResourceFunction[ "SaveReadableNotebook" ][ 66 | ReplaceAll[ 67 | DeleteCases[ 68 | Import[ file, "NB" ], 69 | TaggingRules -> { }, 70 | Infinity 71 | ], 72 | a: _RawArray | _NumericArray :> 73 | With[ { b = Check[ a, $Failed ] }, b /; ! FailureQ @ b ] 74 | ], 75 | file, 76 | opts 77 | ] 78 | ]; 79 | 80 | (* ::**************************************************************************************************************:: *) 81 | (* ::Subsection::Closed:: *) 82 | (*$overrideFormats*) 83 | (* :!CodeAnalysis::BeginBlock:: *) 84 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 85 | $overrideFormats = HoldComplete[ 86 | Alternatives, 87 | Apply, 88 | Composition, 89 | Decrement, 90 | Increment, 91 | Map, 92 | MessageName, 93 | Not, 94 | Out, 95 | Part, 96 | Pattern, 97 | PatternTest, 98 | PreDecrement, 99 | PreIncrement, 100 | RightComposition 101 | ]; 102 | 103 | (* :!CodeAnalysis::EndBlock:: *) 104 | 105 | (* ::**************************************************************************************************************:: *) 106 | (* ::Subsection::Closed:: *) 107 | (*formatHashID*) 108 | formatHashID[ id_ ] := 109 | StringRiffle[ 110 | { "Wolfram", "PacletCICD", "FormatNotebooks", "FileHashes", id }, 111 | "/" 112 | ]; 113 | 114 | (* ::**************************************************************************************************************:: *) 115 | (* ::Subsection::Closed:: *) 116 | (*skipFormattingQ*) 117 | skipFormattingQ[ file_, hash_Integer ] := 118 | skipFormattingQ[ file, hash, Hash[ file, "Expression", "HexString" ] ]; 119 | 120 | skipFormattingQ[ file_, hash_Integer, id_String ] := 121 | PersistentSymbol @ formatHashID @ id === hash; 122 | 123 | (* ::**************************************************************************************************************:: *) 124 | (* ::Subsection::Closed:: *) 125 | (*saveHash*) 126 | saveHash[ file_ ] := saveHash[ file, Hash @ ReadByteArray @ file ]; 127 | 128 | saveHash[ file_, hash_Integer ] := 129 | saveHash[ file, hash, Hash[ file, "Expression", "HexString" ] ]; 130 | 131 | saveHash[ file_, hash_Integer, id_String ] := 132 | Set[ PersistentSymbol[ formatHashID @ id ], hash ]; 133 | 134 | (* ::**************************************************************************************************************:: *) 135 | (* ::Subsection::Closed:: *) 136 | (*rawArrayQ*) 137 | rawArrayQ // Attributes = { HoldFirst }; 138 | rawArrayQ[ arr_RawArray ] := Developer`RawArrayQ @ Unevaluated @ arr; 139 | 140 | (* ::**************************************************************************************************************:: *) 141 | (* ::Subsection::Closed:: *) 142 | (*numericArrayQ*) 143 | numericArrayQ // Attributes = { HoldFirst }; 144 | numericArrayQ[ arr_RawArray ] := NumericArrayQ @ Unevaluated @ arr; 145 | 146 | (* ::**************************************************************************************************************:: *) 147 | (* ::Subsection::Closed:: *) 148 | (*overrideFormat*) 149 | overrideFormat // Attributes = { HoldAllComplete }; 150 | 151 | overrideFormat[ sym_Symbol ] := ( 152 | Unprotect @ sym; 153 | Format[ x_sym, InputForm ] := 154 | OutputForm @ ToString @ Unevaluated @ FullForm @ x 155 | ); 156 | 157 | (* ::**************************************************************************************************************:: *) 158 | (* ::Section::Closed:: *) 159 | (*Package Footer*) 160 | End[ ]; 161 | 162 | EndPackage[ ]; -------------------------------------------------------------------------------- /Tests/TestPaclet.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/TestPaclet.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/TestPaclet.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize-Paclet-Needs@@Tests/TestPaclet.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*Context Check*) 36 | VerificationTest[ 37 | Context @ TestPaclet, 38 | "Wolfram`PacletCICD`", 39 | TestID -> "TestPaclet-Context@@Tests/TestPaclet.wlt:36,1-40,2" 40 | ] 41 | 42 | VerificationTest[ 43 | Context @ ResetExampleDirectory, 44 | "Wolfram`PacletCICD`", 45 | TestID -> "Context-ResetExampleDirectory@@Tests/TestPaclet.wlt:42,1-46,2" 46 | ] 47 | 48 | (* ::**********************************************************************:: *) 49 | (* ::Subsection::Closed:: *) 50 | (*ResetExampleDirectory*) 51 | VerificationTest[ 52 | ResetExampleDirectory @ All, 53 | { __Success }, 54 | SameTest -> MatchQ, 55 | TestID -> "ResetExampleDirectory-Initialization@@Tests/TestPaclet.wlt:51,1-56,2" 56 | ] 57 | 58 | (* ::**********************************************************************:: *) 59 | (* ::Subsection::Closed:: *) 60 | (*Definitions*) 61 | VerificationTest[ 62 | $PublisherID; 63 | $PublisherID = "SamplePublisher", 64 | "SamplePublisher", 65 | TestID -> "SetPublisherID@@Tests/TestPaclet.wlt:61,1-66,2" 66 | ] 67 | 68 | VerificationTest[ 69 | DefinitionNotebookClient`ConsolePrint; 70 | suppressConsole // Attributes = { HoldFirst }; 71 | suppressConsole[ eval_ ] := 72 | Block[ 73 | { 74 | DefinitionNotebookClient`ConsolePrint, 75 | Wolfram`PacletCICD`Private`setOutput, 76 | Wolfram`PacletCICD`Private`appendStepSummary 77 | }, 78 | DefinitionNotebookClient`ConsolePrint // Options = { 79 | "Output" -> None 80 | }; 81 | Wolfram`PacletCICD`Private`noExit @ eval 82 | ], 83 | Null, 84 | TestID -> "SuppressConsole-Definition@@Tests/TestPaclet.wlt:68,1-85,2" 85 | ] 86 | 87 | (* ::**********************************************************************:: *) 88 | (* ::Section::Closed:: *) 89 | (*TestPaclet*) 90 | 91 | (* ::**********************************************************************:: *) 92 | (* ::Subsection::Closed:: *) 93 | (*Sample*) 94 | VerificationTest[ 95 | FileNames[ 96 | "*.paclet", 97 | FileNameJoin @ { First @ ExampleDirectory[ "Sample" ], "build" } 98 | ], 99 | { }, 100 | SameTest -> MatchQ, 101 | TestID -> "Empty-Build-Directory-Sample@@Tests/TestPaclet.wlt:94,1-102,2" 102 | ] 103 | 104 | VerificationTest[ 105 | result = suppressConsole @ TestPaclet[ 106 | ExampleDirectory[ "Sample" ], 107 | "AnnotateTestIDs" -> False, 108 | "SetWorkflowValue" -> False 109 | ], 110 | _Success, 111 | SameTest -> MatchQ, 112 | TestID -> "TestPaclet-Sample@@Tests/TestPaclet.wlt:104,1-113,2" 113 | ] 114 | 115 | VerificationTest[ 116 | reports = result[ "Result" ], 117 | _Association? (AllTrue @ MatchQ[ _TestReportObject ]), 118 | SameTest -> MatchQ, 119 | TestID -> "TestPaclet-Report-Sample@@Tests/TestPaclet.wlt:115,1-120,2" 120 | ] 121 | 122 | VerificationTest[ 123 | AllTrue[ reports, #[ "AllTestsSucceeded" ] & ], 124 | True, 125 | TestID -> "TestPaclet-Passed-Sample@@Tests/TestPaclet.wlt:122,1-126,2" 126 | ] 127 | 128 | VerificationTest[ 129 | Positive @ Total[ #[ "TestsSucceededCount" ] & /@ reports ], 130 | True, 131 | TestID -> "TestPaclet-Pass-Count-Sample@@Tests/TestPaclet.wlt:128,1-132,2" 132 | ] 133 | 134 | (* ::**********************************************************************:: *) 135 | (* ::Subsection::Closed:: *) 136 | (*FewIssues*) 137 | VerificationTest[ 138 | FileNames[ 139 | "*.paclet", 140 | FileNameJoin @ { First @ ExampleDirectory[ "FewIssues" ], "build" } 141 | ], 142 | { }, 143 | SameTest -> MatchQ, 144 | TestID -> "Empty-Build-Directory-FewIssues@@Tests/TestPaclet.wlt:137,1-145,2" 145 | ] 146 | 147 | VerificationTest[ 148 | result = suppressConsole @ TestPaclet[ 149 | ExampleDirectory[ "FewIssues" ], 150 | "AnnotateTestIDs" -> False, 151 | "SetWorkflowValue" -> False 152 | ], 153 | _Failure, 154 | { TestPaclet::Failures }, 155 | SameTest -> MatchQ, 156 | TestID -> "TestPaclet-FewIssues@@Tests/TestPaclet.wlt:147,1-157,2" 157 | ] 158 | 159 | VerificationTest[ 160 | reports = result[ "Result" ], 161 | _Association? (AllTrue @ MatchQ[ _TestReportObject ]), 162 | SameTest -> MatchQ, 163 | TestID -> "TestPaclet-Report-FewIssues@@Tests/TestPaclet.wlt:159,1-164,2" 164 | ] 165 | 166 | VerificationTest[ 167 | AllTrue[ reports, #[ "AllTestsSucceeded" ] & ], 168 | False, 169 | TestID -> "TestPaclet-Passed-FewIssues@@Tests/TestPaclet.wlt:166,1-170,2" 170 | ] 171 | 172 | VerificationTest[ 173 | Positive @ Total[ #[ "TestsSucceededCount" ] & /@ reports ], 174 | True, 175 | TestID -> "TestPaclet-Pass-Count-FewIssues@@Tests/TestPaclet.wlt:172,1-176,2" 176 | ] 177 | 178 | VerificationTest[ 179 | Positive @ Total[ #[ "TestsFailedCount" ] & /@ reports ], 180 | True, 181 | TestID -> "TestPaclet-Fail-Count-FewIssues@@Tests/TestPaclet.wlt:178,1-182,2" 182 | ] 183 | 184 | VerificationTest[ 185 | Union @ Cases[ 186 | #[ "TestsFailed" ] & /@ reports, 187 | tro_TestResultObject :> tro[ "TestID" ], 188 | Infinity 189 | ], 190 | { "Addition", "AddOne-2" }, 191 | TestID -> "TestPaclet-Failed-TestIDs-FewIssues@@Tests/TestPaclet.wlt:184,1-192,2" 192 | ] 193 | 194 | (* ::**********************************************************************:: *) 195 | (* ::Section::Closed:: *) 196 | (*Cleanup*) 197 | VerificationTest[ 198 | ResetExampleDirectory @ All, 199 | { __Success }, 200 | SameTest -> MatchQ, 201 | TestID -> "ResetExampleDirectory-Cleanup@@Tests/TestPaclet.wlt:197,1-202,2" 202 | ] -------------------------------------------------------------------------------- /Tests/ExampleDirectory.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/ExampleDirectory.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/ExampleDirectory.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize@@Tests/ExampleDirectory.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*Context Check*) 36 | VerificationTest[ 37 | Context @ $ExamplesLocation, 38 | "Wolfram`PacletCICD`", 39 | TestID -> "$ExamplesLocation-Context@@Tests/ExampleDirectory.wlt:36,1-40,2" 40 | ] 41 | 42 | VerificationTest[ 43 | Context @ ExampleDirectory, 44 | "Wolfram`PacletCICD`", 45 | TestID -> "ExampleDirectory-Context@@Tests/ExampleDirectory.wlt:42,1-46,2" 46 | ] 47 | 48 | (* ::**********************************************************************:: *) 49 | (* ::Section::Closed:: *) 50 | (*$ExamplesLocation*) 51 | VerificationTest[ 52 | DeleteDirectory[ 53 | $ExamplesLocation, 54 | DeleteContents -> True 55 | ], 56 | Null, 57 | TestID -> "$ExamplesLocation-Delete@@Tests/ExampleDirectory.wlt:51,1-58,2" 58 | ] 59 | 60 | VerificationTest[ 61 | DirectoryQ[ exDir = ExampleDirectory[ "Sample" ] ], 62 | True, 63 | TestID -> "$ExamplesLocation-Create@@Tests/ExampleDirectory.wlt:60,1-64,2" 64 | ] 65 | 66 | VerificationTest[ 67 | ResetExampleDirectory @ All, 68 | { __Success }, 69 | SameTest -> MatchQ, 70 | TestID -> "ResetExampleDirectory-All@@Tests/ExampleDirectory.wlt:66,1-71,2" 71 | ] 72 | 73 | VerificationTest[ 74 | DirectoryQ @ exDir, 75 | False, 76 | TestID -> "ResetExampleDirectory-Verified@@Tests/ExampleDirectory.wlt:73,1-77,2" 77 | ] 78 | 79 | (* ::**********************************************************************:: *) 80 | (* ::Section::Closed:: *) 81 | (*ExampleDirectory*) 82 | VerificationTest[ 83 | DeleteDirectory[ ExampleDirectory[ "FewIssues" ], DeleteContents -> True ], 84 | Null, 85 | TestID -> "ExampleDirectory-FewIssues-Delete@@Tests/ExampleDirectory.wlt:82,1-86,2" 86 | ] 87 | 88 | VerificationTest[ 89 | DeleteDirectory[ ExampleDirectory[ "MoreIssues" ], DeleteContents -> True ], 90 | Null, 91 | TestID -> "ExampleDirectory-MoreIssues-Delete@@Tests/ExampleDirectory.wlt:88,1-92,2" 92 | ] 93 | 94 | VerificationTest[ 95 | DirectoryQ @ ExampleDirectory[ "FewIssues" ], 96 | True, 97 | TestID -> "ExampleDirectory-FewIssues-Create@@Tests/ExampleDirectory.wlt:94,1-98,2" 98 | ] 99 | 100 | VerificationTest[ 101 | DirectoryQ @ ExampleDirectory[ "MoreIssues" ], 102 | True, 103 | TestID -> "ExampleDirectory-MoreIssues-Create@@Tests/ExampleDirectory.wlt:100,1-104,2" 104 | ] 105 | 106 | VerificationTest[ 107 | DirectoryQ @ ExampleDirectory[ "Sample" ], 108 | True, 109 | TestID -> "ExampleDirectory-Sample-Create@@Tests/ExampleDirectory.wlt:106,1-110,2" 110 | ] 111 | 112 | VerificationTest[ 113 | DirectoryQ @ ExampleDirectory[ "AdvancedSample" ], 114 | True, 115 | TestID -> "ExampleDirectory-AdvancedSample-Create@@Tests/ExampleDirectory.wlt:112,1-116,2" 116 | ] 117 | 118 | VerificationTest[ 119 | FileNameTake /@ FileNames[ All, ExampleDirectory[ "FewIssues" ] ], 120 | { 121 | "Documentation", 122 | "Kernel", 123 | "LICENSE", 124 | "PacletInfo.wl", 125 | "README.md", 126 | "ResourceDefinition.nb", 127 | "Tests" 128 | }, 129 | TestID -> "ExampleDirectory-FewIssues-Files@@Tests/ExampleDirectory.wlt:118,1-130,2" 130 | ] 131 | 132 | VerificationTest[ 133 | FileNameTake /@ FileNames[ All, ExampleDirectory[ "MoreIssues" ] ], 134 | { 135 | "Documentation", 136 | "Kernel", 137 | "LICENSE", 138 | "PacletInfo.wl", 139 | "README.md", 140 | "ResourceDefinition.nb" 141 | }, 142 | TestID -> "ExampleDirectory-MoreIssues-Files@@Tests/ExampleDirectory.wlt:132,1-143,2" 143 | ] 144 | 145 | VerificationTest[ 146 | FileNameTake /@ FileNames[ All, ExampleDirectory[ "Sample" ] ], 147 | { 148 | "Documentation", 149 | "Kernel", 150 | "LICENSE", 151 | "PacletInfo.wl", 152 | "README.md", 153 | "ResourceDefinition.nb", 154 | "Tests" 155 | }, 156 | TestID -> "ExampleDirectory-Sample-Files@@Tests/ExampleDirectory.wlt:145,1-157,2" 157 | ] 158 | 159 | VerificationTest[ 160 | FileNameTake /@ FileNames[ All, ExampleDirectory[ "AdvancedSample" ] ], 161 | { 162 | "Documentation", 163 | "Kernel", 164 | "LICENSE", 165 | "PacletInfo.wl", 166 | "README.md", 167 | "ResourceDefinition.nb", 168 | "Scripts", 169 | "Source", 170 | "Tests" 171 | }, 172 | TestID -> "ExampleDirectory-AdvancedSample-Files@@Tests/ExampleDirectory.wlt:159,1-173,2" 173 | ] 174 | 175 | (* ::**********************************************************************:: *) 176 | (* ::Subsection::Closed:: *) 177 | (*Regression Tests*) 178 | 179 | (* ::**********************************************************************:: *) 180 | (* ::Subsubsection::Closed:: *) 181 | (*Missing files*) 182 | VerificationTest[ 183 | dir = ExampleDirectory[ "Sample" ], 184 | _File? DirectoryQ, 185 | SameTest -> MatchQ, 186 | TestID -> "MissingFile-Get-Sample@@Tests/ExampleDirectory.wlt:182,1-187,2" 187 | ] 188 | 189 | VerificationTest[ 190 | file = FileNameJoin @ { First @ dir, "PacletInfo.wl" }, 191 | _? FileExistsQ, 192 | SameTest -> MatchQ, 193 | TestID -> "MissingFile-Get-PacletInfo@@Tests/ExampleDirectory.wlt:189,1-194,2" 194 | ] 195 | 196 | VerificationTest[ 197 | hash = FileHash @ file, 198 | _? IntegerQ, 199 | SameTest -> MatchQ, 200 | TestID -> "MissingFile-Get-FileHash@@Tests/ExampleDirectory.wlt:196,1-201,2" 201 | ] 202 | 203 | VerificationTest[ 204 | DeleteFile @ file; FileExistsQ @ file, 205 | False, 206 | TestID -> "MissingFile-DeleteFile@@Tests/ExampleDirectory.wlt:203,1-207,2" 207 | ] 208 | 209 | VerificationTest[ 210 | ExampleDirectory[ "Sample" ], 211 | _File? DirectoryQ, 212 | SameTest -> MatchQ, 213 | TestID -> "MissingFile-Restore-Directory@@Tests/ExampleDirectory.wlt:209,1-214,2" 214 | ] 215 | 216 | VerificationTest[ 217 | FileExistsQ @ file && FileHash @ file === hash, 218 | True, 219 | TestID -> "MissingFile-Restore-File@@Tests/ExampleDirectory.wlt:216,1-220,2" 220 | ] 221 | -------------------------------------------------------------------------------- /Tools/GenerateBannerImage.wl: -------------------------------------------------------------------------------- 1 | $pacDir = DirectoryName[ $InputFileName, 2 ]; 2 | 3 | $icon = Graphics[ 4 | { 5 | Thickness[ 0.016667 ], 6 | { 7 | FaceForm @ { RGBColor[ 1.0, 0.41569, 0.058824 ], Opacity[ 1.0 ] }, 8 | FilledCurve[ 9 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 10 | { 11 | { 12 | { 4.0, 30.63 }, 13 | { 4.0, 43.68 }, 14 | { 15.31, 37.16 }, 15 | { 15.31, 24.1 }, 16 | { 4.0, 30.63 } 17 | } 18 | } 19 | ] 20 | }, 21 | { 22 | FaceForm @ { RGBColor[ 1.0, 0.41569, 0.058824 ], Opacity[ 1.0 ] }, 23 | FilledCurve[ 24 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 25 | { 26 | { 27 | { 42.43, 7.14 }, 28 | { 31.13, 0.62 }, 29 | { 31.13, 13.67 }, 30 | { 42.43, 20.19 }, 31 | { 42.43, 7.14 } 32 | } 33 | } 34 | ] 35 | }, 36 | { 37 | FaceForm @ { RGBColor[ 1.0, 0.41569, 0.058824 ], Opacity[ 1.0 ] }, 38 | FilledCurve[ 39 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 40 | { 41 | { 42 | { 43.56, 52.17 }, 43 | { 54.86, 45.64 }, 44 | { 43.56, 39.11 }, 45 | { 32.26, 45.64 }, 46 | { 43.56, 52.17 } 47 | } 48 | } 49 | ] 50 | }, 51 | { 52 | FaceForm @ { RGBColor[ 0.98039, 0.66274, 0.25098 ], Opacity[ 1.0 ] }, 53 | FilledCurve[ 54 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 55 | { 56 | { 57 | { 4.0, 14.97 }, 58 | { 4.0, 28.02 }, 59 | { 28.87, 13.67 }, 60 | { 28.87, 0.62 }, 61 | { 4.0, 14.97 } 62 | } 63 | } 64 | ] 65 | }, 66 | { 67 | FaceForm @ { RGBColor[ 0.98039, 0.66274, 0.25098 ], Opacity[ 1.0 ] }, 68 | FilledCurve[ 69 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 70 | { 71 | { 72 | { 30.0, 60.0 }, 73 | { 41.3, 53.47 }, 74 | { 16.44, 39.11 }, 75 | { 5.14, 45.64 }, 76 | { 30.0, 60.0 } 77 | } 78 | } 79 | ] 80 | }, 81 | { 82 | FaceForm @ { RGBColor[ 0.98039, 0.66274, 0.25098 ], Opacity[ 1.0 ] }, 83 | FilledCurve[ 84 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 85 | { 86 | { 87 | { 56.0, 14.97 }, 88 | { 44.7, 8.44 }, 89 | { 44.69, 37.16 }, 90 | { 55.99, 43.68 }, 91 | { 56.0, 14.97 } 92 | } 93 | } 94 | ] 95 | }, 96 | { 97 | FaceForm @ { RGBColor[ 0.67059, 0.21176, 0.14902 ], Opacity[ 1.0 ] }, 98 | FilledCurve[ 99 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 100 | { 101 | { 102 | { 17.57, 22.8 }, 103 | { 17.57, 35.85 }, 104 | { 28.87, 29.33 }, 105 | { 28.87, 16.28 }, 106 | { 17.57, 22.8 } 107 | } 108 | } 109 | ] 110 | }, 111 | { 112 | FaceForm @ { RGBColor[ 0.67059, 0.21176, 0.14902 ], Opacity[ 1.0 ] }, 113 | FilledCurve[ 114 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 115 | { 116 | { 117 | { 30.0, 44.33 }, 118 | { 41.3, 37.8 }, 119 | { 30.0, 31.28 }, 120 | { 18.7, 37.8 }, 121 | { 30.0, 44.33 } 122 | } 123 | } 124 | ] 125 | }, 126 | { 127 | FaceForm @ { RGBColor[ 0.67059, 0.21176, 0.14902 ], Opacity[ 1.0 ] }, 128 | FilledCurve[ 129 | { { { 0, 2, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 } } }, 130 | { 131 | { 132 | { 42.43, 22.8 }, 133 | { 31.13, 16.28 }, 134 | { 31.13, 29.33 }, 135 | { 42.43, 35.85 }, 136 | { 42.43, 22.8 } 137 | } 138 | } 139 | ] 140 | } 141 | }, 142 | AspectRatio -> Automatic, 143 | ImageSize -> { 60.0, 60.0 }, 144 | PlotRange -> { { 0.0, 60.0 }, { 0.0, 60.0 } } 145 | ]; 146 | 147 | $banner = 148 | Framed[ 149 | Grid[ 150 | { 151 | { 152 | Graphics[ 153 | First @ $icon, 154 | PlotRange -> { { 0, 60 }, { 0, 60 } }, 155 | ImageSize -> 65, 156 | BaselinePosition -> Scaled[ 0.4 ] 157 | ], 158 | Column @ { 159 | Style[ 160 | "Paclet CI/CD", 161 | (* FontColor -> RGBColor[ "#ff6a0f" ], *) 162 | FontColor -> RGBColor[ "#ab3626" ], 163 | FontFamily -> "Source Sans Pro", 164 | FontWeight -> "Heavy", 165 | FontSize -> 54, 166 | FontTracking -> "Plain" 167 | ], 168 | Style[ 169 | " Continuous integration and deployment for Wolfram Language Paclets", 170 | FontSize -> 10.5, 171 | FontColor -> GrayLevel[ 0.5 ], 172 | FontFamily -> "Roboto", 173 | FontTracking -> "Condensed" 174 | ] 175 | } 176 | } 177 | }, 178 | Alignment -> { Automatic, Baseline }, 179 | Spacings -> 0.5 180 | ], 181 | Background -> White, 182 | FrameStyle -> None, 183 | RoundingRadius -> 12, 184 | FrameMargins -> { { 8, 12 }, { 6, 8 } } 185 | ]; 186 | 187 | $bannerImage = 188 | Rasterize[ 189 | $banner, 190 | ImageResolution -> 144, 191 | RasterSize -> { Automatic, 250 }, 192 | Background -> None 193 | ]; 194 | 195 | Export[ 196 | FileNameJoin @ { $pacDir, "Images", "Banner.png" }, 197 | $bannerImage, 198 | "PNG", 199 | ImageSize -> { Automatic, 250 } 200 | ] 201 | -------------------------------------------------------------------------------- /Resources/GitHubEnvironmentData.wl: -------------------------------------------------------------------------------- 1 | { 2 | { 3 | "CI", 4 | "Always set to true." 5 | }, 6 | { 7 | "GITHUB_ACTION", 8 | "The name of the action currently running, or the id of a step. For example, for an action, __repo-owner_name-of-action-repo . GitHub removes special characters, and uses the name __run when the current step runs a script without an id . If you use the same script or action more than once in the same job, the name will include a suffix that consists of the sequence number preceded by an underscore. For example, the first script you run will have the name __run , and the second script will be named __run_2 . Similarly, the second invocation of actions/checkout will be actionscheckout2 ." 9 | }, 10 | { 11 | "GITHUB_ACTION_PATH", 12 | "The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action. For example, /home/runner/work/_actions/repo-owner/name-of-action-repo/v1 ." 13 | }, 14 | { 15 | "GITHUB_ACTION_REPOSITORY", 16 | "For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout ." 17 | }, 18 | { 19 | "GITHUB_ACTIONS", 20 | "Always set to true when GitHub Actions is running the workflow. You can use this variable to differentiate when tests are being run locally or by GitHub Actions." 21 | }, 22 | { 23 | "GITHUB_ACTOR", 24 | "The name of the person or app that initiated the workflow. For example, octocat ." 25 | }, 26 | { 27 | "GITHUB_API_URL", 28 | "Returns the API URL. For example: https://api.github.com ." 29 | }, 30 | { 31 | "GITHUB_BASE_REF", 32 | "The name of the base ref or target branch of the pull request in a workflow run. This is only set when the event that triggers a workflow run is either pull_request or pull_request_target . For example, main ." 33 | }, 34 | { 35 | "GITHUB_ENV", 36 | "The path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and changes for each step in a job. For example, /home/runner/work/_temp/_runner_file_commands/set_env_87406d6e-4979-4d42-98e1-3dab1f48b13a . For more information, see \" Workflow commands for GitHub Actions .\"" 37 | }, 38 | { 39 | "GITHUB_EVENT_NAME", 40 | "The name of the event that triggered the workflow. For example, workflow_dispatch ." 41 | }, 42 | { 43 | "GITHUB_EVENT_PATH", 44 | "The path to the file on the runner that contains the full event webhook payload. For example, /github/workflow/event.json ." 45 | }, 46 | { 47 | "GITHUB_GRAPHQL_URL", 48 | "Returns the GraphQL API URL. For example: https://api.github.com/graphql ." 49 | }, 50 | { 51 | "GITHUB_HEAD_REF", 52 | "The head ref or source branch of the pull request in a workflow run. This property is only set when the event that triggers a workflow run is either pull_request or pull_request_target . For example, feature-branch-1 ." 53 | }, 54 | { 55 | "GITHUB_JOB", 56 | "The job_id of the current job. For example, greeting_job ." 57 | }, 58 | { 59 | "GITHUB_PATH", 60 | "The path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and changes for each step in a job. For example, /home/runner/work/_temp/_runner_file_commands/add_path_899b9445-ad4a-400c-aa89-249f18632cf5 . For more information, see \" Workflow commands for GitHub Actions .\"" 61 | }, 62 | { 63 | "GITHUB_REF", 64 | "The branch or tag ref that triggered the workflow run. For branches this is the format refs/heads/ , for tags it is refs/tags/ , and for pull requests it is refs/pull//merge . This variable is only set if a branch or tag is available for the event type. For example, refs/heads/feature-branch-1 ." 65 | }, 66 | { 67 | "GITHUB_REF_NAME", 68 | "The branch or tag name that triggered the workflow run. For example, feature-branch-1 ." 69 | }, 70 | { 71 | "GITHUB_REF_PROTECTED", 72 | "true if branch protections are configured for the ref that triggered the workflow run." 73 | }, 74 | { 75 | "GITHUB_REF_TYPE", 76 | "The type of ref that triggered the workflow run. Valid values are branch or tag ." 77 | }, 78 | { 79 | "GITHUB_REPOSITORY", 80 | "The owner and repository name. For example, octocat/Hello-World ." 81 | }, 82 | { 83 | "GITHUB_REPOSITORY_OWNER", 84 | "The repository owner's name. For example, octocat ." 85 | }, 86 | { 87 | "GITHUB_RETENTION_DAYS", 88 | "The number of days that workflow run logs and artifacts are kept. For example, 90 ." 89 | }, 90 | { 91 | "GITHUB_RUN_ATTEMPT", 92 | "A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run. For example, 3 ." 93 | }, 94 | { 95 | "GITHUB_RUN_ID", 96 | "A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run. For example, 1658821493 ." 97 | }, 98 | { 99 | "GITHUB_RUN_NUMBER", 100 | "A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run. For example, 3 ." 101 | }, 102 | { 103 | "GITHUB_SERVER_URL", 104 | "The URL of the GitHub server. For example: https://github.com ." 105 | }, 106 | { 107 | "GITHUB_SHA", 108 | "The commit SHA that triggered the workflow. For example, ffac537e6cbbf934b08745a378932722df287a53 ." 109 | }, 110 | { 111 | "GITHUB_WORKFLOW", 112 | "The name of the workflow. For example, My test workflow . If the workflow file doesn't specify a name , the value of this variable is the full path of the workflow file in the repository." 113 | }, 114 | { 115 | "GITHUB_WORKSPACE", 116 | "The default working directory on the runner for steps, and the default location of your repository when using the checkout action. For example, /home/runner/work/my-repo-name/my-repo-name ." 117 | }, 118 | { 119 | "RUNNER_ARCH", 120 | "The architecture of the runner executing the job. Possible values are X86 , X64 , ARM , or ARM64 ." 121 | }, 122 | { 123 | "RUNNER_NAME", 124 | "The name of the runner executing the job. For example, Hosted Agent" 125 | }, 126 | { 127 | "RUNNER_OS", 128 | "The operating system of the runner executing the job. Possible values are Linux , Windows , or macOS . For example, Windows" 129 | }, 130 | { 131 | "RUNNER_TEMP", 132 | "The path to a temporary directory on the runner. This directory is emptied at the beginning and end of each job. Note that files will not be removed if the runner's user account does not have permission to delete them. For example, D:\\a\\_temp" 133 | }, 134 | { 135 | "RUNNER_TOOL_CACHE", 136 | "The path to the directory containing preinstalled tools for GitHub-hosted runners. For more information, see \" About GitHub-hosted runners \". For example, C:\\hostedtoolcache\\windows" 137 | } 138 | } -------------------------------------------------------------------------------- /Tests/ErrorHandling.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/ErrorHandling.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/ErrorHandling.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ]; 29 | PacletCICD; 30 | MessageFailure; 31 | , 32 | Null, 33 | TestID -> "Initialize@@Tests/ErrorHandling.wlt:27,1-34,2" 34 | ] 35 | 36 | (* ::**********************************************************************:: *) 37 | (* ::Subsection::Closed:: *) 38 | (*Context Check*) 39 | VerificationTest[ 40 | Context @ PacletCICD, 41 | "Wolfram`PacletCICD`", 42 | TestID -> "PacletCICD-Context@@Tests/ErrorHandling.wlt:39,1-43,2" 43 | ] 44 | 45 | VerificationTest[ 46 | Context @ MessageFailure, 47 | "Wolfram`PacletCICD`", 48 | TestID -> "MessageFailure-Context@@Tests/ErrorHandling.wlt:45,1-49,2" 49 | ] 50 | 51 | (* ::**********************************************************************:: *) 52 | (* ::Section::Closed:: *) 53 | (*catchUndefined*) 54 | VerificationTest[ 55 | ExampleDirectory; 56 | Wolfram`PacletCICD`Private`tryFetchExampleData[ 1, 2, 3, 4 ], 57 | Failure[ 58 | "PacletCICD::undefined", 59 | <| 60 | "MessageParameters" :> { 61 | HoldForm @ Wolfram`PacletCICD`Private`tryFetchExampleData, 62 | HoldForm @ Wolfram`PacletCICD`Private`tryFetchExampleData[ 63 | 1, 64 | 2, 65 | 3, 66 | 4 67 | ], 68 | DownValues 69 | }, 70 | "MessageTemplate" :> PacletCICD::undefined 71 | |> 72 | ], 73 | { PacletCICD::undefined }, 74 | TestID -> "catchUndefined@@Tests/ErrorHandling.wlt:54,1-75,2" 75 | ] 76 | 77 | (* ::**********************************************************************:: *) 78 | (* ::Section::Closed:: *) 79 | (*throwError*) 80 | VerificationTest[ 81 | Wolfram`PacletCICD`Private`throwError[ "Error message: `1`", 1, 2, 3 ], 82 | Failure[ 83 | "PacletCICD::error", 84 | <| 85 | "MessageParameters" :> { "Error message: 1", 1, 2, 3 }, 86 | "MessageTemplate" :> PacletCICD::error 87 | |> 88 | ], 89 | { PacletCICD::error }, 90 | TestID -> "throwError@@Tests/ErrorHandling.wlt:80,1-91,2" 91 | ] 92 | 93 | VerificationTest[ 94 | Wolfram`PacletCICD`Private`throwError[ 95 | "Error message: `expr`", 96 | <| "expr" -> { 1, 2, 3 }, "other" -> 123 |> 97 | ], 98 | Failure[ 99 | "PacletCICD::error", 100 | <| 101 | "MessageParameters" :> Evaluate @ { 102 | "Error message: {1, 2, 3}", 103 | <| "expr" -> { 1, 2, 3 }, "other" -> 123 |> 104 | }, 105 | "MessageTemplate" :> PacletCICD::error 106 | |> 107 | ], 108 | { PacletCICD::error }, 109 | TestID -> "throwError@@Tests/ErrorHandling.wlt:93,1-110,2" 110 | ] 111 | 112 | VerificationTest[ 113 | Wolfram`PacletCICD`Private`catchTop[ 114 | Wolfram`PacletCICD`Private`throwError[ "a" ]; 115 | Wolfram`PacletCICD`Private`throwError[ "b" ] 116 | ], 117 | Failure[ 118 | "PacletCICD::error", 119 | <| 120 | "MessageParameters" :> { "a" }, 121 | "MessageTemplate" :> PacletCICD::error 122 | |> 123 | ], 124 | { PacletCICD::error }, 125 | TestID -> "throwError@@Tests/ErrorHandling.wlt:112,1-126,2" 126 | ] 127 | 128 | (* ::**********************************************************************:: *) 129 | (* ::Section::Closed:: *) 130 | (*catchTop*) 131 | VerificationTest[ 132 | Wolfram`PacletCICD`Private`catchTop[ 133 | 134 | Wolfram`PacletCICD`Private`catch @ 135 | Wolfram`PacletCICD`Private`throwError[ "a" ]; 136 | 137 | Wolfram`PacletCICD`Private`throwGeneralMessage[ "warning", "b" ] 138 | ], 139 | Failure[ 140 | "PacletCICD::warning", 141 | <| 142 | "MessageParameters" :> { "b" }, 143 | "MessageTemplate" :> PacletCICD::warning 144 | |> 145 | ], 146 | { 147 | PacletCICD::error, 148 | PacletCICD::warning 149 | }, 150 | TestID -> "catchTop@@Tests/ErrorHandling.wlt:131,1-151,2" 151 | ] 152 | 153 | VerificationTest[ 154 | Wolfram`PacletCICD`Private`catchTop[ 155 | 156 | Wolfram`PacletCICD`Private`catchQuiet @ 157 | Wolfram`PacletCICD`Private`throwError[ "a" ]; 158 | 159 | Wolfram`PacletCICD`Private`throwGeneralMessage[ "warning", "b" ] 160 | ], 161 | Failure[ 162 | "PacletCICD::warning", 163 | <| 164 | "MessageParameters" :> { "b" }, 165 | "MessageTemplate" :> PacletCICD::warning 166 | |> 167 | ], 168 | { PacletCICD::warning }, 169 | TestID -> "catchTop@@Tests/ErrorHandling.wlt:153,1-170,2" 170 | ] 171 | 172 | VerificationTest[ 173 | Wolfram`PacletCICD`Private`catchTop @ { 174 | Wolfram`PacletCICD`Private`throwError[ "a" ], 175 | Wolfram`PacletCICD`Private`throwError[ "b" ] 176 | }, 177 | Failure[ 178 | "PacletCICD::error", 179 | <| 180 | "MessageParameters" :> { "a" }, 181 | "MessageTemplate" :> PacletCICD::error 182 | |> 183 | ], 184 | { PacletCICD::error }, 185 | TestID -> "catchTop@@Tests/ErrorHandling.wlt:172,1-186,2" 186 | ] 187 | 188 | VerificationTest[ 189 | Wolfram`PacletCICD`Private`catchTop @ { 190 | Wolfram`PacletCICD`Private`catchTop @ 191 | Wolfram`PacletCICD`Private`throwError[ "a" ], 192 | Wolfram`PacletCICD`Private`catchTop @ 193 | Wolfram`PacletCICD`Private`throwError[ "b" ] 194 | }, 195 | Failure[ 196 | "PacletCICD::error", 197 | <| 198 | "MessageParameters" :> { "a" }, 199 | "MessageTemplate" :> PacletCICD::error 200 | |> 201 | ], 202 | { PacletCICD::error }, 203 | TestID -> "catchTop@@Tests/ErrorHandling.wlt:188,1-204,2" 204 | ] 205 | 206 | VerificationTest[ 207 | Wolfram`PacletCICD`Private`catchTop @ { 208 | Wolfram`PacletCICD`Private`catch @ 209 | Wolfram`PacletCICD`Private`throwError[ "a" ], 210 | Wolfram`PacletCICD`Private`catch @ 211 | Wolfram`PacletCICD`Private`throwError[ "b" ] 212 | }, 213 | { 214 | Failure[ 215 | "PacletCICD::error", 216 | <| 217 | "MessageParameters" :> { "a" }, 218 | "MessageTemplate" :> PacletCICD::error 219 | |> 220 | ], 221 | Failure[ 222 | "PacletCICD::error", 223 | <| 224 | "MessageParameters" :> { "b" }, 225 | "MessageTemplate" :> PacletCICD::error 226 | |> 227 | ] 228 | }, 229 | { 230 | PacletCICD::error, 231 | PacletCICD::error 232 | }, 233 | TestID -> "catchTop@@Tests/ErrorHandling.wlt:206,1-234,2" 234 | ] 235 | -------------------------------------------------------------------------------- /Tests/BuildPaclet.wlt: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Initialization*) 4 | VerificationTest[ 5 | PacletObjectQ @ PacletObject @ File[ 6 | $pacletDir = 7 | Module[ { root, mx }, 8 | root = DirectoryName[ $TestFileName, 2 ]; 9 | mx = FileNameJoin @ { root, "MXBuild", "Wolfram__PacletCICD" }; 10 | If[ DirectoryQ @ mx, mx, root ] 11 | ] 12 | ], 13 | TestID -> "Initialize-PacletObject@@Tests/BuildPaclet.wlt:4,1-14,2" 14 | ] 15 | 16 | (* :!CodeAnalysis::BeginBlock:: *) 17 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 18 | VerificationTest[ 19 | Echo[ $TestFileName, "TestFileName" ]; 20 | PacletDirectoryLoad @ Echo[ $pacletDir, "PacletDirectory" ], 21 | { ___, $pacletDir, ___ }, 22 | SameTest -> MatchQ, 23 | TestID -> "Initialize-PacletDirectoryLoad@@Tests/BuildPaclet.wlt:18,1-24,2" 24 | ] 25 | (* :!CodeAnalysis::EndBlock:: *) 26 | 27 | VerificationTest[ 28 | Needs[ "Wolfram`PacletCICD`" ], 29 | Null, 30 | TestID -> "Initialize-Needs-PacletCICD@@Tests/BuildPaclet.wlt:27,1-31,2" 31 | ] 32 | 33 | (* ::**********************************************************************:: *) 34 | (* ::Subsection::Closed:: *) 35 | (*Context Check*) 36 | VerificationTest[ 37 | Context @ BuildPaclet, 38 | "Wolfram`PacletCICD`", 39 | TestID -> "Context-BuildPaclet@@Tests/BuildPaclet.wlt:36,1-40,2" 40 | ] 41 | 42 | VerificationTest[ 43 | Context @ ResetExampleDirectory, 44 | "Wolfram`PacletCICD`", 45 | TestID -> "Context-ResetExampleDirectory@@Tests/BuildPaclet.wlt:42,1-46,2" 46 | ] 47 | 48 | (* ::**********************************************************************:: *) 49 | (* ::Subsection::Closed:: *) 50 | (*ResetExampleDirectory*) 51 | VerificationTest[ 52 | ResetExampleDirectory @ All, 53 | { __Success }, 54 | SameTest -> MatchQ, 55 | TestID -> "ResetExampleDirectory-Initialization@@Tests/BuildPaclet.wlt:51,1-56,2" 56 | ] 57 | 58 | (* ::**********************************************************************:: *) 59 | (* ::Subsection::Closed:: *) 60 | (*Definitions*) 61 | VerificationTest[ 62 | $PublisherID; 63 | $PublisherID = "SamplePublisher", 64 | "SamplePublisher", 65 | TestID -> "SetPublisherID@@Tests/BuildPaclet.wlt:61,1-66,2" 66 | ] 67 | 68 | (* :!CodeAnalysis::BeginBlock:: *) 69 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 70 | VerificationTest[ 71 | DefinitionNotebookClient`ConsolePrint; 72 | suppressConsole // Attributes = { HoldFirst }; 73 | suppressConsole[ eval_ ] := 74 | Block[ 75 | { 76 | DefinitionNotebookClient`ConsolePrint, 77 | DefinitionNotebookClient`BeginConsoleGroup, 78 | DefinitionNotebookClient`EndConsoleGroup, 79 | Wolfram`PacletCICD`Private`setOutput, 80 | Wolfram`PacletCICD`Private`appendStepSummary, 81 | Print = Null & 82 | }, 83 | DefinitionNotebookClient`ConsolePrint // Options = { 84 | "Output" -> None 85 | }; 86 | Wolfram`PacletCICD`Private`noExit @ eval 87 | ], 88 | Null, 89 | TestID -> "SuppressConsole-Definition@@Tests/BuildPaclet.wlt:70,1-90,2" 90 | ] 91 | (* :!CodeAnalysis::EndBlock:: *) 92 | 93 | (* ::**********************************************************************:: *) 94 | (* ::Section::Closed:: *) 95 | (*BuildPaclet*) 96 | VerificationTest[ 97 | FileNames[ 98 | "*.paclet", 99 | FileNameJoin @ { First @ ExampleDirectory[ "FewIssues" ], "build" } 100 | ], 101 | { }, 102 | SameTest -> MatchQ, 103 | TestID -> "Empty-Build-Directory@@Tests/BuildPaclet.wlt:96,1-104,2" 104 | ] 105 | 106 | VerificationTest[ 107 | suppressConsole @ BuildPaclet[ 108 | ExampleDirectory[ "FewIssues" ], 109 | "SetWorkflowValue" -> False 110 | ], 111 | _Success, 112 | SameTest -> MatchQ, 113 | TestID -> "BuildPaclet-FewIssues@@Tests/BuildPaclet.wlt:106,1-114,2" 114 | ] 115 | 116 | VerificationTest[ 117 | FileNames[ 118 | "*.paclet", 119 | FileNameJoin @ { First @ ExampleDirectory[ "FewIssues" ], "build" } 120 | ], 121 | { _ }, 122 | SameTest -> MatchQ, 123 | TestID -> "Nonempty-Build-Directory@@Tests/BuildPaclet.wlt:116,1-124,2" 124 | ] 125 | 126 | VerificationTest[ 127 | suppressConsole @ BuildPaclet[ 128 | ExampleDirectory[ "MoreIssues" ], 129 | "Check" -> True, 130 | "SetWorkflowValue" -> False 131 | ], 132 | Failure[ "CheckPaclet::errors", _ ], 133 | { CheckPaclet::errors }, 134 | SameTest -> MatchQ, 135 | TestID -> "BuildPaclet-Check-Failure@@Tests/BuildPaclet.wlt:126,1-136,2" 136 | ] 137 | 138 | VerificationTest[ 139 | suppressConsole @ BuildPaclet[ 140 | ExampleDirectory[ "MoreIssues" ], 141 | "Check" -> False, 142 | "SetWorkflowValue" -> False 143 | ], 144 | Success[ "PacletBuild", _ ], 145 | SameTest -> MatchQ, 146 | TestID -> "BuildPaclet-Check-Skipped@@Tests/BuildPaclet.wlt:138,1-147,2" 147 | ] 148 | 149 | (* ::**********************************************************************:: *) 150 | (* ::Subsection::Closed:: *) 151 | (*Options*) 152 | 153 | (* ::**********************************************************************:: *) 154 | (* ::Subsubsection::Closed:: *) 155 | (*DisabledHints*) 156 | VerificationTest[ 157 | suppressConsole @ BuildPaclet[ 158 | ExampleDirectory[ "MoreIssues" ], 159 | "Check" -> True, 160 | "SetWorkflowValue" -> False, 161 | "DisabledHints" -> { 162 | Inherited, 163 | "CodeInspectionIssues", 164 | "CodeInspectionFileIssue/*", 165 | "PublisherUpdateNotAllowed", 166 | "NotPublisherContext" 167 | } 168 | ], 169 | Success[ "PacletBuild", _ ], 170 | SameTest -> MatchQ, 171 | TestID -> "BuildPaclet-DisabledHints-1@@Tests/BuildPaclet.wlt:156,1-172,2" 172 | ] 173 | 174 | VerificationTest[ 175 | suppressConsole @ BuildPaclet[ 176 | ExampleDirectory[ "MoreIssues" ], 177 | "Check" -> True, 178 | "SetWorkflowValue" -> False, 179 | "DisabledHints" -> { 180 | Inherited, 181 | "CodeInspectionIssues", 182 | "CodeInspectionFileIssue", 183 | "PublisherUpdateNotAllowed", 184 | "NotPublisherContext" 185 | } 186 | ], 187 | Success[ "PacletBuild", _ ], 188 | SameTest -> MatchQ, 189 | TestID -> "BuildPaclet-DisabledHints-2@@Tests/BuildPaclet.wlt:174,1-190,2" 190 | ] 191 | 192 | (* ::**********************************************************************:: *) 193 | (* ::Subsubsection::Closed:: *) 194 | (*FailureCondition*) 195 | VerificationTest[ 196 | suppressConsole @ BuildPaclet[ 197 | ExampleDirectory[ "MoreIssues" ], 198 | "Check" -> True, 199 | "SetWorkflowValue" -> False, 200 | "DisabledHints" -> { 201 | Inherited, 202 | "CodeInspectionIssues", 203 | "CodeInspectionFileIssue" 204 | }, 205 | "FailureCondition" -> { "Warning", "Error" } 206 | ], 207 | Failure[ "CheckPaclet::errors", _ ], 208 | { CheckPaclet::errors }, 209 | SameTest -> MatchQ, 210 | TestID -> "BuildPaclet-FailureCondition@@Tests/BuildPaclet.wlt:195,1-211,2" 211 | ] 212 | 213 | (* ::**********************************************************************:: *) 214 | (* ::Section::Closed:: *) 215 | (*Cleanup*) 216 | VerificationTest[ 217 | ResetExampleDirectory @ All, 218 | { __Success }, 219 | SameTest -> MatchQ, 220 | TestID -> "ResetExampleDirectory-Cleanup@@Tests/BuildPaclet.wlt:216,1-221,2" 221 | ] 222 | 223 | VerificationTest[ 224 | $PublisherID = None, 225 | None, 226 | TestID -> "ClearPublisherID@@Tests/BuildPaclet.wlt:223,1-227,2" 227 | ] -------------------------------------------------------------------------------- /Kernel/Loading.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`Internal`", { "Wolfram`PacletCICD`" } ]; 5 | 6 | $BuildingMX; 7 | $MXFile; 8 | 9 | ClearAll[ 10 | BuildMX, 11 | SetContextLoad, 12 | LoadSubPackage, 13 | LoadSubPackages, 14 | $SubPackageSymbols 15 | ]; 16 | 17 | Begin[ "`Private`" ]; 18 | 19 | (* ::**********************************************************************:: *) 20 | (* ::Section::Closed:: *) 21 | (*$SubPackageSymbols*) 22 | $SubPackageSymbols := Association @ Internal`BagPart[ $subPackageSymbols, All ]; 23 | $subPackageSymbols = Internal`Bag[ ]; 24 | 25 | (* ::**********************************************************************:: *) 26 | (* ::Section::Closed:: *) 27 | (*LoadSubPackage*) 28 | 29 | (* :!CodeAnalysis::BeginBlock:: *) 30 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 31 | LoadSubPackage[ name_String, label_ ] /; StringFreeQ[ name, "`" ] := 32 | LoadSubPackage[ "Wolfram`PacletCICD`" <> name <> "`", label ]; 33 | 34 | LoadSubPackage[ ctx_String, label_ ] := 35 | Block[ { $ContextPath }, 36 | If[ TrueQ @ $Debug, 37 | Print[ "Loading: " , label, 38 | "\n\tContext: ", ctx, 39 | "\n\tFile: ", FindFile @ ctx 40 | ] 41 | ]; 42 | LoadSubPackage[ ctx, _ ] = Quiet[ Get @ ctx, General::shdw ]; 43 | ]; 44 | 45 | LoadSubPackage[ ctx_String ] := LoadSubPackage[ ctx, "" ]; 46 | (* :!CodeAnalysis::EndBlock:: *) 47 | 48 | (* ::**********************************************************************:: *) 49 | (* ::Section::Closed:: *) 50 | (*LoadSubPackages*) 51 | LoadSubPackages[ ] := Keys @ KeyValueMap[ Rule, $SubPackageSymbols ]; 52 | 53 | (* ::**********************************************************************:: *) 54 | (* ::Section::Closed:: *) 55 | (*SetContextLoad*) 56 | SetContextLoad // Attributes = { HoldFirst }; 57 | SetContextLoad::context = "Warning: suspicious context in `1`."; 58 | 59 | SetContextLoad[ sym_Symbol, name_String ] /; StringFreeQ[ name, "`" ] := 60 | SetContextLoad[ sym, "Wolfram`PacletCICD`" <> name <> "`" ]; 61 | 62 | SetContextLoad[ sym_Symbol, context_String ] := 63 | With[ { full = Context @ sym <> SymbolName @ Unevaluated @ sym }, 64 | If[ $Debug && Context @ sym =!= "Wolfram`PacletCICD`", 65 | Message[ SetContextLoad::context, full ] 66 | ]; 67 | Internal`StuffBag[ $subPackageSymbols, full :> sym ]; 68 | sym // ClearAll; 69 | sym := (ClearAll @ sym; LoadSubPackage[ context, full ]; sym) 70 | ]; 71 | 72 | (* ::**********************************************************************:: *) 73 | (* ::Section::Closed:: *) 74 | (*BuildMX*) 75 | BuildMX[ ] := ( 76 | LoadSubPackages[ ]; 77 | GeneralUtilities`EnsureDirectory @ DirectoryName @ $MXFile; 78 | $BuildingMX = False; 79 | DumpSave[ $MXFile, "Wolfram`PacletCICD`", "SymbolAttributes" -> False ]; 80 | $MXFile 81 | ); 82 | 83 | (* ::**********************************************************************:: *) 84 | (* ::Section::Closed:: *) 85 | (*Definitions*) 86 | 87 | (* ::**********************************************************************:: *) 88 | (* ::Subsection::Closed:: *) 89 | (*ASTUtilities*) 90 | SetContextLoad[ ASTCondition , "ASTUtilities" ]; 91 | SetContextLoad[ ASTConditionValue, "ASTUtilities" ]; 92 | SetContextLoad[ ASTPattern , "ASTUtilities" ]; 93 | SetContextLoad[ ASTPatternTest , "ASTUtilities" ]; 94 | SetContextLoad[ EquivalentNodeQ , "ASTUtilities" ]; 95 | SetContextLoad[ FromAST , "ASTUtilities" ]; 96 | 97 | (* ::**********************************************************************:: *) 98 | (* ::Subsection::Closed:: *) 99 | (*Examples*) 100 | SetContextLoad[ $ExamplesLocation , "Examples" ]; 101 | SetContextLoad[ ExampleDirectory , "Examples" ]; 102 | SetContextLoad[ ResetExampleDirectory, "Examples" ]; 103 | 104 | (* ::**********************************************************************:: *) 105 | (* ::Subsection::Closed:: *) 106 | (*TestPaclet*) 107 | SetContextLoad[ TestPaclet , "TestPaclet" ]; 108 | SetContextLoad[ AnnotateTestIDs, "TestPaclet" ]; 109 | 110 | (* ::**********************************************************************:: *) 111 | (* ::Subsection::Closed:: *) 112 | (*Workflows*) 113 | SetContextLoad[ GitHubSecret , "Workflows" ]; 114 | SetContextLoad[ Workflow , "Workflows" ]; 115 | SetContextLoad[ WorkflowEvaluate, "Workflows" ]; 116 | SetContextLoad[ WorkflowExport , "Workflows" ]; 117 | SetContextLoad[ WorkflowJob , "Workflows" ]; 118 | SetContextLoad[ WorkflowJobQ , "Workflows" ]; 119 | SetContextLoad[ WorkflowQ , "Workflows" ]; 120 | SetContextLoad[ WorkflowStep , "Workflows" ]; 121 | SetContextLoad[ WorkflowStepQ , "Workflows" ]; 122 | 123 | (* ::**********************************************************************:: *) 124 | (* ::Subsection::Closed:: *) 125 | (*Console*) 126 | SetContextLoad[ ConsoleDebug , "Console" ]; 127 | SetContextLoad[ ConsoleError , "Console" ]; 128 | SetContextLoad[ ConsoleLog , "Console" ]; 129 | SetContextLoad[ ConsoleNotice , "Console" ]; 130 | SetContextLoad[ ConsoleWarning, "Console" ]; 131 | 132 | (* ::**********************************************************************:: *) 133 | (* ::Subsection::Closed:: *) 134 | (*PublisherTokens*) 135 | SetContextLoad[ CreatePublisherToken, "PublisherTokens" ]; 136 | SetContextLoad[ DeletePublisherToken, "PublisherTokens" ]; 137 | SetContextLoad[ PublisherTokenObject, "PublisherTokens" ]; 138 | SetContextLoad[ PublisherTokens , "PublisherTokens" ]; 139 | 140 | (* ::**********************************************************************:: *) 141 | (* ::Subsection::Closed:: *) 142 | (*PacletInformation*) 143 | SetContextLoad[ ReplacePacletInfo, "PacletInformation" ]; 144 | SetContextLoad[ SetPacletInfo , "PacletInformation" ]; 145 | 146 | (* ::**********************************************************************:: *) 147 | (* ::Subsection::Closed:: *) 148 | (*Units*) 149 | SetContextLoad[ SecondsToQuantity, "Units" ]; 150 | SetContextLoad[ BytesToQuantity , "Units" ]; 151 | 152 | (* ::**********************************************************************:: *) 153 | (* ::Subsection::Closed:: *) 154 | (*WorkflowValue*) 155 | SetContextLoad[ $WorkflowValueScope , "WorkflowValue" ]; 156 | SetContextLoad[ InitializeWorkflowValues, "WorkflowValue" ]; 157 | SetContextLoad[ WorkflowValue , "WorkflowValue" ]; 158 | 159 | (* ::**********************************************************************:: *) 160 | (* ::Subsection::Closed:: *) 161 | (*ScriptConfirm*) 162 | SetContextLoad[ ScriptConfirm , "ScriptConfirmation" ]; 163 | SetContextLoad[ ScriptConfirmAssert, "ScriptConfirmation" ]; 164 | SetContextLoad[ ScriptConfirmBy , "ScriptConfirmation" ]; 165 | SetContextLoad[ ScriptConfirmMatch , "ScriptConfirmation" ]; 166 | 167 | (* ::**********************************************************************:: *) 168 | (* ::Subsection::Closed:: *) 169 | (*Other*) 170 | SetContextLoad[ BuildPaclet , "BuildPaclet" ]; 171 | SetContextLoad[ CheckDependencies , "CheckDependencies" ]; 172 | SetContextLoad[ CheckPaclet , "CheckPaclet" ]; 173 | SetContextLoad[ CompileLibraryResources, "Compilation" ]; 174 | SetContextLoad[ DeployPaclet , "DeployPaclet" ]; 175 | SetContextLoad[ FormatNotebooks , "FormatNotebooks" ]; 176 | SetContextLoad[ FormattingHelper , "Formatting" ]; 177 | SetContextLoad[ GitHubPacletInstall , "GitHubPacletInstall" ]; 178 | SetContextLoad[ MessageFailure , "MessageFailure" ]; 179 | SetContextLoad[ SubmitPaclet , "SubmitPaclet" ]; 180 | SetContextLoad[ ToMarkdownString , "Markdown" ]; 181 | SetContextLoad[ ReadableForm , "ReadableForm" ]; 182 | 183 | (* ::**********************************************************************:: *) 184 | (* ::Section::Closed:: *) 185 | (*Preloading*) 186 | WorkflowValue /: (wfv: WorkflowValue[ ___ ] = rhs_) /; 187 | (LoadSubPackage[ "WorkflowValue" ]; True) := 188 | wfv = rhs; 189 | 190 | WorkflowValue /: (wfv: WorkflowValue[ ___ ] := rhs_) /; 191 | (LoadSubPackage[ "WorkflowValue" ]; True) := 192 | wfv := rhs; 193 | 194 | WorkflowValue /: (wfv: WorkflowValue[ ___ ][ ___ ] = rhs_) /; 195 | (LoadSubPackage[ "WorkflowValue" ]; True) := 196 | wfv = rhs; 197 | 198 | WorkflowValue /: (wfv: WorkflowValue[ ___ ][ ___ ] := rhs_) /; 199 | (LoadSubPackage[ "WorkflowValue" ]; True) := 200 | wfv := rhs; 201 | 202 | (* ::**********************************************************************:: *) 203 | (* ::Section::Closed:: *) 204 | (*Package Footer*) 205 | End[ ]; 206 | EndPackage[ ]; -------------------------------------------------------------------------------- /Kernel/Units.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | ClearAll[ 7 | BytesToQuantity, 8 | SecondsToQuantity 9 | ]; 10 | 11 | Begin[ "`Units`Private`" ]; 12 | 13 | $ContextAliases[ "p`" ] = "Wolfram`PacletCICD`Private`"; 14 | 15 | (* ::**********************************************************************:: *) 16 | (* ::Section::Closed:: *) 17 | (*BytesToQuantity*) 18 | (*https://resources.wolframcloud.com/FunctionRepository/resources/BytesToQuantity*) 19 | 20 | BytesToQuantity // Options = { "Chop" -> False }; 21 | 22 | BytesToQuantity[ b_, opts: OptionsPattern[ ] ] := 23 | p`catchTop @ With[ { conv = bytesToQuantity @ b }, 24 | If[ TrueQ @ QuantityQ @ conv, 25 | If[ TrueQ @ OptionValue @ Chop, integerChop @ conv, conv ], 26 | b (* TODO: error handling *) 27 | ] 28 | ]; 29 | 30 | BytesToQuantity // p`catchUndefined; 31 | 32 | bytesToQuantity[ b_Integer? Negative ] := -bytesToQuantity[ -b ]; 33 | bytesToQuantity[ b_Integer ] := Replace[ b, $btqRules ]; 34 | 35 | bytesToQuantity[ q_Quantity ] := 36 | With[ { b = Check[ UnitConvert[ q, "Bytes" ], $Failed, Quantity::compat ] }, 37 | bytesToQuantity @ QuantityMagnitude @ b /; ! FailureQ @ b 38 | ]; 39 | 40 | (* ::**********************************************************************:: *) 41 | (* ::Subsection::Closed:: *) 42 | (*$btqRules*) 43 | $btqRules = Dispatch @ Flatten @ { 44 | MapIndexed[ 45 | Function[ 46 | With[ { e = First[ #2 ] }, 47 | n_ /; n < 1000^e :> 48 | UnitConvert[ Quantity[ 1.0 * n, "Bytes" ], #1 ] 49 | ] 50 | ], 51 | { 52 | "Bytes", 53 | "Kilobytes", 54 | "Megabytes", 55 | "Gigabytes", 56 | "Terabytes", 57 | "Petabytes", 58 | "Exabytes", 59 | "Zettabytes" 60 | } 61 | ], 62 | n_ /; n >= 1000^8 :> 63 | UnitConvert[ Quantity[ 1.0 * n, "Bytes" ], "Yottabytes" ], 64 | n_ :> Quantity[ 1.0 * n, "Bytes" ] 65 | }; 66 | 67 | (* ::**********************************************************************:: *) 68 | (* ::Section::Closed:: *) 69 | (*SecondsToQuantity*) 70 | (*https://resources.wolframcloud.com/FunctionRepository/resources/SecondsToQuantity/*) 71 | 72 | SecondsToQuantity::compat = 73 | "`1` is not a compatible time unit."; 74 | 75 | SecondsToQuantity::mmu = 76 | "The value given for MaxMixedUnits is expected to be a positive integer or Automatic."; 77 | 78 | SecondsToQuantity // Attributes = { Listable }; 79 | 80 | SecondsToQuantity // Options = { 81 | "Chop" -> False, 82 | "MixedUnits" -> True, 83 | "MaxMixedUnits" -> Automatic 84 | }; 85 | 86 | SecondsToQuantity[ q_Quantity, opts: OptionsPattern[ ] ] := 87 | p`catchTop @ With[ { sec = convertSeconds @ q }, 88 | SecondsToQuantity[ sec, opts ] 89 | ]; 90 | 91 | SecondsToQuantity[ sec: Except[ _Quantity ], OptionsPattern[ ] ] := 92 | p`catchTop @ secondsToQuantity[ 93 | sec, 94 | TrueQ @ OptionValue[ "MixedUnits" ], 95 | OptionValue[ "MaxMixedUnits" ] 96 | ]; 97 | 98 | SecondsToQuantity // p`catchUndefined; 99 | 100 | secondsToQuantity[ sec_? Internal`SyntacticNegativeQ, mixed_, max_ ] := 101 | -secondsToQuantity[ -sec, mixed, max ]; 102 | 103 | secondsToQuantity[ sec_, True, Automatic ] := Replace[ sec, $mixedDispatch ]; 104 | secondsToQuantity[ sec_, _ , Automatic ] := Replace[ sec, $dispatch ]; 105 | 106 | secondsToQuantity[ sec_, True, max_ ] := 107 | Replace[ sec, mixedDispatch @ Min[ $maxMixedUnits, max ] ]; 108 | 109 | $maxMixedUnits = 3; 110 | 111 | 112 | convertSeconds[ Quantity[ q_, unit_ ] ] := 113 | Module[ { conv }, 114 | conv = compatQuietCheck @ qConvert[ q, unit, "Seconds" ]; 115 | validateConversion[ conv, unit ] 116 | ]; 117 | 118 | 119 | compatQuietCheck // Attributes = { HoldFirst }; 120 | compatQuietCheck[ eval_ ] := 121 | Quiet[ Check[ eval, $fail, Quantity::compat ], Quantity::compat ]; 122 | 123 | validateConversion // ClearAll; 124 | validateConversion[ $fail, unit_ ] := (message[ "compat", unit ]; $fail); 125 | validateConversion[ q: Quantity[ _, "Seconds" ], _ ] := QuantityMagnitude @ q; 126 | validateConversion[ ___ ] := $fail; 127 | 128 | message // ClearAll; 129 | message[ tag_, args___ ] := 130 | Block[ { PrintTemporary }, 131 | Message[ 132 | MessageName[ SecondsToQuantity, tag ], 133 | args 134 | ] 135 | ]; 136 | 137 | qConvert // ClearAll; 138 | qConvert[ n_, unit1_, unit2_ ] := UnitConvert[ Quantity[ n, unit1 ], unit2 ]; 139 | 140 | qConvertMag // ClearAll; 141 | qConvertMag[ n_, unit1_, unit2_ ] := QuantityMagnitude @ qConvert[ n, unit1, unit2 ]; 142 | 143 | makeThresholds // ClearAll; 144 | makeThresholds[ units_ ] := 145 | AssociationMap[ Function @ qConvertMag[ 1.0, #1, "Seconds" ], units ]; 146 | 147 | makeRules // ClearAll; 148 | makeRules[ thresholds_ ] := 149 | Flatten @ { 150 | If[ FreeQ[ Keys @ thresholds, MixedUnit ], 151 | Nothing, 152 | With[ { max = qConvertMag[ 10000.0, "Years", "Seconds" ] }, 153 | x_ /; x > max :> UnitConvert[ Quantity[ x, "Seconds" ], "Years" ] 154 | ] 155 | ], 156 | KeyValueMap[ 157 | x_ /; x >= #2 :> UnitConvert[ Quantity[ x, "Seconds" ], #1 ] &, 158 | thresholds 159 | ], 160 | With[ { pMin = $planckThreshold, min = Min @ thresholds }, 161 | { 162 | x_ /; 0 < x < pMin :> UnitConvert[ Quantity[ x, "Seconds" ], "PlanckTime" ], 163 | x_ /; 0 < x < min :> UnitConvert[ Quantity[ x, "Seconds" ], "Yoctoseconds" ] 164 | } 165 | ], 166 | x_ :> Quantity[ x, "Seconds" ] 167 | }; 168 | 169 | $unitThresholds = makeThresholds @ { 170 | "Years", 171 | "Months", 172 | "Days", 173 | "Hours", 174 | "Minutes", 175 | "Seconds", 176 | "Milliseconds", 177 | "Microseconds", 178 | "Nanoseconds", 179 | "Picoseconds", 180 | "Femtoseconds", 181 | "Attoseconds", 182 | "Zeptoseconds" 183 | }; 184 | 185 | $mixedUnitThresholds = makeThresholds @ { 186 | MixedUnit @ { "Years", "Months", "Days" }, 187 | MixedUnit @ { "Months", "Days" }, 188 | MixedUnit @ { "Days", "Hours" }, 189 | MixedUnit @ { "Hours", "Minutes", "Seconds" }, 190 | MixedUnit @ { "Minutes", "Seconds" }, 191 | "Seconds", 192 | "Milliseconds", 193 | "Microseconds", 194 | "Nanoseconds", 195 | "Picoseconds", 196 | "Femtoseconds", 197 | "Attoseconds", 198 | "Zeptoseconds" 199 | }; 200 | 201 | mixedUnitThresholds[ n_Integer? Positive ] := mixedUnitThresholds[ n ] = 202 | makeThresholds @ { 203 | MixedUnit @ Take[ { "Years", "Months", "Days" }, UpTo @ n ], 204 | MixedUnit @ Take[ { "Months", "Days" }, UpTo @ n ], 205 | MixedUnit @ Take[ { "Days", "Hours" }, UpTo @ n ], 206 | MixedUnit @ Take[ { "Hours", "Minutes", "Seconds" }, UpTo @ n ], 207 | MixedUnit @ Take[ { "Minutes", "Seconds" }, UpTo @ n ], 208 | "Seconds", 209 | "Milliseconds", 210 | "Microseconds", 211 | "Nanoseconds", 212 | "Picoseconds", 213 | "Femtoseconds", 214 | "Attoseconds", 215 | "Zeptoseconds" 216 | }; 217 | 218 | mixedUnitThresholds[ ___ ] := p`throwMessageFailure[ SecondsToQuantity::mmu ]; 219 | 220 | $planckThreshold = 221 | QuantityMagnitude @ UnitConvert[ 222 | Quantity[ 1000.0, "PlanckTime" ], 223 | "Seconds" 224 | ]; 225 | 226 | $rules = makeRules @ $unitThresholds; 227 | 228 | $mixedRules = makeRules @ $mixedUnitThresholds; 229 | 230 | $dispatch := $dispatch = Dispatch @ $rules; 231 | 232 | $mixedDispatch := $mixedDispatch = Dispatch @ $mixedRules; 233 | 234 | mixedDispatch[ n_ ] := mixedDispatch[ n ] = 235 | Dispatch @ makeRules @ mixedUnitThresholds @ n; 236 | 237 | (* ::**********************************************************************:: *) 238 | (* ::Section::Closed:: *) 239 | (*Utilities*) 240 | 241 | (* ::**********************************************************************:: *) 242 | (* ::Subsection::Closed:: *) 243 | (*integerChop*) 244 | integerChop[ expr_ ] := integerChop[ expr, 10^-5 ]; 245 | 246 | integerChop[ expr_, delta_ ] := 247 | ReplaceAll[ 248 | expr, 249 | n: (_Real|_Complex)? AtomQ :> 250 | With[ { m = Round @ n }, m /; Chop[ m - n, delta ] === 0 ] 251 | ]; 252 | 253 | (* ::**********************************************************************:: *) 254 | (* ::Section::Closed:: *) 255 | (*Package Footer*) 256 | End[ ]; 257 | 258 | EndPackage[ ]; -------------------------------------------------------------------------------- /Documentation/English/Guides/CustomCICDWorkflows.nb: -------------------------------------------------------------------------------- 1 | Notebook[ 2 | { 3 | Cell[ 4 | TextData[ 5 | { 6 | "New in: ", 7 | Cell["??", "HistoryData", CellTags -> "New"], 8 | " | Modified in: ", 9 | Cell[" ", "HistoryData", CellTags -> "Modified"], 10 | " | Obsolete in: ", 11 | Cell[" ", "HistoryData", CellTags -> "Obsolete"], 12 | " | Excised in: ", 13 | Cell[" ", "HistoryData", CellTags -> "Excised"] 14 | } 15 | ], 16 | "History", 17 | CellID -> 837855486 18 | ], 19 | Cell[ 20 | "Created by: rhennigan on 02-21-2022 14:48:19", 21 | "AuthorDate", 22 | CellID -> 162480164 23 | ], 24 | Cell[ 25 | CellGroupData[ 26 | { 27 | Cell[ 28 | "Categorization", 29 | "CategorizationSection", 30 | CellID -> 185479684 31 | ], 32 | Cell[ 33 | "Guide", 34 | "Categorization", 35 | CellLabel -> "Entity Type", 36 | CellID -> 97543335 37 | ], 38 | Cell[ 39 | "Wolfram/PacletCICD", 40 | "Categorization", 41 | CellLabel -> "Paclet Name", 42 | CellID -> 140923947 43 | ], 44 | Cell[ 45 | "Wolfram`PacletCICD`", 46 | "Categorization", 47 | CellLabel -> "Context", 48 | CellID -> 39735646 49 | ], 50 | Cell[ 51 | "Wolfram/PacletCICD/guide/CustomCICDWorkflows", 52 | "Categorization", 53 | CellLabel -> "URI", 54 | CellID -> 58651964 55 | ] 56 | }, 57 | Open 58 | ] 59 | ], 60 | Cell[ 61 | CellGroupData[ 62 | { 63 | Cell["Keywords", "KeywordsSection", CellID -> 4843762], 64 | Cell["XXXX", "Keywords", CellID -> 14752974] 65 | }, 66 | Open 67 | ] 68 | ], 69 | Cell[ 70 | CellGroupData[ 71 | { 72 | Cell[ 73 | "Custom CI/CD Workflows", 74 | "GuideTitle", 75 | CellID -> 486586233 76 | ], 77 | Cell["XXXX", "GuideAbstract", CellID -> 74912579] 78 | }, 79 | Open 80 | ] 81 | ], 82 | Cell[ 83 | CellGroupData[ 84 | { 85 | Cell["", "GuideFunctionsSection", CellID -> 38250252], 86 | Cell[ 87 | TextData[ 88 | { 89 | Cell[ 90 | BoxData[ 91 | ButtonBox[ 92 | "CheckPaclet", 93 | BaseStyle -> "Link", 94 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/CheckPaclet" 95 | ] 96 | ], 97 | "InlineGuideFunction" 98 | ], 99 | " \[LongDash] ", 100 | "check a Paclet for potential issues" 101 | } 102 | ], 103 | "GuideText", 104 | CellID -> 243238051 105 | ], 106 | Cell[ 107 | TextData[ 108 | { 109 | Cell[ 110 | BoxData[ 111 | ButtonBox[ 112 | "BuildPaclet", 113 | BaseStyle -> "Link", 114 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/BuildPaclet" 115 | ] 116 | ], 117 | "InlineGuideFunction" 118 | ], 119 | " \[LongDash] ", 120 | "build a Paclet" 121 | } 122 | ], 123 | "GuideText", 124 | CellID -> 30181968 125 | ], 126 | Cell[ 127 | CellGroupData[ 128 | { 129 | Cell[ 130 | "Testing", 131 | "GuideFunctionsSubsection", 132 | CellID -> 61580765 133 | ], 134 | Cell[ 135 | TextData[ 136 | { 137 | Cell[ 138 | BoxData[ 139 | ButtonBox[ 140 | "TestPaclet", 141 | BaseStyle -> "Link", 142 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/TestPaclet" 143 | ] 144 | ], 145 | "InlineGuideFunction" 146 | ], 147 | " \[LongDash] ", 148 | "run the test files for a given Paclet" 149 | } 150 | ], 151 | "GuideText", 152 | CellID -> 113437997 153 | ], 154 | Cell[ 155 | TextData[ 156 | { 157 | Cell[ 158 | BoxData[ 159 | ButtonBox[ 160 | "AnnotateTestIDs", 161 | BaseStyle -> "Link", 162 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/AnnotateTestIDs" 163 | ] 164 | ], 165 | "InlineGuideFunction" 166 | ], 167 | " \[LongDash] ", 168 | "rewrite the TestID for all VerificationTest expressions in a test file to include source information" 169 | } 170 | ], 171 | "GuideText", 172 | CellID -> 855709449 173 | ] 174 | }, 175 | Open 176 | ] 177 | ], 178 | Cell[ 179 | CellGroupData[ 180 | { 181 | Cell[ 182 | "Console Output", 183 | "GuideFunctionsSubsection", 184 | CellID -> 102000112 185 | ], 186 | Cell[ 187 | TextData[ 188 | { 189 | Cell[ 190 | BoxData[ 191 | ButtonBox[ 192 | "ConsoleLog", 193 | BaseStyle -> "Link", 194 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/ConsoleLog" 195 | ] 196 | ], 197 | "InlineGuideFunction" 198 | ], 199 | " \[LongDash] ", 200 | "print a message to the console formatted for the current environment" 201 | } 202 | ], 203 | "GuideText", 204 | CellID -> 312274386 205 | ], 206 | Cell[ 207 | TextData[ 208 | { 209 | Cell[ 210 | BoxData[ 211 | ButtonBox[ 212 | "ConsoleError", 213 | BaseStyle -> "Link", 214 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/ConsoleError" 215 | ] 216 | ], 217 | "InlineFormula" 218 | ], 219 | " \[EmptyVerySmallSquare] ", 220 | Cell[ 221 | BoxData[ 222 | ButtonBox[ 223 | "ConsoleWarning", 224 | BaseStyle -> "Link", 225 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/ConsoleWarning" 226 | ] 227 | ], 228 | "InlineFormula" 229 | ], 230 | " \[EmptyVerySmallSquare] ", 231 | Cell[ 232 | BoxData[ 233 | ButtonBox[ 234 | "ConsoleNotice", 235 | BaseStyle -> "Link", 236 | ButtonData -> "paclet:Wolfram/PacletCICD/ref/ConsoleNotice" 237 | ] 238 | ], 239 | "InlineFormula" 240 | ] 241 | } 242 | ], 243 | "InlineGuideFunctionListing", 244 | CellID -> 253771968 245 | ] 246 | }, 247 | Open 248 | ] 249 | ] 250 | }, 251 | Open 252 | ] 253 | ], 254 | Cell[ 255 | CellGroupData[ 256 | { 257 | Cell[ 258 | "Tech Notes", 259 | "GuideTutorialsSection", 260 | CellID -> 305427168 261 | ], 262 | Cell[ 263 | TextData[ 264 | ButtonBox[ 265 | "License Entitlements and Repository Secrets", 266 | BaseStyle -> "Link", 267 | ButtonData -> "paclet:Wolfram/PacletCICD/tutorial/LicenseEntitlementsAndRepositorySecrets" 268 | ] 269 | ], 270 | "GuideTutorial", 271 | CellID -> 10713611 272 | ] 273 | }, 274 | Open 275 | ] 276 | ], 277 | Cell[ 278 | CellGroupData[ 279 | { 280 | Cell[ 281 | "Related Guides", 282 | "GuideMoreAboutSection", 283 | CellID -> 174912451 284 | ], 285 | Cell[ 286 | TextData[ 287 | ButtonBox[ 288 | "Continuous Integration and Deployment", 289 | BaseStyle -> "Link", 290 | ButtonData -> "paclet:Wolfram/PacletCICD/guide/ContinuousIntegrationAndDeployment" 291 | ] 292 | ], 293 | "GuideMoreAbout", 294 | CellID -> 12117664 295 | ], 296 | Cell[ 297 | TextData[ 298 | ButtonBox[ 299 | "Workflows", 300 | BaseStyle -> "Link", 301 | ButtonData -> "paclet:Wolfram/PacletCICD/guide/Workflows" 302 | ] 303 | ], 304 | "GuideMoreAbout", 305 | CellID -> 51271203 306 | ] 307 | }, 308 | Open 309 | ] 310 | ], 311 | Cell[ 312 | "Related Links", 313 | "GuideRelatedLinksSection", 314 | CellID -> 5139173 315 | ] 316 | }, 317 | WindowStatusArea -> Automatic, 318 | TaggingRules -> <| 319 | "InformationPopupMenuItemAdded" -> True, 320 | "Author" -> "rhennigan", 321 | "CreationDate" -> "02-21-2022 14:48:19", 322 | "NotebookIndexQ" -> True, 323 | "NotebookLastIndexed" -> DateObject[ 324 | {2022, 2, 21, 15, 1, 23.626062`8.125966322200151}, 325 | "Instant", 326 | "Gregorian", 327 | -5. 328 | ], 329 | "NotebookUUID" -> "e486dcb9-cea9-4b81-886e-e0d5194d5992" 330 | |>, 331 | FrontEndVersion -> "13.1 for Microsoft Windows (64-bit) (February 16, 2022)", 332 | StyleDefinitions -> FrontEnd`FileName[ 333 | {"Wolfram"}, 334 | "GuidePageStylesExt.nb", 335 | CharacterEncoding -> "UTF-8" 336 | ], 337 | ExpressionUUID -> "e486dcb9-cea9-4b81-886e-e0d5194d5992" 338 | ] -------------------------------------------------------------------------------- /Kernel/Console.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | ClearAll[ 7 | ConsoleDebug, 8 | ConsoleError, 9 | ConsoleLog, 10 | ConsoleNotice, 11 | ConsoleWarning 12 | ]; 13 | 14 | Begin[ "`Private`" ]; 15 | 16 | $ContextAliases[ "dnc`" ] = "DefinitionNotebookClient`"; 17 | $ContextAliases[ "dcc`" ] = "DefinitionNotebookClient`Console`PackagePrivate`"; 18 | $ContextAliases[ "ci`" ] = "CodeInspector`"; 19 | 20 | (* ::**********************************************************************:: *) 21 | (* ::Section::Closed:: *) 22 | (*ConsoleLog*) 23 | ConsoleLog // Options = { 24 | "ConsoleType" -> Automatic, 25 | "IndentSize" -> Automatic, 26 | "Level" -> None, 27 | "Output" -> Automatic 28 | }; 29 | 30 | ConsoleLog[ expr_, opts: OptionsPattern[ ] ] := 31 | catchTop @ ConsoleLog[ Unevaluated @ expr, None, opts ]; 32 | 33 | ConsoleLog[ e_, file_, opts: OptionsPattern[ ] ] := 34 | catchTop @ ConsoleLog[ Unevaluated @ e, file, None, opts ]; 35 | 36 | ConsoleLog[ e_, f_, line_Integer, opts: OptionsPattern[ ] ] := 37 | catchTop @ ConsoleLog[ Unevaluated @ e, f, { line, line }, opts ]; 38 | 39 | ConsoleLog[ e_, f_, { l1_Integer, l2_Integer }, opts: OptionsPattern[ ] ] := 40 | catchTop @ ConsoleLog[ 41 | Unevaluated @ e, 42 | f, 43 | { { l1, 1 }, { l2, -1 } }, 44 | opts 45 | ]; 46 | 47 | ConsoleLog[ ins_ci`InspectionObject, opts: OptionsPattern[ ] ] := 48 | catchTop @ Enclose @ Module[ { msg0, more, msg, level }, 49 | msg = ConfirmBy[ ins[ "Description" ], StringQ ]; 50 | more = ConfirmMatch[ ins[ "AdditionalDescriptions" ], { ___String } ]; 51 | msg = StringRiffle[ Flatten @ { msg, more }, " " ]; 52 | level = ConfirmBy[ ins[ "Severity" ], StringQ ]; 53 | 54 | ConsoleLog[ 55 | StringReplace[ msg, "``" -> "`" ], 56 | ins, 57 | opts, 58 | "Level" -> level 59 | ] 60 | ]; 61 | 62 | ConsoleLog[ expr_, ins_ci`InspectionObject, opts: OptionsPattern[ ] ] := 63 | catchTop @ ConsoleLog[ Unevaluated @ expr, ins, None, opts ]; 64 | 65 | ConsoleLog[ expr_, ins_ci`InspectionObject, None, opts: OptionsPattern[ ] ] := 66 | catchTop @ Module[ { type, level, indent, output }, 67 | 68 | type = OptionValue[ "ConsoleType" ]; 69 | level = OptionValue[ "Level" ]; 70 | indent = consoleIndentSize @ OptionValue[ "IndentSize" ]; 71 | output = consoleOutputRule @ OptionValue[ "Output" ]; 72 | 73 | withConsoleSettings[ 74 | { level, type }, 75 | dnc`ConsolePrint[ 76 | Defer @ expr, 77 | output, 78 | "IndentSize" -> indent, 79 | "Level" -> level, 80 | "SourceInformation" -> ins 81 | ] 82 | ] 83 | ]; 84 | 85 | ConsoleLog[ expr_, file_, pos_, opts: OptionsPattern[ ] ] := 86 | catchTop @ Module[ { type, level, indent, output }, 87 | 88 | type = OptionValue[ "ConsoleType" ]; 89 | level = OptionValue[ "Level" ]; 90 | indent = consoleIndentSize @ OptionValue[ "IndentSize" ]; 91 | output = consoleOutputRule @ OptionValue[ "Output" ]; 92 | 93 | withConsoleSettings[ 94 | { level, type }, 95 | dnc`ConsolePrint[ 96 | Defer @ expr, 97 | output, 98 | "IndentSize" -> indent, 99 | "Level" -> level, 100 | "SourceInformation" -> <| "File" -> file, "Position" -> pos |> 101 | ] 102 | ] 103 | ]; 104 | 105 | ConsoleLog // catchUndefined; 106 | 107 | (* ::**********************************************************************:: *) 108 | (* ::Subsection::Closed:: *) 109 | (*consoleIndentSize*) 110 | consoleIndentSize[ Automatic ] := 2; 111 | consoleIndentSize[ other_ ] := other; 112 | 113 | (* ::**********************************************************************:: *) 114 | (* ::Subsection::Closed:: *) 115 | (*consoleOutputRule*) 116 | consoleOutputRule[ Automatic ] := 117 | OptionValue[ 118 | dnc`ConsolePrint, 119 | { }, 120 | "Output", 121 | Function[ v, "Output" :> v, HoldFirst ] 122 | ]; 123 | 124 | consoleOutputRule[ other_ ] := "Output" -> other; 125 | 126 | consoleOutputRule // catchUndefined; 127 | 128 | (* ::**********************************************************************:: *) 129 | (* ::Subsection::Closed:: *) 130 | (*withConsoleSettings*) 131 | withConsoleSettings // Attributes = { HoldRest }; 132 | 133 | withConsoleSettings[ { level_, type_ }, eval_ ] := 134 | withConsoleType[ 135 | type, 136 | withConsoleSettings0[ { level, dnc`$ConsoleType }, eval ] 137 | ]; 138 | 139 | withConsoleSettings // catchUndefined; 140 | 141 | 142 | withConsoleSettings0 // Attributes = { HoldRest }; 143 | 144 | withConsoleSettings0[ { "Notice", "Notebook" }, eval_ ] := 145 | Internal`InheritedBlock[ { dcc`nbLevel }, 146 | dcc`nbLevel[ "Notice" ] := dcc`nbLevel[ "Suggestion", "Notice" ]; 147 | eval 148 | ]; 149 | 150 | withConsoleSettings0[ { "Notice", "TTY" }, eval_ ] := 151 | Internal`InheritedBlock[ { dcc`ttyLevel }, 152 | dcc`ttyLevel[ "Notice" ] := dcc`ttyLevel[ "Suggestion", "Notice" ]; 153 | eval 154 | ]; 155 | 156 | withConsoleSettings0[ _, eval_ ] := eval; 157 | 158 | withConsoleSettings0 // Attributes = { HoldRest }; 159 | 160 | (* ::**********************************************************************:: *) 161 | (* ::Section::Closed:: *) 162 | (*ConsoleDebug*) 163 | ConsoleDebug // Options = { "ConsoleType" -> Automatic }; 164 | 165 | ConsoleDebug[ ___ ] /; ! TrueQ @ $consoleDebug := Null; 166 | 167 | ConsoleDebug[ expr_, a___, opts: OptionsPattern[ ] ] := 168 | catchTop @ ConsoleLog[ 169 | Unevaluated @ expr, 170 | a, 171 | "ConsoleType" -> OptionValue[ "ConsoleType" ], 172 | "Level" -> "Debug" 173 | ]; 174 | 175 | ConsoleDebug // catchUndefined; 176 | 177 | (* ::**********************************************************************:: *) 178 | (* ::Subsection::Closed:: *) 179 | (*$consoleDebug*) 180 | $consoleDebug := $Debug || StringQ @ Environment[ "GITHUB_WORKFLOW" ]; 181 | 182 | (* ::**********************************************************************:: *) 183 | (* ::Section::Closed:: *) 184 | (*ConsoleNotice*) 185 | ConsoleNotice // Options = { "ConsoleType" -> Automatic }; 186 | 187 | ConsoleNotice[ expr_, opts: OptionsPattern[ ] ] := 188 | catchTop @ ConsoleNotice[ Unevaluated @ expr, None, opts ]; 189 | 190 | ConsoleNotice[ expr_, file_, opts: OptionsPattern[ ] ] := 191 | catchTop @ ConsoleNotice[ Unevaluated @ expr, file, None, opts ]; 192 | 193 | ConsoleNotice[ expr_, file_, pos_, opts: OptionsPattern[ ] ] := 194 | catchTop @ ConsoleLog[ 195 | Unevaluated @ expr, 196 | file, 197 | pos, 198 | "ConsoleType" -> OptionValue[ "ConsoleType" ], 199 | "Level" -> "Notice" 200 | ]; 201 | 202 | ConsoleNotice // catchUndefined; 203 | 204 | (* ::**********************************************************************:: *) 205 | (* ::Section::Closed:: *) 206 | (*ConsoleWarning*) 207 | ConsoleWarning // Options = { "ConsoleType" -> Automatic }; 208 | 209 | ConsoleWarning[ expr_, opts: OptionsPattern[ ] ] := 210 | catchTop @ ConsoleWarning[ Unevaluated @ expr, None, opts ]; 211 | 212 | ConsoleWarning[ expr_, file_, opts: OptionsPattern[ ] ] := 213 | catchTop @ ConsoleWarning[ Unevaluated @ expr, file, None, opts ]; 214 | 215 | ConsoleWarning[ expr_, file_, pos_, opts: OptionsPattern[ ] ] := 216 | catchTop @ ConsoleLog[ 217 | Unevaluated @ expr, 218 | file, 219 | pos, 220 | "ConsoleType" -> OptionValue[ "ConsoleType" ], 221 | "Level" -> "Warning" 222 | ]; 223 | 224 | ConsoleWarning // catchUndefined; 225 | 226 | (* ::**********************************************************************:: *) 227 | (* ::Section::Closed:: *) 228 | (*ConsoleError*) 229 | ConsoleError::fatal = "Encountered a fatal error."; 230 | 231 | ConsoleError // Options = { 232 | "ConsoleType" -> Automatic, 233 | "Fatal" -> False 234 | }; 235 | 236 | ConsoleError[ expr_, opts: OptionsPattern[ ] ] := 237 | catchTop @ ConsoleError[ Unevaluated @ expr, None, opts ]; 238 | 239 | ConsoleError[ expr_, file_, opts: OptionsPattern[ ] ] := 240 | catchTop @ ConsoleError[ Unevaluated @ expr, file, None, opts ]; 241 | 242 | ConsoleError[ expr_, file_, pos_, opts: OptionsPattern[ ] ] := 243 | catchTop @ Module[ { res }, 244 | 245 | res = ConsoleLog[ 246 | Unevaluated @ expr, 247 | file, 248 | pos, 249 | "ConsoleType" -> OptionValue[ "ConsoleType" ], 250 | "Level" -> "Error" 251 | ]; 252 | 253 | If[ TrueQ @ OptionValue[ "Fatal" ], 254 | exitFailure[ ConsoleError::fatal, 1, res ], 255 | res 256 | ] 257 | ]; 258 | 259 | ConsoleError // catchUndefined; 260 | 261 | (* ::**********************************************************************:: *) 262 | (* ::Section::Closed:: *) 263 | (*Package Footer*) 264 | End[ ]; 265 | 266 | EndPackage[ ]; -------------------------------------------------------------------------------- /Kernel/SubmitPaclet.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | SubmitPaclet // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | $ContextAliases[ "dnc`" ] = "DefinitionNotebookClient`"; 11 | $ContextAliases[ "prdn`" ] = "PacletResource`DefinitionNotebook`"; 12 | $ContextAliases[ "rsc`" ] = "ResourceSystemClient`"; 13 | 14 | (* ::**********************************************************************:: *) 15 | (* ::Section::Closed:: *) 16 | (*SubmitPaclet*) 17 | SubmitPaclet::invfile = 18 | "`1` is not a valid definition notebook file or directory."; 19 | 20 | SubmitPaclet::errors = 21 | "Errors encountered while checking paclet."; 22 | 23 | SubmitPaclet::undefined = 24 | "Unhandled arguments for `1` in `2`."; 25 | 26 | SubmitPaclet::archive = 27 | "Could not find built paclet archive"; 28 | 29 | SubmitPaclet::PublisherID = 30 | "Invalid setting for PublisherID: `1`"; 31 | 32 | SubmitPaclet::ResourceSystemBase = 33 | "Invalid setting for ResourceSystemBase: `1`"; 34 | 35 | SubmitPaclet::PublisherToken = 36 | "Invalid setting for PublisherToken: `1`"; 37 | 38 | (* ::**********************************************************************:: *) 39 | (* ::Subsection::Closed:: *) 40 | (*Options*) 41 | SubmitPaclet // Options = { 42 | "ExitOnFail" -> Automatic, 43 | ConsoleType -> Automatic, 44 | PublisherID -> Automatic, 45 | PublisherToken -> Automatic, 46 | ResourceSystemBase -> Automatic, 47 | "SetWorkflowValue" -> True 48 | }; 49 | 50 | (* ::**********************************************************************:: *) 51 | (* ::Subsection::Closed:: *) 52 | (*Argument patterns*) 53 | $$spOpts = OptionsPattern[ SubmitPaclet ]; 54 | 55 | (* ::**********************************************************************:: *) 56 | (* ::Subsection::Closed:: *) 57 | (*Main definition*) 58 | SubmitPaclet[ opts: $$spOpts ] := 59 | catchTop @ SubmitPaclet[ File @ Directory[ ], opts ]; 60 | 61 | SubmitPaclet[ dir_File? DirectoryQ, opts: $$spOpts ] := 62 | catchTop @ SubmitPaclet[ findDefinitionNotebook @ dir, opts ]; 63 | 64 | SubmitPaclet[ file_File? defNBQ, opts: $$spOpts ] := 65 | catchTop @ usingFrontEnd @ withDNCSettings[ 66 | { OptionValue @ ConsoleType, "Submit" }, 67 | needs[ "ResourceSystemClient`" -> None ]; 68 | Block[ 69 | { 70 | $PublisherID = toPublisherID @ OptionValue @ PublisherID, 71 | $ResourceSystemBase = toRSB @ OptionValue @ ResourceSystemBase, 72 | rsc`$PublisherToken = toPToken @ OptionValue @ PublisherToken 73 | }, 74 | If[ rsc`$PublisherToken === None, 75 | ccPromptFix @ submitPaclet[ file, opts ], 76 | disableCloudConnect @ submitPaclet[ file, opts ] 77 | ] 78 | ] 79 | ]; 80 | 81 | (* ::**********************************************************************:: *) 82 | (* ::Subsection::Closed:: *) 83 | (*Error cases*) 84 | 85 | (* Invalid file specification: *) 86 | e: SubmitPaclet[ file: Except[ _File? defNBQ ], ___ ] := 87 | throwMessageFailure[ SubmitPaclet::invfile, file, HoldForm @ e ]; 88 | 89 | (* Invalid options specification: *) 90 | e: SubmitPaclet[ 91 | file_File? defNBQ, 92 | a: OptionsPattern[ ], 93 | inv: Except[ OptionsPattern[ ] ], 94 | ___ 95 | ] := 96 | throwMessageFailure[ 97 | SubmitPaclet::nonopt, 98 | HoldForm @ inv, 99 | 1 + Length @ HoldComplete @ a, 100 | HoldForm @ e 101 | ]; 102 | 103 | (* Unexpected arguments: *) 104 | e: SubmitPaclet[ ___ ] := 105 | throwMessageFailure[ SubmitPaclet::undefined, SubmitPaclet, HoldForm @ e ]; 106 | 107 | (* ::**********************************************************************:: *) 108 | (* ::Section::Closed:: *) 109 | (*Dependencies*) 110 | 111 | (* ::**********************************************************************:: *) 112 | (* ::Subsection::Closed:: *) 113 | (*submitPaclet*) 114 | submitPaclet[ file_File, opts___ ] := 115 | withTokenPublisher @ Module[ { nbo }, 116 | nbo = First[ Notebooks @ ExpandFileName @ file, $Failed ]; 117 | If[ MatchQ[ nbo, _NotebookObject ], 118 | submitPaclet[ nbo, opts ], 119 | openNotebookAndSubmit[ file, opts ] 120 | ] 121 | ]; 122 | 123 | submitPaclet[ nbo_NotebookObject, opts___ ] := Enclose[ 124 | Module[ { setWF, built, submitted }, 125 | LoadSubPackage[ "Wolfram`PacletCICD`BuildPaclet`" ]; 126 | built = buildPaclet[ nbo, opts ]; 127 | submitted = scrapeAndSubmit @ nbo; 128 | setWF = optionValue[ SubmitPaclet, { opts }, "SetWorkflowValue" ]; 129 | If[ setWF, ghSetWFOutput[ "SubmitPaclet", submitted ] ]; 130 | Confirm @ submitted 131 | ], 132 | exitFailure[ 133 | "SubmitPacletFailure", 134 | <| 135 | "MessageTemplate" -> "Failed to submit paclet.", 136 | "Result" -> # 137 | |> 138 | ] & 139 | ]; 140 | 141 | (* ::**********************************************************************:: *) 142 | (* ::Subsection::Closed:: *) 143 | (*openNotebookAndSubmit*) 144 | openNotebookAndSubmit[ file_, opts___ ] := 145 | Module[ { nbo }, 146 | needs[ "DefinitionNotebookClient`" -> None ]; 147 | hiddenDirectoryFix[ ]; 148 | WithCleanup[ 149 | dnc`BeginConsoleGroup[ "SubmitPaclet" ]; 150 | nbo = dnc`OpenTemporaryNotebook @ file, 151 | submitPaclet[ nbo, opts ], 152 | dnc`CloseTemporaryNotebook @ nbo; 153 | dnc`EndConsoleGroup[ "SubmitPaclet" ]; 154 | ] 155 | ]; 156 | 157 | openNotebookAndSubmit // catchUndefined; 158 | 159 | (* ::**********************************************************************:: *) 160 | (* ::Subsection::Closed:: *) 161 | (*toPublisherID*) 162 | toPublisherID[ pub_String ] := pub; 163 | toPublisherID[ Automatic ] := $PublisherID; 164 | toPublisherID[ other_ ] := exitFailure[ SubmitPaclet::PublisherID, other ]; 165 | toPublisherID // catchUndefined; 166 | 167 | (* ::**********************************************************************:: *) 168 | (* ::Subsection::Closed:: *) 169 | (*toRSB*) 170 | toRSB[ rsb_String ] := rsb; 171 | toRSB[ Automatic ] := $ResourceSystemBase; 172 | toRSB[ other_ ] := exitFailure[ SubmitPaclet::ResourceSystemBase, other ]; 173 | toRSB // catchUndefined; 174 | 175 | (* ::**********************************************************************:: *) 176 | (* ::Subsection::Closed:: *) 177 | (*toPToken*) 178 | toPToken[ Automatic ] := rsc`$PublisherToken; 179 | toPToken[ None ] := None; 180 | toPToken[ str_String ] := StringToByteArray @ str; 181 | toPToken[ ba_ByteArray ] := ba; 182 | toPToken[ other_ ] := exitFailure[ SubmitPaclet::PublisherToken, other ]; 183 | toPToken // catchUndefined; 184 | 185 | (* ::**********************************************************************:: *) 186 | (* ::Subsection::Closed:: *) 187 | (*scrapeAndSubmit*) 188 | scrapeAndSubmit[ nbo_NotebookObject ] := 189 | Enclose @ Module[ { dir, pac, ver, ro }, 190 | needs[ "DefinitionNotebookClient`" -> None ]; 191 | needs[ "PacletResource`DefinitionNotebook`" -> None ]; 192 | 193 | dir = ConfirmBy[ prdn`ScrapePacletDirectory @ nbo, DirectoryQ ]; 194 | pac = ConfirmBy[ PacletObject @ Flatten @ File @ dir, PacletObjectQ ]; 195 | ver = ConfirmBy[ pac[ "Version" ], StringQ ]; 196 | 197 | ro = ConfirmMatch[ 198 | dnc`ScrapeResource[ 199 | "Paclet", 200 | nbo, 201 | Interactive -> False, 202 | "ClickedButton" -> "Submit", 203 | "FailureCondition" -> "Error" 204 | ], 205 | _ResourceObject 206 | ]; 207 | 208 | ResourceSubmit[ ro, "Version" -> ver ] 209 | ]; 210 | 211 | scrapeAndSubmit // catchUndefined; 212 | 213 | (* ::**********************************************************************:: *) 214 | (* ::Subsection::Closed:: *) 215 | (*withTokenPublisher*) 216 | withTokenPublisher // Attributes = { HoldFirst }; 217 | 218 | withTokenPublisher[ eval_ ] := ( 219 | needs[ "ResourceSystemClient`" -> None ]; 220 | withTokenPublisher[ eval, $PublisherID, rsc`$PublisherToken ] 221 | ); 222 | 223 | withTokenPublisher[ eval_, _String, _ ] := eval; 224 | withTokenPublisher[ eval_, _ , None ] := eval; 225 | 226 | withTokenPublisher[ eval_, _, _String|_ByteArray ] := Enclose[ 227 | Module[ { info, publisher }, 228 | info = ConfirmBy[ getTokenInfo[ ], AssociationQ ]; 229 | publisher = ConfirmBy[ info[ "PublisherID" ], StringQ ]; 230 | Block[ { $PublisherID = publisher }, eval ] 231 | ], 232 | eval & 233 | ]; 234 | 235 | withTokenPublisher // catchUndefined; 236 | 237 | (* ::**********************************************************************:: *) 238 | (* ::Subsubsection::Closed:: *) 239 | (*getTokenInfo*) 240 | getTokenInfo[ ] :=( 241 | needs[ "ResourceSystemClient`" -> None ]; 242 | rsc`ResourceSystemExecute[ 243 | "CheckPublisherToken", 244 | { "Request" -> "Information" } 245 | ] 246 | ); 247 | 248 | (* ::**********************************************************************:: *) 249 | (* ::Section::Closed:: *) 250 | (*Package Footer*) 251 | End[ ]; 252 | 253 | EndPackage[ ]; -------------------------------------------------------------------------------- /Scripts/FormatNotebooks.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | (* ::Package:: *) 3 | 4 | BeginPackage[ "Wolfram`PacletCICD`Scripts`" ]; 5 | 6 | (* 7 | To configure this script to run automatically before each commit, run the 8 | following in the repository directory: 9 | ``` 10 | git config --local core.hooksPath Scripts/.githooks 11 | ``` 12 | *) 13 | 14 | (* :!CodeAnalysis::BeginBlock:: *) 15 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 16 | (* :!CodeAnalysis::Disable::NoSurroundingEnclose:: *) 17 | 18 | (* ::**********************************************************************:: *) 19 | (* ::Section::Closed:: *) 20 | (*Defaults*) 21 | $defaultRoot = DirectoryName[ $InputFileName, 2 ]; 22 | $defaultIgnore = FileNameJoin @ { $defaultRoot, "build" }; 23 | 24 | (* ::**********************************************************************:: *) 25 | (* ::Section::Closed:: *) 26 | (*Get arguments*) 27 | $root := $root = makeRootString @ getArg[ "root" , $defaultRoot ]; 28 | $ignore := $ignore = makeIgnorePatt @ getArg[ "ignore", $defaultIgnore ]; 29 | $strict := $strict = Interpreter[ "Boolean" ][ getArg[ "strict", "True" ] ]; 30 | 31 | (* ::**********************************************************************:: *) 32 | (* ::Section::Closed:: *) 33 | (*Definitions*) 34 | 35 | (* ::**********************************************************************:: *) 36 | (* ::Subsection::Closed:: *) 37 | (*getArg*) 38 | getArg[ name_String, default_: None ] := 39 | SelectFirst[ Flatten @ StringCases[ 40 | $ScriptCommandLine, 41 | "--" <> name <> "=" ~~ root___ ~~ EndOfString :> root 42 | ], 43 | StringQ, 44 | default 45 | ]; 46 | 47 | (* ::**********************************************************************:: *) 48 | (* ::Subsection::Closed:: *) 49 | (*enclose*) 50 | enclose // Attributes = { HoldAll }; 51 | enclose[ eval_ ] := enclose[ eval, "An unspecified error occurred." ]; 52 | enclose[ eval_, msg_ ] := enclose[ eval, msg, 1 ]; 53 | enclose[ eval_, msg_, n_ ] := Enclose[ 54 | eval, 55 | (Print @ ToString @ msg; If[ $strict, Exit @ n ]) & 56 | ]; 57 | 58 | (* ::**********************************************************************:: *) 59 | (* ::Subsection::Closed:: *) 60 | (*makeRootString*) 61 | makeRootString[ str_String ] := enclose[ 62 | ConfirmBy[ ExpandFileName @ StringTrim[ str, "\"" ], DirectoryQ ], 63 | StringForm[ "`1` is not a valid directory", str ] 64 | ]; 65 | 66 | (* ::**********************************************************************:: *) 67 | (* ::Subsection::Closed:: *) 68 | (*makeIgnorePatt*) 69 | makeIgnorePatt[ "None"|"none" ] := None; 70 | 71 | makeIgnorePatt[ str_String ] := 72 | Alternatives @@ (StringTrim[ #1, "\"" ] & /@ StringSplit[ str, "," ]); 73 | 74 | (* ::**********************************************************************:: *) 75 | (* ::Subsection::Closed:: *) 76 | (*getNotebookFiles*) 77 | getNotebookFiles[ ] := getNotebookFiles @ $root; 78 | getNotebookFiles[ root_ ] := getNotebookFiles[ root, $ignore ]; 79 | getNotebookFiles[ root_, None ] := FileNames[ "*.nb", root, Infinity ]; 80 | 81 | getNotebookFiles[ root_, ignore_ ] := 82 | getNotebookFiles[ root, ignore, getNotebookArgs[ ] ]; 83 | 84 | getNotebookFiles[ root_, ignore_, files: { ___String? FileExistsQ } ] := files; 85 | 86 | getNotebookFiles[ root_, ignore_, _ ] := 87 | Select[ FileNames[ "*.nb", root, Infinity ], 88 | Not @* StringMatchQ[ ignore~~___ ] 89 | ]; 90 | 91 | getNotebookArgs[ ] := getNotebookArgs @ getArg[ "notebooks", None ]; 92 | 93 | getNotebookArgs[ nbs_String ] := getNotebookArgs @ StringSplit[ nbs, "\n" ]; 94 | 95 | getNotebookArgs[ { files___String? FileExistsQ } ] := { files }; 96 | 97 | getNotebookArgs[ ___ ] := Missing[ "NotFound" ]; 98 | 99 | (* ::**********************************************************************:: *) 100 | (* ::Subsection::Closed:: *) 101 | (*print*) 102 | print[ ind_Integer, msg__ ] := 103 | Print[ StringJoin @ ConstantArray[ " ", ind ], 104 | DateString @ { "DateTimeShort", ".", "Millisecond" }, 105 | ": ", 106 | msg 107 | ]; 108 | 109 | print[ msg__ ] := print[ 0, msg ]; 110 | 111 | (* ::**********************************************************************:: *) 112 | (* ::Subsection::Closed:: *) 113 | (*makeUnreadable*) 114 | makeUnreadable[ file_ ] := enclose[ 115 | ConfirmBy[ Export[ file, Import[ file, "NB" ] ], FileExistsQ ], 116 | StringForm[ "Failed to unformat notebook: `1`", file ] 117 | ]; 118 | 119 | (******************************************************************************) 120 | (* ::Subsection::Closed:: *) 121 | (*makeReadable*) 122 | makeReadable[ file_ ] := makeReadable[ file, Hash @ ReadByteArray @ file ]; 123 | 124 | makeReadable[ file_, hash_Integer ] /; skipFormattingQ[ file, hash ] := 125 | print[ "Skipping: ", StringDelete[ file, StartOfString~~$root ] ]; 126 | 127 | makeReadable[ file_, hash_Integer ] := enclose[ 128 | Module[ { res }, 129 | print[ "Formatting: ", StringDelete[ file, StartOfString~~$root ] ]; 130 | res = ConfirmBy[ makeReadable[ file, hash, $overrideFormats ], 131 | FileExistsQ 132 | ]; 133 | saveHash @ file; 134 | res 135 | ], 136 | StringForm[ "Failed to format notebook: `1`", file ] 137 | ]; 138 | 139 | (* :!CodeAnalysis::Disable::VariableError::Block:: *) 140 | makeReadable[ file_, hash_, HoldComplete[ overrides___ ] ] := 141 | Internal`InheritedBlock[ { RawArray, NumericArray, overrides }, 142 | Unprotect[ RawArray, NumericArray, overrides ]; 143 | ReleaseHold[ overrideFormat /@ HoldComplete @ overrides ]; 144 | 145 | Format[ x_RawArray? rawArrayQ, InputForm ] := 146 | OutputForm[ "CompressedData[\"" <> Compress @ x <> "\"]" ]; 147 | 148 | Format[ x_NumericArray? numericArrayQ, InputForm ] := 149 | OutputForm[ "CompressedData[\"" <> Compress @ x <> "\"]" ]; 150 | 151 | ResourceFunction[ "SaveReadableNotebook" ][ 152 | unpackImages @ ReplaceAll[ 153 | DeleteCases[ 154 | Import[ file, "NB" ], 155 | TaggingRules -> { }, 156 | Infinity 157 | ], 158 | a: _RawArray | _NumericArray :> 159 | With[ { b = Check[ a, $Failed ] }, b /; ! FailureQ @ b ] 160 | ], 161 | file, 162 | "ExcludedNotebookOptions" -> { WindowMargins, WindowSize } 163 | ] 164 | ]; 165 | 166 | (* ::**********************************************************************:: *) 167 | (* ::Subsubsection::Closed:: *) 168 | (*skipFormattingQ*) 169 | skipFormattingQ[ file_, hash_Integer ] := 170 | skipFormattingQ[ file, hash, Hash[ file, "Expression", "HexString" ] ]; 171 | 172 | skipFormattingQ[ file_, hash_Integer, id_String ] := 173 | (PersistentSymbol[ "FormatNotebooks/FileHashes/" <> id ] === hash); 174 | 175 | (* ::**********************************************************************:: *) 176 | (* ::Subsubsection::Closed:: *) 177 | (*saveHash*) 178 | saveHash[ file_ ] := saveHash[ file, Hash @ ReadByteArray @ file ]; 179 | 180 | saveHash[ file_, hash_Integer ] := 181 | saveHash[ file, hash, Hash[ file, "Expression", "HexString" ] ]; 182 | 183 | saveHash[ file_, hash_Integer, id_String ] := 184 | (PersistentSymbol[ "FormatNotebooks/FileHashes/" <> id ] = hash); 185 | 186 | (* ::**********************************************************************:: *) 187 | (* ::Subsubsection::Closed:: *) 188 | (*rawArrayQ*) 189 | rawArrayQ // Attributes = { HoldFirst }; 190 | rawArrayQ[ arr_RawArray ] := Developer`RawArrayQ @ Unevaluated @ arr; 191 | 192 | (* ::**********************************************************************:: *) 193 | (* ::Subsubsection::Closed:: *) 194 | (*numericArrayQ*) 195 | numericArrayQ // Attributes = { HoldFirst }; 196 | numericArrayQ[ arr_RawArray ] := NumericArrayQ @ Unevaluated @ arr; 197 | 198 | (* ::**********************************************************************:: *) 199 | (* ::Subsubsection::Closed:: *) 200 | (*overrideFormat*) 201 | overrideFormat // Attributes = { HoldAllComplete }; 202 | 203 | overrideFormat[ sym_Symbol ] := ( 204 | Unprotect @ sym; 205 | Format[ x_sym, InputForm ] := 206 | OutputForm @ ToString @ Unevaluated @ FullForm @ x 207 | ); 208 | 209 | (* ::**********************************************************************:: *) 210 | (* ::Subsubsection::Closed:: *) 211 | (*unpackImages*) 212 | unpackImages[ expr_ ] := 213 | ReplaceAll[ 214 | expr, 215 | img_Image /; ImageQ @ Unevaluated @ img :> 216 | With[ { u = unpackImage @ img }, RuleCondition[ u, True ] ] 217 | ]; 218 | 219 | unpackImage[ img_Image, wrapper_: $ConditionHold ] := 220 | Module[ { type, arr, opts }, 221 | type = ImageType @ img; 222 | arr = NumericArray[ ImageData[ img, type ], type ]; 223 | opts = Sequence @@ Options @ img; 224 | Replace[ { arr, opts }, { a___ } :> wrapper @ Image @ a ] 225 | ]; 226 | 227 | (* ::**********************************************************************:: *) 228 | (* ::Subsubsection::Closed:: *) 229 | (*$overrideFormats*) 230 | $overrideFormats = HoldComplete[ 231 | Alternatives, 232 | Apply, 233 | Composition, 234 | Decrement, 235 | Increment, 236 | Map, 237 | MessageName, 238 | Not, 239 | Out, 240 | Part, 241 | Pattern, 242 | PatternTest, 243 | PreDecrement, 244 | PreIncrement, 245 | RightComposition 246 | ]; 247 | 248 | (* ::**********************************************************************:: *) 249 | (* ::Section::Closed:: *) 250 | (*Run*) 251 | 252 | If[ MemberQ[ $ScriptCommandLine, "--unformat" ], 253 | makeUnreadable /@ getNotebookFiles[ ], 254 | makeReadable /@ getNotebookFiles[ ] 255 | ] 256 | 257 | 258 | (* :!CodeAnalysis::EndBlock:: *) 259 | 260 | EndPackage[ ]; 261 | -------------------------------------------------------------------------------- /Kernel/ErrorHandling.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | PacletCICD // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | $ContextAliases[ "dnc`" ] = "DefinitionNotebookClient`"; 11 | 12 | (* ::**********************************************************************:: *) 13 | (* ::Section::Closed:: *) 14 | (*Definition Utilities*) 15 | 16 | (* ::**********************************************************************:: *) 17 | (* ::Subsection::Closed:: *) 18 | (*catchUndefined*) 19 | catchUndefined // Attributes = { HoldFirst }; 20 | 21 | catchUndefined[ sym_Symbol ] := 22 | catchUndefined [ sym, DownValues ]; 23 | 24 | catchUndefined[ sym_Symbol, DownValues ] := 25 | e: sym[ ___ ] := 26 | throwMessageFailure[ 27 | PacletCICD::undefined, 28 | HoldForm @ sym, 29 | HoldForm @ e, 30 | DownValues 31 | ]; 32 | 33 | catchUndefined[ sym_Symbol, SubValues ] := 34 | e: sym[ ___ ][ ___ ] := 35 | throwMessageFailure[ 36 | PacletCICD::undefined, 37 | HoldForm @ sym, 38 | HoldForm @ e, 39 | SubValues 40 | ]; 41 | 42 | (* ::**********************************************************************:: *) 43 | (* ::Section::Closed:: *) 44 | (*PacletCICD*) 45 | (* PacletCICD is just a symbol for attaching general messages *) 46 | PacletCICD::unknown = 47 | "An internal error occurred."; 48 | 49 | PacletCICD::undefined = 50 | "An internal error occurred (encountered unexpected pattern for `1`)."; 51 | 52 | PacletCICD::error = "`1`"; 53 | PacletCICD::warning = "`1`"; 54 | PacletCICD::notice = "`1`"; 55 | PacletCICD::debug = "`1`"; 56 | 57 | PacletCICD::empty = 58 | "An evaluation failed."; 59 | 60 | PacletCICD::tagged = 61 | "A failure of type \"`1`\" occurred."; 62 | 63 | PacletCICD::expression = 64 | "An evaluation failed with expression `1`."; 65 | 66 | PacletCICD::arguments = 67 | "An evaluation failed with arguments `1`."; 68 | 69 | PacletCICD::message = 70 | "`1`"; 71 | 72 | (* ::**********************************************************************:: *) 73 | (* ::Section::Closed:: *) 74 | (*Definitions*) 75 | 76 | (* ::**********************************************************************:: *) 77 | (* ::Subsection::Closed:: *) 78 | (*catchTop*) 79 | catchTop // Attributes = { HoldFirst }; 80 | 81 | catchTop[ eval_ ] /; $catching := eval; 82 | 83 | catchTop[ eval_ ] := 84 | Block[ { $catching = True, $messaged = False }, 85 | Catch[ eval, $top ] 86 | ]; 87 | 88 | catchTop // catchUndefined; 89 | 90 | (* ::**********************************************************************:: *) 91 | (* ::Subsection::Closed:: *) 92 | (*catch*) 93 | catch // Attributes = { HoldFirst }; 94 | 95 | catch[ eval_ ] := 96 | Block[ { $catching = True, $messaged = False }, 97 | noExit @ Catch[ eval, $top ] 98 | ]; 99 | 100 | catch // catchUndefined; 101 | 102 | (* ::**********************************************************************:: *) 103 | (* ::Subsection::Closed:: *) 104 | (*catchQuiet*) 105 | catchQuiet // Attributes = { HoldFirst }; 106 | catchQuiet[ eval_ ] := Quiet @ catch @ eval; 107 | catchQuiet // catchUndefined; 108 | 109 | (* ::**********************************************************************:: *) 110 | (* ::Subsection::Closed:: *) 111 | (*throwFailure*) 112 | throwFailure // Attributes = { HoldFirst }; 113 | throwFailure[ args___ ] := Quiet @ throwMessageFailure @ args; 114 | 115 | (* ::**********************************************************************:: *) 116 | (* ::Subsection::Closed:: *) 117 | (*throwMessageFailure*) 118 | throwMessageFailure // Attributes = { HoldFirst }; 119 | 120 | throwMessageFailure[ msg_MessageName, args___ ] := 121 | StackInhibit @ Module[ { failure }, 122 | failure = messageFailure[ msg, args ]; 123 | If[ TrueQ @ $catching, 124 | Throw[ failure, $top ], 125 | failure 126 | ] 127 | ]; 128 | 129 | throwMessageFailure[ failure_Failure ] := 130 | StackInhibit[ 131 | messageFailure @ failure; 132 | If[ TrueQ @ $catching, Throw[ failure, $top ], failure ] 133 | ]; 134 | 135 | throwMessageFailure[ msg_String ] := 136 | throwMessageFailure[ PacletCICD::error, msg ]; 137 | 138 | throwMessageFailure[ args___ ] := 139 | throwMessageFailure[ 140 | PacletCICD::unknown, 141 | HoldForm @ throwMessageFailure @ args 142 | ]; 143 | 144 | (* ::**********************************************************************:: *) 145 | (* ::Subsection::Closed:: *) 146 | (*messageFailure*) 147 | messageFailure := ( 148 | LoadSubPackage[ "MessageFailure" ]; 149 | 150 | SetOptions[ 151 | MessageFailure, 152 | "GeneralSymbol" -> PacletCICD, 153 | "MessagedFlag" :> $messaged 154 | ]; 155 | 156 | messageFailure = MessageFailure 157 | ); 158 | 159 | (* ::**********************************************************************:: *) 160 | (* ::Subsection::Closed:: *) 161 | (*joinFailure*) 162 | joinFailure[ Failure[ _, a_Association ], Failure[ tag_, b_Association ] ] := 163 | Module[ { as }, 164 | as = Join[ a, b ]; 165 | If[ AssociationQ @ as, 166 | Failure[ tag, as ], 167 | throwError[ "Expected an association in `1`.", as ] 168 | ] 169 | ]; 170 | 171 | joinFailure // catchUndefined; 172 | 173 | (* ::**********************************************************************:: *) 174 | (* ::Subsection::Closed:: *) 175 | (*generalMessage*) 176 | $$messageType = "error"|"warning"|"notice"|"debug"; 177 | 178 | generalMessage[ tag: $$messageType, template_, as_Association ] := 179 | StackInhibit @ messageFailure[ 180 | MessageName[ PacletCICD, tag ], 181 | TemplateApply[ template, as ], 182 | as 183 | ]; 184 | 185 | generalMessage[ tag: $$messageType, template_, a___ ] := 186 | StackInhibit @ messageFailure[ 187 | MessageName[ PacletCICD, tag ], 188 | TemplateApply[ template, { a } ], 189 | a 190 | ]; 191 | 192 | generalMessage[ template_, a___ ] := 193 | StackInhibit @ generalMessage[ "notice", template, a ]; 194 | 195 | generalMessage // catchUndefined; 196 | 197 | (* ::**********************************************************************:: *) 198 | (* ::Subsection::Closed:: *) 199 | (*throwGeneralMessage*) 200 | throwGeneralMessage[ tag: $$messageType, template_, a___ ] := 201 | StackInhibit @ Module[ { failure }, 202 | failure = generalMessage[ tag, template, a ]; 203 | If[ TrueQ @ $catching, 204 | Throw[ failure, $top ], 205 | failure 206 | ] 207 | ]; 208 | 209 | throwGeneralMessage[ template_, a___ ] := 210 | StackInhibit @ throwGeneralMessage[ "error", template, a ]; 211 | 212 | throwGeneralMessage // catchUndefined; 213 | 214 | (* ::**********************************************************************:: *) 215 | (* ::Subsection::Closed:: *) 216 | (*throwError*) 217 | throwError[ template_, a___ ] := 218 | StackInhibit @ throwGeneralMessage[ "error", template, a ]; 219 | 220 | (* ::**********************************************************************:: *) 221 | (* ::Subsection::Closed:: *) 222 | (*exitFailure*) 223 | exitFailure // Attributes = { HoldFirst }; 224 | 225 | exitFailure[ msg_MessageName, code_, result_ ] := ( 226 | If[ $EvaluationEnvironment === "Script" 227 | , 228 | Block[ { dnc`$ConsoleType = Automatic }, 229 | dnc`ConsolePrint[ 230 | ToString @ Unevaluated @ msg <> ": " <> ToString @ msg, 231 | "Level" -> "Error" 232 | ] 233 | ]; 234 | printShort @ result 235 | ]; 236 | exitOr[ code, throwMessageFailure[ msg, result ] ] 237 | ); 238 | 239 | exitFailure[ fail_Failure, code_Integer: 1 ] := ( 240 | If[ $EvaluationEnvironment === "Script" 241 | , 242 | Block[ { dnc`$ConsoleType = Automatic }, 243 | dnc`ConsolePrint[ 244 | fail[ "Message" ], 245 | "Level" -> "Error" 246 | ] 247 | ]; 248 | With[ { res = fail[ "Result" ] }, 249 | If[ MissingQ @ res, 250 | printShort @ fail, 251 | printShort @ res 252 | ] 253 | ] 254 | ]; 255 | exitOr[ code, throwMessageFailure @ fail ] 256 | ); 257 | 258 | exitFailure[ tag_String, as_Association, code_Integer: 1 ] := 259 | exitFailure[ Failure[ tag, as ], code ]; 260 | 261 | exitFailure // catchUndefined; 262 | 263 | (* ::**********************************************************************:: *) 264 | (* ::Subsubsection::Closed:: *) 265 | (*printShort*) 266 | printShort[ expr_ ] := printShort[ Unevaluated @ expr, 5 ]; 267 | printShort[ expr_, h_ ] := printShort[ Unevaluated @ expr, h, Automatic ]; 268 | 269 | printShort[ expr_, h_, Automatic ] := 270 | printShort[ Unevaluated @ expr, h, consoleWidth[ ] ]; 271 | 272 | printShort[ expr_, h_, w_ ] := 273 | ConsoleLog @ StringReplace[ 274 | ToString[ Unevaluated @ Short[ expr, h ], PageWidth -> w ], 275 | "\r\n" -> "\n" 276 | ]; 277 | 278 | (* ::**********************************************************************:: *) 279 | (* ::Subsubsubsection::Closed:: *) 280 | (*consoleWidth*) 281 | consoleWidth[ ] := consoleWidth @ Terminal`ConsoleSize[ ]; 282 | consoleWidth[ { _, w_Integer } ] := w; 283 | consoleWidth[ ___ ] := 80; 284 | 285 | (* ::**********************************************************************:: *) 286 | (* ::Subsection::Closed:: *) 287 | (*exitOr*) 288 | exitOr // Attributes = { HoldRest }; 289 | 290 | (* :!CodeAnalysis::BeginBlock:: *) 291 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 292 | exitOr[ code_, eval_ ] /; $noExit := eval; 293 | exitOr[ code_, eval_ ] /; $evaluationEnvironment === "Script" := Exit @ code; 294 | exitOr[ code_, eval_ ] := eval; 295 | (* :!CodeAnalysis::EndBlock:: *) 296 | 297 | $evaluationEnvironment := $EvaluationEnvironment; 298 | 299 | (* ::**********************************************************************:: *) 300 | (* ::Subsection::Closed:: *) 301 | (*noExit*) 302 | noExit // Attributes = { HoldFirst }; 303 | noExit[ eval_ ] := Block[ { $noExit = True }, eval ]; 304 | 305 | (* ::**********************************************************************:: *) 306 | (* ::Section::Closed:: *) 307 | (*Package Footer*) 308 | End[ ]; 309 | EndPackage[ ]; 310 | -------------------------------------------------------------------------------- /Kernel/BuildPaclet.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | BuildPaclet // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | $ContextAliases[ "dnc`" ] = "DefinitionNotebookClient`"; 11 | $ContextAliases[ "prdn`" ] = "PacletResource`DefinitionNotebook`"; 12 | $ContextAliases[ "pt`" ] = "PacletTools`"; 13 | 14 | (* ::**********************************************************************:: *) 15 | (* ::Section::Closed:: *) 16 | (*BuildPaclet*) 17 | BuildPaclet::invfile = 18 | "`1` is not a valid definition notebook file or directory."; 19 | 20 | BuildPaclet::errors = 21 | "Errors encountered while checking paclet."; 22 | 23 | BuildPaclet::undefined = 24 | "Unhandled arguments for `1` in `2`."; 25 | 26 | BuildPaclet::archive = 27 | "Could not find built paclet archive"; 28 | 29 | (* ::**********************************************************************:: *) 30 | (* ::Subsection::Closed:: *) 31 | (*Options*) 32 | BuildPaclet // Options = { 33 | "Check" -> False, 34 | "ConsoleType" -> Automatic, 35 | "ExitOnFail" -> Automatic, 36 | "SetWorkflowValue" -> True, 37 | "Target" -> "Build" 38 | }; 39 | 40 | (* ::**********************************************************************:: *) 41 | (* ::Subsection::Closed:: *) 42 | (*Argument patterns*) 43 | $$bpOpts = OptionsPattern @ { 44 | BuildPaclet, 45 | CheckPaclet, 46 | dnc`CheckDefinitionNotebook, 47 | prdn`BuildPaclet 48 | }; 49 | 50 | (* ::**********************************************************************:: *) 51 | (* ::Subsection::Closed:: *) 52 | (*Main definition*) 53 | BuildPaclet[ opts: $$bpOpts ] := 54 | catchTop @ BuildPaclet[ File @ Directory[ ], opts ]; 55 | 56 | BuildPaclet[ dir_File? DirectoryQ, opts: $$bpOpts ] := 57 | catchTop @ BuildPaclet[ findDefinitionNotebook @ dir, opts ]; 58 | 59 | BuildPaclet[ file_File? defNBQ, opts: $$bpOpts ] := 60 | catchTop @ usingFrontEnd @ withDNCSettings[ 61 | { OptionValue[ "ConsoleType" ], OptionValue[ "Target" ] }, 62 | Module[ { checked, built }, 63 | 64 | checked = If[ TrueQ @ OptionValue[ "Check" ], 65 | checkForBuild[ file, opts ], 66 | Missing[ "NotAvailable" ] 67 | ]; 68 | 69 | built = If[ FailureQ @ checked, 70 | Missing[ "CheckFailed" ], 71 | buildPaclet[ file, opts ] 72 | ]; 73 | 74 | combineBuildResult[ file, built, checked ] 75 | ] 76 | ]; 77 | 78 | (* ::**********************************************************************:: *) 79 | (* ::Subsection::Closed:: *) 80 | (*Error cases*) 81 | 82 | (* Invalid file specification: *) 83 | e: BuildPaclet[ file: Except[ _File? defNBQ ], ___ ] := 84 | throwMessageFailure[ BuildPaclet::invfile, file, HoldForm @ e ]; 85 | 86 | (* Invalid options specification: *) 87 | e: BuildPaclet[ 88 | file_File? defNBQ, 89 | a: OptionsPattern[ ], 90 | inv: Except[ OptionsPattern[ ] ], 91 | ___ 92 | ] := 93 | throwMessageFailure[ 94 | BuildPaclet::nonopt, 95 | HoldForm @ inv, 96 | 1 + Length @ HoldComplete @ a, 97 | HoldForm @ e 98 | ]; 99 | 100 | (* Unexpected arguments: *) 101 | e: BuildPaclet[ ___ ] := 102 | throwMessageFailure[ BuildPaclet::undefined, BuildPaclet, HoldForm @ e ]; 103 | 104 | (* ::**********************************************************************:: *) 105 | (* ::Subsection::Closed:: *) 106 | (*Dependencies*) 107 | 108 | $$cpOpts = OptionsPattern @ { 109 | CheckPaclet, 110 | dnc`CheckDefinitionNotebook 111 | }; 112 | 113 | (* ::**********************************************************************:: *) 114 | (* ::Subsubsection::Closed:: *) 115 | (*checkForBuild*) 116 | checkForBuild[ file_, opts___ ] := 117 | catch @ CheckPaclet[ file, filterOptions[ $$cpOpts, opts ] ]; 118 | 119 | checkForBuild // catchUndefined; 120 | 121 | (* ::**********************************************************************:: *) 122 | (* ::Subsubsection::Closed:: *) 123 | (*buildPaclet*) 124 | buildPaclet[ file_File, opts___ ] := 125 | Module[ { nbo }, 126 | nbo = First[ Notebooks @ ExpandFileName @ file, $Failed ]; 127 | If[ MatchQ[ nbo, _NotebookObject ], 128 | buildPaclet[ nbo, opts ], 129 | openNotebookAndBuild[ file, opts ] 130 | ] 131 | ]; 132 | 133 | buildPaclet[ nbo_NotebookObject, opts___ ] := 134 | Module[ { result, setWF }, 135 | needs[ "PacletResource`DefinitionNotebook`" -> None ]; 136 | 137 | result = pacletToolsMessageFix @ prdn`BuildPaclet[ 138 | nbo, 139 | filterOptions[ Interactive -> False, opts ] 140 | ]; 141 | 142 | setWF = optionValue[ BuildPaclet, { opts }, "SetWorkflowValue" ]; 143 | setGHBuildOutput[ result, setWF ] 144 | ]; 145 | 146 | buildPaclet // catchUndefined; 147 | 148 | (* ::**********************************************************************:: *) 149 | (* ::Subsubsection::Closed:: *) 150 | (*combineBuildResult*) 151 | combineBuildResult[ 152 | file_, 153 | b_, 154 | Failure[ "CheckPaclet::errors", as_Association ] 155 | ] := 156 | Failure[ 157 | "CheckPaclet::errors", 158 | Join[ 159 | KeyDrop[ as, "Result" ], 160 | <| 161 | "Source" -> file, 162 | "BuildResult" -> b, 163 | "CheckResult" -> Lookup[ as, "Result" ] 164 | |> 165 | ] 166 | ]; 167 | 168 | combineBuildResult[ file_, Success[ tag_, as_Association ], c_ ] := 169 | Success[ tag, Join[ as, <| "Source" -> file, "CheckResult" -> c |> ] ]; 170 | 171 | combineBuildResult[ file_, built_? FailureQ, checked_ ] := 172 | exitFailure[ 173 | "BuildPacletFailure", 174 | <| 175 | "MessageTemplate" -> "Failed to build Paclet.", 176 | "Source" -> file, 177 | "BuildResult" -> built, 178 | "CheckResult" -> checked 179 | |> 180 | ]; 181 | 182 | combineBuildResult[ file_, built_, checked_? FailureQ ] := 183 | exitFailure[ 184 | "CheckPacletFailure", 185 | <| 186 | "MessageTemplate" -> "Failed to check Paclet.", 187 | "Source" -> file, 188 | "BuildResult" -> built, 189 | "CheckResult" -> checked 190 | |> 191 | ]; 192 | 193 | combineBuildResult // catchUndefined; 194 | 195 | (* ::**********************************************************************:: *) 196 | (* ::Subsubsubsection::Closed:: *) 197 | (*openNotebookAndBuild*) 198 | openNotebookAndBuild[ file_, opts___ ] := 199 | Module[ { nbo }, 200 | needs[ "DefinitionNotebookClient`" -> None ]; 201 | hiddenDirectoryFix[ ]; 202 | WithCleanup[ 203 | dnc`BeginConsoleGroup[ "BuildPaclet" ]; 204 | nbo = dnc`OpenTemporaryNotebook @ file, 205 | buildPaclet[ nbo, opts ], 206 | dnc`CloseTemporaryNotebook @ nbo; 207 | dnc`EndConsoleGroup[ "BuildPaclet" ]; 208 | ] 209 | ]; 210 | 211 | (* ::**********************************************************************:: *) 212 | (* ::Subsubsection::Closed:: *) 213 | (*setGHBuildOutput*) 214 | setGHBuildOutput[ res_, set_ ] := Enclose[ 215 | If[ set, ghSetWFOutput[ "BuildPaclet", res ] ]; 216 | Confirm @ setGHBuildOutput0 @ res 217 | , 218 | exitFailure[ BuildPaclet::archive, 1, res ] & 219 | ]; 220 | 221 | setGHBuildOutput0[ KeyValuePattern[ "BuildResult"|"Result" -> res_ ] ] := 222 | setGHBuildOutput0 @ res; 223 | 224 | setGHBuildOutput0[ 225 | result: Success[ _, KeyValuePattern[ "PacletArchive" -> pa_ ] ] 226 | ] := 227 | Enclose @ Module[ { cStr, archive, file, pac, vers, full, buildDir, path }, 228 | 229 | cStr = ConfirmBy[ #, StringQ ] &; 230 | archive = ConfirmBy[ ExpandFileName @ pa, FileExistsQ ]; 231 | file = cStr @ checkPacArchiveExtension @ archive; 232 | pac = ConfirmBy[ PacletObject @ File @ file, PacletObjectQ ]; 233 | vers = cStr @ pac[ "Version" ]; 234 | full = cStr @ ExpandFileName @ file; 235 | buildDir = cStr @ ghRelativePath @ DirectoryName @ full; 236 | path = cStr @ ghRelativePath @ full; 237 | 238 | setOutput[ "PACLET_BUILD_DIR" , buildDir ]; 239 | setOutput[ "PACLET_PATH" , path ]; 240 | setOutput[ "PACLET_FILE" , cStr @ FileNameTake @ file ]; 241 | setOutput[ "PACLET_RELEASE_TAG", "v" <> vers ]; 242 | 243 | result 244 | ]; 245 | 246 | setGHBuildOutput0[ ___ ] := $Failed; 247 | 248 | (* ::**********************************************************************:: *) 249 | (* ::Subsubsubsection::Closed:: *) 250 | (*checkPacArchiveExtension*) 251 | checkPacArchiveExtension[ archive_? FileExistsQ ] := 252 | If[ ToLowerCase @ FileExtension @ archive === "paclet", 253 | archive, 254 | RenameFile[ 255 | archive, 256 | ExpandFileName @ archive <> ".paclet", 257 | OverwriteTarget -> True 258 | ] 259 | ]; 260 | 261 | checkPacArchiveExtension[ ___ ] := $Failed; 262 | 263 | (* ::**********************************************************************:: *) 264 | (* ::Subsection::Closed:: *) 265 | (*pacletToolsMessageFix*) 266 | pacletToolsMessageFix // Attributes = { HoldFirst }; 267 | 268 | (* :!CodeAnalysis::BeginBlock:: *) 269 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 270 | pacletToolsMessageFix[ eval_ ] := ( 271 | needs[ "PacletTools`" -> None ]; 272 | needs[ "PacletResource`DefinitionNotebook`" -> None ]; 273 | Internal`InheritedBlock[ { pt`PacletBuild, $Line }, 274 | Unprotect @ pt`PacletBuild; 275 | pt`PacletBuild // Options = DeleteDuplicates @ Append[ 276 | Options @ pt`PacletBuild, 277 | OverwriteTarget -> Automatic 278 | ]; 279 | Quiet[ 280 | eval, 281 | { 282 | FileHash::noopen, 283 | DocumentationBuild`DocumentationBuild::warning, 284 | DocumentationBuild`Info`GetNotebookHistoryData::notfound, 285 | DocumentationBuild`Utils`CreateInputForm::err, 286 | DocumentationBuild`Utils`CreateInputForm::str, 287 | DocumentationBuild`Utils`Localized::nokey 288 | } 289 | ] 290 | ] 291 | ); 292 | (* :!CodeAnalysis::EndBlock:: *) 293 | 294 | (* ::**********************************************************************:: *) 295 | (* ::Section::Closed:: *) 296 | (*Package Footer*) 297 | End[ ]; 298 | EndPackage[ ]; 299 | -------------------------------------------------------------------------------- /Kernel/CheckPaclet.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | CheckPaclet // ClearAll; 7 | 8 | Begin[ "`Private`" ]; 9 | 10 | $ContextAliases[ "dnc`" ] = "DefinitionNotebookClient`"; 11 | 12 | (* ::**********************************************************************:: *) 13 | (* ::Section::Closed:: *) 14 | (*CheckPaclet*) 15 | CheckPaclet::invfile = 16 | "`1` is not a valid definition notebook file or directory."; 17 | 18 | CheckPaclet::invfmt = 19 | "`1` is not a valid format specification."; 20 | 21 | CheckPaclet::errors = 22 | "Errors encountered while checking paclet."; 23 | 24 | CheckPaclet::undefined = 25 | "Unhandled arguments for `1` in `2`."; 26 | 27 | CheckPaclet::unknown = 28 | "An unexpected error occurred."; 29 | 30 | (* ::**********************************************************************:: *) 31 | (* ::Subsection::Closed:: *) 32 | (*Options*) 33 | CheckPaclet // Options = { 34 | "Target" -> "Submit", 35 | "DisabledHints" -> Automatic, 36 | "FailureCondition" -> "Error", 37 | "SetWorkflowValue" -> True 38 | (* TODO: MarkdownSummary option *) 39 | }; 40 | 41 | (* ::**********************************************************************:: *) 42 | (* ::Subsection::Closed:: *) 43 | (*Argument patterns*) 44 | $$hintProp = _String | All | Automatic | { ___String }; 45 | $$cpFMTName = "JSON"|"Dataset"|Automatic|None; 46 | $$cpFMT = $$cpFMTName | { $$cpFMTName, $$hintProp }; 47 | 48 | $$cpOpts = OptionsPattern @ { 49 | CheckPaclet, 50 | dnc`CheckDefinitionNotebook 51 | }; 52 | 53 | (* ::**********************************************************************:: *) 54 | (* ::Subsection::Closed:: *) 55 | (*Main definition*) 56 | CheckPaclet[ opts: $$cpOpts ] := 57 | catchTop @ CheckPaclet[ File @ Directory[ ], opts ]; 58 | 59 | CheckPaclet[ dir_File? DirectoryQ, opts: $$cpOpts ] := 60 | catchTop @ CheckPaclet[ findDefinitionNotebook @ dir, opts ]; 61 | 62 | CheckPaclet[ file_File, opts: $$cpOpts ] := 63 | catchTop @ CheckPaclet[ file, Automatic, opts ]; 64 | 65 | CheckPaclet[ file_File? defNBQ, fmt: $$cpFMT, opts: $$cpOpts ] := 66 | catchTop @ checkPaclet[ 67 | file, 68 | "DisabledHints" -> toDisabledHints @ OptionValue[ "DisabledHints" ], 69 | takeCheckDefNBOpts @ opts, 70 | "ConsoleType" -> Automatic, 71 | "ClickedButton" -> OptionValue[ "Target" ], 72 | "Format" -> toCheckFormat @ fmt, 73 | "FailureCondition" -> OptionValue[ "FailureCondition" ] 74 | ]; 75 | 76 | (* ::**********************************************************************:: *) 77 | (* ::Subsection::Closed:: *) 78 | (*Error cases*) 79 | 80 | (* Invalid file specification: *) 81 | e: CheckPaclet[ file: Except[ _File? defNBQ ], ___ ] := 82 | throwMessageFailure[ CheckPaclet::invfile, file, HoldForm @ e ]; 83 | 84 | (* Invalid format specification: *) 85 | e: CheckPaclet[ file_File? defNBQ, fmt: Except[ $$cpFMT ], ___ ] := 86 | throwMessageFailure[ CheckPaclet::invfmt, fmt, HoldForm @ e ]; 87 | 88 | (* Invalid options specification: *) 89 | e: CheckPaclet[ 90 | file_File? defNBQ, 91 | fmt: $$cpFMT, 92 | a: OptionsPattern[ ], 93 | inv: Except[ OptionsPattern[ ] ], 94 | ___ 95 | ] := 96 | throwMessageFailure[ 97 | CheckPaclet::nonopt, 98 | HoldForm @ inv, 99 | 2 + Length @ HoldComplete @ a, 100 | HoldForm @ e 101 | ]; 102 | 103 | (* Unexpected arguments: *) 104 | e: CheckPaclet[ ___ ] := 105 | throwMessageFailure[ CheckPaclet::undefined, CheckPaclet, HoldForm @ e ]; 106 | 107 | (* ::**********************************************************************:: *) 108 | (* ::Subsection::Closed:: *) 109 | (*Dependencies*) 110 | 111 | (* ::**********************************************************************:: *) 112 | (* ::Subsubsection::Closed:: *) 113 | (*checkPaclet*) 114 | checkPaclet[ nb_, opts___ ] := 115 | ccPromptFix @ Module[ { res, hints, data, setWF }, 116 | needs[ "DefinitionNotebookClient`" -> None ]; 117 | hiddenDirectoryFix[ ]; 118 | res = dnc`CheckDefinitionNotebook[ nb, opts ]; 119 | hints = $checkHintData; 120 | data = <| "Result" -> res, "HintData" -> hints, "File" -> nb |>; 121 | setWF = optionValue[ CheckPaclet, { opts }, "SetWorkflowValue" ]; 122 | If[ setWF, ghSetWFOutput[ "CheckPaclet", data ] ]; 123 | generateCheckReport @ data; 124 | checkExit @ res 125 | ]; 126 | 127 | (* ::**********************************************************************:: *) 128 | (* ::Subsubsection::Closed:: *) 129 | (*$checkHintData*) 130 | $checkHintData := withConsoleType[ 131 | Automatic, 132 | DeleteMissing /@ dnc`HintData[ 133 | "Paclet", 134 | None, 135 | { "Tag", "Level", "Message", "CellID", "SourcePosition" } 136 | ] 137 | ]; 138 | 139 | (* ::**********************************************************************:: *) 140 | (* ::Subsubsection::Closed:: *) 141 | (*generateCheckReport*) 142 | generateCheckReport[ KeyValuePattern[ "HintData" -> { } ] ] := 143 | appendStepSummary @ ToMarkdownString @ { 144 | Style[ "Check paclet results", "Section" ], 145 | ":white_check_mark: No issues found." 146 | }; 147 | 148 | generateCheckReport[ KeyValuePattern @ { 149 | "HintData" -> hints_, 150 | "File" -> file_ 151 | } ] := 152 | Enclose @ Module[ { job, index, head, title, grid, md }, 153 | job = ConfirmBy[ Environment[ "GITHUB_JOB" ], StringQ ]; 154 | index = ConfirmBy[ notebookCellIDIndex @ file, AssociationQ ]; 155 | head = Style[ #, Bold ] & /@ { "Level", "Tag", "Message", "Link" }; 156 | title = Style[ "Definition Notebook (" <> job <> ")", "Section" ]; 157 | grid = Grid @ Prepend[ reportHintRow[ file, index ] /@ hints, head ]; 158 | md = ConfirmBy[ ToMarkdownString @ { title, grid }, StringQ ]; 159 | appendStepSummary @ md 160 | ]; 161 | 162 | generateCheckReport // catchUndefined; 163 | 164 | (* ::**********************************************************************:: *) 165 | (* ::Subsubsubsection::Closed:: *) 166 | (*reportHintRow*) 167 | reportHintRow[ file_, index_ ][ hint_Association ] := 168 | Enclose @ Module[ { lookup, level, tag, msg, url, link }, 169 | lookup = ConfirmBy[ Lookup[ hint, # ], StringQ ] &; 170 | level = hintIcon @ lookup[ "Level" ]; 171 | tag = lookup[ "Tag" ]; 172 | msg = Style[ lookup[ "Message" ], "Text" ]; 173 | url = sourceFileURL[ file, index, hint ]; 174 | link = Hyperlink[ ":link:", url ]; 175 | { level, tag, msg, link } 176 | ]; 177 | 178 | (* ::**********************************************************************:: *) 179 | (* ::Subsubsubsection::Closed:: *) 180 | (*sourceFileURL*) 181 | sourceFileURL[ nbFile_, cellIndex_, hint_ ] := 182 | With[ { url = ghCommitFileURL @ hint }, 183 | url /; StringQ @ url 184 | ]; 185 | 186 | sourceFileURL[ nbFile_, cellIndex_, hint_ ] := 187 | Enclose @ Module[ { id, pos, url }, 188 | id = ConfirmBy[ Lookup[ hint, "CellID" ], IntegerQ ]; 189 | pos = Lookup[ cellIndex, id ]; 190 | ghCommitFileURL[ nbFile, pos ] 191 | ]; 192 | 193 | sourceFileURL // catchUndefined; 194 | 195 | (* ::**********************************************************************:: *) 196 | (* ::Subsubsubsection::Closed:: *) 197 | (*hintIcon*) 198 | hintIcon[ "Error" ] := ":x: Error"; 199 | hintIcon[ "Warning" ] := ":warning: Warning"; 200 | hintIcon[ "Suggestion" ] := ":grey_question: Suggestion"; 201 | hintIcon[ other_ ] := other; 202 | hintIcon // catchUndefined; 203 | 204 | (* ::**********************************************************************:: *) 205 | (* ::Subsubsection::Closed:: *) 206 | (*checkExit*) 207 | checkExit[ Failure[ "FailureCondition", as_Association ] ] := 208 | exitFailure[ 209 | "CheckPaclet::errors", 210 | Association[ 211 | "MessageTemplate" :> CheckPaclet::errors, 212 | "MessageParameters" :> { }, 213 | KeyTake[ as, { "FailureCondition", "Result" } ] 214 | ], 215 | 1 216 | ]; 217 | 218 | checkExit[ res_? FailureQ ] := 219 | exitFailure[ CheckPaclet::unknown, 1, res ]; 220 | 221 | checkExit[ result_ ] := result; 222 | 223 | (* ::**********************************************************************:: *) 224 | (* ::Subsubsection::Closed:: *) 225 | (*takeCheckDefNBOpts*) 226 | takeCheckDefNBOpts[ opts: $$cpOpts ] := ( 227 | needs[ "DefinitionNotebookClient`" -> None ]; 228 | filterOptions[ dnc`CheckDefinitionNotebook, opts ] 229 | ); 230 | 231 | (* ::**********************************************************************:: *) 232 | (* ::Subsubsection::Closed:: *) 233 | (*toDisabledHints*) 234 | toDisabledHints[ Automatic|Inherited ] := ( 235 | needs[ "DefinitionNotebookClient`" -> None ]; 236 | toDisabledHints @ Flatten @ { 237 | dnc`$DisabledHints, 238 | "PacletRequiresBuild", 239 | "PacletFileChanged", 240 | "PacletFilesChanged", 241 | $eventDisabledHints 242 | } 243 | ); 244 | 245 | toDisabledHints[ tag_String ] := 246 | Map[ <| "MessageTag" -> tag, "Level" -> #1, "ID" -> All |> &, 247 | { "Suggestion", "Warning", "Error" } 248 | ]; 249 | 250 | toDisabledHints[ as: KeyValuePattern[ "MessageTag" -> _ ] ] := 251 | { as }; 252 | 253 | toDisabledHints[ as: KeyValuePattern[ "Tag" -> tag_ ] ] := 254 | { Append[ as, "MessageTag" -> tag ] }; 255 | 256 | toDisabledHints[ hints_List ] := 257 | DeleteDuplicates @ Flatten[ toDisabledHints /@ hints ]; 258 | 259 | toDisabledHints[ ___ ] := { }; 260 | 261 | 262 | $eventDisabledHints := eventDisabledHints @ Environment[ "GITHUB_EVENT_NAME" ]; 263 | 264 | eventDisabledHints[ "schedule" ] := { "PacletVersionUnchanged" }; 265 | eventDisabledHints[ "workflow_dispatch" ] := { "PacletVersionUnchanged" }; 266 | eventDisabledHints[ ___ ] := { }; 267 | 268 | (* ::**********************************************************************:: *) 269 | (* ::Subsubsection::Closed:: *) 270 | (*toCheckFormat*) 271 | toCheckFormat[ None ] := None; 272 | toCheckFormat[ fmt: $$cpFMTName ] := { fmt, $defaultHintProps }; 273 | toCheckFormat[ fmt_ ] := fmt; 274 | 275 | $defaultHintProps = { "Level", "Message", "Tag", "CellID" }; 276 | 277 | (* ::**********************************************************************:: *) 278 | (* ::Subsubsubsection::Closed:: *) 279 | (*disableTag*) 280 | disableTag[ tag_ ] := 281 | Map[ <| "MessageTag" -> tag, "Level" -> #1, "ID" -> All |> &, 282 | { "Suggestion", "Warning", "Error" } 283 | ]; 284 | 285 | (* ::**********************************************************************:: *) 286 | (* ::Section::Closed:: *) 287 | (*Package Footer*) 288 | End[ ]; 289 | EndPackage[ ]; -------------------------------------------------------------------------------- /Kernel/ScriptConfirmation.wl: -------------------------------------------------------------------------------- 1 | (* ::**********************************************************************:: *) 2 | (* ::Section::Closed:: *) 3 | (*Package Header*) 4 | BeginPackage[ "Wolfram`PacletCICD`" ]; 5 | 6 | ClearAll[ 7 | ScriptConfirm, 8 | ScriptConfirmAssert, 9 | ScriptConfirmBy, 10 | ScriptConfirmMatch 11 | ]; 12 | 13 | Begin[ "`Private`" ]; 14 | 15 | (* ::**********************************************************************:: *) 16 | (* ::Section::Closed:: *) 17 | (*ScriptConfirm*) 18 | ScriptConfirm // Attributes = { HoldAll, SequenceHold }; 19 | 20 | (* ::**********************************************************************:: *) 21 | (* ::Subsection::Closed:: *) 22 | (*Messages*) 23 | ScriptConfirm::Session = 24 | "ScriptConfirm encountered a failure in an evaluation environment other than \ 25 | \"Script\". Aborting instead of quitting with exit code `1`."; 26 | 27 | ScriptConfirm::ArgumentCount = 28 | "ScriptConfirm called with `1` arguments; between 1 and 3 arguments are \ 29 | expected."; 30 | 31 | (* ::**********************************************************************:: *) 32 | (* ::Subsection::Closed:: *) 33 | (*Main Definition*) 34 | 35 | (* Default message template: *) 36 | ScriptConfirm[ expr_ ] := 37 | ScriptConfirm[ expr, $scriptConfirmTemplate ]; 38 | 39 | (* Default exit code: *) 40 | ScriptConfirm[ expr_, template_ ] := 41 | ScriptConfirm[ expr, template, 1 ]; 42 | 43 | (* Automatic message template: *) 44 | ScriptConfirm[ expr_, Automatic, code_ ] := 45 | ScriptConfirm[ expr, $scriptConfirmTemplate, code ]; 46 | 47 | ScriptConfirm[ expr_, template_, code_ ] := 48 | catchTop @ With[ { result = expr }, 49 | If[ successfulQ @ result, 50 | result, 51 | scriptFail @ <| 52 | "ConfirmationType" -> ScriptConfirm, 53 | "ExitCode" -> code, 54 | "Expression" -> HoldForm @ result, 55 | "Input" -> HoldForm @ expr, 56 | "MessageTemplate" -> template 57 | |> 58 | ] 59 | ]; 60 | 61 | (* Invalid arguments: *) 62 | ScriptConfirm[ args___ ] := 63 | catchTop @ With[ { len = Length @ HoldComplete @ args }, 64 | messageFailure[ ScriptConfirm::ArgumentCount, len ] 65 | ]; 66 | 67 | (* ::**********************************************************************:: *) 68 | (* ::Subsubsection::Closed:: *) 69 | (*$scriptConfirmTemplate*) 70 | $scriptConfirmTemplate = "ScriptConfirm: `Expression` encountered."; 71 | 72 | (* ::**********************************************************************:: *) 73 | (* ::Section::Closed:: *) 74 | (*ScriptConfirmAssert*) 75 | ScriptConfirmAssert // Attributes = { HoldAll, SequenceHold }; 76 | 77 | (* ::**********************************************************************:: *) 78 | (* ::Subsection::Closed:: *) 79 | (*Messages*) 80 | ScriptConfirmAssert::Session = 81 | "ScriptConfirmAssert encountered an assertion failure in an evaluation \ 82 | environment other than \"Script\". Aborting instead of quitting with exit \ 83 | code `1`."; 84 | 85 | ScriptConfirmAssert::ArgumentCount = 86 | "ScriptConfirmAssert called with `1` arguments; between 1 and 3 arguments are \ 87 | expected."; 88 | 89 | (* ::**********************************************************************:: *) 90 | (* ::Subsection::Closed:: *) 91 | (*Main Definition*) 92 | 93 | (* Default message template: *) 94 | ScriptConfirmAssert[ expr_ ] := 95 | ScriptConfirmAssert[ expr, $scriptConfirmAssertTemplate ]; 96 | 97 | (* Default exit code: *) 98 | ScriptConfirmAssert[ expr_, template_ ] := 99 | ScriptConfirmAssert[ expr, template, 1 ]; 100 | 101 | (* Automatic message template: *) 102 | ScriptConfirmAssert[ expr_, Automatic, code_ ] := 103 | ScriptConfirmAssert[ expr, $scriptConfirmAssertTemplate, code ]; 104 | 105 | ScriptConfirmAssert[ expr_, template_, code_ ] := 106 | catchTop @ With[ { result = expr }, 107 | If[ TrueQ @ result, 108 | Null, 109 | scriptFail @ <| 110 | "ConfirmationType" -> ScriptConfirmAssert, 111 | "ExitCode" -> code, 112 | "Expression" -> HoldForm @ result, 113 | "Input" -> HoldForm @ expr, 114 | "MessageTemplate" -> template 115 | |> 116 | ] 117 | ]; 118 | 119 | (* Invalid arguments: *) 120 | ScriptConfirmAssert[ args___ ] := 121 | catchTop @ With[ { len = Length @ HoldComplete @ args }, 122 | messageFailure[ ScriptConfirmAssert::ArgumentCount, len ] 123 | ]; 124 | 125 | (* ::**********************************************************************:: *) 126 | (* ::Subsubsection::Closed:: *) 127 | (*$scriptConfirmAssertTemplate*) 128 | $scriptConfirmAssertTemplate = "ScriptConfirmAssert: Assertion `Input` failed."; 129 | 130 | (* ::**********************************************************************:: *) 131 | (* ::Section::Closed:: *) 132 | (*ScriptConfirmBy*) 133 | ScriptConfirmBy // Attributes = { HoldAll, SequenceHold }; 134 | 135 | (* ::**********************************************************************:: *) 136 | (* ::Subsection::Closed:: *) 137 | (*Messages*) 138 | ScriptConfirmBy::Session = 139 | "ScriptConfirmBy encountered a failure in an evaluation environment other than \ 140 | \"Script\". Aborting instead of quitting with exit code `1`."; 141 | 142 | ScriptConfirmBy::ArgumentCount = 143 | "ScriptConfirmBy called with `1` arguments; between 2 and 4 arguments are \ 144 | expected."; 145 | 146 | (* ::**********************************************************************:: *) 147 | (* ::Subsection::Closed:: *) 148 | (*Main Definition*) 149 | 150 | (* Default message template: *) 151 | ScriptConfirmBy[ expr_, f_ ] := 152 | ScriptConfirmBy[ expr, f, $scriptConfirmByTemplate ]; 153 | 154 | (* Default exit code: *) 155 | ScriptConfirmBy[ expr_, f_, template_ ] := 156 | ScriptConfirmBy[ expr, f, template, 1 ]; 157 | 158 | (* Automatic message template: *) 159 | ScriptConfirmBy[ expr_, f_, Automatic, code_ ] := 160 | ScriptConfirmBy[ expr, f, $scriptConfirmByTemplate, code ]; 161 | 162 | ScriptConfirmBy[ expr_, f_, template_, code_ ] := 163 | catchTop @ With[ { result = expr }, 164 | If[ TrueQ @ f @ result, 165 | result, 166 | scriptFail @ <| 167 | "ConfirmationType" -> ScriptConfirmBy, 168 | "ExitCode" -> code, 169 | "Expression" -> HoldForm @ result, 170 | "Function" -> HoldForm @ f, 171 | "Input" -> HoldForm @ expr, 172 | "MessageTemplate" -> template 173 | |> 174 | ] 175 | ]; 176 | 177 | (* Invalid arguments: *) 178 | ScriptConfirmBy[ args___ ] := 179 | catchTop @ With[ { len = Length @ HoldComplete @ args }, 180 | messageFailure[ ScriptConfirmBy::ArgumentCount, len ] 181 | ]; 182 | 183 | (* ::**********************************************************************:: *) 184 | (* ::Subsubsection::Closed:: *) 185 | (*$scriptConfirmByTemplate*) 186 | $scriptConfirmByTemplate = 187 | "ScriptConfirmBy: `Function`[`Expression`] did not return True."; 188 | 189 | (* ::**********************************************************************:: *) 190 | (* ::Section::Closed:: *) 191 | (*ScriptConfirmMatch*) 192 | ScriptConfirmMatch // Attributes = { HoldAll, SequenceHold }; 193 | 194 | (* ::**********************************************************************:: *) 195 | (* ::Subsection::Closed:: *) 196 | (*Messages*) 197 | ScriptConfirmMatch::Session = 198 | "ScriptConfirmMatch encountered a failure in an evaluation environment other than \ 199 | \"Script\". Aborting instead of quitting with exit code `1`."; 200 | 201 | ScriptConfirmMatch::ArgumentCount = 202 | "ScriptConfirmMatch called with `1` arguments; between 2 and 4 arguments are \ 203 | expected."; 204 | 205 | (* ::**********************************************************************:: *) 206 | (* ::Subsection::Closed:: *) 207 | (*Main Definition*) 208 | 209 | (* Default message template: *) 210 | ScriptConfirmMatch[ expr_, patt_ ] := 211 | ScriptConfirmMatch[ expr, patt, $scriptConfirmMatchTemplate ]; 212 | 213 | (* Default exit code: *) 214 | ScriptConfirmMatch[ expr_, patt_, template_ ] := 215 | ScriptConfirmMatch[ expr, patt, template, 1 ]; 216 | 217 | (* Automatic message template: *) 218 | ScriptConfirmMatch[ expr_, patt_, Automatic, code_ ] := 219 | ScriptConfirmMatch[ expr, patt, $scriptConfirmMatchTemplate, code ]; 220 | 221 | ScriptConfirmMatch[ expr_, patt_, template_, code_ ] := 222 | catchTop @ With[ { result = expr }, 223 | If[ MatchQ[ result, patt ], 224 | result, 225 | scriptFail @ <| 226 | "ConfirmationType" -> ScriptConfirmMatch, 227 | "ExitCode" -> code, 228 | "Expression" -> HoldForm @ result, 229 | "Pattern" -> HoldForm @ patt, 230 | "Input" -> HoldForm @ expr, 231 | "MessageTemplate" -> template 232 | |> 233 | ] 234 | ]; 235 | 236 | (* Invalid arguments: *) 237 | ScriptConfirmMatch[ args___ ] := 238 | catchTop @ With[ { len = Length @ HoldComplete @ args }, 239 | messageFailure[ ScriptConfirmMatch::ArgumentCount, len ] 240 | ]; 241 | 242 | (* ::**********************************************************************:: *) 243 | (* ::Subsubsection::Closed:: *) 244 | (*$scriptConfirmMatchTemplate*) 245 | $scriptConfirmMatchTemplate = 246 | "ScriptConfirmMatch: `Expression` does not match `Pattern`."; 247 | 248 | (* ::**********************************************************************:: *) 249 | (* ::Section::Closed:: *) 250 | (*Common*) 251 | 252 | (* ::**********************************************************************:: *) 253 | (* ::Subsection::Closed:: *) 254 | (*scriptFail*) 255 | scriptFail[ as: KeyValuePattern[ "MessageTemplate" -> tmp_ ] ] := ( 256 | ConsoleError @ TemplateApply[ 257 | tmp, 258 | as, 259 | InsertionFunction -> toConsoleString 260 | ]; 261 | scriptExit @ as 262 | ); 263 | 264 | (* ::**********************************************************************:: *) 265 | (* ::Subsection::Closed:: *) 266 | (*scriptExit*) 267 | (* :!CodeAnalysis::BeginBlock:: *) 268 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 269 | scriptExit[ head_Symbol, code_Integer ] := 270 | If[ $EvaluationEnvironment === "Script", 271 | Exit @ code, 272 | messageFailure[ head::Session, code ]; 273 | Abort[ ] 274 | ]; 275 | (* :!CodeAnalysis::EndBlock:: *) 276 | 277 | scriptExit[ as_Association ] := 278 | scriptExit[ Lookup[ as, "ConfirmationType" ], Lookup[ as, "ExitCode" ] ]; 279 | 280 | scriptExit[ head_, _ ] := scriptExit[ head, 1 ]; 281 | 282 | scriptExit // catchUndefined; 283 | 284 | (* ::**********************************************************************:: *) 285 | (* ::Section::Closed:: *) 286 | (*Package Footer*) 287 | End[ ]; 288 | 289 | EndPackage[ ]; --------------------------------------------------------------------------------