-
-
Notifications
You must be signed in to change notification settings - Fork 518
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
☁️ Add analytics
option to Config.Cloud
to enable sending analytics event to cloud backend
#3547
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.
Small nits but otherwise looks good to me. However, I am not extremely familiar with this area, so I'd definitely wait for someone else also having a look!
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 is awesome @danyf90. Thanks for making it happen. As part of Tuist Cloud we are planning to have a home dashboard where I think we can present the data sent by this logic.
self.cloudConfig = cloudConfig | ||
} | ||
|
||
func storeResource(encodedCommandEvent: Data) -> CloudStoreResource { |
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.
Nitpick:
- I'd rename the method to
create
. The resource is already in the name of the factory andcreate
aligns more with API conventions. - I'd turn
Data
into a serializable object that matches the schema expected don't the API side. If the strong contract between the client and the server becomes too limiting, we can figure out ways to add more flexibility. - I'd rename
CloudStoreResource
toCloudAnalyticsCreateResource
.
Moreover, I'd add a unit test to ensure the resource that we get from the factory is the expected one.
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.
Not sure about the naming, we are not really creating anything here 🤔
I have used store for consistency with the cache API, but maybe in this case send
is more appropriate?
|
||
public final class TuistAnalytics { | ||
public static func bootstrap() { | ||
AsyncQueue.sharedInstance.register(dispatcher: TuistAnalyticsDispatcher()) | ||
public static func bootstrap(configLoader: ConfigLoader = ConfigLoader(manifestLoader: ManifestLoader())) throws { |
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.
To be consistent with the rest of the codebase, I'd avoid loading the configuration as part of bootstrapping analytics. If the bootstrapping of analytics requires cloudReporting: Bool
, let's make that partof the method's signature and pass it from outside:
public static bootstrap(cloudReporting: Bool)
The command's service is the one loading the config and passing it down to the components that need it.
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.
Makes sense, but this part is common across all commands, and hence bootstrapped directly in the main.swift
. Would you put this logic there?
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'd create another static
method on this component such as loadCloudConfig
that would handle that logic. It's not ideal but I agree with the expectation of boostrap()
not containing config loading. I'd then call this method explicitly in main.swift
Sources/tuist/main.swift
Outdated
@@ -6,7 +6,7 @@ import TuistSupport | |||
if CommandLine.arguments.contains("--verbose") { try? ProcessEnv.setVar(Constants.EnvironmentVariables.verbose, value: "true") } | |||
|
|||
TuistSupport.LogOutput.bootstrap() | |||
TuistAnalytics.bootstrap() | |||
try TuistAnalytics.bootstrap() |
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 see why the bootstrap business logic was modified to read the config. What about moving this to the commands?
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.
You mean adding TuistAnalytics.bootstrap()
to each command?
Given we know it is for each command it would be better to handle it upfront, avoiding duplications and forgetting to add it in some commands
…lytics event to cloud backend
eec059e
to
d2cdcf4
Compare
👋🏼 I pushed some tests. I slept over the idea of loading the config as part of initializing analytics, and I think it's a bad idea for user experience because loading the config invokes the compiler, and that might take some time if the cache is not warmed. I'm pondering the idea of using environment variables instead for passing the cloud configuration:
Because we'd like developers to have those as part of the project, I'm thinking about having a Thoughts @danyf90 / @fortmarek ? |
I gave another thought, and I think we can keep things as they are, but turn the |
@pepibumur Isn't JSON enough for our use case, and easier to parse from Swift? We might even go hybrid and have the logic that:
Apart from the above, I would expect the parsing of Config.swift should be quick anyway, do we have any benchmark on it? Also, it will be parsed anyway at some point of the command logic, maybe we can reuse the one parsed for analytics, and "gain" back the time we have spent earlier? |
The issue with this is you can edit the file without using |
Another issue is that you would lose any "executable code" you have in the swift file and the next If the performance problem is not too big I would probably address it separately from this PR, what do you think @fortmarek and @pepibumur ? |
JSON and YAML can get very messy and therefore I propose using
If you have numbers that'd be great but considering it's invoking the compiler and the dynamic linker, I doubt it's under 300 ms.
You are right. Choosing
Let's refrain from merging it until we address the other concern. If we merge and introduce a performance regression we might be worsening the DX using the tool, and the user experience should always be first when prioritizing. |
@fortmarek / @pepibumur I have pushed some changes to share the ConfigLoader between the analytics bootstrap and the command execution, which should mitigate the problem of having to parse the config there. Let me know what you think about it 🙏 |
Turning the config loader into a singleton is not necessary because the manifest loader is already caching the JSON-representation of the manifests (plus it makes the instance a place for storing state that might make some code flows non-deterministic). |
0308d6d
to
7c59046
Compare
Done ✅ 🤞 |
# Conflicts: # CHANGELOG.md
Resolves #3527
Short description 📝
Checklist ✅
CHANGELOG.md
has been updated to reflect the changes. In case of a breaking change, it's been flagged as such.