GitHub, GitLab, Azure DevOps, and Bitbucket Integration: Guided Hands-On Workflow
Map GitHub, GitLab, Azure DevOps, and Bitbucket integration models, run a real local Community Build analysis, and simulate one least-privilege provider-decoration path without production credentials.
Learning objectives
- Map the GitHub, GitLab, Azure DevOps, and Bitbucket trust models side by side before configuring any credential.
-
Run one real local Community Build analysis with a project-scoped
token and preserve
ceTaskId/Quality Gate evidence. - Create a provider-binding manifest containing only fake identifiers and permission metadata.
- Simulate a GitHub-style provider check locally without network access or production credentials.
- Diagnose one deliberately insufficient provider-permission manifest and repair it without broadening unrelated scopes.
- Revoke the SonarQube lab credential and document provider-credential cleanup as simulated/not-created.
1. Disposable lab contract
.invalid values. If you have an authorized Developer+
sandbox, you may replace the simulator with one native provider
binding, but never use a production organization/repository merely
for the lesson.
| Item | Lab value |
|---|---|
| SonarQube |
Community Build 26.9.0.129388 at
http://localhost:9000
|
| Scanner | SonarScanner CLI 8.1.0.6389 |
| Project | sq-ch18-provider-lab |
| Provider flow | GitHub-style local simulation; no actual GitHub API call |
| Provider identity |
example.invalid/acme/demo, simulated PR 18
|
| Credential | Real disposable SonarQube project-analysis token; provider secrets are not created |
2. Build the four-provider trust matrix before the fixture
GitHub
identity: GitHub App installed on selected repositories
outbound decoration: Checks + Pull Requests read/write
private repo read: Contents read
webhook: normally disabled unless optional security-alert feature needs it
GitLab
identity: dedicated Reporter-capable technical user or Group Access Token
global decoration credential: api scope on analyzed repositories
import-only credential: read_api where documented
Azure DevOps
identity: dedicated technical account
global decoration credential: PAT, Code read/write on analyzed repositories
pipeline integration: SonarQube Azure DevOps extension/service endpoint
import/onboarding: separate PAT flow
Bitbucket Cloud
identity: OAuth Client for global PR reporting
permission: Pull requests Read; client-credentials flow
import credential: API token with read:repository:bitbucket
compatibility: older Sonar labels may still say OAuth consumer/key/secret
This matrix is the evidence baseline. Do not create credentials until the action, owner, repository boundary, and revocation path are known.
3. Create a tiny local project
mkdir -p sq-ch18-provider-lab/src
cd sq-ch18-provider-lab
git init -b main
git config user.email "learner@example.invalid"
git config user.name "SonarQube Learner"
cat > src/app.py <<'PYCODE'
def normalize(value: str) -> str:
return value.strip().lower()
PYCODE
cat > sonar-project.properties <<'EOF'
sonar.projectKey=sq-ch18-provider-lab
sonar.projectName=SQ Chapter 18 Provider Lab
sonar.sources=src
sonar.sourceEncoding=UTF-8
EOF
cat > .gitignore <<'EOF'
.scannerwork/
evidence/
EOF
git add .
git commit -m "provider integration lab baseline"
git rev-parse HEAD
Create the disposable project in the local SonarQube UI, generate a
project-analysis token, store it only in the
current shell as SONAR_TOKEN, and record the token
name/creation time—not the value.
4. Real SonarQube analysis: prove server state before decoration
export SONAR_HOST_URL="http://localhost:9000"
export SONAR_PROJECT_KEY="sq-ch18-provider-lab"
mkdir -p evidence/sonar
git rev-parse HEAD | tee evidence/sonar/revision.txt
sonar-scanner -X 2>&1 | tee evidence/sonar/scanner.log
cp .scannerwork/report-task.txt evidence/sonar/report-task.txt
CE_TASK_ID="$(sed -n 's/^ceTaskId=//p' .scannerwork/report-task.txt)"
echo "$CE_TASK_ID" | tee evidence/sonar/ce-task-id.txt
curl -fsS -H "Authorization: Bearer $SONAR_TOKEN" \
"$SONAR_HOST_URL/api/ce/task?id=$CE_TASK_ID" \
| tee evidence/sonar/ce-task.json
# Query only after CE status is SUCCESS.
curl -fsS -H "Authorization: Bearer $SONAR_TOKEN" \
"$SONAR_HOST_URL/api/qualitygates/project_status?projectKey=$SONAR_PROJECT_KEY" \
| tee evidence/sonar/gate.json
At this point you have a real SonarQube result but no real provider decoration. That distinction is the lab’s control condition.
5. Create a fake binding and permission manifest
mkdir -p evidence/provider
cat > evidence/provider/binding.json <<'JSON'
{
"provider": "github",
"mode": "local-simulation-only",
"organization": "acme-example-invalid",
"repository": "demo",
"repository_url": "https://example.invalid/acme/demo",
"pull_request_key": "18",
"target": "main",
"sonarqube_project_key": "sq-ch18-provider-lab",
"credential_kind": "GitHub App (simulated; no key created)",
"permissions": {
"metadata": "read",
"contents": "read",
"checks": "read_write",
"pull_requests": "read_write"
}
}
JSON
python -m json.tool evidence/provider/binding.json
The manifest contains no private key, client secret, PAT, or bearer token. It records only the trust contract that a real GitHub App would need for this use case.
6. Trace the Quality Gate to a simulated provider check
The simulator reads the real SonarQube gate result and the fake provider permission contract. It refuses to “decorate” if the minimum simulated GitHub permissions are missing.
cat > provider_sim.py <<'PYCODE'
import json, sys, time
from pathlib import Path
binding = json.loads(Path("evidence/provider/binding.json").read_text())
gate = json.loads(Path("evidence/sonar/gate.json").read_text())
perms = binding["permissions"]
required = {"checks": "read_write", "pull_requests": "read_write"}
missing = [k for k,v in required.items() if perms.get(k) != v]
if missing:
print("SIMULATED_PROVIDER_403 insufficient permissions:", ",".join(missing))
sys.exit(3)
status = gate.get("projectStatus", {}).get("status", "UNKNOWN")
check = {
"provider": "github-simulator",
"repository": binding["repository_url"],
"pull_request_key": binding["pull_request_key"],
"check_name": "SonarQube Code Analysis (simulated)",
"sonarqube_gate": status,
"provider_conclusion": "success" if status == "OK" else "failure",
"simulated": True,
"created_at_epoch": int(time.time())
}
Path("evidence/provider/check.json").write_text(json.dumps(check, indent=2))
print(json.dumps(check, indent=2))
PYCODE
python provider_sim.py | tee evidence/provider/simulator.log
This is a faithful state simulation, not a claim that Community Build posted a GitHub check. The evidence packet contains a real analysis/gate and a clearly labeled local provider-delivery artifact.
7. Deliberate failure: permission is too narrow
cp evidence/provider/binding.json evidence/provider/binding-good.json
python - <<'PYCODE'
import json
from pathlib import Path
p=Path("evidence/provider/binding.json")
d=json.loads(p.read_text())
d["permissions"]["checks"]="read"
p.write_text(json.dumps(d,indent=2))
PYCODE
python provider_sim.py 2>&1 | tee evidence/provider/permission-failure.log || true
Expected evidence: the real SonarQube gate is unchanged, while the
simulated provider path returns SIMULATED_PROVIDER_403.
This proves that provider delivery can fail independently of
scanner/Compute Engine/gate success.
8. Repair only the missing permission contract
mv evidence/provider/binding-good.json evidence/provider/binding.json
python provider_sim.py | tee evidence/provider/simulator-repaired.log
python -m json.tool evidence/provider/check.json
Do not “repair” a permission failure by granting organization administration, deleting/recreating the SonarQube project, changing project key, rerunning blindly, or embedding a provider secret in YAML.
9. Optional Developer+ sandbox execution
If you have an authorized disposable provider organization/repository and Developer+ SonarQube instance:
- Create the provider integration identity using the provider-specific minimum permissions documented in Lesson 1.
- Store credentials in SonarQube’s DevOps Platform Integration configuration or provider/CI secret store as documented—never in source.
- Import/bind the disposable repository to the SonarQube project.
- Run PR/MR analysis from supported CI with full-enough Git history.
-
Preserve
ceTaskId, Quality Gate, provider check/comment/status identifier, app/token permission screenshot with secret values hidden, and audit event. - Revoke/delete the sandbox credential after the packet is complete.
10. Challenge: identify the owning layer
The scanner uploads successfully, Compute Engine is
SUCCESS, and the Quality Gate is OK, but
the provider returns HTTP 403. Which action is justified?
Inspect the provider binding, app/token identity, repository installation/access, and exact write scope. Do not change analyzer rules, retry the scanner repeatedly, restart SonarQube, or widen organization privileges before the provider evidence identifies the missing permission.
11. Cleanup
- Revoke the disposable SonarQube project-analysis token and verify it no longer authenticates.
- Delete the local project only if it exists solely for this lab and after exporting evidence.
-
No provider credential was created in the mandatory path, so
record
provider_credential_created=false. - Delete only the local fixture/evidence working copy after preserving the packet.
- Do not delete global DevOps integrations, shared apps, organization tokens, or unrelated repositories.
Knowledge check
Why is the provider simulator still useful if it never contacts GitHub?
It makes the state boundary executable: a real SonarQube gate can exist while provider delivery independently fails or succeeds according to a scoped provider contract.
What does the deliberate permission failure prove?
Provider authorization can fail after successful SonarQube processing; rerunning analysis does not repair a provider scope defect.
Why is the fake binding file allowed to contain repository URL and permission names?
Those are non-secret identity/scope metadata. Private keys, PATs, client secrets, and bearer tokens must not be stored there.
Could you replace the GitHub App simulation by pasting a personal admin token into the file?
No. That would violate least privilege and create a real credential exposure.
What should the evidence say if no Developer+ sandbox was available?
Native provider binding/decoration was not executed; the packet contains a Community Build analysis plus a faithful local provider-delivery simulation.
Official references and version notes
- SonarQube downloads / edition matrix — current Community Build and Server editions; branch/PR analysis and provider decoration begin in Developer Edition.
- GitHub App integration — dedicated App, Checks/Pull Requests permissions, private repository Contents read, optional permissions, and webhook guidance.
- GitHub project integration — bound project and pull-request Quality Gate/check behavior.
- GitLab global integration — dedicated Reporter account or Group Access Token with API scope and stored/revocable token.
-
GitLab repository import
— import token
read_apiflow and repository binding. - Azure DevOps integration — global configuration, technical PAT, extension/pipeline model, Quality Gate reporting and PR feedback.
- Azure repository import — project import PAT and bound-project behavior.
- Bitbucket Cloud global integration — SonarQube’s global OAuth configuration and pull-request permission model.
-
Bitbucket Cloud repository import
— API token with
read:repository:bitbucket; App passwords deprecated. - Sonar staff clarification on Bitbucket Cloud OAuth Client terminology (Aug–Sep 2026) — current UI uses OAuth Client / Client ID / Secret, client credentials, Pull requests Read; older maintained Sonar lines needed specific patched releases after a Bitbucket OAuth field change.
- SonarQube Web API — bearer authentication and API guidance used for local evidence queries.
- SonarScanner CLI 8.1.0.6389 — scanner baseline used by the mandatory lab.
Rechecked 2026-09-08. Mandatory examples target
Community Build 26.9.0.129388 and SonarScanner
CLI 8.1.0.6389; current commercial reference
points are SonarQube Server 2026 Release 4.1 and
2026.1.5 LTA. Native feature/maintenance branch
and pull/merge-request analysis plus provider decoration start in
Developer Edition. GitHub integration uses a
GitHub App with explicit repository permissions; GitLab global
integration uses a dedicated Reporter-capable personal/group token
with api scope; Azure DevOps uses a dedicated
technical PAT and Azure DevOps Extension/service endpoint;
Bitbucket Cloud is currently transitioning
terminology/credentials, with API tokens replacing App passwords
for import and current provider UI using OAuth Clients even where
some Sonar documentation still says OAuth Consumer. Multiple
configurations/instances and several monorepo/enterprise
integration capabilities begin in Enterprise. Recheck provider UI,
token deprecations, patch notes, callback/base URL requirements,
and exact permissions before production rollout.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.