From 2c753961976536c7e1c87bb7400cf6a13d534b54 Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 19:28:47 -0600 Subject: [PATCH 1/8] Create AzDO pipeline for auto update JDKs versions --- .devops/update-versions.yml | 197 ++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 .devops/update-versions.yml diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml new file mode 100644 index 0000000..11a6e97 --- /dev/null +++ b/.devops/update-versions.yml @@ -0,0 +1,197 @@ +# update-versions.yml +# +# Azure DevOps equivalent of the "Update JDK Versions" GitHub Actions workflow. +# It refreshes versions.json from the upstream release sources and opens (or +# refreshes) a pull request on github.com/microsoft/openjdk-docker when a newer +# patch version is found. +# +# Authentication: git pushes and the PR REST call are authenticated with the +# GitHub App service connection (Github-Java-Engineering), not a PAT. The +# ms_openjdk_token_src repository resource is checked out with persistCredentials +# to obtain a short-lived OAuth token, which is then scoped to github.com/microsoft/*. + +name: "UpdateJdkVersions-$(Date:yyyyMMdd)$(Rev:.r)" + +trigger: none +pr: none + +schedules: + # Tuesdays and Thursdays at 12:00 AM PT so version-bump PRs land before the + # automated Monday/Wednesday/Friday image builds. Azure DevOps cron is UTC + # only and does not observe DST, so this is pinned to 08:00 UTC (midnight PST). + # always: true runs the pipeline even though the source branch has not changed. + - cron: "0 8 * * 2,4" + displayName: "Tue/Thu JDK version check" + branches: + include: + - main + always: true + +parameters: + # dry-run only prints the detected changes; create-pull-request opens/updates a PR. + - name: mode + displayName: "Run mode" + type: string + default: dry-run + values: + - dry-run + - create-pull-request + +resources: + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + + # Token source for GitHub App auth (Github-Java-Engineering service connection). + - repository: ms_openjdk_token_src + type: github + endpoint: Github-Java-Engineering + name: microsoft/openjdk-adoptium-marketplace-data + +variables: + repoSlug: microsoft/openjdk-docker + targetBranch: main + bumpBranch: auto/update-jdk-versions + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + sdl: + sourceAnalysisPool: + name: JEG-windows-x64-release + os: windows + sourceRepositoriesToScan: + exclude: + - repository: ms_openjdk_token_src + + pool: + name: JEG-pipeline-support + os: linux + + stages: + - stage: update_versions + displayName: Update JDK versions + jobs: + - job: update_versions + displayName: Update versions.json and open PR + steps: + # Obtain a short-lived GitHub App OAuth token via the token-source + # repo and scope it to github.com/microsoft/* for all git operations. + - checkout: ms_openjdk_token_src + persistCredentials: true + path: ms-openjdk-token-src + fetchDepth: 1 + fetchTags: false + displayName: Checkout microsoft repo to obtain GitHub App token + + - bash: | + set -uo pipefail + repo_dir="$(Pipeline.Workspace)/ms-openjdk-token-src" + + # persistCredentials stores the OAuth token as an http extraheader; + # the exact key name is agent-dependent, so discover it. + key=$(git -C "$repo_dir" config --local --name-only --get-regexp '^http\..*\.extraheader$' \ + | grep -i 'github.com' | head -n1) + + if [ -z "${key}" ]; then + echo "ERROR: Could not find a github.com extraheader in ${repo_dir} git config." >&2 + git -C "$repo_dir" config --local --name-only --get-regexp '^http\.' >&2 || true + exit 1 + fi + + extraheader=$(git -C "$repo_dir" config --local --get "${key}") + if [ -z "${extraheader}" ]; then + echo "ERROR: Found key '${key}' but its value was empty." >&2 + exit 1 + fi + + git config --global "http.https://github.com/microsoft/.extraheader" "${extraheader}" + echo "Configured GitHub App credentials for github.com/microsoft/*" + displayName: Configure GitHub App credentials for microsoft/* + + # Clone the target repo fresh (authenticated via the microsoft/* creds). + - bash: | + set -euo pipefail + git clone "https://github.com/$(repoSlug).git" repo + git -C repo config user.name "Java Platform Infrastructure" + git -C repo config user.email "javaplatinfra@microsoft.com" + displayName: Clone openjdk-docker + workingDirectory: $(Pipeline.Workspace) + + # Rewrites versions.json in place with the latest patch versions from the + # upstream sources (Microsoft download page for msopenjdk, Adoptium API + # for temurin). No-op if everything is already current. + - bash: | + set -euo pipefail + ./scripts/update-versions.sh versions.json + displayName: Update versions.json from upstream release sources + workingDirectory: $(Pipeline.Workspace)/repo + + # Determine whether the script changed anything and expose the result + # as an output variable (changed=true/false) for later steps. + - bash: | + set -euo pipefail + if git diff --quiet -- versions.json; then + echo "No JDK version changes detected." + echo "##vso[task.setvariable variable=changed;isOutput=true]false" + else + echo "Detected JDK version changes:" + git --no-pager diff -- versions.json + echo "##vso[task.setvariable variable=changed;isOutput=true]true" + fi + name: diff + displayName: Detect changes + workingDirectory: $(Pipeline.Workspace)/repo + + # Manual dry-run: changes were found but the user asked not to open a + # PR, so just report what would have happened. + - bash: echo "Changes detected but skipping pull request creation (mode=dry-run)." + displayName: Dry run (no pull request created) + condition: and(eq(variables['diff.changed'], 'true'), eq('${{ parameters.mode }}', 'dry-run')) + + # Open (or refresh) the version-bump PR. Skipped in dry-run mode. + - bash: | + set -euo pipefail + + # Reuse the GitHub App credential scoped to microsoft/* for the + # PR REST call (the git push is authenticated via the same creds). + auth_header="$(git config --global --get 'http.https://github.com/microsoft/.extraheader' || true)" + if [ -z "$auth_header" ]; then + echo "##vso[task.logissue type=error]Missing GitHub App credentials; auth step must run first." + exit 1 + fi + + # Reuse the same branch every run: -C resets it to the current commit + # so an existing (stale) branch is force-updated with the latest versions. + git switch -C "$(bumpBranch)" + git add versions.json + git commit -m "Update JDK versions from upstream release sources" + git push -f origin "HEAD:refs/heads/$(bumpBranch)" + + api="https://api.github.com/repos/$(repoSlug)" + repo="$(repoSlug)" + owner="${repo%%/*}" + + # Check if there is already an open PR for this branch. If none is + # open, create one; if one exists, the force-push updated it. + open_count="$(curl -fsSL -H "$auth_header" -H "Accept: application/vnd.github+json" \ + "$api/pulls?state=open&head=$owner:$(bumpBranch)" | jq 'length')" + + if [ "$open_count" = "0" ]; then + body="Automated update of versions.json from upstream release sources: the Microsoft Build of OpenJDK download page (msopenjdk) and the Adoptium API (temurin). Generated by the Update JDK Versions pipeline." + payload="$(jq -n \ + --arg title "Update JDK versions" \ + --arg head "$(bumpBranch)" \ + --arg base "$(targetBranch)" \ + --arg body "$body" \ + '{title: $title, head: $head, base: $base, body: $body}')" + curl -fsSL -X POST -H "$auth_header" -H "Accept: application/vnd.github+json" \ + "$api/pulls" -d "$payload" | jq -r '"Created PR: " + .html_url' + else + echo "An open PR already exists for $(bumpBranch); updated it with a force-push." + fi + displayName: Create or update pull request + workingDirectory: $(Pipeline.Workspace)/repo + condition: and(eq(variables['diff.changed'], 'true'), ne('${{ parameters.mode }}', 'dry-run')) From 96e103d9bce68893b78f70770cba30e5806d0f3d Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 20:00:32 -0600 Subject: [PATCH 2/8] Enhance JDK version change detection with human-readable summary and attach to AzDO run --- .devops/update-versions.yml | 39 ++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml index 11a6e97..2bf8f9e 100644 --- a/.devops/update-versions.yml +++ b/.devops/update-versions.yml @@ -130,17 +130,50 @@ extends: workingDirectory: $(Pipeline.Workspace)/repo # Determine whether the script changed anything and expose the result - # as an output variable (changed=true/false) for later steps. + # as an output variable (changed=true/false) for later steps. When + # versions changed, render a clean "vendor major: old -> new" summary + # (comparing committed vs updated versions.json) instead of a raw diff, + # and attach it to the run summary. - bash: | set -euo pipefail if git diff --quiet -- versions.json; then echo "No JDK version changes detected." echo "##vso[task.setvariable variable=changed;isOutput=true]false" + exit 0 + fi + + # Build a human-readable summary of the changed versions. + summary="$(jq -rn \ + --slurpfile old <(git show HEAD:versions.json) \ + --slurpfile new versions.json ' + ($old[0]) as $o | ($new[0]) as $n + | [ $n | to_entries[] as $vendor | $vendor.value | to_entries[] as $major + | { vendor: $vendor.key, major: $major.key, + new: $major.value, old: ($o[$vendor.key][$major.key]) } ] + | map(select(.new != .old)) + | .[] | " \(.vendor) \(.major): \(.old // "(new)") -> \(.new)" + ')" + + echo "JDK version changes detected:" + if [ -n "$summary" ]; then + echo "$summary" else - echo "Detected JDK version changes:" + # Fallback (e.g. formatting-only change): show the raw diff. git --no-pager diff -- versions.json - echo "##vso[task.setvariable variable=changed;isOutput=true]true" fi + + # Attach the summary to the AzDO run (equivalent of a step summary). + summary_md="$(Agent.TempDirectory)/version-changes.md" + { + echo "### Detected JDK version changes" + echo "" + echo '```' + [ -n "$summary" ] && echo "$summary" || git --no-pager diff -- versions.json + echo '```' + } > "$summary_md" + echo "##vso[task.uploadsummary]$summary_md" + + echo "##vso[task.setvariable variable=changed;isOutput=true]true" name: diff displayName: Detect changes workingDirectory: $(Pipeline.Workspace)/repo From 4a0a58c9285e8c339118f17b3ab9e5d0449a4348 Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 20:20:43 -0600 Subject: [PATCH 3/8] Refactor to use scripts --- .devops/update-versions.yml | 60 ++++------------------------ scripts/open-version-pr.sh | 59 +++++++++++++++++++++++++++ scripts/summarize-version-changes.sh | 43 ++++++++++++++++++++ 3 files changed, 109 insertions(+), 53 deletions(-) create mode 100755 scripts/open-version-pr.sh create mode 100755 scripts/summarize-version-changes.sh diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml index 2bf8f9e..d8ea5e2 100644 --- a/.devops/update-versions.yml +++ b/.devops/update-versions.yml @@ -132,7 +132,6 @@ extends: # Determine whether the script changed anything and expose the result # as an output variable (changed=true/false) for later steps. When # versions changed, render a clean "vendor major: old -> new" summary - # (comparing committed vs updated versions.json) instead of a raw diff, # and attach it to the run summary. - bash: | set -euo pipefail @@ -142,25 +141,9 @@ extends: exit 0 fi - # Build a human-readable summary of the changed versions. - summary="$(jq -rn \ - --slurpfile old <(git show HEAD:versions.json) \ - --slurpfile new versions.json ' - ($old[0]) as $o | ($new[0]) as $n - | [ $n | to_entries[] as $vendor | $vendor.value | to_entries[] as $major - | { vendor: $vendor.key, major: $major.key, - new: $major.value, old: ($o[$vendor.key][$major.key]) } ] - | map(select(.new != .old)) - | .[] | " \(.vendor) \(.major): \(.old // "(new)") -> \(.new)" - ')" - + summary="$(./scripts/summarize-version-changes.sh)" echo "JDK version changes detected:" - if [ -n "$summary" ]; then - echo "$summary" - else - # Fallback (e.g. formatting-only change): show the raw diff. - git --no-pager diff -- versions.json - fi + echo "$summary" # Attach the summary to the AzDO run (equivalent of a step summary). summary_md="$(Agent.TempDirectory)/version-changes.md" @@ -168,7 +151,7 @@ extends: echo "### Detected JDK version changes" echo "" echo '```' - [ -n "$summary" ] && echo "$summary" || git --no-pager diff -- versions.json + echo "$summary" echo '```' } > "$summary_md" echo "##vso[task.uploadsummary]$summary_md" @@ -185,46 +168,17 @@ extends: condition: and(eq(variables['diff.changed'], 'true'), eq('${{ parameters.mode }}', 'dry-run')) # Open (or refresh) the version-bump PR. Skipped in dry-run mode. + # The GitHub App credential scoped to microsoft/* authenticates both + # the git push and the PR REST call made by the script. - bash: | set -euo pipefail - - # Reuse the GitHub App credential scoped to microsoft/* for the - # PR REST call (the git push is authenticated via the same creds). auth_header="$(git config --global --get 'http.https://github.com/microsoft/.extraheader' || true)" if [ -z "$auth_header" ]; then echo "##vso[task.logissue type=error]Missing GitHub App credentials; auth step must run first." exit 1 fi - - # Reuse the same branch every run: -C resets it to the current commit - # so an existing (stale) branch is force-updated with the latest versions. - git switch -C "$(bumpBranch)" - git add versions.json - git commit -m "Update JDK versions from upstream release sources" - git push -f origin "HEAD:refs/heads/$(bumpBranch)" - - api="https://api.github.com/repos/$(repoSlug)" - repo="$(repoSlug)" - owner="${repo%%/*}" - - # Check if there is already an open PR for this branch. If none is - # open, create one; if one exists, the force-push updated it. - open_count="$(curl -fsSL -H "$auth_header" -H "Accept: application/vnd.github+json" \ - "$api/pulls?state=open&head=$owner:$(bumpBranch)" | jq 'length')" - - if [ "$open_count" = "0" ]; then - body="Automated update of versions.json from upstream release sources: the Microsoft Build of OpenJDK download page (msopenjdk) and the Adoptium API (temurin). Generated by the Update JDK Versions pipeline." - payload="$(jq -n \ - --arg title "Update JDK versions" \ - --arg head "$(bumpBranch)" \ - --arg base "$(targetBranch)" \ - --arg body "$body" \ - '{title: $title, head: $head, base: $base, body: $body}')" - curl -fsSL -X POST -H "$auth_header" -H "Accept: application/vnd.github+json" \ - "$api/pulls" -d "$payload" | jq -r '"Created PR: " + .html_url' - else - echo "An open PR already exists for $(bumpBranch); updated it with a force-push." - fi + GH_AUTH_HEADER="$auth_header" \ + ./scripts/open-version-pr.sh "$(repoSlug)" "$(targetBranch)" "$(bumpBranch)" displayName: Create or update pull request workingDirectory: $(Pipeline.Workspace)/repo condition: and(eq(variables['diff.changed'], 'true'), ne('${{ parameters.mode }}', 'dry-run')) diff --git a/scripts/open-version-pr.sh b/scripts/open-version-pr.sh new file mode 100755 index 0000000..072e144 --- /dev/null +++ b/scripts/open-version-pr.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# +# Commits versions.json on a dedicated branch, force-pushes it, and opens a +# pull request on GitHub (or refreshes the existing open one). CI-agnostic: +# authentication is provided via the GH_AUTH_HEADER environment variable, which +# must contain a full HTTP Authorization header for github.com, e.g.: +# +# AUTHORIZATION: basic +# +# The same credential is expected to authorize `git push` to origin (e.g. set as +# an http extraheader in git config by the caller). +# +# Usage: +# GH_AUTH_HEADER="..." scripts/open-version-pr.sh +# +# Example: +# scripts/open-version-pr.sh microsoft/openjdk-docker main auto/update-jdk-versions + +set -euo pipefail + +REPO_SLUG="${1:?repo slug required (e.g. microsoft/openjdk-docker)}" +BASE_BRANCH="${2:?base branch required (e.g. main)}" +BUMP_BRANCH="${3:?bump branch required (e.g. auto/update-jdk-versions)}" + +if [[ -z "${GH_AUTH_HEADER:-}" ]]; then + echo "error: GH_AUTH_HEADER environment variable is required" >&2 + exit 1 +fi + +owner="${REPO_SLUG%%/*}" +api="https://api.github.com/repos/${REPO_SLUG}" + +# Reuse the same branch every run: -C resets it to the current commit so an +# existing (stale) branch is force-updated with the latest versions. +git switch -C "${BUMP_BRANCH}" +git add versions.json +git commit -m "Update JDK versions from upstream release sources" +git push -f origin "HEAD:refs/heads/${BUMP_BRANCH}" + +# Check if there is already an open PR for this branch. If none is open, create +# one; if one exists, the force-push above already updated it. +open_count="$(curl -fsSL -H "${GH_AUTH_HEADER}" -H "Accept: application/vnd.github+json" \ + "${api}/pulls?state=open&head=${owner}:${BUMP_BRANCH}" | jq 'length')" + +if [[ "${open_count}" != "0" ]]; then + echo "An open PR already exists for ${BUMP_BRANCH}; updated it with a force-push." + exit 0 +fi + +body="Automated update of versions.json from upstream release sources: the Microsoft Build of OpenJDK download page (msopenjdk) and the Adoptium API (temurin). Generated by the Update JDK Versions pipeline." +payload="$(jq -n \ + --arg title "Update JDK versions" \ + --arg head "${BUMP_BRANCH}" \ + --arg base "${BASE_BRANCH}" \ + --arg body "${body}" \ + '{title: $title, head: $head, base: $base, body: $body}')" + +curl -fsSL -X POST -H "${GH_AUTH_HEADER}" -H "Accept: application/vnd.github+json" \ + "${api}/pulls" -d "${payload}" | jq -r '"Created PR: " + .html_url' diff --git a/scripts/summarize-version-changes.sh b/scripts/summarize-version-changes.sh new file mode 100755 index 0000000..3565b44 --- /dev/null +++ b/scripts/summarize-version-changes.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Prints a human-readable summary of the JDK version changes between two +# versions.json states, one line per changed entry: +# +# vendor major: old -> new +# +# By default it compares the committed versions.json (git HEAD) against the +# current working-tree versions.json. Both refs can be overridden for testing. +# +# Usage: +# scripts/summarize-version-changes.sh [current_file] [old_git_ref] +# +# Prints nothing (exit 0) when there are no version changes. + +set -euo pipefail + +CURRENT_FILE="${1:-versions.json}" +OLD_REF="${2:-HEAD}" + +if ! command -v jq >/dev/null 2>&1; then + echo "error: jq is required but not installed" >&2 + exit 1 +fi + +# Load both files into jq: `old` = the previous versions.json (from git), `new` +# = the current one. --slurpfile reads each JSON file into a single-element array, +# so $old[0] / $new[0] are the actual objects. +# +# The filter walks every "vendor -> { major: version }" entry in the new file, +# pairs each with the matching version in the old file, keeps only the ones whose +# version actually changed, and prints one line per change: +# " : -> " (old shown as "(new)" for a brand-new major) +jq -rn \ + --slurpfile old <(git show "${OLD_REF}:${CURRENT_FILE}") \ + --slurpfile new "${CURRENT_FILE}" ' + ($old[0]) as $o | ($new[0]) as $n + | [ $n | to_entries[] as $vendor | $vendor.value | to_entries[] as $major + | { vendor: $vendor.key, major: $major.key, + new: $major.value, old: ($o[$vendor.key][$major.key]) } ] + | map(select(.new != .old)) + | .[] | " \(.vendor) \(.major): \(.old // "(new)") -> \(.new)" + ' From 295d4e6026e226a740217f9132f23765cc7c8f34 Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 20:30:04 -0600 Subject: [PATCH 4/8] Source from current branch --- .devops/update-versions.yml | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml index d8ea5e2..062be7a 100644 --- a/.devops/update-versions.yml +++ b/.devops/update-versions.yml @@ -77,8 +77,16 @@ extends: - job: update_versions displayName: Update versions.json and open PR steps: + # Check out this repo (self) so the scripts and versions.json used at + # runtime come from the branch the pipeline runs on - not from a fresh + # clone of main (which may not yet contain new scripts). + - checkout: self + path: openjdk-docker + displayName: Checkout openjdk-docker (self) + # Obtain a short-lived GitHub App OAuth token via the token-source - # repo and scope it to github.com/microsoft/* for all git operations. + # repo and scope it to github.com/microsoft/* for all git operations + # (this is what authorizes the push and PR creation against this repo). - checkout: ms_openjdk_token_src persistCredentials: true path: ms-openjdk-token-src @@ -111,14 +119,14 @@ extends: echo "Configured GitHub App credentials for github.com/microsoft/*" displayName: Configure GitHub App credentials for microsoft/* - # Clone the target repo fresh (authenticated via the microsoft/* creds). + # Configure the git identity used for the version-bump commit. Push + # authorization comes from the microsoft/* credential configured above. - bash: | set -euo pipefail - git clone "https://github.com/$(repoSlug).git" repo - git -C repo config user.name "Java Platform Infrastructure" - git -C repo config user.email "javaplatinfra@microsoft.com" - displayName: Clone openjdk-docker - workingDirectory: $(Pipeline.Workspace) + git config user.name "Java Platform Infrastructure" + git config user.email "javaplatinfra@microsoft.com" + displayName: Configure git identity + workingDirectory: $(Pipeline.Workspace)/openjdk-docker # Rewrites versions.json in place with the latest patch versions from the # upstream sources (Microsoft download page for msopenjdk, Adoptium API @@ -127,7 +135,7 @@ extends: set -euo pipefail ./scripts/update-versions.sh versions.json displayName: Update versions.json from upstream release sources - workingDirectory: $(Pipeline.Workspace)/repo + workingDirectory: $(Pipeline.Workspace)/openjdk-docker # Determine whether the script changed anything and expose the result # as an output variable (changed=true/false) for later steps. When @@ -159,7 +167,7 @@ extends: echo "##vso[task.setvariable variable=changed;isOutput=true]true" name: diff displayName: Detect changes - workingDirectory: $(Pipeline.Workspace)/repo + workingDirectory: $(Pipeline.Workspace)/openjdk-docker # Manual dry-run: changes were found but the user asked not to open a # PR, so just report what would have happened. @@ -180,5 +188,5 @@ extends: GH_AUTH_HEADER="$auth_header" \ ./scripts/open-version-pr.sh "$(repoSlug)" "$(targetBranch)" "$(bumpBranch)" displayName: Create or update pull request - workingDirectory: $(Pipeline.Workspace)/repo + workingDirectory: $(Pipeline.Workspace)/openjdk-docker condition: and(eq(variables['diff.changed'], 'true'), ne('${{ parameters.mode }}', 'dry-run')) From b3d2826cf14dbe89cc4655c81fab83cad20348dd Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 20:44:45 -0600 Subject: [PATCH 5/8] Fix git identity configuration to prevent duplicate authorization headers during version-bump commits --- .devops/update-versions.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml index 062be7a..a2baed5 100644 --- a/.devops/update-versions.yml +++ b/.devops/update-versions.yml @@ -119,12 +119,17 @@ extends: echo "Configured GitHub App credentials for github.com/microsoft/*" displayName: Configure GitHub App credentials for microsoft/* - # Configure the git identity used for the version-bump commit. Push - # authorization comes from the microsoft/* credential configured above. + # Configure the git identity used for the version-bump commit, and + # remove any per-repo auth header left by the self checkout so it does + # not stack with the global microsoft/* GitHub App credential (two + # Authorization headers on one request causes "Duplicate header"). - bash: | - set -euo pipefail + set -uo pipefail git config user.name "Java Platform Infrastructure" git config user.email "javaplatinfra@microsoft.com" + for key in $(git config --local --name-only --get-regexp '^http\..*\.extraheader$' || true); do + git config --local --unset-all "$key" || true + done displayName: Configure git identity workingDirectory: $(Pipeline.Workspace)/openjdk-docker From 76ea0ee922be27cbdf60a4060211213bd2a3e176 Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 20:59:52 -0600 Subject: [PATCH 6/8] Refactor update-versions.yml and open-version-pr.sh for improved version handling and commit clarity --- .devops/update-versions.yml | 2 +- scripts/open-version-pr.sh | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml index a2baed5..116c4da 100644 --- a/.devops/update-versions.yml +++ b/.devops/update-versions.yml @@ -10,7 +10,7 @@ # ms_openjdk_token_src repository resource is checked out with persistCredentials # to obtain a short-lived OAuth token, which is then scoped to github.com/microsoft/*. -name: "UpdateJdkVersions-$(Date:yyyyMMdd)$(Rev:.r)" +name: "$(Date:yyyyMMdd)$(Rev:.r)" trigger: none pr: none diff --git a/scripts/open-version-pr.sh b/scripts/open-version-pr.sh index 072e144..1f9c13e 100755 --- a/scripts/open-version-pr.sh +++ b/scripts/open-version-pr.sh @@ -32,8 +32,23 @@ api="https://api.github.com/repos/${REPO_SLUG}" # Reuse the same branch every run: -C resets it to the current commit so an # existing (stale) branch is force-updated with the latest versions. -git switch -C "${BUMP_BRANCH}" +# Rebuild the bump branch cleanly on top of the base branch so the PR contains +# ONLY the versions.json change - not whatever commits happen to be on the branch +# the pipeline ran from. Capture the regenerated file, reset onto origin/, +# then re-apply it. +tmp="$(mktemp)" +cp versions.json "${tmp}" +git fetch origin "${BASE_BRANCH}" +git checkout -f -B "${BUMP_BRANCH}" "origin/${BASE_BRANCH}" +cp "${tmp}" versions.json +rm -f "${tmp}" + git add versions.json +if git diff --cached --quiet; then + echo "versions.json already up to date on ${BASE_BRANCH}; nothing to do." + exit 0 +fi + git commit -m "Update JDK versions from upstream release sources" git push -f origin "HEAD:refs/heads/${BUMP_BRANCH}" @@ -47,7 +62,7 @@ if [[ "${open_count}" != "0" ]]; then exit 0 fi -body="Automated update of versions.json from upstream release sources: the Microsoft Build of OpenJDK download page (msopenjdk) and the Adoptium API (temurin). Generated by the Update JDK Versions pipeline." +body="Automated update of versions.json from upstream release sources: the Microsoft Build of OpenJDK download page (msopenjdk) and the Adoptium API (temurin). Generated by the OpenJDK Docker - Update JDK Versions pipeline." payload="$(jq -n \ --arg title "Update JDK versions" \ --arg head "${BUMP_BRANCH}" \ From 012a0ecd91b5782c4fee9d164995626d0ec03a32 Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 21:08:52 -0600 Subject: [PATCH 7/8] Update default mode in update-versions.yml to create-pull-request for scheduled runs --- .devops/update-versions.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.devops/update-versions.yml b/.devops/update-versions.yml index 116c4da..ae26b13 100644 --- a/.devops/update-versions.yml +++ b/.devops/update-versions.yml @@ -28,14 +28,16 @@ schedules: always: true parameters: - # dry-run only prints the detected changes; create-pull-request opens/updates a PR. + # create-pull-request opens/updates a PR; dry-run only prints the detected + # changes. Defaults to create-pull-request so scheduled runs (which always use + # the default) open the PR; pick dry-run manually for testing. - name: mode displayName: "Run mode" type: string - default: dry-run + default: create-pull-request values: - - dry-run - create-pull-request + - dry-run resources: repositories: From 80d9a85e05c55a8a4f329108ed68c7f0410c5e66 Mon Sep 17 00:00:00 2001 From: Luigi Montoya Date: Thu, 23 Jul 2026 21:15:12 -0600 Subject: [PATCH 8/8] Remove GH workflow --- .github/workflows/update-jdk-versions.yml | 89 ----------------------- 1 file changed, 89 deletions(-) delete mode 100644 .github/workflows/update-jdk-versions.yml diff --git a/.github/workflows/update-jdk-versions.yml b/.github/workflows/update-jdk-versions.yml deleted file mode 100644 index 896eacc..0000000 --- a/.github/workflows/update-jdk-versions.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Update JDK Versions - -on: - schedule: - # Tuesdays and Thursdays at 12:00 AM PT, so version-bump PRs land before the - # automated Monday/Wednesday/Friday image builds. GitHub Actions cron is UTC - # only and does not observe DST, so this is pinned to 08:00 UTC (midnight PST). - - cron: "0 8 * * 2,4" - workflow_dispatch: - inputs: - mode: - description: "dry-run only prints the detected changes; create-pull-request opens/updates a PR" - type: choice - default: dry-run - options: - - dry-run - - create-pull-request - -permissions: - contents: write - pull-requests: write - -jobs: - update-versions: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - # Rewrites versions.json in place with the latest patch versions pulled from - # the upstream sources (Microsoft download page for msopenjdk, Adoptium API - # for temurin). No-op if everything is already current. - - name: Update versions.json from upstream release sources - run: ./scripts/update-versions.sh versions.json - - # Determine whether the script actually changed anything, and surface the - # diff on the run's summary page so it is visible without opening logs. - - name: Detect changes - id: diff - run: | - if git diff --quiet -- versions.json; then - echo "changed=false" >> "$GITHUB_OUTPUT" - echo "No JDK version changes detected." >> "$GITHUB_STEP_SUMMARY" - else - echo "changed=true" >> "$GITHUB_OUTPUT" - { - echo "### Detected JDK version changes" - echo '```diff' - git --no-pager diff -- versions.json - echo '```' - } >> "$GITHUB_STEP_SUMMARY" - git --no-pager diff -- versions.json - fi - - # Manual dry-run: changes were found but the user asked not to open a PR, so - # just report what would have happened. Only reachable from workflow_dispatch. - - name: Dry run (no pull request created) - if: steps.diff.outputs.changed == 'true' && github.event_name == 'workflow_dispatch' && inputs.mode == 'dry-run' - run: echo "::notice title=Dry run::Changes detected but skipping pull request creation (mode=dry-run)." - - # Open (or refresh) the version-bump PR. Runs on every scheduled execution, - # and on manual runs unless dry-run mode was selected. - - name: Create or update pull request - if: steps.diff.outputs.changed == 'true' && !(github.event_name == 'workflow_dispatch' && inputs.mode == 'dry-run') - env: - GH_TOKEN: ${{ github.token }} - run: | - branch="auto/update-jdk-versions" - - # Commit the updated versions.json as the bot on a dedicated branch. - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - # Reuse the same branch every run: -C resets it to the current commit so - # an existing (stale) branch is force-updated with the latest versions. - git switch -C "$branch" - git add versions.json - git commit -m "Update JDK versions from upstream release sources" - git push -f origin "$branch" - - # Check if there is already an open PR for this branch. If none is open, - # create one; if one already exists, the force-push above updated it, so - # there is nothing more to do. - if [ "$(gh pr list --head "$branch" --state open --json number --jq 'length')" = "0" ]; then - gh pr create \ - --head "$branch" \ - --base "${{ github.event.repository.default_branch }}" \ - --title "Update JDK versions" \ - --body "Automated update of \`versions.json\` from upstream release sources: [Microsoft Build of OpenJDK](https://learn.microsoft.com/en-us/java/openjdk/download) (msopenjdk) and the [Adoptium API](https://api.adoptium.net) (temurin). Generated by the \`Update JDK Versions\` workflow." - fi