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

feat: Refactor generator inputs, and first pass at a container interface #13896

Merged
merged 35 commits into from
Dec 10, 2024

Conversation

jskeet
Copy link
Collaborator

@jskeet jskeet commented Nov 28, 2024

No description provided.

@jskeet jskeet requested a review from amanda-tarafa November 28, 2024 18:48
@jskeet jskeet force-pushed the generator-input branch 2 times, most recently from 9f0b98c to 1fd2fe3 Compare December 6, 2024 12:44
@jskeet
Copy link
Collaborator Author

jskeet commented Dec 6, 2024

@amanda-tarafa: This is far enough along now that it's worth reviewing. I'd encourage you to review one commit at a time, and then note how far you've got (to avoid having to re-review). I don't anticipate modifying earlier commits any more - I suspect it'll be most productive to make changes as new commits.

Work still to do is noted in a second tab in the internal document.

# TODO: Use the appropriate output directory

GENERATOR_OUTPUT=../../..
COMPUTE_OUTPUT=$GENERATOR_OUTPUT/apis/Google.Cloud.Compute.V1
Copy link
Contributor

Choose a reason for hiding this comment

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

Leaving this comment here so I don't forget to ask about this:

Since we'll have <output>/apis I'm guessing each language can have whatever structure they need under <output>, right? Which means the CLI can make no assumptions about the structure of <output>?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Basically output is intended to overlay on top of the repo root - the CLI will (currently) make no assumptions about the structure of the repo other than the generator-input directory and a file specifying the container to use.

We may revisit this later, if we want all generated files to live until a single output directory (which then doesn't have any non-generated files in).

@@ -0,0 +1,74 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The file itself? It's an input to the generator, so that the whole file is generated - we update "ignorePaths" based on the APIs we generate.

Comment on lines 9 to 18
# Build the production library in a temporary directory so that we don't depend on
# whether we're within a repo or not.
rm -rf /tmp/compute
mkdir -p /tmp/compute

# Even if we've got handwritten code available, we don't want it for this build.
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 like this. This code is very concerned with how a tool may use it. It feels like these isolation concerns should be handled by the tool and not by this code?

And we just had a chat about this, and the general case is that this is happening in an empty directory, that is, no handwritten code available. So, why do we have to double guard ourselves against that happening?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's the general case, but not the local development case. We could potentially make sure that we always generate to an empty output directory (and then copy the results) in which case this could be simplified (and yes, I don't like it either). One downside of that would be slowing down local generation - we used to generate separately and then copy, and generating in-place instead massively improved performance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm adding a TODO here to remind us to think about this more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think moving the enum constant generator into the GAPIC generator would probably be the best fix for this, to be honest.

/// <summary>
/// Projects that exist in a non-predictable fashion.
/// Only the suffix is required - so for "Google.Cloud.Storage.V2.IntegrationTests" in "Google.Cloud.Storage.V2", this
/// would just have "IntegrationTests". Where the suffix doesn't match, a leading caret (^) is used to indicate this.
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, do we actually have "where the suffix doesn't match"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

One that I'm aware of: in Google.Cloud.Logging.Console, there's a SampleApp directory. It's deliberately not Google.Cloud.Logging.Console.SampleApp, as we don't want namespace defaulting to change what we import.

