Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f0c6374
docs(site): split the guide into per-topic pages
vmaerten Aug 30, 2026
99294f2
docs(site): turn the guide into an index with anchor redirects
vmaerten Aug 30, 2026
a91e228
docs(site): add a docs landing page
vmaerten Aug 30, 2026
88034ba
docs(site): fill in missing page titles and descriptions
vmaerten Aug 30, 2026
ba747f8
docs(site): use relative links with extensions throughout
vmaerten Aug 30, 2026
5e3f7cd
docs(site): restore the link definitions the split stranded
vmaerten Aug 30, 2026
552b30d
docs(site): keep the released channel out of the docs landing page
vmaerten Aug 30, 2026
0bdd948
docs(site): group the sidebar by reader intent
vmaerten Aug 30, 2026
36176e0
docs(site): explain variable resolution order
vmaerten Aug 30, 2026
6c6d2fb
docs(site): explain dependencies and concurrency
vmaerten Aug 30, 2026
f8285f4
fix(site): stop the llms plugin from flattening output paths
vmaerten Aug 30, 2026
7116985
docs(site): add an entry point for coding agents
vmaerten Aug 30, 2026
a6418e1
feat(site): emit section and type metadata for search
vmaerten Aug 30, 2026
db8d1a7
fix(site): clean the right dist directory
vmaerten Aug 30, 2026
adce7fc
docs(site): define the missing link reference in the any-variables post
vmaerten Aug 30, 2026
98db9fc
docs(site): drop em dashes from the documentation
vmaerten Aug 30, 2026
a81b7f5
docs(site): make the concurrency examples runnable
vmaerten Aug 30, 2026
20c55b5
docs(site): correct what secret and env actually do
vmaerten Aug 30, 2026
c7e2953
docs(site): fold the concepts pages into the guide
vmaerten Aug 30, 2026
9e8f91d
feat(site): put the DocSearch crawler configuration in the repository
vmaerten Aug 30, 2026
d227c18
feat(site): harden the crawler configuration
vmaerten Aug 30, 2026
2f4a65c
fix(site): preserve Markdown links across documentation releases
vmaerten Sep 8, 2026
07071cf
docs: clarify variable resolution and output grouping
vmaerten Sep 8, 2026
d1b2584
fix(site): improve documentation navigation and readability
vmaerten Sep 8, 2026
55eb2bd
docs(site): organize guides around practical workflows
vmaerten Sep 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,58 @@ jobs:
- name: 📋 Validate JSON Schema
run: check-jsonschema --check-metaschema website/src/public/next-schema.json website/src/public/schema.json

website:
name: 📚 Build documentation channels
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: ⬇️ Setup Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.x

- name: ⬇️ Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'

- name: ⬇️ Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
with:
package_json_file: website/package.json

- name: ⬇️ Install website dependencies
run: pnpm install --frozen-lockfile
working-directory: website

- name: 📚 Check next documentation
run: pnpm build && pnpm check:links
working-directory: website
env:
DOCS_CHANNEL: next
DOCS_SITE: preview

- name: 📚 Check released documentation
run: pnpm build && pnpm check:links
working-directory: website
env:
DOCS_CHANNEL: latest
DOCS_SITE: production

# Exercise the actual promotion in this disposable checkout. The current
# latest content alone cannot reveal links to files missing from promotion.
- name: 📚 Simulate a release
run: go run ./cmd/release patch

- name: 📚 Check promoted documentation
run: pnpm build && pnpm check:links
working-directory: website
env:
DOCS_CHANNEL: latest
DOCS_SITE: production

check-latest-content:
name: 📚 Check latest content
# Pull requests only: the release commit is pushed straight to main and is
Expand Down
1 change: 1 addition & 0 deletions cmd/release/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (
}

promotedFiles = []promotion{
{"website/src/next/agents.md", "website/src/latest/agents.md"},
{"website/.vitepress/sidebar/next.ts", "website/.vitepress/sidebar/latest.ts"},
{"website/src/public/next-schema.json", "website/src/public/schema.json"},
{"website/src/public/next-schema-taskrc.json", "website/src/public/schema-taskrc.json"},
Expand Down
30 changes: 30 additions & 0 deletions website/.vitepress/components/GuideRedirect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { guideAnchors } from '../guideAnchors';

// The guide used to be a single page, so links to it in issues, blog posts and
// Stack Overflow answers point at anchors that now live on other pages. Netlify
// never receives the fragment, so this has to be resolved in the browser.
//
// location.replace rather than the VitePress router: the router leaves the new
// fragment unscrolled, and it pushes a history entry, so going back would land
// on the guide with the old hash still set and redirect again.
function resolve() {
const hash = window.location.hash.slice(1);
if (!hash) return;

const target = guideAnchors[decodeURIComponent(hash).toLowerCase()];
if (target) window.location.replace(target);
}

onMounted(() => {
resolve();
window.addEventListener('hashchange', resolve);
});

onUnmounted(() => window.removeEventListener('hashchange', resolve));
</script>

<template>
<span hidden />
</template>
40 changes: 37 additions & 3 deletions website/.vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig, HeadConfig } from 'vitepress';
import githubLinksPlugin from './plugins/github-links';
import { renderSearchContent } from './plugins/local-search';
import { readdirSync, readFileSync, writeFileSync } from 'fs';
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';
import matter from 'gray-matter';
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
Expand Down Expand Up @@ -85,6 +85,10 @@ const urlVersion =
next: 'https://next.taskfile.dev/'
};

