Stack (Haskell) 使用 GitHub Actions 构建源文件的缓存

2024-02-06

当使用本地构建我的 Haskell 项目时stack build,仅重新编译更改的源文件。不幸的是,我无法让 Stack 在 GitHub Actions 上表现得像这样。请问有什么建议吗?

Example

我创建了一个简单的例子Lib.hs and Fib.hs,我什至检查缓存的 .stack-work 文件夹是否在构建之间更新,但它总是编译这两个文件,即使只更改一个文件。

这是示例:

  1. (不使用缓存,构建两者Lib.hs and Fib.hs+ 依赖项):https://github.com/MarekSuchanek/stack-test/runs/542163994 https://github.com/MarekSuchanek/stack-test/runs/542163994
  2. (only Lib.hs改变,构建两者Lib.hs and Fib.hs): https://github.com/MarekSuchanek/stack-test/runs/542174351 https://github.com/MarekSuchanek/stack-test/runs/542174351

我可以从日志(详细堆栈)中观察到缓存中的某些内容正在更新,但我完全不清楚更新内容和原因。它正确地发现只有Lib.hs已更改:“stack-test-0.1.0.0: unregistering (local file changes: src/Lib.hs)“所以我不明白为什么所有的东西都会被编译。我在2中注意到了这一点。Fib.hi未更新于.stack-work但其他人(Fib.o, Fib.dyn_hi, and Fib.dyn_o) are.

Note

当没有更改源文件时,~/.stack 的缓存是可以的,也可以不构建。当然,这是虚拟示例,但我们有不同的项目,其中包含更多源文件,这将显着加快构建速度。当非源文件发生更改(例如自述文件)时,不会按预期构建任何内容。


这个问题的罪魁祸首是堆栈使用时间戳(就像许多其他工具一样)来确定源文件是否已更改。当您在 CI 上恢复缓存并且操作正确时,不会重建任何依赖项,但源文件的问题是,当 CI 提供程序为您克隆存储库时,存储库中所有文件的时间戳都会被设置到克隆的日期和时间。

希望重新编译未更改的源文件的原因现在有意义。我们如何解决这个问题。获取它的唯一真正方法是恢复更改特定文件的最后一次 git 提交的时间戳。我很久以前就注意到了这一点,谷歌搜索给了我一些答案,我认为这是其中之一:在 Git 中恢复文件的修改时间 https://stackoverflow.com/questions/2458042/restore-a-files-modification-time-in-git

对它进行了一些修改以满足我的需求,这就是我最终得到的结果:

  git ls-tree -r --name-only HEAD | while read filename; do
    TS="$(git log -1 --format="%ct" -- ${filename})"
    touch "${filename}" -mt "$(date --date="@$TS" "+%Y%m%d%H%M.%S")"
  done

该工作人员在 Ubuntu CI 上对我来说非常有用,但是当我需要设置 Azure CI 时,我不想用 bash 以与操作系统无关的方式解决这个问题。因此,我编写了一个适用于所有 GHC-8.2 版本及更高版本的 Haskell 脚本,无需任何非核心依赖项。我将它用于我的所有项目,我将在这里嵌入它的精华,但也提供链接到永久要点 https://gist.github.com/lehins/fd36a8cc8bf853173437b17f6b6426ad:

main = do
  args <- getArgs
  let rev = case args of
        [] -> "HEAD"
        (x:_) -> x
  fs <- readProcess "git" ["ls-tree", "-r", "-t", "--full-name", "--name-only", rev] ""
  let iso8601 = iso8601DateFormat (Just "%H:%M:%S%z")
      restoreFileModtime fp = do
        modTimeStr <- readProcess "git" ["log", "--pretty=format:%cI", "-1", rev, "--", fp] ""
        modTime <- parseTimeM True defaultTimeLocale iso8601 modTimeStr
        setModificationTime fp modTime
        putStrLn $ "[" ++ modTimeStr ++ "] " ++ fp
  putStrLn "Restoring modification time for all these files:"
  mapM_ restoreFileModtime $ lines fs

您将如何在没有太多开销的情况下使用它?诀窍是:

  • use stack本身运行脚本
  • 使用与项目完全相同的解析器。

以上两点将确保不会安装多余的依赖项或 ghc 版本。总而言之,只需要两件事:stack和类似的东西curl or wget它将跨平台工作:

# Script for restoring source files modification time from commit to avoid recompilation.
curl -sSkL https://gist.githubusercontent.com/lehins/fd36a8cc8bf853173437b17f6b6426ad/raw/4702d0252731ad8b21317375e917124c590819ce/git-modtime.hs -o git-modtime.hs
# Restore mod time and setup ghc, if it wasn't restored from cache
stack script --resolver ${RESOLVER} git-modtime.hs --package base --package time --package directory --package process

这是一个使用这种方法的真实项目,您可以深入研究它以了解它是如何工作的:massiv-io https://github.com/lehins/massiv-io

Edit@Simon Michael 在评论中提到他无法在本地重现这个问题。原因是 CI 上的一切与本地的并不相同。很多时候,绝对路径是不同的,例如,可能是我现在想不到的其他东西。这些东西与源文件时间戳一起导致源文件的重新编译。

例如,按照以下步骤操作,您会发现您的项目将被重新编译:

~/tmp$ git clone [email protected] /cdn-cgi/l/email-protection:fpco/safe-decimal.git
~/tmp$ cd safe-decimal
~/tmp/safe-decimal$ stack build
safe-decimal> configure (lib)
[1 of 2] Compiling Main
...
Configuring safe-decimal-0.2.0.0...
safe-decimal> build (lib)
Preprocessing library for safe-decimal-0.2.0.0..
Building library for safe-decimal-0.2.0.0..
[1 of 3] Compiling Numeric.Decimal.BoundedArithmetic
[2 of 3] Compiling Numeric.Decimal.Internal
[3 of 3] Compiling Numeric.Decimal
...
~/tmp/safe-decimal$ cd ../
~/tmp$ mv safe-decimal safe-decimal-moved
~/tmp$ cd safe-decimal-moved/
~/tmp/safe-decimal-moved$ stack build
safe-decimal-0.2.0.0: unregistering (old configure information not found)
safe-decimal> configure (lib)
[1 of 2] Compiling Main
...

您将看到项目的位置触发了项目构建。尽管项目本身已重建,但您会注意到没有重新编译任何源文件。现在,如果您将该过程与touch源文件的,该源文件将被重新编译。

把它们加起来:

  • 环境可能导致项目需要重建
  • 源文件的内容可能会导致源文件(以及依赖于它的其他文件)被重新编译
  • 环境以及源文件内容或时间戳更改可能会导致项目以及该源文件被重新编译
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Stack (Haskell) 使用 GitHub Actions 构建源文件的缓存 的相关文章

  • 使用redis进行树形数据结构

    我需要为基于树的键值开发一个缓存系统 与Windows注册表编辑器非常相似 其中缓存键是字符串 表示树中到值的路径 可以是原始类型 int string bool double 等 或子树本身 例如 key root x y z w val
  • 关于“没有绑定的类型签名”的错误

    我在 Haskell 中遇到 ASCII 问题 fromEnum Char gt Int toEnum Int gt Char offset Int offset fromEnum A fromEnum a toUpper Char gt
  • 约束包如何工作?

    背后的想法数据 约束 Forall http hackage haskell org packages archive constraints 0 3 2 doc html src Data Constraint Forall html据我
  • System.Web.Caching.Cache 在模型中抛出 null 异常

    也许这个问题应该很简单 但事实并非如此 我读过了在 ASP NET 中使用 System Web Caching Cache 类时出现问题 https stackoverflow com questions 531014 problem u
  • 如何强制刷新 CallLog.Calls.CACHED_NAME 列?

    我的目标是从通话记录中收集所有未知的电话号码 这可以通过以下代码来实现 private static final String CALLOG PROJECTION CallLog Calls ID CallLog Calls CACHED
  • 无法将消息发布到服务工作人员,因为控制器值为空

    我正在尝试做一个website https secure depths 31934 herokuapp com 在 Service Worker 的帮助下可以离线使用 以缓存页面所需的文件 我试图让用户控制他希望缓存的图像 为此 我使用一个
  • 自定义 monad 的 MonadTransControl 实例

    的文档monad control提供有关如何创建实例的示例MonadTransControl using defaultLiftWith and defaultRestoreT 该示例适用于以下情况newtype newtype Count
  • 为什么 mod 在表达式中给出的结果与在函数调用中给出的结果不同?

    假设有人想要计算函数 f x y x mod 3 y mod 3 mod 2 那么 如果再展开f 1 0 手动 可以得到 1 mod 3 0 mod 3 mod 2 1 然而 如果使用内联函数 结果是 let f x y x mod 3 y
  • cabal install wx 缺少 C 库

    Env 操作系统 feodra 16 Haskell 平台 wxGTK 开发 GHHC 7 0 4 我正在尝试安装 wxHaskell 阴谋集团安装wx 然后给出这些错误 缺少对外国库的依赖 缺少 C 库 wx baseu 2 8 wx b
  • 如何让 do 块提前返回?

    我正在尝试使用 Haskell 抓取网页并将结果编译到一个对象中 如果出于某种原因 我无法从页面获取所有项目 我想停止尝试处理页面并提前返回 例如 scrapePage String gt IO scrapePage url do doc
  • Data.Array 有多快?

    The 文档 http haskell org ghc docs latest html libraries array 0 3 0 3 Data Array html of Data Array reads Haskell 提供了可索引数
  • 无法禁用 jQuery 缓存

    Update 我发现这一定是缓存问题 但我无法关闭缓存 这是我更改后的脚本
  • 在列表中查找元素及其索引

    我需要让列表的两个元素都满足谓词and这些元素的索引 我可以通过以下方式实现这一点 import Data List findIndices list Int list 3 2 4 1 9 indices findIndices gt 2
  • 在 Spring Boot 中重新加载/刷新缓存

    我正在使用 Spring Boot 对于缓存 我使用 Ehcache 到目前为止一切正常 但现在我必须重新加载 刷新 那么我该如何执行此操作 以便我的应用程序不会出现任何停机时间 我在Spring Ehcache中尝试了很多方法 但它不起作
  • 我如何知道是否启用了 PHP 缓存?

    我曾经认为缓存很难安装 所以我从来没有这样做过 在阅读了有关 APC 的内容后 它似乎很容易安装 我一直认为我必须修改应用程序中的大量 PHP 代码才能使用它 哈哈 不管怎样 我想安装APC 我可以使用 phpinfo 并注意到它没有在页面
  • 使用 Parsec 解析正则表达式

    我正在尝试通过实现一个小型正则表达式解析器来学习秒差距 在 BNF 中 我的语法类似于 EXP EXP LIT EXP LIT 我尝试在 Haskell 中实现这一点 expr try star lt gt try litE lt gt l
  • 并行 Haskell - GHC GC 火花

    我有一个正在尝试并行化的程序 带有可运行代码的完整粘贴here http lpaste net 101528 我进行了分析 发现大部分时间都花在findNearest这本质上是一个简单的foldr超过一个大Data Map findNear
  • 这是 unsafeCoerce 的安全使用吗?

    我遇到的情况是 我目前正在使用极其可怕的函数 unsafeCoerce 幸运的是 这并不是为了任何重要的事情 但我想知道这是否是该函数的安全使用 或者是否有其他方法可以解决其他人知道的这个特定问题 我的代码类似于以下内容 data Toke
  • 集群环境下如何管理spring缓存

    我正在尝试使用 spring 为我的应用程序构建缓存服务 缓存需要从数据库填充 我的应用程序在三个节点上运行 并希望所有三个节点都与缓存同步 如果一个节点在缓存中获得更新的值 它应该通知其他节点 我在看Spring 缓存抽象 http do
  • 检索 Haskell 项目中所有导入的列表

    因此 我的最终目标是通过确保项目导入的所有实体都存在于其声称可以使用的版本中 来评估 cabal 文件中依赖项的准确性 一个好的开始是找到单个源文件使用的所有导入实体的列表 可选地包含有关它们来自何处的信息 我愿意暂时忽略类实例的情况 因为

