diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..a6f769c4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,102 @@ +# © 2025. TU Dortmund University, +# Institute of Energy Systems, Energy Efficiency and Energy Economics, +# Research group Distribution grid planning and operation +# + +name: CI + +on: + push: + paths-ignore: + - 'docs/**' + branches: + - main + - dev + - 'hotfix/*' + - 'rel/*' + - 'dependabot/*' + pull_request: + branches: + - main + - dev + +jobs: + buildAndTest: + runs-on: ubuntu-latest + + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Check Branch + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + BRANCH_NAME="${{ github.head_ref }}" + else + BRANCH_NAME="${{ github.ref_name }}" + fi + + if [[ "$BRANCH_NAME" == refs/heads/* ]]; then + BRANCH_NAME="${BRANCH_NAME#refs/heads/}" + fi + + export BRANCH_NAME + + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + + ./gradlew checkBranchName -PbranchName="$BRANCH_NAME" --warning-mode=none + + bash scripts/branch_type.sh + + - name: Version Check + if: ${{ github.event_name == 'pull_request' }} + env: + BASE_BRANCH: ${{ github.event.pull_request.base.ref }} + run: bash scripts/run-version-check.sh + + - name: Build Project + run: ./gradlew --refresh-dependencies clean assemble spotlessCheck + + - name: Run Tests + run: ./gradlew pmdMain pmdTest spotbugsMain spotbugsTest test reportScoverage checkScoverage + + - name: Build Scala-Docs + run: ./gradlew scaladoc + + - name: SonarQube + run: | + ./gradlew sonar \ + -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \ + -Dsonar.host.url=${{ vars.SONAR_HOST_URL }} \ + -Dsonar.login=${{ secrets.SONAR_TOKEN }} \ + -Dsonar.qualitygate.wait=true + + #Deployment + - name: Deploy + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + env: + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MAVENCENTRAL_SIGNINGKEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MAVENCENTRAL_SIGNINGPASS }} + ORG_GRADLE_PROJECT_user: ${{ secrets.MAVENCENTRAL_USER }} + ORG_GRADLE_PROJECT_password: ${{ secrets.MAVENCENTRAL_PASS }} + run: | + if [ "${GITHUB_REF}" == "refs/heads/main" ]; then + currentVersion=$(./gradlew -q currentVersion) + else + currentVersion=$(./gradlew -q devVersion) + fi + + echo "currentVersion=$currentVersion" + + ./gradlew publish -PdeployVersion=$currentVersion diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff06664..83cf10c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Include highway filter in example config [#513](https://github.com/ie3-institute/OSMoGrid/issues/513) - Added Marius Staudt to list of reviewers [#516](https://github.com/ie3-institute/OSMoGrid/issues/501) - Create `CITATION.cff` [#531](https://github.com/ie3-institute/OSMoGrid/issues/531) +- Implemented GitHub Actions Pipeline [#545](https://github.com/ie3-institute/OSMoGrid/issues/545) ### Changed - Rely on Java 17 diff --git a/build.gradle b/build.gradle index 8d347cae..0102867a 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,7 @@ apply from: scriptsLocation + 'scoverage.gradle' // scoverage scala code coverag apply from: scriptsLocation + 'vcs.gradle' apply from: scriptsLocation + 'semVer.gradle' apply from: scriptsLocation + 'tscfg.gradle' +apply from: scriptsLocation + 'branchName.gradle' // checks naming scheme of branches configurations { scalaCompilerPlugin diff --git a/gradle/scripts/branchName.gradle b/gradle/scripts/branchName.gradle new file mode 100644 index 00000000..99ba2c50 --- /dev/null +++ b/gradle/scripts/branchName.gradle @@ -0,0 +1,26 @@ +tasks.register('checkBranchName') { + doLast { + if (!project.hasProperty('branchName')) { + throw new GradleException("Error: Missing required property 'branchName'.") + } + + def branchName = project.property('branchName') + + def patterns = [ + ~/^(developer|develop|dev)$/, + ~/.*rel\/.*/, + ~/^dependabot\/.*$/, + ~/.*hotfix\/\pL{2}\/#\d+.*/, + ~/.*main/, + ~/^[a-z]{2}\/#[0-9]+(?:-.+)?$/ + ] + + def isValid = patterns.any { pattern -> branchName ==~ pattern } + + if (!isValid) { + throw new GradleException("Error: Check Branch name format (e.g., ps/#1337-FeatureName). Current branch name is $branchName.") + } + + println "Branch name is $branchName" + } +} diff --git a/gradle/scripts/semVer.gradle b/gradle/scripts/semVer.gradle index ec680a48..c3300893 100644 --- a/gradle/scripts/semVer.gradle +++ b/gradle/scripts/semVer.gradle @@ -1,13 +1,13 @@ // tasks for semantic versioning using semver-gradle https://github.com/ethauvin/semver-gradle -task currentVersion { - doFirst{ +tasks.register('currentVersion') { + doFirst { println semver.semver } } -task devVersion { - doFirst{ +tasks.register('devVersion') { + doFirst { println "${semver.major}.${semver.minor}-SNAPSHOT" } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c..d64cd491 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e093..37f853b1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/scripts/branch_type.sh b/scripts/branch_type.sh new file mode 100755 index 00000000..e1c27248 --- /dev/null +++ b/scripts/branch_type.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -z "${BRANCH_NAME:-}" ]; then + echo "Error: BRANCH_NAME variable is not set." + exit 1 +fi + + +pattern_dev='^(developer|develop|dev)$' +pattern_release='.*rel/.*' +pattern_dependabot='^dependabot/.*' +pattern_hotfix='.*hotfix/.*' +pattern_main='.*main' +pattern_feature='^[a-z]{2}/#[0-9]+(-.+)?$' + +BRANCH_TYPE="unknown" + +if [[ "$BRANCH_NAME" =~ $pattern_dev ]]; then + BRANCH_TYPE="dev" +elif [[ "$BRANCH_NAME" =~ $pattern_release ]]; then + BRANCH_TYPE="release" +elif [[ "$BRANCH_NAME" =~ $pattern_dependabot ]]; then + BRANCH_TYPE="dependabot" +elif [[ "$BRANCH_NAME" =~ $pattern_hotfix ]]; then + BRANCH_TYPE="hotfix" +elif [[ "$BRANCH_NAME" =~ $pattern_main ]]; then + BRANCH_TYPE="main" +elif [[ "$BRANCH_NAME" =~ $pattern_feature ]]; then + BRANCH_TYPE="feature" +else + echo "Error:'$BRANCH_NAME' does not match any pattern." + exit 1 +fi + +echo "=========================" +echo "Branch type: $BRANCH_TYPE" +echo "BRANCH_TYPE=$BRANCH_TYPE" >> "$GITHUB_ENV" +echo "=========================" diff --git a/scripts/get_versions.sh b/scripts/get_versions.sh new file mode 100755 index 00000000..7001c49d --- /dev/null +++ b/scripts/get_versions.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")/.." + +REPO_URL=$(git config --get remote.origin.url) +export REPO_URL +echo "REPO_URL=$REPO_URL" >> $GITHUB_ENV + +echo "Fetching current version of PR..." +PR_VERSION=$(./gradlew -q currentVersion) +echo "PR_VERSION=$PR_VERSION" +echo "export PR_VERSION=$PR_VERSION" >> versions.env +echo "PR_VERSION=$PR_VERSION" >> "$GITHUB_ENV" + +get_branch_version() { + local BRANCH_NAME=$1 + local DIR_NAME="${BRANCH_NAME}-branch" + + git clone --depth 1 --branch "$BRANCH_NAME" "$REPO_URL" "$DIR_NAME" + cd "$DIR_NAME" + + echo "Fetching version from $BRANCH_NAME branch..." + BRANCH_VERSION=$(./gradlew -q currentVersion) + cd .. + + echo "${BRANCH_NAME^^}_VERSION=$BRANCH_VERSION" + echo "export ${BRANCH_NAME^^}_VERSION=$BRANCH_VERSION" >> versions.env + echo "${BRANCH_NAME^^}_VERSION=$BRANCH_VERSION" >> "$GITHUB_ENV" + + rm -rf "$DIR_NAME" +} + + +get_branch_version "dev" +get_branch_version "main" + +echo "Get Versions: OK!" diff --git a/scripts/run-version-check.sh b/scripts/run-version-check.sh new file mode 100644 index 00000000..ebd2bb25 --- /dev/null +++ b/scripts/run-version-check.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -euo pipefail + +rm -f versions.env + +scripts/get_versions.sh + +source versions.env + +scripts/version_check.sh diff --git a/scripts/version_check.sh b/scripts/version_check.sh new file mode 100755 index 00000000..bfa3328c --- /dev/null +++ b/scripts/version_check.sh @@ -0,0 +1,76 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")/.." + +echo "=========================" +echo "LOADED ENV VARIABLES:" +echo "PR_VERSION: $PR_VERSION" +echo "DEV_VERSION: $DEV_VERSION" +echo "MAIN_VERSION: $MAIN_VERSION" +echo "BASE_BRANCH: $BASE_BRANCH" +echo "=========================" + +semver_gt() { + IFS='.' read -r major1 minor1 patch1 <<< "$1" + IFS='.' read -r major2 minor2 patch2 <<< "$2" + + # Compare major version + if [ "$major1" -gt "$major2" ]; then + return 0 + elif [ "$major1" -lt "$major2" ]; then + return 1 + fi + + # Compare minor version + if [ "$minor1" -gt "$minor2" ]; then + return 0 + elif [ "$minor1" -lt "$minor2" ]; then + return 1 + fi + + # Compare patch version + if [ "$patch1" -gt "$patch2" ]; then + return 0 + else + return 1 + fi +} + +# Version Checking Logic +if [ "$BASE_BRANCH" = "dev" ]; then + echo "PR into dev => applying dev rules" + if [ "$DEV_VERSION" = "$PR_VERSION" ]; then + echo "OK: PR version ($PR_VERSION) matches the current dev version ($DEV_VERSION)." + exit 0 + else + if [ "$MAIN_VERSION" = "$DEV_VERSION" ]; then + if semver_gt "$PR_VERSION" "$DEV_VERSION"; then + echo "OK: Increasing working version in dev from $DEV_VERSION to $PR_VERSION" + exit 0 + else + echo "FAIL: Release and working version are $MAIN_VERSION, but PR is not increasing the working version in dev" + exit 1 + fi + else + echo "FAIL: PR version ($PR_VERSION) does not match the current dev version ($DEV_VERSION)." + echo "Regular PRs must not update the working version. The working version should only change in controlled updates." + exit 1 + fi + fi + +elif [ "$BASE_BRANCH" = "main" ]; then + echo "PR into main => applying main rules" + if semver_gt "$PR_VERSION" "$MAIN_VERSION"; then + echo "OK: PR version ($PR_VERSION) is greater than the current main version ($MAIN_VERSION)." + exit 0 + else + echo "FAIL: PR version ($PR_VERSION) is NOT greater than the current main version ($MAIN_VERSION)." + echo "A new release must have a higher version than the existing main version." + exit 1 + fi + +else + echo "Skipping version check: Base branch is '$BASE_BRANCH'. No version enforcement required." + exit 0 +fi