{"id":28708309,"url":"https://github.com/pmd/build-tools","last_synced_at":"2025-06-14T18:10:36.424Z","repository":{"id":31397603,"uuid":"34960791","full_name":"pmd/build-tools","owner":"pmd","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-06T19:31:17.000Z","size":924,"stargazers_count":4,"open_issues_count":5,"forks_count":11,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-06-08T12:35:14.379Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pmd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-05-02T19:36:01.000Z","updated_at":"2025-06-06T19:31:20.000Z","dependencies_parsed_at":"2024-01-23T21:17:26.838Z","dependency_job_id":"61f45592-033c-4555-a1af-181cac8c8569","html_url":"https://github.com/pmd/build-tools","commit_stats":{"total_commits":407,"total_committers":9,"mean_commits":45.22222222222222,"dds":"0.10073710073710074","last_synced_commit":"d19662661e942563ecc2af26cd67473c78bb86e0"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/pmd/build-tools","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmd%2Fbuild-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmd%2Fbuild-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmd%2Fbuild-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmd%2Fbuild-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pmd","download_url":"https://codeload.github.com/pmd/build-tools/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmd%2Fbuild-tools/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259860437,"owners_count":22922989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-14T18:10:33.374Z","updated_at":"2025-06-14T18:10:36.411Z","avatar_url":"https://github.com/pmd.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://github.com/pmd/build-tools/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/pmd/build-tools/actions/workflows/build.yml)\n\n# PMD build tools\n\nArtifact containing configuration data and scripts to build and release pmd/pmd from source.\n\n**Note:** This project does not use semantic versioning.\n\n-----\n\n*   [build-env](#build-env)\n*   [scripts](#scripts)\n    *   [Overview](#overview)\n    *   [Usage](#usage)\n        *   [inc/fetch_ci_scripts.bash](#incfetch_ci_scriptsbash)\n        *   [inc/log.bash](#inclogbash)\n        *   [inc/utils.bash](#incutilsbash)\n        *   [inc/openjdk.bash](#incopenjdkbash)\n        *   [inc/github-releases-api.bash](#incgithub-releases-apibash)\n        *   [inc/setup-secrets.bash](#incsetup-secretsbash)\n        *   [inc/sourceforge-api.bash](#incsourceforge-apibash)\n        *   [inc/maven.bash](#incmavenbash)\n        *   [inc/pmd-code-api.bash](#incpmd-code-apibash)\n        *   [check-environment.sh](#check-environmentsh)\n*   [files](#user-content-files)\n    *   [private-env.asc](#private-envasc)\n    *   [release-signing-key-D0BF1D737C9A1C22.asc](#release-signing-key-d0bf1d737c9a1c22asc)\n    *   [pmd.github.io_deploy_key.asc](#pmdgithubio_deploy_keyasc)\n    *   [pmd-eclipse-plugin-p2-site_deploy_key.asc](#pmd-eclipse-plugin-p2-site_deploy_keyasc)\n    *   [pmd-code.org_deploy_key.asc](#pmdcodeorg_deploy_keyasc)\n    *   [web.sourceforge.net_deploy_key.asc](#websourceforgenet_deploy_keyasc)\n    *   [maven-settings.xml](#maven-settingsxml)\n*   [Testing](#testing)\n*   [Miscellaneous](#miscellaneous)\n    *   [Nexus Staging Maven Plugin](#nexus-staging-maven-plugin)\n    *   [Remote debugging](#remote-debugging)\n    *   [Intermittent connection resets or timeouts while downloading dependencies from maven central](#intermittent-connection-resets-or-timeouts-while-downloading-dependencies-from-maven-central)\n\n## build-env\n\nUbuntu Linux based, same as github actions runner, see [Runner Images](https://github.com/actions/runner-images).\nIt can be used to test the scripts and perform the builds without github actions.\n\nOnce build the docker container: \n\n    $ docker build \\\n        --tag pmd-build-env \\\n        build-env\n\nThis is only needed once. This builds the image, from which new containers can be started.\nA new image needs to be created, if e.g. ubuntu is to be updated or any other program.\n\nThen run the container, mounting in the pmd-build-tools repo as a volume:\n\n    $ docker run \\\n        --interactive \\\n        --tty \\\n        --name pmd-build-env \\\n        --mount type=bind,source=\"$(pwd)\",target=/workspaces/pmd/build-tools \\\n        pmd-build-env:latest\n\nYou're now in a shell inside the container. You can start a second shell in the same container:\n\n    $ docker exec \\\n        --interactive \\\n        --tty pmd-build-env \\\n        /bin/bash --login\n\nThe container is stopped, if the first shell is exited. To start the same container again:\n\n    $ docker start \\\n        --interactive \\\n        pmd-build-env\n\nTo list the running and stopped containers:\n\n    $ docker ps \\\n        --all \\\n        --filter name=pmd-build-env\n\nIf not needed anymore, you can destroy the container:\n\n    $ docker rm pmd-build-env\n\n## scripts\n\n### Overview\n\nScripts are stored in `scripts` subfolder. There are two types:\n\n1. Shell scripts to be executed as programs. The extension is \".sh\".\n2. Library functions to be included by those scripts. The extension is \".bash\" and they are\n   located in `scripts/inc`.\n\nAll scripts are bash scripts.\n\nThe shell scripts might depend on one or more library scripts. They need to fetch their dependencies\nbefore doing any work. This is always done in the function \"fetch_ci_scripts()\". The global variable\n`PMD_CI_SCRIPTS_URL` is used as the base url to fetch the scripts.\n\nLibrary functions may depend on other library functions as well.\n\nNamespaces: Exported global variables use the prefix `PMD_CI_`. Functions of a library use the same\ncommon prefix starting with `pmd_ci_` followed by the library name, followed by the actual function name.\n\nUse [shellcheck](https://www.shellcheck.net/) to verify the scripts.\n\n### Usage\n\n#### inc/fetch_ci_scripts.bash\n\nLittle helper script to download dependencies.\n\nThe only function is `fetch_ci_scripts`.\n\nUse it in other scripts like this:\n\n```\nMODULE=\"my-library\"\nSCRIPT_INCLUDES=\"log.bash\"\n# shellcheck source=inc/fetch_ci_scripts.bash\nsource \"$(dirname \"$0\")/inc/fetch_ci_scripts.bash\" \u0026\u0026 fetch_ci_scripts\n\n# other parts of your script\n```\n\nThat's the only script, that needs to be copied and existing before. Only with this script, the\nother scripts can be fetched as needed.\n\nUsed global vars:\n\n*   PMD_CI_SCRIPTS_URL - defaults to https://raw.githubusercontent.com/pmd/build-tools/main/scripts\n\n#### inc/log.bash\n\nNamespace: pmd_ci_log\n\nFunctions:\n\n*   pmd_ci_log_error\n*   pmd_ci_log_info\n*   pmd_ci_log_success\n*   pmd_ci_log_debug\n\nVars:\n\n*   PMD_CI_LOG_COL_GREEN\n*   PMD_CI_LOG_COL_RED\n*   PMD_CI_LOG_COL_RESET\n*   PMD_CI_LOG_COL_YELLOW\n\nUsed global vars:\n\n*   PMD_CI_DEBUG: true|false.\n\n#### inc/utils.bash\n\nNamespace: pmd_ci_utils\n\nFunctions:\n\n*   pmd_ci_utils_get_os: returns one of \"linux\", \"macos\", \"windows\"\n*   pmd_ci_utils_determine_build_env. Sets many variables, e.g. GITHUB_BASE_URL, PMD_CI_IS_FORK, ...\n*   pmd_ci_utils_is_fork_or_pull_request\n*   pmd_ci_utils_fetch_ci_file\n\nUsed global vars:\n\n*   PMD_CI_SCRIPTS_URL: This is the base url from where to fetch additional files. For setting up\n    secrets, the file `private-env.asc` is fetched from there.\n    Defaults to https://raw.githubusercontent.com/pmd/build-tools/main/scripts\n    The files are fetched from the sub directory \"files\".\n\nTest with: `bash -c \"source inc/utils.bash; pmd_ci_utils_get_os\" $(pwd)/test.sh`\n\n#### inc/openjdk.bash\n\nNamespace: pmd_ci_openjdk\n\nFunctions:\n\n*   pmd_ci_openjdk_install_adoptium. Usage e.g. `pmd_ci_openjdk_install_adoptium 11`\n    Supports also EA builds, e.g. `pmd_ci_openjdk_install_adoptium 16-ea`\n*   pmd_ci_openjdk_install_zuluopenjdk. Usage e.g. `pmd_ci_openjdk_install_zuluopenjdk 7`\n*   pmd_ci_openjdk_setdefault. Usage e.g. `pmd_ci_openjdk_setdefault 11`\n\nTest with: `bash -c \"source inc/openjdk.bash; pmd_ci_openjdk_install_adoptium 11\" $(pwd)/test.sh`\n\n#### inc/github-releases-api.bash\n\nNamespace: pmd_ci_gh_releases\n\nFunctions:\n\n*   pmd_ci_gh_releases_createDraftRelease\n*   pmd_ci_gh_releases_getLatestDraftRelease\n*   pmd_ci_gh_releases_deleteRelease\n*   pmd_ci_gh_releases_getIdFromData\n*   pmd_ci_gh_releases_getTagNameFromData\n*   pmd_ci_gh_releases_uploadAsset\n*   pmd_ci_gh_releases_updateRelease\n*   pmd_ci_gh_releases_publishRelease\n\n\nUsed global vars:\n\n*   GITHUB_TOKEN - this is the default github actions token\n*   GITHUB_BASE_URL\n\nTest with: \n\n```\nbash -c 'set -x ; \\\n         export GITHUB_TOKEN=.... ; \\\n         export GITHUB_BASE_URL=https://api.github.com/repos/pmd/pmd ; \\\n         export PMD_CI_DEBUG=false ; \\\n         source inc/github-releases-api.bash ; \\\n         pmd_ci_gh_releases_createDraftRelease \"pmd_releases/6.30.0\" \"d2e4fb4ca370e7d5612dcc96fb74c29767a7671e\" ; \\\n         sleep 1; \\\n         pmd_ci_gh_releases_getLatestDraftRelease ; \\\n         export therelease=\"$RESULT\" ; \\\n         pmd_ci_gh_releases_uploadAsset \"$therelease\" \"inc/github-releases-api.bash\"\n         export body='\\''the body \\\n         line2'\\'' ; \\\n         pmd_ci_gh_releases_updateRelease \"$therelease\" \"test release\" \"$body\" ; \\\n         #pmd_ci_gh_releases_deleteRelease \"$therelease\" ; \\\n         #pmd_ci_gh_releases_publishRelease \"$therelease\" ; \\\n         ' $(pwd)/test.sh\n```\n\n#### inc/setup-secrets.bash\n\nNamespace: pmd_ci_setup_secrets\n\nFunctions:\n\n*   pmd_ci_setup_secrets_private_env\n*   pmd_ci_setup_secrets_gpg_key\n*   pmd_ci_setup_secrets_ssh\n\nUsed global vars:\n\n*   PMD_CI_SECRET_PASSPHRASE: This is provided as a github secret\n    (`PMD_CI_SECRET_PASSPHRASE: ${{ secrets.PMD_CI_SECRET_PASSPHRASE }}`) in github actions workflow.\n    It is used to decrypt further secrets used by other scripts (github releases api, ...)\n*   PMD_CI_GPG_PRIVATE_KEY: The exported private key used for release signing, provided as a secret\n    (`PMD_CI_GPG_PRIVATE_KEY: ${{ secrets.PMD_CI_GPG_PRIVATE_KEY }}`) in github actions workflow.\n\nTest with:\n\n```\nbash -c 'set -e; \\\n         export PMD_CI_SECRET_PASSPHRASE=.... ; \\\n         export PMD_CI_GPG_PRIVATE_KEY=.... ; \\\n         export PMD_CI_DEBUG=false ; \\\n         source inc/setup-secrets.bash ; \\\n         pmd_ci_setup_secrets_private_env ; \\\n         pmd_ci_setup_secrets_gpg_key ; \\\n         pmd_ci_setup_secrets_ssh ; \\\n         # env # warning: prints out the passwords in clear! ; \\\n         ' $(pwd)/test.sh\n```\n\n#### inc/sourceforge-api.bash\n\nNamespace: pmd_ci_sourceforge\n\nFunctions:\n\n*   pmd_ci_sourceforge_uploadReleaseNotes\n*   pmd_ci_sourceforge_uploadFile\n*   pmd_ci_sourceforge_selectDefault\n*   pmd_ci_sourceforge_rsyncSnapshotDocumentation\n*   pmd_ci_sourceforge_createDraftBlogPost\n*   pmd_ci_sourceforge_publishBlogPost\n\nUsed global vars:\n\n*   PMD_SF_USER\n*   PMD_SF_APIKEY\n*   PMD_SF_BEARER_TOKEN\n\nTest with:\n\n```\nbash -c 'set -e; \\\n         export PMD_CI_SECRET_PASSPHRASE=.... ; \\\n         export PMD_CI_DEBUG=false ; \\\n         source inc/setup-secrets.bash ; \\\n         source inc/sourceforge-api.bash ; \\\n         pmd_ci_setup_secrets_private_env ; \\\n         #pmd_ci_setup_secrets_gpg_key ; \\\n         pmd_ci_setup_secrets_ssh ; \\\n         pmd_ci_sourceforge_uploadReleaseNotes \"pmd/Release-Script-Test\" \"Testing release notes\" ; \\\n         echo \"test file\" \u003e \"release-test-file.txt\" ; \\\n         pmd_ci_sourceforge_uploadFile \"pmd/Release-Script-Test\" \"release-test-file.txt\" ; \\\n         rm \"release-test-file.txt\" ; \\\n         pmd_ci_sourceforge_selectDefault \"Release-Script-Test\" ; \\\n         mkdir -p \"docs/pmd-doc-Release-Script-Test/\" ; \\\n         echo \"test-file\" \u003e \"docs/pmd-doc-Release-Script-Test/release-test.txt\" ; \\\n         pmd_ci_sourceforge_rsyncSnapshotDocumentation \"Release-Script-Test\" \"test-Release-Script-Test\" ; \\\n         rm \"docs/pmd-doc-Release-Script-Test/release-test.txt\"; rmdir \"docs/pmd-doc-Release-Script-Test\"; rmdir \"docs\" ; \\\n         pmd_ci_sourceforge_createDraftBlogPost \"draft post 1\" \"text with labels\" \"label1,label2\" ; \\\n         blog=\"${RESULT}\" ; \\\n         echo \"URL: ${blog}\" ; \\\n         pmd_ci_sourceforge_createDraftBlogPost \"draft post 2\" \"text without labels\" ; \\\n         blog=\"${RESULT}\" ; \\\n         echo \"URL: ${blog}\" ; \\\n         #pmd_ci_sourceforge_publishBlogPost \"${blog}\" ; \\\n         ' $(pwd)/test.sh\n```\n\nNote that \"pmd_ci_sourceforge_selectDefault\" won't be successful, because the file to be selected as default\ndoesn't exist.\n\nDon't forget to delete \u003chttps://sourceforge.net/projects/pmd/files/pmd/Release-Script-Test\u003e and\n\u003chttps://pmd.sourceforge.io/test-Release-Script-Test\u003e after the test.\n\nAnd also the created blog posts under \u003chttps://sourceforge.net/p/pmd/news/\u003e.\n\n#### inc/maven.bash\n\nNamespace: pmd_ci_maven\n\nFunctions:\n\n*   pmd_ci_maven_setup_settings\n*   pmd_ci_maven_get_project_version: exports PMD_CI_MAVEN_PROJECT_VERSION\n*   pmd_ci_maven_get_project_name\n*   pmd_ci_maven_verify_version\n*   pmd_ci_maven_display_info_banner\n*   pmd_ci_maven_isSnapshotBuild\n*   pmd_ci_maven_isReleaseBuild\n\nUsed global vars:\n\n*   PMD_CI_BRANCH\n*   PMD_CI_TAG\n\nExported global vars:\n\n*   PMD_CI_MAVEN_PROJECT_VERSION\n\nTest with:\n\n```\nbash -c 'set -e; \\\n         export PMD_CI_SECRET_PASSPHRASE=.... ; \\\n         export PMD_CI_DEBUG=true ; \\\n         source inc/maven.bash ; \\\n         pmd_ci_maven_setup_settings ; \\\n         cd .. ; \\\n         pmd_ci_maven_get_project_version ; \\\n         echo \"version: $RESULT\" ; \\\n         pmd_ci_maven_get_project_name ; \\\n         echo \"name: $RESULT\" ; \\\n         PMD_CI_MAVEN_PROJECT_VERSION=\"1.2.3-SNAPSHOT\" ; \\\n         PMD_CI_BRANCH=\"test-branch\" ; \\\n         pmd_ci_maven_verify_version ; \\\n         unset PMD_CI_BRANCH ; \\\n         PMD_CI_TAG=\"test-tag\" ; \\\n         PMD_CI_MAVEN_PROJECT_VERSION=\"1.2.3\" ; \\\n         pmd_ci_maven_verify_version ; \\\n         pmd_ci_maven_display_info_banner ; \\\n         pmd_ci_maven_isReleaseBuild \u0026\u0026 echo \"release build\" ; \\\n         PMD_CI_MAVEN_PROJECT_VERSION=\"1.2.3-SNAPSHOT\" ; \\\n         unset PMD_CI_TAG ; \\\n         PMD_CI_BRANCH=\"test-branch\" ; \\\n         pmd_ci_maven_isSnapshotBuild \u0026\u0026 echo \"snapshot build\" ; \\\n         ' $(pwd)/test.sh\n```\n\n#### inc/pmd-code-api.bash\n\nNamespace: pmd_ci_pmd_code\n\nFunctions:\n\n*   pmd_ci_pmd_code_uploadFile\n*   pmd_ci_pmd_code_uploadZipAndExtract\n*   pmd_ci_pmd_code_removeFolder\n*   pmd_ci_pmd_code_createSymlink\n\nUsed global vars:\n\nTest with:\n\n```\nbash -c 'set -e; \\\n         export PMD_CI_SECRET_PASSPHRASE=.... ; \\\n         export PMD_CI_DEBUG=true ; \\\n         source inc/setup-secrets.bash ; \\\n         source inc/pmd-code-api.bash ; \\\n         pmd_ci_setup_secrets_private_env ; \\\n         pmd_ci_setup_secrets_ssh ; \\\n         echo \"test file\" \u003e \"test-file-for-upload.txt\" ; \\\n         zip \"test-zip.zip\" \"test-file-for-upload.txt\" ; \\\n         pmd_ci_pmd_code_uploadFile \"/httpdocs/test-folder\" \"test-file-for-upload.txt\" ; \\\n         echo \"test file\" \u003e \"test-file-for-upload.txt\" ; \\\n         pmd_ci_pmd_code_uploadZipAndExtract \"/httpdocs/test-folder2\" \"test-zip.zip\" ; \\\n         rm \"test-zip.zip\" \"test-file-for-upload.txt\" ; \\\n         pmd_ci_pmd_code_createSymlink \"/httpdocs/test-folder\" \"/httpdocs/test-folder3\" ; \\\n         pmd_ci_pmd_code_removeFolder \"/httpdocs/test-folder\" ; \\\n         pmd_ci_pmd_code_removeFolder \"/httpdocs/test-folder2\" ; \\\n         pmd_ci_pmd_code_removeFolder \"/httpdocs/test-folder3\" ; \\\n         ' $(pwd)/test.sh\n```\n\n\n#### check-environment.sh\n\nUsage in github actions step:\n\n```yaml\n- name: Setup Environment\n  shell: bash\n  run: |\n    echo \"LANG=en_US.UTF-8\" \u003e\u003e $GITHUB_ENV\n    echo \"MAVEN_OPTS=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3\" \u003e\u003e $GITHUB_ENV\n    echo \"PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/main/scripts\" \u003e\u003e $GITHUB_ENV\n- name: Check Environment\n  shell: bash\n  run: |\n    f=check-environment.sh; \\\n    mkdir -p .ci \u0026\u0026 \\\n    ( [ -e .ci/$f ] || curl -sSL \"${PMD_CI_SCRIPTS_URL}/$f\" \u003e \".ci/$f\" ) \u0026\u0026 \\\n    chmod 755 .ci/$f \u0026\u0026 \\\n    .ci/$f\n```\n\nThe script exits with code 0, if everything is fine and with 1, if one or more problems have been detected.\nThus it can fail the build.\n\n## files\n\n### private-env.asc\n\nThis file contains the encrypted secrets used during the build, e.g. github tokens, passwords for sonatype, ...\n\nIt is encrypted with the password in `PMD_CI_SECRET_PASSPHRASE`.\n\nHere's a template for the file:\n\n```\n#\n# private-env\n#\n# encrypt:\n# printenv PMD_CI_SECRET_PASSPHRASE | gpg --symmetric --cipher-algo AES256 --batch --armor --passphrase-fd 0 private-env\n#\n# decrypt:\n# printenv PMD_CI_SECRET_PASSPHRASE | gpg --batch --decrypt --passphrase-fd 0 --output private-env private-env.asc\n#\n\nexport PMD_CI_SECRET_PASSPHRASE=...\n\n# CI_DEPLOY_USERNAME - the user which can upload net.sourceforge.pmd:* to https://oss.sonatype.org/\n# CI_DEPLOY_PASSWORD\nexport CI_DEPLOY_USERNAME=...\nexport CI_DEPLOY_PASSWORD=...\n\nexport PMD_SF_USER=...\n# https://sourceforge.net/p/forge/documentation/Using%20the%20Release%20API/\nexport PMD_SF_APIKEY=...\n# https://sourceforge.net/p/forge/documentation/Allura%20API/ (blog, wiki, ...)\n# https://sourceforge.net/auth/oauth/\nexport PMD_SF_BEARER_TOKEN=...\n\n# https://sonarcloud.io/dashboard?id=net.sourceforge.pmd%3Apmd\n# The token can be configured here: https://sonarcloud.io/account/security/\nexport SONAR_TOKEN=...\n\n# https://coveralls.io/github/pmd/pmd\n# when logged in, the token is display on that page\nexport COVERALLS_REPO_TOKEN=...\n\n# for pmd-regression-tester\n# https://rubygems.org/settings/edit\nexport GEM_HOST_API_KEY=...\n\n# These are also in public-env:\nexport PMD_CI_CHUNK_TOKEN=...\n```\n\n### pmd.github.io_deploy_key.asc\n\nCreated with `ssh-keygen -t ed25519 -C \"ssh key for pmd. used for github actions to push to pmd.github.io\" -f pmd.github.io_deploy_key`.\n\nEncrypt it with PMD_CI_SECRET_PASSPHRASE:\n\n```\nprintenv PMD_CI_SECRET_PASSPHRASE | gpg --symmetric --cipher-algo AES256 --batch --armor \\\n  --passphrase-fd 0 \\\n  pmd.github.io_deploy_key\n```\n\nThe corresponding public key `pmd.github.io_deploy_key.pub` is here for convenience. It is configured as a\ndeploy key for the repository [pmd.github.io](https://github.com/pmd/pmd.github.io/settings/keys) with\nwrite access.\n\nIn order to use this key to push, you need to clone the repo with\nthis url: `git@github.com-pmd.github.io:pmd/pmd.github.io.git`.\n\n### pmd-eclipse-plugin-p2-site_deploy_key.asc\n\nCreated with `ssh-keygen -t ed25519 -C \"ssh key for pmd. used for github actions to push to pmd-eclipse-plugin-p2-site\" -f pmd-eclipse-plugin-p2-site_deploy_key`.\n\nEncrypt it with PMD_CI_SECRET_PASSPHRASE:\n\n```\nprintenv PMD_CI_SECRET_PASSPHRASE | gpg --symmetric --cipher-algo AES256 --batch --armor \\\n  --passphrase-fd 0 \\\n  pmd-eclipse-plugin-p2-site_deploy_key\n```\n\nThe corresponding public key `pmd-eclipse-plugin-p2-site_deploy_key.pub` is here for convenience.\nIt is configured as a\ndeploy key for the repository [pmd-eclipse-plugin-p2-site](https://github.com/pmd/pmd-eclipse-plugin-p2-site/settings/keys)\nwith write access.\n\nIn order to use this key to push, you need to clone the repo with\nthis url: `git@github.com-pmd-eclipse-plugin-p2-site:pmd/pmd-eclipse-plugin-p2-site.git`.\n\n### pmd-code.org_deploy_key.asc\n\nCreated with `ssh-keygen -t ed25519 -C \"ssh key for pmd. used for github actions push to pmd-code.org\" -f pmd-code.org_deploy_key`.\n\nEncrypt it with PMD_CI_SECRET_PASSPHRASE:\n\n```\nprintenv PMD_CI_SECRET_PASSPHRASE | gpg --symmetric --cipher-algo AES256 --batch --armor \\\n  --passphrase-fd 0 \\\n  pmd-code.org_deploy_key\n```\n\nThe corresponding public key `pmd-code.org_deploy_key.pub` is here for convenience.\nIt is configured in `~/.ssh/authorized_keys` on pmd@pmd-code.org.\n\n### web.sourceforge.net_deploy_key.asc\n\nCreated with `ssh-keygen -t ed25519 -C \"ssh key for pmd. used for github actions push to web.sourceforge.net\" -f web.sourceforge.net_deploy_key`.\n\nEncrypt it with PMD_CI_SECRET_PASSPHRASE:\n\n```\nprintenv PMD_CI_SECRET_PASSPHRASE | gpg --symmetric --cipher-algo AES256 --batch --armor \\\n  --passphrase-fd 0 \\\n  web.sourceforge.net_deploy_key\n```\n\nThe corresponding public key `web.sourceforge.net_deploy_key.pub` is here for convenience.\nIt is configured in for user \"PMD_SF_USER\" (see private-env)\non sourceforge: \u003chttps://sourceforge.net/auth/shell_services\u003e.\n\nNote: The same key is used to push to \"git.code.sf.net\" as user \"PMD_SF_USER\".\n\n### maven-settings.xml\n\nIt contains the credentials for uploading the artifacts to maven-central for the server `ossrh`.\nThe actual configuration comes in via environment variables: `CI_DEPLOY_USERNAME` and `CI_DEPLOY_PASSWORD`.\n\n## Testing\n\nTo test a complete build (or run it manually), you can use the docker build-env.\nThe script `create-gh-actions-env.sh` can simulate a Github Actions environment by setting up\nsome specific environment variables. With these variables set, `utils.bash/pmd_ci_utils_determine_build_env`\ncan figure out the needed information and `utils.bash/pmd_ci_utils_is_fork_or_pull_request` works.\n\nExample session for a pull request:\n\n```\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ unset PMD_CI_SECRET_PASSPHRASE\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ export PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/adangel/build-tools/gh-action-scripts/scripts\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ eval $(~/create-gh-actions-env.sh pull_request adangel/build-tools gh-actions-scripts)\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ .ci/build.sh\n...\n```\n\nExample session for a forked build (a build executing on a forked repository):\n\n```\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ unset PMD_CI_SECRET_PASSPHRASE\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ export PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/adangel/build-tools/gh-action-scripts/scripts\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ eval $(~/create-gh-actions-env.sh push adangel/build-tools gh-actions-scripts)\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ .ci/build.sh\n...\n```\n\nExample session for a push build on the main repository:\n\n```\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ export PMD_CI_SECRET_PASSPHRASE=...\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ export PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/adangel/build-tools/gh-action-scripts/scripts\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ eval $(~/create-gh-actions-env.sh push pmd/build-tools gh-actions-scripts)\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ .ci/build.sh\n...\n```\n\nExample session for a release build on the main repository from tag \"v1.0.0\":\n\n```\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ export PMD_CI_SECRET_PASSPHRASE=...\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ export PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/adangel/build-tools/gh-action-scripts/scripts\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ eval $(~/create-gh-actions-env.sh push pmd/build-tools refs/tags/v1.0.0)\npmd-ci@6cc27446ef02:~/workspaces/pmd/build-tools$ .ci/build.sh\n...\n```\n\nNote, that `create-gh-actions-env.sh` sets up `MAVEN_OPTS` with `-DskipRemoteStaging=true`, so that no maven\nartifacts are deployed automatically. You need to remove this, if you really want to perform a release.\nAlso note, that the property `autoReleaseAfterClose` is not configured and the default is `false`, so that\nyou would need to manually publish the staging repo. See also the section below about \"Nexus Staging Maven Plugin\".\n\n## Miscellaneous\n\n### Release Signing Keys\n\n#### Creating a new key\nIn general, a key created once should be reused. However, if the key is (potentially) compromised, a new\nkey needs to be generated. A gpg key consists of a master key and one or more subkeys. The master key\ndefines the identity (fingerpringt, key ID) and subkeys can be used for actual signing. The master key is\nthen only used to create new subkeys or renew subkeys. For a more safe operation, the master key should\nbe kept offline and only the subkeys should be used for signing. A Release Signing Key also doesn't need\na subkey for encryption. In case a signing key gets compromised, the subkey can be revoked and a new key\ncan be generated. But the master key still is safe.\n\nCreating such a key is not straightforward, hence this how to (there are a couple of guides\nin the internet about best practices):\n\n```\n$ gpg --expert --full-generate-key\n...\nPlease select what kind of key you want:\n\u003e 8 (RSA (set your own capabilities)\n\u003e S (Toggle Sign)\n\u003e E (Toggle Encrypt)\n\u003e Q\nCurrent allowed actions: Certify\nWhat keysize do you want?\n\u003e 4096\nPlease specify how long the key should be valid.\n\u003e 2y\nReal name:\n\u003e PMD Release Signing Key\nEmail address:\n\u003e releases@pmd-code.org\n...\npub   rsa4096 2025-01-04 [C] [expires: 2027-01-04]\n      2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\nuid                      PMD Release Signing Key \u003creleases@pmd-code.org\u003e\n```\n\nThen we create a subkey for signing:\n```\n$ gpg --edit-key 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\ngpg\u003e addkey\n\u003e 4 (RSA (sign only))\nkeysize:\n\u003e 4096\nExpiration\n\u003e 2y\n...\n\u003e save\n```\n\nNow let's publish the public key:\n```\n$ gpg --armor --export 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838 | curl -T - https://keys.openpgp.org\nKey successfully uploaded. Proceed with verification here:\nhttps://keys.openpgp.org/upload/....\n```\n\nExport the key to upload it to \u003chttps://keyserver.ubuntu.com/#submitKey\u003e:\n`gpg --armor --export 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838 | wl-copy`\nAlso upload it to \u003chttp://pgp.mit.edu/\u003e.\n\nAlso export the (public) key into a file and add it to build-tools repo:\n```\n$ gpg --armor --export 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838 \u003e scripts/files/release-signing-key-2EFA55D0785C31F956F2F87EA0B5CA1A4E086838-public.asc\n```\n\nVerify the uploaded key (and expiration date):\n\n```\ngpg --show-keys release-signing-key-2EFA55D0785C31F956F2F87EA0B5CA1A4E086838-public.asc\ncurl 'https://keys.openpgp.org/vks/v1/by-fingerprint/2EFA55D0785C31F956F2F87EA0B5CA1A4E086838' | gpg --show-keys\ncurl 'https://keyserver.ubuntu.com/pks/lookup?search=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\u0026fingerprint=on\u0026exact=on\u0026options=mr\u0026op=get' | gpg --show-keys\ncurl 'http://pgp.mit.edu/pks/lookup?op=get\u0026search=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838' | gpg --show-keys\n```\n\n#### Current Key\n\n* Used since January 2025\n* Fingerprint `2EFA 55D0 785C 31F9 56F2  F87E A0B5 CA1A 4E08 6838`\n* Used for signing artifacts in Maven Central\n\n```\n$ gpg --list-keys --fingerprint 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\npub   rsa4096 2025-01-04 [C] [expires: 2027-01-04]\n      2EFA 55D0 785C 31F9 56F2  F87E A0B5 CA1A 4E08 6838\nuid           [ultimate] PMD Release Signing Key \u003creleases@pmd-code.org\u003e\nsub   rsa4096 2025-01-04 [S] [expires: 2027-01-04]\n```\n\nThe public key is available here:\n* \u003chttps://keys.openpgp.org/search?q=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\u003e\n* \u003chttps://keyserver.ubuntu.com/pks/lookup?search=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\u0026fingerprint=on\u0026op=index\u003e\n* \u003chttp://pgp.mit.edu/pks/lookup?search=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\u0026fingerprint=on\u0026op=index\u003e\n* \u003chttps://github.com/pmd/build-tools/blob/main/scripts/files/release-signing-key-2EFA55D0785C31F956F2F87EA0B5CA1A4E086838-public.asc\u003e\n\n\n#### Old keys\n\n* Fingerprint `EBB2 41A5 45CB 17C8 7FAC B2EB D0BF 1D73 7C9A 1C22`\n  * Used until December 2024\n  * Replaced as the passphrase has been compromised and therefore the key is potentially\n    compromised. Note - as until now (January 2025) we don't have any indication that the key\n    actually has been misused.\n  * Revoked 2025-01-04.\n  * see file `release-signing-key-D0BF1D737C9A1C22-public.asc`.\n\n* Fingerprint `94A5 2756 9CAF 7A47 AFCA  BDE4 86D3 7ECA 8C2E 4C5B`\n  * Old key used to sign PMD Designer\n  * Revoked 2025-01-04.\n\n#### Private key\n\nIn order for GitHub Action to automatically sign the artifacts for snapshot builds and release builds,\nwe need to make the private key along with the passphrase available. This is done using\nmultiple [`secrets`](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets).\nThe secrets are configured on the organization level of PMD, so that the Release Signing key is available\nfor all repositories.\n\nTo not expose the master key, we only export the subkeys we use for signing and store this in the secret\n`PMD_CI_GPG_PRIVATE_KEY`.\n\nFor setting up, export the secret key and copy-paste it into a new secret:\n\n```\ngpg --armor --export-secret-subkeys 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838 | wl-copy\n```\n\n(instead of wl-copy, use xclip or pbcopy, depending on your os).\n\nThis private key will be imported by the script `setup-secrets.bash`.\n\n**Note 1:** We use option `--export-secret-subkeys` to only export the subkey and not the master key.\nThat way, we don't need to transfer the master key.\n\n**Note 2:** In order to use the key later on, the passphrase is needed. This is also setup as a secret:\n`PMD_CI_GPG_PASSPHRASE`. This secret is then exported as \"MAVEN_GPG_PASSPHRASE\" where needed\n(`MAVEN_GPG_PASSPHRASE: ${{ secrets.PMD_CI_GPG_PASSPHRASE }}`) in github actions workflows.\nSee also \u003chttps://maven.apache.org/plugins/maven-gpg-plugin/usage.html#sign-artifacts-with-gnupg\u003e.\n\n**Note 3:** The private key is now only secured by the passphrase. It is stored as a GitHub Actions\nsecret and available in an environment variable. It is not anymore committed in\nthis build-tools repository and is therefore not encrypted with another key (e.g. PMD_CI_SECRET_PASSPHRASE). \n\n#### Updating the key\n\nFrom time to time the key needs to be renewed, passphrase needs to be changed or a whole (sub)key needs to\nbe replaced.\n\nFor renewing or changing the passphrase, import the private master key and public key into your local gpg keystore\n(if you don't have it already in your keyring) and renew it.\nMake sure to renew all subkeys. Then export the public key again.\n\nFor replacing, generate a new (sub) key, just export it.\n\nYou can verify the expiration date with `gpg --fingerprint --list-key 2EFA55D0785C31F956F2F87EA0B5CA1A4E086838`:\n\n```\npub   rsa4096 2025-01-04 [C] [expires: 2027-01-04]\n      2EFA 55D0 785C 31F9 56F2  F87E A0B5 CA1A 4E08 6838\nuid           [ultimate] PMD Release Signing Key \u003creleases@pmd-code.org\u003e\nsub   rsa4096 2025-01-04 [S] [expires: 2027-01-04]\n\n```\n\nUpload the exported *public* key to\n\n* \u003chttps://keys.openpgp.org/upload\u003e\n* \u003chttps://keyserver.ubuntu.com/#submitKey\u003e\n* \u003chttp://pgp.mit.edu/\u003e\n\nVerify the uploaded key expiration date:\n\n```\ngpg --show-keys release-signing-key-2EFA55D0785C31F956F2F87EA0B5CA1A4E086838-public.asc\ncurl 'https://keys.openpgp.org/vks/v1/by-fingerprint/2EFA55D0785C31F956F2F87EA0B5CA1A4E086838' | gpg --show-keys\ncurl 'https://keyserver.ubuntu.com/pks/lookup?search=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838\u0026fingerprint=on\u0026exact=on\u0026options=mr\u0026op=get' | gpg --show-keys\ncurl 'http://pgp.mit.edu/pks/lookup?op=get\u0026search=0x2EFA55D0785C31F956F2F87EA0B5CA1A4E086838' | gpg --show-keys\n```\n\nDon't forget to update the secret `PMD_CI_GPG_PRIVATE_KEY` with the renewed private signing subkey.\n\n### Nexus Staging Maven Plugin\n\n⚠ This is deprecated, see \u003chttps://central.sonatype.org/news/20250326_ossrh_sunset/\u003e\n\nSee \u003chttps://github.com/sonatype/nexus-maven-plugins/tree/master/staging/maven-plugin\u003e.\n\nThis plugin is used, to upload maven artifacts to https://oss.sonatype.org/ and eventually to maven central\nusing the open source workflow by sonatype, see [OSSRH Guide](https://central.sonatype.org/publish/publish-guide/).\n\nThe plugin can be configured, see \u003chttps://github.com/sonatype/nexus-maven-plugins/tree/master/staging/maven-plugin#configuring-the-plugin\u003e for some options.\n\nMost important here are these:\n\n*   `skipRemoteStaging=true`: Used during test runs of releases. This makes sure, the artifacts are only staged\n    locally and never uploaded to https://oss.sonatype.org/.\n    \n    Property: [skipRemoteStaging](https://github.com/sonatype/nexus-maven-plugins/blob/0aee3defb33cb133ff536aba59b11d32a368b1e6/staging/maven-plugin/src/main/java/org/sonatype/nexus/maven/staging/deploy/DeployMojo.java#L106)\n\n*   `autoReleaseAfterClose=true`: After all modules have been uploaded to the staging repository it is\n    automatically closed (this can be controlled through `skipStagingRepositoryClose` but is the default\n    behavior). And with `autoReleaseAfterClose`, the closed staging repository will be automatically released\n    and published to maven central. This allows for fully automated releases.\n    \n    This property is set via `MAVEN_OPTS` in the workflow (`build.yml`). It is not set in the pom.xml as a plugin\n    configuration directly in order to allow to override this setting from command line\n    if needed (e.g. during release tests).\n    \n    Property: [autoReleaseAfterClose](https://github.com/sonatype/nexus-maven-plugins/blob/0aee3defb33cb133ff536aba59b11d32a368b1e6/staging/maven-plugin/src/main/java/org/sonatype/nexus/maven/staging/AbstractStagingMojo.java#L158)\n\n*   `stagingProgressTimeoutMinutes=30`: This increases the default timeout of 5 minutes to 30 minutes for\n    interaction with oss.sonatype.org. The main PMD repo has a lot of modules and depending on the load\n    of oss.sonatype.org, the release of the staging repo might take a while.\n    \n    Property: [stagingProgressTimeoutMinutes](https://github.com/sonatype/nexus-maven-plugins/blob/0aee3defb33cb133ff536aba59b11d32a368b1e6/staging/maven-plugin/src/main/java/org/sonatype/nexus/maven/staging/AbstractStagingMojo.java#L174)\n\nAfter the staging repository has been released, it is eventually synced to maven central. The release\nwon't appear here immediately but usually within 2 hours. You can check the current publish latency at\n\u003chttps://status.maven.org/\u003e.\n\n### Remote debugging\n\nDebugging remotely is possible with \u003chttps://github.com/mxschmitt/action-tmate\u003e.\n\nJust add the following step into the job:\n\n```\n      - name: Setup tmate session\n        uses: mxschmitt/action-tmate@v3\n```\n\nThe workflow [`troubleshooting`](https://github.com/pmd/pmd/blob/main/.github/workflows/troubleshooting.yml)\nin PMD can be started manually, which already contains the tmate action.\n\n**Note**: This is dangerous for push/pull builds on repositories of pmd itself, because these have access\nto the secrets and the SSH session\nis not protected. Builds triggered by pull requests from forked repositories don't have access to the secrets.\n\nSee also \u003chttps://docs.github.com/en/actions/reference/encrypted-secrets\u003e.\n\n### Intermittent connection resets or timeouts while downloading dependencies from maven central\n\nRoot issue seems to be SNAT Configs in Azure, which closes long running [idle TCP connections\nafter 4 minutes](https://docs.microsoft.com/en-us/azure/load-balancer/troubleshoot-outbound-connection#idletimeout).\n\nThe workaround is described in [actions/virtual-environments#1499](https://github.com/actions/virtual-environments/issues/1499)\nand [WAGON-545](https://issues.apache.org/jira/browse/WAGON-545)\nand [WAGON-486](https://issues.apache.org/jira/browse/WAGON-486):\n\nThe setting `-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3`\nmakes sure, that Maven doesn't try to use pooled connections that have been unused for more than 180 seconds.\nThese settings are placed as environment variable `MAVEN_OPTS` in the workflow, so that they are active for\nall Maven executions (including builds done by regression tester).\n\nAlternatively, pooling could be disabled completely via `-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false`.\nThis has the consequence, that for each dependency, that is being downloaded, a new https connection is\nestablished.\n\nMore information about configuring this can be found at [wagon-http](https://maven.apache.org/wagon/wagon-providers/wagon-http/).\n\n**Update: Since [Maven 3.9.0](https://maven.apache.org/docs/3.9.0/release-notes.html)**, the native transport instead of wagon is used:\n\n\u003e The Maven Resolver transport has changed from Wagon to “native HTTP”, see [Resolver Transport guide](https://maven.apache.org/guides/mini/guide-resolver-transport.html).\n\nTherefore, the property to configure the timeouts changed to `-Daether.connector.http.connectionMaxTtl=180`.\nRetry count is by default 3 and can be omitted.\nSee \u003chttps://maven.apache.org/resolver/configuration.html\u003e for all available properties.\n\nNote: This system property only works with Maven 3.9.2 or later!\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmd%2Fbuild-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpmd%2Fbuild-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmd%2Fbuild-tools/lists"}