docs: isolate preview and production deploys (#3004)

This commit is contained in:
Valentin Maerten
2026-08-30 19:26:54 +02:00
committed by GitHub
parent 01697af9e0
commit 7eee5cc7e1
8 changed files with 167 additions and 76 deletions

View File

@@ -1,6 +1,6 @@
import { defineConfig, HeadConfig } from 'vitepress';
import githubLinksPlugin from './plugins/github-links';
import { readdirSync, readFileSync } from 'fs';
import { readdirSync, readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';
import matter from 'gray-matter';
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
@@ -11,7 +11,7 @@ import {
} from 'vitepress-plugin-group-icons';
import { team } from './team.ts';
import { adopters } from './adopters.ts';
import { taskDescription, taskName, ogUrl, ogImage } from './meta.ts';
import { taskDescription, taskName, ogImage } from './meta.ts';
import { fileURLToPath, URL } from 'node:url';
import llmstxt from 'vitepress-plugin-llms';
import { sidebar as nextSidebar } from './sidebar/next.ts';
@@ -30,6 +30,9 @@ const version = readFileSync(
const isLatest = process.env.DOCS_CHANNEL === 'latest';
const channel = isLatest ? 'latest' : 'next';
const other = isLatest ? 'next' : 'latest';
const isPublicDeploy =
process.env.DOCS_SITE === 'production' && process.env.DOCS_LOCAL !== '1';
const isProduction = isLatest && isPublicDeploy;
const docsSidebar = isLatest ? latestSidebar : nextSidebar;
@@ -98,65 +101,89 @@ export default defineConfig({
{ name: 'author', content: `${team.map((c) => c.name).join(', ')}` }
],
// Open Graph
['meta', { property: 'og:type', content: 'website' }],
['meta', { property: 'og:site_name', content: 'Task' }],
['meta', { property: 'og:image', content: ogImage }],
// Twitter Card
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['meta', { name: 'twitter:site', content: '@taskfiledev' }],
['meta', { name: 'twitter:image', content: ogImage }],
...(isPublicDeploy
? ([
[
'script',
{
defer: '',
src: 'https://u.taskfile.dev/script.js',
'data-website-id': '084030b0-0e3f-4891-8d2a-0c12c40f5933'
}
]
] satisfies HeadConfig[])
: []),
[
'meta',
{
name: 'keywords',
content:
'task runner, build tool, taskfile, yaml build tool, go task runner, make alternative, cross-platform build tool, makefile alternative, automation tool, ci cd pipeline, developer productivity, build automation, command line tool, go binary, yaml configuration'
}
],
[
"script",
{
defer: "",
src: "https://u.taskfile.dev/script.js",
"data-website-id": "084030b0-0e3f-4891-8d2a-0c12c40f5933"
}
],
[
"script",
{ type: "application/ld+json" },
'script',
{ type: 'application/ld+json' },
JSON.stringify({
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Task",
"url": "https://taskfile.dev/"
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Task',
url: 'https://taskfile.dev/'
})
]
],
transformHead({ pageData }) {
const head: HeadConfig[] = []
const head: HeadConfig[] = [];
// Canonical URL dynamique
const canonicalUrl = `https://taskfile.dev/${pageData.relativePath
const canonicalPath = pageData.relativePath
.replace(/\.md$/, '')
.replace(/index$/, '')}`
head.push(['link', { rel: 'canonical', href: canonicalUrl }])
.replace(/index$/, '');
const canonicalUrl = new URL(
typeof pageData.frontmatter.canonical === 'string'
? pageData.frontmatter.canonical
: canonicalPath,
'https://taskfile.dev/'
).href;
head.push(['link', { rel: 'canonical', href: canonicalUrl }]);
// Dynamic Open Graph and Twitter meta tags
const isHome = pageData.relativePath === 'index.md';
var pageTitle = pageData.frontmatter.title || pageData.title || taskName;
const isHome = new URL(canonicalUrl).pathname === '/';
let pageTitle = pageData.frontmatter.title || pageData.title || taskName;
if (!isHome) {
pageTitle = `${pageTitle} | ${taskName}`;
}
const pageDescription = pageData.frontmatter.description || pageData.description || taskDescription
head.push(['meta', { property: 'og:title', content: pageTitle }])
head.push(['meta', { property: 'og:description', content: pageDescription }])
head.push(['meta', { property: 'og:url', content: canonicalUrl }])
head.push(['meta', { name: 'twitter:title', content: pageTitle }])
head.push(['meta', { name: 'twitter:description', content: pageDescription }])
const pageDescription =
pageData.frontmatter.description ||
pageData.description ||
taskDescription;
head.push([
'meta',
{
property: 'og:type',
content:
canonicalUrl.includes('/blog/') && !canonicalUrl.endsWith('/blog/')
? 'article'
: 'website'
}
]);
head.push(['meta', { property: 'og:title', content: pageTitle }]);
head.push([
'meta',
{ property: 'og:description', content: pageDescription }
]);
head.push(['meta', { property: 'og:url', content: canonicalUrl }]);
head.push(['meta', { name: 'twitter:title', content: pageTitle }]);
head.push([
'meta',
{ name: 'twitter:description', content: pageDescription }
]);
// Noindex pour 404
if (pageData.relativePath === '404.md') {
head.push(['meta', { name: 'robots', content: 'noindex, nofollow' }])
// Only the released public site is indexable. The public next site and
// previews keep production canonicals but must never be indexed.
if (
!isProduction ||
pageData.relativePath === '404.md' ||
pageData.frontmatter.noindex === true
) {
head.push(['meta', { name: 'robots', content: 'noindex, nofollow' }]);
}
// Structured data for the adopters carousel on the homepage: an ItemList
@@ -184,7 +211,7 @@ export default defineConfig({
}
}))
})
])
]);
}
// On the /adopters page, emit CollectionPage + ItemList (richer than the
@@ -219,7 +246,7 @@ export default defineConfig({
}))
}
})
])
]);
head.push([
'script',
@@ -262,14 +289,14 @@ export default defineConfig({
}
]
})
])
]);
}
return head
return head;
},
srcDir: 'src',
cleanUrls: true,
srcExclude: [`${other}/**`],
srcExclude: [`${other}/**`, `${channel}/docs/**/template.md`],
rewrites: { [`${channel}/:path*`]: ':path*' },
markdown: {
config: (md) => {
@@ -333,14 +360,28 @@ export default defineConfig({
code: 'CESI65QJ',
placement: 'taskfiledev'
},
search: {
provider: 'algolia',
options: {
appId: '7IZIJ13AI7',
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
indexName: 'taskfile'
}
},
search: isProduction
? {
provider: 'algolia',
options: {
appId: '7IZIJ13AI7',
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
indexName: 'taskfile'
}
}
: {
provider: 'local',
options: {
detailedView: true,
miniSearch: {
searchOptions: {
fuzzy: 0.2,
prefix: true,
boost: { title: 4, titles: 2, text: 1 }
}
}
}
},
nav: [
{ text: 'Home', link: '/' },
{
@@ -411,12 +452,18 @@ export default defineConfig({
}
},
sitemap: {
hostname: 'https://taskfile.dev',
transformItems: (items) => {
return items.map((item) => ({
...item,
lastmod: new Date().toISOString()
}));
}
hostname: 'https://taskfile.dev'
},
buildEnd({ outDir }) {
const robots = isProduction
? [
'User-agent: *',
'Allow: /',
'',
'Sitemap: https://taskfile.dev/sitemap.xml',
''
]
: ['User-agent: *', 'Disallow: /', ''];
writeFileSync(resolve(outDir, 'robots.txt'), robots.join('\n'));
}
});

View File

@@ -21,6 +21,7 @@ tasks:
PORT: '{{default "3001" .PORT}}'
env:
DOCS_CHANNEL: '{{.CHANNEL | default "next"}}'
DOCS_SITE: '{{.SITE | default "preview"}}'
# Only the dev server sets this: it is what keeps the localhost URLs of
# the version selector out of a build. See .vitepress/config.ts.
DOCS_LOCAL: '1'
@@ -51,6 +52,7 @@ tasks:
deps: [install]
env:
DOCS_CHANNEL: '{{.CHANNEL | default "next"}}'
DOCS_SITE: '{{.SITE | default "preview"}}'
cmds:
- pnpm build
@@ -58,7 +60,7 @@ tasks:
desc: Build website with the content of the released version
cmds:
- task: build
vars: { CHANNEL: latest }
vars: { CHANNEL: latest, SITE: production }
preview:
desc: Preview Website
@@ -80,8 +82,9 @@ tasks:
# channel these tasks just built.
deploy:next:
desc: Build and deploy next.taskfile.dev
deps: [build:next]
cmds:
- task: build
vars: { CHANNEL: next, SITE: production }
- pnpm netlify deploy --prod --no-build --site=4e13dfcf-fc0d-4bec-ad60-b918a8dc3942
deploy:prod:

View File

@@ -1,3 +1,18 @@
[build]
publish = ".vitepress/dist"
command = "pnpm run build"
[build.environment]
NODE_VERSION = "24"
[context.production.environment]
DOCS_CHANNEL = "latest"
DOCS_SITE = "production"
[context.deploy-preview.environment]
DOCS_CHANNEL = "next"
DOCS_SITE = "preview"
[context.branch-deploy.environment]
DOCS_CHANNEL = "next"
DOCS_SITE = "preview"

View File

@@ -9,6 +9,6 @@ The Remote Taskfiles experiment has now [been released][changelog] :tada:. To
learn more, you can read the [remote Taskfile docs][remote-taskfile-docs] or
check out our [blog post][blog-post].
[changelog]: ../changelog.md#v3511---2026-05-16
[changelog]: ../changelog.md#v3-51-1-2026-05-16
[remote-taskfile-docs]: ../remote-taskfiles.md
[blog-post]: ../../blog/remote-taskfiles

View File

@@ -85,7 +85,7 @@ experiments:
- **Type**: `boolean`
- **Default**: `false`
- **Description**: Enable verbose output for all tasks
- **CLI equivalent**: [`-v, --verbose`](./cli.md#-v---verbose)
- **CLI equivalent**: [`-v, --verbose`](./cli.md#v-verbose)
- **Environment variable**: [`TASK_VERBOSE`](./environment.md#task-verbose)
```yaml
@@ -97,7 +97,7 @@ verbose: true
- **Type**: `boolean`
- **Default**: `false`
- **Description**: Disables echoing of commands
- **CLI equivalent**: [`-s, --silent`](./cli.md#-s---silent)
- **CLI equivalent**: [`-s, --silent`](./cli.md#s-silent)
- **Environment variable**: [`TASK_SILENT`](./environment.md#task-silent)
```yaml
@@ -110,7 +110,7 @@ silent: true
- **Default**: `true`
- **Description**: Enable colored output. Colors are automatically enabled in CI
environments (`CI=true`).
- **CLI equivalent**: [`-c, --color`](./cli.md#-c---color)
- **CLI equivalent**: [`-c, --color`](./cli.md#c-color)
- **Environment variable**: [`TASK_COLOR`](./environment.md#task-color)
```yaml
@@ -123,7 +123,7 @@ color: false
- **Default**: `false`
- **Description**: Disable fuzzy matching for task names. When enabled, Task
will not suggest similar task names when you mistype a task name.
- **CLI equivalent**: [`--disable-fuzzy`](./cli.md#--disable-fuzzy)
- **CLI equivalent**: [`--disable-fuzzy`](./cli.md#disable-fuzzy)
- **Environment variable**:
[`TASK_DISABLE_FUZZY`](./environment.md#task-disable-fuzzy)
@@ -136,7 +136,7 @@ disable-fuzzy: true
- **Type**: `integer`
- **Minimum**: `1`
- **Description**: Number of concurrent tasks to run
- **CLI equivalent**: [`-C, --concurrency`](./cli.md#-c---concurrency-number)
- **CLI equivalent**: [`-C, --concurrency`](./cli.md#c-concurrency-number)
- **Environment variable**:
[`TASK_CONCURRENCY`](./environment.md#task-concurrency)
@@ -149,7 +149,7 @@ concurrency: 4
- **Type**: `boolean`
- **Default**: `false`
- **Description**: Stop executing dependencies as soon as one of them fail
- **CLI equivalent**: [`-F, --failfast`](./cli.md#-f---failfast)
- **CLI equivalent**: [`-F, --failfast`](./cli.md#f-failfast)
- **Environment variable**: [`TASK_FAILFAST`](./environment.md#task-failfast)
```yaml
@@ -164,7 +164,7 @@ failfast: true
When enabled, Task will display an interactive prompt for any missing required
variable. Requires a TTY. Task automatically detects non-TTY environments (CI
pipelines, etc.) and skips prompts.
- **CLI equivalent**: [`--interactive`](./cli.md#--interactive)
- **CLI equivalent**: [`--interactive`](./cli.md#interactive)
```yaml
interactive: true

View File

@@ -87,7 +87,7 @@ files > defaults.
- **Type**: `string` (`interleaved`, `group`, `prefixed`)
- **Description**: Sets the output style
- **CLI equivalent**: [`--output`](./cli.md#--output-string)
- **CLI equivalent**: [`--output`](./cli.md#o-output-mode)
### `TASK_OUTPUT_GROUP_BEGIN`
@@ -95,7 +95,7 @@ files > defaults.
- **Description**: Message template to print before a task's grouped output.
Only applies when the output style is `group`.
- **CLI equivalent**:
[`--output-group-begin`](./cli.md#--output-group-begin-template)
[`--output-group-begin`](./cli.md#output-group-begin-template)
### `TASK_OUTPUT_GROUP_END`
@@ -103,7 +103,7 @@ files > defaults.
- **Description**: Message template to print after a task's grouped output. Only
applies when the output style is `group`.
- **CLI equivalent**:
[`--output-group-end`](./cli.md#--output-group-end-template)
[`--output-group-end`](./cli.md#output-group-end-template)
### `TASK_OUTPUT_GROUP_ERROR_ONLY`
@@ -112,7 +112,7 @@ files > defaults.
- **Description**: Swallow output from successful tasks. Only applies when the
output style is `group`.
- **CLI equivalent**:
[`--output-group-error-only`](./cli.md#--output-group-error-only)
[`--output-group-error-only`](./cli.md#output-group-error-only)
### `TASK_TEMP_DIR`

View File

@@ -983,7 +983,7 @@ When a command exceeds its timeout, it is terminated and the task fails with an
error, preventing commands from hanging indefinitely in a pipeline. The timeout
bounds the whole step, so an [`if`](#command) condition that hangs is cut short
too, and [`ignore_error`](#command) covers a timeout like any other failure. A
timed-out command reports [`EXIT_CODE`](/docs/reference/templating#exit_code)
timed-out command reports [`EXIT_CODE`](/docs/reference/templating#exit-code)
`124`, following the convention of `timeout(1)`.
A dependency takes the same key:

View File

@@ -1,2 +1,28 @@
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), geolocation=(), microphone=()
Cache-Control: public, max-age=0, must-revalidate
/assets/*
Cache-Control: public, max-age=31536000, immutable
/img/*
Cache-Control: public, max-age=604800, stale-while-revalidate=86400
/*.md
Content-Type: text/markdown; charset=utf-8
Access-Control-Allow-Origin: *
/llms*.txt
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: *
/*.json
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
/Taskfile.yml
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: *