Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
116 changes: 116 additions & 0 deletions .github/workflows/prepare-release-3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Prepare Release 3.0

# Manual pre-release workflow for creating the prepare PR on branch 3.0.0.
on:
workflow_dispatch:
inputs:
codegen_version:
description: 'Release version, for example 3.0.80. Leave empty to derive from current SNAPSHOT.'
required: false
type: string
next_codegen_snapshot_version:
description: 'Next development version, for example 3.0.81-SNAPSHOT. Leave empty to increment codegen_version.'
required: false
type: string
release_generators:
description: 'Prepare for releasing swagger-codegen-generators with codegen'
required: true
default: 'false'
type: choice
options:
- 'false'
- 'true'
generators_version:
description: 'Generator release version when release_generators=true, for example 1.0.61'
required: false
type: string
previous_generators_version:
description: 'Optional bootstrap generator version for circular dependency validation'
required: false
type: string
permissions:
contents: write
pull-requests: write

jobs:
prepare:
# Resolves release versions, applies file updates, validates build, then opens PR.
runs-on: ubuntu-latest
env:
CODEGEN_VERSION: ${{ inputs.codegen_version }}
NEXT_CODEGEN_SNAPSHOT_VERSION: ${{ inputs.next_codegen_snapshot_version }}
RELEASE_GENERATORS: ${{ inputs.release_generators }}
GENERATORS_VERSION: ${{ inputs.generators_version }}
PREVIOUS_GENERATORS_VERSION: ${{ inputs.previous_generators_version }}
steps:
- name: Checkout swagger-codegen 3.0.0
uses: actions/checkout@v6
with:
ref: 3.0.0
fetch-depth: 0

- uses: actions/create-github-app-token@v3
id: generate-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Set up Java and Maven
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: temurin
cache: maven

- name: Add Central Portal snapshot repository
uses: s4u/maven-settings-action@v4.0.0
with:
repositories: '[{"id":"central-portal-snapshots","name":"Sonatype Central Portal snapshots","url":"https://central.sonatype.com/repository/maven-snapshots/","releases":{"enabled":false},"snapshots":{"enabled":true}}]'
servers: '[{"id":"central","username":"${{ secrets.MAVEN_CENTRAL_USERNAME }}","password":"${{ secrets.MAVEN_CENTRAL_PASSWORD }}"}]'

- name: Validate bootstrap generator snapshot
if: inputs.previous_generators_version != ''
run: |
# Optional safeguard for circular dependency bootstrap inputs.
source CI/release/common.sh
require_release_or_snapshot_version "previous_generators_version" "${PREVIOUS_GENERATORS_VERSION}"
if [[ "${PREVIOUS_GENERATORS_VERSION}" =~ SNAPSHOT$ ]]; then
assert_snapshot_metadata_exists "${GENERATORS_ARTIFACT}" "${PREVIOUS_GENERATORS_VERSION}"
elif ! release_artifact_exists "${GENERATORS_ARTIFACT}" "${PREVIOUS_GENERATORS_VERSION}"; then
fail "previous_generators_version ${PREVIOUS_GENERATORS_VERSION} does not exist in Maven Central"
fi

- name: Prepare release file changes
id: prepare-release
# Performs version bump to release and updates docs/poms/openapi.
run: bash CI/release/prepare-codegen-release.sh

- name: Build release candidate
# Build with the resolved generators version to catch dependency issues early.
run: |
mvn -B -U clean install -Pdocker \
-Dswagger-codegen-generators-version="${BUILD_GENERATORS_VERSION}" \
-DJETTY_TEST_HTTP_PORT=8090 \
-DJETTY_TEST_STOP_PORT=8089

- name: Print generator repo follow-up
if: inputs.release_generators == 'true'
run: |
# Generators repo changes are intentionally handled in a separate repository PR.
echo "::notice::Open a separate PR in swagger-api/swagger-codegen-generators master setting version ${GENERATORS_VERSION} and a usable swagger-codegen-version. This repository workflow does not push cross-repository generator changes."

- name: Create prepare release pull request
# Opens PR with all prepare-release changes targeting 3.0.0.
uses: peter-evans/create-pull-request@v8
with:
token: ${{ steps.generate-token.outputs.token }}
commit-message: prepare release ${{ steps.prepare-release.outputs.codegen_version }}
title: prepare release ${{ steps.prepare-release.outputs.codegen_version }}
branch: prepare-release-${{ steps.prepare-release.outputs.codegen_version }}
base: 3.0.0
body: |
Prepare Swagger Codegen ${{ steps.prepare-release.outputs.codegen_version }}.

release_generators: ${{ inputs.release_generators }}
swagger-codegen-generators version: ${{ steps.prepare-release.outputs.generators_version }}
next codegen snapshot: ${{ steps.prepare-release.outputs.next_codegen_snapshot_version }}
125 changes: 125 additions & 0 deletions CI/release/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env bash

set -euo pipefail