const hasDocsOverview = existsSync(
resolve(__dirname, `../src/${channel}/docs/index.md`)
);

// https://vitepress.dev/reference/site-config
export default defineConfig({
title: taskName,
Expand Down Expand Up @@ -145,6 +149,23 @@ export default defineConfig({
).href;
head.push(['link', { rel: 'canonical', href: canonicalUrl }]);

// The DocSearch crawler otherwise has to infer a record's section from the
// active sidebar link in the DOM. Stating it on the page is steadier: it
// survives a theme upgrade, and it is what hierarchy.lvl0 - the breadcrumb
// on every search result - should be set from.
if (pageData.frontmatter.section) {
head.push([
'meta',
{ name: 'docsearch:section', content: pageData.frontmatter.section }
]);
}
if (pageData.frontmatter.docType) {
head.push([
'meta',
{ name: 'docsearch:doc_type', content: pageData.frontmatter.docType }
]);
}

// Dynamic Open Graph and Twitter meta tags
const isHome = new URL(canonicalUrl).pathname === '/';
let pageTitle = pageData.frontmatter.title || pageData.title || taskName;
Expand Down Expand Up @@ -298,6 +319,11 @@ export default defineConfig({
srcDir: 'src',
cleanUrls: true,
srcExclude: [`${other}/**`, `${channel}/docs/**/template.md`],
// A function rather than the equivalent `{ '<channel>/:path*': ':path*' }`.
// vitepress-plugin-llms reuses this config to name its Markdown output, and
// on the object form it compiles the `:path*` array parameter back without
// separators, producing dist/docsreferencecli.md instead of
// dist/docs/reference/cli.md and breaking every relative link in them.
rewrites: (id) =>
id.startsWith(`${channel}/`) ? id.slice(channel.length + 1) : id,
markdown: {
Expand Down Expand Up @@ -352,6 +378,7 @@ export default defineConfig({

themeConfig: {
logo: '/img/logo.svg',
sidebarMenuLabel: 'Documentation',
carbonAds: {
code: 'CESI65QJ',
placement: 'taskfiledev'
Expand All @@ -368,8 +395,15 @@ export default defineConfig({
: {
provider: 'local',
options: {
_render: renderSearchContent,
detailedView: true,
// Match the public DocSearch scope: current docs, without release
// notes or blog posts competing with feature documentation.
_render(src, env, md) {
const path = env.relativePath.replace(/^(next|latest)\//, '');
if (!path.startsWith('docs/') || path === 'docs/changelog.md')
return '';
return renderSearchContent(src, env, md);
},
miniSearch: {
searchOptions: {
fuzzy: 0.2,
Expand All @@ -383,7 +417,7 @@ export default defineConfig({
{ text: 'Home', link: '/' },
{
text: 'Docs',
link: '/docs/guide',
link: hasDocsOverview ? '/docs/' : '/docs/guide',
activeMatch: '^/docs'
},
{ text: 'Blog', link: '/blog', activeMatch: '^/blog' },
Expand Down
103 changes: 103 additions & 0 deletions website/.vitepress/guideAnchors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Where each section of the old single-page guide went when it was
// split up. Netlify never sees the URL fragment, so a _redirects rule
// cannot route these; GuideRedirect.vue resolves them in the browser.
export const guideAnchors: Record<string, string> = {
'running-taskfiles': '/docs/guide/running-tasks',
'supported-file-names': '/docs/guide/running-tasks#supported-file-names',
'running-a-taskfile-from-a-subdirectory':
'/docs/guide/running-tasks#running-a-taskfile-from-a-subdirectory',
'running-a-global-taskfile':
'/docs/guide/running-tasks#running-a-global-taskfile',
'running-a-taskfile-from-stdin':
'/docs/guide/running-tasks#running-a-taskfile-from-stdin',
'running-a-remote-taskfile':
'/docs/remote-taskfiles#specifying-a-remote-entrypoint',
'environment-variables': '/docs/guide/environment',
task: '/docs/guide/environment#task',
'env-files': '/docs/guide/environment#env-files',
'including-other-taskfiles': '/docs/guide/includes',
'remote-taskfiles': '/docs/guide/includes#remote-taskfiles',
'os-specific-taskfiles': '/docs/guide/includes#os-specific-taskfiles',
'directory-of-included-taskfile':
'/docs/guide/includes#directory-of-included-taskfile',
'optional-includes': '/docs/guide/includes#optional-includes',
'internal-includes': '/docs/guide/includes#internal-includes',
'flatten-includes': '/docs/guide/includes#flatten-includes',
'exclude-tasks-from-being-included':
'/docs/guide/includes#exclude-tasks-from-being-included',
'vars-of-included-taskfiles':
'/docs/guide/includes#vars-of-included-taskfiles',
'namespace-aliases': '/docs/guide/includes#namespace-aliases',
'internal-tasks': '/docs/guide/defining-tasks#internal-tasks',
'task-directory': '/docs/guide/defining-tasks#task-directory',
'task-dependencies': '/docs/guide/dependencies#task-dependencies',
'fail-fast-dependencies': '/docs/guide/dependencies#fail-fast-dependencies',
'platform-specific-tasks-and-commands':
'/docs/guide/platforms#platform-specific-tasks-and-commands',
'calling-another-task': '/docs/guide/dependencies#calling-another-task',
'prevent-unnecessary-work': '/docs/guide/up-to-date',
'by-fingerprinting-locally-generated-files-and-their-sources':
'/docs/guide/up-to-date#by-fingerprinting-locally-generated-files-and-their-sources',
'using-programmatic-checks-to-indicate-a-task-is-up-to-date':
'/docs/guide/up-to-date#using-programmatic-checks-to-indicate-a-task-is-up-to-date',
'using-programmatic-checks-to-cancel-the-execution-of-a-task-and-its-dependencies':
'/docs/guide/conditional-execution#using-programmatic-checks-to-cancel-the-execution-of-a-task-and-its-dependencies',
'conditional-execution-with-if':
'/docs/guide/conditional-execution#conditional-execution-with-if',
'task-level-if': '/docs/guide/conditional-execution#task-level-if',
'command-level-if': '/docs/guide/conditional-execution#command-level-if',
'using-templates-in-if-conditions':
'/docs/guide/conditional-execution#using-templates-in-if-conditions',
'using-if-with-for-loops':
'/docs/guide/conditional-execution#using-if-with-for-loops',
'if-vs-preconditions':
'/docs/guide/conditional-execution#if-vs-preconditions',
'limiting-when-tasks-run': '/docs/guide/dependencies#repeated-calls',
'ensuring-required-variables-are-set':
'/docs/guide/required-variables#ensuring-required-variables-are-set',
'ensuring-required-variables-have-allowed-values':
'/docs/guide/required-variables#ensuring-required-variables-have-allowed-values',
'using-variable-references-for-enum-values':
'/docs/guide/required-variables#using-variable-references-for-enum-values',
'prompting-for-missing-variables-interactively':
'/docs/guide/required-variables#prompting-for-missing-variables-interactively',
variables: '/docs/guide/variables',
'dynamic-variables': '/docs/guide/variables#dynamic-variables',
'referencing-other-variables':
'/docs/guide/variables#referencing-other-variables',
'parsing-json-yaml-into-map-variables':
'/docs/guide/variables#parsing-json-yaml-into-map-variables',
'secret-variables': '/docs/guide/secret-variables',
'looping-over-values': '/docs/guide/loops',
'looping-over-a-static-list': '/docs/guide/loops#looping-over-a-static-list',
'looping-over-a-matrix': '/docs/guide/loops#looping-over-a-matrix',
'looping-over-your-task-s-sources-or-generated-files':
'/docs/guide/loops#looping-over-your-task-s-sources-or-generated-files',
'looping-over-variables': '/docs/guide/loops#looping-over-variables',
'renaming-variables': '/docs/guide/loops#renaming-variables',
'looping-over-tasks': '/docs/guide/loops#looping-over-tasks',
'looping-over-dependencies': '/docs/guide/loops#looping-over-dependencies',
'forwarding-cli-arguments-to-commands':
'/docs/guide/arguments#forwarding-cli-arguments-to-commands',
'wildcard-arguments': '/docs/guide/arguments#wildcard-arguments',
'doing-task-cleanup-with-defer':
'/docs/guide/errors-and-cleanup#cleanup-with-defer',
help: '/docs/guide/defining-tasks#help',
'display-summary-of-task':
'/docs/guide/defining-tasks#display-summary-of-task',
'task-aliases': '/docs/guide/defining-tasks#task-aliases',
'overriding-task-name': '/docs/guide/defining-tasks#overriding-task-name',
'warning-prompts': '/docs/guide/conditional-execution#confirmation-prompts',
'silent-mode': '/docs/guide/output#silent-mode',
'dry-run-mode': '/docs/guide/running-tasks#dry-run-mode',
'ignore-errors': '/docs/guide/errors-and-cleanup#ignoring-command-errors',
'output-syntax': '/docs/guide/output#output-syntax',
'ci-integration': '/docs/guide/output#ci-integration',
'colored-output': '/docs/guide/output#colored-output',
'error-annotations': '/docs/guide/output#error-annotations',
'interactive-cli-application':
'/docs/guide/running-tasks#interactive-cli-application',
'short-task-syntax': '/docs/guide/defining-tasks#short-task-syntax',
'set-and-shopt': '/docs/guide/platforms#set-and-shopt',
'watch-tasks': '/docs/guide/watch'
};
Loading