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

Detect Elan self update disabled (e.g. installed from distro repos) #998

Merged
merged 11 commits into from
Dec 12, 2024

Conversation

grahnen
Copy link
Contributor

@grahnen grahnen commented Dec 9, 2024

What does this PR do

Adds a config option to disable elan self-update (for people like me, who installed elan from distro repos).

Standards checklist

  • The PR title is descriptive
  • I have read CONTRIBUTING.md
  • Optional: I have tested the code myself
  • If this PR introduces new user-facing messages they are translated

For new steps

  • Optional: Topgrade skips this step where needed
  • Optional: The --dry-run option works with this step
  • Optional: The --yes option works with this step if it is supported by
    the underlying command

If you developed a feature or a bug fix for someone else and you do not have the
means to test it, please tag this person here.

@SteveLauC
Copy link
Member

who installed elan from distro repos

Is there any way we can know this? If so, we can automatically skip this rather than hardcoding it? Does elan self update give any error in this case?

@grahnen
Copy link
Contributor Author

grahnen commented Dec 9, 2024

who installed elan from distro repos

Is there any way we can know this? If so, we can automatically skip this rather than hardcoding it? Does elan self update give any error in this case?

It prints an error message with the reason, but returns with error code 1, just like any other error.

@SteveLauC
Copy link
Member

It prints an error message with the reason, but returns with error code 1, just like any other error.

Great, then we can use it! Just like what we did here: https://github.com/topgrade-rs/topgrade/pull/971/files

@grahnen
Copy link
Contributor Author

grahnen commented Dec 9, 2024

It prints an error message with the reason, but returns with error code 1, just like any other error.

Great, then we can use it! Just like what we did here: https://github.com/topgrade-rs/topgrade/pull/971/files

There is no error when elan self --help is called; only when elan self update is called.

I reused the code for Helm, which checks if the error message contains this specific error, and in such a case just keeps going with the rest of the update.

It still prints the error, which may or may not be desired behaviour.

@grahnen grahnen changed the title Add config option "self_update" for Elan Detect Elan self update disabled (e.g. installed from distro repos) Dec 9, 2024
let disabled = "self-update is disabled";
let mut exec = ctx.run_type().execute(&elan);
let mut success = true;
if let Err(e) = exec.arg("self").arg("update").status_checked() {
Copy link
Member

@SteveLauC SteveLauC Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still prints the error, which may or may not be desired behaviour.

If you use output_checked() directly, then the error won't be printed.

Can you just do, I guess this would work

let result = ctx
        .run_type()
        .execute(&uv_exec)
        .args(["self", "update"])
        .output_checked();
if result.is_ok() {
    ctx.run_type()
        .execute(&uv_exec)
        .args(["self", "update"])
        .status_checked()?;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would do self-update twice, if the first one succeeds, right?
Is that better than comparing the output of the "self update" call to a saved error message?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would do self-update twice, if the first one succeeds, right?

Right, thanks for catching this. I was probably dizzy at that time 🤦‍♂️


Is that better than comparing the output of the "self update" call to a saved error message?

I think we do need to compare the output with the saved message, what about:

  1. Do elan self update, capture the output
    1. If 1 succeeds, the self-update feature is available and it just successfully performed the update, we print!() the captured stdout and eprint!() the captured stderr
    2. If 1 fails, there are possibly 2 cases why this failure happened:
      1. The self-update feature is unavailable (stdout or stderr contains the saved error message), nothing further needs to be done
      2. The self-update feature is available, but the operation just failed, error out
  2. Do elan update
    // If elan is externally managed, then running `elan self update` will give an 
    // error message that contains `disabled_error_msg`, in this case, Topgrade 
    // tries to hide that `elan self update` has just been executed.
    let disabled_error_msg = "self-update is disabled";
    let executor_output = ctx.run_type().execute(&elan).args(["self", "update"]).output()?;
    match executor_output{
        ExecutorOutput::Wet(command_output) => {
            if command_output.status.success() {
                // Flush the captured output
                std::io::stdout().lock().write(&command_output.stdout).unwrap();
                std::io::stderr().lock().write(&command_output.stderr).unwrap();
            } else {
                let stderr_as_str = std::str::from_utf8(&command_output.stderr).unwrap();
                if stderr_as_str.contains(disabled_error_msg) {
                    // `elan` is externally managed, we cannot do the update. Users
                    // won't see any error message because Topgrade captures them
                    // all.
                } else {
                    // `elan` is NOT externally managed, `elan self update` can 
                    // be performed, but the invocation failed, so we report the
                    // error to the user and error out.
                    std::io::stdout().lock().write(&command_output.stdout).unwrap();
                    std::io::stderr().lock().write(&command_output.stderr).unwrap();

                    return Err(StepFailed.into());
                }
            }

        }
        ExecutorOutput::Dry => {/* nothing needed because in a dry run */}
    }

@grahnen
Copy link
Contributor Author

grahnen commented Dec 10, 2024

The latest commit hides the output if the "self update" command fails because it is disabled, otherwise returns the error of that command. This, to me, seems better than running "self update" twice. Let me know if you disagree.

@SteveLauC
Copy link
Member

The latest commit hides the output if the "self update" command fails because it is disabled, otherwise returns the error of that command. This, to me, seems better than running "self update" twice. Let me know if you disagree.

Looks like we are doing the same thing, check out my last comment, it provides an implementation with slightly better UX

@SteveLauC
Copy link
Member

Though my patch needs this #1002 to work

@grahnen
Copy link
Contributor Author

grahnen commented Dec 10, 2024

The latest commit hides the output if the "self update" command fails because it is disabled, otherwise returns the error of that command. This, to me, seems better than running "self update" twice. Let me know if you disagree.

Looks like we are doing the same thing, check out my last comment, it provides an implementation with slightly better UX

Sorry, I missed that one. Yes, we are doing the same thing, and I agree yours do look better code-wise. Let's wait for #1002, and fix this once that is merged.

The "filtering error messages" methodology might be common enough to create a function
output_checked_or (similar to output_checked_with but filtering errors rather than successes).

@SteveLauC
Copy link
Member

The "filtering error messages" methodology might be common enough to create a function
output_checked_or (similar to output_checked_with but filtering errors rather than successes).

If it becomes commonly used, yeah, we should make it a utility

@SteveLauC
Copy link
Member

Let's wait for #1002, and fix this once that is merged.

#1002 merged

@grahnen
Copy link
Contributor Author

grahnen commented Dec 11, 2024

I took your code and adjusted it with clippy's suggestions. It should be ready now :)

Copy link
Member

@SteveLauC SteveLauC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@SteveLauC SteveLauC merged commit c5f2d7b into topgrade-rs:main Dec 12, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants