Merge pull request #20563 from dvdksn/learning-paths

site: add learning paths
This commit is contained in:
David Karlsson 2024-08-23 06:04:56 +02:00 committed by GitHub
commit b7f0232bc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 697 additions and 96 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View File

@ -0,0 +1,13 @@
---
title: Learning paths
description: |
Docker Learning Paths offer structured guides to help you master Docker.
keywords: docker, learning, paths, tutorials, resources
---
<p class="w-2/3">
Docker Learning Paths offer structured guides to help you master Docker tools
and services. Each path includes tutorials and resources on topics like Docker
Scout and Docker Build Cloud. Start learning at your own pace and enhance your
Docker skills.
</p>

View File

@ -0,0 +1,158 @@
---
title: "Docker Build Cloud: Reclaim your time with fast, multi-architecture builds"
description: |
Learn how to build and deploy Docker images to the cloud with Docker Build Cloud.
summary: |
Create applications up to 39x faster using cloud-based resources, shared team cache, and native multi-architecture support.
params:
image: images/learning-paths/build-cloud.png
skill: Beginner
time: 10 minutes
prereq: None
---
{{< columns >}}
<!-- vale Vale.Spelling = NO -->
98% of developers spend up to an hour every day waiting for builds to finish
([Incredibuild: 2022 Big Dev Build Times](https://www.incredibuild.com/survey-report-2022)).
Heavy, complex builds can become a major roadblock for development teams,
slowing down both local development and CI/CD pipelines.
<!-- vale Vale.Spelling = YES -->
Docker Build Cloud speeds up image build times to improve developer
productivity, reduce frustrations, and help you shorten the release cycle.
## Whos this for?
- Anyone who wants to tackle common causes of slow image builds: limited local
resources, slow emulation, and lack of build collaboration across a team.
- Developers working on older machines who want to build faster.
- Development teams working on the same repository who want to cut wait times
with a shared cache.
- Developers performing multi-architecture builds who dont want to spend hours
configuring and rebuilding for emulators.
<!-- break -->
## What youll learn
- Building container images faster locally and in CI
- Accelerating builds for multi-platform images
- Reusing pre-built images to expedite workflows
## Tools integration
Works well with Docker Compose, GitHub Actions, and other CI solutions
{{< /columns >}}
## Modules
{{< accordion large=true title=`Why Docker Build Cloud?` icon=`play_circle` >}}
Docker Build Cloud is a service that lets you build container images faster,
both locally and in CI. Builds run on cloud infrastructure optimally
dimensioned for your workloads, with no configuration required. The service
uses a remote build cache, ensuring fast builds anywhere and for all team
members.
- Docker Build Cloud provides several benefits over local builds:
- Improved build speed
- Shared build cache
- Native multi-platform builds
Theres no need to worry about managing builders or infrastructure — simply
connect to your builders and start building. Each cloud builder provisioned to
an organization is completely isolated to a single Amazon EC2 instance, with a
dedicated EBS volume for build cache and encryption in transit. That means
there are no shared processes or data between cloud builders.
{{< youtube-embed "8AqKhEO2PQA" >}}
{{< /accordion >}}
{{< accordion large=true title=`Demo: using Docker Build Cloud in CI` icon=`play_circle` >}}
Speed up your build pipelines — delegate the build execution to Docker Build Cloud in CI.
{{< youtube-embed "wvLdInoVBGg" >}}
{{< /accordion >}}
{{< accordion large=true title=`Common challenges and questions` icon=`quiz` >}}
### Is Docker Build Cloud a standalone product or a part of Docker Desktop?
Docker Build Cloud is a service that can be used both with Docker Desktop and
standalone. It lets you build your container images faster, both locally and in
CI, with builds running on cloud infrastructure. The service uses a remote
build cache, ensuring fast builds anywhere and for all team members.
When used with Docker Desktop, the [Builds view](/desktop/use-desktop/builds/)
works with Docker Build Cloud out-of-the-box. It shows information about your
builds and those initiated by your team members using the same builder,
enabling collaborative troubleshooting.
To use Docker Build Cloud without Docker Desktop, you must
[download and install](/build-cloud/setup/#use-docker-build-cloud-without-docker-desktop)
a version of Buildx with support for Docker Build Cloud (the `cloud` driver).
If you plan on building with Docker Build Cloud using the `docker compose
build` command, you also need a version of Docker Compose that supports Docker
Build Cloud.
### How does Docker Build Cloud work with Docker Compose?
Docker Compose works out of the box with Docker Build Cloud. Install the Docker
Build Cloud-compatible client (buildx) and it works with both commands.
### How many minutes are included in Docker Build Cloud Team plans?
You receive 200 minutes per month per purchased seat. If you are also a Docker
subscriber (Personal, Pro, Team, Business), you will also receive your included
build minutes from that plan.
For example, if a Docker Team customer purchases 5 Build Cloud Team seats, they
will have 400 minutes from their Docker Team plan plus 1000 minutes (200 min/mo
* 5 seats) for a total of 1400 minutes per month.
### Im a Docker personal user. Can I try Docker Build Cloud?
Docker subscribers (Pro, Team, Business) receive a set number of minutes each
month, shared across the account, to use Build Cloud.
If you do not have a Docker subscription, you may sign up for a free Personal
account and get 50 minutes per month. Personal accounts are limited to a single
user.
For teams to receive the shared cache benefit, they must either be on a Docker
Team, Docker Business, or paid Build Cloud Team plan. You may buy a month of
Build Cloud Team for the number of seats testing.
### Does Docker Build Cloud support CI platforms? Does it work with GitHub Actions?
Yes, Docker Build Cloud can be used with various CI platforms including GitHub
Actions, CircleCI, Jenkins, and others. It can speed up your build pipelines,
which means less time spent waiting and context switching.
Docker Build Cloud can be used with GitHub Actions to automate your build,
test, and deployment pipeline. Docker provides a set of official GitHub Actions
that you can use in your workflows.
Using GitHub Actions with Docker Build Cloud is straightforward. With a
one-line change in your GitHub Actions configuration, everything else stays the
same. You don't need to create new pipelines. Learn more in the [CI
documentation](/build-cloud/ci/) for Docker Build Cloud.
{{< /accordion >}}
{{< accordion large=true title=`Resources` icon=`link` >}}
- [Product page](https://www.docker.com/products/build-cloud/)
- [Docker Build Cloud overview](/build-cloud/)
- [Subscriptions and features](/subscription/build-cloud/build-details/)
- [Using Docker Build Cloud](/build-cloud/usage/)
{{< /accordion >}}

View File

@ -0,0 +1,156 @@
---
title: Defining and Running Multi-Container Applications with Docker Compose
summary: Simplify the process of defining, configuring, and running multi-container Docker applications to enable efficient development, testing, and deployment.
description: Learn how to use Docker Compose to define and run multi-container Docker applications.
params:
image: images/learning-paths/compose.png
skill: Beginner
time: 5 minutes
prereq: None
---
{{< columns >}}
Developers face challenges with multi-container Docker applications, including
complex configuration, dependency management, and maintaining consistent
environments. Networking, resource allocation, data persistence, logging, and
monitoring add to the difficulty. Security concerns and troubleshooting issues
further complicate the process, requiring effective tools and practices for
efficient management.
Docker Compose solves the problem of managing multi-container Docker
applications by providing a simple way to define, configure, and run all the
containers needed for an application using a single YAML file. This approach
helps developers to easily set up, share, and maintain consistent development,
testing, and production environments, ensuring that complex applications can be
deployed with all their dependencies and services properly configured and
orchestrated.
<!-- break -->
## Whos this for?
- Developers and DevOps engineers who need to define, manage, and orchestrate
multi-container Docker applications efficiently across multiple environments.
- Development teams that want to increase productivity by streamlining
development workflows and reducing setup time.
## Tools integration
Works well with Docker CLI, CI/CD tools, and container orchestration tools.
{{< /columns >}}
## Modules
{{< accordion large=true title=`Why Docker Compose?` icon=`play_circle` >}}
Docker Compose is an essential tool for defining and running multi-container
Docker applications. Docker Compose simplifies the Docker experience, making it
easier for developers to create, manage, and deploy applications by using YAML
files to configure application services.
Docker Compose provides several benefits:
- Lets you define multi-container applications in a single YAML file.
- Ensures consistent environments across development, testing, and production.
- Manages the startup and linking of multiple containers effortlessly.
- Streamlines development workflows and reduces setup time.
- Ensures that each service runs in its own container, avoiding conflicts.
{{< youtube-embed 2EqarOM2V4U >}}
{{< /accordion >}}
{{< accordion large=true title=`Demo: Set up and use Docker Compose` icon=`play_circle` >}}
{{< youtube-embed P5RBKmOLPH4 >}}
{{< /accordion >}}
{{< accordion large=true title=`Common challenges and questions` icon=`quiz` >}}
<!-- vale Docker.HeadingLength = NO -->
### Do I need to maintain a separate Compose file for my development, testing, and staging environments?
You don't necessarily need to maintain entirely separate Compose files for your
development, testing, and staging environments. You can define all your
services in a single Compose file (`compose.yml`). You can use profiles to
group service configurations specific to each environment (`dev`, `test`,
`staging`).
When you need to spin up an environment, you can activate the corresponding
profiles. For example, to set up the development environment:
```console
$ docker compose --profile dev up
```
This command starts only the services associated with the `dev` profile,
leaving the rest inactive.
For more information on using profiles, see [Using profiles with
Compose](/compose/profiles/).
### How can I enforce the database service to start up before the frontend service?
Docker Compose ensures services start in a specific order by using the
`depends_on` property. This tells Compose to start the database service before
even attempting to launch the frontend service. This is crucial since
applications often rely on databases being ready for connections.
However, `depends_on` only guarantees the order, not that the database is fully
initialized. For a more robust approach, especially if your application relies
on a prepared database (e.g., after migrations), consider [health
checks](/reference/compose-file/services.md#healthcheck). Here, you can
configure the frontend to wait until the database passes its health check
before starting. This ensures the database is not only up but also ready to
handle requests.
For more information on setting the startup order of your services, see
[Control startup and shutdown order in Compose](/compose/startup-order/).
### Can I use Compose to build a Docker image?
Yes, you can use Docker Compose to build Docker images. Docker Compose is a
tool for defining and running multi-container applications. Even if your
application isn't a multi-container application, Docker Compose can make it
easier to run by defining all the `docker run` options in a file.
To use Compose, you need a `compose.yml` file. In this file, you can specify
the build context and Dockerfile for each service. When you run the command
`docker compose up --build`, Docker Compose will build the images for each
service and then start the containers.
For more information on building Docker images using Compose, see the [Compose
Build Specification](/compose/compose-file/build/).
### What is the difference between Docker Compose and Dockerfile?
A Dockerfile provides instructions to build a container image while a Compose
file defines your running containers. Quite often, a Compose file references a
Dockerfile to build an image to use for a particular service.
### What is the difference between the `docker compose up` and `docker compose run` commands?
The `docker compose up` command creates and starts all your services. It's
perfect for launching your development environment or running the entire
application. The `docker compose run` command focuses on individual services.
It starts a specified service along with its dependencies, allowing you to run
tests or perform one-off tasks within that container.
<!-- vale Docker.HeadingLength = YES -->
{{< /accordion >}}
{{< accordion large=true title=`Resources` icon=`link` >}}
- [Overview of Docker Compose CLI](/compose/reference/)
- [Overview of Docker Compose](/compose/)
- [How Compose works](/compose/compose-application-model/)
- [Using profiles with Compose](/compose/profiles/)
- [Control startup and shutdown order with Compose](/compose/startup-order/)
- [Compose Build Specification](/compose/compose-file/build/)
{{< /accordion >}}

View File

@ -0,0 +1,177 @@
---
title: Securing your software supply chain with Docker Scout
summary: |
Enhance container security by automating vulnerability detection and
remediation, ensuring compliance, and protecting your development workflow.
description: |
Learn how to use Docker Scout to enhance container security by automating
vulnerability detection and remediation, ensuring compliance, and protecting
your development workflow.
params:
image: images/learning-paths/scout.png
skill: Beginner
time: 10 minutes
prereq: None
---
{{< columns >}}
When container images are insecure, significant risks can arise. Around 60% of
organizations have reported experiencing at least one security breach or
vulnerability incident within a year, resulting in operational
disruption.[^CSA] These incidents often result in considerable downtime, with
44% of affected companies experiencing over an hour of downtime per event. The
financial impact is substantial, with the average data breach cost reaching
$4.45 million.[^IBM] This highlights the critical importance of maintaining
robust container security measures.
Docker Scout enhances container security by providing automated vulnerability
detection and remediation, addressing insecure container images, and ensuring
compliance with security standards.
[^CSA]: https://cloudsecurityalliance.org/blog/2023/09/21/2023-global-cloud-threat-report-cloud-attacks-are-lightning-fast
[^IBM]: https://www.ibm.com/reports/data-breach
<!-- break -->
## What you'll learn
- Define secure software supply chain (SSSC)
- Review SBOMs and how to use them
- Detect and monitor vulnerabilities
## Tools integration
Works well with Docker Desktop, GitHub Actions, Jenkins, Kubernetes, and
other CI solutions.
{{< /columns >}}
## Whos this for?
DevOps engineers who need to integrate automated security checks into CI/CD
pipelines to enhance the security and efficiency of their workflows. Developers
who want to use Docker Scout to identify and remediate vulnerabilities early in
the development process, ensuring the production of secure container images.
Security professionals who must enforce security compliance, conduct
vulnerability assessments, and ensure the overall security of containerized
applications.
## Modules
{{< accordion large=true title=`Why Docker Scout?` icon=`play_circle` >}}
Organizations face significant challenges from data breaches,
including financial losses, operational disruptions, and long-term damage to
brand reputation and customer trust. Docker Scout addresses critical problems
such as identifying insecure container images, preventing security breaches,
and reducing the risk of operational downtime due to vulnerabilities.
Docker Scout provides several benefits:
- Secure and trusted content
- A system of record for your SDLC
- Continuous security posture improvement
Docker Scout offers automated vulnerability detection and remediation, helping
organizations identify and fix security issues in container images early in the
development process. It also integrates with popular development tools like
Docker Desktop and GitHub Actions, providing seamless security management and
compliance checks within existing workflows.
{{< youtube-embed "-omsQ7Uqyc4" >}}
{{< /accordion >}}
{{< accordion large=true title=`Docker Scout Demo` icon=`play_circle` >}}
Organizations face significant challenges from data breaches,
including financial losses, operational disruptions, and long-term damage to
brand reputation and customer trust. Docker Scout addresses critical problems
such as identifying insecure container images, preventing security breaches,
and reducing the risk of operational downtime due to vulnerabilities.
Docker Scout provides several benefits:
- Secure and trusted content
- A system of record for your SDLC
- Continuous security posture improvement
Docker Scout offers automated vulnerability detection and remediation, helping
organizations identify and fix security issues in container images early in the
development process. It also integrates with popular development tools like
Docker Desktop and GitHub Actions, providing seamless security management and
compliance checks within existing workflows.
{{< youtube-embed "TkLwJ0p46W8" >}}
{{< /accordion >}}
{{< accordion large=true title=`Common challenges and questions` icon=`quiz` >}}
<!-- vale Docker.HeadingLength = NO -->
### How is Docker Scout different from other security tools?
Docker Scout takes a broader approach to container security compared to
third-party security tools. Third-party security tools, if they offer
remediation guidance at all, miss the mark on their limited scope of
application security posture within the software supply chain, and often
limited guidance when it comes to suggested fixes. Such tools have either
limitations on runtime monitoring or no runtime protection at all. When they do
offer runtime monitoring, its limited in its adherence to key policies.
Third-party security tools offer a limited scope of policy evaluation for
Docker-specific builds. By focusing on the entire software supply chain,
providing actionable guidance, and offering comprehensive runtime protection
with strong policy enforcement, Docker Scout goes beyond just identifying
vulnerabilities in your containers. It helps you build secure applications from
the ground up.
### Can I use Docker Scout with external registries other than Docker Hub?
You can use Scout with registries other than Docker Hub. Integrating Docker Scout
with third-party container registries enables Docker Scout to run image
analysis on those repositories so that you can get insights into the
composition of those images even if they aren't hosted on Docker Hub.
The following container registry integrations are available:
- Artifactory
- Amazon Elastic Container Registry
- Azure Container Registry
Learn more about configuring Scout with your registries in [Integrating Docker Scout with third-party registries](/scout/integrations/#container-registries).
### Does Docker Scout CLI come by default with Docker Desktop?
Yes, the Docker Scout CLI plugin comes pre-installed with Docker Desktop.
### Is it possible to run `docker scout` commands on a Linux system without Docker Desktop?
If you run Docker Engine without Docker Desktop, Docker Scout doesn't come
pre-installed, but you can [install it as a standalone binary](/scout/install/).
### How is Docker Scout using an SBOM?
An SBOM, or software bill of materials, is a list of ingredients that make up
software components. [Docker Scout uses SBOMs](/scout/concepts/sbom/) to
determine the components that are used in a Docker image. When you analyze an
image, Docker Scout will either use the SBOM that is attached to the image (as
an attestation), or generate an SBOM on the fly by analyzing the contents of
the image.
The SBOM is cross-referenced with the advisory database to determine if any of
the components in the image have known vulnerabilities.
{{< /accordion >}}
{{< accordion large=true title=`Resources` icon=`link` >}}
- [Docker Scout overview](/scout/)
- [Docker Scout quickstart](/scout/quickstart/)
- [Install Docker Scout](/scout/install/)
- [Software Bill of Materials](/scout/concepts/sbom/)
<!-- vale Docker.HeadingLength = YES -->
{{< /accordion >}}

View File

@ -2446,3 +2446,13 @@ Manuals:
title: Get support
- path: /release-lifecycle/
title: Product release lifecycle
Learning paths:
- title: Overview
path: /learning-paths/
- title: Docker Build Cloud
path: /learning-paths/docker-build-cloud/
- title: Docker Scout
path: /learning-paths/docker-scout/
- title: Docker Compose
path: /learning-paths/docker-compose/

View File

@ -132,6 +132,9 @@ menus:
- name: Reference
url: /reference/
weight: 4
- name: Learning paths
url: /learning-paths/
weight: 5
footer:
- url: https://www.docker.com/products

View File

@ -6,6 +6,7 @@
"--mount",
"--tmpfs",
"-mb-3",
"-mt-1",
"-mt-4",
"-top-16",
"-v",
@ -287,7 +288,9 @@
"h-16",
"h-2",
"h-32",
"h-48",
"h-8",
"h-96",
"h-auto",
"h-full",
"h-max",
@ -376,10 +379,12 @@
"my-0",
"my-2",
"my-4",
"my-6",
"my-8",
"no-underline",
"no-wrap",
"not-prose",
"object-cover",
"openSUSE-and-SLES",
"origin-bottom-right",
"outline",
@ -443,6 +448,8 @@
"shadow",
"sidebar-hover",
"sm:flex-row",
"sm:grid-cols-2",
"sm:items-center",
"space-x-2",
"space-y-4",
"sticky",
@ -487,6 +494,7 @@
"underline-offset-2",
"underline-offset-8",
"w-2",
"w-2/3",
"w-8",
"w-[1200px]",
"w-[32px]",

View File

@ -13,7 +13,7 @@
{{- else if (strings.HasPrefix $url "/") -}}
{{ if (strings.HasSuffix (urls.Parse $url).Path ".md") }}
{{/* abs path to markdown file, use ref */}}
{{ $url = (ref .Page $url) }}
{{ $url = (ref page $url) }}
{{ end }}
<a class="link" href="{{ $url }}">{{ .Text | safeHTML }}</a>
{{- else -}}
@ -21,12 +21,12 @@
{{- if (strings.FindRE `([^_]|^)index.md` $url 1) -}}
<a
class="link"
href="{{ ref .Page (strings.Replace $url "index.md" "_index.md") }}"
href="{{ ref page (strings.Replace $url "index.md" "_index.md") }}"
>{{ .Text | safeHTML }}</a
>
{{- else -}}
{{/* relative link, use ref */}}
<a class="link" href="{{ ref .Page $url }}"
<a class="link" href="{{ ref page $url }}"
>{{ .Text | safeHTML }}</a
>
{{- end -}}

View File

@ -44,58 +44,9 @@
</div>
</main>
<footer>{{ partialCached "footer.html" . }}</footer>
{{ with (.Store.Get "youtube") }}
<script>
(function() {
var tag = document.createElement('script');
tag.id = "youtube-iframe-api";
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
window.onYouTubeIframeAPIReady = function() {
var youtubeDivs = document.querySelectorAll(".youtube-video");
for (var i = 0; i < youtubeDivs.length; i++) {
createPlayer(youtubeDivs[i].id, youtubeDivs[i].dataset.videoId);
}
}
function createPlayer(domElementId, videoId) {
new YT.Player(domElementId, {
width: "100%",
height: "100%",
videoId: videoId,
playerVars: {
'rel': 0,
'iv_load_policy': 3,
'enablejsapi': 1,
'origin': window.location.origin
},
events: {
'onStateChange': function (event) {
onPlayerStateChange(event, videoId);
}
}
});
}
function onPlayerStateChange(event, videoId) {
if (window.heap === undefined) return;
var properties = {
video_id: videoId,
page_path: window.location.pathname,
page_title: document.title,
};
if (event.data == YT.PlayerState.PLAYING) {
heap.track("Video Play", properties);
} else if (event.data == YT.PlayerState.PAUSED) {
heap.track("Video Paused", properties);
}
}
})();
</script>
{{/* Load the YouTube player if the page embeds a YouTube video */}}
{{ with .Store.Get "youtube" }}
{{- partial "youtube-script.html" . }}
{{ end }}
</body>

View File

@ -7,43 +7,14 @@
<article class="prose max-w-none dark:prose-invert">
<h1 data-pagefind-weight="10" class="scroll-mt-36">{{ .Title }}</h1>
<div class="text-lg">{{ .Summary }}</div>
<div class="not-prose">
<div class="flex flex-col sm:flex-row w-full justify-between gap-4 p-6 m-4 bg-gray-light-200 dark:bg-gray-dark-300">
<div class="flex flex-col">
<span><strong>Skill level</strong></span>
<span>{{ .Params.skill }}</span>
</div>
<div class="flex flex-col">
<span><strong>Time to complete</strong></span>
<span>{{ .Params.time }}</span>
</div>
<div class="flex flex-col">
<span><strong>Prerequisites</strong></span>
<span>{{ .Params.prereq }}</span>
</div>
</div>
</div>
{{ partial "components/guide-summary.html" . }}
{{ .Content }}
{{ partial "heading.html" (dict "text" "Modules" "level" 2) }}
{{ range $i, $e := .Pages }}
{{ $open := compare.Conditional (eq $i 0) true false }}
<div x-data="{ open: {{ $open }} }" class="border border-gray-light-200 dark:border-gray-dark-200 bg-white dark:bg-gray-dark-100 py-2">
<button class="not-prose w-full py-2 px-4 flex justify-between" x-on:click="open = ! open">
<span class="text-xl">{{ fmt.Printf "%d. %s" (add $i 1) .Title }}</span>
<span :class="{ 'hidden' : !open }" class="icon-svg">{{ partialCached "icon" "expand_less" "expand_less" }}</span>
<span :class="{ 'hidden' : open }" class="icon-svg">{{ partialCached "icon" "expand_more" "expand_more" }}</span>
</button>
<div x-show="open" x-collapse class="px-4">
<div class="mb-4">
<div class="mb-4">{{ $e.Summary }}</div>
<div>
<a href="{{ $e.Permalink }}"
class="no-underline cursor-pointer py-2 px-4 rounded bg-blue-light-500 dark:bg-blue-dark-400 hover:bg-blue-light-400 dark:hover:bg-blue-dark-500 text-white"
>Start</a>
</div>
</div>
</div>
</div>
{{ $o := compare.Conditional (eq $i 0) true false }}
{{ $t := fmt.Printf "%d. %s" (add $i 1) $e.Title }}
{{ $b := fmt.Printf "%s\n\n{{< button url=`%s` text=`%s` >}}" $e.Summary $e.Permalink "Start" }}
{{ partial "components/accordion.html" (dict "large" true "open" $o "title" $t "body" $b) }}
{{ end }}
</article>
{{ end }}

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
{{ partial "head.html" . }}
</head>
<body class="flex flex-col min-h-screen bg-background-light text-base dark:bg-background-dark dark:text-white">
{{ partial "header.html" . }}
<main class="flex justify-center">
<div class="lg:w-[1200px] overflow-clip p-6 pt-0 w-lvw">
{{ block "main" . }}
{{ end }}
</div>
</main>
<footer class="mt-auto">{{ partialCached "footer.html" . }}</footer>
{{/* Load the YouTube player if the page embeds a YouTube video */}}
{{ with .Store.Get "youtube" }}
{{- partial "youtube-script.html" . }}
{{ end }}
</body>
</html>

View File

@ -0,0 +1,9 @@
{{ define "main" }}
{{ partial "breadcrumbs.html" . }}
<article class="prose max-w-none dark:prose-invert">
<h1>{{ .Title }}</h1>
{{ .Content }}
<hr>
{{ partial "post-links.html" .Pages }}
</article>
{{ end }}

View File

@ -0,0 +1,14 @@
{{ define "main" }}
{{ partial "breadcrumbs.html" . }}
<article class="prose max-w-none dark:prose-invert">
{{- $img := resources.Get .Params.image }}
<img src="{{ $img.Permalink }}" alt="{{ .Title }}" class="w-full rounded-lg object-cover h-96">
<h1 class="scroll-mt-36">{{ .Title }}</h1>
<div class="text-lg">{{ .Summary }}</div>
{{ partial "components/guide-summary.html" . }}
{{ .Content }}
<hr>
<div class="text-xl">More learning paths</div>
{{ partial "post-links.html" (where .CurrentSection.Pages "Permalink" "ne" page.Permalink) }}
</article>
{{ end }}

View File

@ -1,11 +1,17 @@
<div x-data="{ open: false }" class="border border-gray-light-200 dark:border-gray-dark-200 bg-white dark:bg-gray-dark-100">
<div
id="{{ urls.Anchorize .title }}"
x-data="{ open: {{ .open | default false }} }" class="border border-gray-light-200 dark:border-gray-dark-200 bg-white dark:bg-gray-dark-100 py-2 my-6 rounded">
<button class="not-prose w-full py-2 px-4 flex justify-between" x-on:click="open = ! open">
<span>{{ .title }}</span>
<div class="{{ with .large }} text-xl {{ end }} flex items-center gap-2">
{{- with .icon }}
<span class="icon-svg -mt-1">{{ partialCached "icon" . . }}</span>
{{- end }}
{{ .title }}
</div>
<span :class="{ 'hidden' : !open }" class="icon-svg">{{ partialCached "icon" "expand_less" "expand_less" }}</span>
<span :class="{ 'hidden' : open }" class="icon-svg">{{ partialCached "icon" "expand_more" "expand_more" }}</span>
</button>
<div x-show="open" x-collapse class="px-4">
{{ .body | .page.RenderString (dict "display" "block") }}
{{ markdownify .body }}
</div>
</div>

View File

@ -0,0 +1,16 @@
<div class="not-prose">
<div class="flex flex-col sm:flex-row gap-4 p-6 m-4 bg-gray-light-200 dark:bg-gray-dark-300">
<div class="flex-grow flex flex-col sm:items-center">
<span><strong>Skill level</strong></span>
<span>{{ .Params.skill }}</span>
</div>
<div class="flex-grow flex flex-col sm:items-center">
<span><strong>Time to complete</strong></span>
<span>{{ .Params.time }}</span>
</div>
<div class="flex-grow flex flex-col sm:items-center">
<span><strong>Prerequisites</strong></span>
<span>{{ .Params.prereq }}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,13 @@
<div class="not-prose gap-12 py-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
{{ range . }}
<div class="flex flex-col">
<a class="hover:underline" href="{{ .Permalink }}">
{{- $img := resources.Get .Params.image }}
{{- $img = $img.Process "resize 600x" }}
<img class="h-48 w-full object-cover rounded shadow" src="{{ $img.Permalink }}">
<p class="text-xl leading-snug my-4">{{ .Title }}</p>
</a>
<p class="text-sm">{{ .Summary }} <a class="link" href="{{ .Permalink }}">Read more</a></p>
</div>
{{ end }}
</div>

View File

@ -0,0 +1,51 @@
<script>
(function() {
var tag = document.createElement('script');
tag.id = "youtube-iframe-api";
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
window.onYouTubeIframeAPIReady = function() {
var youtubeDivs = document.querySelectorAll(".youtube-video");
for (var i = 0; i < youtubeDivs.length; i++) {
createPlayer(youtubeDivs[i].id, youtubeDivs[i].dataset.videoId);
}
}
function createPlayer(domElementId, videoId) {
new YT.Player(domElementId, {
width: "100%",
height: "100%",
videoId: videoId,
playerVars: {
'rel': 0,
'iv_load_policy': 3,
'enablejsapi': 1,
'origin': window.location.origin
},
events: {
'onStateChange': function (event) {
onPlayerStateChange(event, videoId);
}
}
});
}
function onPlayerStateChange(event, videoId) {
if (window.heap === undefined) return;
var properties = {
video_id: videoId,
page_path: window.location.pathname,
page_title: document.title,
};
if (event.data == YT.PlayerState.PLAYING) {
heap.track("Video Play", properties);
} else if (event.data == YT.PlayerState.PAUSED) {
heap.track("Video Paused", properties);
}
}
})();
</script>

View File

@ -1,3 +1,13 @@
{{ $title := .Get "title" }}
{{ $body := .Inner }}
{{ partial "components/accordion.html" (dict "title" $title "body" $body "page" .Page) }}
{{ $icon := .Get "icon" }}
{{ $open := .Get "open" }}
{{ $large := .Get "large" }}
{{ $body := .InnerDeindent }}
{{ partial "components/accordion.html"
(dict "title" $title
"body" $body
"icon" $icon
"open" $open
"large" $large
)
}}

View File

@ -0,0 +1,8 @@
{{ $cols := strings.Split .InnerDeindent "<!-- break -->" }}
<div class="md:flex gap-8">
{{ range $cols }}
<div class="flex-1">
{{ markdownify . }}
</div>
{{ end }}
</div>

View File

@ -1,2 +1,2 @@
{{- .Page.Store.Set "youtube" true -}}
<div id="youtube-player-{{ .Get 0 }}" data-video-id="{{ .Get 0 }}" class="youtube-video aspect-video w-full"></div>
<div id="youtube-player-{{ .Get 0 }}" data-video-id="{{ .Get 0 }}" class="youtube-video aspect-video w-full py-2"></div>

View File

@ -14,6 +14,10 @@
</div>
</main>
<footer class="mt-auto">{{ partialCached "footer.html" . }}</footer>
{{/* Load the YouTube player if the page embeds a YouTube video */}}
{{ with .Store.Get "youtube" }}
{{- partial "youtube-script.html" . }}
{{ end }}
</body>
</html>