mirror of
https://github.com/go-task/task.git
synced 2026-09-01 19:50:16 +02:00
docs: isolate preview and production deploys (#3004)
This commit is contained in:
@@ -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'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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: *
|
||||
|
||||
Reference in New Issue
Block a user