cheat.sh icon indicating copy to clipboard operation
cheat.sh copied to clipboard

JSON output from site?

Open alphapapa opened this issue 7 years ago • 13 comments

I feel like I must be missing something, but I can't find any mention of it: Is there no way to get data in JSON or another structured format from the site? It would make it much easier to cleanly present individual entries in clients.

alphapapa avatar Jul 11 '18 22:07 alphapapa

Yes, it would be possible.

What exactly information would you encode in JSON?

chubin avatar Jul 12 '18 22:07 chubin

Well, I only just discovered this project, and I don't fully grok its organization, so I will probably leave some things out. But how about this for starters:

  • Programming language
  • Subdirectory
  • Comments
  • Source code
  • URL to view in browser
  • Special page name when applicable
  • Original source (i.e. StackOverflow, etc)

alphapapa avatar Jul 12 '18 22:07 alphapapa

Yes, it is an interesting idea. Actually, we have all of this either in the URL of the query or in the output (bottom line), but the idea seems to be really interesting.

chubin avatar Jul 12 '18 22:07 chubin

It would be much easier to implement a client without having to parse this out manually. In Python, if the data is well-organized, it could be as easy as serializing a list of dicts to JSON.

alphapapa avatar Jul 12 '18 22:07 alphapapa

I would love to get the output as a JSON too.... this is the closest I could get so far.... still not being able to parse languages or special use cases like :help (mainly because the huge ammount of things to escape):

#!/usr/bin/env bash

###############################################
# Change the cheat sheet you want to search for
search_for="kubectl"
###############################################

result=$(/usr/bin/curl -s "cheat.sh/${search_for}?qT")
previous_line=""
json_array=""
command_buffer=""

SAVEIFS=$IFS        # Save current IFS (Internal Field Separator)
IFS=$'\n'           # Change IFS to newline char

for line in $result; do

  # Escape chars like double quotes
  line="${line//\"/\\\"}"

  # Check if the line is a comment
  if [[ "${line:0:1}" == '#' ]]; then

    # It whe have commands on buffer
    if [[ -n "${command_buffer}" ]]; then
      # output to json array
      new_element="{\"title\":\"$previous_line\",\"command\":\"${command_buffer}\"}"
      if [[ -z "${json_array}" ]]; then
        # Initialize the array
        json_array=$new_element
      else
        # Append to the end of the array
        json_array="${json_array},${new_element}"
      fi
    fi

    # clear command buffer
    command_buffer=""
    previous_line=$line

  else
    # Puts the line on the buffer
    if [[ -z "${command_buffer}" ]]; then
      command_buffer="${line}"
    else
      command_buffer="${command_buffer}\n${line}"
    fi

  fi

done

# It whe still have commands on buffer
if [[ -n "${command_buffer}" ]]; then
  # output to json array
  new_element="{\"title\":\"$previous_line\",\"command\":\"${command_buffer}\"}"
  if [[ -z "${json_array}" ]]; then
    # Initialize the array
    json_array=$new_element
  else
    # Append to the end of the array
    json_array="${json_array},${new_element}"
  fi
fi

# Append to the end of the array
json_array="{\"item\": \"$search_for\",\"cheat_sheets\": [${json_array}]}"

echo "$json_array" | /usr/local/bin/jq

IFS=$SAVEIFS        # Restore original IFS

Example result:

{
  "item": "kubectl",
  "cheat_sheets": [
    {
      "title": "# Display list of all available commands",
      "command": "kubectl -h"
    },
    {
      "title": "# Display an explanation of a specific command",
      "command": "kubectl command_name -h"
    },
    {
      "title": "# Display complete list of supported resources",
      "command": "kubectl api-resources"
    },
    {
      "title": "# Display an explanation of a specific reource",
      "command": "kubectl explain resource_name"
    },
    {
      "title": "# Display an explanation of a specific field of resource",
      "command": "kubectl explain resource_name.field_name"
    },
    {
      "title": "# Display list of global command-line options",
      "command": "kubectl options"
    },
    {
      "title": "# Setup Kubectl autocomplete in bash(to use press Tab)",
      "command": "source <(kubectl completion bash"
    },
    {
      "title": "# Display all resources in all namespaces",
      "command": "kubectl get all -A"
    },
    {
      "title": "# List all information about a resource with more details:",
      "command": "kubectl get pod|service|deployment|ingress|... -o "
    },
    {
      "title": "# Update specified pod with the label 'unhealthy' and the value 'true':",
      "command": "kubectl label pods name unhealthy=true"
    },
    {
      "title": "# List all resources with different types:",
      "command": "kubectl get all"
    },
    {
      "title": "# Display resource (CPU/Memory/Storage) usage of nodes or pods:",
      "command": "kubectl top pod|node"
    },
    {
      "title": "# Print the address of the master and cluster services:",
      "command": "kubectl cluster-info"
    },
    {
      "title": "# Display an explanation of a specific field:",
      "command": "kubectl explain pods.spec.containers"
    },
    {
      "title": "# Print the logs for a container in a pod or specified resource:",
      "command": "kubectl logs pod_name"
    },
    {
      "title": "# Run command in an existing pod:",
      "command": "kubectl exec pod_name -- ls /"
    }
  ]
}

gohoyer avatar Jan 30 '22 16:01 gohoyer

The JSON output is desired for me because of this project: https://github.com/gohoyer/Alfred-Cheat.sh

gohoyer avatar Jan 31 '22 20:01 gohoyer

Looks like the formatting part is already there.