RELEASED_MAVEN_BASE="${RELEASED_MAVEN_BASE:-https://repo1.maven.org/maven2}"
SNAPSHOT_MAVEN_BASE="${SNAPSHOT_MAVEN_BASE:-https://central.sonatype.com/repository/maven-snapshots}"
CODEGEN_GROUP_PATH="io/swagger/codegen/v3"
GENERATORS_ARTIFACT="swagger-codegen-generators"

fail() {
echo "::error::$*"
exit 1
}

require_release_version() {
local name="$1"
local version="$2"

[[ -n "${version}" ]] || fail "${name} is required"
[[ ! "${version}" =~ SNAPSHOT$ ]] || fail "${name} must be a release version, got ${version}"
[[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || fail "${name} must match X.Y.Z, got ${version}"
}

require_release_or_snapshot_version() {
local name="$1"
local version="$2"

[[ -n "${version}" ]] || fail "${name} is required"
[[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-SNAPSHOT)?$ ]] || fail "${name} must match X.Y.Z or X.Y.Z-SNAPSHOT, got ${version}"
}

require_snapshot_version() {
local name="$1"
local version="$2"

[[ -n "${version}" ]] || fail "${name} is required"
[[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-SNAPSHOT$ ]] || fail "${name} must match X.Y.Z-SNAPSHOT, got ${version}"
}

release_from_snapshot_version() {
local version="$1"

[[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-SNAPSHOT$ ]] || fail "Cannot derive release version from non-SNAPSHOT version ${version}"
printf '%s\n' "${version%-SNAPSHOT}"
}

next_snapshot_from_release_version() {
local version="$1"
local major minor patch

require_release_version "version" "${version}"
IFS=. read -r major minor patch <<< "${version}"
printf '%s.%s.%s-SNAPSHOT\n' "${major}" "${minor}" "$((patch + 1))"
}

## Thin Maven helpers for reading project-level values from pom.xml.
maven_project_version() {
mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec
}

## Shared curl wrapper for metadata fetch with retries and hard timeouts.
curl_metadata() {
local url="$1"
curl --fail --silent --show-error --location --retry 5 --retry-delay 2 --connect-timeout 20 --max-time 90 "${url}"
}

## Parse <version> entries from Maven metadata.xml.
versions_from_metadata() {
awk -F'[<>]' '/<version>/{print $3}'
}

## Resolve the newest version matching a regex pattern from metadata.
latest_matching_version() {
local metadata_url="$1"
local pattern="$2"
local version

version="$(curl_metadata "${metadata_url}" | versions_from_metadata | grep -E "${pattern}" | sort -V | tail -n 1 || true)"
[[ -n "${version}" ]] || fail "No version matching '${pattern}' found in ${metadata_url}"
printf '%s\n' "${version}"
}

latest_released_generators_version() {
latest_matching_version "${RELEASED_MAVEN_BASE}/${CODEGEN_GROUP_PATH}/${GENERATORS_ARTIFACT}/maven-metadata.xml" '^1\.[0-9]+\.[0-9]+$'
}

latest_snapshot_generators_version() {
latest_matching_version "${SNAPSHOT_MAVEN_BASE}/${CODEGEN_GROUP_PATH}/${GENERATORS_ARTIFACT}/maven-metadata.xml" '^1\.[0-9]+\.[0-9]+-SNAPSHOT$'
}

## Build canonical artifact URLs and probe existence without downloading payloads.
release_artifact_url() {
local artifact="$1"
local version="$2"

printf '%s/%s/%s/%s/%s-%s.pom\n' "${RELEASED_MAVEN_BASE}" "${CODEGEN_GROUP_PATH}" "${artifact}" "${version}" "${artifact}" "${version}"
}

release_artifact_exists() {
local artifact="$1"
local version="$2"
local url

url="$(release_artifact_url "${artifact}" "${version}")"
curl --fail --silent --show-error --head --location --retry 3 --connect-timeout 20 --max-time 60 "${url}" >/dev/null 2>&1
}

## SNAPSHOT coordinates resolve via maven-metadata.xml, not fixed file names.
snapshot_metadata_url() {
local artifact="$1"
local version="$2"

printf '%s/%s/%s/%s/maven-metadata.xml\n' "${SNAPSHOT_MAVEN_BASE}" "${CODEGEN_GROUP_PATH}" "${artifact}" "${version}"
}

assert_snapshot_metadata_exists() {
local artifact="$1"
local version="$2"
local metadata_url

metadata_url="$(snapshot_metadata_url "${artifact}" "${version}")"
if ! curl_metadata "${metadata_url}" >/dev/null; then
fail "Required SNAPSHOT ${CODEGEN_GROUP_PATH}:${artifact}:${version} cannot be resolved from ${metadata_url}. Sonatype snapshots can expire. Recovery: publish that exact snapshot version, then rerun this workflow."
fi
}
110 changes: 110 additions & 0 deletions CI/release/prepare-codegen-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"

codegen_version="${CODEGEN_VERSION:-}"
next_codegen_snapshot_version="${NEXT_CODEGEN_SNAPSHOT_VERSION:-}"
release_generators="${RELEASE_GENERATORS:-false}"
generators_version="${GENERATORS_VERSION:-}"
previous_generators_version="${PREVIOUS_GENERATORS_VERSION:-}"
build_generators_version=""

# Prepare flow must start from a SNAPSHOT on branch 3.0.0.
current_version="$(maven_project_version)"
[[ "${current_version}" =~ SNAPSHOT$ ]] || fail "Prepare release must start from a SNAPSHOT codegen version, got ${current_version}"

## Resolve target release + next snapshot versions when not provided explicitly.
if [[ -z "${codegen_version}" ]]; then
codegen_version="$(release_from_snapshot_version "${current_version}")"
fi
require_release_version "CODEGEN_VERSION" "${codegen_version}"

if [[ -z "${next_codegen_snapshot_version}" ]]; then
next_codegen_snapshot_version="$(next_snapshot_from_release_version "${codegen_version}")"
fi
require_snapshot_version "NEXT_CODEGEN_SNAPSHOT_VERSION" "${next_codegen_snapshot_version}"

if [[ "${release_generators}" == "true" ]]; then
# Releasing generators: release version is explicit, build can bootstrap from previous snapshot/release.
require_release_version "GENERATORS_VERSION" "${generators_version}"
if [[ -n "${previous_generators_version}" ]]; then
require_release_or_snapshot_version "PREVIOUS_GENERATORS_VERSION" "${previous_generators_version}"
build_generators_version="${previous_generators_version}"
else
build_generators_version="$(latest_snapshot_generators_version)"
fi
else
# Not releasing generators: pin codegen to an already released generators artifact.
if [[ -z "${generators_version}" ]]; then
generators_version="$(latest_released_generators_version)"
fi
require_release_version "resolved generators version" "${generators_version}"
build_generators_version="${generators_version}"
fi

## Validate the exact generators coordinate used for the candidate build.
if [[ "${build_generators_version}" =~ SNAPSHOT$ ]]; then
assert_snapshot_metadata_exists "${GENERATORS_ARTIFACT}" "${build_generators_version}"
else
release_artifact_exists "${GENERATORS_ARTIFACT}" "${build_generators_version}" || fail "Generator release ${build_generators_version} does not exist in Maven Central"
fi

echo "Preparing codegen ${codegen_version} from ${current_version}"
echo "Using swagger-codegen-generators ${generators_version}"
echo "Building release candidate with swagger-codegen-generators ${build_generators_version}"

## Expose resolved values to later workflow steps and PR metadata.
if [[ -n "${GITHUB_ENV:-}" ]]; then
echo "GENERATORS_VERSION=${generators_version}" >> "${GITHUB_ENV}"
echo "BUILD_GENERATORS_VERSION=${build_generators_version}" >> "${GITHUB_ENV}"
echo "CODEGEN_VERSION=${codegen_version}" >> "${GITHUB_ENV}"
echo "NEXT_CODEGEN_SNAPSHOT_VERSION=${next_codegen_snapshot_version}" >> "${GITHUB_ENV}"
fi

if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "generators_version=${generators_version}" >> "${GITHUB_OUTPUT}"
echo "build_generators_version=${build_generators_version}" >> "${GITHUB_OUTPUT}"
echo "codegen_version=${codegen_version}" >> "${GITHUB_OUTPUT}"
echo "next_codegen_snapshot_version=${next_codegen_snapshot_version}" >> "${GITHUB_OUTPUT}"
fi

## Move project from snapshot to release version before file-level content updates.
mvn -B versions:set -DnewVersion="${codegen_version}"
mvn -B versions:commit

# Generate a minimal release-notes draft aligned with current GitHub release style.
mkdir -p docs/release-notes
previous_tag="$(git tag --merged HEAD --list 'v3.*' | sort -V | tail -n 1 || true)"
release_notes_file="docs/release-notes/v${codegen_version}.md"
{
echo "# Swagger Codegen v${codegen_version}"
echo
echo "## What's Changed"
echo
if [[ -n "${previous_tag}" ]]; then
git log --first-parent --pretty=format:'* %s' "${previous_tag}..HEAD"
echo
echo
echo "Full Changelog: ${previous_tag}...v${codegen_version}"
else
git log --first-parent --pretty=format:'* %s' HEAD
echo
echo
echo "Full Changelog: initial...v${codegen_version}"
echo
fi
} > "${release_notes_file}"

update_codegen_release_files_script="CI/release/update-codegen-release-files.py"
[[ -f "${update_codegen_release_files_script}" ]] || fail "Missing ${update_codegen_release_files_script}"

## Keep docs/poms/openapi in sync with the release state.
python3 "${update_codegen_release_files_script}" prepare \
"${codegen_version}" \
"${next_codegen_snapshot_version}" \
"${generators_version}"

echo "Prepared release file updates for ${codegen_version}"
Loading
Loading