docs(website): restructure documentation and move content to dedicated pages

- Split README.md into usage.md and library.md for better organization
- Move library section to its own page and update references
- Rename "Usage" section to "Examples" in README.md
- Update navigation links in base layout to reflect new structure
- Fix typo in function definition in image/README.md
- Add frontmatter to AGENTS.md for proper layout rendering
- Update footer link from GitHub to Codeberg
This commit is contained in:
Janik von Rotz committed 2025-10-29 18:27:56 +01:00
1 parent c054077663
commit a4b7c38d10
6 files changed
+653 -761

No files matched your search

+420
View File
@@ -0,0 +1,420 @@
---
layout: layouts/base.njk
---
# Usage
Running the task file requires a shell alias: `alias task='./task'`\
Show the available commands with `task help`.\
From the specification the project can be installed with `task install`.\
Execute all commands with `task all`.
## Image
See [taskfile.build/image](/image) for details.
## Naming
The naming of functions is important. There are basically two styles:
1. Action + Object
2. Object + Action
The task file functions use the first style. The name of the function starts with an action followed by an object. The object name can be singular or plural.
**Examples for actions**: <span id="actions">activate, install, dev, develop, init, build, start, update, remove, delete, enable, disable, template, convert, create, edit, change, get, set, patch, fetch, generate, push, pull, import, export, list, publish, release, test, setup, prepare, restart, stop, store, restore, translate, upgrade, zip, visualize, sync, switch, run, reset, load, dump, checkout, commit, drop, deploy, handle, trigger, render, lint, uninstall, split, parse, fix, refactor, transform, cat, ls, rm, serve, help, show, filter, login, logout, encrypt, decrypt, upload, download, analyse, transpile, compile, minify, copy</span>
**Examples for objects**: <span id="objects">env, venv, submodule, container, database, snippet, model, module, repo, mail, doc, dependency, view, user, vault, file, host, node, log, password, hash, script, requirement, part, component, system, workspace, image, process, state, platform, dir, folder, readme, overview, lang, level, request, response, result, worker, server, proxy, workflow, volume, network, package, field, value, secret, chart, node, edge, function, method, firewall, html, css, image, svg, style, query, native, group, notebook</span>
**Objects can be tools**: <span id="object-tools">odoo, vupress, nodejs, zsh, bash, fish, podman, kind, minikube, helm, nvim, docker, podman, rust, python, tmux, vim, helix, system, git, pass, llm, sql, dotenv, javascript, vue, vite, astro, typescript, turbo, pnpm, eslint, jenkins, k8s, nextcloud, postgres, metabase, ansible, prometheus, grafana, hugo, deno, bun, babel, panda, gulp, grunt, electron, react, express, mongodb, angular, ionic, meteor, webpack, bower, jupyter</span>
<button id="generate-function-names">Generate Function Names</button> <ul id="function-names"> <!-- Random function names will be inserted here by JS --> </ul>
## Style
The bash scripts follow the [Bash Style Guide | ysap.sh](https://style.ysap.sh/). Have a look at the [AGENTS.md](/AGENTS) for details on the applied styling.
## Patterns
The task file showed above is very basic. Commands can have parameters and call each other. The following is a collection of more complex task file patterns.
### Set default parameter
Fallback to a default value for a parameter.
```bash
build() {
local platform="amd64"
if [[ -n "$1" ]]; then
platform="$1"
fi
```
### Ensure parameter is not empty
Check the first param and exit if it is empty.
```bash
deploy() {
if [[ -z "$1" ]]; then echo "\$1 is empty."; exit; fi
```
### Prompt for input
Use `read` to ask for inputs.
```bash
if [[ -z "$2" ]]; then
read -p "Enter the task description: " task_description
else
task_description="$2"
fi
```
### Setup local env vars
Define env vars at the beginning of the task file.
```bash
CONFIGURATION_FILE="file.conf"
GIT_BRANCH=$(git symbolic-ref --short -q HEAD)
```
### Template with env vars
Create a parameterized file from a template. Requires `envsubst`.
```bash
template-with-env() {
echo "Template $CONFIGURATION_FILE"
export CONFIGURATION_1
export CONFIGURATION_2=${CONFIGURATION_2:="value"}
envsubst < "file.conf.template" > "$CONFIGURATION_FILE"
}
```
### Create Python virtual env
Initialize Python virtual env with uv.
```bash
init-venv() {
if [[ ! -d "venv$GIT_BRANCH" ]]; then
echo "Init venv$GIT_BRANCH with $(uv --version)."
uv venv "venv$GIT_BRANCH"
fi
}
```
### Activate Python virtual env
```bash
activate-venv() {
echo "Source virtualenv venv$GIT_BRANCH."
source "venv$GIT_BRANCH/bin/activate"
echo "$(python --version) is active."
}
```
### Call a Python script
Run a Python script.
```bash
generate-password-hash() {
activate-venv
if [[ -z "$1" ]]; then echo "\$1 is empty."; exit; fi
export PASSWORD_PLAIN="$1"
bin/password-hash
}
```
The Python script: `bin/password-hash`
```python
#!/usr/bin/env python3
import os
from passlib.context import CryptContext
crypt_context = CryptContext(schemes=['pbkdf2_sha512', 'plaintext'], deprecated=['plaintext'])
password = os.environ.get('PASSWORD_PLAIN')
print(crypt_context.hash(password))
```
### Command with named parameters
Assuming you have a `docker-compose.yml` and would like to start selected or all containers.
```bash
start() {
if [[ "$1" =~ "db" ]]; then
docker compose up -d db
fi
if [[ "$1" =~ "admin" ]]; then
docker compose up -d admin
echo "Open http://localhost:8000 url in your browser."
fi
if [[ "$1" =~ "odoo" ]]; then
docker compose up -d odoo
echo "Open http://localhost:8069 url in your browser."
fi
if [[ "$1" =~ "mail" ]]; then
docker compose up -d mail
echo "Open http://localhost:8025 url in your browser."
fi
}
```
Start all containers with `task start` and selected with `task start db,admin`.
### Run commands in container
Use `docker exec -i` to run commands in a container.
```bash
drop-db() {
local database="$1"
if [[ -z "$database" ]]; then
database="example"
fi
docker exec -i db psql "postgres://odoo:odoo@localhost:5432/postgres" -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database';"
docker exec -i db psql "postgres://odoo:odoo@localhost:5432/postgres" -c "DROP DATABASE \"$database\";"
}
```
### Loop over files and folders
Use `for` to loop over file, folders or arrays.
```bash
render() {
echo "Update index.html for all folders"
for folder in ./*; do
if [[ -f "$folder/README.md" ]]; then
cd "$folder" || exit
md2html README.md _site/index.html
cd .. || exit
fi
done
}
```
### Parse and convert a file
With this function you can split a file into multiple parts whenever a specific keyword is matching. In this example the keyword is `!vault`.
```bash
convert-vault-file() {
local file_path="$1"
local temp_file=$(mktemp)
local temp_part_file=$(mktemp)
local write_finished=false
local current_key=""
while IFS= read -r line; do
# Check for keyword
if [[ "$line" =~ "!vault" ]]; then
# Process previous vault entry if it exists
if [[ "$write_finished" = true ]] && [[ -n "$current_key" ]] && [[ -s "$temp_part_file" ]]; then
# Decrypt part file and write to assemble file
ansible-vault decrypt "$temp_part_file"
local value=$(cat "$temp_part_file")
echo "$current_key: $value" >> "$temp_file"
fi
# Set up for new vault entry
current_key=$(echo "$line" | cut -d':' -f1)
# Clear the file
: > "$temp_part_file"
# Flag as ready to write
write_finished=true
else
if [[ "$write_finished" = true ]]; then
# Pipe into part file
echo "$line" >> "$temp_part_file"
fi
done
done < "$file_path"
# Process the final vault entry
if [[ "$write_finished" = true ]] && [[ -n "$current_key" ]] && [[ -s "$temp_part_file" ]]; then
ansible-vault decrypt "$temp_part_file"
local value=$(cat "$temp_part_file")
echo "$current_key: $value" >> "$temp_file"
fi
# Output assembled file
cat "$temp_file"
# Cleanup temp files
rm -f "$temp_file"
rm -f "$temp_part_file"
}
```
### Process data
This pattern tries to match the worklow of a jupyter notebook. Create a folder with the data processing scripts:
```
scripts
├── 01_import-mail-data
├── 02_transform-mail-data
└── 03_export-mail-data
```
The following function lists the scripts and asks for the number of scripts to run. Every script until the entered number is concatenated and executed.
```bash
process-data() {
while true; do
local scripts=($(ls scripts))
echo -e "Available scripts:\n"
for index in "${!scripts[@]}"; do
echo "$((index+1)): ${scripts[$index]}"
done
echo ""
read -p "Enter the number of scripts to run (or 'q' to quit): " user_input
if [[ "$user_input" = "q" ]]; then
break
fi
local temp_file=$(mktemp)
for index in "${!scripts[@]}"; do
if (( $user_input >= $((index+1)) )); then
cat "scripts/${scripts[$index]}" >> "$temp_file"
echo >> "$temp_file"
fi
done
clear
echo -e "\nPython Output:\n\n---"
python "$temp_file"
local exit_code=$?
echo -e "---\n"
echo -e "Exit code: $exit_code\n"
rm "$temp_file"
done
}
```
## Integration
The task file is intended to be integrated into the shell setup. Where applications provides a domain specific language (DSL) ensure that the DSL only calls the task file.
### Completion
With the output of `task help` the task commands can be completed.
Completion for bash: `/etc/bash_completion.d/task_completions`
```bash
#!/usr/bin/env bash
_task_completions() {
local cur prev commands
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
commands=$(./task help | grep -A999 -e '---' | awk '{print $2}' | sed 's/^[[:space:]]*//' | grep -v '^$' | tr '\n' ' ')
if [[ ${COMP_CWORD} == 1 ]]; then
COMPREPLY=( $(compgen -W "${commands}" -- "${cur}") )
elif [[ ${COMP_CWORD} == 2 ]]; then
COMPREPLY=( $(compgen -f -- "${cur}") )
else
COMPREPLY=()
fi
}
complete -F _task_completions task
complete -F _task_completions t
```
Completion for zsh: `~/.oh-my-zsh/completions/_task`
```bash
#compdef task t
_arguments '1: :->tasks' '*: :_files'
case "$state" in
tasks)
args=$(./task help | grep -A999 -e '---' | awk '{print $2}' | sed 's/^[[:space:]]*//' | grep -v '^$' | tr '\n' ' ')
args="$args help"
_arguments "1:profiles:($args)"
;;
esac
```
The completion also works for task alias `alias t='./task'`.
### GitHub Actions
Running task file commands in GitHub Actions is highly recommended. This way you can run the same CI/CD procedures in the GitHub runner as you do on your localhost.
The GitHub Actions config is simple: `.github/workflows/test.yml `
```yml
on:
pull_request:
branches:
- 'main'
push:
branches:
- 'main'
jobs:
task-all:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install
run: ./task install
- name: Lint
run: ./task lint
```
### Jenkins
Run task file commands in Jenkins: `Jenkinsfile`
```groovy
pipeline {
agent any
stages {
stage('version') {
steps {
script {
currentBuild.description = sh (script: 'git log -1 --pretty=%B', returnStdout: true).trim()
}
sh './task version'
}
}
stage('install') {
steps {
sh './task install'
}
}
stage('lint') {
steps {
sh './task lint'
}
}
}
}%
```