/katacoda-solver

Solver is a command-line interface (CLI) that helps authors create O'Reilly Challenges. The Solver utility helps authors organize the verifications, the myriad of hints, and provides an enhanced solutions mechanism for rapid testing. Solver is not required, but it can shorten your time for producing quality Challenges.

Primary LanguageJavaMIT LicenseMIT

Solver Tool for Authoring O'Reilly Challenges

Solver is a command-line interface (CLI) that helps authors create O'Reilly Challenges. Solver helps authors organize the verifications, the myriad of hints, and provides an enhanced solutions mechanism for rapid testing. Solver is not required, but it can shorten your time for producing quality Challenges.

The usage of Solver for O'Reilly Challenge authors is detailed in the Katacoda Documentation.

The command line offers information on the commands via solver --help.

If you are an author using this utility, your feedback is important, and please feel free to add issues in this project for reporting problems or suggesting ideas.

Running Solver for Authors

As an author there are two places where Solver can help you:

  1. Locally in your development environment when creating the content
  2. At runtime, within the challenge.

Solver is a tool that both supports your challenge development and execution.

These two installations types are covered in the documentation at Challenges Solver Utility.

Solver on Windows and MacOS

The solver CLI utility binary is targeted for Linux. To use Solver while you are developing your challenge on Windows or macOS one option is to run the utility from the published container for solver. Here is a bash function that you can apply if you decide to use a bash terminal on Windows or macOS:

function solver() {
  SOLVER_IMAGE=ghcr.io/javajon/solver:0.5.4   ## <-- Set to the latest semver release @ https://bit.ly/3sSEiBD
  SCENARIOS_ROOT=~/my-scenarios               ## <-- Set to your base source directory for your challenges and scenarios
  if [[ ! "$(pwd)" =~ ^$SCENARIOS_ROOT.* ]]; then
    echo "Please run this from $SCENARIOS_ROOT or one of its scenario or challenge subdirectory."
    return 1;
  fi
  SUBDIR=$(echo $(pwd) | grep -oP "^$SCENARIOS_ROOT\K.*")    ## <-- change to ggrep if on macOS
  docker run --rm -it -v "$SCENARIOS_ROOT":/workdir -w /workdir/$SUBDIR $SOLVER_IMAGE "$@";
}

macOS Nuance

The above function expects GNU grep. This is not the version installed by default on macOS. You can easily install the GNU version by using homebrew and then just changing the call to grep in the above function to ggrep.

Windows Nuance

On Windows, another option is wsl2.


The remaining instructions are for developers of this utility, not for authors of the challenges.

Architecture stack

This command-line tool is written in Java. Picocli is leveraged for the CLI framework. Quarkus with Graal creates a fast and efficient Linux binary. The project is built with Gradle and CI/CD is automated using GitHub actions.

Running App in Dev Mode

You can run your application in developer mode that enables live coding using:

./gradlew quarkusDev

Packaging and Running

The application can be packaged using:

./gradlew build

It produces the quarkus-run.jar file in the build/quarkus-app/ directory. Be aware that it’s not an über-jar as the dependencies are copied into the build/quarkus-app/lib/ directory.

If you want to build an über-jar, execute the following command:

./gradlew build -Dquarkus.package.type=uber-jar

The application is now runnable using java -jar build/quarkus-app/quarkus-run.jar.

Creating Native Executable

You can create a native executable using:

./gradlew build -Dquarkus.package.type=native -Dquarkus.native.native-image-xmx=8g

Or, if you don't have GraalVM installed, you can run the native executable build in a container using:

./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true -Dquarkus.native.native-image-xmx=8g

If the quarkusBuild fails with an exit code 137 out of memory message, the error message suggests increasing the xmx heap size. Others indicate this is a problem with your local docker engine. Consider a purge of your docker engine caches docker purge system --all followed with a full host reboot.

With either of the above, you can then execute your native executable with:

./build/solver-[version]-runner

This binary, renamed to solver, may be used by a Challenge.

Developing and Debugging the Solver project code

If you are a developer or tester of the Solver utility below are some techniques to run it locally or copy it to a Challenge without the GitOps process.

You can run solver from Linux shells, but without the context of an O'Reilly Challenge, it is like watching Fred Flintstone drive a car with his feet with no engine. You're not fooling anyone, Fred.

Run Locally via über-jar

./gradlew build -Dquarkus.package.type=uber-jar && java -jar build/quarkus-app/quarkus-run.jar --help

Installing Solver into a Challenge

The solver command-line tool cannot be loaded via the scenario's assets as there is a size limit at 9MB and the CLI tool is too large of an asset. Instead, there is a wget command in the init-background.sh script that installs Solver when the challenge starts. This incurs a slight vulnerability if GitHub fails to deliver the requested CLI binary artifact from the release page then the challenge will break and the learner will have to reload the scenario. This source may change and remains on the roadmap.

Ensure the wget pulls a specific version of Solver and your challenge is tested with that specific version in place.

Rapid Development Testing

For fast, local, iterative development and testing of the Solver tool with a live challenge it's best to copy the updated Solver binary directly to the challenge. There are a variety of places where a binary can be uploaded. Here is an example using the public service transfer.sh

  1. Build the binary with
    ./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true
  2. Identify the binary to copy:
    export SOLVER_RUNNER=build/solver-$(git describe --tags $(git rev-list --tags --max-count=1))-$(git rev-parse --abbrev-ref HEAD)-runner
    
  3. Upload the executable to an ephemeral cloud directory:
    STORAGE_URL=$(curl --upload-file $SOLVER_RUNNER https://transfer.sh/solver) && echo $STORAGE_URL
  4. Copy the resulting storage URL to your clipboard.
  5. Start Challenge
  6. Copy solver binary into Challenge using the clipboard pasted URL:
    curl -o solver <pasted-url>
  7. Make executable, copy, and verify:
    chmod +x solver && cp solver /usr/local/bin && solver --version

Some of these public services throttle throughput over repeated usage. You can also use gdrive to download artifacts from Google drive. Never use these for the published challenges.

To compress the binary before transfer use UPX. The releases are compressed with this UPX tool:

  • Install UPX with
    sudo apt-get update && yes | sudo apt-get install upx
    
  • Compress the executable with:
    upx --best --lzma $solver
    

Solver Version Tracking

Solver uses SemVer and the versions are tracked and bumped automatically. A release is created for any commit with a new SemVer git tag. There are GitHub actions to build, tag, and create releases. The SemVer tagging, bumping, and releasing process is based on the GitHub action jefflinse/pr-semver-bump.

Direct commits are not permitted to the main branch via a GitHub branch rule. Only PRs are committed to main. A Merged pull request (PR) triggers the automated SemVer advancement and a new release. With this comes the PR comments and PR labels and direct the bumping of the major, minor, and patch numbers. When a PR is merged to main, it must be labeled with either major, minor, or patch. When a new SemVer tag is created a new GitHub release is created with the update solver binary. This technique follows some best practices for automated GitOps. Branch names can be reused, such as update. The workflow for the PR roughly follows this flow:

git checkout -b update
(make changes)
git add .
git commit -m "(the reasons for the new release)"
git push --set-upstream origin update

In GitHub merge the pull request and be sure to add the label patch release, as described here.

Related Guides

Origins

This project was inspired by the try-picocli-gradle repository.