-
Notifications
You must be signed in to change notification settings - Fork 415
Support non-lock files for C# cache key computation #3117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR enhances C# dependency caching support by allowing non-lock files to be used when computing cache keys. Currently, only packages.lock.json and paket.lock files are used for cache key computation, which limits caching benefits to projects that have these lock files checked in. The new implementation maintains backward compatibility while expanding support to more C# projects through an optional feature flag.
Key changes:
- Adds a new
CsharpNewCacheKeyfeature flag with environment variable support - Refactors cache configuration from static data structures to dynamic functions
- Implements fallback logic to use
.csproj,packages.config, andnuget.configfiles when lock files are unavailable
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/feature-flags.ts | Adds the new CsharpNewCacheKey feature flag configuration |
| src/dependency-caching.ts | Core refactoring to support dynamic hash pattern generation and C# fallback logic |
| src/init-action.ts | Updates function calls to pass CodeQL and features parameters |
| src/analyze-action.ts | Updates function calls to pass CodeQL and features parameters |
| lib/* | Generated JavaScript files reflecting the TypeScript changes |
henrymercer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable from an Action perspective
michaelnebel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks plausible to me!
…o a function Changing `paths` to be a function is necessary to allow `getTemporaryDirectory` to be called
Add tests for `makePatternCheck` and `checkHashPatterns`
7f81d73 to
2680455
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
| * @param codeql The CodeQL instance to use. | ||
| * @param features Information about which FFs are enabled. | ||
| * @param language The language being analyzed. | ||
| * @param cacheConfig The cache configuration for the language. |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSDoc parameter description for cacheConfig is incorrect. This parameter has been removed from the function signature but the documentation still references it. The function now receives patterns instead.
| * @param cacheConfig The cache configuration for the language. | |
| * @param patterns The file patterns to hash for the cache key. |
|
|
||
| if (patterns === undefined) { | ||
| logger.info( | ||
| `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`, |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log message says 'Skipping download' but the function checkHashPatterns is also used in the upload path. Consider making the message more generic or having separate messages for download vs upload contexts.
| `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`, | |
| `Skipping dependency cache for ${language} as we cannot calculate a hash for the cache key.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems valid, though "Skipping dependency caching" seems more natural.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new pattern for deciding which C# files to use in the cache hash LGTM!
Should we use this opportunity to also extend the list of paths that are included in the cache for C#? The setup-dotnet Action considers more paths and computes them dynamically. Now that we have getDependencyPaths as a function, it would be easy to do this.
Does this mean that we are "only" including "{working_dir}/.nuget/packages" in the cache?
The dependency manager for C# use the "scratch dir" for storing downloaded packages.
nickrolfe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only reviewed the parts that generalise the mechanism to add a feature-flag-specific prefix to the cache key, but that looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generally looks good. The only blocking comment from me is the naming of the cache entries.
For PRs like this in the future, consider splitting up into a core functionality part (e.g. here, handling the addition of feature flags to the cache key), and a language-specific part (e.g. here, changing the cache key for C#).
| // If we get to this point, the `basePatterns` didn't find any files, | ||
| // and `Feature.CsharpNewCacheKey` is either not enabled or we didn't | ||
| // find any files using those patterns either. | ||
| return undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The last bit isn't true since we'll return the result of makePatternCheck, even if it's undefined. It might be clearer to have a helper function that checks whether the patterns match anything, and use that here instead.
|
|
||
| if (patterns === undefined) { | ||
| logger.info( | ||
| `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems valid, though "Skipping dependency caching" seems more natural.
|
|
||
| return `${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`; | ||
| // Assemble the cache key. | ||
| return `${featurePrefix}${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If users list cache entries using the API, I think it'd be clearer to them what those cache entries are for if we keep codeql-dependencies- at the front.
This PR modifies our implementation of dependency caching for C# to allow non-lock files to be used when computing a hash for the cache key.
The hash that's included in the cache key is derived from files which contain information about the project's dependency. The idea being that the hash changes only if the dependencies change. Currently, we only use
packages.lock.jsonandpaket.lockfiles for this. These files contain exact dependency information (including the exact versions that were resolved by the respective package manager). The resulting hashes we compute for the cache keys should therefore only change if the dependencies (including versions) change. This is the desired behaviour.However, relatively few C# projects use lock files and fewer yet have them checked-in to the repository. This means that most C# projects do not benefit from our implementation of dependency caching at the moment.
This PR modifies the behaviour of our dependency caching implementation for C# as follows:
packages.lock.jsonorpaket.lockfiles. If they exist, we use them to compute a hash for the cache key.CsharpNewCacheKeyfeature is enabled (via FF or environment variable), we search for additional files that typically contain dependency information for C# projects. If they exist, we use them to compute the hash for the cache key.Using the additional files is less precise, because they may not contain exact version information (only version ranges or no versions at all) and they contain information unrelated to dependencies. This means that the hash we compute can change, even if the dependencies don't change. However, dependency caching will benefit more C# projects.
This PR is best reviewed commit-by-commit. The first few commits just add the new
Featureand refactor the code a bit to facilitate the changes.Questions for reviewers:
Right now, disabling theFeaturewouldn't avoid restoring caches that were produced with theFeatureenabled. We likely want to do something similar as for Add feature flag to roll out JAR minimization in the Java extractor #3107 that marks caches produced with thisFeatureon.setup-dotnetAction considers more paths and computes them dynamically. Now that we havegetDependencyPathsas a function, it would be easy to do this.If so, should that be gated behind the same FF?Are there any additional files we should include for C# hashes?Risk assessment
For internal use only. Please select the risk level of this change:
Merge / deployment checklist