随机推荐

  • Amazon S3 签名 URL 和 Cloudfront - 访问被拒绝

    我正在创建一个signed url使用以下内容 AWS ACCESS KEY ID my access key AWS SECRET ACCESS KEY my secret access key KEYPAIR ID my keypair
  • 如何创建堆叠折线图 D3,多个 Y 轴和公共 X 轴

    我正在尝试使用 d3 创建一个折线图 该折线图将有多个 y 轴 但有一个公共 x 轴 有人可以为我提供如何使用 D3 库创建它的示例吗 它应该如下所示 很简单 只需绘制 2 个图表 但仅附加一个 x 轴 这里有一个小提琴可以帮助您入门 ht
  • C++ 无法计算包含向量大小的公式?

    int main vector
  • Visual Studio 使用标准格式复制/粘贴到 Outlook 中?

    我在 Visual Studio 中有包含深色背景的自定义设置 复制 粘贴到 Outlook 会对邮件的白色背景上的代码中的深色背景产生视觉犯罪 有谁知道如何以标准和格式粘贴代码 不是特殊粘贴中的未格式化文本 也不使用我的自定义 VS 格式
  • 如何在滚动运算符中访问多列?

    我想在 pandas 中进行一些滚动窗口计算 需要同时处理两列 我举一个简单的例子来清楚地表达问题 import pandas as pd df pd DataFrame x 1 2 3 2 1 5 4 6 7 9 y 4 3 4 6 5
  • Django 多对多模型 DRF

    我有以下模型结构 class Project models Model author models ManyToManyField Account name models CharField max length 40 default Ne
  • 如何在View和ViewModel之间传递数据

    我是 MVVM 设计模式的新手 我正在尝试创建一个简单的应用程序 其中主窗口中显示学生列表 我希望用户能够将新学生添加到我已完成的列表中绑定学生数据所在的可观察集合 但如何通过从文本框中获取数据并将其用作命令中的参数来创建新用户 这是我的观
  • 如何测试飞行路线迁移?

    通常迁移脚本很简单 例如添加新列等 如果应用程序已部署 则一切正常 但有时需要测试一些复杂的逻辑 推荐的方法是什么 有一个单独的数据库用于测试 将其作为每个构建的一部分进行迁移 并针对它运行测试 您还可以根据需要添加额外的测试数据 包括用于
  • Laravel Sanctum 可以使用 Multiauth 防护

    我正在用 laravel sainttum 进行测试 但这里有一些问题 我正在创建管理员警卫 当我将中间件更改为 auth sanctum admin 它应该只能由管理员访问 但在这里我可以使用带有网络防护的普通用户帐户进行访问 我不知道为
  • 有没有一种方法可以创建一个 pandas 数据框,其行是整数,这些整数会增加直到每行达到某个值?

    例如 假设我有整数数组 5 3 7 6 4 我希望找到一种有效的方法来创建如下所示的 pandas 数据框 数据帧中的第一行应包含数字 1 到 5 第二行应包含数字 1 到 3 依此类推 有没有办法在不循环的情况下实现这一目标 一个简单的单
  • 将枚举值传递给 PowerShell 中的函数

    我有一个接受枚举值作为参数的函数 举个例子 考虑一下这样的事情 PS gt function IsItFriday System DayOfWeek dayOfWeek if dayOfWeek eq System DayOfWeek Fr
  • SQL 行之间的差异

    我有一个像这样的 SQL 2008 数据库表 name score steve 207 steve 205 steve 200 steve 139 我想得到行之间的差异 eqn 行 行 1 所以我理想地希望它是 steve 2 207 20
  • 如何编写 Nexus Rest Api 的 Get 方法?

    I have groupId artifactId and version 如何使用 Nexus Rest API 编写 GET 请求以获得进一步的工件描述 不确定您要查找什么信息 REST API 文档可在此处获取 Core API ht
  • 如何在 Xamarin Forms 中获取设备的 GPS 位置?

    我想在输入框名为时获取我的设备的经度和纬度实体位置 is focused 我在用Xamarin Essential 地理定位为了获取我的设备的 GPS 位置 我按照文档和教程进行操作 但仍然无法获取 GPS 位置 我已经在我的 Androi
  • 如何在 Android webview 中打开 Microsoft Teams 会议

    我正在尝试在 Web 视图中打开 Microsoft Teams 会议 当我在 Web 视图中打开会议网址时 它开始寻找 MS Teams 应用程序 这是我不想要的 我只想在 Web 视图中参加会议 请帮助我在 Android 应用程序的
  • 包括 ionic 2/Angular 2 的通用标题栏

    我有一个 ionic 2 标题栏 其中包含主页或注销按钮和公司标志这对于所有页面都是通用的 我如何编写一个通用函数 Injectable 这样就可以很容易地包含在所有页面中 而不是重复代码
  • Protractor + AngularJS + Jasmine 在xml文件上获取输出结果

    我正在尝试将量角器结果导出到 xml 文件 我在网上找到了这个很棒的链接 https github com angular protractor issues 60 https github com angular protractor i
  • QMessageBox 带有“不再显示”复选框

    如何显示下面带有 不再显示 复选框的消息框 我想象的东西看起来像这样 Qt 5 2 添加了添加QCheckBox to a QMessageBox 看一下QMessageBox setCheckbox http doc qt io qt 5
  • 同时在 iPhone 和模拟器上进行多点连接测试

    您好 我正在论坛的所有主题中搜索有关如何测试使用多点连接框架的应用程序的信息 有人可以告诉我如何同时使用 iPhone 设备和模拟器 设备上的播放器一和模拟器上的播放器二 来测试应用程序 反之亦然 我已经通过使用两个模拟器 每个 mac 一
  • Stack (Haskell) 使用 GitHub Actions 构建源文件的缓存

    当使用本地构建我的 Haskell 项目时stack build 仅重新编译更改的源文件 不幸的是 我无法让 Stack 在 GitHub Actions 上表现得像这样 请问有什么建议吗 Example 我创建了一个简单的例子Lib hs