You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

文件部分内容变更时运行指定Action的推荐实现方法

解决Shake中Elm依赖变更触发安装命令的问题

我明白你现在的困境:想用Shake的Oracle特性监控elm-package.json里dependencies字段的变化,一旦变更就自动执行elm package install --yes,但在处理自定义ElmDependencies类型和Action String的函数结合时,卡在了do规则的编写上。咱们一步步来搞定这个问题:

1. 先确认自定义类型的正确性

首先,你的ElmDependencies newtype需要正确派生Eq(Oracle依赖这个特性来对比历史和当前值),同时还要能从JSON文件中解析出来。示例定义如下:

import Data.Aeson (FromJSON, eitherDecode)
import qualified Data.Map as Map

newtype ElmDependencies = ElmDependencies
  { unElmDependencies :: Map String String
  } deriving (Eq, Show, FromJSON)

这里Map String String用来存储依赖包名和版本号,正好对应elm-package.json里的dependencies结构。

2. 编写获取当前依赖的Action函数

接下来需要一个能读取并解析elm-package.json的函数,返回Action ElmDependencies:

import Development.Shake (Action, readFile')

getCurrentElmDeps :: Action ElmDependencies
getCurrentElmDeps = do
  fileContent <- readFile' "elm-package.json"
  case eitherDecode fileContent of
    Left err -> fail $ "解析elm-package.json失败: " ++ err
    Right deps -> return deps

用readFile'而不是普通readFile是因为Shake的这个函数会自动追踪文件变化,当文件修改时触发重新执行。

3. 结合Oracle编写触发规则

现在核心问题是用Oracle追踪依赖变化,然后在变化时执行安装命令。这里的关键是利用newOracle创建一个状态追踪器,再通过askOracle获取当前状态,对比历史状态:

import Development.Shake (Rules, cmd, newOracle, askOracle, getOracleHistory, when, (~>))

elmAutoInstallRule :: Rules ()
elmAutoInstallRule = do
  -- 创建Oracle,用()作为查询标记(因为我们只需要追踪一组依赖状态)
  depOracle <- newOracle $ \() -> getCurrentElmDeps

  -- 监控elm-package.json文件,当文件变更时触发检查
  "elm-package.json" ~> do
    currentDeps <- askOracle depOracle ()
    -- 获取历史记录(第一次运行时为Nothing)
    maybePrevDeps <- getOracleHistory depOracle ()

    case maybePrevDeps of
      -- 首次运行:直接执行安装
      Nothing -> cmd "elm package install --yes"
      -- 对比前后依赖,变化则执行安装
      Just prevDeps -> when (currentDeps /= prevDeps) $
        cmd "elm package install --yes"

4. 关于你提到的ElmDependencies -> Action String的问题

如果你的场景中确实需要一个ElmDependencies -> Action String的函数(比如把依赖转换为某种标识字符串),可以这样编写并融入规则:

-- 示例:将依赖转换为格式化的字符串
depsToString :: ElmDependencies -> Action String
depsToString (ElmDependencies deps) = return $ unlines $ map (\(name, ver) -> name ++ "@" ++ ver) $ Map.toList deps

-- 在规则中使用这个函数
elmAutoInstallRule :: Rules ()
elmAutoInstallRule = do
  depOracle <- newOracle $ \() -> getCurrentElmDeps

  "elm-package.json" ~> do
    currentDeps <- askOracle depOracle ()
    maybePrevDeps <- getOracleHistory depOracle ()

    -- 调用自定义函数转换为字符串
    currentDepsStr <- depsToString currentDeps
    putStrLn $ "当前依赖: " ++ currentDepsStr

    case maybePrevDeps of
      Nothing -> cmd "elm package install --yes"
      Just prevDeps -> when (currentDeps /= prevDeps) $ do
        prevDepsStr <- depsToString prevDeps
        putStrLn $ "依赖变更: 从 " ++ prevDepsStr ++ " 到 " ++ currentDepsStr
        cmd "elm package install --yes"

关键注意点

  • 确保ElmDependencies派生了Eq:这是Oracle能对比前后状态的核心
  • 用Shake的readFile'和cmd函数:它们会自动处理文件追踪和命令执行的副作用
  • 处理JSON解析错误:避免因为文件格式问题导致构建失败

内容的提问来源于stack exchange,提问作者Neil Mitchell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:23:22