Skip to content

Commit e82a680

Browse files
authored
feat(internal/librarian/dart): add generate (#3585)
Add generate functionality for Dart. For #3581
1 parent b931aca commit e82a680

File tree

7 files changed

+618
-12
lines changed

7 files changed

+618
-12
lines changed

.github/workflows/dart.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
name: Dart
15+
on: [push, pull_request, merge_group]
16+
permissions:
17+
contents: read
18+
jobs:
19+
test:
20+
runs-on: ubuntu-24.04
21+
steps:
22+
- uses: actions/checkout@v5
23+
- uses: actions/setup-go@v6
24+
with:
25+
go-version-file: "go.mod"
26+
- uses: ./.github/actions/install-protoc
27+
- name: Display Go version
28+
run: go version
29+
- name: Install Dart SDK
30+
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
31+
with:
32+
sdk: 3.6.0
33+
- name: Display Dart version
34+
run: dart --version
35+
- name: Verify git is available
36+
run: git --version
37+
- name: Run internal/sidekick/dart tests
38+
run: go test -race ./internal/sidekick/dart
39+
- name: Run internal/librarian/dart tests
40+
run: go test -race ./internal/librarian/dart

internal/config/config.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,19 @@ type Default struct {
121121
// ReleaseLevel is either "stable" or "preview".
122122
ReleaseLevel string `yaml:"release_level,omitempty"`
123123

124-
// Rust contains Rust-specific default configuration.
125-
Rust *RustDefault `yaml:"rust,omitempty"`
126-
127124
// TagFormat is the template for git tags, such as "{name}/v{version}".
128125
TagFormat string `yaml:"tag_format,omitempty"`
129126

130127
// Transport is the transport protocol, such as "grpc+rest" or "grpc".
131128
Transport string `yaml:"transport,omitempty"`
129+
130+
// Language-specific fields are below.
131+
132+
// Dart contains Dart-specific default configuration.
133+
Dart *DartPackage `yaml:"dart,omitempty"`
134+
135+
// Rust contains Rust-specific default configuration.
136+
Rust *RustDefault `yaml:"rust,omitempty"`
132137
}
133138

134139
// Library represents a library configuration.
@@ -153,29 +158,20 @@ type Library struct {
153158
// DescriptionOverride overrides the library description.
154159
DescriptionOverride string `yaml:"description_override,omitempty"`
155160

156-
// Go contains Go-specific library configuration.
157-
Go *GoModule `yaml:"go,omitempty"`
158-
159161
// Keep lists files and directories to preserve during regeneration.
160162
Keep []string `yaml:"keep,omitempty"`
161163

162164
// Output is the directory where code is written. This overrides
163165
// Default.Output.
164166
Output string `yaml:"output,omitempty"`
165167

166-
// Python contains Python-specific library configuration.
167-
Python *PythonPackage `yaml:"python,omitempty"`
168-
169168
// ReleaseLevel is the release level, such as "stable" or "preview". This
170169
// overrides Default.ReleaseLevel.
171170
ReleaseLevel string `yaml:"release_level,omitempty"`
172171

173172
// Roots specifies the source roots to use for generation. Defaults to googleapis.
174173
Roots []string `yaml:"roots,omitempty"`
175174

176-
// Rust contains Rust-specific library configuration.
177-
Rust *RustCrate `yaml:"rust,omitempty"`
178-
179175
// SkipGenerate disables code generation for this library.
180176
SkipGenerate bool `yaml:"skip_generate,omitempty"`
181177

@@ -198,6 +194,20 @@ type Library struct {
198194
// configuration (e.g., rust.modules) instead of generating a complete crate
199195
// from channels.
200196
Veneer bool `yaml:"veneer,omitempty"`
197+
198+
// Language-specific fields are below.
199+
200+
// Dart contains Dart-specific library configuration.
201+
Dart *DartPackage `yaml:"dart,omitempty"`
202+
203+
// Go contains Go-specific library configuration.
204+
Go *GoModule `yaml:"go,omitempty"`
205+
206+
// Python contains Python-specific library configuration.
207+
Python *PythonPackage `yaml:"python,omitempty"`
208+
209+
// Rust contains Rust-specific library configuration.
210+
Rust *RustCrate `yaml:"rust,omitempty"`
201211
}
202212

203213
// Channel describes a Channel to include in a library.

internal/config/language.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,52 @@ type PythonPackage struct {
271271
// Example: {"google/cloud/secrets/v1beta": ["python-gapic-name=secretmanager"]}
272272
OptArgsByChannel map[string][]string `yaml:"opt_args_by_channel,omitempty"`
273273
}
274+
275+
// DartPackage contains Dart-specific library configuration.
276+
type DartPackage struct {
277+
// APIKeysEnvironmentVariables is a comma-separated list of environment variable names
278+
// that can contain API keys (e.g., "GOOGLE_API_KEY,GEMINI_API_KEY").
279+
APIKeysEnvironmentVariables string `yaml:"api_keys_environment_variables,omitempty"`
280+
281+
// Dependencies is a comma-separated list of dependencies.
282+
Dependencies string `yaml:"dependencies,omitempty"`
283+
284+
// DevDependencies is a comma-separated list of development dependencies.
285+
DevDependencies string `yaml:"dev_dependencies,omitempty"`
286+
287+
// ExtraImports is additional imports to include in the generated library.
288+
ExtraImports string `yaml:"extra_imports,omitempty"`
289+
290+
// IssueTrackerURL is the URL for the issue tracker.
291+
IssueTrackerURL string `yaml:"issue_tracker_url,omitempty"`
292+
293+
// LibraryPathOverride overrides the library path.
294+
LibraryPathOverride string `yaml:"library_path_override,omitempty"`
295+
296+
// NotForPublication indicates whether this package should not be published.
297+
NotForPublication string `yaml:"not_for_publication,omitempty"`
298+
299+
// Packages maps Dart package names to version constraints.
300+
// Keys are in the format "package:googleapis_auth" and values are version strings like "^2.0.0".
301+
Packages map[string]string `yaml:"packages,omitempty"`
302+
303+
// PartFile is the path to a part file to include in the generated library.
304+
PartFile string `yaml:"part_file,omitempty"`
305+
306+
// Prefixes maps protobuf package names to Dart import prefixes.
307+
// Keys are in the format "prefix:google.protobuf" and values are the prefix names.
308+
Prefixes map[string]string `yaml:"prefixes,omitempty"`
309+
310+
// Protos maps protobuf package names to Dart import paths.
311+
// Keys are in the format "proto:google.api" and values are import paths like "package:google_cloud_api/api.dart".
312+
Protos map[string]string `yaml:"protos,omitempty"`
313+
314+
// ReadmeAfterTitleText is text to insert in the README after the title.
315+
ReadmeAfterTitleText string `yaml:"readme_after_title_text,omitempty"`
316+
317+
// ReadmeQuickstartText is text to use for the quickstart section in the README.
318+
ReadmeQuickstartText string `yaml:"readme_quickstart_text,omitempty"`
319+
320+
// RepositoryURL is the URL to the repository for this package.
321+
RepositoryURL string `yaml:"repository_url,omitempty"`
322+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package dart provides functionality for generating and releasing Dart client
16+
// libraries.
17+
package dart
18+
19+
import (
20+
"context"
21+
22+
"github.com/googleapis/librarian/internal/command"
23+
"github.com/googleapis/librarian/internal/config"
24+
"github.com/googleapis/librarian/internal/serviceconfig"
25+
sidekickconfig "github.com/googleapis/librarian/internal/sidekick/config"
26+
sidekickdart "github.com/googleapis/librarian/internal/sidekick/dart"
27+
"github.com/googleapis/librarian/internal/sidekick/parser"
28+
)
29+
30+
// Generate generates a Dart client library.
31+
func Generate(ctx context.Context, library *config.Library, googleapisDir string) error {
32+
sidekickConfig, err := toSidekickConfig(library, library.Channels[0], googleapisDir)
33+
if err != nil {
34+
return err
35+
}
36+
model, err := parser.CreateModel(sidekickConfig)
37+
if err != nil {
38+
return err
39+
}
40+
if err := sidekickdart.Generate(ctx, model, library.Output, sidekickConfig); err != nil {
41+
return err
42+
}
43+
return nil
44+
}
45+
46+
// Format formats a generated Dart library.
47+
func Format(ctx context.Context, library *config.Library) error {
48+
if err := command.Run(ctx, "dart", "format", library.Output); err != nil {
49+
return err
50+
}
51+
return nil
52+
}
53+
54+
func toSidekickConfig(library *config.Library, ch *config.Channel, googleapisDir string) (*sidekickconfig.Config, error) {
55+
source := map[string]string{
56+
"googleapis-root": googleapisDir,
57+
}
58+
59+
channel, err := serviceconfig.Find(googleapisDir, ch.Path)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
sidekickCfg := &sidekickconfig.Config{
65+
General: sidekickconfig.GeneralConfig{
66+
Language: "dart",
67+
SpecificationFormat: "protobuf",
68+
ServiceConfig: channel.ServiceConfig,
69+
SpecificationSource: ch.Path,
70+
},
71+
Source: source,
72+
Codec: buildCodec(library),
73+
}
74+
return sidekickCfg, nil
75+
}
76+
77+
func buildCodec(library *config.Library) map[string]string {
78+
codec := make(map[string]string)
79+
if library.CopyrightYear != "" {
80+
codec["copyright-year"] = library.CopyrightYear
81+
}
82+
if library.Version != "" {
83+
codec["version"] = library.Version
84+
}
85+
if library.Dart == nil {
86+
return codec
87+
}
88+
89+
dart := library.Dart
90+
if dart.APIKeysEnvironmentVariables != "" {
91+
codec["api-keys-environment-variables"] = dart.APIKeysEnvironmentVariables
92+
}
93+
if dart.Dependencies != "" {
94+
codec["dependencies"] = dart.Dependencies
95+
}
96+
if dart.DevDependencies != "" {
97+
codec["dev-dependencies"] = dart.DevDependencies
98+
}
99+
if dart.ExtraImports != "" {
100+
codec["extra-imports"] = dart.ExtraImports
101+
}
102+
if dart.IssueTrackerURL != "" {
103+
codec["issue-tracker-url"] = dart.IssueTrackerURL
104+
}
105+
if dart.LibraryPathOverride != "" {
106+
codec["library-path-override"] = dart.LibraryPathOverride
107+
}
108+
if dart.NotForPublication != "" {
109+
codec["not-for-publication"] = dart.NotForPublication
110+
}
111+
if dart.PartFile != "" {
112+
codec["part-file"] = dart.PartFile
113+
}
114+
if dart.ReadmeAfterTitleText != "" {
115+
codec["readme-after-title-text"] = dart.ReadmeAfterTitleText
116+
}
117+
if dart.ReadmeQuickstartText != "" {
118+
codec["readme-quickstart-text"] = dart.ReadmeQuickstartText
119+
}
120+
if dart.RepositoryURL != "" {
121+
codec["repository-url"] = dart.RepositoryURL
122+
}
123+
for key, value := range dart.Packages {
124+
codec["package:"+key] = value
125+
}
126+
for key, value := range dart.Prefixes {
127+
codec["prefix:"+key] = value
128+
}
129+
for key, value := range dart.Protos {
130+
codec["proto:"+key] = value
131+
}
132+
return codec
133+
}

0 commit comments

Comments
 (0)