AWS Code Commit

AWS CodeCommit is a Git repository service provided by Amazon Web Services. This service is like other AWS services in that it's scriptable via AWS command line, any of the language SDKs, or using AWS CloudFormation.

Why not just use Github?

AWS CodeCommit is integrated with AWS IAM so roles related to Git repositories can be integrated with the rest of your AWS security. CodeCommit supports approval rule templates based on your IAM roles. This would require a separate configuration for GitHub for instance.

An example using the AWS SDK for JavaScript (ClojureScript)

See example below to setup a git repository and assign a role to create branches in that repostory.

Go ahead and read Clojure(Script) Tools and return here to follow along.

Clojure(Script) Tools

Clojure has a large selection of tools you can use to manage dependencies and build deployment artifacts:

All of these projects, however, are Clojure specific and not designed to integrate into existing Java shops where Maven is the defacto build tool. There is talios/clojure-maven-plugin which is a very good Maven alternative but it's built using Maven mojos written in Java. I wanted a way to completely leverage maven while still allowing me to script build tasks in Clojure.

I want everything run via Maven so I get a classpath based on my dependencies but still script everything in Clojure.

Just Maven

Just Maven is an archetype. These give Clojure projects a base (archetype) from which to create new Clojure projects. It's not a plugin. It leverages an existing plugin, MojoHaus Exec. There are two initial scripts for compile and test phases that can be modified as needed. It's very easy to add new scripts for other tasks and everything continues to run via Maven.

How to Get and Use the Archetype

When creating a new project, just run:

            
      mvn archetype:generate ^
      -DarchetypeGroupId=cloud.seltzer1717.clojure ^
      -DarchetypeArtifactId=just-maven-clojure-archetype ^
      -DarchetypeVersion=0.3-RELEASE
            
          

You'll then be prompted for your project's coordinates and root package:

            
      Define value for property 'groupId': com.example
      Define value for property 'artifactId': my-project
      Define value for property 'version' 1.0-SNAPSHOT: : 
      Define value for property 'package' com.example: : 
      Confirm properties configuration:
      groupId: com.example
      artifactId: my-project
      version: 1.0-SNAPSHOT
      package: com.example
      Y: : Y
            
          
This creates the following folder structure:
            
      my-project
      |   pom.xml
      |   repl.bat
      |
      +---.clojure
      |   \---scripts
      |           build.clj
      |           test.clj
      |
      \---src
          +---main
          |   +---java
          |   |   \---com
          |   |       \---example
          |   |               App.java
          |   |               core.clj
          |   |
          |   \---resources
          |       \---com
          |           \---example
          +---site
          |       site.xml
          |
          \---test
              \---java
                  \---com
                      \---example
                              AppTest.java
                              core_test.clj
            
          
A REPL can be launched by must running the repl.bat script.
            
      repl.bat
      C:\Users\jonseltzer\Documents\development\java\projects\my-project>repl
      Clojure 1.10.1
      user=> 
            
          
Run mvn clean install to how Maven compiles both Java and Clojure including tests:
            
      mvn clean install
      ...
            
          
There are two scripts which handle the compile and test phases. Here's the compile script:
            
      ;; Import Java classes
      (import (java.nio.file SimpleFileVisitor FileVisitResult Files)
              (java.io File))

      ;; Target folder
      (def output-dir-file
        "File representing target (or output) folder."
        (-> (System/getProperties)
            (get "clojure.compile.path")
            (File.)))

      ;; Source directory
      (def source-path
        "Path representing source directory"
        (-> (System/getProperties)
            (get "maven.source.dir")
            (File.)
            (.toPath)))

      ;; FileVisitor compile proxy.
      (def visitor
        "Java SimpleFileVisitor - overrides visitFile:
        takes path and attributes, compiles .clj files,
        and returns FileVisitResult/CONTINUE"
        (proxy [SimpleFileVisitor]
              []
              (visitFile [path attributes]
                (let [file-path   (.toFile path)
                      string-path (.toString path)]
                  (if (.isFile file-path)
                      (if (.endsWith string-path ".clj")
                          (let [relative-path         (.relativize source-path path)
                                relative-string       (.toString relative-path)
                                dot-clj-index         (.indexOf relative-string ".clj")
                                without-suffix        (.substring relative-string 0 dot-clj-index)
                                ;; standardizing windows systems
                                forward-slash         (.replaceAll without-suffix "\\\\" "/")
                                underscores-to-dashes (.replaceAll forward-slash "_" "-")
                                source-namespace      (.replaceAll underscores-to-dashes "/" ".")]
                            (load forward-slash)
                            (compile (symbol source-namespace))))))
                FileVisitResult/CONTINUE)))

      ;; Create target/classes folders if missing.
      (if (not (.exists output-dir-file))
          (.mkdirs output-dir-file))

      ;; Walk source folder tree and compile Clojure source files.
      (Files/walkFileTree source-path visitor)

            
          

What About ClojureScript

ClojureScript is Clojure for JavaScript. ClojureScript is transpiled via Java. It uses Googles Closure. For ClojureScript we have a different archetype. So I need maven again to assist with Google and ClojureScript dependencies to create a classpath. However, after you have a classpath, maven is no longer useful. ClojureScript is designed with 54 separate transpilation options which control how build artifacts are created. Additionally, tools like npm, yarn, and others are needed to manage JavaScript dependencies, minification, and packaging. So this prototype only attempts to support the initial ClojureScript transpilation and REPL and does not try to manage any of these other things.

There are many targets for JavaScript: Web Browsers, NodeJS (Deno?), IOT hardware, and others. Web Browsers are an entirely different target than NodeJS. One optimizes JavaScript for deployment to a users browser while the other runs on the server. Additionally, applications deployed to a server will have a wide variety of deployment optimizations. In the case of this blog, I'm only interested in transpilation to a users browser with React.js and a transpilation to NodeJS for deployment to AWS Lambda. For now let's focus on NodeJS deployed to AWS Lambda.

              
        mvn archetype:generate ^
        -DarchetypeGroupId=cloud.seltzer1717.clojure ^
        -DarchetypeArtifactId=just-maven-clojurescript-archetype ^
        -DarchetypeVersion=0.21-RELEASE
              
            

This will create a ClojureScript project that can be used to create NodeJS programs to deploy where necessary. We'll look at this later when we talk about deploying NodeJS AWS Lambda functions in a future blog.