ghactions_toolcache/
cache.rs1use std::path::PathBuf;
4#[cfg(feature = "download")]
5use std::sync::{Arc, OnceLock};
6
7use super::{Tool, ToolCacheArch, platform::ToolPlatform};
8use crate::ToolCacheError;
9use crate::builder::ToolCacheBuilder;
10
11pub(crate) const RETRY_COUNT: u8 = 10;
13
14#[cfg(target_family = "unix")]
16const TOOL_CACHE_PATHS: [&str; 3] = [
17 "/opt/hostedtoolcache",
18 "/usr/local/share/toolcache",
19 "/tmp/toolcache",
20];
21#[cfg(target_family = "windows")]
23const TOOL_CACHE_PATHS: [&str; 3] = [
24 "C:\\hostedtoolcache",
25 "C:\\Program Files\\toolcache",
26 "C:\\tmp\\toolcache",
27];
28
29#[derive(Debug, Clone)]
31pub struct ToolCache {
32 pub(crate) tool_cache: PathBuf,
34
35 pub(crate) arch: ToolCacheArch,
37
38 pub(crate) platform: ToolPlatform,
40
41 pub(crate) retry_count: u8,
43
44 #[cfg(feature = "download")]
46 pub(crate) client: Arc<OnceLock<reqwest::Client>>,
47}
48
49impl ToolCache {
50 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn build() -> ToolCacheBuilder {
73 ToolCacheBuilder::new()
74 }
75
76 pub fn platform(&self) -> ToolPlatform {
81 self.platform
82 }
83
84 pub fn arch(&self) -> ToolCacheArch {
89 self.arch
90 }
91
92 pub fn get_tool_cache(&self) -> &PathBuf {
97 &self.tool_cache
98 }
99
100 pub async fn find(
102 &self,
103 tool: impl Into<String>,
104 version: impl Into<String>,
105 ) -> Result<Tool, ToolCacheError> {
106 match self.platform() {
107 ToolPlatform::Windows => self.find_with_arch(tool, version, ToolCacheArch::X64).await,
108 ToolPlatform::Linux => self.find_with_arch(tool, version, ToolCacheArch::X64).await,
109 ToolPlatform::MacOS => {
110 self.find_with_arch(tool, version, ToolCacheArch::ARM64)
111 .await
112 }
113 ToolPlatform::Any => self.find_with_arch(tool, version, ToolCacheArch::Any).await,
114 }
115 }
116
117 pub async fn find_all_version(
119 &self,
120 tool: impl Into<String>,
121 ) -> Result<Vec<Tool>, ToolCacheError> {
122 Tool::find(self.get_tool_cache(), tool, "*", ToolCacheArch::Any)
123 }
124
125 pub async fn find_with_arch(
127 &self,
128 tool: impl Into<String>,
129 version: impl Into<String>,
130 arch: impl Into<ToolCacheArch>,
131 ) -> Result<Tool, ToolCacheError> {
132 let tool = tool.into();
133 let version = version.into();
134 let arch = arch.into();
135
136 Tool::find(self.get_tool_cache(), tool.clone(), &version, arch)?
137 .into_iter()
138 .find(|t| t.name() == tool)
139 .ok_or(crate::ToolCacheError::ToolNotFound {
140 name: tool,
141 version,
142 arch: Some(arch),
143 })
144 }
145
146 pub fn new_tool_path(&self, tool: impl Into<String>, version: impl Into<String>) -> PathBuf {
148 Tool::tool_path(self.get_tool_cache(), tool, version, self.arch())
149 }
150
151 #[deprecated(since = "0.17.0", note = "Use the ToolCacheBuilder instead")]
153 pub fn set_retry_count(&mut self, count: u8) {
154 self.retry_count = count;
155 }
156}
157
158pub(crate) fn get_tool_cache_path() -> PathBuf {
160 let tool_cache = std::env::var("RUNNER_TOOL_CACHE")
161 .map(PathBuf::from)
162 .unwrap_or_else(|_| {
163 TOOL_CACHE_PATHS
164 .iter()
165 .find_map(|path| {
166 let path = PathBuf::from(path);
167 if let Err(err) = std::fs::create_dir_all(&path) {
169 log::trace!("Error creating tool cache dir: {:?}", err);
170 None
171 } else {
172 log::debug!("Using tool cache found at: {:?}", path);
173 Some(path)
174 }
175 })
176 .unwrap_or_else(|| PathBuf::from("./.toolcache").canonicalize().unwrap())
177 });
178
179 if !tool_cache.exists() {
180 log::debug!("Creating tool cache at: {:?}", tool_cache);
181 std::fs::create_dir_all(&tool_cache)
182 .unwrap_or_else(|_| panic!("Failed to create tool cache directory: {:?}", tool_cache));
183 }
184 tool_cache
185}
186
187impl From<&str> for ToolCache {
188 fn from(cache: &str) -> Self {
189 let tool_cache = PathBuf::from(cache);
190 if !tool_cache.exists() {
191 panic!("Tool Cache does not exist: {:?}", tool_cache);
192 }
193 Self {
194 tool_cache,
195 ..Default::default()
196 }
197 }
198}
199
200impl From<PathBuf> for ToolCache {
201 fn from(value: PathBuf) -> Self {
202 let tool_cache = value;
203 if !tool_cache.exists() {
204 panic!("Tool Cache does not exist: {:?}", tool_cache);
205 }
206 Self {
207 tool_cache,
208 ..Default::default()
209 }
210 }
211}
212
213impl Default for ToolCache {
214 fn default() -> Self {
215 let tool_cache = get_tool_cache_path();
216
217 Self {
218 tool_cache,
219 retry_count: RETRY_COUNT,
220 arch: match std::env::consts::ARCH {
221 "x86_64" | "amd64" => ToolCacheArch::X64,
222 "aarch64" => ToolCacheArch::ARM64,
223 _ => ToolCacheArch::Any,
224 },
225 platform: ToolPlatform::from_current_os(),
226 #[cfg(feature = "download")]
227 client: Arc::new(OnceLock::new()),
228 }
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 fn local_toolcache() -> (PathBuf, ToolCache) {
237 let cwd = std::env::current_dir()
239 .unwrap()
240 .join("..")
241 .canonicalize()
242 .unwrap();
243
244 (cwd.clone(), ToolCache::from(cwd.join("examples/toolcache")))
245 }
246
247 #[test]
248 fn test_tool_cache() {
249 let tool_cache = ToolCache::default();
251 if let Ok(env_path) = std::env::var("RUNNER_TOOL_CACHE") {
252 assert_eq!(tool_cache.get_tool_cache(), &PathBuf::from(env_path));
253 } else {
254 assert!(tool_cache.get_tool_cache().exists());
255 }
256 }
257
258 #[tokio::test]
259 async fn test_find_all_version() {
260 let (_cwd, tool_cache) = local_toolcache();
261 let versions = tool_cache.find_all_version("node").await.unwrap();
262 assert!(!versions.is_empty());
263 }
264}