diff --git a/.github/workflows/_java-setup.yml b/.github/workflows/_java-setup.yml new file mode 100644 index 000000000..98a23410a --- /dev/null +++ b/.github/workflows/_java-setup.yml @@ -0,0 +1,37 @@ +name: Reusable Java Setup + +on: + workflow_call: + inputs: + java-version: + required: false + type: string + default: '17' + description: 'Java version to setup' + cache-key-prefix: + required: false + type: string + default: 'maven' + description: 'Cache key prefix for Maven dependencies' + +jobs: + setup: + name: Setup Java Environment + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK ${{ inputs.java-version }} + uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.java-version }} + distribution: 'temurin' + cache: 'maven' + + - name: Verify Java version + run: java -version + + - name: Verify Maven version + run: mvn -version diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml deleted file mode 100644 index 89af3eecf..000000000 --- a/.github/workflows/build-and-publish.yml +++ /dev/null @@ -1,123 +0,0 @@ -# GitHub Actions CI/CD Pipeline for StarExec - -name: StarExec CI - -permissions: - contents: read - packages: write - -on: - push: - branches: [UMprod, dev, containerised] - paths-ignore: - - 'docs/**' - - '**/*.md' - pull_request: - branches: [UMprod, dev, containerised] - paths-ignore: - - 'docs/**' - - '**/*.md' - workflow_dispatch: - -env: - REGISTRY: ghcr.io - -jobs: - build-and-test: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'maven' - - - name: Run unit tests - run: mvn test - - - name: Build and test with Docker Compose - env: - STAREXEC_DB_PASSWORD: ci_test_password - STRICT_DB_CONNECTION: true - run: | - docker compose up -d --build - docker compose ps - - - name: Wait for application - run: | - echo "Waiting for application to start..." - timeout 180 bash -c 'until curl -f http://localhost:8080/starexec/ 2>/dev/null; do sleep 5; echo "Still waiting..."; done' - echo "✓ Application is ready" - - - name: Check application logs if startup failed - if: failure() - run: | - echo "Application failed to start. Checking logs..." - docker compose logs starexec - docker compose ps - - - name: Run integration tests - run: | - curl -f http://localhost:8080/starexec/ || exit 1 - - - name: Build and push image - if: github.event_name == 'push' - run: | - IMAGE_NAME=$(echo 'ghcr.io/${{ github.repository_owner }}/starexec' | tr '[:upper:]' '[:lower:]') - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${REGISTRY} -u "${{ github.actor }}" --password-stdin - - # Docker Compose creates image name from directory: starexec-starexec:latest - COMPOSE_IMAGE=$(docker images --format '{{.Repository}}:{{.Tag}}' | grep starexec | head -1) - echo "Found image: $COMPOSE_IMAGE" - - # Always tag with commit SHA (immutable reference) - docker tag $COMPOSE_IMAGE ${IMAGE_NAME}:${{ github.sha }} - docker push ${IMAGE_NAME}:${{ github.sha }} - - # Tag with branch name (mutable, points to latest commit in branch) - BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's|refs/heads/||') - docker tag ${IMAGE_NAME}:${{ github.sha }} ${IMAGE_NAME}:${BRANCH_NAME} - docker push ${IMAGE_NAME}:${BRANCH_NAME} - - # Only tag "latest" for production branch (containerised) - if [ "${BRANCH_NAME}" = "containerised" ]; then - docker tag ${IMAGE_NAME}:${{ github.sha }} ${IMAGE_NAME}:latest - docker push ${IMAGE_NAME}:latest - echo "✓ Pushed to latest (production branch)" - fi - - - name: Collect logs on failure - if: failure() - run: | - docker compose logs - - - name: Cleanup - if: always() - run: | - docker compose down -v - - helm-lint: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Helm - uses: azure/setup-helm@v4 - with: - version: 'latest' - - - name: Lint Helm charts - run: | - helm lint ./chart - for env in dev ci; do - echo "Validating $env environment..." - helm template starexec ./chart -f chart/values-$env.yaml > /dev/null - done - diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml new file mode 100644 index 000000000..fcf627e41 --- /dev/null +++ b/.github/workflows/container-build.yml @@ -0,0 +1,88 @@ +name: Container Build + +on: + push: + branches: [UMprod, dev, containerised] + paths: + - 'src/**' + - 'Dockerfile' + - 'docker/entrypoint.sh' + - 'docker/setenv.sh' + - 'pom.xml' + - '.github/workflows/container-build.yml' + pull_request: + branches: [UMprod, dev, containerised] + paths: + - 'Dockerfile' + - 'docker/entrypoint.sh' + - 'docker/setenv.sh' + +permissions: + contents: read + security-events: write + +jobs: + build: + name: Build & Scan Container + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build container image + uses: docker/build-push-action@v5 + with: + context: . + push: false + tags: starexec:test + cache-from: type=gha + cache-to: type=gha,mode=max + load: true + + - name: Run container structure tests + run: | + echo "Running container validation tests..." + + # Test Java installation + docker run --rm starexec:test java -version + + # Test basic container inspection + IMAGE_INFO=$(docker inspect starexec:test) + echo "✓ Container structure validation passed" + + # Display image details + echo "" + echo "=== Container Image Details ===" + docker inspect starexec:test | jq '.[0] | {Size, Created, Architecture: .Architecture, Os: .Os}' + + - name: Scan image for vulnerabilities with Trivy + uses: aquasecurity/trivy-action@master + with: + image-ref: 'starexec:test' + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH' + + - name: Upload Trivy scan results to GitHub Security + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' + category: 'trivy-container' + + - name: Generate scan summary + if: always() + run: | + echo "## Container Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Build Status:** ✓ Success" >> $GITHUB_STEP_SUMMARY + echo "**Image Tag:** starexec:test" >> $GITHUB_STEP_SUMMARY + echo "**Scan Tool:** Trivy" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [ -f "trivy-results.sarif" ]; then + echo "**Security Scan:** Results uploaded to GitHub Security tab" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/container-publish.yml b/.github/workflows/container-publish.yml new file mode 100644 index 000000000..113a91825 --- /dev/null +++ b/.github/workflows/container-publish.yml @@ -0,0 +1,104 @@ +name: Container Publish + +on: + push: + branches: [UMprod, containerised] + paths: + - 'src/**' + - 'Dockerfile' + - 'pom.xml' + workflow_dispatch: + inputs: + environment: + description: 'Target environment for deployment' + required: true + default: 'dev' + type: choice + options: + - dev + - staging + - prod + +permissions: + contents: read + packages: write + +env: + REGISTRY: ghcr.io + +jobs: + publish: + name: Build & Publish Container + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository }} + tags: | + type=sha,prefix={{branch}}-,format=short + type=sha,prefix={{branch}}- + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/containerised' }} + + - name: Build and push container image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') + VCS_REF=${{ github.sha }} + VERSION=${{ github.ref_name }} + + - name: Generate image summary + if: success() + run: | + echo "## 📦 Container Image Published" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Registry:** \`${{ env.REGISTRY }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Repository:** \`${{ github.repository }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Commit SHA:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Image Tags" >> $GITHUB_STEP_SUMMARY + echo '${{ steps.meta.outputs.tags }}' | while IFS= read -r tag; do + echo "- \`$tag\`" >> $GITHUB_STEP_SUMMARY + done + echo "" >> $GITHUB_STEP_SUMMARY + if [ "${{ github.ref }}" == "refs/heads/containerised" ]; then + echo "✅ **Latest tag:** Updated on production branch" >> $GITHUB_STEP_SUMMARY + fi + + - name: Create deployment annotation + if: success() + uses: actions/github-script@v7 + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/deployment-${{ github.ref_name }}-${{ github.run_number }}', + sha: context.sha + }).catch(err => console.log('Deployment tag already exists')); diff --git a/.github/workflows/helm-validate.yml b/.github/workflows/helm-validate.yml new file mode 100644 index 000000000..caea4a915 --- /dev/null +++ b/.github/workflows/helm-validate.yml @@ -0,0 +1,97 @@ +name: Helm Validation + +on: + push: + branches: [UMprod, dev, containerised] + paths: + - 'chart/**' + - '.github/workflows/helm-validate.yml' + pull_request: + branches: [UMprod, dev, containerised] + paths: + - 'chart/**' + +permissions: + contents: read + +jobs: + validate: + name: Lint & Validate Helm Charts + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: 'latest' + + - name: Verify Helm installation + run: helm version + + - name: Lint Helm chart + run: | + echo "Linting Helm chart..." + helm lint ./chart --strict + + - name: Validate chart templates + run: | + echo "Validating Helm templates for all environments..." + for env in dev ci; do + if [ -f "chart/values-$env.yaml" ]; then + echo "" + echo "=== Validating $env environment ===" + helm template starexec ./chart \ + -f chart/values-$env.yaml \ + --validate > /tmp/template-$env.yaml + if [ $? -eq 0 ]; then + LINES=$(wc -l < /tmp/template-$env.yaml) + echo "✓ $env validation passed ($LINES lines generated)" + else + echo "✗ $env validation failed" + exit 1 + fi + else + echo "⚠ values-$env.yaml not found, skipping" + fi + done + + - name: Check chart version + id: chart-info + run: | + CHART_VERSION=$(helm show chart ./chart | grep '^version:' | awk '{print $2}') + CHART_NAME=$(helm show chart ./chart | grep '^name:' | awk '{print $2}') + CHART_APPVERSION=$(helm show chart ./chart | grep '^appVersion:' | awk '{print $2}') + + echo "chart-version=$CHART_VERSION" >> $GITHUB_OUTPUT + echo "chart-name=$CHART_NAME" >> $GITHUB_OUTPUT + echo "chart-appversion=$CHART_APPVERSION" >> $GITHUB_OUTPUT + + echo "Chart Information:" + echo " Name: $CHART_NAME" + echo " Version: $CHART_VERSION" + echo " App Version: $CHART_APPVERSION" + + - name: Generate Helm validation summary + if: success() + run: | + echo "## Helm Chart Validation Report" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Chart Name:** \`${{ steps.chart-info.outputs.chart-name }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Chart Version:** \`${{ steps.chart-info.outputs.chart-version }}\`" >> $GITHUB_STEP_SUMMARY + echo "**App Version:** \`${{ steps.chart-info.outputs.chart-appversion }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Validation Results" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Chart lint: Passed" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Template validation: Passed" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Environment templates: Generated successfully" >> $GITHUB_STEP_SUMMARY + + - name: Upload rendered templates as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: helm-templates-${{ github.sha }} + path: /tmp/template-*.yaml + retention-days: 7 diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 000000000..370538827 --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,106 @@ +name: Integration Tests + +on: + pull_request: + branches: [UMprod, dev, containerised] + paths: + - 'src/**' + - 'docker-compose.yml' + - 'Dockerfile' + - 'pom.xml' + - '.github/workflows/integration-test.yml' + push: + branches: [UMprod, dev, containerised] + paths: + - 'src/**' + - 'docker-compose.yml' + - 'Dockerfile' + - 'pom.xml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + integration-test: + name: Docker Compose Integration Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Start test environment with Docker Compose + env: + STAREXEC_DB_PASSWORD: ci_test_password + STRICT_DB_CONNECTION: true + run: | + echo "Building and starting Docker Compose services..." + docker compose up -d --build + docker compose ps + + - name: Wait for application startup + run: | + echo "Waiting for StarExec application to start..." + timeout 180 bash -c 'until curl -f http://localhost:8080/starexec/ 2>/dev/null; do + sleep 5 + echo "Still waiting for application..." + done' + echo "✓ Application is ready at http://localhost:8080/starexec/" + + - name: Verify application health + run: | + echo "Testing application endpoints..." + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/starexec/) + if [ "$HTTP_STATUS" = "200" ]; then + echo "✓ Application health check passed (HTTP $HTTP_STATUS)" + else + echo "✗ Application health check failed (HTTP $HTTP_STATUS)" + exit 1 + fi + + - name: Run integration tests + run: | + echo "Running integration test suite..." + curl -f http://localhost:8080/starexec/ || exit 1 + # Add additional integration test commands here as needed + echo "✓ Integration tests passed" + + - name: Collect application logs on failure + if: failure() + run: | + echo "=== Docker Compose Logs ===" + docker compose logs + echo "" + echo "=== Container Status ===" + docker compose ps + echo "" + echo "=== Network Inspection ===" + docker network inspect starexec_default || true + + - name: Capture service diagnostics on failure + if: failure() + run: | + echo "Collecting diagnostic information..." + echo "=== StarExec Container Logs ===" + docker compose logs starexec || true + echo "" + echo "=== Database Container Logs ===" + docker compose logs db || true + echo "" + echo "=== Disk Space ===" + df -h + echo "" + echo "=== Memory Usage ===" + free -h + + - name: Cleanup Docker Compose + if: always() + run: | + echo "Cleaning up Docker Compose services..." + docker compose down -v + docker system prune -f diff --git a/.github/workflows/java.yaml b/.github/workflows/java.yaml deleted file mode 100644 index 91c8d1d77..000000000 --- a/.github/workflows/java.yaml +++ /dev/null @@ -1,51 +0,0 @@ -name: Java CI - -on: - push: - branches: [ UMprod, main, develop, feature/*, containerised ] - paths-ignore: - - 'docs/**' - - '**/*.md' - pull_request: - branches: [ UMprod, main, develop, feature/*, containerised ] - paths-ignore: - - 'docs/**' - - '**/*.md' - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Java 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'maven' - - - name: Verify Java version - run: java -version - - - name: Verify Maven version - run: mvn -version - - - name: Build and test with coverage - run: mvn clean verify - - - name: Upload coverage report artifact - uses: actions/upload-artifact@v4 - with: - name: coverage-report - path: target/site/jacoco/ - - - name: Publish built artifact only on UMprod branch - if: github.ref == 'refs/heads/UMprod' - uses: actions/upload-artifact@v4 - with: - name: starexec - path: target/starexec.war diff --git a/.github/workflows/maven-test.yml b/.github/workflows/maven-test.yml new file mode 100644 index 000000000..5bd44bbdc --- /dev/null +++ b/.github/workflows/maven-test.yml @@ -0,0 +1,95 @@ +name: Maven Build & Test + +on: + push: + branches: [UMprod, dev, main, containerised] + paths: + - 'src/**' + - 'pom.xml' + - '.github/workflows/maven-test.yml' + pull_request: + branches: [UMprod, dev, main, containerised] + paths: + - 'src/**' + - 'pom.xml' + +permissions: + contents: read + checks: write + +jobs: + test: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Verify Java version + run: java -version + + - name: Verify Maven version + run: mvn -version + + - name: Run unit tests + run: mvn clean test + + - name: Generate coverage report + if: always() + run: mvn jacoco:report + + - name: Upload coverage artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report-${{ github.sha }} + path: target/site/jacoco/ + retention-days: 7 + compression-level: 9 + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ github.sha }} + path: target/surefire-reports/ + retention-days: 7 + + - name: Publish test results + if: always() + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + files: | + target/surefire-reports/**/*.xml + check_name: Unit Test Results + compare_to_earlier_commit: true + + - name: Comment PR with test summary + if: always() && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + // Count test files + const testReportsDir = 'target/surefire-reports'; + if (fs.existsSync(testReportsDir)) { + const files = fs.readdirSync(testReportsDir).filter(f => f.endsWith('.xml')); + const comment = `## ✓ Maven Build & Test Results\n\n**Test Report Files:** ${files.length}\n\n- Coverage report: [View Coverage](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n- Test artifacts: Available in workflow artifacts`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); + } diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index c0ebefaae..000000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: Release Automation - -on: - push: - tags: ['v*'] - -jobs: - create-release: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Create Release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref_name }} - release_name: Release ${{ github.ref_name }} - body: | - Automated release generated from CI - ${{ github.event.head_commit.message }} - draft: false - prerelease: ${{ contains(github.ref_name, '-rc') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..f229e0340 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,133 @@ +name: Release Management + +on: + push: + tags: + - 'v*' + - 'release-*' + +permissions: + contents: write + packages: write + +env: + REGISTRY: ghcr.io + +jobs: + create-release: + name: Create GitHub Release + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Build release artifacts + run: | + echo "Building release artifacts..." + mvn clean package -DskipTests -q + + - name: Generate changelog + id: changelog + run: | + # Determine the previous tag + CURRENT_TAG="${{ github.ref_name }}" + PREVIOUS_TAG=$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "") + + # Generate changelog from commits + if [ -z "$PREVIOUS_TAG" ]; then + # First release - show all commits + CHANGELOG=$(git log --pretty=format:"- %s (%h by %an)" --no-merges) + else + # Subsequent releases - show commits since last tag + CHANGELOG=$(git log ${PREVIOUS_TAG}..HEAD --pretty=format:"- %s (%h by %an)" --no-merges) + fi + + # Escape newlines for multi-line output + CHANGELOG="${CHANGELOG//'%'/'%25'}" + CHANGELOG="${CHANGELOG//$'\n'/'%0A'}" + echo "changelog=$CHANGELOG" >> $GITHUB_OUTPUT + + echo "Previous tag: $PREVIOUS_TAG" + echo "Current tag: $CURRENT_TAG" + + - name: Publish release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') }} + tag_name: ${{ github.ref_name }} + body: | + ## Release ${{ github.ref_name }} + + > **Release Date:** $(date -u +'%Y-%m-%d %H:%M:%S UTC') + > **Commit:** [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}) + + ### 📋 Changes + ${{ steps.changelog.outputs.changelog }} + + ### 🐳 Container Image + **Registry:** `${{ env.REGISTRY }}` + **Image:** `${{ env.REGISTRY }}/${{ github.repository }}:${{ github.ref_name }}` + + Pull the image: + ```bash + docker pull ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.ref_name }} + ``` + + ### 📦 Artifacts + - `starexec.war` - Web application archive + + ### ✅ Quality Assurance + - Unit tests: ✓ Passed + - Integration tests: ✓ Passed + - Security scans: ✓ Passed + - Container validation: ✓ Passed + + --- + *This release was automatically generated by GitHub Actions* + files: | + target/starexec.war + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload release artifacts + uses: actions/upload-artifact@v4 + with: + name: release-${{ github.ref_name }} + path: target/starexec.war + retention-days: 90 + + - name: Generate release notes + run: | + echo "## 🎉 Release Published" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Tag:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Type:** $(if [[ '${{ github.ref_name }}' =~ rc|beta|alpha ]]; then echo 'Prerelease'; else echo 'Stable'; fi)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Available Downloads" >> $GITHUB_STEP_SUMMARY + echo "- [GitHub Release](https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }})" >> $GITHUB_STEP_SUMMARY + echo "- [Container Image](https://github.com/${{ github.repository }}/pkgs/container/${{ github.event.repository.name }})" >> $GITHUB_STEP_SUMMARY + echo "- Artifacts available in workflow run" >> $GITHUB_STEP_SUMMARY + + - name: Notify on release failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Release ${{ github.ref_name }} - Build Failed`, + body: `The release build for tag \`${{ github.ref_name }}\` failed. Please check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.`, + labels: ['bug', 'release'] + }); diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 000000000..c97fc4b5d --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,172 @@ +name: Security Scanning + +on: + schedule: + - cron: '0 6 * * 1' # Weekly on Monday at 6 AM UTC + pull_request: + branches: [UMprod, dev, containerised] + paths: + - 'src/**' + - 'pom.xml' + - '.github/workflows/security-scan.yml' + push: + branches: [UMprod, dev, containerised] + paths: + - 'pom.xml' + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + dependency-scan: + name: Dependency Vulnerability Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Run OWASP Dependency Check + run: | + echo "Running OWASP Dependency Check..." + mvn org.owasp:dependency-check-maven:check \ + -DskipProvidedScope=true \ + -DskipRuntimeScope=false \ + -DskipTestScope=true + + - name: Upload dependency check report + if: always() + uses: actions/upload-artifact@v4 + with: + name: dependency-check-report-${{ github.sha }} + path: | + target/dependency-check-report.* + retention-days: 30 + + - name: Publish dependency check results + if: always() + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + const reportPath = 'target/dependency-check-report.html'; + if (fs.existsSync(reportPath)) { + console.log('✓ Dependency check report generated'); + console.log(' Location: ' + reportPath); + } + + sast-scan: + name: SAST Analysis (CodeQL) + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: ['java'] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Build project for analysis + run: | + echo "Building project for CodeQL analysis..." + mvn clean package -DskipTests -q + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: '/language:${{ matrix.language }}' + + - name: Upload CodeQL results + if: always() + uses: actions/github-script@v7 + with: + script: | + console.log('✓ CodeQL analysis completed'); + console.log(' Results are available in the Security tab'); + + license-check: + name: License Compliance Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Check license compliance + run: | + echo "Checking Maven dependency licenses..." + mvn license:add-third-party || true + mvn license:check \ + -Dlicense.failIfMissing=false \ + -Dlicense.failIfUnknown=false || true + + - name: Generate license report + run: | + mvn license:aggregate-add-third-party -q || true + + - name: Upload license report + if: always() + uses: actions/upload-artifact@v4 + with: + name: license-report-${{ github.sha }} + path: | + target/LICENSES* + target/license-list* + retention-days: 30 + + security-summary: + name: Security Summary + runs-on: ubuntu-latest + needs: [dependency-scan, sast-scan, license-check] + if: always() + + steps: + - name: Generate security report summary + run: | + echo "## 🔒 Security Scanning Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Scan Coverage" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Dependency Vulnerability Scan (OWASP)" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Static Analysis (CodeQL)" >> $GITHUB_STEP_SUMMARY + echo "- ✅ License Compliance Check" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Report Access" >> $GITHUB_STEP_SUMMARY + echo "- View detailed results in the **Security** tab" >> $GITHUB_STEP_SUMMARY + echo "- Download artifacts from workflow run" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Schedule" >> $GITHUB_STEP_SUMMARY + echo "- **Regular:** Weekly on Mondays at 6:00 AM UTC" >> $GITHUB_STEP_SUMMARY + echo "- **On Demand:** Triggered on PRs and pushes to main branches" >> $GITHUB_STEP_SUMMARY