Skip to content

Commit

Permalink
Merge pull request #28 from javajon/update
Browse files Browse the repository at this point in the history
Added lint command. Updated linux archetype. Fixed solver download
  • Loading branch information
javajon authored Feb 24, 2022
2 parents 5659616 + cc74002 commit 5b1f364
Show file tree
Hide file tree
Showing 27 changed files with 1,208 additions and 203 deletions.
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,34 @@ If you are an author using this utility, your feedback is important, and please
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 scenario.
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](https://www.katacoda.community/challenges/challenges-solver.html).

The solver CLI utility is targeted for Linux. To use Solver while you are developing your challenge on a Windows or Mac machine 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 Mac:
## 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:

```bash
function solver() {
SOLVER_IMAGE=ghcr.io/javajon/solver:0.4.2 ## <-- Set to the latest semver from https://github.com/javajon/katacoda-solver/releases]
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.*")
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 "$@";
}
```

**Note**: 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](https://brew.sh/) and then just changing the call to `grep` in the above function to `ggrep`.
### 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](https://brew.sh/) and then just changing the call to `grep` in the above function to `ggrep`.

### Windows Nuance

On Windows, another option is [wsl2](https://docs.microsoft.com/en-us/windows/wsl/about).

Expand Down Expand Up @@ -153,7 +159,6 @@ To compress the binary before transfer use [UPX](https://upx.github.io/). The re
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](https://github.com/jefflinse/pr-semver-bump).
Expand Down
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ dependencies {
implementation("io.quarkus:quarkus-arc")
implementation("com.jayway.jsonpath:json-path:2.7.0")

implementation ("com.fasterxml.jackson.core:jackson-core:2.13.1")
implementation ("com.fasterxml.jackson.core:jackson-databind:2.13.1")
implementation ("com.fasterxml.jackson.core:jackson-annotations:2.13.1")

testImplementation("io.quarkus:quarkus-junit5:2.4.1.Final")
}

Expand Down
24 changes: 11 additions & 13 deletions roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ Upcoming considerations to improve _Solver_:

## Near term feature goals and issues

- Add .cypress tests to archetypes.
- More archetypes for `solver create`, currently just `linux`, `basic` may be next.
- Test command line for oddball values.
- More archetypes for `solver create`, currently just `linux` and `basic` maybe next.
- Enhance help descriptions for each command and parameter.
- Add more unit tests.
- `solver check` to be finished:
- implement checks for author and challenge contexts.
- Encourage authors in documentation to add pre-commit hook to reject commit if `solver check` returns error code. Will provide recipe.
- An authoring check is needed to see if `solutions.sh` is newer than `solutions.sh.enc`.
- `solver check` needs a `--fix` switch for the fixable problems it encounters.
- Add more unit tests for oddball CLI values.
- `solver lint` is improving:
- Many checks exist for authoring mode. Considering checks when in challenge context.
- Encourage authors in documentation to add pre-commit hook to reject commit if `solver lint` returns error code. Will provide a recipe.
- `solver lint` could offer a `--fix` switch to correct some of the problems it encounters.

The current commands for _Solver_ are marked with their implementation and testing status:

Expand All @@ -33,15 +30,16 @@ The current commands for _Solver_ are marked with their implementation and testi
|| request_hint | internal call by `hint.sh` only |
|| request_advance_task | internal call by verify.sh only |
|| create | Create a Challenge project from the given archetype when in authoring context |
| 🤔 todo | check | Verify the required artifacts for the challenge are present and valid. Can check authoring and Challenge environments. |
| | lint | Verify the required artifacts for the challenge are present and valid. Can check-in authoring mode. Checking Challenge mode after decryption is proposed. |

## Longer-term feature goals

- ☢ The CLI binary (Linux native) is currently above the 9MB limit for scenario asset size. The CLI binary has been compressed using UPX, but so far cannot be distilled below 9MB. For the time being it is recommended to `wget` the tool from the GitHub release page when the challenge starts. This can potentially lead to the challenge not working if GitHub fails to deliver the artifact due to issues such as GitHub stability, rate limiting, or pure networking.
- Currently assuming all solutions are in a sh file, instead, consider putting all contents in a `solutions` directory into an encrypted zip.
- ☢ The CLI binary (Linux native) is currently above the 9MB limit for scenario asset size. The CLI binary has been compressed using UPX, but so far cannot be distilled below 10.6MB. For the time being it is recommended to `wget` the tool from the GitHub release page when the challenge starts. This can potentially lead to the challenge not working if GitHub fails to deliver the artifact due to issues such as GitHub stability, rate limiting, or pure networking.
- Currently assuming all solutions are in a single sh file, instead, consider putting all contents in a `solutions` directory into an encrypted zip.
- Perhaps a verbose logging switch currently logs in `/var/log/solver.log`.
- OSX and Windows native binary on the release page, a container image is currently encouraged
- Add .cypress tests to archetypes. Cypress tests will call `solver all`. Currently, Cypress testing is generally not working for scenarios or challenges. May be helpful to layer in BBD with Cucumber with Cypress.

## Your Feedback is Important
## Your feedback is important

Your insights as a Challenge author around the authoring process and how to improve this utility and the rest of the platform are important. When you have feedback please consider adding an [issue](https://github.com/javajon/katacoda-solver/issues) to this project. Issues beyond the scope of authoring challenges with _Solver_ and more about Katacoda may be emailed to [email protected].
3 changes: 2 additions & 1 deletion src/main/docker/Dockerfile.native-distroless
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
###
FROM alpine as workdir-creator
RUN mkdir /workdir

RUN apt-get shellcheck

FROM quay.io/quarkus/quarkus-distroless-image:1.0

ENV SOLVER_CONTEXT=challenge
ENV SOLVER_CONTAINERIZED=true

COPY --from=workdir-creator /workdir /workdir
COPY --from=workdir-creator /usr/local/bin/shellcheck /usr/local/bin
WORKDIR /workdir

COPY build/*-runner /application
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/katacoda/solver/SolverTopCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

// To help authors at writing time
SubcommandCreate.class,
SubcommandCheck.class,
SubcommandLint.class,

// Called from challenge framework (hidden from CLI)
SubcommandRequestTaskAdvance.class,
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/katacoda/solver/models/CheckItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.katacoda.solver.models;

public class CheckItem {
private final Status status;
private final String message;

/**
* Messages such as from script validator that appear at end of report.
*/
private String appendix = "";

public CheckItem(Status status, String message) {
this.status = status;
this.message = message;
}

public Status getStatus() {
return status;
}

public String getMessage() {
return message;
}

public void setAppendix(String appendix) {
this.appendix = appendix;
}
public String getAppendix() {
return appendix;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/katacoda/solver/models/CheckItemBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.katacoda.solver.models;

public class CheckItemBuilder {
private Status status = Status.Undetermined;
private String message = "";

public CheckItemBuilder status(Status status) {
this.status = status;
return this;
}

public CheckItemBuilder message(String message) {
this.message = message;
return this;
}

public CheckItem create() {
return new CheckItem(status, message);
}
}
33 changes: 23 additions & 10 deletions src/main/java/com/katacoda/solver/models/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import java.nio.file.Path;
import java.util.Properties;

/** Persist state of challenge across multiple calls to this application. */
/**
* Persist state of challenge across multiple calls to this application.
*/
public class Configuration {
private static final Logger LOG = Logger.getLogger(Configuration.class);

Expand All @@ -16,11 +18,13 @@ public class Configuration {
private final static File SOLVER_SHARED_PROPERTIES = new File(System.getProperty("java.io.tmpdir"),
"solver.properties");

public static final String SOLVER_CONTEXT = "SOLVER_CONTEXT";

public static void resetCurrentTask() {
setCurrentTask(1);
}

public static void setHintEnabled(boolean enabled) {
public static void setHintEnabled(boolean enabled) {
try {
Properties props = load();
props.setProperty("hints.enabled", Boolean.toString(enabled));
Expand Down Expand Up @@ -50,7 +54,7 @@ public static int getCurrentTask() {
return -1;
}

public static void setCurrentTask(int task) {
public static void setCurrentTask(int task) {
Properties props;
try {
props = load();
Expand Down Expand Up @@ -102,20 +106,29 @@ public enum ContextType {
}

public static ContextType getContextType() {
ContextType context = ContextType.development;

if (Path.of("/usr", "local", "bin", "challenge.sh").toFile().exists()) {
return ContextType.challenge;
context = ContextType.challenge;
}

if (new File("index.json").exists()) {
return ContextType.authoring;
String[] evidenceOfChallenge = {"index.json", "assets", "hint.sh", "verify.sh"};
for (String artifact : evidenceOfChallenge) {
if (new File(artifact).exists()) {
context = ContextType.authoring;
break;
}
}

ContextType context = ContextType.development;

String contextValue = System.getProperty("SOLVER_CONTEXT", context.name());
// Consider environment setting override, often done in unit tests
String contextValue = System.getProperty(SOLVER_CONTEXT, context.name());
try {
ContextType beforeOverride = context;
context = ContextType.valueOf(contextValue.toLowerCase().trim());
} catch(IllegalArgumentException e) {
if (context != beforeOverride) {
LOG.info("Context override from " + beforeOverride + " to " + context + ".");
}
} catch (IllegalArgumentException e) {
LOG.error("Ignoring invalid SOLVER_CONTEXT value of " + contextValue + " using context " + context.name(), e);
}

Expand Down
48 changes: 48 additions & 0 deletions src/main/java/com/katacoda/solver/models/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.katacoda.solver.models;

import java.util.ArrayList;
import java.util.List;

public class Node<T> {

private T data = null;

private List<Node<T>> children = new ArrayList<>();

private Node<T> parent = null;

public Node(T data) {
this.data = data;
}

public Node<T> add(Node<T> child) {
child.setParent(this);
this.children.add(child);
return child;
}

public void add(List<Node<T>> children) {
children.forEach(each -> each.setParent(this));
this.children.addAll(children);
}

public List<Node<T>> getChildren() {
return children;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}

private void setParent(Node<T> parent) {
this.parent = parent;
}

public Node<T> getParent() {
return parent;
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/katacoda/solver/models/Solutions.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private int solve(int task, PrintWriter out) {
if (exist(task)) {
solveFunction(task);
} else {
String message = String.format("Cannot solve the task. The solutions.sh testing functions script was not found or the solve_task_%d function was not found. O'Reilly team members can run `solver solution --decrypt <key>` to enable the solutions script. The key can be referenced in the source code's solutions.sh.md file.", task);
String message = String.format("Cannot solve the task. The solutions.sh testing functions script was not found or the solve_task_%d function was not found. O'Reilly team members can run `solver solutions --decrypt <key>` to enable the solutions script. The key can be referenced in the source code's solutions.sh.md file.", task);
out.println(Ansi.AUTO.string("@|bold,yellow " + message + "|@"));
return -1;
}
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/com/katacoda/solver/models/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.katacoda.solver.models;

import io.quarkus.runtime.util.StringUtil;
import picocli.CommandLine;

public enum Status {
Undetermined('☐', "white"),
Info('ⓘ', "green"),
Pass('✅', "green"),
Warning('⚠', "yellow"),
Fixed((char)0xD83D, "blue"),
Error('❌', "red");

public static int indent = 1;
private final char icon;
private final String color;

Status(char icon, String color) {
this.icon = icon;
this.color = color;
}

public String message(String message) {
String indentSpaces = new String(new char[indent]).replace('\0', ' ');

if (this == Status.Undetermined) {
return String.format("%-10s%s%s", "", indentSpaces, message);
}

return CommandLine.Help.Ansi.AUTO.string(String.format("@|bold,%s %s %-8s%s%s|@", color, icon, name(), indentSpaces, message));
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/katacoda/solver/models/Tallies.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.katacoda.solver.models;

public class Tallies {
private int pass;
private int warning;
private int error;

public void add(Status status) {
switch (status) {
case Pass:
pass++;
break;
case Warning:
warning++;
break;
case Error:
error++;
break;
}
}

public String report() {
return String.format("Passes: %d, Warnings: %d, Errors: %d", pass, warning, error);
}

public boolean hasErrors() {
return error > 0;
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/katacoda/solver/models/mapping/AssetFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.katacoda.solver.models.mapping;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class AssetFile {
private String file = "";
private String target = "";
private String chmod = "";

public String getChmod() {
return chmod;
}

public String getTarget() {
return target;
}

public String getFile() {
return file;
}
}
Loading

0 comments on commit 5b1f364

Please sign in to comment.