Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4aa8470
Add VertexAiClient interface and FakeVertexAiClient for changelog gen…
Sandesh282 Jul 6, 2026
14bee6f
Add GoogleVertexAiClient: production Vertex AI REST implementation
Sandesh282 Jul 6, 2026
8e13e92
Add GenerateChangelogs script and Bazel targets for automated changel…
Sandesh282 Jul 6, 2026
0bf63b8
Fix: remove unused kt_jvm_binary from release/BUILD.bazel load statement
Sandesh282 Jul 6, 2026
9814e60
minor fix
Sandesh282 Jul 12, 2026
a07aac8
Add unit tests for GenerateChangelogs script
Sandesh282 Jul 20, 2026
368ffc3
Fix mutable companion apiBaseUrl; add GoogleVertexAiClientTest
Sandesh282 Jul 20, 2026
c6af637
Add gh pr create arg assertions to GenerateChangelogsTest
Sandesh282 Jul 20, 2026
510cb3e
Fix ktlint blank line violations in GenerateChangelogsTest
Sandesh282 Jul 20, 2026
3161be3
Exempt VertexAiClient and FakeVertexAiClient from test file check
Sandesh282 Jul 20, 2026
e6bac62
Move FakeVertexAiClient to src/javatests; exempt VertexAiClient inter…
Sandesh282 Jul 21, 2026
6ee5859
Fix buildifier blank lines in release BUILD.bazel
Sandesh282 Jul 21, 2026
f583f28
Add FakeVertexAiClientTest to satisfy test file presence check
Sandesh282 Jul 21, 2026
8985c0e
move FakeVertexAiClient to prod source set to fix test file presence …
Sandesh282 Jul 26, 2026
f95b6d3
remove unused kt_jvm_library from javatests BUILD load statement
Sandesh282 Jul 26, 2026
66120f5
apply buildifier reformat to javatests BUILD
Sandesh282 Jul 26, 2026
aa6d3e3
add generate_changelog.yml workflow triggered on version.bzl push
Sandesh282 Jul 26, 2026
1136d88
Address reviewer comments: fix action pins, Closes→Fix/Fixes, cleanup…
Sandesh282 Jul 31, 2026
ab14f99
Add target_version input to workflow_dispatch
Sandesh282 Jul 31, 2026
cdd68bc
Wire target_version input to TARGET_VERSION env var in script step
Sandesh282 Jul 31, 2026
53c0ee2
Add set-up-android-bazel-build-environment and Bazel 6.5.0 version pin
Sandesh282 Aug 2, 2026
6ece400
add git fetch command
Sandesh282 Aug 2, 2026
302b027
Use oppia-android-automation-env (no approval gate) for changelog wor…
Sandesh282 Aug 3, 2026
4b67051
Merge upstream/develop into pr2.2 (pr2.1 landed)
Sandesh282 Aug 8, 2026
bf29ef0
Clarify secrets setup; drop automated-changelog label; delete 0.18.md…
Sandesh282 Aug 8, 2026
422044a
Update Set up Bazel step
Sandesh282 Aug 10, 2026
3eed5c3
Address review: use dedicated changelog SA, fix release branch refs t…
Sandesh282 Aug 13, 2026
de8a9af
Fix ktlint: wrap long gitMergeBase call
Sandesh282 Aug 13, 2026
cfddd58
Fix ktlint: wrap gitMergeBase args
Sandesh282 Aug 13, 2026
8efcd23
Update Bazel setup step
Sandesh282 Aug 18, 2026
6317a13
Address review: trim overinflated docstrings in generate_changelog.yml
Sandesh282 Sep 6, 2026
5dbd4e4
Add 0.18 and 0.18_alpha changelogs from upstream develop
Sandesh282 Sep 6, 2026
1a489e2
Address review
Sandesh282 Sep 7, 2026
d73380f
minor fix
Sandesh282 Sep 7, 2026
8a045e4
Fix KDoc placement: move private constants before KDoc block
Sandesh282 Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions .github/workflows/generate_changelog.yml
Comment thread
adhiamboperes marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Triggered when a version-bump PR is merged to develop (path filter: version.bzl).
# Runs the GenerateChangelogs Kotlin script, which:
# 1. Reads the new MINOR_VERSION from version.bzl and derives the *previous* version
# (e.g. 0.17->0.18 bump generates the changelog for 0.17).
# 2. Collects all PRs merged into develop between the two most recent release branches.
# 3. Parses "Fixes #NNNN" / "Closes #NNNN" references to surface issue titles.
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated
# 4. Calls Vertex AI (Gemini) to produce a 2-3 sentence user-facing changelog summary.
# 5. Writes config/changelogs/<major>.<minor>.md and opens a PR on develop for review.
#
# If the Vertex AI call fails (timeout, quota, API error), the workflow still creates the
# PR with a raw commit list and an <!-- LLM generation failed --> marker. The release
# coordinator then writes the summary manually.
#
# Can also be triggered manually via workflow_dispatch to test or regenerate a changelog.
#
# Note: this workflow authenticates to GCP to call the Vertex AI API. The environment
# (oppia-android-release-env) must NOT have required reviewers configured, since the
# workflow is triggered automatically on every version.bzl push. If required reviewers
# are needed for other workflows in that environment, consider creating a separate
# non-gated environment for changelog generation (e.g. oppia-android-automation-env).
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated

