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