diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 830a7908b..5cbae4162 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -189,3 +189,52 @@ jobs: ${SCCACHE_PATH} --show-stats ${SCCACHE_PATH} --show-stats | grep -e "Cache hits\s*[1-9]" + + gha: + runs-on: ubuntu-latest + needs: build + + env: + SCCACHE_GHA_VERSION: "sccache_integration_tests" + RUSTC_WRAPPER: /home/runner/.cargo/bin/sccache + + steps: + - name: Clone repository + uses: actions/checkout@v3 + + - name: Configure Cache Env + uses: actions/github-script@v6 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Install rust + uses: ./.github/actions/rust-toolchain + with: + toolchain: "stable" + + - uses: actions/download-artifact@v3 + with: + name: integration-tests + path: /home/runner/.cargo/bin/ + - name: Chmod for binary + run: chmod +x ${SCCACHE_PATH} + + - name: Test + run: cargo clean && cargo build + + - name: Output + run: | + ${SCCACHE_PATH} --show-stats + + ${SCCACHE_PATH} --show-stats | grep gha + + - name: Test Twice for Cache Read + run: cargo clean && cargo build + + - name: Output + run: | + ${SCCACHE_PATH} --show-stats + + ${SCCACHE_PATH} --show-stats | grep -e "Cache hits\s*[1-9]" diff --git a/Cargo.lock b/Cargo.lock index 2b1749a9e..ae3dd90df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,16 +127,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "async-lock" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" -dependencies = [ - "event-listener", - "futures-lite", -] - [[package]] name = "async-trait" version = "0.1.60" @@ -908,34 +898,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gha-toolkit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56168956d9bb259029ae3187c78d618b4d001037898c50ec069d747690bdc5f3" -dependencies = [ - "async-lock", - "bytes", - "futures", - "hex", - "http", - "hyperx", - "md-5", - "reqwest", - "reqwest-middleware", - "reqwest-retry", - "reqwest-retry-after", - "reqwest-tracing", - "serde", - "serde_json", - "serde_urlencoded", - "sha2", - "shell-escape", - "thiserror", - "tracing", - "url", -] - [[package]] name = "gzp" version = "0.11.1" @@ -1588,9 +1550,9 @@ checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opendal" -version = "0.22.6" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c32203b1d5644a1cbd7b918a36269f59a055ff8c6c29f021a34b612a68234f07" +checksum = "97541724cf371973b28f5a873404f2a2a4f7bb1efe7ca36a27836c13958781c2" dependencies = [ "anyhow", "async-compat", @@ -1854,9 +1816,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-xml" -version = "0.26.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41" dependencies = [ "memchr", "serde", @@ -2018,9 +1980,9 @@ dependencies = [ [[package]] name = "reqsign" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bdb7e4031af194baaf4422662b753ae625400ed8d562b3b3530935e1a52a83" +checksum = "1c97ac0f771c78ddf4bcb73c8454c76565a7249780e7296767f7e89661b0e045" dependencies = [ "anyhow", "backon", @@ -2089,69 +2051,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "reqwest-middleware" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69539cea4148dce683bec9dc95be3f0397a9bb2c248a49c8296a9d21659a8cdd" -dependencies = [ - "anyhow", - "async-trait", - "futures", - "http", - "reqwest", - "serde", - "task-local-extensions", - "thiserror", -] - -[[package]] -name = "reqwest-retry" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce246a729eaa6aff5e215aee42845bf5fed9893cc6cd51aeeb712f34e04dd9f3" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "futures", - "http", - "hyper", - "reqwest", - "reqwest-middleware", - "retry-policies", - "task-local-extensions", - "tokio", - "tracing", -] - -[[package]] -name = "reqwest-retry-after" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80dd628f731727ef10bd58e09e5a6970ea130a9615c12da0d16579326482148" -dependencies = [ - "async-trait", - "reqwest", - "reqwest-middleware", - "task-local-extensions", - "tokio", -] - -[[package]] -name = "reqwest-tracing" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64977f9a47fa7768cc88751e29026e569730ac1667c2eaeaac04b32624849fbe" -dependencies = [ - "async-trait", - "reqwest", - "reqwest-middleware", - "task-local-extensions", - "tokio", - "tracing", -] - [[package]] name = "retain_mut" version = "0.1.9" @@ -2167,17 +2066,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "retry-policies" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09bbcb5003282bcb688f0bae741b278e9c7e8f378f561522c9806c58e075d9b" -dependencies = [ - "anyhow", - "chrono", - "rand 0.8.5", -] - [[package]] name = "ring" version = "0.16.20" @@ -2333,7 +2221,6 @@ dependencies = [ "flate2", "futures", "futures-locks", - "gha-toolkit", "gzp", "http", "hyper", @@ -2579,12 +2466,6 @@ dependencies = [ "digest", ] -[[package]] -name = "shell-escape" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" - [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -2717,15 +2598,6 @@ dependencies = [ "xattr", ] -[[package]] -name = "task-local-extensions" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4167afbec18ae012de40f8cf1b9bf48420abb390678c34821caa07d924941cc4" -dependencies = [ - "tokio", -] - [[package]] name = "tempfile" version = "3.3.0" @@ -3018,21 +2890,9 @@ dependencies = [ "cfg-if 1.0.0", "log", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" version = "0.1.30" diff --git a/Cargo.toml b/Cargo.toml index 596d0ade7..8d257c652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ bincode = "1" blake3 = "1" byteorder = "1.0" bytes = "1" -opendal = { version= "0.22.6", optional=true } +opendal = { version= "0.24", optional=true } reqsign = {version="0.7.3", optional=true} chrono = { version = "0.4.23", optional = true } clap = { version = "4.0.29", features = ["derive", "env", "wrap_help"] } @@ -38,7 +38,6 @@ filetime = "0.2" flate2 = { version = "1.0", optional = true, default-features = false, features = ["rust_backend"] } futures = "0.3" futures-locks = "0.7" -gha-toolkit = { version = "0.3.1", optional = true } gzp = { version = "0.11.1", default-features = false, features = ["deflate_rust"] } http = "0.2" hyper = { version = "0.14.10", optional = true, features = ["server"] } @@ -123,7 +122,7 @@ all = ["dist-client", "redis", "s3", "memcached", "gcs", "azure", "gha"] azure = ["opendal","reqsign"] s3 = ["opendal","reqsign"] gcs = ["opendal","reqsign", "url", "reqwest/blocking"] -gha = ["gha-toolkit"] +gha = ["opendal"] memcached = ["memcached-rs"] native-zlib = [] redis = ["url", "opendal/services-redis"] diff --git a/docs/GHA.md b/docs/GHA.md index 86249ac83..e5b222ac5 100644 --- a/docs/GHA.md +++ b/docs/GHA.md @@ -1,8 +1,8 @@ # GitHub Actions -To use the [GitHub Actions cache](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows), you need to set the `SCCACHE_GHA_CACHE_URL`/`ACTIONS_CACHE_URL` and `SCCACHE_GHA_RUNTIME_TOKEN`/`ACTIONS_RUNTIME_TOKEN` environmental variables. The `SCCACHE_` prefixed environmental variables override the variables without the prefix. +To use the [GitHub Actions cache](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows), you need to set the `SCCACHE_GHA_VERSION` which is a namespace for the whole cache set. -In a GitHub Actions workflow, you can set these environmental variables using the following step. +This cache type will needs token like `ACTIONS_CACHE_URL` and `ACTIONS_RUNTIME_TOKEN` to work. You can set these environmental variables using the following step in a GitHub Actions workflow. ```yaml - name: Configure sccache @@ -12,17 +12,3 @@ In a GitHub Actions workflow, you can set these environmental variables using th core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); ``` - -To write to the cache, set `SCCACHE_GHA_CACHE_TO` to a cache key, for example -`sccache-latest`. To read from cache key prefixes, set `SCCACHE_GHA_CACHE_FROM` -to a comma-separated list of cache key prefixes, for example `sccache-`. - -In contrast to the [`@actions/cache`](https://github.com/actions/cache) action, which saves a single large archive per cache key, `sccache` with GHA cache storage saves each cache entry separately. - -GHA cache storage will create many small caches with the same cache key, e.g. `SCCACHE_GHA_CACHE_TO` and `SCCACHE_GHA_CACHE_FROM`. These GHA caches are differentiated by their [_version_](https://github.com/actions/cache#cache-version). The GHA cache implementation in `sccache` calculates the cache version from the [`sccache` entry key](docs/Caching.md), e.g. the source file path. - -For example, if a cache entry has the version `main.rs` and has GHA cache entries for the `sccache-1` and `sccache-2` keys, then `SCCACHE_GHA_CACHE_FROM=sccache-` will match both and [return the most recent entry](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key). - -This behavior is useful for scoping caches from different versions of Rust or for cross-platform builds (`rust-sdk-{RUST_TOOLKIT}-{TARGET_TRIPLE}-`), and to allow newer commits to override older caches by adding the Git SHA as a suffix (`-{GITHUB_SHA}`), as in the following screenshot. - - diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 8fb3d3ba1..46c6d4a45 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -394,6 +394,7 @@ impl Storage for opendal::Operator { let can_write = match self.object(path).write("Hello, World!").await { Ok(_) => true, + Err(err) if err.kind() == ErrorKind::ObjectAlreadyExists => true, Err(err) if err.kind() == ErrorKind::ObjectPermissionDenied => false, Err(err) => bail!("cache storage failed to write: {:?}", err), }; @@ -485,15 +486,10 @@ pub fn storage_from_config( return Ok(Arc::new(storage)); } #[cfg(feature = "gha")] - CacheType::GHA(config::GHACacheConfig { - ref url, - ref token, - ref cache_to, - ref cache_from, - }) => { - debug!("Init gha cache with url {url}"); + CacheType::GHA(config::GHACacheConfig { ref version }) => { + debug!("Init gha cache with version {version}"); - let storage = GHACache::new(url, token, cache_to.clone(), cache_from.clone()) + let storage = GHACache::build(version) .map_err(|err| anyhow!("create gha cache failed: {err:?}"))?; return Ok(Arc::new(storage)); } diff --git a/src/cache/gha.rs b/src/cache/gha.rs index 06ed18edf..29189ff67 100644 --- a/src/cache/gha.rs +++ b/src/cache/gha.rs @@ -12,82 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::io; -use std::time::{Duration, Instant}; +use opendal::layers::LoggingLayer; +use opendal::services::ghac; +use opendal::Operator; -use gha_toolkit::cache::*; - -use crate::cache::{Cache, CacheRead, CacheWrite, Storage}; use crate::errors::*; -/// A cache that stores entries in Amazon S3. -pub struct GHACache { - client: CacheClient, -} +/// A cache that stores entries in GHA Cache Services. +pub struct GHACache; impl GHACache { - /// Create a new `GHACache` storing data in `bucket`. - pub fn new( - url: &str, - token: &str, - cache_to: Option, - cache_from: Option, - ) -> Result { - let mut builder = CacheClient::builder(url, token); - if let Some(key) = cache_to { - builder = builder.cache_to(key); - } - if let Some(cache_from) = cache_from { - builder = builder.cache_from( - cache_from - .split(',') - .map(str::trim) - .filter(|s| !s.is_empty()), - ); - } - let client = builder.build()?; - Ok(GHACache { client }) - } -} - -#[async_trait] -impl Storage for GHACache { - async fn get(&self, key: &str) -> Result { - let entry = self.client.entry(key).await?; - if let Some(ArtifactCacheEntry { - archive_location: Some(url), - .. - }) = entry - { - let data = self.client.get(&url).await?; - let hit = CacheRead::from(io::Cursor::new(data))?; - Ok(Cache::Hit(hit)) - } else { - Ok(Cache::Miss) - } - } - - async fn put(&self, key: &str, entry: CacheWrite) -> Result { - let start = Instant::now(); - let data = entry.finish()?; - self.client.put(key, io::Cursor::new(data)).await?; - Ok(start.elapsed()) - } - - fn location(&self) -> String { - format!( - "GHA, url: {}, cache_to: {:?}, cache_from: {:?}", - self.client.base_url(), - self.client.cache_to(), - self.client.cache_from() - ) - } - - async fn current_size(&self) -> Result> { - Ok(None) - } + pub fn build(version: &str) -> Result { + let mut builder = ghac::Builder::default(); + builder.version(version); - async fn max_size(&self) -> Result> { - Ok(None) + let op: Operator = builder.build()?.into(); + Ok(op.layer(LoggingLayer::default())) } } diff --git a/src/config.rs b/src/config.rs index 1ba495c0b..ea62d02a3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -192,10 +192,9 @@ pub struct GCSCacheConfig { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct GHACacheConfig { - pub url: String, - pub token: String, - pub cache_to: Option, - pub cache_from: Option, + /// Version for gha cache is a namespace. By setting different versions, + /// we can avoid mixed caches. + pub version: String, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -573,20 +572,8 @@ fn config_from_env() -> Result { }); // ======= GHA ======= - let gha = if let (Some(url), Some(token)) = ( - env::var("SCCACHE_GHA_CACHE_URL") - .ok() - .or_else(|| env::var("ACTIONS_CACHE_URL").ok()), - env::var("SCCACHE_GHA_RUNTIME_TOKEN") - .ok() - .or_else(|| env::var("ACTIONS_RUNTIME_TOKEN").ok()), - ) { - Some(GHACacheConfig { - url, - token, - cache_to: env::var("SCCACHE_GHA_CACHE_TO").ok(), - cache_from: env::var("SCCACHE_GHA_CACHE_FROM").ok(), - }) + let gha = if let Ok(version) = env::var("SCCACHE_GHA_VERSION") { + Some(GHACacheConfig { version }) } else { None }; @@ -1083,10 +1070,7 @@ key_prefix = "prefix" service_account = "example_service_account" [cache.gha] -url = "http://localhost" -token = "secret" -cache_to = "sccache-latest" -cache_from = "sccache-" +version = "sccache" [cache.memcached] url = "..." @@ -1122,10 +1106,7 @@ no_credentials = true credential_url: None, }), gha: Some(GHACacheConfig { - url: "http://localhost".to_owned(), - token: "secret".to_owned(), - cache_to: Some("sccache-latest".to_owned()), - cache_from: Some("sccache-".to_owned()), + version: "sccache".to_string() }), redis: Some(RedisCacheConfig { url: "redis://user:passwd@1.2.3.4:6379/1".to_owned(),