Files
spring-cloud-static/spring-cloud-contract/2.2.0.RC1/reference/html/docker-project.html
2019-10-24 21:38:37 +00:00

585 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.8">
<title>Docker Project</title>
<link rel="stylesheet" href="css/spring.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
.hidden {
display: none;
}
.switch {
border-width: 1px 1px 0 1px;
border-style: solid;
border-color: #7a2518;
display: inline-block;
}
.switch--item {
padding: 10px;
background-color: #ffffff;
color: #7a2518;
display: inline-block;
cursor: pointer;
}
.switch--item:not(:first-child) {
border-width: 0 0 0 1px;
border-style: solid;
border-color: #7a2518;
}
.switch--item.selected {
background-color: #7a2519;
color: #ffffff;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script type="text/javascript">
function addBlockSwitches() {
$('.primary').each(function() {
primary = $(this);
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
primary.children('.title').remove();
});
$('.secondary').each(function(idx, node) {
secondary = $(node);
primary = findPrimary(secondary);
switchItem = createSwitchItem(secondary, primary.children('.switch'));
switchItem.content.addClass('hidden');
findPrimary(secondary).append(switchItem.content);
secondary.remove();
});
}
function createBlockSwitch(primary) {
blockSwitch = $('<div class="switch"></div>');
primary.prepend(blockSwitch);
return blockSwitch;
}
function findPrimary(secondary) {
candidate = secondary.prev();
while (!candidate.is('.primary')) {
candidate = candidate.prev();
}
return candidate;
}
function createSwitchItem(block, blockSwitch) {
blockName = block.children('.title').text();
content = block.children('.content').first().append(block.next('.colist'));
item = $('<div class="switch--item">' + blockName + '</div>');
item.on('click', '', content, function(e) {
$(this).addClass('selected');
$(this).siblings().removeClass('selected');
e.data.siblings('.content').addClass('hidden');
e.data.removeClass('hidden');
});
blockSwitch.append(item);
return {'item': item, 'content': content};
}
$(addBlockSwitches);
</script>
</head>
<body id="docker" class="book toc2 toc-left">
<div id="header">
<h1>Docker Project</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#docker-intro">1. A Short Introduction to Maven, JARs and Binary storage</a></li>
<li><a href="#docker-how-it-works">2. Generating Tests on the Producer Side</a>
<ul class="sectlevel2">
<li><a href="#docker-env-vars">2.1. Environment Variables</a></li>
<li><a href="#docker-example-of-usage">2.2. Example of Usage</a></li>
</ul>
</li>
<li><a href="#docker-stubrunner">3. Running Stubs on the Consumer Side</a>
<ul class="sectlevel2">
<li><a href="#docker-stubrunner-env-vars">3.1. Environment Variables</a></li>
<li><a href="#docker-stubrunner-example">3.2. Example of Usage</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In this section, we publish a <code>springcloud/spring-cloud-contract</code> Docker image
that contains a project that generates tests and runs them in <code>EXPLICIT</code> mode
against a running application.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
The <code>EXPLICIT</code> mode means that the tests generated from contracts send
real requests and not the mocked ones.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We also publish a <code>spring-cloud/spring-cloud-contract-stub-runner</code> Docker image
that starts the standalone version of Stub Runner.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="docker-intro"><a class="anchor" href="#docker-intro"></a><a class="link" href="#docker-intro">1. A Short Introduction to Maven, JARs and Binary storage</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Since non-JVM projects can use the Docker image, it is good to
explain the basic terms behind Spring Cloud Contract packaging defaults.</p>
</div>
<div class="paragraph">
<p>Parts of the following definitions were taken from the <a href="https://maven.apache.org/glossary.html">Maven Glossary</a>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>Project</code>: Maven thinks in terms of projects. Projects
are all you build. Those projects follow a well defined
“Project Object Model”. Projects can depend on other projects,
in which case the latter are called “dependencies”. A project may
consistent of several subprojects. However, these subprojects are still
treated equally as projects.</p>
</li>
<li>
<p><code>Artifact</code>: An artifact is something that is either produced or used
by a project. Examples of artifacts produced by Maven for a project
include JAR files and source and binary distributions. Each artifact
is uniquely identified by a group ID and an artifact ID that is
unique within a group.</p>
</li>
<li>
<p><code>JAR</code>: JAR stands for Java ARchive. Its format is based on
the ZIP file format. Spring Cloud Contract packages the contracts and generated
stubs in a JAR file.</p>
</li>
<li>
<p><code>GroupId</code>: A group ID is a universally unique identifier for a project.
While this is often just the project name (for example, <code>commons-collections</code>),
it is helpful to use a fully-qualified package name to distinguish it
from other projects with a similar name (for example, <code>org.apache.maven</code>).
Typically, when published to the Artifact Manager, the <code>GroupId</code> gets
slash separated and forms part of the URL. For example, for a group ID of <code>com.example</code>
and an artifact ID of <code>application</code>, the result would be <code>/com/example/application/</code>.</p>
</li>
<li>
<p><code>Classifier</code>: The Maven dependency notation looks as follows:
<code>groupId:artifactId:version:classifier</code>. The classifier is an additional suffix
passed to the dependency&#8201;&#8212;&#8201;for example, <code>stubs</code> or <code>sources</code>. The same dependency
(for example, <code>com.example:application</code>) can produce multiple artifacts that
differ from each other with the classifier.</p>
</li>
<li>
<p><code>Artifact manager</code>: When you generate binaries, sources, or packages, you would
like them to be available for others to download, reference, or reuse. In the case
of the JVM world, those artifacts are generally JARs. For Ruby, those artifacts are gems.
For Docker, those artifacts are Docker images. You can store those artifacts
in a manager. Examples of such managers include <a href="https://jfrog.com/artifactory/">Artifactory</a>
or <a href="https://www.sonatype.org/nexus/">Nexus</a>.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="docker-how-it-works"><a class="anchor" href="#docker-how-it-works"></a><a class="link" href="#docker-how-it-works">2. Generating Tests on the Producer Side</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The image searches for contracts under the <code>/contracts</code> folder.
The output from running the tests is available in the
<code>/spring-cloud-contract/build</code> folder (useful for debugging
purposes).</p>
</div>
<div class="paragraph">
<p>You can mount your contracts and pass the environment variables.
The image then:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Generates the contract tests</p>
</li>
<li>
<p>Runs the tests against the provided URL</p>
</li>
<li>
<p>Generates the <a href="https://github.com/tomakehurst/wiremock">WireMock</a> stubs</p>
</li>
<li>
<p>Publishes the stubs to a Artifact Manager (optional - turned on by default)</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="docker-env-vars"><a class="anchor" href="#docker-env-vars"></a><a class="link" href="#docker-env-vars">2.1. Environment Variables</a></h3>
<div class="paragraph">
<p>The Docker image requires some environment variables to point to
your running application, to the Artifact manager instance, and so on.
The following list describes the environment variables:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>PROJECT_GROUP</code>: Your project&#8217;s group ID. Defaults to <code>com.example</code>.</p>
</li>
<li>
<p><code>PROJECT_VERSION</code>: Your project&#8217;s version. Defaults to <code>0.0.1-SNAPSHOT</code>.</p>
</li>
<li>
<p><code>PROJECT_NAME</code>: Your project&#8217;s artifact id. Defaults to <code>example</code>.</p>
</li>
<li>
<p><code>PRODUCER_STUBS_CLASSIFIER</code>: Archive classifier used for generated producer stubs. Defaults to <code>stubs</code>.</p>
</li>
<li>
<p><code>REPO_WITH_BINARIES_URL</code>: URL of your Artifact Manager. Defaults to <code><a href="http://localhost:8081/artifactory/libs-release-local" class="bare">localhost:8081/artifactory/libs-release-local</a></code>,
which is the default URL of <a href="https://jfrog.com/artifactory/">Artifactory</a> running locally.</p>
</li>
<li>
<p><code>REPO_WITH_BINARIES_USERNAME</code>: (optional) Username when the Artifact Manager is secured. Defaults to <code>admin</code>.</p>
</li>
<li>
<p><code>REPO_WITH_BINARIES_PASSWORD</code>: (optional) Password when the Artifact Manager is secured. Defaults to <code>password</code>.</p>
</li>
<li>
<p><code>PUBLISH_ARTIFACTS</code>: If set to <code>true</code>, publishes the artifact to binary storage. Defaults to <code>true</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>These environment variables are used when contracts lay in an external repository. To enable
this feature, you must set the <code>EXTERNAL_CONTRACTS_ARTIFACT_ID</code> environment variable.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>EXTERNAL_CONTRACTS_GROUP_ID</code>: Group ID of the project with contracts. Defaults to <code>com.example</code></p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_ARTIFACT_ID</code>: Artifact ID of the project with contracts.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_CLASSIFIER</code>: Classifier of the project with contracts. Empty by default.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_VERSION</code>: Version of the project with contracts. Defaults to <code>+</code>, equivalent to picking the latest.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL</code>: URL of your Artifact Manager. It defaults to
the value of <code>REPO_WITH_BINARIES_URL</code> environment variable.
If that is not set, it defaults to <code><a href="http://localhost:8081/artifactory/libs-release-local" class="bare">localhost:8081/artifactory/libs-release-local</a></code>,
which is the default URL of <a href="https://jfrog.com/artifactory/">Artifactory</a> running locally.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_USERNAME</code>: (optional) Username if the <code>EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL</code>
requires authentication. It defaults to <code>REPO_WITH_BINARIES_USERNAME</code>. If that is not set, it defaults to <code>admin</code>.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_PASSWORD</code>: (optional) Password if the <code>EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL</code>
requires authentication. It defaults to <code>REPO_WITH_BINARIES_PASSWORD</code>. If that is not set, it defaults to <code>password</code>.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_PATH</code>: Path to contracts for the given project, inside the project with contracts.
Defaults to slash-separated <code>EXTERNAL_CONTRACTS_GROUP_ID</code> concatenated with <code>/</code> and <code>EXTERNAL_CONTRACTS_ARTIFACT_ID</code>. For example,
for group id <code>cat-server-side.dog</code> and artifact id <code>fish</code>, would result in <code>cat/dog/fish</code> for the contracts path.</p>
</li>
<li>
<p><code>EXTERNAL_CONTRACTS_WORK_OFFLINE</code>; If set to <code>true</code>, retrieves the artifact with contracts
from the container&#8217;s <code>.m2</code>. Mount your local <code>.m2</code> as a volume available at the container&#8217;s <code>/root/.m2</code> path.</p>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
You must not set both <code>EXTERNAL_CONTRACTS_WORK_OFFLINE</code> and <code>EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL</code>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The following environment variables are used when tests are executed:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>APPLICATION_BASE_URL</code>: URL against which tests should be run.
Remember that it has to be accessible from the Docker container (for example, <code>localhost</code>
does not work)</p>
</li>
<li>
<p><code>APPLICATION_USERNAME</code>: (optional) Username for basic authentication to your application.</p>
</li>
<li>
<p><code>APPLICATION_PASSWORD</code>: (optional) Password for basic authentication to your application.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="docker-example-of-usage"><a class="anchor" href="#docker-example-of-usage"></a><a class="link" href="#docker-example-of-usage">2.2. Example of Usage</a></h3>
<div class="paragraph">
<p>In this section, we explore a simple MVC application. To get started, clone the following
git repository and cd to the resulting directory, by running the following commands:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>The contracts are available in the <code>/contracts</code> folder.</p>
</div>
<div class="paragraph">
<p>Since we want to run tests, we can run the following command:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ npm test</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>However, for learning purposes, we split it into pieces, as follows:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash"># Stop docker infra (nodejs, artifactory)
$ ./stop_infra.sh
# Start docker infra (nodejs, artifactory)
$ ./setup_infra.sh
# Kill &amp; Run app
$ pkill -f "node app"
$ nohup node app &amp;
# Prepare environment variables
$ SC_CONTRACT_DOCKER_VERSION="..."
$ APP_IP="192.168.0.100"
$ APP_PORT="3000"
$ ARTIFACTORY_PORT="8081"
$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
$ CURRENT_DIR="$( pwd )"
$ CURRENT_FOLDER_NAME=${PWD##*/}
$ PROJECT_VERSION="0.0.1.RELEASE"
# Execute contract tests
$ docker run --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
# Kill app
$ pkill -f "node app"</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Through bash scripts, the following happens:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The infrastructure (MongoDb and Artifactory) is set up.
In a real-life scenario, you would run the NodeJS application
with a mocked database. In this example, we want to show how we can
benefit from Spring Cloud Contract in very little time.</p>
</li>
<li>
<p>Due to those constraints, the contracts also represent the
stateful situation.</p>
<div class="ulist">
<ul>
<li>
<p>The first request is a <code>POST</code> that causes data to get inserted to the database.</p>
</li>
<li>
<p>The second request is a <code>GET</code> that returns a list of data with 1 previously inserted element.</p>
</li>
</ul>
</div>
</li>
<li>
<p>The NodeJS application is started (on port <code>3000</code>).</p>
</li>
<li>
<p>The contract tests are generated through Docker, and tests
are run against the running application.</p>
<div class="ulist">
<ul>
<li>
<p>The contracts are taken from <code>/contracts</code> folder.</p>
</li>
<li>
<p>The output of the test execution is available under
<code>node_modules/spring-cloud-contract/output</code>.</p>
</li>
</ul>
</div>
</li>
<li>
<p>The stubs are uploaded to Artifactory. You can find them in
<a href="http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/" class="bare">localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/</a> .
The stubs are at <a href="http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar" class="bare">localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar</a>.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="docker-stubrunner"><a class="anchor" href="#docker-stubrunner"></a><a class="link" href="#docker-stubrunner">3. Running Stubs on the Consumer Side</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section describes how to use Docker on the consumer side to fetch and run stubs.</p>
</div>
<div class="paragraph">
<p>We publish a <code>spring-cloud/spring-cloud-contract-stub-runner</code> Docker image
that starts the standalone version of Stub Runner.</p>
</div>
<div class="sect2">
<h3 id="docker-stubrunner-env-vars"><a class="anchor" href="#docker-stubrunner-env-vars"></a><a class="link" href="#docker-stubrunner-env-vars">3.1. Environment Variables</a></h3>
<div class="paragraph">
<p>You can run the docker image and pass any of the <a href="project-features.html#features-stub-runner-common-properties-junit-spring">Common Properties for JUnit and Spring</a>
as environment variables. The convention is that all the
letters should be upper case.
The dot (<code>.</code>) should be replaced with underscore (<code>_</code>) characters. For example,
the <code>stubrunner.repositoryRoot</code> property should be represented
as a <code>STUBRUNNER_REPOSITORY_ROOT</code> environment variable.</p>
</div>
</div>
<div class="sect2">
<h3 id="docker-stubrunner-example"><a class="anchor" href="#docker-stubrunner-example"></a><a class="link" href="#docker-stubrunner-example">3.2. Example of Usage</a></h3>
<div class="paragraph">
<p>We want to use the stubs created in this <a href="#docker-server-side">[docker-server-side]</a> step.
Assume that we want to run the stubs on port <code>9876</code>. You can see the NodeJS code
by cloning the repository and changing to the directory indicated in the following commands:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Now we can run the Stub Runner Boot application with the stubs, by running the following
commands:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash"># Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>When the preceding commands run,</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A standalone Stub Runner application gets started.</p>
</li>
<li>
<p>It downloads the stub with coordinates <code>com.example:bookstore:0.0.1.RELEASE:stubs</code> on port <code>9876</code>.</p>
</li>
<li>
<p>It gets downloads from Artifactory running at <code><a href="http://192.168.0.100:8081/artifactory/libs-release-local" class="bare">192.168.0.100:8081/artifactory/libs-release-local</a></code>.</p>
</li>
<li>
<p>After a whil, Stub Runner is running on port <code>8083</code>.</p>
</li>
<li>
<p>The stubs are running at port <code>9876</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>On the server side, we built a stateful stub. We can use curl to assert
that the stubs are setup properly. To do so, run the following commands:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash"># let's execute the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON</code></pre>
</div>
</div>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
If you want use the stubs that you have built locally, on your host,
you should set the <code>-e STUBRUNNER_STUBS_MODE=LOCAL</code> environment variable and mount
the volume of your local m2 (<code>-v "${HOME}/.m2/:/root/.m2:ro"</code>).
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="js/tocbot/tocbot.min.js"></script>
<script type="text/javascript" src="js/toc.js"></script>
<link rel="stylesheet" href="js/highlight/styles/atom-one-dark-reasonable.min.css">
<script src="js/highlight/highlight.min.js"></script>
<script>hljs.initHighlighting()</script>
</body>
</html>