name: Generate Changelog

on:
push:
branches:
- develop
paths:
- 'version.bzl'
workflow_dispatch:
Comment thread
adhiamboperes marked this conversation as resolved.

# Only one changelog generation may run at a time. An in-flight run is never cancelled
# (cancel-in-progress: false) so that a rapid double-push to version.bzl does not leave
# a half-committed changelog branch in an inconsistent state.
concurrency:
group: generate-changelog
Comment thread
adhiamboperes marked this conversation as resolved.
cancel-in-progress: false

jobs:
generate_changelog:
name: Generate Changelog
runs-on: ubuntu-24.04
environment: oppia-android-release-env
permissions:
id-token: write # Required for Workload Identity Federation (Vertex AI auth).
contents: write # Required so the script can push the changelog branch.
pull-requests: write # Required so the script can open the PR via gh.

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated
with:
# Full history is required: the script uses `git log` to collect commits between
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated
# release branches and needs the complete DAG to find merge bases correctly.
fetch-depth: 0

# Configure a bot identity so that the commit the script creates on the changelog
# branch is attributed to github-actions[bot] rather than an arbitrary user.
- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
Comment thread
adhiamboperes marked this conversation as resolved.
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated

# Allow the script to `git push` the changelog branch and `gh pr create` without a
# personal access token. The built-in GITHUB_TOKEN is sufficient here because:
# - contents: write allows pushing a new branch.
# - pull-requests: write allows opening the PR.
- name: Configure git remote credentials
run: |
git remote set-url origin \
https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}

- name: Authenticate to GCP via Workload Identity Federation
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
Comment thread
adhiamboperes marked this conversation as resolved.
service_account: ${{ secrets.GCP_RELEASE_SERVICE_ACCOUNT }}
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated

- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f

# Obtain a short-lived OAuth2 access token for the WIF-impersonated service account.
# GenerateChangelogs passes this as the Bearer token when calling the Vertex AI API.
- name: Get and mask GCP access token
run: |
ACCESS_TOKEN="$(gcloud auth print-access-token)"
echo "::add-mask::$ACCESS_TOKEN"
echo "GCP_ACCESS_TOKEN=$ACCESS_TOKEN" >> "$GITHUB_ENV"

# The GenerateChangelogs script is a pure Kotlin script with no Android resources.
# The Android build environment (NDK, SDK, etc.) is not needed here.
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated
- name: Set up Bazel
uses: bazel-contrib/setup-bazel@8cb04a772ab4c1eb984e9c1b493a182e96c5e425
with:
bazelisk-cache: true
Comment thread
adhiamboperes marked this conversation as resolved.

# Run the GenerateChangelogs script. It will:
# - Determine the version to generate for from version.bzl.
# - Collect commits between the two newest release branches.
# - Call Vertex AI and write config/changelogs/<version>.md.
# - Push the automated/changelog-<version> branch and open a PR.
#
# The GITHUB_TOKEN env var is picked up by the `gh` CLI (used internally by the
# script for `gh pr create`). GCP_PROJECT, GCP_LOCATION, and VERTEX_MODEL are
# non-sensitive configuration values stored as repository variables
# (Settings -> Variables -> Actions).
Comment thread
adhiamboperes marked this conversation as resolved.
Outdated
- name: Generate changelog and open PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bazel run //scripts:generate_changelogs -- \
"$(pwd)" \
"${{ vars.GCP_PROJECT }}" \
"${{ vars.GCP_LOCATION }}" \
"${{ vars.VERTEX_MODEL }}" \
"$GCP_ACCESS_TOKEN"
8 changes: 8 additions & 0 deletions scripts/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,14 @@ kt_jvm_binary(
)

