Skip to content

Commit 93ef9f5

Browse files
authored
Merge branch 'main' into copybara_803481433
2 parents db80c43 + 2f0f298 commit 93ef9f5

File tree

12,839 files changed

+3269306
-225336
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

12,839 files changed

+3269306
-225336
lines changed

.github/release-please.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ branches:
3333
manifest: true
3434
handleGHRelease: true
3535
branch: 1.58.x
36+
- primaryBranch: main
37+
releaseType: java-yoshi
38+
manifest: true
39+
handleGHRelease: true
40+
branch: protobuf-4.x-rc
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
google-cloud-asset:1.2.3:1.2.3

.github/release/partial_release.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
class VersionType(Enum):
2525
MAJOR = (1,)
2626
MINOR = (2,)
27-
PATCH = 3
27+
PATCH = (3,)
28+
SNAPSHOT = (4,)
2829

2930

3031
@click.group(invoke_without_command=False)
@@ -34,6 +35,27 @@ def main(ctx):
3435
pass
3536

3637

38+
@main.command()
39+
@click.option(
40+
"--artifact-ids",
41+
required=True,
42+
type=str,
43+
help="""
44+
Artifact IDs whose version needs to update, separated by comma.
45+
""",
46+
)
47+
@click.option(
48+
"--versions",
49+
required=False,
50+
default="./versions.txt",
51+
type=str,
52+
help="""
53+
The path to the versions.txt.
54+
""",
55+
)
56+
def bump_snapshot_version(artifact_ids: str, versions: str) -> None:
57+
bump_version(artifact_ids, "snapshot", versions)
58+
3759
@main.command()
3860
@click.option(
3961
"--artifact-ids",
@@ -49,7 +71,7 @@ def main(ctx):
4971
default="patch",
5072
type=str,
5173
help="""
52-
The type of version bump, one of major, minor or patch.
74+
The type of version bump, one of major, minor, patch.
5375
""",
5476
)
5577
@click.option(
@@ -62,6 +84,9 @@ def main(ctx):
6284
""",
6385
)
6486
def bump_released_version(artifact_ids: str, version_type: str, versions: str) -> None:
87+
bump_version(artifact_ids, version_type, versions)
88+
89+
def bump_version(artifact_ids: str, version_type: str, versions: str) -> None:
6590
target_artifact_ids = set(artifact_ids.split(","))
6691
version_enum = _parse_type_or_raise(version_type)
6792
newlines = []
@@ -95,6 +120,12 @@ def bump_released_version(artifact_ids: str, version_type: str, versions: str) -
95120
minor += 1
96121
case VersionType.PATCH:
97122
patch += 1
123+
case VersionType.SNAPSHOT:
124+
# Keep the released version as is.
125+
newlines.append(
126+
f"{artifact_id}:{major}.{minor}.{patch}:{major}.{minor + 1}.0-SNAPSHOT"
127+
)
128+
continue
98129
newlines.append(
99130
f"{artifact_id}:{major}.{minor}.{patch}:{major}.{minor}.{patch}"
100131
)

.github/release/release_unit_tests.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import shutil
55
import tempfile
66
import unittest
7-
from partial_release import bump_released_version
7+
from partial_release import bump_released_version, bump_snapshot_version
88

99
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
1010
GOLDEN = os.path.join(SCRIPT_DIR, "testdata")
@@ -45,6 +45,22 @@ def test_bump_multiple_versions_success(self):
4545
actual = f.read()
4646
self.assertEqual(expected, actual)
4747

48+
def test_bump_snapshot_version_success(self):
49+
golden = f"{GOLDEN}/snapshot/versions-snapshot-golden.txt"
50+
with copied_fixtures_dir(f"{FIXTURES}/snapshot"):
51+
runner.invoke(
52+
bump_snapshot_version,
53+
[
54+
"--artifact-ids=google-cloud-asset",
55+
"--versions=versions-snapshot.txt",
56+
],
57+
)
58+
with open(golden) as g:
59+
expected = g.read()
60+
with open("./versions-snapshot.txt") as f:
61+
actual = f.read()
62+
self.assertEqual(expected, actual)
63+
4864

4965
@contextlib.contextmanager
5066
def change_dir_to(path: str) -> str:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
google-cloud-asset:1.2.3:1.3.0-SNAPSHOT

.github/scripts/go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module github.com/googleapis/google-cloud-java/scripts
2+
3+
go 1.24.4
4+
5+
require (
6+
github.com/google/go-github/v62 v62.0.0
7+
golang.org/x/oauth2 v0.31.0
8+
)
9+
10+
require github.com/google/go-querystring v1.1.0 // indirect

.github/scripts/go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
2+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
3+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
4+
github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4=
5+
github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4=
6+
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
7+
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
8+
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
9+
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
10+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright 2025 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+
// http://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+
// release_manager_merge_bot is a script that automatically retries and merges a pull request.
16+
//
17+
// This script is designed to be run manually for a specific pull request. It will:
18+
// 1. Check the CI/CD status of the pull request.
19+
// 2. If the status is "failure", it will add Kokoro labels to retry the tests. It retries once.
20+
// 3. If the status is "success", it will squash and merge the pull request.
21+
// 4. If the status is "pending", it will wait and check again.
22+
//
23+
// Prerequisites:
24+
// - Go must be installed (https://golang.org/doc/install).
25+
// - A GitHub personal access token with repo scope must be set in the GITHUB_TOKEN environment variable.
26+
//
27+
// Example Usage:
28+
//
29+
// export GITHUB_TOKEN="<your GitHub token>"
30+
// cd .github/scripts
31+
// go run ./release_manager_merge_bot.go <PR URL>
32+
33+
package main
34+
35+
import (
36+
"context"
37+
"fmt"
38+
"log"
39+
"net/url"
40+
"os"
41+
"strconv"
42+
"strings"
43+
"time"
44+
45+
"github.com/google/go-github/v62/github"
46+
"golang.org/x/oauth2"
47+
)
48+
49+
// --- Configuration ---
50+
// The labels to add when a test fails.
51+
var labelsToAdd = []string{"kokoro:force-run", "kokoro:run"}
52+
53+
// --- End of Configuration ---
54+
55+
// parseURL parses a GitHub pull request URL and returns the owner, repository, and PR number.
56+
func parseURL(prURL string) (string, string, int, error) {
57+
parsedURL, err := url.Parse(prURL)
58+
if err != nil {
59+
return "", "", 0, fmt.Errorf("failed to parse URL: %w", err)
60+
}
61+
62+
pathParts := strings.Split(strings.Trim(parsedURL.Path, "/"), "/")
63+
if len(pathParts) < 4 || pathParts[2] != "pull" {
64+
return "", "", 0, fmt.Errorf("invalid GitHub pull request URL format")
65+
}
66+
67+
owner := pathParts[0]
68+
repo := pathParts[1]
69+
prNumber, err := strconv.Atoi(pathParts[3])
70+
if err != nil {
71+
return "", "", 0, fmt.Errorf("failed to parse PR number: %w", err)
72+
}
73+
74+
return owner, repo, prNumber, nil
75+
}
76+
77+
// getMissingLabels checks for required labels on a PR and returns any that are missing.
78+
func getMissingLabels(ctx context.Context, client *github.Client, owner, repo string, prNumber int) ([]string, error) {
79+
currentLabels, _, err := client.Issues.ListLabelsByIssue(ctx, owner, repo, prNumber, nil)
80+
if err != nil {
81+
return nil, fmt.Errorf("failed to get PR labels: %w", err)
82+
}
83+
84+
labelSet := make(map[string]bool)
85+
for _, label := range currentLabels {
86+
labelSet[*label.Name] = true
87+
}
88+
89+
var missingLabels []string
90+
for _, requiredLabel := range labelsToAdd {
91+
if !labelSet[requiredLabel] {
92+
missingLabels = append(missingLabels, requiredLabel)
93+
}
94+
}
95+
return missingLabels, nil
96+
}
97+
98+
func main() {
99+
log.Println("Starting the release manager merge bot.")
100+
101+
if len(os.Args) < 2 {
102+
log.Fatal("Error: Pull request URL is required. Example: go run ./release_manager_merge_bot.go <PR URL>")
103+
}
104+
prURL := os.Args[1]
105+
106+
githubToken := os.Getenv("GITHUB_TOKEN")
107+
if githubToken == "" {
108+
log.Fatal("Error: GITHUB_TOKEN environment variable is not set.")
109+
}
110+
111+
owner, repo, prNumber, err := parseURL(prURL)
112+
if err != nil {
113+
log.Fatalf("Error parsing URL: %v", err)
114+
}
115+
116+
ctx := context.Background()
117+
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
118+
tc := oauth2.NewClient(ctx, ts)
119+
client := github.NewClient(tc)
120+
121+
// --- Initial Label Check ---
122+
retryCount := 0
123+
log.Printf("Performing initial label check for PR #%d...", prNumber)
124+
missingLabels, err := getMissingLabels(ctx, client, owner, repo, prNumber)
125+
if err != nil {
126+
log.Printf("Warning: could not perform initial label check: %v", err)
127+
} else {
128+
if len(missingLabels) > 0 {
129+
log.Println("Required Kokoro labels are missing. Adding them now...")
130+
_, _, err := client.Issues.AddLabelsToIssue(ctx, owner, repo, prNumber, missingLabels)
131+
if err != nil {
132+
log.Printf("Warning: failed to add labels: %v", err)
133+
}
134+
retryCount++
135+
} else {
136+
log.Println("Required Kokoro labels are already present.")
137+
}
138+
}
139+
// --- End of Initial Label Check ---
140+
141+
for {
142+
log.Printf("Checking status of PR #%d in %s/%s...", prNumber, owner, repo)
143+
144+
// Declare variables at the top of the loop to avoid 'goto jumps over declaration' errors.
145+
var (
146+
pr *github.PullRequest
147+
status *github.CombinedStatus
148+
state string
149+
err error
150+
)
151+
152+
pr, _, err = client.PullRequests.Get(ctx, owner, repo, prNumber)
153+
if err != nil {
154+
log.Printf("An error occurred while getting PR info: %v", err)
155+
goto wait
156+
}
157+
158+
status, _, err = client.Repositories.GetCombinedStatus(ctx, owner, repo, *pr.Head.SHA, nil)
159+
if err != nil {
160+
log.Printf("An error occurred while getting commit status: %v", err)
161+
goto wait
162+
}
163+
164+
state = *status.State
165+
log.Printf("Overall status: %s", state)
166+
167+
switch state {
168+
case "failure":
169+
if retryCount >= 2 {
170+
log.Fatal("The PR has failed twice after applying the Kokoro labels. Failing the script.")
171+
}
172+
log.Println("Some checks have failed. Retrying the tests...")
173+
_, _, err := client.Issues.AddLabelsToIssue(ctx, owner, repo, prNumber, labelsToAdd)
174+
if err != nil {
175+
log.Printf("An error occurred while adding labels: %v", err)
176+
}
177+
retryCount++
178+
case "success":
179+
log.Println("All checks have passed. Merging the pull request...")
180+
commitMessage := fmt.Sprintf("Merge pull request #%d from %s/%s", prNumber, owner, repo)
181+
mergeResult, _, err := client.PullRequests.Merge(ctx, owner, repo, prNumber, commitMessage, &github.PullRequestOptions{
182+
MergeMethod: "squash",
183+
})
184+
if err != nil {
185+
log.Fatalf("Failed to merge PR: %v", err)
186+
}
187+
log.Printf("Successfully squashed and merged PR #%d: %s", prNumber, *mergeResult.Message)
188+
return // Exit the program on success
189+
case "pending":
190+
log.Println("Some checks are still pending. Waiting for them to complete.")
191+
default:
192+
log.Printf("Unknown state: %s. No action taken.", state)
193+
}
194+
195+
wait:
196+
log.Println("Waiting for 1 minute before retrying...")
197+
time.Sleep(60 * time.Second)
198+
}
199+
}

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
strategy:
2626
fail-fast: false
2727
matrix:
28-
java: [8, 11, 17, 21, 24]
28+
java: [8, 11, 17, 21, 25]
2929
steps:
3030
- name: Get current week within the year
3131
id: date
@@ -127,7 +127,7 @@ jobs:
127127
gcr.io/cloud-devrel-public-resources/java-library-generation:"${library_generation_image_tag}" \
128128
/src/library_generation/cli/entry_point.py validate-generation-config
129129
env:
130-
library_generation_image_tag: 2.62.0
130+
library_generation_image_tag: 2.62.3
131131
workspace_name: /workspace
132132

133133
# TODO: Uncomment the needed Github Actions

.github/workflows/generated_files_sync.yaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@
1414
# GitHub action job to test core java library features on
1515
# downstream client libraries before they are released.
1616
on:
17-
push:
18-
branches:
19-
- main
2017
pull_request:
2118
name: generation diff
2219
env:
23-
library_generation_image_tag: 2.62.0
20+
library_generation_image_tag: 2.62.3
2421
jobs:
2522
root-pom:
2623
# root pom.xml does not have diff from generated one

0 commit comments

Comments
 (0)