diff --git a/.github/workflows/gradle-build.yml b/.github/workflows/gradle-build.yml index e81d18b7693..d200426957f 100644 --- a/.github/workflows/gradle-build.yml +++ b/.github/workflows/gradle-build.yml @@ -18,8 +18,10 @@ jobs: java-version: 21 distribution: 'temurin' cache: gradle - - name: Detect Secrets - uses: RobertFischer/detect-secrets-action@v2.0.0 + - name: Detect secrets + run: | + pip install detect-secrets + git ls-files -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline - name: Grant Execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle @@ -46,7 +48,7 @@ jobs: settings.gradle gradle.properties **/gradle/** - Scripts/** + scripts/** build-java-8: runs-on: ubuntu-latest diff --git a/.github/workflows/preview-and-release.yml b/.github/workflows/preview-and-release.yml index ea569ac9505..58b67582fa2 100644 --- a/.github/workflows/preview-and-release.yml +++ b/.github/workflows/preview-and-release.yml @@ -14,8 +14,10 @@ on: workflow_dispatch: env: - PREVIEW_TASK: publishSnapshotPublicationToSonatypeSnapshotRepository - PUBLISH_TASK: publishMavenCentralReleasePublicationToSonatypeRepository + PREVIEW_TASK: publishToSonatype + PUBLISH_TASK: publishToSonatype closeAndReleaseSonatypeStagingRepository + JAVA_VERSION: 21 + JAVA_DISTRIBUTION: 'temurin' permissions: contents: write @@ -26,24 +28,27 @@ jobs: environment: name: maven_central_snapshot runs-on: ubuntu-latest + needs: validate-package-contents steps: - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21 - distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + distribution: ${{ env.JAVA_DISTRIBUTION}} cache: gradle - - name: Easy detect-secrets - uses: RobertFischer/detect-secrets-action@v2.0.0 + - name: Detect secrets + run: | + pip install detect-secrets + git ls-files -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline - name: Download File - run: .\Scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH + run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH shell: pwsh env: ENCODED_VALUE: ${{ secrets.LOCAL_PROPERTIES }} OUTPUT_PATH: '.\local.properties' - name: Download File - run: .\Scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH + run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH shell: pwsh env: ENCODED_VALUE: ${{ secrets.SECRING_GPG }} @@ -58,24 +63,27 @@ jobs: environment: name: maven_central_release runs-on: ubuntu-latest + needs: validate-package-contents steps: - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 21 - distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + distribution: ${{ env.JAVA_DISTRIBUTION}} cache: gradle - - name: Easy detect-secrets - uses: RobertFischer/detect-secrets-action@v2.0.0 + - name: Detect secrets + run: | + pip install detect-secrets + git ls-files -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline - name: Download File - run: .\Scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH + run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH shell: pwsh env: ENCODED_VALUE: ${{ secrets.LOCAL_PROPERTIES }} OUTPUT_PATH: '.\local.properties' - name: Download File - run: .\Scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH + run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH shell: pwsh env: ENCODED_VALUE: ${{ secrets.SECRING_GPG }} @@ -83,7 +91,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Publish - run: ./gradlew $PUBLISH_TASK + run: ./gradlew $PUBLISH_TASK -PmavenCentralSnapshotArtifactSuffix="" - name: Upload Build Artifact uses: actions/upload-artifact@v4 with: @@ -98,7 +106,7 @@ jobs: settings.gradle gradle.properties **/gradle/** - Scripts/** + scripts/** - name: GitHub Release uses: softprops/action-gh-release@v2 with: @@ -106,3 +114,46 @@ jobs: fail_on_unmatched_files: true files: | build/**/*.jar + + validate-package-contents: + runs-on: ubuntu-latest + environment: ${{ contains(github.ref, 'refs/tags/v') && 'maven_central_release' || 'maven_central_snapshot' }} + defaults: + run: + working-directory: ./ + steps: + - uses: actions/checkout@v4 + - name: Setup JDK + uses: actions/setup-java@v4 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: ${{ env.JAVA_DISTRIBUTION}} + cache: gradle + - name: Download file + run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH + shell: pwsh + env: + ENCODED_VALUE: ${{ secrets.LOCAL_PROPERTIES }} + OUTPUT_PATH: 'local.properties' + - name: Download file + run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH + shell: pwsh + env: + ENCODED_VALUE: ${{ secrets.SECRING_GPG }} + OUTPUT_PATH: '.\secring.gpg' + - name: Publish to local Maven cache for validation + run: ./gradlew --no-daemon publishToMavenLocal + - name: Get current SNAPSHOT version + shell: pwsh + run: | + $contents = Get-Content gradle.properties -Raw + $major = $contents | Select-String -Pattern 'mavenMajorVersion\s+= ([0-9]+)' | ForEach-Object { $_.Matches.Groups[1].Value } + $minor = $contents | Select-String -Pattern 'mavenMinorVersion\s+= ([0-9]+)' | ForEach-Object { $_.Matches.Groups[1].Value } + $patch = $contents | Select-String -Pattern 'mavenPatchVersion\s+= ([0-9]+)' | ForEach-Object { $_.Matches.Groups[1].Value } + $version = "$major.$minor.$patch-SNAPSHOT" + echo "Current version is $version" + echo "PACKAGE_VERSION=$version" >> $Env:GITHUB_ENV + - name: Inspect contents of local Maven cache + shell: pwsh + run: | + .\scripts\validatePackageContents.ps1 -ArtifactId msgraph-sdk-java -Version $env:PACKAGE_VERSION diff --git a/.secrets.baseline b/.secrets.baseline index cd595090cdf..6c35446437b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1,143 +1,139 @@ -{ - "version": "1.5.0", - "plugins_used": [ - { - "name": "ArtifactoryDetector" - }, - { - "name": "AWSKeyDetector" - }, - { - "name": "AzureStorageKeyDetector" - }, - { - "name": "Base64HighEntropyString", - "limit": 4.5 - }, - { - "name": "BasicAuthDetector" - }, - { - "name": "CloudantDetector" - }, - { - "name": "DiscordBotTokenDetector" - }, - { - "name": "GitHubTokenDetector" - }, - { - "name": "GitLabTokenDetector" - }, - { - "name": "HexHighEntropyString", - "limit": 3.0 - }, - { - "name": "IbmCloudIamDetector" - }, - { - "name": "IbmCosHmacDetector" - }, - { - "name": "IPPublicDetector" - }, - { - "name": "JwtTokenDetector" - }, - { - "name": "KeywordDetector", - "keyword_exclude": "" - }, - { - "name": "MailchimpDetector" - }, - { - "name": "NpmDetector" - }, - { - "name": "OpenAIDetector" - }, - { - "name": "PrivateKeyDetector" - }, - { - "name": "PypiTokenDetector" - }, - { - "name": "SendGridDetector" - }, - { - "name": "SlackDetector" - }, - { - "name": "SoftlayerDetector" - }, - { - "name": "SquareOAuthDetector" - }, - { - "name": "StripeDetector" - }, - { - "name": "TelegramBotTokenDetector" - }, - { - "name": "TwilioKeyDetector" - } - ], - "filters_used": [ - { - "path": "detect_secrets.filters.allowlist.is_line_allowlisted" - }, - { - "path": "detect_secrets.filters.common.is_baseline_file", - "filename": ".secrets.baseline" - }, - { - "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", - "min_level": 2 - }, - { - "path": "detect_secrets.filters.heuristic.is_indirect_reference" - }, - { - "path": "detect_secrets.filters.heuristic.is_likely_id_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_lock_file" - }, - { - "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_potential_uuid" - }, - { - "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" - }, - { - "path": "detect_secrets.filters.heuristic.is_sequential_string" - }, - { - "path": "detect_secrets.filters.heuristic.is_swagger_file" - }, - { - "path": "detect_secrets.filters.heuristic.is_templated_secret" - }, - { - "path": "detect_secrets.filters.HexHighEntropyString.should_exclude_file", - "pattern": [ - "release-please-config.json" - ] - }, - { - "path": "detect_secrets.filters.regex.should_exclude_file", - "pattern": [ - "release-please-config.json" - ] - } - ], - "results": {}, - "generated_at": "2024-05-08T12:56:29Z" -} +{ + "version": "1.5.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "GitLabTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "IPPublicDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "OpenAIDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "PypiTokenDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TelegramBotTokenDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_baseline_file", + "filename": ".secrets.baseline" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + }, + { + "path": "detect_secrets.filters.regex.should_exclude_file", + "pattern": [ + "src/main/java/com/microsoft/graph/generated/.*", + "release-please-config.json", + "kiota-lock.json" + ] + } + ], + "results": {}, + "generated_at": "2024-08-13T14:19:43Z" +} diff --git a/build.gradle b/build.gradle index 3545bbfbf13..ac8ad28be32 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ plugins { id 'eclipse' id 'maven-publish' id 'signing' + id 'io.github.gradle-nexus.publish-plugin' version '2.0.0' } java { @@ -52,9 +53,6 @@ def pomConfig = { tasks.withType(Javadoc).all { enabled = false } -//Publishing tasks- -//Maven Central Snapshot: publishSnapshotPublicationToMavenRepository -//Maven Central Release: publishMavenCentralReleasePublicationToMaven2Repository tasks.jar { manifest { @@ -67,38 +65,11 @@ publishing { publications { - maven(MavenPublication) { - groupId project.property('mavenGroupId') - artifactId project.property('mavenArtifactId') - version "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}${mavenArtifactSuffix}" - from components.java - pom.withXml { - def root = asNode() - root.appendNode('name', 'Microsoft Graph SDK for Java') - root.appendNode('url', 'https://github.com/microsoftgraph/msgraph-sdk-java') - root.children().last() + pomConfig - def pomFile = file("${project.buildDir}/libs/microsoft-graph.pom") - writeTo(pomFile) - } - - } - Snapshot(MavenPublication) { + maven(MavenPublication) { customizePom(pom) groupId project.property('mavenGroupId') artifactId project.property('mavenArtifactId') - version "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}${mavenCentralSnapshotArtifactSuffix}" - from components.java - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - } - } - - mavenCentralRelease(MavenPublication) { - customizePom(pom) - groupId project.property('mavenGroupId') - artifactId project.property('mavenArtifactId') - version "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}" + version getVersionName() from components.java pom.withXml { def pomFile = file("${project.buildDir}/generated-pom.xml") @@ -106,43 +77,30 @@ publishing { } } } - repositories { - maven { - url = 'https://oss.sonatype.org/content/repositories/snapshots' - name = 'sonatypeSnapshot' - - credentials { - if (project.rootProject.file('local.properties').exists()) { - Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - username = properties.getProperty('sonatypeUsername') - password = properties.getProperty('sonatypePassword') - } - } - } - - maven { - url = 'https://oss.sonatype.org/service/local/staging/deploy/maven2' - name = 'sonatype' - - credentials { - if (project.rootProject.file('local.properties').exists()) { - Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - username = properties.getProperty('sonatypeUsername') - password = properties.getProperty('sonatypePassword') - } +} + +nexusPublishing { + repositories { + sonatype { + if (project.rootProject.file('local.properties').exists()) { + Properties properties = new Properties() + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + username = properties.getProperty('sonatypeUsername') + password = properties.getProperty('sonatypePassword') } } } } +group = project.property('mavenGroupId') +version = "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}${mavenCentralSnapshotArtifactSuffix}" + signing { - sign publishing.publications.mavenCentralRelease + sign publishing.publications.maven } tasks.withType(Sign)*.enabled = mavenCentralPublishingEnabled.toBoolean() -def fixAscNames = { name -> +def fixAscNames = { name -> if(name.contains('pom')) { "${project.property('mavenArtifactId')}-${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}.pom.asc" } else { @@ -161,7 +119,7 @@ def getVersionCode() { } def getVersionName() { - return "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}${mavenArtifactSuffix}" + return "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}${mavenCentralSnapshotArtifactSuffix}" } artifacts { @@ -223,7 +181,7 @@ gradle.taskGraph.whenReady { taskGraph -> } model { - tasks.generatePomFileForMavenCentralReleasePublication { + tasks.generatePomFileForMavenPublication { destination = file("${project.buildDir}/generated-pom.xml") } } diff --git a/Scripts/decodeAndWrite.ps1 b/scripts/decodeAndWrite.ps1 similarity index 100% rename from Scripts/decodeAndWrite.ps1 rename to scripts/decodeAndWrite.ps1 diff --git a/Scripts/getLatestVersion.ps1 b/scripts/getLatestVersion.ps1 similarity index 100% rename from Scripts/getLatestVersion.ps1 rename to scripts/getLatestVersion.ps1 diff --git a/scripts/validatePackageContents.ps1 b/scripts/validatePackageContents.ps1 new file mode 100644 index 00000000000..c79aba5186d --- /dev/null +++ b/scripts/validatePackageContents.ps1 @@ -0,0 +1,58 @@ +# Checks that expected files are present & have contents after the publish process to the local cache +param( + [Parameter(Mandatory=$true)][string] $ArtifactId, + [Parameter(Mandatory=$true)][string] $Version, + [Parameter()][string] $GroupId = "com.microsoft.graph", + [Parameter()][string] $MavenLocalCachePath = "~" + [System.IO.Path]::DirectorySeparatorChar + ".m2" + [System.IO.Path]::DirectorySeparatorChar + "repository" +) + +$groupIdPath = $GroupId -replace "\.", [System.IO.Path]::DirectorySeparatorChar +$packagePath = Join-Path -Path $groupIdPath -ChildPath $ArtifactId +$packageFullPath = Join-Path -Path $MavenLocalCachePath -ChildPath $packagePath -AdditionalChildPath $Version + +Write-Output "---------------------------------------------------" +Write-Output "Validating package contents at $packageFullPath" + +if(-not (Test-Path -Path $packageFullPath)) { + Write-Output "Package not found in local cache." + exit 1 +} + +Write-Output "Package exists in local cache." + +$expectedFiles = @( + "-javadoc.jar", + "-javadoc.jar.asc", + "-sources.jar", + "-sources.jar.asc", + ".module", + ".module.asc", + ".pom", + ".pom.asc", + ".jar", + ".jar.asc" +) + +foreach($file in $expectedFiles) { + $file = $ArtifactId + "-" + $Version + $file + $filePath = Join-Path -Path $packageFullPath -ChildPath $file + if(-not (Test-Path -Path $filePath)) { + Write-Output "Expected file $file not found in package." + exit 1 + } + $fileSize = (Get-Item -Path $filePath).length + if($fileSize -eq 0) { + Write-Output "File $file is empty." + exit 1 + } +} + +$mavenMetadataFiles = Get-ChildItem -Path $packageFullPath -Filter "maven-metadata*.xml" +if($mavenMetadataFiles.Count -eq 0) { + Write-Output "No maven-metadata*.xml files found in package." + exit 1 +} + +Write-Output "Package $ArtifactId is valid." +Write-Output "---------------------------------------------------" +exit 0