JArray ignorePaths = (JArray) jobj["ignorePaths"];
foreach (var api in _catalog.Apis)
{
ignorePaths.Add($"apis/{api.Id}/{api.Id}/**");
Copy link
Contributor

Choose a reason for hiding this comment

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

Duh, I left a comment somewhere about why renovate was in input.

Maybe there's a way to configure renovate so that we don't have to be explicit about each folder we want to skip? Maybe we can just add the ones we want renovate to look at.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Have added a TODO for this. Now that we're using Directory.Packages.props, we might not need to skip at all.


public sealed class UpdateProjectPropertiesCommand : CommandBase
{
public UpdateProjectPropertiesCommand()
Copy link
Contributor

Choose a reason for hiding this comment

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

This is to run whenever we delete a "special" project then right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can have a github action run this on submit, or a check based on this. Similar to how we check "generate projects".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's also what we'd run when adding a project, I think.

But yes, a github action wouldn't be a bad idea. Will add a TODO.

@@ -0,0 +1,29 @@
<Project>
Copy link
Contributor

Choose a reason for hiding this comment

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

Yikes, what makes certain these are the same?

Can't you have the root Directory.Packages.props and global.json point to these somehow?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't have a plan right now. We could at least have some validation step (e.g. a GitHub action).

Mind you, to some extent it doesn't really matter if they're not aligned (and I'd expect there to be Roslyn-based packages in here that aren't in the root one). Let's chat about this more though.

// we get consistent project IDs (hashed from the project name).
// This also means we don't need the project files to exist before we
// create the solution file (as they may be handwritten projects).
// Currently this is really ugly - it will be a lot cleaner with slnx files.
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh but so much better than the ghost project files. I prefer this.

@jskeet jskeet marked this pull request as ready for review December 9, 2024 16:37
@jskeet jskeet requested review from a team as code owners December 9, 2024 16:37
(Some scripts will need to be edited due to the change in location.)
(The scripts still need editing.)
This also adjusts the post-generation steps to make sure they work without any current handwritten production code.
(In other words, we should be able to generate into an entirely empty directory, and the result should be the same as is in the repo, for all the files that end up being generated.)

There's still work to do around project and solution files.
…m generator input

This includes moving apis.json into generator-input.

Changes compared with the status quo:

- Coverage XML files are no longer generated
- Solution files won't contain project references at the moment
- Documentation stubs won't be generated
  - Either this will become part of the onboarding step, or we'll
    create one temporarily during documentation generation if the
    file is absent at that point
- We generate test/snippet project files for OsLogin.Common, but there
  are no C# files. I believe that should be okay, but it's a change.
…r code

Code in the generator-input directory needs to be runnable even if it's separate from the rest of the repo.
This allows us to generate solution files predictably - whereas running "dotnet sln add" generates a new project GUID each time.
We should assume we can write to /tmp or equivalent, but not to the current working directory
The "tweaks" should assume they can modify googleapis - the calling code should take a copy beforehand if necessary.
This "supports" generate and clean as subcommands, but both are unimplemented.
Additionally, use a factory method to create an in-place NonSourceGenerator.

We'll want further refactoring later, but this is at least better.
…eans

We have some code which *is* generated code, but not generated in a regular way - e.g. conformance tests, common resource names, BigQuery resource classes. Those are all updated by running their generators manually, so shouldn't be cleaned here.
This is the result of regenerating all APIs.
(There's an uncommitted Artifact Registry change due to duplicate JSON files, which can be fixed separately.)
@jskeet jskeet changed the title Prototyping of a generator-input directory feat: Refactor generator inputs, and first pass at a container interface Dec 10, 2024
@jskeet
Copy link
Collaborator Author

jskeet commented Dec 10, 2024

I believe the test failure is because we're missing the Grafeas project in the solution file. Given that we want project references anyway, I'll just amend the solution generator for that (and check that it actually improves things anyway).

…rences

This solves the client creating testing issue for Google.Cloud.DevTools.ContainerAnalysis.V1, as well as helping for projects such as Spanner.Data.

(Regeneration in the next commit.)
Copy link
Contributor

@amanda-tarafa amanda-tarafa left a comment

Choose a reason for hiding this comment

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

Just a few nits, this looks good. I'm fine with merging.

var options = new Dictionary<string, string>();
foreach (var arg in args.Skip(1))
{
if (!arg.StartsWith("--") || !arg.Contains('='))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, but: should this be &&?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nope - it's an error if either it doesn't start with -- or it doesn't contain =.

Could change it to: if (!(arg.StartsWith("--") && arg.Contains('='))) if that would be clearer.

// Source code
DeleteAll(Directory.EnumerateFiles(layout.SourceDirectory, "*.g.cs", SearchOption.AllDirectories));
// Snippet metadata
DeleteAll(Directory.EnumerateFiles(Path.Combine(layout.SourceDirectory, $"{id}.GeneratedSnippets"), "*.json"));
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: But maybe you can just delete de GeneratedSnippets folder entirely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wondered about that, but felt it was risky in terms of potentially accidentally deleting handwritten code. While it would be weird for GeneratedSnippets to have non-generated code, I wouldn't be hugely surprised if it happened...

/// - api-root: effectively the googleapis directory. We take a copy of this before running generation, unless we're running raw generation.
/// - generator-input: optional, when omitted, run "raw generation"
/// - output: root folder for result; required, must exist
/// - api-path: e.g. google/cloud/functions/v2
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add: "relative to api-root"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

var id = catalog.Apis.FirstOrDefault(api => api.ProtoPath == apiPath)?.Id;
if (id is null)
{
// TODO: Optionally, create a temporary configuration.
Copy link
Contributor

Choose a reason for hiding this comment

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

Possibly using "oboard" command but without saving the result.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Maybe. I'm really not sure at the moment, to be honest. It's possible that in-memory will be a better approach, so that we can make generator-input readonly...

@jskeet jskeet merged commit 13dd39d into googleapis:main Dec 10, 2024
9 checks passed
@jskeet jskeet deleted the generator-input branch December 10, 2024 18:33
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