diff --git a/run-iris/README.md b/run-iris/README.md new file mode 100644 index 00000000..410ef2c1 --- /dev/null +++ b/run-iris/README.md @@ -0,0 +1,120 @@ +# Run IRIS Analysis Action + +A reusable GitHub composite action that runs SonarSource IRIS analysis tool to synchronize projects between SonarQube instances. + +## Usage + +### External Repository Usage + +When using this action from another repository (e.g., if moved to a dedicated action repository): + +```yaml +- name: Run IRIS Analysis + uses: SonarSource/sonar-iris-action/.github/actions/run-iris@main + with: + source_project_key: "SonarSource_your-project-name" + source_organization: "sonarsource" + destination_project_key: "SonarSource_your-project-name" + destination_organization: "sonarsource" +``` + +### Basic Usage + +```yaml +jobs: + run-iris: + runs-on: sonar-runner-on-demand + permissions: + id-token: write + contents: read + steps: + - name: Run IRIS Analysis + uses: ./.github/actions/run-iris + with: + source_project_key: "SonarSource_your-project-name" + source_organization: "your-organization" + destination_project_key: "SonarSource_your-project-name" + destination_organization: "your-organization" +``` + +### Complete Workflow Example + +```yaml +name: Run IRIS Analysis + +permissions: + id-token: write + contents: read + pull-requests: read + statuses: read + checks: read + +on: + schedule: + - cron: '20 */12 * * *' + workflow_dispatch: + inputs: + github_environment: + description: 'GitHub Environment' + required: false + type: string + default: "ManualDispatch" + runner_label: + description: 'GitHub Action runner' + required: false + type: string + default: "ubuntu-latest" + +jobs: + run-iris: + runs-on: ${{ github.event_name == 'workflow_dispatch' && inputs.runner_label || 'ubuntu-latest' }} + steps: + - name: Run IRIS Analysis + uses: ./.github/actions/run-iris + with: + source_project_key: "SonarSource_your-project-name" + source_organization: "your-organization" + destination_project_key: "SonarSource_your-project-name" + destination_organization: "your-organization" + github_environment: ${{ github.event_name == 'workflow_dispatch' && inputs.github_environment || 'Scheduled' }} +``` + +## Inputs + +### Required Inputs + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `source_project_key` | Source project key | Yes | - | +| `source_organization` | Source organization name | Yes | - | +| `destination_project_key` | Destination project key | Yes | - | +| `destination_organization` | Destination organization name | Yes | - | + +### Optional Inputs + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `github_environment` | GitHub Environment | No | `ManualDispatch` | +| `sonar_sqc_eu_url` | SonarCloud EU URL | No | `https://sonarcloud.io` | +| `sonar_sqc_us_url` | SonarCloud US URL | No | `https://sonarqube.us` | +| `sonar_next_url` | SonarQube Next URL | No | `https://next.sonarqube.com/sonarqube` | + +## Prerequisites + +### Required Permissions + +The workflow using this action must have the following permissions: + +```yaml +permissions: + id-token: write # Required for vault authentication + contents: read # Required for checkout +``` + +### Required Secrets + +This action requires access to SonarSource vault with the following secrets: + +- `development/kv/data/iris` - IRIS tokens for different instances +- `development/artifactory/token/{REPO_OWNER_NAME_DASH}-private-reader` - Artifactory credentials +- `development/kv/data/repox` - Artifactory URL diff --git a/run-iris/action.yaml b/run-iris/action.yaml new file mode 100644 index 00000000..341ba9c1 --- /dev/null +++ b/run-iris/action.yaml @@ -0,0 +1,105 @@ +name: Run IRIS Analysis +description: Runs SonarSource IRIS analysis tool to synchronize projects between SonarQube instances + +inputs: + github_environment: + description: GitHub Environment + required: false + default: ManualDispatch + source_project_key: + description: Source project key + required: true + source_organization: + description: Source organization name + required: true + destination_project_key: + description: Destination project key + required: true + destination_organization: + description: Destination organization name + required: true + sonar_sqc_eu_url: + description: SonarCloud EU URL + required: false + default: https://sonarcloud.io + sonar_sqc_us_url: + description: SonarCloud US URL + required: false + default: https://sonarqube.us + sonar_next_url: + description: SonarQube Next URL + required: false + default: https://next.sonarqube.com/sonarqube + +runs: + using: composite + steps: + - name: Checkout repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 1 # Having a shallow clone here reduces overhead significantly + fetch-tags: false + + - name: Mise setup + id: mise-setup + uses: jdx/mise-action@13abe502c30c1559a5c37dff303831bab82c9402 # v2.2.3 + with: + cache_save: true + cache_key_prefix: mise-v1 + mise_toml: | + [tools] + poetry = '1.8.5' + python = '3.13' + pipenv = '2024.4.0' + sops = '3.9.0' + "npm:aws-cdk" = "v2" + "npm:newman" = "latest" + java = ['corretto-21', 'corretto-17'] + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: Print versions + shell: bash + run: | + echo "NPM: $(npm -v)" + echo "Python: $(python3 --version)" + echo "Pip: $(pip --version)" + echo "Pipenv: $(pipenv --version)" + echo "Poetry: $(poetry --version)" + echo "Node: $(node --version)" + echo "CDK: $(cdk --version)" + echo "Newman: $(newman --version)" + echo "Default Java: $(java --version)" + echo "All Java distributions installed: $(env | grep JAVA_)" + + - name: Get secrets from Vault + id: secrets + uses: SonarSource/vault-action-wrapper@eeb41b89722805725f07028c393860a50c60b51d + with: + secrets: | + development/kv/data/iris next | SONAR_IRIS_NEXT_TOKEN; + development/kv/data/iris sqc-eu | SONAR_IRIS_SQC_EU_TOKEN; + development/kv/data/iris sqc-us | SONAR_IRIS_SQC_US_TOKEN; + development/artifactory/token/{REPO_OWNER_NAME_DASH}-private-reader username | ARTIFACTORY_USERNAME; + development/artifactory/token/{REPO_OWNER_NAME_DASH}-private-reader access_token | ARTIFACTORY_ACCESS_TOKEN; + development/kv/data/repox artifactory_url | ARTIFACTORY_URL; + + - name: Run IRIS + shell: bash + run: | + chmod +x ${{ github.action_path }}/run_iris.sh + ${{ github.action_path }}/run_iris.sh + env: + SOURCE_PROJECT_KEY: ${{ inputs.source_project_key }} + SOURCE_ORGANIZATION: ${{ inputs.source_organization }} + DESTINATION_PROJECT_KEY: ${{ inputs.destination_project_key }} + DESTINATION_ORGANIZATION: ${{ inputs.destination_organization }} + SONAR_SQC_EU_URL: ${{ inputs.sonar_sqc_eu_url }} + SONAR_IRIS_SQC_EU_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_IRIS_SQC_EU_TOKEN }} + SONAR_SQC_US_URL: ${{ inputs.sonar_sqc_us_url }} + SONAR_IRIS_SQC_US_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_IRIS_SQC_US_TOKEN }} + SONAR_NEXT_URL: ${{ inputs.sonar_next_url }} + SONAR_IRIS_NEXT_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_IRIS_NEXT_TOKEN }} + ARTIFACTORY_USERNAME: ${{ fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_USERNAME }} + ARTIFACTORY_ACCESS_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_ACCESS_TOKEN }} + ARTIFACTORY_URL: ${{ fromJSON(steps.secrets.outputs.vault).ARTIFACTORY_URL }} diff --git a/run-iris/run_iris.sh b/run-iris/run_iris.sh new file mode 100755 index 00000000..c906486f --- /dev/null +++ b/run-iris/run_iris.sh @@ -0,0 +1,77 @@ +#!/bin/bash +set -euo pipefail + +: "${ARTIFACTORY_USERNAME?}" "${ARTIFACTORY_ACCESS_TOKEN?}" "${ARTIFACTORY_URL?}" +: "${SONAR_SQC_EU_URL?}" "${SONAR_IRIS_SQC_EU_TOKEN?}" +: "${SONAR_SQC_US_URL?}" "${SONAR_IRIS_SQC_US_TOKEN?}" +: "${SONAR_NEXT_URL?}" "${SONAR_IRIS_NEXT_TOKEN?}" +: "${SOURCE_PROJECT_KEY?}" "${SOURCE_ORGANIZATION?}" +: "${DESTINATION_PROJECT_KEY?}" "${DESTINATION_ORGANIZATION?}" + +# Run IRIS from SQC EU to SQS +function run_iris_next () { + java \ + -Diris.source.projectKey="$SOURCE_PROJECT_KEY" \ + -Diris.source.organization="$SOURCE_ORGANIZATION" \ + -Diris.source.url="$SONAR_SQC_EU_URL" \ + -Diris.source.token="$SONAR_IRIS_SQC_EU_TOKEN" \ + -Diris.destination.projectKey="$DESTINATION_PROJECT_KEY" \ + -Diris.destination.url="$SONAR_NEXT_URL" \ + -Diris.destination.token="$SONAR_IRIS_NEXT_TOKEN" \ + -Diris.dryrun="$1" \ + -jar iris-\[RELEASE\]-jar-with-dependencies.jar +} + +# Run IRIS from SQC EU to SQC US +function run_iris_sqc_us () { + java \ + -Diris.source.projectKey="$SOURCE_PROJECT_KEY" \ + -Diris.source.organization="$SOURCE_ORGANIZATION" \ + -Diris.source.url="$SONAR_SQC_EU_URL" \ + -Diris.source.token="$SONAR_IRIS_SQC_EU_TOKEN" \ + -Diris.destination.projectKey="$DESTINATION_PROJECT_KEY" \ + -Diris.destination.organization="$DESTINATION_ORGANIZATION" \ + -Diris.destination.url="$SONAR_SQC_US_URL" \ + -Diris.destination.token="$SONAR_IRIS_SQC_US_TOKEN" \ + -Diris.dryrun="$1" \ + -jar iris-\[RELEASE\]-jar-with-dependencies.jar +} + +VERSION="\[RELEASE\]" +HTTP_CODE=$(\ + curl \ + --write-out '%{http_code}' \ + --location \ + --remote-name \ + --user "$ARTIFACTORY_USERNAME:$ARTIFACTORY_ACCESS_TOKEN" \ + "$ARTIFACTORY_URL/sonarsource-private-releases/com/sonarsource/iris/iris/$VERSION/iris-$VERSION-jar-with-dependencies.jar"\ +) + +if [ "$HTTP_CODE" != "200" ]; then + echo "Download $VERSION failed -> $HTTP_CODE" + exit 1 +else + echo "Downloaded $VERSION" +fi + +echo "===== Execute IRIS Next as dry-run" +run_iris_next "true" +STATUS=$? +if [ $STATUS -ne 0 ]; then + echo "===== Failed to run IRIS dry-run" + exit 1 +else + echo "===== Successful IRIS Next dry-run - executing IRIS for real." + run_iris_next "false" +fi + +echo "===== Execute IRIS SQC US as dry-run" +run_iris_sqc_us "true" +STATUS=$? +if [ $STATUS -ne 0 ]; then + echo "===== Failed to run IRIS dry-run" + exit 1 +else + echo "===== Successful IRIS SQC US dry-run - executing IRIS for real." + run_iris_sqc_us "false" +fi