Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying different versions of JDKs per operating system and architecture #286

Merged
merged 16 commits into from
Feb 21, 2024

Conversation

CRogers
Copy link
Contributor

@CRogers CRogers commented Feb 16, 2024

Before this PR

Currently, gradle-jdks only allows you to specify one version of a JDK per java major version. This means all operating systems and architectures must use the same JDK version.

However, this falls apart in the presence of operating system only releases by JDK vendors. For example, Amazon Corretto recently released a "linux" only release for JDK 17 (17.0.10.8.1) and 21 (21.0.2.14.1).

We attempt to use same JDKs in both our prod environments and CI (which are linux based) and on developer's local machines (almost always macos). We had an unfortunate situation where automated upgrades of JDKs upgraded people's repos to these versions that did not have macos distributions, causing them to be unable to compile or test code locally.

After this PR

==COMMIT_MSG==
Allow specifying different versions of JDKs per operating system and architecture
==COMMIT_MSG==

We still want to get the latest JDKs in prod, even if they're on a linux only release. Our compliance constraints mean we must fix CVEs on a short timeframe, and these CVE fixes may be in a linux only release. Or there could be some critically important bugfix we need (but currently wouldn't be able to get without breaking people's dev setups). Even the current bug is scary and we should ensure gets onto prod ("AArch64: crypto pmull based CRC32/CRC32C intrinsics clobber V8-V15 registers").

There's changes internally to rest of the stack of Gradle plugins that deal with JDKs. Most notably, they all use the same json representation to list the latest JDKs, and we have an excavator that can produce this json file automatically.

This file format is in this repo because:

  1. I want to make it easy to load into our Gradle configuration
  2. When we build the feature to manage the Gradle daemon JDK, I want to produce a native-image binary that will "pre-install" the latest JDKs on CI images etc so the JDKs are already downloaded, which would necessitate the file format code living in this repo.
Example latest jdk json format
{
  "jdksPerJavaVersion": {
    "8": {
      "distribution": "amazon-corretto",
      "os": {
        "macos": {
          "arch": {
            "x86-64": {
              "version": "8.402.08.1"
            },
            "aarch64": {
              "version": "8.402.08.1"
            }
          }
        },
        "linux-glibc": {
          "arch": {
            "x86-64": {
              "version": "8.402.08.1"
            },
            "aarch64": {
              "version": "8.402.08.1"
            }
          }
        },
        "linux-musl": {
          "arch": {
            "x86-64": {
              "version": "8.402.08.1"
            },
            "aarch64": {
              "version": "8.402.08.1"
            }
          }
        },
        "windows": {
          "arch": {
            "x86": {
              "version": "8.402.08.1"
            },
            "x86-64": {
              "version": "8.402.08.1"
            }
          }
        }
      }
    },
    "11": {
      "distribution": "amazon-corretto",
      "os": {
        "macos": {
          "arch": {
            "x86-64": {
              "version": "11.0.22.7.1"
            },
            "aarch64": {
              "version": "11.0.22.7.1"
            }
          }
        },
        "linux-glibc": {
          "arch": {
            "x86": {
              "version": "11.0.22.7.1"
            },
            "x86-64": {
              "version": "11.0.22.7.1"
            },
            "aarch64": {
              "version": "11.0.22.7.1"
            }
          }
        },
        "linux-musl": {
          "arch": {
            "x86-64": {
              "version": "11.0.22.7.1"
            },
            "aarch64": {
              "version": "11.0.22.7.1"
            }
          }
        },
        "windows": {
          "arch": {
            "x86": {
              "version": "11.0.22.7.1"
            },
            "x86-64": {
              "version": "11.0.22.7.1"
            }
          }
        }
      }
    },
    "17": {
      "distribution": "amazon-corretto",
      "os": {
        "macos": {
          "arch": {
            "x86-64": {
              "version": "17.0.10.7.1"
            },
            "aarch64": {
              "version": "17.0.10.7.1"
            }
          }
        },
        "linux-glibc": {
          "arch": {
            "x86-64": {
              "version": "17.0.10.8.1"
            },
            "aarch64": {
              "version": "17.0.10.8.1"
            }
          }
        },
        "linux-musl": {
          "arch": {
            "x86-64": {
              "version": "17.0.10.8.1"
            },
            "aarch64": {
              "version": "17.0.10.8.1"
            }
          }
        },
        "windows": {
          "arch": {
            "x86-64": {
              "version": "17.0.10.7.1"
            }
          }
        }
      }
    },
    "21": {
      "distribution": "amazon-corretto",
      "os": {
        "macos": {
          "arch": {
            "x86-64": {
              "version": "21.0.2.13.1"
            },
            "aarch64": {
              "version": "21.0.2.13.1"
            }
          }
        },
        "linux-glibc": {
          "arch": {
            "x86-64": {
              "version": "21.0.2.14.1"
            },
            "aarch64": {
              "version": "21.0.2.14.1"
            }
          }
        },
        "linux-musl": {
          "arch": {
            "x86-64": {
              "version": "21.0.2.14.1"
            },
            "aarch64": {
              "version": "21.0.2.14.1"
            }
          }
        },
        "windows": {
          "arch": {
            "x86-64": {
              "version": "21.0.2.13.1"
            }
          }
        }
      }
    }
  }
}