# Release automation binaries — invoked by GitHub Actions release workflows.
kt_jvm_binary(
name = "generate_changelogs",
main_class = "org.oppia.android.scripts.release.GenerateChangelogsKt",
runtime_deps = [
"//scripts/src/java/org/oppia/android/scripts/release:generate_changelogs_lib",
],
)

kt_jvm_binary(
name = "sign_release_binary",
main_class = "org.oppia.android.scripts.release.SignReleaseBinaryKt",
Expand Down
4 changes: 4 additions & 0 deletions scripts/assets/test_file_exemptions.textproto
Original file line number Diff line number Diff line change
Expand Up @@ -4939,3 +4939,7 @@ test_file_exemption {
exempted_file_path: "scripts/src/java/org/oppia/android/scripts/release/CloudSigner.kt"
test_file_not_required: true
}
test_file_exemption {
exempted_file_path: "scripts/src/java/org/oppia/android/scripts/release/VertexAiClient.kt"
test_file_not_required: true
}
37 changes: 37 additions & 0 deletions scripts/src/java/org/oppia/android/scripts/release/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ kt_jvm_library(
deps = [":cloud_signer"],
)

kt_jvm_library(
name = "fake_vertex_ai_client",
testonly = True,
srcs = ["FakeVertexAiClient.kt"],
visibility = ["//scripts:oppia_script_test_visibility"],
deps = [":vertex_ai_client"],
)

kt_jvm_library(
name = "version_inversion_checker",
srcs = ["VersionInversionChecker.kt"],
Expand Down Expand Up @@ -106,6 +114,35 @@ kt_jvm_library(
],
)

kt_jvm_library(
name = "vertex_ai_client",
srcs = ["VertexAiClient.kt"],
visibility = ["//scripts:oppia_script_library_visibility"],
)

kt_jvm_library(
name = "google_vertex_ai_client",
srcs = ["GoogleVertexAiClient.kt"],
visibility = ["//scripts:oppia_script_library_visibility"],
deps = [
":vertex_ai_client",
"//third_party:com_squareup_okhttp3_okhttp",
"//third_party:moshi",
],
)

kt_jvm_library(
name = "generate_changelogs_lib",
srcs = ["GenerateChangelogs.kt"],
visibility = ["//scripts:oppia_script_binary_visibility"],
deps = [
":google_vertex_ai_client",
":vertex_ai_client",
"//scripts/src/java/org/oppia/android/scripts/common:command_executor",
"//scripts/src/java/org/oppia/android/scripts/common:script_background_coroutine_dispatcher",
],
)

kt_jvm_library(
name = "sign_release_binary_lib",
srcs = ["SignReleaseBinary.kt"],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.oppia.android.scripts.release

/**
* In-memory fake implementation of [VertexAiClient] for use in unit tests.
*
* By default, [generateText] returns [defaultResponse]. Tests can simulate an LLM failure by
* setting [shouldFail] to `true`, which causes the next call to throw an [IllegalStateException].
* The flag resets to `false` after each thrown exception so that subsequent calls succeed again
* (unless re-set). All prompts received are recorded in [receivedPrompts] for assertion.
*
* @property defaultResponse the text returned by [generateText] when [shouldFail] is false
*/
class FakeVertexAiClient(
private val defaultResponse: String = "Fake generated changelog summary."
) : VertexAiClient {

/** Whether the next call to [generateText] should throw to simulate an LLM failure. */
var shouldFail = false

/** All prompts passed to [generateText], in call order. */
val receivedPrompts = mutableListOf<String>()

override fun generateText(prompt: String): String {
receivedPrompts += prompt
if (shouldFail) {
shouldFail = false
error("FakeVertexAiClient: simulated Vertex AI failure")
}
return defaultResponse
}

/** Resets all recorded state and configuration to defaults. */
fun reset() {
shouldFail = false
receivedPrompts.clear()
}
}
Loading
Loading