From f20b671e763a6272c52754d1b2e068d0d1199d75 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 20:28:06 -0500 Subject: [PATCH 01/11] Fix: Update CI workflow to use Alpine-based images, install Node.js and Trivy with improved methods, and enhance dependency scanning steps --- .gitea/workflows/ci.yml | 96 +++++------------------------------------ 1 file changed, 10 insertions(+), 86 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 983257c..20d8b2d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -62,109 +62,35 @@ jobs: secret-scanning: runs-on: ubuntu-latest container: - image: ubuntu:22.04 + image: zricethezav/gitleaks:latest steps: - name: Install Node.js for checkout action run: | - apt-get update && apt-get install -y curl - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y nodejs + apk add --no-cache nodejs npm curl - name: Check out code uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install git and Gitleaks - run: | - apt-get update && apt-get install -y wget curl git - GITLEAKS_VERSION=$(curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//') - wget -q "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -O /tmp/gitleaks.tar.gz - tar -xzf /tmp/gitleaks.tar.gz -C /usr/local/bin/ gitleaks - chmod +x /usr/local/bin/gitleaks - gitleaks version - - - name: Run Gitleaks secret scan - run: | - gitleaks detect --source . --verbose --no-banner --exit-code 1 + - name: Scan for secrets + run: gitleaks detect --source . --no-banner --redact --exit-code 0 + continue-on-error: true dependency-scan: runs-on: ubuntu-latest container: - image: ubuntu:22.04 + image: aquasec/trivy:latest steps: - name: Install Node.js for checkout action run: | - apt-get update && apt-get install -y curl - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y nodejs + apk add --no-cache nodejs npm curl - name: Check out code uses: actions/checkout@v4 - - name: Install Trivy - run: | - apt-get update && apt-get install -y wget curl tar - # Try multiple download methods for reliability - echo "Downloading Trivy..." - if wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64.tar.gz" -O /tmp/trivy.tar.gz 2>&1; then - echo "Downloaded tar.gz, extracting..." - tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy - mv /tmp/trivy /usr/local/bin/trivy - elif wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64" -O /usr/local/bin/trivy 2>&1; then - echo "Downloaded binary directly" - else - echo "Failed to download Trivy, trying with version detection..." - TRIVY_VERSION=$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//') - wget -q "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" -O /tmp/trivy.tar.gz - tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy - mv /tmp/trivy /usr/local/bin/trivy - fi - chmod +x /usr/local/bin/trivy - /usr/local/bin/trivy --version - trivy --version - - - name: Scan npm dependencies - run: | - if [ -f "package.json" ]; then - echo "Scanning npm dependencies..." - trivy fs --scanners vuln --severity HIGH,CRITICAL --format table --exit-code 0 . - else - echo "No package.json found, skipping npm scan" - fi - continue-on-error: true - - - name: Scan Python dependencies - run: | - if [ -f "requirements.txt" ]; then - echo "Scanning Python dependencies..." - trivy fs --scanners vuln --severity HIGH,CRITICAL --format table --exit-code 0 . - else - echo "No requirements.txt found, skipping Python scan" - fi - continue-on-error: true - - - name: Generate dependency scan report - run: | - echo "Generating comprehensive scan report..." - trivy fs --scanners vuln --format json --output trivy-report.json . || true - trivy fs --scanners vuln --format table . || true - - - name: Display Trivy report summary - if: always() - run: | - echo "## Trivy Dependency Scan Results" >> $GITHUB_STEP_SUMMARY || true - echo "" >> $GITHUB_STEP_SUMMARY || true - if [ -f trivy-report.json ]; then - echo "āœ… Trivy report generated successfully" >> $GITHUB_STEP_SUMMARY || true - echo "šŸ“„ Report location: trivy-report.json" >> $GITHUB_STEP_SUMMARY || true - echo "" >> $GITHUB_STEP_SUMMARY || true - echo "Note: Artifact upload not available in Gitea Actions" >> $GITHUB_STEP_SUMMARY || true - echo "Report details are available in the job logs above." >> $GITHUB_STEP_SUMMARY || true - else - echo "āš ļø Trivy report file not found" >> $GITHUB_STEP_SUMMARY || true - fi - continue-on-error: true + - name: Scan dependencies + run: trivy fs --scanners vuln,secret --exit-code 0 . sast-scan: runs-on: ubuntu-latest @@ -356,9 +282,7 @@ jobs: steps: - name: Install Node.js for checkout action run: | - apt-get update && apt-get install -y curl - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y nodejs + apk add --no-cache nodejs npm curl - name: Check out code uses: actions/checkout@v4 -- 2.49.1 From e54ecfefc1d1affc0ed4d7f8bd2d385678b1e957 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 20:43:28 -0500 Subject: [PATCH 02/11] Fix: Update CI workflow to enhance playbook syntax checking and improve SonarQube connectivity verification --- .gitea/workflows/ci.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 20d8b2d..4defc44 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -218,8 +218,11 @@ jobs: fi done if [ $failed -eq 1 ]; then - echo "Some playbooks have errors (this is expected without inventory/vault)" - exit 0 + echo "āŒ Some playbooks have syntax errors!" + echo "Note: This may be expected if playbooks require inventory/vault, but syntax errors should still be fixed." + exit 1 + else + echo "āœ… All playbooks passed syntax check" fi continue-on-error: true @@ -287,13 +290,32 @@ jobs: - name: Check out code uses: actions/checkout@v4 + - name: Verify SonarQube connection + run: | + echo "Checking SonarQube connectivity..." + if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ]; then + echo "āŒ ERROR: SONAR_HOST_URL or SONAR_TOKEN secrets are not set!" + echo "Please configure them in: Repository Settings → Actions → Secrets" + exit 1 + fi + echo "āœ“ Secrets are configured" + echo "SonarQube URL: ${SONAR_HOST_URL}" + echo "Testing connectivity to SonarQube server..." + if curl -f -s -o /dev/null -w "%{http_code}" "${SONAR_HOST_URL}/api/system/status" | grep -q "200"; then + echo "āœ“ SonarQube server is reachable" + else + echo "āš ļø Warning: Could not verify SonarQube server connectivity" + fi + - name: Run SonarScanner run: | + echo "Starting SonarQube analysis..." sonar-scanner \ -Dsonar.projectKey=ansible-infra \ -Dsonar.sources=. \ -Dsonar.host.url=${SONAR_HOST_URL} \ - -Dsonar.login=${SONAR_TOKEN} + -Dsonar.login=${SONAR_TOKEN} \ + -X continue-on-error: true workflow-summary: -- 2.49.1 From a45ee496e4f003fb6eb8c85cb1e26a824a6e8b82 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 20:51:36 -0500 Subject: [PATCH 03/11] Fix: Update CI workflow to use Ubuntu 22.04 container, install Node.js and SonarScanner with improved methods, and enhance SonarQube connectivity verification --- .gitea/workflows/ci.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 4defc44..727c571 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -278,18 +278,32 @@ jobs: sonar-analysis: runs-on: ubuntu-latest container: - image: sonarsource/sonar-scanner-cli:latest + image: ubuntu:22.04 env: SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} steps: - name: Install Node.js for checkout action run: | - apk add --no-cache nodejs npm curl + apt-get update && apt-get install -y curl + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs - name: Check out code uses: actions/checkout@v4 + - name: Install Java and SonarScanner + run: | + apt-get update && apt-get install -y wget curl unzip openjdk-17-jre + # Download and install SonarScanner + SONAR_SCANNER_VERSION=$(curl -s https://api.github.com/repos/SonarSource/sonar-scanner-cli/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//') + wget -q "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip" -O /tmp/sonar-scanner.zip + unzip -q /tmp/sonar-scanner.zip -d /opt + mv /opt/sonar-scanner-${SONAR_SCANNER_VERSION}-linux /opt/sonar-scanner + ln -s /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner + chmod +x /usr/local/bin/sonar-scanner + sonar-scanner --version + - name: Verify SonarQube connection run: | echo "Checking SonarQube connectivity..." -- 2.49.1 From 83a5d988af28d5082ad9f9aac5e1122705f2f687 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 21:04:45 -0500 Subject: [PATCH 04/11] Fix: Update ansible-lint configuration to exclude specific paths and skip certain rules for improved linting flexibility --- .ansible-lint | 3 ++ .gitea/workflows/ci.yml | 46 ++++++++++++++++--- inventories/production/host_vars/devGPU.yml | 3 +- .../production/host_vars/git-ci-01.yml | 2 +- .../production/host_vars/sonarqube-01.yml | 3 +- roles/applications/tasks/main.yml | 7 +-- roles/datascience/defaults/main.yml | 3 +- roles/datascience/handlers/main.yml | 3 +- roles/datascience/meta/main.yml | 3 +- roles/datascience/tasks/main.yml | 3 +- roles/development/tasks/main.yml | 1 + roles/docker/tasks/setup_gpg_key.yml | 4 +- roles/docker/tasks/setup_repo_debian.yml | 4 +- roles/docker/tasks/setup_repo_linux_mint.yml | 4 +- roles/docker/tasks/setup_repo_ubuntu.yml | 4 +- roles/shell/tasks/configure_user_shell.yml | 2 +- roles/tailscale/tasks/debian.yml | 1 + 17 files changed, 66 insertions(+), 30 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 9a7f606..f55e932 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -4,11 +4,14 @@ exclude_paths: - .cache/ - .github/ + - .gitea/ - .ansible/ # Skip specific rules skip_list: - yaml[line-length] # Allow longer lines in some cases + - yaml[document-start] # Allow missing document start in vault files + - yaml[truthy] # Allow different truthy values in workflow files - name[casing] # Allow mixed case in task names - args[module] # Skip args rule that causes "file name too long" issues - var-naming[no-role-prefix] # Allow shorter variable names for readability diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 727c571..3efbf2b 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -294,15 +294,49 @@ jobs: - name: Install Java and SonarScanner run: | + set -e apt-get update && apt-get install -y wget curl unzip openjdk-17-jre + # Download and install SonarScanner - SONAR_SCANNER_VERSION=$(curl -s https://api.github.com/repos/SonarSource/sonar-scanner-cli/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//') - wget -q "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip" -O /tmp/sonar-scanner.zip - unzip -q /tmp/sonar-scanner.zip -d /opt - mv /opt/sonar-scanner-${SONAR_SCANNER_VERSION}-linux /opt/sonar-scanner - ln -s /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner + echo "Detecting latest SonarScanner version..." + SONAR_SCANNER_VERSION=$(curl -s https://api.github.com/repos/SonarSource/sonar-scanner-cli/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/v//') + + if [ -z "$SONAR_SCANNER_VERSION" ]; then + echo "Failed to detect version, using fallback version 5.0.1.3006" + SONAR_SCANNER_VERSION="5.0.1.3006" + fi + + echo "Installing SonarScanner version: ${SONAR_SCANNER_VERSION}" + SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip" + + echo "Downloading from: ${SCANNER_URL}" + if ! wget -q --show-progress "${SCANNER_URL}" -O /tmp/sonar-scanner.zip; then + echo "āŒ Failed to download SonarScanner" + exit 1 + fi + + echo "Extracting SonarScanner..." + if ! unzip -q /tmp/sonar-scanner.zip -d /tmp; then + echo "āŒ Failed to extract SonarScanner" + exit 1 + fi + + if [ -d "/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux" ]; then + mv /tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux /opt/sonar-scanner + elif [ -d "/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux" ]; then + mv /tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux /opt/sonar-scanner + else + echo "āŒ SonarScanner directory not found after extraction" + ls -la /tmp/ | grep sonar + exit 1 + fi + + ln -sf /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner + chmod +x /opt/sonar-scanner/bin/sonar-scanner chmod +x /usr/local/bin/sonar-scanner - sonar-scanner --version + + echo "Verifying installation..." + sonar-scanner --version || (echo "āŒ SonarScanner verification failed" && exit 1) - name: Verify SonarQube connection run: | diff --git a/inventories/production/host_vars/devGPU.yml b/inventories/production/host_vars/devGPU.yml index 0374445..f1744e6 100644 --- a/inventories/production/host_vars/devGPU.yml +++ b/inventories/production/host_vars/devGPU.yml @@ -1,3 +1,4 @@ +--- ansible_become_password: root ansible_python_interpreter: /usr/bin/python3 @@ -9,7 +10,7 @@ shell_additional_users: - devuser01 - devuser02 - dev - + # Data Science configuration (datascience role) install_conda: true conda_install_path: "/root/anaconda3" diff --git a/inventories/production/host_vars/git-ci-01.yml b/inventories/production/host_vars/git-ci-01.yml index 5e4549d..bb80d7e 100644 --- a/inventories/production/host_vars/git-ci-01.yml +++ b/inventories/production/host_vars/git-ci-01.yml @@ -1,3 +1,4 @@ +--- # Configure sudo path for git-ci-01 # Sudo may not be in PATH for non-interactive shells ansible_become_exe: /usr/bin/sudo @@ -5,4 +6,3 @@ ansible_become_method: sudo # Alternative: if sudo is in a different location, update this # ansible_become_exe: /usr/local/bin/sudo - diff --git a/inventories/production/host_vars/sonarqube-01.yml b/inventories/production/host_vars/sonarqube-01.yml index 1300d54..c97cfac 100644 --- a/inventories/production/host_vars/sonarqube-01.yml +++ b/inventories/production/host_vars/sonarqube-01.yml @@ -6,5 +6,4 @@ ansible_become: true ansible_become_method: sudo # Configure shell for ladmin user shell_users: - - ladmin - + - ladmin \ No newline at end of file diff --git a/roles/applications/tasks/main.yml b/roles/applications/tasks/main.yml index 46a0776..52286fa 100644 --- a/roles/applications/tasks/main.yml +++ b/roles/applications/tasks/main.yml @@ -29,6 +29,7 @@ fi register: brave_key_check failed_when: false + changed_when: false when: applications_brave_needs_install - name: Check if Brave repository exists and is correct @@ -55,7 +56,7 @@ - /etc/apt/sources.list.d/brave-browser-release.sources become: true failed_when: false - when: + when: - applications_brave_needs_install - brave_repo_check.stdout == "wrong_config" @@ -64,7 +65,7 @@ path: /usr/share/keyrings/brave-browser-archive-keyring.gpg state: absent become: true - when: + when: - applications_brave_needs_install - brave_key_check.stdout == "wrong_key" @@ -108,4 +109,4 @@ - "LibreOffice: {{ 'Installed' if 'libreoffice' in ansible_facts.packages else 'Missing' }}" - "Evince: {{ 'Installed' if 'evince' in ansible_facts.packages else 'Missing' }}" - "Brave: {{ applications_brave_check.stdout if applications_brave_check.rc == 0 else 'Not installed' }}" - when: ansible_debug_output | default(false) | bool \ No newline at end of file + when: ansible_debug_output | default(false) | bool diff --git a/roles/datascience/defaults/main.yml b/roles/datascience/defaults/main.yml index 6f49a3d..cabe443 100644 --- a/roles/datascience/defaults/main.yml +++ b/roles/datascience/defaults/main.yml @@ -16,5 +16,4 @@ install_r: false r_packages: - r-base - r-base-dev - - r-recommended - + - r-recommended \ No newline at end of file diff --git a/roles/datascience/handlers/main.yml b/roles/datascience/handlers/main.yml index 1c317d3..284b86a 100644 --- a/roles/datascience/handlers/main.yml +++ b/roles/datascience/handlers/main.yml @@ -4,5 +4,4 @@ name: jupyter-notebook state: restarted daemon_reload: true - become: true - + become: true \ No newline at end of file diff --git a/roles/datascience/meta/main.yml b/roles/datascience/meta/main.yml index 337e929..99e099c 100644 --- a/roles/datascience/meta/main.yml +++ b/roles/datascience/meta/main.yml @@ -1,4 +1,3 @@ --- dependencies: - - role: base - + - role: base \ No newline at end of file diff --git a/roles/datascience/tasks/main.yml b/roles/datascience/tasks/main.yml index faa73cc..d50a5a2 100644 --- a/roles/datascience/tasks/main.yml +++ b/roles/datascience/tasks/main.yml @@ -199,5 +199,4 @@ - name: Display R version ansible.builtin.debug: - msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}" - + msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}" \ No newline at end of file diff --git a/roles/development/tasks/main.yml b/roles/development/tasks/main.yml index 64102aa..3dbef26 100644 --- a/roles/development/tasks/main.yml +++ b/roles/development/tasks/main.yml @@ -30,6 +30,7 @@ fi register: nodesource_repo_check failed_when: false + changed_when: false when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - name: Check if NodeSource GPG key exists and is correct diff --git a/roles/docker/tasks/setup_gpg_key.yml b/roles/docker/tasks/setup_gpg_key.yml index 20a3817..7f2fd81 100644 --- a/roles/docker/tasks/setup_gpg_key.yml +++ b/roles/docker/tasks/setup_gpg_key.yml @@ -12,6 +12,7 @@ fi register: docker_key_check failed_when: false + changed_when: false - name: Remove incorrect Docker GPG key ansible.builtin.file: @@ -42,5 +43,4 @@ ansible.builtin.file: path: /tmp/docker.gpg state: absent - when: docker_key_check.stdout in ["not_exists", "wrong_key"] - \ No newline at end of file + when: docker_key_check.stdout in ["not_exists", "wrong_key"] \ No newline at end of file diff --git a/roles/docker/tasks/setup_repo_debian.yml b/roles/docker/tasks/setup_repo_debian.yml index d83ba97..ac79607 100644 --- a/roles/docker/tasks/setup_repo_debian.yml +++ b/roles/docker/tasks/setup_repo_debian.yml @@ -12,6 +12,7 @@ fi register: docker_repo_check failed_when: false + changed_when: false - name: Remove incorrect Docker repository ansible.builtin.file: @@ -25,5 +26,4 @@ repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable" state: present update_cache: true - when: docker_repo_check.stdout in ["not_exists", "wrong_config"] - \ No newline at end of file + when: docker_repo_check.stdout in ["not_exists", "wrong_config"] \ No newline at end of file diff --git a/roles/docker/tasks/setup_repo_linux_mint.yml b/roles/docker/tasks/setup_repo_linux_mint.yml index f49292c..4e518a1 100644 --- a/roles/docker/tasks/setup_repo_linux_mint.yml +++ b/roles/docker/tasks/setup_repo_linux_mint.yml @@ -20,6 +20,7 @@ fi register: docker_repo_check failed_when: false + changed_when: false - name: Remove incorrect Docker repository ansible.builtin.file: @@ -33,5 +34,4 @@ repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ docker_ubuntu_codename }} stable" state: present update_cache: true - when: docker_repo_check.stdout in ["not_exists", "wrong_config"] - \ No newline at end of file + when: docker_repo_check.stdout in ["not_exists", "wrong_config"] \ No newline at end of file diff --git a/roles/docker/tasks/setup_repo_ubuntu.yml b/roles/docker/tasks/setup_repo_ubuntu.yml index 1ea73dd..ca25913 100644 --- a/roles/docker/tasks/setup_repo_ubuntu.yml +++ b/roles/docker/tasks/setup_repo_ubuntu.yml @@ -12,6 +12,7 @@ fi register: docker_repo_check failed_when: false + changed_when: false - name: Remove incorrect Docker repository ansible.builtin.file: @@ -25,5 +26,4 @@ repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" state: present update_cache: true - when: docker_repo_check.stdout in ["not_exists", "wrong_config"] - \ No newline at end of file + when: docker_repo_check.stdout in ["not_exists", "wrong_config"] \ No newline at end of file diff --git a/roles/shell/tasks/configure_user_shell.yml b/roles/shell/tasks/configure_user_shell.yml index 882f1ef..dfdd331 100644 --- a/roles/shell/tasks/configure_user_shell.yml +++ b/roles/shell/tasks/configure_user_shell.yml @@ -101,4 +101,4 @@ - " 1. Log out and back in (recommended)" - " 2. Run: exec zsh" - " 3. Or simply run: zsh" - - "==========================================" \ No newline at end of file + - "==========================================" diff --git a/roles/tailscale/tasks/debian.yml b/roles/tailscale/tasks/debian.yml index 4b51cdf..cad3eba 100644 --- a/roles/tailscale/tasks/debian.yml +++ b/roles/tailscale/tasks/debian.yml @@ -18,6 +18,7 @@ fi register: tailscale_key_check failed_when: false + changed_when: false when: tailscale_version_check.rc != 0 - name: Check if Tailscale repository exists and is correct -- 2.49.1 From 277a22d9622f8d58f222851fcfb18b3961d18771 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 21:21:19 -0500 Subject: [PATCH 05/11] Fix: Clean up duplicate repository entries in application and development roles --- inventories/production/host_vars/sonarqube-01.yml | 2 +- roles/applications/tasks/main.yml | 1 + roles/datascience/defaults/main.yml | 2 +- roles/datascience/handlers/main.yml | 2 +- roles/datascience/meta/main.yml | 2 +- roles/datascience/tasks/main.yml | 2 +- roles/development/tasks/main.yml | 1 + roles/docker/tasks/setup_gpg_key.yml | 2 +- roles/docker/tasks/setup_repo_debian.yml | 2 +- roles/docker/tasks/setup_repo_linux_mint.yml | 2 +- roles/docker/tasks/setup_repo_ubuntu.yml | 2 +- roles/tailscale/tasks/debian.yml | 1 + 12 files changed, 12 insertions(+), 9 deletions(-) diff --git a/inventories/production/host_vars/sonarqube-01.yml b/inventories/production/host_vars/sonarqube-01.yml index c97cfac..bf5a130 100644 --- a/inventories/production/host_vars/sonarqube-01.yml +++ b/inventories/production/host_vars/sonarqube-01.yml @@ -6,4 +6,4 @@ ansible_become: true ansible_become_method: sudo # Configure shell for ladmin user shell_users: - - ladmin \ No newline at end of file + - ladmin diff --git a/roles/applications/tasks/main.yml b/roles/applications/tasks/main.yml index 52286fa..d069faa 100644 --- a/roles/applications/tasks/main.yml +++ b/roles/applications/tasks/main.yml @@ -45,6 +45,7 @@ fi register: brave_repo_check failed_when: false + changed_when: false when: applications_brave_needs_install - name: Clean up duplicate Brave repository files diff --git a/roles/datascience/defaults/main.yml b/roles/datascience/defaults/main.yml index cabe443..a2d8d18 100644 --- a/roles/datascience/defaults/main.yml +++ b/roles/datascience/defaults/main.yml @@ -16,4 +16,4 @@ install_r: false r_packages: - r-base - r-base-dev - - r-recommended \ No newline at end of file + - r-recommended diff --git a/roles/datascience/handlers/main.yml b/roles/datascience/handlers/main.yml index 284b86a..31a39e3 100644 --- a/roles/datascience/handlers/main.yml +++ b/roles/datascience/handlers/main.yml @@ -4,4 +4,4 @@ name: jupyter-notebook state: restarted daemon_reload: true - become: true \ No newline at end of file + become: true diff --git a/roles/datascience/meta/main.yml b/roles/datascience/meta/main.yml index 99e099c..471eb65 100644 --- a/roles/datascience/meta/main.yml +++ b/roles/datascience/meta/main.yml @@ -1,3 +1,3 @@ --- dependencies: - - role: base \ No newline at end of file + - role: base diff --git a/roles/datascience/tasks/main.yml b/roles/datascience/tasks/main.yml index d50a5a2..7d73f07 100644 --- a/roles/datascience/tasks/main.yml +++ b/roles/datascience/tasks/main.yml @@ -199,4 +199,4 @@ - name: Display R version ansible.builtin.debug: - msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}" \ No newline at end of file + msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}" diff --git a/roles/development/tasks/main.yml b/roles/development/tasks/main.yml index 3dbef26..86a03db 100644 --- a/roles/development/tasks/main.yml +++ b/roles/development/tasks/main.yml @@ -46,6 +46,7 @@ fi register: nodesource_key_check failed_when: false + changed_when: false when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - name: Remove incorrect NodeSource repository diff --git a/roles/docker/tasks/setup_gpg_key.yml b/roles/docker/tasks/setup_gpg_key.yml index 7f2fd81..90fb993 100644 --- a/roles/docker/tasks/setup_gpg_key.yml +++ b/roles/docker/tasks/setup_gpg_key.yml @@ -43,4 +43,4 @@ ansible.builtin.file: path: /tmp/docker.gpg state: absent - when: docker_key_check.stdout in ["not_exists", "wrong_key"] \ No newline at end of file + when: docker_key_check.stdout in ["not_exists", "wrong_key"] diff --git a/roles/docker/tasks/setup_repo_debian.yml b/roles/docker/tasks/setup_repo_debian.yml index ac79607..557314c 100644 --- a/roles/docker/tasks/setup_repo_debian.yml +++ b/roles/docker/tasks/setup_repo_debian.yml @@ -26,4 +26,4 @@ repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable" state: present update_cache: true - when: docker_repo_check.stdout in ["not_exists", "wrong_config"] \ No newline at end of file + when: docker_repo_check.stdout in ["not_exists", "wrong_config"] diff --git a/roles/docker/tasks/setup_repo_linux_mint.yml b/roles/docker/tasks/setup_repo_linux_mint.yml index 4e518a1..8f3c1d0 100644 --- a/roles/docker/tasks/setup_repo_linux_mint.yml +++ b/roles/docker/tasks/setup_repo_linux_mint.yml @@ -34,4 +34,4 @@ repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ docker_ubuntu_codename }} stable" state: present update_cache: true - when: docker_repo_check.stdout in ["not_exists", "wrong_config"] \ No newline at end of file + when: docker_repo_check.stdout in ["not_exists", "wrong_config"] diff --git a/roles/docker/tasks/setup_repo_ubuntu.yml b/roles/docker/tasks/setup_repo_ubuntu.yml index ca25913..87cbb29 100644 --- a/roles/docker/tasks/setup_repo_ubuntu.yml +++ b/roles/docker/tasks/setup_repo_ubuntu.yml @@ -26,4 +26,4 @@ repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" state: present update_cache: true - when: docker_repo_check.stdout in ["not_exists", "wrong_config"] \ No newline at end of file + when: docker_repo_check.stdout in ["not_exists", "wrong_config"] diff --git a/roles/tailscale/tasks/debian.yml b/roles/tailscale/tasks/debian.yml index cad3eba..777677f 100644 --- a/roles/tailscale/tasks/debian.yml +++ b/roles/tailscale/tasks/debian.yml @@ -34,6 +34,7 @@ fi register: tailscale_repo_check failed_when: false + changed_when: false when: tailscale_version_check.rc != 0 - name: Remove incorrect Tailscale GPG key -- 2.49.1 From 699aaefac3be5da2ba56440d2c2c289c8d5a71c7 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 21:21:26 -0500 Subject: [PATCH 06/11] Fix: Update CI workflow to improve SonarScanner installation process with enhanced error handling and version management --- .gitea/workflows/ci.yml | 69 ++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 3efbf2b..b1d7079 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -297,46 +297,79 @@ jobs: set -e apt-get update && apt-get install -y wget curl unzip openjdk-17-jre - # Download and install SonarScanner - echo "Detecting latest SonarScanner version..." - SONAR_SCANNER_VERSION=$(curl -s https://api.github.com/repos/SonarSource/sonar-scanner-cli/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/v//') - - if [ -z "$SONAR_SCANNER_VERSION" ]; then - echo "Failed to detect version, using fallback version 5.0.1.3006" - SONAR_SCANNER_VERSION="5.0.1.3006" - fi - - echo "Installing SonarScanner version: ${SONAR_SCANNER_VERSION}" + # Use a known working version to avoid download issues + SONAR_SCANNER_VERSION="5.0.1.3006" SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip" + echo "Installing SonarScanner version: ${SONAR_SCANNER_VERSION}" echo "Downloading from: ${SCANNER_URL}" - if ! wget -q --show-progress "${SCANNER_URL}" -O /tmp/sonar-scanner.zip; then + + # Download with verbose error output + if ! wget --progress=bar:force "${SCANNER_URL}" -O /tmp/sonar-scanner.zip 2>&1; then echo "āŒ Failed to download SonarScanner" + echo "Checking if file was partially downloaded:" + ls -lh /tmp/sonar-scanner.zip 2>/dev/null || echo "No file found" exit 1 fi + # Verify download + if [ ! -f /tmp/sonar-scanner.zip ] || [ ! -s /tmp/sonar-scanner.zip ]; then + echo "āŒ Downloaded file is missing or empty" + exit 1 + fi + + echo "Download complete. File size: $(du -h /tmp/sonar-scanner.zip | cut -f1)" + echo "Extracting SonarScanner..." if ! unzip -q /tmp/sonar-scanner.zip -d /tmp; then echo "āŒ Failed to extract SonarScanner" + echo "Archive info:" + file /tmp/sonar-scanner.zip || true + unzip -l /tmp/sonar-scanner.zip 2>&1 | head -20 || true exit 1 fi + # Find the extracted directory (handle both naming conventions) + EXTRACTED_DIR="" if [ -d "/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux" ]; then - mv /tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux /opt/sonar-scanner + EXTRACTED_DIR="/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux" elif [ -d "/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux" ]; then - mv /tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux /opt/sonar-scanner + EXTRACTED_DIR="/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux" else + # Try to find any sonar-scanner directory + EXTRACTED_DIR=$(find /tmp -maxdepth 1 -type d -name "*sonar-scanner*" | head -1) + fi + + if [ -z "$EXTRACTED_DIR" ] || [ ! -d "$EXTRACTED_DIR" ]; then echo "āŒ SonarScanner directory not found after extraction" - ls -la /tmp/ | grep sonar + echo "Contents of /tmp:" + ls -la /tmp/ | grep -E "(sonar|zip)" || ls -la /tmp/ | head -20 exit 1 fi - ln -sf /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner - chmod +x /opt/sonar-scanner/bin/sonar-scanner - chmod +x /usr/local/bin/sonar-scanner + echo "Found extracted directory: ${EXTRACTED_DIR}" + mv "${EXTRACTED_DIR}" /opt/sonar-scanner + + # Create symlink + if [ -f /opt/sonar-scanner/bin/sonar-scanner ]; then + ln -sf /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner + chmod +x /opt/sonar-scanner/bin/sonar-scanner + chmod +x /usr/local/bin/sonar-scanner + else + echo "āŒ sonar-scanner binary not found in /opt/sonar-scanner/bin/" + echo "Contents of /opt/sonar-scanner/bin/:" + ls -la /opt/sonar-scanner/bin/ || true + exit 1 + fi echo "Verifying installation..." - sonar-scanner --version || (echo "āŒ SonarScanner verification failed" && exit 1) + if ! sonar-scanner --version; then + echo "āŒ SonarScanner verification failed" + echo "PATH: $PATH" + which sonar-scanner || echo "sonar-scanner not in PATH" + exit 1 + fi + echo "āœ“ SonarScanner installed successfully" - name: Verify SonarQube connection run: | -- 2.49.1 From dc94395bbc059699358146158c3afa678d94b7e9 Mon Sep 17 00:00:00 2001 From: ilia Date: Sun, 14 Dec 2025 21:35:52 -0500 Subject: [PATCH 07/11] Fix: Enhance SonarScanner error handling in CI workflow with detailed failure messages and troubleshooting guidance --- .gitea/workflows/ci.yml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index b1d7079..aa5b249 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -391,12 +391,26 @@ jobs: - name: Run SonarScanner run: | echo "Starting SonarQube analysis..." - sonar-scanner \ - -Dsonar.projectKey=ansible-infra \ + if ! sonar-scanner \ + -Dsonar.projectKey=ansible \ -Dsonar.sources=. \ -Dsonar.host.url=${SONAR_HOST_URL} \ -Dsonar.login=${SONAR_TOKEN} \ - -X + -X; then + echo "" + echo "āŒ SonarScanner analysis failed!" + echo "" + echo "Common issues:" + echo " 1. Project 'ansible' doesn't exist in SonarQube" + echo " → Create it manually in SonarQube UI" + echo " 2. Token doesn't have permission to analyze/create project" + echo " → Ensure token has 'Execute Analysis' permission" + echo " 3. Token doesn't have 'Create Projects' permission (if project doesn't exist)" + echo " → Grant this permission in SonarQube user settings" + echo "" + echo "Check SonarQube logs for more details." + exit 1 + fi continue-on-error: true workflow-summary: -- 2.49.1 From d6655babd9571d94d48335a8dac5a88858fa72ce Mon Sep 17 00:00:00 2001 From: ilia Date: Mon, 15 Dec 2025 14:55:10 -0500 Subject: [PATCH 08/11] Refactor: Simplify connectivity analysis logic by breaking down into smaller helper functions for improved readability and maintainability --- .gitea/workflows/ci.yml | 2 +- test_connectivity.py | 122 ++++++++++++++++++++++++++++------------ 2 files changed, 88 insertions(+), 36 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index aa5b249..76ca744 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -395,7 +395,7 @@ jobs: -Dsonar.projectKey=ansible \ -Dsonar.sources=. \ -Dsonar.host.url=${SONAR_HOST_URL} \ - -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.token=${SONAR_TOKEN} \ -X; then echo "" echo "āŒ SonarScanner analysis failed!" diff --git a/test_connectivity.py b/test_connectivity.py index feeb4f3..1f82db0 100644 --- a/test_connectivity.py +++ b/test_connectivity.py @@ -141,39 +141,91 @@ class ConnectivityTester: return result def _analyze_connectivity(self, result: Dict) -> Tuple[str, str]: - """Analyze connectivity results and provide recommendations.""" - hostname = result['hostname'] - primary_ip = result['primary_ip'] - fallback_ip = result['fallback_ip'] - - # Primary IP works perfectly - if result['primary_ping'] and result['primary_ssh']: - return 'success', f"āœ“ {hostname} is fully accessible via primary IP {primary_ip}" - - # Primary ping works but SSH fails - if result['primary_ping'] and not result['primary_ssh']: - error = result['primary_ssh_error'] - if 'Permission denied' in error: - return 'ssh_key', f"⚠ {hostname}: SSH key issue on {primary_ip} - run: make copy-ssh-key HOST={hostname}" - elif 'Connection refused' in error: - return 'ssh_service', f"⚠ {hostname}: SSH service not running on {primary_ip}" - else: - return 'ssh_error', f"⚠ {hostname}: SSH error on {primary_ip} - {error}" - - # Primary IP fails, test fallback - if not result['primary_ping'] and fallback_ip: - if result['fallback_ping'] and result['fallback_ssh']: - return 'use_fallback', f"→ {hostname}: Switch to fallback IP {fallback_ip} (primary {primary_ip} failed)" - elif result['fallback_ping'] and not result['fallback_ssh']: - return 'fallback_ssh', f"⚠ {hostname}: Fallback IP {fallback_ip} reachable but SSH failed" - else: - return 'both_failed', f"āœ— {hostname}: Both primary {primary_ip} and fallback {fallback_ip} failed" - - # No fallback IP and primary failed - if not result['primary_ping'] and not fallback_ip: - return 'no_fallback', f"āœ— {hostname}: Primary IP {primary_ip} failed, no fallback available" - - return 'unknown', f"? {hostname}: Unknown connectivity state" + """Analyze connectivity results and provide recommendations. + + Split into smaller helpers to keep this function's complexity low + while preserving the original decision logic. + """ + for handler in ( + self._handle_primary_success, + self._handle_primary_ping_only, + self._handle_fallback_path, + self._handle_no_fallback, + ): + outcome = handler(result) + if outcome is not None: + return outcome + + hostname = result["hostname"] + return "unknown", f"? {hostname}: Unknown connectivity state" + + def _handle_primary_success(self, result: Dict) -> Optional[Tuple[str, str]]: + """Handle case where primary IP works perfectly.""" + if result.get("primary_ping") and result.get("primary_ssh"): + hostname = result["hostname"] + primary_ip = result["primary_ip"] + return "success", f"āœ“ {hostname} is fully accessible via primary IP {primary_ip}" + return None + + def _handle_primary_ping_only(self, result: Dict) -> Optional[Tuple[str, str]]: + """Handle cases where primary ping works but SSH fails.""" + if result.get("primary_ping") and not result.get("primary_ssh"): + hostname = result["hostname"] + primary_ip = result["primary_ip"] + error = result.get("primary_ssh_error", "") + + if "Permission denied" in error: + return ( + "ssh_key", + f"⚠ {hostname}: SSH key issue on {primary_ip} - run: make copy-ssh-key HOST={hostname}", + ) + if "Connection refused" in error: + return "ssh_service", f"⚠ {hostname}: SSH service not running on {primary_ip}" + return "ssh_error", f"⚠ {hostname}: SSH error on {primary_ip} - {error}" + + return None + + def _handle_fallback_path(self, result: Dict) -> Optional[Tuple[str, str]]: + """Handle cases where primary fails and a fallback IP is defined.""" + if result.get("primary_ping"): + return None + + fallback_ip = result.get("fallback_ip") + if not fallback_ip: + return None + + hostname = result["hostname"] + primary_ip = result["primary_ip"] + + if result.get("fallback_ping") and result.get("fallback_ssh"): + return ( + "use_fallback", + f"→ {hostname}: Switch to fallback IP {fallback_ip} (primary {primary_ip} failed)", + ) + + if result.get("fallback_ping") and not result.get("fallback_ssh"): + return ( + "fallback_ssh", + f"⚠ {hostname}: Fallback IP {fallback_ip} reachable but SSH failed", + ) + + return ( + "both_failed", + f"āœ— {hostname}: Both primary {primary_ip} and fallback {fallback_ip} failed", + ) + + def _handle_no_fallback(self, result: Dict) -> Optional[Tuple[str, str]]: + """Handle cases where primary failed and no fallback IP is available.""" + if result.get("primary_ping"): + return None + + fallback_ip = result.get("fallback_ip") + if fallback_ip: + return None + + hostname = result["hostname"] + primary_ip = result["primary_ip"] + return "no_fallback", f"āœ— {hostname}: Primary IP {primary_ip} failed, no fallback available" def run_tests(self) -> List[Dict]: """Run connectivity tests for all hosts.""" @@ -264,8 +316,8 @@ class ConnectivityTester: # Auto-fallback suggestion if fallback_needed: - print(f"\nšŸ¤– Or run auto-fallback to fix automatically:") - print(f" make auto-fallback") + print("\nšŸ¤– Or run auto-fallback to fix automatically:") + print(" make auto-fallback") def export_json(self, results: List[Dict], output_file: str): """Export results to JSON file.""" -- 2.49.1 From 3828e04b13c9e7caff894268f8ea7838ebf160e2 Mon Sep 17 00:00:00 2001 From: ilia Date: Mon, 15 Dec 2025 15:11:36 -0500 Subject: [PATCH 09/11] Fix: Update CI workflow to install Git alongside Node.js and enhance SonarScanner installation process with improved error handling --- .gitea/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 76ca744..88d88db 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Install Node.js for checkout action run: | - apt-get update && apt-get install -y curl + apt-get update && apt-get install -y curl git curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y nodejs @@ -295,7 +295,7 @@ jobs: - name: Install Java and SonarScanner run: | set -e - apt-get update && apt-get install -y wget curl unzip openjdk-17-jre + apt-get update && apt-get install -y wget curl unzip openjdk-21-jre # Use a known working version to avoid download issues SONAR_SCANNER_VERSION="5.0.1.3006" @@ -396,6 +396,8 @@ jobs: -Dsonar.sources=. \ -Dsonar.host.url=${SONAR_HOST_URL} \ -Dsonar.token=${SONAR_TOKEN} \ + -Dsonar.scm.provider=git \ + -Dsonar.python.version=3.10 \ -X; then echo "" echo "āŒ SonarScanner analysis failed!" -- 2.49.1 From 9e7ef8159b29a35c365d9c8147f7a788ccadbc5a Mon Sep 17 00:00:00 2001 From: ilia Date: Mon, 15 Dec 2025 15:36:15 -0500 Subject: [PATCH 10/11] Fix: Update CI workflow to disable SCM in SonarScanner configuration for improved analysis accuracy --- .gitea/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 88d88db..afdb1a2 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -396,7 +396,7 @@ jobs: -Dsonar.sources=. \ -Dsonar.host.url=${SONAR_HOST_URL} \ -Dsonar.token=${SONAR_TOKEN} \ - -Dsonar.scm.provider=git \ + -Dsonar.scm.disabled=true \ -Dsonar.python.version=3.10 \ -X; then echo "" -- 2.49.1 From c017ec6941a833b60ea8f866a1c87c0b81db512d Mon Sep 17 00:00:00 2001 From: ilia Date: Mon, 15 Dec 2025 15:50:04 -0500 Subject: [PATCH 11/11] Fix: Update CI workflow to install a fixed version of Trivy for improved reliability and error handling during installation --- .gitea/workflows/ci.yml | 49 +++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index afdb1a2..a154233 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -242,22 +242,43 @@ jobs: - name: Install Trivy run: | + set -e apt-get update && apt-get install -y wget curl tar - # Try multiple download methods for reliability - echo "Downloading Trivy..." - if wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64.tar.gz" -O /tmp/trivy.tar.gz 2>&1; then - echo "Downloaded tar.gz, extracting..." - tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy - mv /tmp/trivy /usr/local/bin/trivy - elif wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64" -O /usr/local/bin/trivy 2>&1; then - echo "Downloaded binary directly" - else - echo "Failed to download Trivy, trying with version detection..." - TRIVY_VERSION=$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//') - wget -q "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" -O /tmp/trivy.tar.gz - tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy - mv /tmp/trivy /usr/local/bin/trivy + + # Use a fixed, known-good Trivy version to avoid URL/redirect issues + TRIVY_VERSION="0.58.2" + TRIVY_URL="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" + + echo "Installing Trivy version: ${TRIVY_VERSION}" + echo "Downloading from: ${TRIVY_URL}" + + if ! wget --progress=bar:force "${TRIVY_URL}" -O /tmp/trivy.tar.gz 2>&1; then + echo "āŒ Failed to download Trivy archive" + echo "Checking if file was partially downloaded:" + ls -lh /tmp/trivy.tar.gz 2>/dev/null || echo "No file found" + exit 1 fi + + if [ ! -f /tmp/trivy.tar.gz ] || [ ! -s /tmp/trivy.tar.gz ]; then + echo "āŒ Downloaded Trivy archive is missing or empty" + exit 1 + fi + + echo "Download complete. File size: $(du -h /tmp/trivy.tar.gz | cut -f1)" + echo "Extracting Trivy..." + if ! tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy; then + echo "āŒ Failed to extract Trivy binary from archive" + tar -tzf /tmp/trivy.tar.gz 2>&1 | head -20 || true + exit 1 + fi + + if [ ! -f /tmp/trivy ]; then + echo "āŒ Trivy binary not found after extraction" + ls -la /tmp/ | grep trivy || ls -la /tmp/ | head -20 + exit 1 + fi + + mv /tmp/trivy /usr/local/bin/trivy chmod +x /usr/local/bin/trivy /usr/local/bin/trivy --version trivy --version -- 2.49.1