Skip to content
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

Option to not make github API requests when it's not needed #2725

Closed
mkalinski opened this issue Jan 31, 2023 · 3 comments · Fixed by #2763
Closed

Option to not make github API requests when it's not needed #2725

mkalinski opened this issue Jan 31, 2023 · 3 comments · Fixed by #2763
Labels
enhancement New feature or request

Comments

@mkalinski
Copy link

Is your feature request related to a problem? Please describe.

When I work with my configuration files, I often like to make a quick tweak, do a chezmoi apply, see the results, tweak again, apply again, and so on.

Lately, I've started using externals, with gitHubLatestRelease to automatically upgrade a tool I'm using. But with my workflow of often calling chezmoi apply, I've been running into github's rate limiting.

I know the usual answer to issues with rate limiting seems to be "connect an API token to lift the limit", but I think it's a wrong solution in my particular case. I just find chezmoi's current behaviour slightly wrong.

Let's assume I had the following external configured:

['example/bin/chezmoi']
type = 'file'
url = 'https://github.com/twpayne/chezmoi/releases/download/{{ (gitHubLatestRelease "twpayne/chezmoi").TagName }}/chezmoi-linux-amd64'
refreshPeriod = '24h'

(I don't update chezmoi this way, this is just a hypothetical.)

Because of refreshPeriod, the download has no chance of running more often than once a day. However, every time I do chezmoi apply, the .chezmoiexternal.toml file gets evaluated, and gitHubLatestRelease is called, causing unnecessary web requests, and eventually rate limiting.

Of course, the problem is that chezmoi needs to evaluate the template functions in .chezmoiexternal.toml before it can read its contents and interpret the refreshPeriod value. But from the user perspective, this is a bit frustrating, as I feel there really should be an option to let chezmoi know that it shouldn't phone github more often than it's logically necessary.

Describe the solution you'd like

I'm going to only consider gitHub* functions in .chezmoiexternal files. I know they could be used in other templates, too, but I feel it's probably really rare, and superfluous to my problem.

I think the least problematic solution would be to add a command line flag that would make chezmoi not touch .chezmoiexternal files during a run. Like --refresh-externals never but stronger (--no-externals?).

It's probably the easiest way to flag that I, as a user, am sure that I need no externals update during this run under any circumstances.

Describe alternatives you've considered

Passing refreshPeriod to gitHub* functions as an argument

Since the problem is that template functions need to be evaluated before chezmoi can read the refreshPeriod value, perhaps it would help to make these functions aware of the value. Something like {{ (gitHubLatestRelease "twpayne/chezmoi" "24h").TagName }}.

Of course, then the problem becomes what should the function evaluate to when it doesn't make a call to github? Should they cache return values between calls? I feel like it's not ideal to ask to make these functions more complicated.

Lazy alternative to template functions for .chezmoiexternal

Some special syntax that would make chezmoi substitute values in the URL only after reading refreshPeriod if needed. For example:

['example/bin/chezmoi']
type = 'file'
url = 'https://github.com/twpayne/chezmoi/releases/download/$GITHUB_LATEST_RELEASE_TAG_NAME/chezmoi-linux-amd64'
refreshPeriod = '24h'

It's unfortunately inelegant compared to the template syntax.

@mkalinski mkalinski added the enhancement New feature or request label Jan 31, 2023
@twpayne
Copy link
Owner

twpayne commented Feb 1, 2023

Thank you for opening the issue and for a very well considered report! For reference, a similar issue is encountered with the gitHubKeys template function (#2253) and a "no network mode" has also been discussed previously (#2354).

tl;dr summary: caching is hard and there might already be a work-around.

A couple of questions/possible solutions:

  1. Is there a particular reason why a GitHub token is not an option for you?
  2. chezmoi apply takes an optional list of targets to update. The default is all targets, but you can run chezmoi apply ~/path/to/tweak to just update a single file or directory. This is both significantly faster than running chezmoi apply. chezmoi goes to some effort to do the minimum work required to get the answer you want (it's not perfect but not too bad). That said, with the current architecture chezmoi always needs to evaluate externals, so it won't actually help with the rate limiting.
  3. Another solution is to move the call to gitHubLatestRelease to your config file template. Roughly you speaking you put data.program.version = {{ gitHubLatestRelease "repo/program" | quote }} in your config file template and then use {{ .program.version }} in your .chezmoiexternal file. This means that gitHubLatestRelease only gets called when you run chezmoi init (solving the rate limiting problem) and the change to a new version only occurs when you want (which can be a bit more secure, as you don't blindly import the latest version).

Would any of these be an option for you?

@mkalinski
Copy link
Author

  1. It's not that it's impossible to use in my case. But it just felt like the wrong solution to the problem, as it clearly makes no sense to evaluate gitHubLatestRelease in this particular situation in .chezmoiexternal. So I created this issue to see what may come of it.
  2. That would be viable for me, as the offending .chezmoiexternal is off in its own directory. But from your comment, I understand that it's not actually viable in the current version?
  3. Here I'm kind of confused. From the docs it sounds like init is used to create a new source root and config file. I already have a source root and a config file, and would not want to overwrite them. If init can be made to act in a way like "evaluate a config template and merge it with existing config", then it's not clear from the documentation.

And on the topic of using sneaky template evaluation methods to solve this problem: I already had an idea that I could use scripts and cache directory to keep my own timestamp of last run, and then gate the offending .chezmoiexternal entry with an if that compares it with output "date" "+%s". I may still end up doing it this way (for now, I just keep the version pinned).

But again, it feels like an ugly hack for something that should be relatively easy to provide by chezmoi itself. Especially since I expect my case to not really be rare. People probably use gitHub* functions in .chezmoiexternal, and those externals probably have reasonable refresh periods, that make unnecessary github requests.

@bradenhilton
Copy link
Collaborator

  1. Here I'm kind of confused. From the docs it sounds like init is used to create a new source root and config file. I already have a source root and a config file, and would not want to overwrite them. If init can be made to act in a way like "evaluate a config template and merge it with existing config", then it's not clear from the documentation.

@mkalinski Running chezmoi init for this purpose will not overwrite your source state. It will execute your config template and overwrite your existing config. This is expected (and encouraged) behavior. If you have local config file changes, you should put them in your config template.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants