From 48ec3aa8c23ae4036387ffc0b75e5941c799d22e Mon Sep 17 00:00:00 2001 From: Stefan Wang <1fannnw@gmail.com> Date: Mon, 23 Sep 2024 12:52:28 -0700 Subject: [PATCH] impl and doc on deprecation --- .github/workflows/README.md | 51 +++++++++++++ .github/workflows/deprecation.yml | 114 ++++++++++++++++++++++++++++++ build.gradle | 17 +++++ gradle/java-publication.gradle | 40 +++++++++++ 4 files changed, 222 insertions(+) create mode 100644 .github/workflows/README.md create mode 100644 .github/workflows/deprecation.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 000000000..b40e4b5cc --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,51 @@ +# Coral GitHub Actions Workflows + +This directory contains the GitHub Actions workflows for the Coral project. These workflows automate various processes including continuous integration, release management, and version deprecation. + +## Workflows + +### 1. [Continuous Integration (CI)](./ci.yml) + +The CI workflow is responsible for building, testing, and releasing the Coral project. + +**Trigger:** +- Push to `master` branch +- Pull requests to any branch + +**Key Steps:** +1. Check out code +2. Set up Java +3. Perform build +4. Run tests +5. Perform release (only on push to `master`) + +**Usage:** +This workflow runs automatically on push and pull request events. No manual intervention is required for normal operation. + +### 2. [Version Deprecation](./deprecation.yml) + +The Version Deprecation workflow handles the deprecation of older Coral versions, either manually or automatically based on configured criteria. + +**Trigger:** +- Manual workflow dispatch + +**Key Steps:** +1. Check if the user triggering the workflow has maintainer or admin permissions +2. Check out code +3. Set up Java +4. Manually deprecate specified versions +5. (TODO) Auto-deprecate old versions based on criteria + +**Usage:** +To use this workflow: + +1. Go to the "Actions" tab in the Coral GitHub repository +2. Select the "Version Deprecation" workflow +3. Click "Run workflow" +4. Fill in the inputs: + - For manual deprecation: Enter versions in "Versions to deprecate" (comma-separated) + - For auto-deprecation: Set "Run auto-deprecate" to true + - Optionally adjust the age and version difference parameters +5. Click "Run workflow" + +**Important Note:** This workflow is restricted to users with maintainer or admin permissions on the repository. If a user without these permissions attempts to run the workflow, it will fail with an error message. diff --git a/.github/workflows/deprecation.yml b/.github/workflows/deprecation.yml new file mode 100644 index 000000000..8518eb714 --- /dev/null +++ b/.github/workflows/deprecation.yml @@ -0,0 +1,114 @@ +name: Version Deprecation + +on: + workflow_dispatch: + inputs: + deprecate_versions: + description: 'Versions to deprecate (comma-separated, e.g., 1.0.0,1.1.0)' + required: true + type: string + auto_deprecate: + description: 'Run auto-deprecation' + required: false + type: boolean + default: false + deprecation_age_months: + description: 'Minimum age in months for auto-deprecation' + required: false + type: number + default: 12 + deprecation_minor_version_diff: + description: 'Minimum minor version difference for auto-deprecation' + required: false + type: number + default: 10 + +jobs: + deprecate: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Check permissions + uses: actions/github-script@v6 + with: + script: | + const permission = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: context.actor + }) + if (!['admin', 'maintain'].includes(permission.data.permission)) { + core.setFailed('This workflow can only be run by repository maintainers or admins.') + } + + - name: Check out code + uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Set up Java + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Manual version deprecation + if: github.event.inputs.deprecate_versions + run: | + IFS=',' read -ra VERSIONS <<< "${{ github.event.inputs.deprecate_versions }}" + for VERSION in "${VERSIONS[@]}"; do + ./gradlew deprecateVersion -PversionToDeprecate=$VERSION + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONATYPE_TOKEN_USERNAME: ${{ secrets.SONATYPE_TOKEN_USERNAME }} + SONATYPE_TOKEN_PASSWORD: ${{ secrets.SONATYPE_TOKEN_PASSWORD }} + PGP_KEY: ${{ secrets.PGP_KEY }} + PGP_PWD: ${{ secrets.PGP_PWD }} + + - name: Auto-deprecate old versions + if: github.event.inputs.auto_deprecate == 'true' + run: | + # Install necessary tools + sudo apt-get update && sudo apt-get install -y jq + + # Set deprecation criteria + DEPRECATION_AGE_MONTHS=${{ github.event.inputs.deprecation_age_months || 12 }} + DEPRECATION_MINOR_VERSION_DIFF=${{ github.event.inputs.deprecation_minor_version_diff || 10 }} + + # Get all releases + RELEASES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/${{ github.repository }}/releases") + + # Get the latest release version + LATEST_VERSION=$(echo "$RELEASES" | jq -r '.[0].tag_name' | sed 's/v//') + + # Function to compare versions + version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; } + + # Loop through releases and deprecate old ones + echo "$RELEASES" | jq -c '.[]' | while read -r release; do + VERSION=$(echo "$release" | jq -r '.tag_name' | sed 's/v//') + RELEASE_DATE=$(echo "$release" | jq -r '.published_at') + + # Calculate age in months + AGE_MONTHS=$(( ($(date +%s) - $(date -d "$RELEASE_DATE" +%s)) / (30*24*60*60) )) + + # Calculate version difference + IFS='.' read -ra LATEST_PARTS <<< "$LATEST_VERSION" + IFS='.' read -ra VERSION_PARTS <<< "$VERSION" + MINOR_DIFF=$((LATEST_PARTS[1] - VERSION_PARTS[1])) + + if [ "$AGE_MONTHS" -ge "$DEPRECATION_AGE_MONTHS" ] && [ "$MINOR_DIFF" -ge "$DEPRECATION_MINOR_VERSION_DIFF" ]; then + if ! echo "$release" | jq -e '.name | contains("[DEPRECATED]")' > /dev/null; then + echo "Deprecating version $VERSION" + ./gradlew deprecateVersion -PversionToDeprecate="$VERSION" + fi + fi + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONATYPE_TOKEN_USERNAME: ${{ secrets.SONATYPE_TOKEN_USERNAME }} + SONATYPE_TOKEN_PASSWORD: ${{ secrets.SONATYPE_TOKEN_PASSWORD }} + PGP_KEY: ${{ secrets.PGP_KEY }} + PGP_PWD: ${{ secrets.PGP_PWD }} diff --git a/build.gradle b/build.gradle index 0a8733780..fdc62bbc5 100644 --- a/build.gradle +++ b/build.gradle @@ -79,3 +79,20 @@ subprojects { apply from: "${rootDir}/gradle/dependencies.gradle" apply from: "${rootDir}/gradle/java-publication.gradle" } + +task deprecateVersion { + doLast { + if (project.hasProperty('versionToDeprecate')) { + def version = project.property('versionToDeprecate') + println "Deprecating version $version across all subprojects" + + subprojects { + tasks.findByName('deprecateVersion')?.execute() + } + + println "Completed deprecation of version $version across all subprojects" + } else { + println "No version specified for deprecation" + } + } +} diff --git a/gradle/java-publication.gradle b/gradle/java-publication.gradle index 930827f1a..c47950846 100644 --- a/gradle/java-publication.gradle +++ b/gradle/java-publication.gradle @@ -93,3 +93,43 @@ signing { sign publishing.publications.javaLibrary } } + +task deprecateVersion { + doLast { + if (project.hasProperty('versionToDeprecate')) { + def version = project.property('versionToDeprecate') + + // Update GitHub release + updateGitHubRelease(version) + + // Update Maven Central metadata + publishing.publications.javaLibrary.pom.withXml { + def root = asNode() + def properties = root.properties + if (properties.isEmpty()) { + properties = root.appendNode('properties') + } + properties.appendNode('coral.deprecated', 'true') + properties.appendNode('coral.deprecationDate', new Date().format("yyyy-MM-dd")) + root.description[0].value = "[DEPRECATED] ${root.description[0].text()}" + } + tasks.publishToMavenLocal.execute() + + println "Deprecated version $version for ${project.name} in GitHub and Maven Central" + } else { + println "No version specified for deprecation" + } + } +} + +def updateGitHubRelease(version) { + def github = org.kohsuke.github.GitHub.connectUsingOAuth(System.getenv('GITHUB_TOKEN')) + def repo = github.getRepository("linkedin/coral") + def release = repo.listReleases().find { it.getTagName() == version } + if (release) { + release.update().name("[DEPRECATED] ${release.getName()}").update() + println "Updated GitHub release for version $version" + } else { + println "No GitHub release found for version $version" + } +}