惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
V
V2EX
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
A
About on SuperTechFans
博客园_首页
L
LangChain Blog
量子位
雷峰网
雷峰网
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
M
MIT News - Artificial intelligence
N
Netflix TechBlog - Medium

博客园 - 丁少华

adb提取本机apk 安卓ADB调试安卓 高德地图车机版本9.5制作共存 Taskfile 拆分子文件 go项目的taskfile Go 内嵌网页 windows的终端像mac一样好用 pgsql指定用户看到某个表 vue不使用根组件 Huma + Orval 前后端 API 自动生成方案 Mac安装nginx 不喜欢Go的几点 代码写错分支拯救指南 React UI 库推荐 Loop Engineering Vue 3 UI 组件库 Neon白嫖免费pgsql 域名利用cloudflare免费图床 nginx反代CloudflarePages提示502 vite使用shadcn vite使用biome Window下Nginx winserver2022安装不上软件 mac已损坏无法打开 代码高亮 命令运行器之just windows开启wsl 白嫖Redis 白嫖MongoDB vercel无服务函数
命令运行器之task
丁少华 · 2026-03-12 · via 博客园 - 丁少华

前言

Makefile/Just/Task 哪个更现代,是未来趋势?

Make/Just: 它们本质上是 Shell 转发器。在 Windows 上它们调 powershell,在 Linux 上调 bash。如果你写了 rm -rf,在没有配置好的 Windows 环境下就会直接报错。

Task: 它内置了一个 Go 语言编写的 Shell 解释器。这意味着你在 Taskfile 里写的脚本逻辑,在所有操作系统上运行结果几乎完全一致,不再需要像你之前在 justfile 里那样痛苦地写 if os() == "windows"。

就连 GitHub Actions 官方文档都推荐 Task(虽然和Just的star相差一倍)!

Task两者Just都宣称自己是Make专门用于任务运行的替代方案。

这里介绍的是Task(file)

安装

windows

推荐使用 scope(类似于mac下的brew)来安装它,那我们先安装scope

# 普通用户
irm get.scoop.sh | iex
# admin用户
iex "& {$(irm get.scoop.sh)} -RunAsAdmin"

使用scope来安装just

scoop install task

macos

brew install go-task

最后确认下

task --version

使用

以下是一个 同时启动前后端的配置!

version: '3'
output: prefixed
vars:
  VENV_BIN: '{{if eq OS "windows"}}.venv/Scripts{{else}}.venv/bin{{end}}'
  PYTHON: '{{.VENV_BIN}}/python'
tasks:
  default:
    desc: "启动"
    cmds:
      - task: dev
  dev:
      desc: "启动开发环境"
      cmds:
        - "echo ✈ 启动开发环境..."
      deps: [backend, frontend]
  install-web:
    desc: "安装前端依赖"
    dir: web
    cmds:
      - pnpm install
  install-server:
    desc: "安装后端依赖"
    dir: server
    cmds:
      - uv sync
  install:
    desc: "安装依赖"
    cmds:
      - task: install-web
      - task: install-server
  frontend:
    desc: "启动前端"
    dir: web
    cmds:
      - "echo 🚀 启动前端"
      - pnpm run dev
  backend:
    desc: "启动后端"
    dir: server
    cmds:
      - "echo 🚀 启动后端"
      - "{{.PYTHON}} -m uvicorn server.app:server --reload --host 0.0.0.0"
  stop:
    desc: "停止后端服务 (强杀端口)"
    cmds:
      - "{{if eq OS \"windows\"}}stop-process -Id (Get-NetTCPConnection -LocalPort 8000).OwningProcess -Force{{else}}lsof -t -i:8000 | xargs kill -9{{end}}"
    ignore_error: true
  restart:
    desc: "重启后端服务"
    cmds:
      - task: stop
      - task: backend
  build-web:
    desc: "构建前端静态文件"
    dir: web
    cmds:
      - pnpm build
  # 其实这个意义不大,相当于本地preview,因为部署服务器如果走cd/ci的话(比如 github action),就无法利用task
  start:
    desc: "启动服务"
    cmds:
      - "echo 🚀 启动服务: 将前端打包到后端静态目录中,并启动后端服务"
      - task: build-web
      - "rm -rf server/server/static/* && mkdir -p server/server/static"
      - "cp -r web/dist/* server/server/static/"
      - task: backend

其它

拆分任务

比如

tasks:
  install:
    desc: "安装依赖"
    cmds:
      - "cd web && pnpm install"
      - "cd server && uv sync"

Task 提供了一个 dir 参数,专门用来指定该任务执行的工作目录。这样你的命令会变得非常干净。而且把你原来的两行拆成 install-web 和 install-server 有一个巨大的好处-并发执行(Speed!)。