https://github.com/chubin/cheat.sh/blob/46d1a5f73c6b88da15d809154245dbf234e9479e/lib/cheat_wrapper.py#L119-L120

But the web server doesn't contain the logic to select it.

https://github.com/chubin/cheat.sh/blob/46d1a5f73c6b88da15d809154245dbf234e9479e/bin/app.py#L290-L293

abitrolly avatar Feb 01 '22 09:02 abitrolly

I also support this, json output would enable a lot of extra tools to work with it.

chrisgrieser avatar Feb 14 '22 17:02 chrisgrieser

@chrisgrieser support like financially, with code, or just +1?

abitrolly avatar Feb 14 '22 22:02 abitrolly

The latter 😅

chrisgrieser avatar Feb 15 '22 02:02 chrisgrieser

@gohoyer It is a cool project. I like it a lot, and I think it is worth to add its input format as a standard output for for cheat.sh. Maybe something like format=alfred.

Let's define the exact format, and then it could be easily implemented on the server side.

Does your example above contain all possible elements, or more elements are possible?

{
  "item": "kubectl",
  "cheat_sheets": [
    {
      "title": "# Display list of all available commands",
      "command": "kubectl -h"
    },
    {
      "title": "# Display an explanation of a specific command",
      "command": "kubectl command_name -h"
    }
  }
} 

chubin avatar Apr 17 '22 12:04 chubin

@chubin, here`s the official JSON Format for an Alfred Script Filter: https://www.alfredapp.com/help/workflows/inputs/script-filter/json/

{"items": [
    {
        "uid": "desktop",
        "type": "file",
        "title": "Desktop",
        "subtitle": "~/Desktop",
        "arg": "~/Desktop",
        "autocomplete": "Desktop",
        "icon": {
            "type": "fileicon",
            "path": "~/Desktop"
        }
    }
]}

The format I`m currently using on the project is:

{"items": [
  {
    "title": "comments/command title",
    "subtitle": "commmand",
    "text": {
      "copy": "command",
      "largetype": "comments/command title"
    },
    "arg": "command",
    "autocomplete": "comments/command title"
  },
  {
    "title": "comments/command title",
    "subtitle": "commmand",
    "text": {
      "copy": "command",
      "largetype": "comments/command title"
    },
    "arg": "command",
    "autocomplete": "comments/command title"
  },
  ]
}

Here`s the script that do this trick: https://github.com/gohoyer/Alfred-Cheat.sh/blob/main/get_details.sh

Real example of 7z details:

{
  "items": [
    {
      "title": "# Encrypt an existing archive (including headers):",
      "subtitle": "7z a encrypted.7z -ppassword -mhe=on archived.7z",
      "text": {
        "copy": "7z a encrypted.7z -ppassword -mhe=on archived.7z",
        "largetype": "# Encrypt an existing archive (including headers):"
      },
      "arg": "7z a encrypted.7z -ppassword -mhe=on archived.7z",
      "autocomplete": "# Encrypt an existing archive (including headers):"
    },
    {
      "title": "# Extract an existing 7z file with original directory structure:",
      "subtitle": "7z x archived.7z",
      "text": {
        "copy": "7z x archived.7z",
        "largetype": "# Extract an existing 7z file with original directory structure:"
      },
      "arg": "7z x archived.7z",
      "autocomplete": "# Extract an existing 7z file with original directory structure:"
    },
    {
      "title": "# Extract an archive with user-defined output path:",
      "subtitle": "7z x archived.7z -opath/to/output",
      "text": {
        "copy": "7z x archived.7z -opath/to/output",
        "largetype": "# Extract an archive with user-defined output path:"
      },
      "arg": "7z x archived.7z -opath/to/output",
      "autocomplete": "# Extract an archive with user-defined output path:"
    },
    {
      "title": "# Extract an archive to stdout:",
      "subtitle": "7z x archived.7z -so",
      "text": {
        "copy": "7z x archived.7z -so",
        "largetype": "# Extract an archive to stdout:"
      },
      "arg": "7z x archived.7z -so",
      "autocomplete": "# Extract an archive to stdout:"
    },
    {
      "title": "# Archive using a specific archive type:",
      "subtitle": "7z a -tzip|gzip|bzip2|tar archived.7z path/to/file_or_directory",
      "text": {
        "copy": "7z a -tzip|gzip|bzip2|tar archived.7z path/to/file_or_directory",
        "largetype": "# Archive using a specific archive type:"
      },
      "arg": "7z a -tzip|gzip|bzip2|tar archived.7z path/to/file_or_directory",
      "autocomplete": "# Archive using a specific archive type:"
    },
    {
      "title": "# List available archive types:",
      "subtitle": "7z i",
      "text": {
        "copy": "7z i",
        "largetype": "# List available archive types:"
      },
      "arg": "7z i",
      "autocomplete": "# List available archive types:"
    },
    {
      "title": "# List the contents of an archive file:",
      "subtitle": "7z l archived.7z",
      "text": {
        "copy": "7z l archived.7z",
        "largetype": "# List the contents of an archive file:"
      },
      "arg": "7z l archived.7z",
      "autocomplete": "# List the contents of an archive file:"
    }
  ]
}

As you can see, I only use the title and the command itself duplicating in different fields to control Alfred behaviour.

gohoyer avatar Apr 17 '22 19:04 gohoyer

I shoud warn you about the current limitations:

"The main workflow doesn't support cheat sheets for programming languages or special pages like :help from cheat.sh (mainly because of the huge ammount of things that must be escaped to properly load as json)."

gohoyer avatar Apr 18 '22 16:04 gohoyer