├── Dockerfile ├── src ├── main.sh ├── install_dependencies.wls └── build_paclet.wls ├── action.yml ├── LICENSE └── README.md /Dockerfile: -------------------------------------------------------------------------------- 1 | # Container image that runs your code 2 | FROM wolframresearch/wolframengine:latest 3 | 4 | COPY ["src", "/src/"] 5 | 6 | USER root 7 | RUN chmod +x /src/main.sh 8 | 9 | ENTRYPOINT ["/src/main.sh"] -------------------------------------------------------------------------------- /src/main.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPT_DIR=$(dirname ${0}) 4 | 5 | wolfram_script () { 6 | wolframscript -file "${1}" 7 | WS_EXIT_CODE=$(echo $?) 8 | case $WS_EXIT_CODE in 9 | 0) 10 | ;; 11 | 139) 12 | echo "::warning::Warning: wolframscript did not exit cleanly" 13 | ;; 14 | *) 15 | exit $WS_EXIT_CODE 16 | ;; 17 | esac 18 | } 19 | 20 | echo "::group::Installing dependencies..." 21 | wolfram_script "${SCRIPT_DIR}/install_dependencies.wls" 22 | echo "::endgroup::" 23 | 24 | echo "::group::Building Paclet..." 25 | wolfram_script "${SCRIPT_DIR}/build_paclet.wls" 26 | echo "::endgroup::" -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Build Paclet' 2 | description: 'Build a paclet using its definition notebook file' 3 | branding: 4 | icon: 'package' 5 | color: 'orange' 6 | inputs: 7 | target: 8 | description: 'The target' 9 | required: false 10 | default: 'Submit' 11 | check: 12 | description: 'Whether to check for errors first' 13 | required: false 14 | default: 'true' 15 | debug: 16 | description: 'Whether to enable additional debug output' 17 | required: false 18 | default: 'false' 19 | definition_notebook: 20 | description: 'Path to the definition notebook' 21 | required: false 22 | default: './ResourceDefinition.nb' 23 | paclet_cicd_version: 24 | description: 'Version of PacletCICD to use' 25 | required: false 26 | default: 'latest' 27 | resource_system_base: 28 | description: 'Specifies the location of the resource system' 29 | required: false 30 | default: 'https://www.wolframcloud.com/obj/resourcesystem/api/1.0' 31 | runs: 32 | using: 'docker' 33 | image: 'Dockerfile' 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/install_dependencies.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | (* ::Package:: *) 3 | 4 | (* :!CodeAnalysis::BeginBlock:: *) 5 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 6 | 7 | print[ a___ ] := WriteString[ "stdout", a, "\n" ]; 8 | 9 | getInput[ name_String ] := 10 | Module[ { val }, 11 | val = Environment[ "INPUT_" <> name ]; 12 | print[ name, "=", val ]; 13 | val 14 | ]; 15 | 16 | 17 | pacletInstall[ url_String? urlQ ] := Enclose[ 18 | Module[ { installed }, 19 | print[ "Installing paclet from: ", url ]; 20 | installed = PacletInstall[ url, ForceVersionInstall -> True ]; 21 | If[ PacletObjectQ @ installed, 22 | print[ "Paclet installed to: ", installed[ "Location" ] ], 23 | print[ "::error:: Paclet installation failed: ", installed ]; 24 | Exit[ 1 ] 25 | ] 26 | ], 27 | Function[ 28 | print[ "::error:: Paclet installation failed: ", url ]; 29 | Exit[ 1 ] 30 | ] 31 | ]; 32 | 33 | 34 | urlQ[ url_String ] := StringQ @ URLParse[ url, "Scheme" ]; 35 | urlQ[ ___ ] := False; 36 | 37 | 38 | toPacletCICDURL[ "latest" ] := 39 | Part[ 40 | URLExecute[ 41 | "https://api.github.com/repos/WolframResearch/PacletCICD/releases/latest", 42 | "RawJSON" 43 | ], 44 | "assets", 45 | 1, 46 | "browser_download_url" 47 | ]; 48 | 49 | toPacletCICDURL[ vers_String ] := 50 | If[ StringMatchQ[ vers, (DigitCharacter..~~".")...~~(DigitCharacter..) ], 51 | TemplateApply[ 52 | "https://github.com/WolframResearch/PacletCICD/releases/download/v`1`/Wolfram__PacletCICD-`1`.paclet", 53 | { vers } 54 | ], 55 | TemplateApply[ 56 | "https://github.com/WolframResearch/PacletCICD/archive/refs/heads/`1`.zip", 57 | vers 58 | ] 59 | ]; 60 | 61 | 62 | pacVer = getInput[ "PACLET_CICD_VERSION" ]; 63 | pacURL = toPacletCICDURL @ pacVer; 64 | 65 | If[ ! StringQ @ pacURL, 66 | Print[ "Could not determine URL for specified version" ]; 67 | Exit[ 1 ] 68 | ]; 69 | 70 | pacletInstall @ pacURL; 71 | 72 | (* :!CodeAnalysis::EndBlock:: *) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build Paclet for the [Wolfram Language Paclet Repository](https://resources.wolframcloud.com/PacletRepository/) 2 | 3 | The Build Paclet action is an interface to the 4 | [`BuildPaclet`](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD/ref/BuildPaclet.html) 5 | function from 6 | [Wolfram/PacletCICD](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD/) 7 | and can be used to build your Wolfram Language paclet from source code within GitHub Actions. 8 | This is roughly equivalent to using the **Build > All** menu item from the resource definition notebook within 9 | Wolfram Desktop or Mathematica. 10 | 11 | ## Usage 12 | 13 | A YAML file that uses this action can be automatically generated for your paclet using 14 | [`WorkflowExport`](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD/ref/WorkflowExport.html): 15 | 16 | ```Mathematica 17 | PacletSymbol["Wolfram/PacletCICD", "WorkflowExport"]["path/to/paclet", "Build"] 18 | ``` 19 | 20 | Alternatively, using GitHub actions YAML syntax directly: 21 | ```yaml 22 | name: Build Paclet 23 | on: [push] 24 | jobs: 25 | Build: 26 | name: Build Paclet 27 | runs-on: ubuntu-latest 28 | container: 29 | image: wolframresearch/wolframengine:latest 30 | options: --user root 31 | env: 32 | WOLFRAMSCRIPT_ENTITLEMENTID: ${{ secrets.WOLFRAMSCRIPT_ENTITLEMENTID }} 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v2 36 | - name: Build paclet 37 | uses: WolframResearch/build-paclet@v1 38 | - name: UploadArtifact 39 | uses: actions/upload-artifact@v3 40 | with: 41 | path: ${{ env.PACLET_BUILD_DIR }} # set during the build-paclet step 42 | ``` 43 | 44 | ## Parameters 45 | 46 | Input | Default | Description 47 | ------------------------- | --------------------------- | --------------- 48 | `check` | `true` | Whether to check the paclet for errors prior to building 49 | `target` | `"Submit"` | The named configuration to use for error checking when `check` is `true`. Some possible values are `"Build"`, `"Check"`, `"Deploy"`, and `"Submit"`. 50 | `definition_notebook` | `"./ResourceDefinition.nb"` | The relative path to the paclet's resource definition notebook 51 | `paclet_cicd_version` | `"latest"` | The version of [PacletCICD](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD/) to use 52 | 53 | ## Notes 54 | For this action to work, your repository needs to have a license entitlement ID defined as a repository secret. See [this tutorial](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD/tutorial/GitHubActionsQuickStart.html) for details. 55 | 56 | 57 | ## See Also 58 | 59 | - [Action for checking your paclet for potential issues](https://github.com/WolframResearch/check-paclet) 60 | - [Action for submitting your paclet to the Wolfram Language Paclet Repository](https://github.com/WolframResearch/submit-paclet) 61 | - [Action for running your paclet test files](https://github.com/WolframResearch/test-paclet) 62 | - [Continuous Integration and Deployment for Wolfram Language Paclets](https://resources.wolframcloud.com/PacletRepository/resources/Wolfram/PacletCICD/) -------------------------------------------------------------------------------- /src/build_paclet.wls: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env wolframscript 2 | (* ::Package:: *) 3 | 4 | (* :!CodeAnalysis::BeginBlock:: *) 5 | (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) 6 | 7 | (* ::**********************************************************************:: *) 8 | (* ::Section::Closed:: *) 9 | (*Initialization*) 10 | print[ a___ ] := WriteString[ "stdout", a, "\n" ]; 11 | 12 | print[ "Loading Wolfram`PacletCICD` from ", FindFile[ "Wolfram`PacletCICD`" ] ]; 13 | 14 | Needs[ "Wolfram`PacletCICD`" ]; 15 | 16 | getInput[ name_String ] := 17 | Module[ { val }, 18 | val = Environment[ "INPUT_" <> name ]; 19 | print[ name, "=", val ]; 20 | val 21 | ]; 22 | 23 | print[ "Building paclet..." ]; 24 | 25 | (* ::**********************************************************************:: *) 26 | (* ::Section::Closed:: *) 27 | (*Inputs*) 28 | 29 | (* ::**********************************************************************:: *) 30 | (* ::Subsection::Closed:: *) 31 | (*Check*) 32 | check = Interpreter[ "Boolean" ][ getInput[ "CHECK" ] ]; 33 | If[ ! BooleanQ @ check, 34 | print[ "::error::Expected a boolean value for input 'check' instead of ", 35 | getInput[ "CHECK" ] 36 | ]; 37 | Exit[ 1 ] 38 | ]; 39 | 40 | (* ::**********************************************************************:: *) 41 | (* ::Subsection::Closed:: *) 42 | (*Debug*) 43 | debug = Interpreter[ "Boolean" ][ getInput[ "DEBUG" ] ]; 44 | If[ ! BooleanQ @ debug, 45 | print[ "::error::Expected a boolean value for input 'debug' instead of ", 46 | getInput[ "DEBUG" ] 47 | ]; 48 | Exit[ 1 ] 49 | ]; 50 | 51 | (* ::**********************************************************************:: *) 52 | (* ::Subsection::Closed:: *) 53 | (*Definition Notebook*) 54 | defNB = getInput[ "DEFINITION_NOTEBOOK" ]; 55 | If[ ! FileExistsQ @ ExpandFileName @ defNB, 56 | print[ "::error::Definition notebook not found: ", defNB ]; 57 | Exit[ 1 ] 58 | ]; 59 | 60 | (* ::**********************************************************************:: *) 61 | (* ::Subsection::Closed:: *) 62 | (*ResourceSystemBase*) 63 | rsBase = getInput[ "RESOURCE_SYSTEM_BASE" ]; 64 | rsTestURL = URLBuild @ { rsBase, "TestSystem" }; 65 | rsBaseTest = URLFetch[ rsTestURL, { "StatusCode", "Content" } ]; 66 | If[ ! MatchQ[ rsBaseTest, { 200, _String } ], 67 | print[ "::error::Invalid ResourceSystemBase: ", rsBase ]; 68 | print[ "::error::ResourceSystemBase test output: ", rsBaseTest ]; 69 | Exit[ 1 ] 70 | ]; 71 | 72 | Needs[ "ResourceSystemClient`" -> None ]; 73 | $ResourceSystemBase = rsBase; 74 | 75 | (* ::**********************************************************************:: *) 76 | (* ::Subsection::Closed:: *) 77 | (*Target*) 78 | target = getInput[ "TARGET" ]; 79 | 80 | (* ::**********************************************************************:: *) 81 | (* ::Section::Closed:: *) 82 | (*Run*) 83 | result = 84 | Block[ 85 | { 86 | Print = print, 87 | DefinitionNotebookClient`BeginConsoleGroup, 88 | DefinitionNotebookClient`EndConsoleGroup 89 | }, 90 | Wolfram`PacletCICD`BuildPaclet[ 91 | File @ defNB, 92 | "Check" -> check, 93 | "Target" -> target, 94 | "Debug" -> debug, 95 | "ConsoleType" -> "GitHub" 96 | ] 97 | ]; 98 | 99 | print @ result; 100 | 101 | If[ MatchQ[ result, _Wolfram`PacletCICD`BuildPaclet ], 102 | print[ "::error::Wolfram`PacletCICD`BuildPaclet not defined" ]; 103 | Exit[ 1 ] 104 | ]; 105 | 106 | (* :!CodeAnalysis::EndBlock:: *) --------------------------------------------------------------------------------