Possible downsides?

@changelog-app
Copy link

changelog-app bot commented Feb 16, 2024

Generate changelog in changelog/@unreleased

What do the change types mean?
  • feature: A new feature of the service.
  • improvement: An incremental improvement in the functionality or operation of the service.
  • fix: Remedies the incorrect behaviour of a component of the service in a backwards-compatible way.
  • break: Has the potential to break consumers of this service's API, inclusive of both Palantir services
    and external consumers of the service's API (e.g. customer-written software or integrations).
  • deprecation: Advertises the intention to remove service functionality without any change to the
    operation of the service itself.
  • manualTask: Requires the possibility of manual intervention (running a script, eyeballing configuration,
    performing database surgery, ...) at the time of upgrade for it to succeed.
  • migration: A fully automatic upgrade migration task with no engineer input required.

Note: only one type should be chosen.

How are new versions calculated?
  • ❗The break and manual task changelog types will result in a major release!
  • 🐛 The fix changelog type will result in a minor release in most cases, and a patch release version for patch branches. This behaviour is configurable in autorelease.
  • ✨ All others will result in a minor version release.

Type

  • Feature
  • Improvement
  • Fix
  • Break
  • Deprecation
  • Manual task
  • Migration

Description

Allow specifying different versions of JDKs per operating system and architecture

Check the box to generate changelog(s)

  • Generate changelog entry

Copy link

@felixdesouza felixdesouza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly curious about mixing of non-provider/property types inside the extensions, but not a huge blocker

@@ -24,11 +30,46 @@ public abstract class JdkExtension {

public abstract Property<JdkDistributionName> getDistributionName();

private final Map<Os, JdkOsExtension> jdkOsExtensions = new HashMap<>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use a NamedDomainObjectContainer ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With NamedDomain.... need you to give a string for the name (or implement Named, which bring in a needless gradle dep). I don't really think it buys much? Unless there's some magic you can do with it I'm not aware of.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how it's used, I think it doesn't buy us as much, since we need it to statically exist. I believe it creates most of the methods for you, but that doesn't really matter if you're calling it from java

import java.util.Set;

final class CurrentArch {
static Arch current() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since Current* is already in the name, how about CurrentArch.get()?

WINDOWS;

@Value.Default
final class CurrentOs {
public static Os current() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing re CurrentOs.get()

Comment on lines 96 to 100
String version = jdkExtension
.jdkFor(currentOs)
.jdkFor(currentArch)
.getJdkVersion()
.get();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious, why force property retrieval here? do you not get into races with configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method, javaInstallationForLanguageVersion is called from a lazy location, but you're right this could probably be even more lazy to avoid problems.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah

then:
stdout.contains('jdkVersion macos aarch64: 11.1')
stdout.contains('jdkVersion linux-glibc aarch64: 11.2')
stdout.contains('jdkVersion linux-glibc x64: 11.3')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@felixdesouza
Copy link

👍 pending Current*.get()

@felixdesouza
Copy link

👍

@bulldozer-bot bulldozer-bot bot merged commit b59b494 into develop Feb 21, 2024
6 checks passed
@bulldozer-bot bulldozer-bot bot deleted the callumr/different-versions branch February 21, 2024 13:16
@svc-autorelease
Copy link
Collaborator

Released 0.35.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants