From 2ad4659de9574cfb02258a36df6f1daeeaafc500 Mon Sep 17 00:00:00 2001 From: Andrey G Date: Fri, 20 Mar 2026 17:35:52 +0100 Subject: [PATCH 1/3] Add publishing GH actions pipeline --- .github/actions/get-version/action.yml | 72 ++++ .../publish-build-info-to-jfrog/action.yml | 63 ++++ .github/actions/publish-to-github/action.yml | 91 +++++ .../actions/publish-to-sonatype/action.yml | 171 ++++++++++ .../stage-release-artifacts/action.yml | 148 ++++++++ .github/workflows/build-release.yml | 152 +++++++++ .github/workflows/promote-prod.yml | 23 ++ .github/workflows/promote.yml | 316 ++++++++++++++++++ .github/workflows/push-to-stage.yml | 22 ++ .github/workflows/release-drafter.yml | 22 ++ .github/workflows/required-labels.yml | 22 ++ .../workflows/sonatype-approve-or-delete.yml | 70 ++++ 12 files changed, 1172 insertions(+) create mode 100644 .github/actions/get-version/action.yml create mode 100644 .github/actions/publish-build-info-to-jfrog/action.yml create mode 100644 .github/actions/publish-to-github/action.yml create mode 100644 .github/actions/publish-to-sonatype/action.yml create mode 100644 .github/actions/stage-release-artifacts/action.yml create mode 100644 .github/workflows/build-release.yml create mode 100644 .github/workflows/promote-prod.yml create mode 100644 .github/workflows/promote.yml create mode 100644 .github/workflows/push-to-stage.yml create mode 100644 .github/workflows/release-drafter.yml create mode 100644 .github/workflows/required-labels.yml create mode 100644 .github/workflows/sonatype-approve-or-delete.yml diff --git a/.github/actions/get-version/action.yml b/.github/actions/get-version/action.yml new file mode 100644 index 0000000..383b4ab --- /dev/null +++ b/.github/actions/get-version/action.yml @@ -0,0 +1,72 @@ +name: Get version +description: Detects if build is a snapshot and gets the release or snapshot version + +runs: + using: composite + steps: + # Checking if this particular build is a snapshot build + - name: Detect if snapshot + id: get-is-snapshot + shell: bash + run: | + # Check if tagged with release or release candidate + TAG=$(git describe --exact-match --tags HEAD 2>/dev/null || echo "") + echo "Detected Git tag: '$TAG'" + + # Define regex patterns + RE_PROD='^v?[0-9]+\.[0-9]+\.[0-9]+$' + RE_RC='^v?[0-9]+\.[0-9]+\.[0-9]+-rc[0-9]+$' + + if [[ "$TAG" =~ $RE_PROD ]]; then + echo "Production release tag detected: $TAG" + echo "is-snapshot=false" >> $GITHUB_OUTPUT + exit 0 + elif [[ "$TAG" =~ $RE_RC ]]; then + echo "Release candidate tag detected: $TAG" + echo "is-snapshot=false" >> $GITHUB_OUTPUT + exit 0 + else + echo "No matching tag found" + echo "is-snapshot=true" >> $GITHUB_OUTPUT + fi + + # Checking if previous commit contains pom.xml. This should always return true + COMMIT_REF="HEAD~1" + if ! git show "${COMMIT_REF}:pom.xml" &>/dev/null; then + echo "Error: pom.xml not found in commit ${COMMIT_REF}" + echo "is-snapshot='true'" >> $GITHUB_OUTPUT + exit 0 + fi + + # Getting previous version + OLD_VERSIONS=$(git show "${COMMIT_REF}:pom.xml" | sed -n '0,// s/.*\([^<]*\)<\/version>.*/\1/p') + + # Getting current version + NEW_VERSIONS=$(sed -n '0,// s/.*\([^<]*\)<\/version>.*/\1/p' pom.xml) + + echo "old versions: ${OLD_VERSIONS}, new versions: ${NEW_VERSIONS}" + # Compare the extracted versions. CI will not commit snapshot version. + if [[ "${OLD_VERSIONS}" != "${NEW_VERSIONS}" ]]; then + echo "is-snapshot=false" >> $GITHUB_OUTPUT + else + echo "is-snapshot=true" >> $GITHUB_OUTPUT + fi + + - name: Get release or snapshot-version + id: get-release-version + shell: bash + run: | + IS_SNAPSHOT=${{ steps.get-is-snapshot.outputs.is-snapshot }} + if [ $IS_SNAPSHOT == "true" ];then + echo release-version="$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)-SNAPSHOT_$GITHUB_SHA" >> $GITHUB_OUTPUT + else + echo release-version="$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT + fi + +outputs: + is-snapshot: + description: Whether this is a snapshot build + value: ${{ steps.get-is-snapshot.outputs.is-snapshot }} + release-version: + description: The release or snapshot version + value: ${{ steps.get-release-version.outputs.release-version }} \ No newline at end of file diff --git a/.github/actions/publish-build-info-to-jfrog/action.yml b/.github/actions/publish-build-info-to-jfrog/action.yml new file mode 100644 index 0000000..45934d1 --- /dev/null +++ b/.github/actions/publish-build-info-to-jfrog/action.yml @@ -0,0 +1,63 @@ +name: Publish build-info to JFrog +description: "Publishes build-info to JFrog" + +inputs: + jfrog-platform-url: + description: "JFrog platform URL" + required: false + default: https://aerospike.jfrog.io + oidc-provider: + description: "OIDC provider name" + required: true + oidc-audience: + description: "ODIC audience" + required: true + build-path: + description: "Path to which build info is to be published" + required: true + variables: + description: "Any additional variables to be published as part of build. The input here should be valid JSON in the form {'env_variable_key': 'env_variable_value'}, .e.g {'SONATYPE_STAGING_BUILD_ID': '070c07e25e937888ed9740ee825afa24bf184722'}" + required: true + +runs: + using: "composite" + steps: + - name: Debug publish to github + shell: bash + run: | + echo "${{ inputs.jfrog-platform-url }}" + echo "${{ inputs.build-path }}" + + - name: Set up JFrog credentials + id: setup-jfrog-cli + uses: step-security/setup-jfrog-cli@a6b41f8338bea0983ddff6bd4ede7d2dcd81e1fa # v4.8.1 + env: + JF_URL: ${{ inputs.jfrog-platform-url }} + JF_PROJECT: database + with: + version: 2.72.2 + oidc-provider-name: ${{ inputs.oidc-provider }} + oidc-audience: ${{ inputs.oidc-audience }} + + # Parsing out env variables and values and setting them in the environment + - name: Set env variables provided with variables + shell: bash + run: | + ENV_VARIABLES='${{ inputs.variables }}' + echo "$ENV_VARIABLES" | jq -r 'to_entries | .[] | "\(.key)=\(.value)"' >> $GITHUB_ENV + + # Pushing build info to JFrog + - name: Upload artifacts + shell: bash + run: | + BUILD_ID=$(echo "${{ inputs.build-path }}" | sed 's/.*_\(.*\)\/.*/\1/') + BUILD_PATH="promote_${BUILD_ID}" + + # record env variables + jf rt bce ${BUILD_PATH} ${{ github.run_number }} + + # record git info + jf rt bag ${BUILD_PATH} ${{ github.run_number }} + + # publish build info + jf rt bp ${BUILD_PATH} ${{ github.run_number }} diff --git a/.github/actions/publish-to-github/action.yml b/.github/actions/publish-to-github/action.yml new file mode 100644 index 0000000..08c4b13 --- /dev/null +++ b/.github/actions/publish-to-github/action.yml @@ -0,0 +1,91 @@ +name: Publish artifacts to github +description: "Publish artifacts to github" + +inputs: + staging-folder: + description: "" + required: false + default: staging + target-folder: + description: "" + required: false + default: github + release-notes: + description: "Release notes" + required: true + github-token: + description: "Github auth token" + required: true + artifact-version: + description: "Version of the artifact to release" + required: true + build-name-number: + description: "String containing build name, number and jdk versions" + required: true + +runs: + using: "composite" + steps: + - name: Debug publish to github + shell: bash + run: | + echo "${{ inputs.staging-folder }}" + echo "${{ inputs.target-folder }}" + echo "${{ inputs.artifact-version }}" + echo "${{ inputs.release-notes }}" + echo "${{ inputs.build-name-number }}" + + - id: get-artifact-version + shell: bash + run: | + VERSION="${{ inputs.artifact-version }}" + echo "artifact-version=${VERSION}" >> $GITHUB_OUTPUT + + - name: Create upload archive for github + id: create-artifact + shell: bash + run: | + src="${{ inputs.staging-folder }}" + dest="${{ inputs.target-folder }}" + + find "$src" -type f \ + -exec cp {} "$dest" \; + + # Listing staged artifacts to be uploaded + - id: get-github-release-artifact-names + working-directory: ${{ inputs.target-folder }} + shell: bash + run: | + ARTIFACTS=$(ls | sed "s|^|${{ inputs.target-folder }}/|") + + echo "release-artifacts<> $GITHUB_OUTPUT + echo "${ARTIFACTS}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Debug show content of the upload archive + shell: bash + run: | + pwd + ls ${{ inputs.target-folder }} | sed 's/^//' + + - name: Debug GitHub publish input + shell: bash + working-directory: ${{ inputs.target-folder }} + run: | + echo "working directory: ${{ inputs.target-folder }}" + echo "tag name: Release ${{ steps.get-artifact-version.outputs.artifact-version }}" + echo "body: Changes for release ${{ steps.get-artifact-version.outputs.artifact-version }}" + echo "body: ${{ inputs.release-notes }}" + echo "files: ${{ steps.get-github-release-artifact-names.outputs.release-artifacts }}" + + - name: Publish release to github + uses: step-security/action-gh-release@5f6a6ab53a5a2c000ff3a16fad038291e5b97ce7 # v2.4.2 + with: + token: ${{ inputs.github-token }} + tag_name: ${{ steps.get-artifact-version.outputs.artifact-version }} + body: | + Changes for release ${{ steps.get-artifact-version.outputs.artifact-version }} + ${{ inputs.release-notes }} + draft: true + prerelease: false + files: ${{ steps.get-github-release-artifact-names.outputs.release-artifacts }} diff --git a/.github/actions/publish-to-sonatype/action.yml b/.github/actions/publish-to-sonatype/action.yml new file mode 100644 index 0000000..e042451 --- /dev/null +++ b/.github/actions/publish-to-sonatype/action.yml @@ -0,0 +1,171 @@ +name: Publish artifacts to Sonatype +description: "Publishes artifacts to Sonatype" + +inputs: + staging-folder: + description: "Folder to which the artifacts are copied" + required: false + default: staging + target-folder: + description: "Subfolder in the staging folder where the artifacts are copied" + required: false + default: sonatype + publish-user: + description: "Username for publishing to Sonatype" + required: true + publish-password: + description: "Password for publishing to Sonatype" + required: true + validation-max-number-checks: + description: "Number of checks we will conduct to check if the package is validated" + required: true + sonatype-domain-name: + description: "Sonatype domain name" + required: true + build-name-number: + required: true + description: "Build name and number" + artifact-version: + description: "Artifact version" + required: true + +outputs: + maven-central-release-id: + description: Maven central id extracted from staging step to maven central + value: ${{ steps.get-maven-central-release-id.outputs.maven-central-release-id }} + +runs: + using: "composite" + steps: + - name: Debug publish to github + shell: bash + run: | + echo "${{ inputs.staging-folder }}" + echo "${{ inputs.target-folder }}" + echo "${{ inputs.sonatype-domain-name }}" + echo "${{ inputs.build-name-number }}" + echo "${{ inputs.artifact-version }}" + + # For Maven Central we need to filter out the jar-with-dependencies + # as it is not allowed to be uploaded to Maven Central + - name: Copy artifacts with filter to sonatype stage folder + shell: bash + working-directory: ${{ inputs.staging-folder }} + run: | + src="./" + dest=../"${{ inputs.target-folder }}" + + find "$src" -type f \ + \! \( -name "*jar-with-dependencies*" \) \ + -exec cp --parents {} "$dest" \; + + - name: Debug target folder contents + shell: bash + working-directory: ${{ inputs.target-folder }} + run: | + echo "Current directory: $(pwd)" + echo "Contents:" + ls -laR + + # Creates uploadable archive + - name: Create upload archive for sonatype + id: create-artifact + shell: bash + working-directory: ${{ inputs.target-folder }} + run: | + # Use the actual artifact version, not parsed from build-name-number + RELEASE="${{ inputs.artifact-version }}" + # Extract just the build number from build-name-number + RELEASE_BUILD_NUMBER=$(echo "${{ inputs.build-name-number }}" | sed -E 's/.*\/([^/]+)$/\1/') + ARTIFACT_NAME="${RELEASE}_${RELEASE_BUILD_NUMBER}" + + echo "artifact-name=${ARTIFACT_NAME}.zip" >> $GITHUB_OUTPUT + zip -r "${ARTIFACT_NAME}.zip" . + + - name: Debug show content of the upload archive + shell: bash + working-directory: ${{ inputs.target-folder }} + run: | + ARTIFACT_NAME='${{ steps.create-artifact.outputs.artifact-name }}' + unzip -l "${ARTIFACT_NAME}" | tail -n +4 | sort -k4,4 + + # Call to Maven Central to stage the release/artifact + - name: Stage artifacts + id: stage-release + working-directory: ${{ inputs.target-folder }} + shell: bash + run: | + TOKEN=$(printf "${{ inputs.publish-user }}:${{ inputs.publish-password }}" | base64) + ARTIFACT_NAME="${{ steps.create-artifact.outputs.artifact-name }}" + UPLOAD_URL="${{ inputs.sonatype-domain-name }}/api/v1/publisher/upload?publishingType=USER_MANAGED" + + run_curl() { + curl --request POST --silent \ + --header "Authorization: Bearer ${TOKEN}" \ + --form "bundle=@${ARTIFACT_NAME}" \ + "${UPLOAD_URL}" + } + + echo "Uploading artifact '${ARTIFACT_NAME}' to Sonatype using URL '${UPLOAD_URL}'" + DEPLOYMENT_ID=$(run_curl) + + echo "Deployment ID: ${DEPLOYMENT_ID}" + echo "deployment-id=${DEPLOYMENT_ID}" >> $GITHUB_OUTPUT + + # Validation check loop + - name: Check validation + working-directory: ${{ inputs.target-folder }} + shell: bash + run: | + + TOKEN=$(printf "%s:%s" "${{ inputs.publish-user }}" "${{ inputs.publish-password }}" | base64) + DEPLOYMENT_ID="${{ steps.stage-release.outputs.deployment-id }}" + + NUMBER_OF_CHECKS=${{ inputs.validation-max-number-checks }} + + echo "Starting Sonatype validation checks (max: ${NUMBER_OF_CHECKS})" + + for ((i = 1; i <= NUMBER_OF_CHECKS; i++)); do + RESPONSE=$(curl --silent --show-error \ + --request POST \ + --header "Authorization: Bearer ${TOKEN}" \ + "${{ inputs.sonatype-domain-name }}/api/v1/publisher/status?id=${DEPLOYMENT_ID}") + + # Validate JSON and extract deploymentState + SONATYPE_RESPONSE=$(echo "${RESPONSE}" | jq -r '.deploymentState // "NULL"') + + if [[ "${SONATYPE_RESPONSE}" == "FAILED" ]]; then + ERRORS=$(echo "${RESPONSE}" | jq '.errors // "Unknown error"') + echo "Package validation failed." + echo "Errors: ${ERRORS}" + exit 1 + + elif [[ "${SONATYPE_RESPONSE}" == "VALIDATING" || "${SONATYPE_RESPONSE}" == "PENDING" ]]; then + echo "Package validation is not done. Status: ${SONATYPE_RESPONSE}" + + # Exponential backoff + sleep_time=$((2 ** (i - 1))) + echo "Next retry in ${sleep_time} seconds..." + sleep "${sleep_time}" + + elif [[ "${SONATYPE_RESPONSE}" == "VALIDATED" ]]; then + echo "Package successfully validated." + exit 0 + + else + echo "Unknown response from Sonatype: ${SONATYPE_RESPONSE}" + echo "Full payload:" + echo "${RESPONSE}" + fi + done + + echo "Validation timed out after ${NUMBER_OF_CHECKS} attempts." + exit 1 + + # Peculating up the maven central release id + - name: Maven Central release id + working-directory: ${{ inputs.target-folder }} + id: get-maven-central-release-id + shell: bash + run: | + echo "maven-central-release-id=${{ steps.stage-release.outputs.deployment-id }}" >> $GITHUB_OUTPUT diff --git a/.github/actions/stage-release-artifacts/action.yml b/.github/actions/stage-release-artifacts/action.yml new file mode 100644 index 0000000..734456a --- /dev/null +++ b/.github/actions/stage-release-artifacts/action.yml @@ -0,0 +1,148 @@ +name: Stage artifacts for release +description: "Prepare artifacts for publish to various portals" + +inputs: + jfrog-platform-url: + description: "JFrog platform URL" + required: false + default: https://aerospike.jfrog.io + oidc-provider: + description: "OIDC provider name" + required: true + oidc-audience: + description: "ODIC audience" + required: true + staging-folder: + description: "Staging folders. Input should be a JSON array of staging folders" + required: false + default: '["staging","github","sonatype"]' + target-repository: + description: "Target repository in JFROG" + required: false + default: database-maven-dev-local + build-name-number: + description: "Build name and number" + required: true + artifact-version: + description: "Artifact version" + required: true + +runs: + using: "composite" + steps: + - name: Debug stage artifact to github + shell: bash + run: | + echo "${{ inputs.jfrog-platform-url }}" + echo "${{ inputs.artifact-id }}" + echo "${{ inputs.staging-folder }}" + echo "first element ${{ fromJson(inputs.staging-folder)[0] }}" + echo "second element ${{ fromJson(inputs.staging-folder)[1] }}" + echo "third element ${{ fromJson(inputs.staging-folder)[2] }}" + echo "${{ inputs.target-repository }}" + echo "${{ inputs.build-name-number }}" + echo "${{ inputs.artifact-version }}" + + - name: Setup jfrog shell + uses: step-security/setup-jfrog-cli@a6b41f8338bea0983ddff6bd4ede7d2dcd81e1fa # v4.8.1 + env: + JF_URL: ${{ inputs.jfrog-platform-url }} + JF_PROJECT: database + with: + oidc-provider-name: ${{ inputs.oidc-provider }} + oidc-audience: ${{ inputs.oidc-audience }} + + - name: Get info + shell: bash + id: get-build-info + run: | + INPUT=${{ inputs.build-name-number }} + + BUILD_NAME="${INPUT%/*}" # Getting the build name + BUILD_NUMBER="${INPUT#*/}" # Getting build number + + BUILD_INFO=$(jf rt curl "/api/build/${BUILD_NAME}/${BUILD_NUMBER}?project=database" | jq -c ".") + echo build-info="${BUILD_INFO}" >> $GITHUB_OUTPUT + + - name: Get build name + shell: bash + id: get-build-name + run: | + echo build-name=$(echo '${{ steps.get-build-info.outputs.build-info }}' | jq -r '.buildInfo.name') >> $GITHUB_OUTPUT + + - name: Get artifact build info + shell: bash + id: get-artifact-build-info + run: | + BUILD_INFO='${{ steps.get-build-info.outputs.build-info }}' + + ARTIFACT_BUILD_ID=$(echo "${BUILD_INFO}" | jq -r '.buildInfo.modules[] | select(.id | contains("-artifacts")) | .id') + echo "Artifact Build ID: ${ARTIFACT_BUILD_ID}" + + ARTIFACT_BUILD_NUMBER=$(echo "${ARTIFACT_BUILD_ID}" | cut -d'/' -f2) + echo "Artifact Build Number: ${ARTIFACT_BUILD_NUMBER}" + + ARTIFACT_BUILD_INFO=$(jf rt curl "/api/build/java-object-mapper/${ARTIFACT_BUILD_NUMBER}?project=database" | jq -c ".") + echo artifact-build-info="${ARTIFACT_BUILD_INFO}" >> $GITHUB_OUTPUT + + # Extract artifact name from the path (com/aerospike/ARTIFACT_NAME/VERSION/...) + ARTIFACT_NAME=$(echo "${ARTIFACT_BUILD_INFO}" | jq -r '.buildInfo.modules[0].artifacts[0].path' | cut -d'/' -f3) + echo "Artifact Name: ${ARTIFACT_NAME}" + echo artifact-name="${ARTIFACT_NAME}" >> $GITHUB_OUTPUT + + + - name: Create staging folders + shell: bash + run: | + echo '${{ inputs.staging-folder }}' | jq -r '.[]' | while read FOLDER; do + mkdir -p "$FOLDER" + done + + # Download artifacts from JFrog + - name: Download artifacts from JFrog + shell: bash + working-directory: ${{ fromJson(inputs.staging-folder)[0] }} + run: | + BUILD_INFO='${{ steps.get-artifact-build-info.outputs.artifact-build-info }}' + module_names=$(echo "${BUILD_INFO}" | jq -r '.buildInfo.modules[].artifacts[].path' | cut -d'/' -f3 | sort -u) + for module_name in $module_names; do + echo "Downloading artifacts for module: $module_name" + jf rt dl "database-maven-dev-local/com/aerospike/${module_name}/${{ inputs.artifact-version }}/*" --project database . + done + + - name: Debug list downloaded content + shell: bash + working-directory: ${{ fromJson(inputs.staging-folder)[0] }} + run: | + pwd + ls -laR + + # The hashes are generated when the artifacts are published (not part of this build). + # When we promote or run releases we download the artifacts and take the hashes + # which are part of the build info. We don't have sha-512 at the moment. + - name: Get hashes from build_info and generate files + shell: bash + working-directory: ${{ fromJson(inputs.staging-folder)[0] }} + run: | + BUILD_INFO='${{ steps.get-artifact-build-info.outputs.artifact-build-info }}' + + # Process artifacts from build info + echo "Creating checksums from build info..." + echo "${BUILD_INFO}" | jq -c '.buildInfo.modules[].artifacts[]' | while read -r MODULE; do + NAME=$(echo "${MODULE}" | jq -r ".name") + SHA1=$(echo "${MODULE}" | jq -r ".sha1") + SHA256=$(echo "${MODULE}" | jq -r ".sha256") + MD5=$(echo "${MODULE}" | jq -r ".md5") + + FILE_PATH=$(find . -type f -name "${NAME}" | head -n 1) + + if [[ -z "${FILE_PATH}" ]]; then + echo "Warning: Could not find file ${NAME}" + continue + fi + + echo "Creating checksums for: ${FILE_PATH}" + echo "${SHA1}" > "${FILE_PATH}.sha1" + echo "${SHA256}" > "${FILE_PATH}.sha256" + echo "${MD5}" > "${FILE_PATH}.md5" + done diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..cab1b40 --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,152 @@ +name: Build and release + +permissions: + id-token: write + packages: write + contents: read + attestations: write + +on: + workflow_call: + secrets: + GPG_SECRET_KEY: + required: true + GPG_PUBLIC_KEY: + required: true + GPG_PASS: + required: true + +jobs: + extract-version: + runs-on: ${{ vars.BUILD_CONTAINER_DISTRO_VERSION }} + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 2 + - name: Extract Version + id: extract + uses: ./.github/actions/get-version + outputs: + version: ${{ steps.extract.outputs.release-version }} + is-snapshot: ${{ steps.extract.outputs.is-snapshot }} + + build-packages: + needs: extract-version + uses: aerospike/shared-workflows/.github/workflows/reusable_execute-build.yaml@1af48ee617c69f3ee53d4d63fc933eb4b14e7a12 + with: + runs-on: ${{ vars.BUILD_CONTAINER_DISTRO_VERSION }} + jf-project: ${{ vars.JFROG_PROJECT }} + jf-build-name: java-object-mapper + jf-build-id: ${{ github.run_number }}-buildinfo-${{ needs.extract-version.outputs.version }} + gh-checkout-path: shared-workflows + gh-source-path: java-object-mapper + working-directory: java-object-mapper + gh-workflows-ref: 1af48ee617c69f3ee53d4d63fc933eb4b14e7a12 + setup-java: true + java-version: 17 + java-distribution: "temurin" + build-script: | + # Start Aerospike via the GitHub Action + echo "Starting Aerospike DB..." + docker run -d --name aerospike -p 3000:3000 aerospike/aerospike-server + sleep 5 + + # Maven build + if [ "${{ needs.extract-version.outputs.is-snapshot }}" == "true" ]; then + pom_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + mvn versions:set -DnewVersion="${pom_version}-SNAPSHOT" -DgenerateBackupPoms=false -DprocessAllModules + fi + + mvn clean install -B -U -Dgpg.skip=true + + pwd + + LOCAL_REPO=$(mvn help:evaluate -Dexpression=settings.localRepository -q -DforceStdout) + GROUP_ID=$(mvn help:evaluate -Dexpression=project.groupId -q -DforceStdout) + ARTIFACT_ID=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout) + VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + + GROUP_PATH=$(echo "$GROUP_ID" | tr '.' '/') + ARTIFACT_PATH="$LOCAL_REPO/$GROUP_PATH/$ARTIFACT_ID/$VERSION" + + echo "ARTIFACT_PATH: $ARTIFACT_PATH" + echo "Copying artifacts to ./build/" + + for file in "$ARTIFACT_PATH"/*; do + if [[ "$file" == *"-jar-with-dependencies.jar" ]] || [[ "$file" == *"_remote.repositories" ]]; then + continue + fi + cp "$file" build/ + done + + PARENT_ARTIFACT_ID=java-object-mapper + PARENT_PATH="$LOCAL_REPO/$GROUP_PATH/$PARENT_ARTIFACT_ID/$VERSION" + + echo "Parent artifact path: $PARENT_PATH" + + if [ -d "$PARENT_PATH" ]; then + echo "Copying parent POM..." + cp "$PARENT_PATH"/*.pom build/ + cp "$PARENT_PATH"/*.asc build/ 2>/dev/null || true + else + echo "WARNING: Parent POM not found in $PARENT_PATH" + fi + + ls -lar build/ + gh-artifact-directory: build + gh-artifact-name: build-artifacts-${{ needs.extract-version.outputs.version }} + gh-retention-days: 1 + jf-url: https://artifact.aerospike.io + publish-build-info: true + oidc-provider-name: ${{ vars.OIDC_PROVIDER_NAME }} + oidc-audience: ${{ vars.OIDC_AUDIENCE }} + + sign-artifacts: + needs: [ build-packages, extract-version ] + uses: aerospike/shared-workflows/.github/workflows/reusable_sign-artifacts.yaml@main + with: + gh-retention-days: 1 + gh-artifact-name: signed_build-artifacts-${{ needs.extract-version.outputs.version }} + gh-unsigned-artifacts: build-artifacts-${{ needs.extract-version.outputs.version }} + gh-workflows-ref: 1af48ee617c69f3ee53d4d63fc933eb4b14e7a12 + secrets: + gpg-private-key: ${{ secrets.GPG_SECRET_KEY }} + gpg-public-key: ${{ secrets.GPG_PUBLIC_KEY }} + gpg-key-pass: ${{ secrets.GPG_PASS }} + + deploy-artifacts: + needs: [ sign-artifacts, extract-version ] + uses: aerospike/shared-workflows/.github/workflows/reusable_deploy-artifacts.yaml@1af48ee617c69f3ee53d4d63fc933eb4b14e7a12 + with: + gh-workflows-ref: 1af48ee617c69f3ee53d4d63fc933eb4b14e7a12 + jf-project: ${{ vars.JFROG_PROJECT }} + jf-build-name: java-object-mapper + jf-metadata-build-id: ${{ github.run_number }}-buildinfo + jf-build-id: ${{ github.run_number }} + gh-artifact-name: ${{ needs.sign-artifacts.outputs.gh-artifact-name }} + jar-group-id: com.aerospike + version: ${{ needs.extract-version.outputs.version }} + gh-retention-days: 1 + oidc-provider-name: ${{ vars.OIDC_PROVIDER_NAME }} + oidc-audience: ${{ vars.OIDC_AUDIENCE }} + dry-run: false + + create-release-bundle: + needs: [ extract-version, deploy-artifacts ] + uses: aerospike/shared-workflows/.github/workflows/reusable_create-release-bundle.yaml@main + with: + jf-project: ${{ vars.JFROG_PROJECT }} + jf-build-names: java-object-mapper:${{ github.run_number }} + jf-bundle-name: java-object-mapper + gh-workflows-ref: 1af48ee617c69f3ee53d4d63fc933eb4b14e7a12 + version: ${{ needs.extract-version.outputs.version }} + oidc-provider-name: ${{ vars.OIDC_PROVIDER_NAME }} + oidc-audience: ${{ vars.OIDC_AUDIENCE }} + dry-run: false diff --git a/.github/workflows/promote-prod.yml b/.github/workflows/promote-prod.yml new file mode 100644 index 0000000..5a94e5d --- /dev/null +++ b/.github/workflows/promote-prod.yml @@ -0,0 +1,23 @@ +name: Promote to Prod + +permissions: + contents: write # For creating releases + id-token: write # For OIDC authentication + +on: + workflow_dispatch: + inputs: + build-number: + type: number + description: Build number used to build artifact to be promoted + +jobs: + promote-from-stage-to-prod: + name: Promote from stage to prod + uses: ./.github/workflows/promote.yml + with: + build-number: ${{ inputs.build-number }} + target-repository: database-maven-dev-local + target-branch: main + source-branch: stage + secrets: inherit diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml new file mode 100644 index 0000000..f427780 --- /dev/null +++ b/.github/workflows/promote.yml @@ -0,0 +1,316 @@ +name: Promote + +permissions: + contents: write # For creating releases + id-token: write # For JFrog OIDC authentication + +on: + workflow_call: + inputs: + build-number: + type: string + description: Build number used to build artifact to be promoted + target-repository: + type: string + description: Repository to promote to + target-branch: + type: string + description: Target branch to promote token + jf-target-build: + type: string + description: Target build name + default: java-object-mapper + source-branch: + type: string + required: true + description: Source branch to merge from + secrets: + AEROSPIKE_SA_CICD_USERNAME: + required: true + AEROSPIKE_SA_CICD_PASSWORD: + required: true + CLIENT_BOT_PAT: + required: true + JFROG_OIDC_PROVIDER: + required: true + JFROG_OIDC_AUDIENCE: + required: true + +jobs: + promote: + runs-on: ${{ vars.BUILD_CONTAINER_DISTRO_VERSION }} + outputs: + build-name-number: ${{ steps.get-build-name-number.outputs.build-name-numbers }} + artifact-version: ${{ steps.get-artifact-version.outputs.artifact-version }} + release-notes: ${{ steps.get-release-notes.outputs.release-notes }} + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Debug step + run: | + echo "build-number: ${{ inputs.build-number }}" + echo "target-repository: ${{ inputs.target-repository }}" + echo "target-branch: ${{ inputs.target-branch }}" + echo "jf-target-build: ${{ inputs.jf-target-build }}" + echo "source-branch: ${{ inputs.source-branch }}" + + # Setting up jfrog cli + - name: Setup jfrog shell + uses: step-security/setup-jfrog-cli@a6b41f8338bea0983ddff6bd4ede7d2dcd81e1fa # v4.8.1 + env: + JF_URL: ${{ vars.JFROG_PLATFORM_URL }} + JF_PROJECT: database + with: + oidc-provider-name: ${{ vars.OIDC_PROVIDER_NAME }} + oidc-audience: ${{ vars.OIDC_AUDIENCE }} + + # Needed since we are using actions which are part of the repository + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + # Fetch the whole history to prevent unrelated history errors + fetch-depth: "0" + ref: ${{ inputs.target-branch }} + token: ${{ secrets.CLIENT_BOT_PAT }} + + # Getting build info which will allow us to get information about the build we are promoting + - name: Get info + id: get-build-info + run: | + API_PATH="/api/build/${{ inputs.jf-target-build }}/${{ inputs.build-number }}?project=database" + status_code=$(jf rt curl "${API_PATH}" -o /dev/null -s -w "%{http_code}") + + # Exit with 1 unless the request succeeded (HTTP 200) + if [[ "$status_code" -ne 200 ]]; then + echo "ERROR: could not fetch build for build number ${{ inputs.build-number }} (HTTP $status_code)" >&2 + + exit 1 + fi + + echo build-info=$(jf rt curl "${API_PATH}" ) >> $GITHUB_OUTPUT + + # Snapshot builds are not promoted to production + - name: Check if snapshot build + id: check-snapshot + run: | + BUILD_INFO='${{ steps.get-build-info.outputs.build-info }}' + BUILD_ID=$(echo "$BUILD_INFO" | jq -r '.buildInfo.modules[] | select(.id | test("buildinfo")) | .id') + # extracting snapshot from build-nfo name. Not ideal, but we don't have is_snapshot information in JF yet + # Extract version from build ID + # Format: /-buildinfo-_hash + if [[ "$BUILD_ID" =~ buildinfo-([0-9.]+(-SNAPSHOT)?) ]]; then + VERSION="${BASH_REMATCH[1]}" + echo "Parsed version: $VERSION" + else + echo "ERROR: Could not parse version" + fi + + echo "Parsed version: $VERSION" + + if [[ "$VERSION" == *"-SNAPSHOT" ]]; then + echo "Error: Build ${{ inputs.jf-target-build }} with build number ${{ inputs.build-number }} is a SNAPSHOT build. Snapshots are not promoted to production." + # exit 1 + fi + + # Fetching commit hash from the build info. The commit hash is used for SNAPSHOT builds + - name: Get commit hash from repo + id: get-commit-hash + run: | + BUILD_INFO='${{ steps.get-build-info.outputs.build-info }}' + + # Try to get from vcs first + COMMIT_HASH=$(echo "$BUILD_INFO" | jq -r '.buildInfo.vcs[0].revision // empty') + + if [ -z "$COMMIT_HASH" ] || [ "$COMMIT_HASH" == "null" ]; then + # Fallback: extract from module ID + COMMIT_HASH=$(echo "$BUILD_INFO" | jq -r '.buildInfo.modules[0].id' | sed -E 's/.*SNAPSHOT_(.*)$/\1/') + fi + echo "commit-hash=$COMMIT_HASH" >> $GITHUB_OUTPUT + + # Fetching build name for build promotion process. The build name will contain build numbers. + # This command parses json and pulls out the build name + - name: Get build name + id: get-build-name + run: | + echo build-names=$(echo '${{ steps.get-build-info.outputs.build-info }}' | jq -r '.buildInfo.name') >> $GITHUB_OUTPUT + + - name: Debug + run: | + echo "commit-hash: '${{ steps.get-commit-hash.outputs.commit-hash }}'" + echo "build-name: '${{ steps.get-build-name.outputs.build-names }}'" + + # Promoting build to production + - name: Promote build + shell: bash + run: | + BUILD_NAME="${{ steps.get-build-name.outputs.build-names }}" + BUILD_NUMBER="${{ inputs.build-number }}" + + if [ -z "$BUILD_NAME" ]; then + echo "Error: Missing build name" + exit 1 + fi + + echo "Promoting build '$BUILD_NAME' (build #$BUILD_NUMBER) to ${{ inputs.target-repository }}" + jf rt build-promote --copy=true "$BUILD_NAME" "$BUILD_NUMBER" "${{ inputs.target-repository }}" + + # Fetching release notes from commit logs + - name: Generate release notes + id: get-release-notes + run: | + RELEASE_NOTES=$(git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"- %s") + echo "release-notes<> $GITHUB_OUTPUT + echo "${RELEASE_NOTES}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Debug print release notes + run: | + echo "Changes for release ${{ steps.get-artifact-version.outputs.artifact-version }}" + echo "${{ steps.get-release-notes.outputs.release-notes }}" + + # Fast forward the target branch to the source branch. + # - name: Fast forward + # shell: bash + # run: | + # # Making sure we have latest history in place to be able to fast forward merge + # git status + # git checkout ${{ inputs.source-branch }} + # git checkout ${{ inputs.target-branch }} + # git merge --ff-only ${{ inputs.source-branch }} + # + # # Adding commit message for promotion + # - name: Add tagging message + # uses: stefanzweifel/git-auto-commit-action@v4 + # with: + # commit_message: "Promote to prod [skip ci]" + # commit_author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + # tagging_message: Promote to PROD + # branch: ${{ inputs.target-branch }} + + # Pushing changes to remote head branch + # This needs updating to create PR instead of direct push + # - name: Upload changes to remote head branch + # shell: bash + # run: git push + + # Passing build-name-numbers and artifact-version to the next job + - name: Get build name with build number + id: get-build-name-number + run: | + echo "DEBUG: inputs.build-number='${{ inputs.build-number }}'" + BUILD_NAME=$(echo '${{ steps.get-build-info.outputs.build-info }}' | jq -r '.buildInfo.name') + BUILD_NUMBER="${{ inputs.build-number }}" + echo "build-name-numbers=${BUILD_NAME}/${BUILD_NUMBER}" >> $GITHUB_OUTPUT + + - name: Get artifact version from JFrog build info + id: get-artifact-version + run: | + BUILD_INFO='${{ steps.get-build-info.outputs.build-info }}' + + VERSION=$(echo "$BUILD_INFO" | jq -r ' + .buildInfo.modules[] + | select(.id | test("-buildinfo-")) + | .id + | capture("-buildinfo-(?[^_]+)") + | .ver + ') + + if [[ -z "$VERSION" ]]; then + echo "ERROR: Could not parse artifact version from JFrog build info!" + exit 1 + fi + + echo "artifact-version=$VERSION" >> $GITHUB_OUTPUT + + - name: Debug show 'build-name-numbers' and 'artifact-version' + run: | + echo "build-name-number: ${{ steps.get-build-name-number.outputs.build-name-numbers }}" + echo "artifact-version: ${{ steps.get-artifact-version.outputs.artifact-version }}" + + - name: Release debug + run: | + GIT_NOTES='${{ steps.get-release-notes.outputs.release-notes }}' + RELEASE_VERSION='${{steps.get-artifact-version.outputs.artifact-version }}' + echo "${RELEASE_VERSION}" + echo "${GIT_NOTES}" + + publish-release-sonatype: + runs-on: ${{ vars.BUILD_CONTAINER_DISTRO_VERSION }} + needs: promote + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + # Needed since we are using actions which are part of the repository + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.CLIENT_BOT_PAT }} + + # Pulling release from JFROG + - uses: ./.github/actions/stage-release-artifacts + with: + oidc-provider: ${{ secrets.JFROG_OIDC_PROVIDER }} + oidc-audience: ${{ secrets.JFROG_OIDC_AUDIENCE }} + target-repository: ${{ inputs.target-repository }} + build-name-number: ${{ needs.promote.outputs.build-name-number }} + artifact-version: ${{ needs.promote.outputs.artifact-version }} + + # Publishing release to Maven Central + - uses: ./.github/actions/publish-to-sonatype + id: publish-to-sonatype + with: + build-name-number: ${{ needs.promote.outputs.build-name-number }} + artifact-version: ${{ needs.promote.outputs.artifact-version }} + publish-user: ${{ secrets.AEROSPIKE_SA_CICD_USERNAME }} + publish-password: ${{ secrets.AEROSPIKE_SA_CICD_PASSWORD }} + validation-max-number-checks: ${{ vars.VALIDATION_MAX_NUMBER_CHECKS }} + sonatype-domain-name: ${{ vars.SONATYPE_DOMAIN_NAME }} + + # Publishing release information to JFrog for aggregation. This information is used later for final release step + # (acknowledgement) to Maven Central + - uses: ./.github/actions/publish-build-info-to-jfrog + with: + oidc-provider: ${{ secrets.JFROG_OIDC_PROVIDER }} + oidc-audience: ${{ secrets.JFROG_OIDC_AUDIENCE }} + build-path: ${{ needs.promote.outputs.build-name-number }} + variables: '{"SONATYPE_STAGING_BUILD_ID":"${{ steps.publish-to-sonatype.outputs.maven-central-release-id }}"}' + + publish-release-github: + runs-on: ${{ vars.BUILD_CONTAINER_DISTRO_VERSION }} + needs: [ promote, publish-release-sonatype ] + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + ref: ${{ inputs.target-branch }} + token: ${{ secrets.CLIENT_BOT_PAT }} + + - uses: ./.github/actions/stage-release-artifacts + with: + oidc-provider: ${{ secrets.JFROG_OIDC_PROVIDER }} + oidc-audience: ${{ secrets.JFROG_OIDC_AUDIENCE }} + target-repository: ${{ inputs.target-repository }} + build-name-number: ${{ needs.promote.outputs.build-name-number }} + artifact-version: ${{ needs.promote.outputs.artifact-version }} + staging-folder: '["staging","github"]' + + # Publishing release to GitHub + # Note: this action knows how to process json internally. It expects inputs to be in json format + - uses: ./.github/actions/publish-to-github + with: + release-notes: ${{ needs.promote.outputs.release-notes }} + github-token: ${{ secrets.CLIENT_BOT_PAT }} + build-name-number: ${{ needs.promote.outputs.build-name-numbers }} + artifact-version: ${{ needs.promote.outputs.artifact-version }} diff --git a/.github/workflows/push-to-stage.yml b/.github/workflows/push-to-stage.yml new file mode 100644 index 0000000..8c366d1 --- /dev/null +++ b/.github/workflows/push-to-stage.yml @@ -0,0 +1,22 @@ +name: Java Build & Release +permissions: + id-token: write + packages: write + contents: read + attestations: write + +on: + push: + branches: + - stage + tags: + - 'v*' # Matches v5.4.0 + - '[0-9]+.[0-9]+.[0-9]+' # Matches 5.4.0 + - '[0-9]+.[0-9]+.[0-9]+-rc*' # Matches 5.4.0-rc1 + workflow_dispatch: + +jobs: + build-stage: + name: Build stage + uses: ./.github/workflows/build-release.yml + secrets: inherit diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..2024714 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,22 @@ +name: Release Drafter + +on: + push: + branches: + - main + pull_request: + types: [ opened, reopened, synchronize ] + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + + - name: Release-drafter + uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/required-labels.yml b/.github/workflows/required-labels.yml new file mode 100644 index 0000000..70dd3a0 --- /dev/null +++ b/.github/workflows/required-labels.yml @@ -0,0 +1,22 @@ +# https://github.com/mheap/github-action-required-labels +name: Pull Request Required Labels +on: + pull_request: + types: [ opened, labeled, unlabeled, synchronize ] +jobs: + label: + if: github.event.pull_request.state == 'open' + runs-on: ubuntu-latest + name: Verify Pull Request has labels + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + + - name: Required-labels + uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1 + with: + mode: minimum + count: 1 + labels: "breaking, breaks, feature, enhancement, fix, bugfix, bug, chore, cleanup, polish, dependencies, documentation" diff --git a/.github/workflows/sonatype-approve-or-delete.yml b/.github/workflows/sonatype-approve-or-delete.yml new file mode 100644 index 0000000..f786f56 --- /dev/null +++ b/.github/workflows/sonatype-approve-or-delete.yml @@ -0,0 +1,70 @@ +name: Approve or Delete Sonatype Deployment +run-name: "Sonatype Deployment: ${{ inputs.action }} ${{ inputs.stage-release-id }}" +# https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment + +permissions: { } + +on: + workflow_dispatch: + inputs: + stage-release-id: + description: "The Sonatype stage-release-id (UUID)" + required: true + type: string + action: + description: "Action to perform on the deployment: POST = approve, DELETE = discard" + required: true + type: choice + options: + - POST + - DELETE + +jobs: + sonatype-deployment-action: + runs-on: ubuntu-latest + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Debug Inputs + run: | + echo "Action: ${{ inputs.action }}" + echo "Stage Release ID: ${{ inputs.stage-release-id }}" + + - name: Build Authorization Header + id: auth + run: | + TOKEN=$(printf "%s:%s" "${{ secrets.AEROSPIKE_SA_CICD_USERNAME }}" "${{ secrets.AEROSPIKE_SA_CICD_PASSWORD }}" | base64) + echo "token=$TOKEN" >> $GITHUB_OUTPUT + + - name: Approve/Delete deployment + shell: bash + run: | + ACTION="${{ inputs.action }}" + ID="${{ inputs.stage-release-id }}" + TOKEN="${{ steps.auth.outputs.token }}" + DOMAIN="${{ vars.SONATYPE_DOMAIN_NAME }}" + + echo "Running Sonatype Deployment API call..." + echo "Endpoint: ${DOMAIN}/api/v1/publisher/deployment/${ID}" + + ACTION=$(echo "${{ inputs.action }}" | tr '[:lower:]' '[:upper:]') + + if [[ "$ACTION" != "POST" && "$ACTION" != "DELETE" ]]; then + echo "ERROR: Invalid action '$ACTION' (expected POST or DELETE)" + exit 1 + fi + + echo "→ Executing $ACTION for deployment ID ${ID}" + + RESPONSE=$(curl --fail --show-error --silent \ + --request "$ACTION" \ + --header "Authorization: Bearer ${TOKEN}" \ + "${DOMAIN}/api/v1/publisher/deployment/${ID}" + ) + + echo "Response from Sonatype:" + echo "$RESPONSE" From 1766d84eedd338aa7cd3401081ebd7fe339a357e Mon Sep 17 00:00:00 2001 From: Andrey G Date: Fri, 20 Mar 2026 17:40:42 +0100 Subject: [PATCH 2/3] Remove release-drafter --- .github/workflows/release-drafter.yml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 2024714..0000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Release Drafter - -on: - push: - branches: - - main - pull_request: - types: [ opened, reopened, synchronize ] - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 - with: - egress-policy: audit - - - name: Release-drafter - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From d4a7ff2b9194b43caeb353e81e810003059f2995 Mon Sep 17 00:00:00 2001 From: Andrey G Date: Fri, 20 Mar 2026 17:42:23 +0100 Subject: [PATCH 3/3] Potential fix for code scanning alert no. 10: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/required-labels.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/required-labels.yml b/.github/workflows/required-labels.yml index 70dd3a0..6ac432b 100644 --- a/.github/workflows/required-labels.yml +++ b/.github/workflows/required-labels.yml @@ -1,5 +1,7 @@ # https://github.com/mheap/github-action-required-labels name: Pull Request Required Labels +permissions: + contents: read on: pull_request: types: [ opened, labeled, unlabeled, synchronize ]