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

Support a relative path for the options to "link to source" for the --checkout-path CLI option #490 #929

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

Anthony-Eid
Copy link
Contributor

@Anthony-Eid Anthony-Eid commented May 24, 2024

Bug/issue #490

Summary

Adds support for relative paths when using --checkout-path as an argument in docc convert cli command.

Paths that start with a slash '/' are assumed to be absolute, otherwise paths are assumed to be relative.

Testing

Updated testSourceRepositoryAllArgumentsSpecified() to use a valid temporary path for SourceRepository checkout path argument.

Created testThrowsValidationErrorWhenCheckoutPathIsInvalid() to make sure that an error is thrown when --checkout-path is an invalid path.

Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

  • Added tests
  • Ran the ./bin/test script and it succeeded
  • Updated documentation if necessary

@Anthony-Eid
Copy link
Contributor Author

Update: SourceRepository is working with relative paths now! Still need to throw an error if an invalid directory is passed into checkout--path and add a test for that scenario.

Updated testSourceRepositoryAllArgumentsSpecified to use a valid temp directory for it's checkout path argument
because SourceRepository init now guards against invalid checkout paths.

Added a test to make sure SourceRepository guards against invalid checkout paths
@Anthony-Eid
Copy link
Contributor Author

Is there a better/idomatic way to make temporary directories for unit testing than what I did below?

func testSourceRepositoryAllArgumentsSpecified() throws {
        let tempDir = try createTemporaryDirectory(pathComponents: "addFileToCreateValidDirectory")
        
        // removing file:// prefix from checkout path because the directory is not
        // recognized as a valid directory with it
        let checkoutPath = tempDir.absoluteString
        let startIndex = checkoutPath.index(checkoutPath.startIndex, offsetBy: 7)
        let absoluteCheckoutPath = String(checkoutPath[startIndex...])


        for sourceService in ["github", "gitlab", "bitbucket"] {
            try assertSourceRepositoryArguments(
                checkoutPath: absoluteCheckoutPath,
                sourceService: sourceService,
                sourceServiceBaseURL: "https://example.com/path/to/base"
            ) { action in
                XCTAssertEqual(action.sourceRepository?.checkoutPath, "\(absoluteCheckoutPath)/")
                XCTAssertEqual(action.sourceRepository?.sourceServiceBaseURL.absoluteString, "https://example.com/path/to/base")
            }
        }
    }

Also, is it intentionally that a FileManager.default.directoryExists(atPath: checkoutPath) doesn't consider empty directories as valid? As shown below.

extension FileManagerProtocol {
    /// Returns a Boolean value that indicates whether a directory exists at a specified path.
    package func directoryExists(atPath path: String) -> Bool {
        var isDirectory = ObjCBool(booleanLiteral: false)
        let fileExistsAtPath = fileExists(atPath: path, isDirectory: &isDirectory)
        return fileExistsAtPath && isDirectory.boolValue
    }
}

@Anthony-Eid Anthony-Eid marked this pull request as ready for review May 31, 2024 01:08
Comment on lines +36 to +40
// Get the absolute path of a file without the file:// prefix because this function used to only
// expect absolute paths from a user and didn't convert checkoutPath to a URL and back.
let absoluteCheckoutPath = URL(fileURLWithPath: checkoutPath).absoluteString
let startIndex = absoluteCheckoutPath.index(absoluteCheckoutPath.startIndex, offsetBy: 7)
self.checkoutPath = String(absoluteCheckoutPath[startIndex...])
Copy link
Contributor

Choose a reason for hiding this comment

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

I've never seen this pattern before and I can't find an input where it behaves differently than URL(fileURLWithPath: checkoutPath).path

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was hesitant to use URL(fileURLWithPath: checkoutPath).path because Xcode said it's going to be deprecated in the future. What should I do in the future when something is going to be deprecated?

Also, I've updated the code to use .path instead of the pattern I was using. Thank you for the advice!

@@ -61,28 +61,25 @@ service configuration flags like so:
docc convert […] \
--source-service github \
--source-service-base-url https://github.com/<org>/<repo>/blob/<branch> \
--checkout-path <absolute path to local checkout>
--checkout-path <absolute or relative path to local checkout>
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't find it necessary to explicitly mention that both absolute and relative paths are supported

Suggested change
--checkout-path <absolute or relative path to local checkout>
--checkout-path <path to local checkout>

@@ -50,6 +59,28 @@ class ConvertSubcommandSourceRepositoryTests: XCTestCase {
}
}

func testThrowsValidationErrorWhenCheckoutPathIsInvalid() throws {
let tempDir = try createTemporaryDirectory(named: "tmp").appendingPathComponent("InvalidDirectory", isDirectory: false)
let absoluteCheckoutPath = tempDir.absoluteString
Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand correctly, this test if verifying that using a file:// prefix for the command line argument raises an error. AFAIK we don't do that for any other path argument and I don't think it's all that likely that developers will do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That test is checking that an error is thrown when a path is used as an argument, but the directories of the path don't exist.

Specifically, it's testing this guard I added in SourceRepositoryArguments.swift

guard FileManager.default.directoryExists(atPath: checkoutPath) else {
    throw ValidationError("Checkout path directory '\(checkoutPath)' doesn't exist for --checkout-path argument.")
}

To the end of the SourceRepository.init?(from arguments: SourceRepositoryArguments) function

Copy link
Contributor

Choose a reason for hiding this comment

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

There's a significant amount of whitespace-only changes in all these files which adds noise to the PR. Please remove all those.

Comment on lines -33 to +42
checkoutPath: "checkout path",
checkoutPath: absoluteCheckoutPath,
Copy link
Contributor

Choose a reason for hiding this comment

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

If you pass the original value ("checkout path") here, then you can use this test to verify that a relative path is transformed into an absolute path. Currently the test would pass even without the changes to the SourceRepository initializer.

@@ -9,33 +9,40 @@
*/

import Foundation
import ArgumentParser

/// A remote repository that hosts source code.
public struct SourceRepository {
/// The path at which the repository is cloned locally.
public var checkoutPath: String
Copy link
Contributor

Choose a reason for hiding this comment

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

If this was defined as a URL then the issue with absolute and relative paths would already be handled by the URL(fileURLWithPath:) initializer.

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