WelcomeΒΆ

Hello Builder!

Scripting is a small, free and important piece of DkSDK.

A few clicks from your web browser and four (4) minutes later you and your Windows and macOS users can start scripting with DkCoder. And all users, including glibc-based Linux desktop users, can use their Unix shells or Windows PowerShell. Nothing needs to be pre-installed on Windows and macOS. Just copy and paste two lines (you'll see examples soon) and your script is running and your project is editable with an LSP-capable IDE like Visual Studio Code.

Unlike most scripting frameworks, DkCoder solves the problem of scale: you start with small scripts that do immediately useful things for you and your team, and when inevitably you need to expand, distribute or embed those scripts to make full-featured applications, you don't need to throw out what you have already written. DkCoder is a re-imagining of the scripting experience that re-uses the best historical ideas:

  1. You don't write build files. If that sounds like Unix /bin/sh or the Windows Command Prompt, that is intentional.
  2. Most files you write can be immediately run. If that sounds like how Python scripts are almost indistinguishable from Python modules, or like JavaScript modules, that is intentional.
  3. Most files you write can be referenced with a fully-qualified name. If that sounds like Java packages and how that has been proven to scale to large code bases, that is intentional.
  4. Your scripts play well together and don't bit rot. It is conventional to add static typing (Typescript, mypy) when scripting projects get large. DkCoder has type-safety from Day One that is safer and easier to use.

You'll start with the mouse-click install and the basic one-liner:

let () = Tr1Stdlib_V414Io.StdIo.print_endline "Hello Builder!"

which you run with a single command:

./dk DkRun_V2_2.Run -- DkHelloScript_Std.AndHello
Hello Builder!

You'll do a quick tour of the prior art where we will acknowledge situations when DkCoder is not the right tool for you.

You'll see three examples of scripts:

  1. the integration test script that produces this documentation page
  2. a 2D game to show non-traditional uses of scripts and the re-use of existing code
  3. the security-conscious production service managing subscriptions for DkSDK Pricing

Along the way you'll encounter a small language made by a community who can write some very good libraries. And a software kit that gets out the way and makes good software accessible.

Let's begin!

You, your co-developers and your users can start scripting in a couple clicks.ΒΆ

FIRST, if you don't have these installed then install Git from https://git-scm.com/downloads and Visual Studio Code from https://code.visualstudio.com/download .

Then click this link: Clone and Open DkHelloScript in Visual Studio Code .

SECOND, open src/DkHelloScript_Std/AndHello.ml in your IDE or open src/DkHelloScript_Std/AndHello.ml in your browser.

You should see:

open Tr1Stdlib_V414Io

let () = StdIo.print_endline "Hello Builder!"

THIRD, open a Terminal > New Terminal and run:

./dk DkRun_V2_2.Run -- DkHelloScript_Std.AndHello
Hello Builder!

βœ”οΈ DONE! Ok, one-liners are not very interesting.

But ... that should have only taken a few minutes.

And ... the same command works on Windows, macOS and GNU/Linux.

Reproducibility or quick typing? Pick oneΒΆ

We specified a version number and a double dash separator (DkRun_V2_2.Run --) in our last example:

./dk DkRun_V2_2.Run -- DkHelloScript_Std.AndHello

You can relax your fingers by instead typing:

./dk DkHelloScript_Std.AndHello

In your everyday scripting you won't want to type DkRun_V2_2.Run --.

Leave it out.

However, when you are publishing documentation (like this!) you should always include the version number. You'll find it easy for your users to copy-and-paste. More importantly, the behavior of your scripts won't change in some future version of ./dk.

Focus on what you are runningΒΆ

Open src/DkHelloScript_Std/AndHelloAgain.ml in your IDE.

It has the same content as DkHelloScript_Std/AndHello.ml. However, it has red squiggly lines.

The red is an indication that DkCoder has not compiled the script. That is a good thing because generally you don't want to waste time compiling every script every time you make an edit in a large project.

Now run the DkHelloScript_Std.AndHelloAgain script with:

./dk DkRun_V2_2.Run -- DkHelloScript_Std.AndHelloAgain

Visual Studio Code should remove the red in a minute.

DkCoder optimizes for rapid iterative development by:

  1. Only compiling the script that you last ran (ex. DkHelloScript_Std/AndHelloAgain.ml). If your script requires other scripts to run, those are also compiled.
  2. Compiling all the scripts in a project when you first open Visual Studio Code. That means you can browse your project when you first start your day without seeing any red. Then, when you have found a script you want to edit or a new script to add, edit and run that script repeatedly throughout the day.

Basic shell scriptingΒΆ

In an earlier section an OCaml programming language environment was transparently downloaded for you. You will be using OCaml in this walkthrough, even though you may find other DkSDK documentation that uses DkCoder with C and Java. You can proceed through this walkthrough without knowing OCaml.

In this section we'll be using the shexp library that was created by Jane Street Capital . If you are familiar with traditional shell scripts like bash you'll find shexp more powerful.

Open src/DkHelloScript_Std/B43Shell/B35Shexp/B43Countdown.ml in your IDE or open src/DkHelloScript_Std/B43Shell/B35Shexp/B43Countdown.ml in your browser.

You should see:

open Tr1Shexp_Std.Shexp_process
open Tr1Shexp_Std.BindsShexp

(* Counts down from n with a one second delay between ticks *)
let rec countdown (n : int) : unit t =
  if n > 0 then begin
    echo (string_of_int n) ;%bind
    sleep 1.0 ;%bind
    countdown (n - 1)
  end
  else echo "Done countdown. Bye Builder!"

let main_t : unit t =
  echo "Hello Builder! ..." ;%bind
  echo "------------------------------------------------------------" ;%bind
  echo "Starting countdown..." ;%bind
  countdown 5

let () = if Tr1EntryName.module_id = __MODULE_ID__ then eval main_t

And running it with:

./dk DkRun_V2_2.Run -- DkHelloScript_Std.B43Shell.B35Shexp.B43Countdown

gives:

Hello Builder! ...
------------------------------------------------------------
Starting countdown...
5
4
3
2
1
Done countdown. Bye Builder!

Prior ArtΒΆ

This is only a quick tour of the most popular alternatives to DkCoder. Most of the advantages for DkCoder boil down to it works on Windows and Unix without pre-installing other packages.

  • Shell scripts like /bin/bash. A POSIX-compatible shell script is pre-installed on almost all Unix distributions. By comparison, DkCoder has a POSIX shell script launcher that transparently installs pre-compiled binaries for macOS and the major Linux desktop distributions. However, DkCoder has expanded reach with its Windows shell script launcher that transparently installs on Windows (and Unix) machines.
  • PowerShell. The scripts are written in a full-featured programming language, and in that respect PowerShell is similar to DkCoder. Furthermore, PowerShell has an command shell that is a complete alternative to both the Command Prompt on Windows and /bin/sh on Unix. DkCoder does not have a command shell. However, DkCoder scripts work on both Windows and Unix without pre-installing the PowerShell distribution, so DkCoder may be easier to adopt.
  • Perl. CPAN gives Perl a huge standardized package registry with standardized package tools. By comparison, DkCoder has no packages (today!) in its package registry. However, DkCoder will soon have an alternative to package registries that is expected to be the main form of sharing until DkCoder is out of its infancy stage. Also, as with shell scripts, Perl is pre-installed on almost all Unix distributions. However, DkCoder has expanded reach with its Windows and Unix shell script launcher.
  • Python. Most of the advantages and disadvantages for Perl apply to Python. Python is less ubiquitous but more popular than Perl. DkCoder scripting should have a similar learning curve to Python, as evidenced by its early use by high-schoolers.
  • Fortran. The DkCoder author (me!) has never written a line of Fortran, but its "dynamic dependency" system of Fortran files containing declarations of which Fortran modules are used is similar to DkCoder. DkCoder goes a step further and drops the need to declare dependencies that are available in the DkRegistry.
  • etc.

There are a few reasons not to use DkCoder:

  • OCaml has memory inefficiences due to garbage collection, boxing, cache non-locality and wide native types. This would affect you if your application is memory-constrained rather than CPU or IO-constrained. The main mitigation is that using OCaml FFI to interface with memory efficient languages like C and Rust is not overly complex.
  • The OCaml ecosystem is small and biased towards 64-bit Linux applications. This would affect you if you develop applications on Windows or for mobile devices. The main mitigation is to use cross-platform distributions like esy, DkML and DkSDK.
  • The company behind DkCoder is very small (at times only one FTE). In particular, some features like fast native compilation are going to take substantial time to deliver. The main mitigation is that DkSDK customers get source code rights for life, and can prioritize their wishlist with sponsored feature development.
  • DkCoder is currently in Alpha. Major features like auto-downloading of other scripts are almost feature complete but paused while we collect Alpha feedback. The main mitigation is to wait if you need post-Alpha features.
  • The licensing of OCaml (LGPL) and the broader OCaml ecosystem (many GPL) may be too restrictive for some companies to adopt, especially in comparison to many modern (Rust, Go) or dominant (C, Python, JavaScript) ecosystems. The main mitigation is to strategically choose either static and shared linking (tools in DkSDK CMake and DkSDK FFI C make this easier).

Integration TestingΒΆ

Let's run a script that is simultaneously both an integration test and source of documentation:

./dk DkRun_V2_2.Run DkHelloScript_Std.Y33Article --serve

While that is running, open your web browser to http://localhost:8080

You will see a preview of the documentation you are reading right now! The documentation you are reading was a side-effect of running integration tests. Open src/DkHelloScript_Std/Y33Article.ml in your IDE or open src/DkHelloScript_Std/Y33Article.ml in your browser.

You should see:

open Tr1Tezt_C.Tezt
open Tr1Htmlit_Std.Htmlit

(** {1 Register Tests incl. Documentation and Server}

    If you are familiar with React, you can "push down" functions and values
    so that your deeper content can render itself. We do the same thing
    here, and that is why you see all the [~make_capture ~run_dk] and other
    parameters.

    We could have also used a `src/DkHelloScript_Std/open__.ml` file
    to provide values to all the scripts of the [DkHelloScript_Std] library.
    If you are familiar with React, that "global" style of distributing
    values would be called a "context".

    Even simpler would be to create functions inside [Doc] since
    [Y33ArticleX/Doc] is visible to the [Y33ArticleX/Section*] scripts.

    If possible find a module like [Doc] that is visible to all the scripts
    that need it, and place your functions in the module. We don't do it
    in this project except for one function {!ucodeblock} for demonstration
    purposes.

    If that doesn't work for the way your have organized your project code,
    prefer the "push down" method over the "global context". It is simpler
    to test, your functions will be re-usable outside of the current
    library (ex. outside of [DkHelloScript_Std]), and you won't pull in
    unnecessary dependencies for scripts that don't need it.

    You can avoid "unused parameters" errors with the "push down" method
    by attaching the following extension on your "push down" functions:

    [[
      let register_before_tests (* ... *) =
         (* ... *)
         unit
         [@@warning "-unused-var-strict"]
    ]] *)

let title_section () =
  Y33ArticleX.Doc.(
    append
      (usection
         [ ucontainer
             El.
               [p ~at:[At.class' "subtitle"] [txt "DkCoder: Scripting at Scale"]]
         ] ) )

let () =
  if Tr1EntryName.module_id = __MODULE_ID__ then begin
    (* SECTIONS *)
    let andhello_file = AndHello.__FILE__ in
    let andhello_module = AndHello.__MODULE_ID__ in
    title_section () ;
    Y33ArticleX.S004Intro.register_before_tests ~andhello_module () ;
    Y33ArticleX.S008StartScript.register_before_tests ~andhello_module
      ~andhello_file () ;
    Y33ArticleX.S012Reproducib.register_before_tests ~andhello_module () ;
    Y33ArticleX.S016Focus.register_before_tests ~andhello_file
      ~andhelloagain_module:AndHelloAgain.__MODULE_ID__
      ~andhelloagain_file:AndHelloAgain.__FILE__ () ;
    Y33ArticleX.S020RuntimeReqs.register_before_tests () ;
    Y33ArticleX.S022RuntimeSys.register_before_tests () ;
    Y33ArticleX.S024BasShell.register_before_tests
      ~shexpcountdown_module:B43Shell.Index.B35Shexp.B43Countdown.__MODULE_ID__
      ~shexpcountdown_file:B43Shell.Index.B35Shexp.B43Countdown.__FILE__ () ;
    Y33ArticleX.S028PriorArt.register_before_tests () ;
    Y33ArticleX.S032Testing.register_before_tests ~article_module:__MODULE_ID__
      ~article_file:__FILE__ () ;
    Y33ArticleX.S036FlairGraph.register_before_tests
      ~boguetiny_module:B57Graphics.Index.B43Bogue.B43Tiny.__MODULE_ID__
      ~boguetiny_file:B57Graphics.Index.B43Bogue.B43Tiny.__FILE__ () ;
    Y33ArticleX.S038SnokeGame.register_before_tests () ;
    (* Y33ArticleX.S040ReusingYouScripts.register_before_tests () ; *)
    (* Y33ArticleX.S044ListingThirdParties.register_before_tests () ; *)
    (* Y33ArticleX.S048UsingListedThirdParties.register_before_tests () ; *)
    (* Y33ArticleX.S052FunBreak.register_before_tests () ; *)
    (* Y33ArticleX.S056UsingRegThirdParties.register_before_tests () ; *)
    (* Y33ArticleX.S060DkRegistry.register_before_tests () ; *)
    Y33ArticleX.S064MakeProject.register_before_tests () ;
    Y33ArticleX.S066Production.register_before_tests () ;
    Y33ArticleX.S068Parties.register_before_tests () ;
    Y33ArticleX.S072Stdlib.register_before_tests () ;
    Y33ArticleX.S076RuntimeLibs.register_before_tests () ;
    Y33ArticleX.S080InducLimits.register_before_tests () ;
    Y33ArticleX.S084EarlyLimits.register_before_tests () ;
    Y33ArticleX.S088SecDesign.register_before_tests () ;
    Y33ArticleX.S092InTouch.register_before_tests () ;
    (* HTTP SERVER *)
    Y33ArticleX.Httpd.register_before_tests () ;
    (* DOCUMENTATION PRINTING *)
    Y33ArticleX.Doc.register_before_tests () ;
    (* RUN TESTS *)
    Test.run ()
  end

The test infrastructure is provided by the module Tr1Tezt_C.Tezt. We recommend you read the Announcing Tezt by Nomadic Labs article to see what it can do and why it was developed.

I wrote the module DkHelloScript_Std.Y33ArticleX.Doc in a very "scripty" style: it incrementally builds documentation in memory. It renders to either HTML or Markdown, and makes use of the Bulma CSS Framework for styling and layout. You can copy and customize the module in your own scripts. For those of you familiar with JavaScript and the DOM , the incremental approach is similar to how your web browser renders a web page. For everybody else, just think of a file being created in-memory the first time DkHelloScript_Std.Y33ArticleX.Doc is accessed, and through the use of several helper functions that write ("append") fragments at the end of the file, the DkHelloScript_Std.Y33ArticleX.Doc.register_before_tests () is able to print a complete documentation file to the console or a real file.

The title_section () function is the first documentation fragment that is built. You should recognize it on the top of this documentation page.

Each section of the documentation has its own test script which registers its own integration test.

Once all of the tests are registered, they are all run with the OCaml function call:

Test.run ()

Let's drill down into this section of the documentation. Open src/DkHelloScript_Std/Y33ArticleX/S032Testing.ml in your IDE or open src/DkHelloScript_Std/Y33ArticleX/S032Testing.ml in your browser.

open Tr1Tezt_C.Tezt
open Tr1Tezt_C.Tezt.Base
open Tr1Htmlit_Std.Htmlit
module Printf = Tr1Stdlib_V414CRuntime.Printf

let register_before_tests ~article_module ~article_file () =
  Test.register ~__FILE__ ~title:"testing" ~tags:[]
  @@ fun () ->
  let self_FILE = __FILE__ in
  let open Doc in
  let open El in
  append
    (usection
       [ ucontainer
           [ ucard ~title:"Integration Testing" []
           ; upar
               [ txt
                   "Let's run a script that is simultaneously both an \
                    integration test and source of documentation:" ]
           ; ucodeblock `Shell
               (Printf.sprintf "./dk %s %s --serve" Tr1Version.run_module
                  article_module )
           ; upar
               [ txt "While that is running, open your web browser to "
               ; ulink ~url:"http://localhost:8080" "http://localhost:8080" ]
           ; upar
               [ txt
                   "You will see a preview of the documentation you are \
                    reading right now! "
               ; unsafe_raw
                   "<strong>The documentation you are reading is a side-effect \
                    of running integration tests.</strong>" ]
           ; ucodeaction ~ide:(txt "Open") ~browser:(Some "open") article_file
           ; txt "You should see:"
           ; ucodefile article_file
           ; upar
               [ txt "The test infrastructure is provided by the module "
               ; ucode "Tr1Tezt_C.Tezt"
               ; txt ". We recommend you read the "
               ; ulink
                   ~url:
                     "https://research-development.nomadic-labs.com/announcing-tezt.html"
                   "Announcing Tezt by Nomadic Labs"
               ; txt " article to see what it can do and why it was developed. "
               ]
           ; uinfo
               [ txt
                   "DkCoder has Tezt pre-installed, so you won't have to \
                    create 'dune' files or do any 'dune build' steps that \
                    regular OCaml developers would have to do. To use Tezt, \
                    follow the examples that are given to you in this \
                    documentation." ]
           ; upar
               [ txt "I wrote the module "
               ; ucode Doc.__MODULE_ID__
               ; txt
                   " in a very \"scripty\" style: it incrementally builds \
                    documentation in memory. "
               ; txt
                   "It renders to either HTML or Markdown, and makes use of \
                    the "
               ; ulink ~url:"https://bulma.io/" "Bulma CSS Framework"
               ; txt " for styling and layout. "
               ; txt
                   "You can copy and customize the module in your own scripts. "
               ; txt "For those of you familiar with "
               ; ulink
                   ~url:
                     "https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction"
                   "JavaScript and the DOM"
               ; txt
                   ", the incremental approach is similar to how your web \
                    browser renders a web page. For everybody else, just think \
                    of a file being created in-memory the first time "
               ; ucode Doc.__MODULE_ID__
               ; txt
                   " is accessed, and through the use of several helper \
                    functions that write (\"append\") fragments at the end of \
                    the file, the "
               ; ucode
                   (Printf.sprintf "%s.register_before_tests ()"
                      Doc.__MODULE_ID__ )
               ; txt
                   " is able to print a complete documentation file to the \
                    console or a real file." ]
           ; upar
               [ txt "The "
               ; ucode "title_section ()"
               ; txt
                   " function is the first documentation fragment that is \
                    built. "
               ; txt
                   "You should recognize it on the top of this documentation \
                    page." ]
           ; uinfo
               ~header:[txt "Imperative programming"]
               [ txt "You may have heard that OCaml is a functional language. "
               ; txt
                   "However, OCaml also supports imperative programming with \
                    global variables. "
               ; txt
                   "The Tezt test framework uses imperative programming to \
                    allow you to register tests whenever you want. "
               ; txt
                   "I personally find imperative programming the most natural \
                    programming model when writing scripts. " ]
           ; upar
               [ txt
                   "Each section of the documentation has its own test script \
                    which registers its own integration test. " ]
           ; upar
               [ txt
                   "Once all of the tests are registered, they are all run \
                    with the OCaml function call:"
               ; ucodeblock `OCaml "Test.run ()" ]
           ; upar
               [txt "Let's drill down into this section of the documentation. "]
           ; ucodeaction ~ide:(txt "Open") ~browser:(Some "open") self_FILE
           ; ucodefile self_FILE
           ; upar
               [ txt "You will see some brief boilerplate to register the test ("
               ; ucode "Test.register ~__FILE__ ..."
               ; txt
                   "). The test adds a \"section\" fragment to the in-memory \
                    documentation. In fact, the side-effect of creating the \
                    section fragment is all that the test does." ]
           ; upar [txt "Let's see something more complex."]
           ; ucodeaction
               ~ide:(txt "Open the \"Basic shell scripting\" test")
               ~browser:(Some "open") S024BasShell.__FILE__
           ; ucodefile S024BasShell.__FILE__
           ; upar
               [ txt "In this test we run a script from the command line ("
               ; ucode "RunDk.make_capture ()"
               ; txt " and "
               ; ucode "RunDk.run_dk ~hooks [shexpcountdown_module]"
               ; txt "). "
               ; txt
                   "The source code, the script command and its output become \
                    part of the documentation:"
               ; ucodeblock `OCaml
                   {|
; ucodefile shexpcountdown_file
; ucodeblock `Shell !ref_cmd
; ucodeblock `Output dk_output
|}
               ] ] ] ) ;
  unit
[@@warning "-unused-var-strict"]

You will see some brief boilerplate to register the test (Test.register ~__FILE__ ...). The test adds a "section" fragment to the in-memory documentation. In fact, the side-effect of creating the section fragment is all that the test does.

Let's see something more complex. Open the "Basic shell scripting" test src/DkHelloScript_Std/Y33ArticleX/S024BasShell.ml in your IDE or open src/DkHelloScript_Std/Y33ArticleX/S024BasShell.ml in your browser.

open Tr1Tezt_C.Tezt
open Tr1Tezt_C.Tezt.Base
open Tr1Htmlit_Std.Htmlit

let register_before_tests ~shexpcountdown_module ~shexpcountdown_file () =
  Test.register ~__FILE__ ~title:"basic shell scripting" ~tags:[]
  @@ fun () ->
  let ref_cmd, hooks = RunDk.make_capture () in
  let* dk_output = RunDk.run_dk ~hooks [shexpcountdown_module] in
  let open Doc in
  let open El in
  append
    (usection
       [ ucontainer
           [ ucard ~title:"Basic shell scripting" []
           ; upar
               [ unsafe_raw
                   "In an earlier section an **OCaml** \
                    programming language environment was transparently \
                    downloaded for you. "
               ; txt
                   "You will be using OCaml in this walkthrough, even though \
                    you may find other DkSDK documentation that uses DkCoder \
                    with C and Java. "
               ; txt
                   "You can proceed through this walkthrough without knowing \
                    OCaml." ]
           ; uinfo
               [ unsafe_raw
                   {|Once you are done the DkCoder walkthrough you may want to go to the <a href="https://ocaml.org/docs">OCaml - Learn</a> site. |}
               ; unsafe_raw
                   {|For this walkthrough **we'll stick to ordinary OCaml open-source examples** that you can replicate in a conventional OCaml environment.|}
               ]
           ; upar
               [ txt "In this section we'll be using the "
               ; ulink ~url:"https://github.com/janestreet/shexp" "shexp"
               ; txt " library that was created by "
               ; ulink
                   ~url:"https://www.janestreet.com/join-jane-street/overview/"
                   "Jane Street Capital"
               ; txt
                   ". If you are familiar with traditional shell scripts like "
               ; ucode "bash"
               ; txt " you'll find "
               ; ucode "shexp"
               ; txt " more powerful." ]
           ; upar
               [ ucodeaction ~ide:(txt "Open") ~browser:(Some "open")
                   shexpcountdown_file
               ; txt "You should see:" ]
           ; ucodefile shexpcountdown_file
           ; upar [txt "And running it with:"]
           ; ucodeblock `Shell !ref_cmd
           ; txt "gives:"
           ; ucodeblock `Output dk_output ] ] ) ;
  unit
[@@warning "-unused-var-strict"]

In this test we run a script from the command line (RunDk.make_capture () and RunDk.run_dk ~hooks [shexpcountdown_module]). The source code, the script command and its output become part of the documentation:

; ucodefile shexpcountdown_file
; ucodeblock `Shell !ref_cmd
; ucodeblock `Output dk_output

Adding some flair with graphicsΒΆ

Open src/DkHelloScript_Std/B57Graphics/B43Bogue/B43Tiny.ml in your IDE or open src/DkHelloScript_Std/B57Graphics/B43Bogue/B43Tiny.ml in your browser.

You should see:

open Tr1Bogue_Std.Bogue

let () =
  if Tr1EntryName.module_id = __MODULE_ID__ then
    Widget.label "Hello world"
    |> Layout.resident
    |> Bogue.of_layout
    |> Bogue.run

Run it with:

./dk DkRun_V2_2.Run -- DkHelloScript_Std.B57Graphics.B43Bogue.B43Tiny

to see:

Bogue's Hello World

The Snoke gameΒΆ

So far you have seen a very simple use of the Bogue graphics library.

The author of Bogue has created a demonstration game which was β€œported” to DkCoder.

The port did not change a single line of the original code. The directory structure was re-arranged (recall that there is a Java-like package mechanism underneath DkCoder) and an extra .ml file was added.

Run it outside your project (perhaps your home directory) with:

git clone --branch V2_1 https://gitlab.com/diskuv/samples/dkcoder/SanetteBogue.git

./SanetteBogue/dk DkRun_V2_1.Run -- SanetteBogue_Snoke.Snoke

You should see a really fun game!

sanette's Snoke game

You can explore its GPL-3.0 licensed source code at https://gitlab.com/diskuv/samples/dkcoder/SanetteBogue.git

Making Your Own Script ProjectΒΆ

Create an empty folder for your project. In the Terminal run:

git clone https://github.com/diskuv/dkcoder.git
dkcoder/dk user.dkcoder.project.init

βœ”οΈ DONE! You can edit the StartHere.ml script, or add new scripts now.

Using scripts in ProductionΒΆ

We've gone through the mechanics of creating your own projects and seeing what scripts can do on a desktop. However, scripts are not limited to running the desktop!

Let's run a real production script:

git clone --branch V2_2 https://gitlab.com/diskuv/samples/devops/DkSubscribeWebhook.git

./DkSubscribeWebhook/dk DkRun_V2_2.Run -- DkSubscribeWebhook_Std.Subscriptions --help

You'll be seeing the numerous help and options available to run the production webhook that manages the DkSDK subscriptions at DkSDK Pricing.

A webhook is a production microservice that responds to Internet requests from third parties. You could run the webhook yourself to manage your own customer subscriptions after you have configured some cloud SaaS services (more on this soon). The basic flow is:

  1. Stripe is the payments provider for DkSDK. Stripe sends an invoice.paid event to the webhook after establishing a subscription from the https://diskuv.com/pricing/ website.
  2. GitLab is the source control provider for DkSDK. A GitLab group token is created for the subscriber that expires after the subscription (plus a grace period).
  3. AWS SES is one of the email gateways used by DkSDK. An AWS SES email is sent to the subscriber containing the group token.

You could run the webhook script just like you did above. However, it is common to package up your scripts into a Docker container for deployment to production. Then in production when you execute:

docker-compose up --build

the 100MB webhook container image starts up with all the command line options and all the credentials to your cloud SaaS services. The Docker Compose example we provide also has a Let's Encrypt web interface so that you can manage SSL certificates for the webhook.

We recommend you read the README at https://gitlab.com/diskuv/samples/devops/DkSubscribeWebhook.git if you would like to see the production scripts in detail.

What may not be obvious from looking at the whole DkSubscribeWebhook project is how it was developed. I encourage you to look at the git commit history to see how each SaaS provider was tested and developed separately as runnable scripts. Those same "provider" scripts can be used for manually administering subscriptions. They don't bitrot and they compose well into the larger webhook service.