tasks:
  install-web:
    desc: "安装前端依赖"
    dir: web
    cmds:
      - pnpm install

  install-server:
    desc: "安装后端依赖"
    dir: server
    cmds:
      - uv sync

  # 组合任务
  install:
    desc: "一键安装所有依赖"
    cmds:
      - task: install-web
      - task: install-server

变量和使用变量

在 Task (Taskfile) 的语法中,{{.PYTHON}} 是一个变量引用。
你可以把它理解为 Python 里的 f"{PYTHON}" 或者 JS 里的 ${PYTHON}。它的作用是把你在顶部 vars 模块里定义的内容,“填”到你的命令里。

vars:
  PYTHON: ".venv/bin/python" # 1. 在这里定义

tasks:
  backend:
    cmds:
      - "{{.PYTHON}} --version" # 2. 在这里引用

并发

deps (Dependencies) 是 Taskfile 中处理并发启动的最佳方式。当你把任务放在 deps 里时,它们会同时启动。

dev:
  cmds:
    - task: backend   # 1. 启动后端,进程在这里被阻塞(因为 uvicorn 会一直运行)
    - task: frontend  # 2. 只有当前面的后端关闭了,才会运行前端

因为 uvicorn 和 pnpm dev 都是常驻进程(Long-running processes),它们不主动结束,下一行命令就永远不会开始。

# 建议在顶部加上这个,让并发运行的日志带有前缀颜色,方便分辨
output: prefixed

tasks:
  dev:
      desc: "一键启动前后端"
      # deps 里的任务会并行(parallel)执行
      deps: [backend, frontend]

为什么方案 A (deps) 更好?
日志交织处理:当你同时启动前后端时,屏幕上会瞬间涌出大量日志。配合 output: prefixed,Task 会在每行日志前加上 [WEB] 或 [API],否则你根本分不清谁是谁。

生命周期管理:当你按 Ctrl + C 时,Task 会作为“父进程”捕捉到信号,然后同时关闭它启动的所有子进程(前端和后端)。

真正的跨平台:不需要去考虑 Shell 的后台运行符(如 &),因为 & 在 Windows 的 PowerShell 下行为很怪异,而 deps 是 Task 内部实现的,全平台表现一致。

vscode

image

关于taskfile支持的vscode插件,一共两个,只用第一个就够了:

  • 在侧边栏查看任务。
  • 从侧边栏和命令面板运行任务。
  • 从侧边栏和命令面板转到定义。
  • 运行最后一个任务命令。
  • 支持多根工作区。
  • 在当前工作区中初始化任务文件。

image

github aciton

当前项目如何利用GitHub action自动部署呢?

参考 .github\workflows\deploy.yml

name: Build & Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:

env:
  DEPLOY_DIR: /home/apps/lark-monitor-server
  REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
  REMOTE_PORT: ${{ secrets.REMOTE_PORT || 22 }}
  SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # ---------- 前端构建 ----------
      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
          cache-dependency-path: web/pnpm-lock.yaml

      - name: Install frontend dependencies
        run: pnpm install
        working-directory: web

      - name: Build frontend
        run: pnpm build
        working-directory: web

      # ---------- 打包产物 ----------
      - name: Prepare artifact
        run: |
          rm -rf server/server/static/*
          mkdir -p server/server/static
          cp -r web/dist/* server/server/static/
          tar -czf release.tar.gz -C server .

      # ---------- 部署到服务器 ----------
      - name: Deploy to server
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ env.REMOTE_HOST }}
          port: ${{ env.REMOTE_PORT }}
          username: root
          key: ${{ env.SSH_PRIVATE_KEY }}
          source: release.tar.gz
          target: /tmp

      - name: Deploy and restart
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ env.REMOTE_HOST }}
          port: ${{ env.REMOTE_PORT }}
          username: root
          key: ${{ env.SSH_PRIVATE_KEY }}
          script: |
            mkdir -p ${{ env.DEPLOY_DIR }}
            tar -xzf /tmp/release.tar.gz -C ${{ env.DEPLOY_DIR }}
            rm /tmp/release.tar.gz
            cd ${{ env.DEPLOY_DIR }} && uv sync
            # 杀掉旧进程,重新启动
            lsof -t -i:8000 | xargs -r kill -9 || true
            nohup .venv/bin/uvicorn server.app:server --host 0.0.0.0 --port 8000 > uvicorn.log 2>&1 &

nginx代理

非必看,和本教程无关。
如上的项目我部署的服务器和nginx服务不是同一台,怎么代理过去呢?

# xx.dingshaohua.com
server {
    listen       80;
    server_name  lark.dingshaohua.com;
    rewrite ^(.*)$ https://$host$1 permanent;
}
server {
    listen       443 ssl;
    server_name  xx.dingshaohua.com;
    include conf.d/common.conf;

     location / {
        proxy_pass http://xx.xx.xx.xx:8000;
    }
}

参考

https://blog.csdn.net/gitblog_00247/article/details/151125318