Интеграция с Jira, Sentry, SonarQube и другими инструментами.
Интеграция CI/CD с инструментами разработки создаёт единую экосистему для отслеживания кода от commit до production. Изучите интеграцию с Jira, Sentry, SonarQube и другими системами.
Jira автоматически связывает деплои с задачами по ключу задачи (например, PROJ-123).
Источники ключа задачи:
feat: implement login (PROJ-123)feature/PROJ-123-add-loginJIRA_ISSUE=PROJ-123name: Deploy with Jira Integration
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for commit parsing
- name: Extract Jira Issue
id: jira
run: |
# Extract from branch name
ISSUE=$(echo "${{ github.ref_name }}" | grep -oE '[A-Z]+-[0-9]+' | head -1)
# Fallback: extract from commit message
if [ -z "$ISSUE" ]; then
ISSUE=$(git log -1 --pretty=%B | grep -oE '[A-Z]+-[0-9]+' | head -1)
fi
echo "issue=$ISSUE" >> $GITHUB_OUTPUT
echo "Found Jira issue: $ISSUE"
- name: Deploy
run: ./deploy.sh
env:
JIRA_ISSUE: ${{ steps.jira.outputs.issue }}
- name: Update Jira Status
if: success() && steps.jira.outputs.issue != ''
run: |
# Transition issue to "Done" status
curl -X POST "https://api.atlassian.com/ex/jira/${JIRA_CLOUD_ID}/rest/api/3/issue/${JIRA_ISSUE}/transitions" \
-H "Authorization: Bearer $JIRA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"transition\": {\"id\": \"31\"}
}"
# Add comment with deploy info
curl -X POST "https://api.atlassian.com/ex/jira/${JIRA_CLOUD_ID}/rest/api/3/issue/${JIRA_ISSUE}/comment" \
-H "Authorization: Bearer $JIRA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"body\": {
\"type\": \"doc\",
\"version\": 1,
\"content\": [
{
\"type\": \"paragraph\",
\"content\": [
{\"type\": \"text\", \"text\": \"✅ Deployed to production\"},
{\"type\": \"text\", \"text\": \"\\nCommit: \"},
{\"type\": \"text\", \"text\": \"${{ github.sha }}\", \"marks\": [{\"type\": \"code\"}]},
{\"type\": \"text\", \"text\": \"\\nWorkflow: \"},
{\"type\": \"hyperlink\", \"text\": \"View\", \"attrs\": {\"url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}}
]
}
]
}
}"
env:
JIRA_CLOUD_ID: ${{ secrets.JIRA_CLOUD_ID }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_ISSUE: ${{ steps.jira.outputs.issue }}GitLab имеет встроенную интеграцию с Jira:
# .gitlab-ci.yml
variables:
JIRA_TRANSITION_ID: "31" # ID перехода в "Done"
deploy_production:
stage: deploy
environment: production
script:
- ./deploy.sh
after_script:
- |
if [ -n "$JIRA_ISSUE" ]; then
# Update Jira status
curl -X POST "https://api.atlassian.com/ex/jira/${JIRA_CLOUD_ID}/rest/api/3/issue/${JIRA_ISSUE}/transitions" \
-H "Authorization: Bearer $JIRA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"transition\": {\"id\": \"$JIRA_TRANSITION_ID\"}}"
fi
rules:
- if: $CI_COMMIT_BRANCH == "main"Jira поддерживает Smart Commits для управления задачами из commit messages:
PROJ-123 #comment Deployed to production #close
PROJ-123 #time 2h #assign john.doe
- name: Process Smart Commits
run: |
# Extract Smart Commands from commit message
COMMIT_MSG=$(git log -1 --pretty=%B)
# Extract time tracking
if [[ $COMMIT_MSG =~ #time[[:space:]]*([0-9]+h)?([0-9]+m)? ]]; then
TIME_LOGGED="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
echo "Logging time: $TIME_LOGGED"
# Call Jira API to log work
fi
# Extract close command
if [[ $COMMIT_MSG =~ \#close ]]; then
echo "Closing issue via Smart Commit"
# Transition to Done
fi
env:
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}Sentry CLI позволяет управлять releases из CI/CD.
name: Deploy with Sentry Release
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Sentry CLI
uses: getsentry/action-setup-cli@v2
with:
auth_token: ${{ secrets.SENTRY_AUTH_TOKEN }}
org_slug: my-org
project_slug: my-project
- name: Create Sentry Release
run: |
# Generate version from commit
VERSION="prod-${GITHUB_SHA:0:7}"
# Create new release
sentry-cli releases new $VERSION
# Associate commits with release
sentry-cli releases set-commits $VERSION --auto
# Mark deployment start
sentry-cli releases deploys $VERSION new -e production
- name: Deploy Application
run: ./deploy.sh
env:
SENTRY_RELEASE: ${{ env.SENTRY_RELEASE }}
- name: Mark Deploy Complete
if: always()
run: |
VERSION="prod-${GITHUB_SHA:0:7}"
if [ "${{ job.status }}" = "success" ]; then
sentry-cli releases deploys $VERSION finalize -e production
else
sentry-cli releases deploys $VERSION new -e production --status failed
fi
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
ORG_SLUG: my-org
PROJECT_SLUG: my-project// Sentry SDK configuration
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
release: process.env.SENTRY_RELEASE,
// Performance monitoring
tracesSampleRate: 0.1,
// Session replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
integrations: [
Sentry.replayIntegration({
maskAllText: true,
blockAllMedia: true,
}),
],
}); - name: Upload Source Maps
run: |
sentry-cli sourcemaps inject ./dist
sentry-cli sourcemaps upload ./dist \
--release $VERSION \
--dist $GITHUB_SHA
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
ORG_SLUG: my-org
PROJECT_SLUG: my-project - name: Check Sentry Performance
run: |
# Query Sentry API for error rate after deploy
sleep 60 # Wait for data
ERROR_RATE=$(curl -s "https://sentry.io/api/0/projects/${ORG_SLUG}/${PROJECT_SLUG}/stats/" \
-H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
-G --data-urlencode "stat=generated" \
--data-urlencode "interval=1h" \
--data-urlencode "query=is:unresolved" | jq '.[0].data[-1].count')
if [ "$ERROR_RATE" -gt 100 ]; then
echo "High error rate detected: $ERROR_RATE"
exit 1
fi
echo "Error rate OK: $ERROR_RATE"
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
ORG_SLUG: my-org
PROJECT_SLUG: my-projectname: CI with SonarQube
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for better blame
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm run test:coverage
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v3
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.projectKey=my-project
-Dsonar.projectName=My Project
-Dsonar.sources=src
-Dsonar.tests=tests
-Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
-Dsonar.coverage.exclusions=**/*.test.ts,**/*.spec.ts
- name: Check Quality Gate
id: quality-gate
run: |
# Wait for analysis to complete
sleep 30
# Get Quality Gate status
RESPONSE=$(curl -s "$SONAR_HOST_URL/api/qualitygates/project_status?projectKey=my-project" \
-u $SONAR_TOKEN:)
STATUS=$(echo $RESPONSE | jq -r '.projectStatus.status')
CONDITIONS=$(echo $RESPONSE | jq -r '.projectStatus.conditions[] | select(.status == "ERROR")')
echo "Quality Gate Status: $STATUS"
if [ "$STATUS" != "OK" ]; then
echo "Quality Gate failed:"
echo "$CONDITIONS" | jq '.'
exit 1
fi
echo "Quality Gate passed!"
env:
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}Примеры условий Quality Gate в SonarQube:
| Condition | Operator | Threshold |
|---|---|---|
| Coverage on New Code | is less than | 80% |
| Bugs on New Code | is greater than | 0 |
| Vulnerabilities on New Code | is greater than | 0 |
| Code Smells on New Code | is greater than | 5 |
| Technical Debt Ratio | is greater than | 5% |
| Duplicated Lines | is greater than | 3% |
name: SonarQube Monorepo Scan
on: push
jobs:
scan-services:
runs-on: ubuntu-latest
strategy:
matrix:
service: [api, frontend, worker]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan ${{ matrix.service }}
uses: SonarSource/sonarqube-scan-action@v3
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.projectKey=monorepo-${{ matrix.service }}
-Dsonar.projectName=${{ matrix.service }}
-Dsonar.sources=services/${{ matrix.service }}/src
-Dsonar.tests=services/${{ matrix.service }}/tests
-Dsonar.javascript.lcov.reportPaths=services/${{ matrix.service }}/coverage/lcov.info
- name: Check Quality Gate for ${{ matrix.service }}
run: |
RESPONSE=$(curl -s "$SONAR_HOST_URL/api/qualitygates/project_status?projectKey=monorepo-${{ matrix.service }}" \
-u $SONAR_TOKEN:)
STATUS=$(echo $RESPONSE | jq -r '.projectStatus.status')
if [ "$STATUS" != "OK" ]; then
echo "Quality Gate failed for ${{ matrix.service }}"
exit 1
fi
env:
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - name: SonarQube Branch Analysis
uses: SonarSource/sonarqube-scan-action@v3
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.projectKey=my-project
-Dsonar.branch.name=${{ github.ref_name }}
-Dsonar.branch.target=main - name: Send Deploy Event to Datadog
run: |
curl -X POST "https://api.datadoghq.com/api/v1/events" \
-H "DD-API-KEY: $DD_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Deploy to production\",
\"text\": \"${{ github.repository }} deployed ${{ github.sha }}\",
\"priority\": \"normal\",
\"host\": \"github-actions\",
\"tags\": [
\"env:production\",
\"service:${{ github.repository }}\",
\"commit:${{ github.sha }}\",
\"deployer:${{ github.actor }}\"
],
\"alert_type\": \"success\"
}"
env:
DD_API_KEY: ${{ secrets.DATADOG_API_KEY }} - name: Mark Deployment in New Relic
run: |
curl -X POST "https://api.newrelic.com/v2/applications/${NEW_RELIC_APP_ID}/deployments.json" \
-H "X-Api-Key: $NEW_RELIC_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"deployment\": {
\"revision\": \"${{ github.sha }}\",
\"changelog\": \"Deployed via GitHub Actions\",
\"description\": \"Production deployment\",
\"user\": \"${{ github.actor }}\"
}
}"
env:
NEW_RELIC_API_KEY: ${{ secrets.NEW_RELIC_API_KEY }}
NEW_RELIC_APP_ID: ${{ secrets.NEW_RELIC_APP_ID }} - name: Update LaunchDarkly Feature Flags
run: |
# Enable feature flag for new release
curl -X PATCH "https://app.launchdarkly.com/api/v2/flags/${LD_PROJECT_KEY}/${LD_FLAG_KEY}" \
-H "Authorization: $LD_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"patch\": [{
\"op\": \"replace\",
\"path\": \"/environments/production/on\",
\"value\": true
}]
}"
env:
LD_API_KEY: ${{ secrets.LAUNCHDARKLY_API_KEY }}
LD_PROJECT_KEY: my-project
LD_FLAG_KEY: new-feature - name: Notify Slack with Jira Link
if: always()
run: |
STATUS="${{ job.status }}"
COLOR=$([ "$STATUS" = "success" ] && echo "good" || echo "danger")
EMOJI=$([ "$STATUS" = "success" ] && echo "✅" || echo "❌")
# Build Jira link if issue exists
JIRA_LINK=""
if [ -n "$JIRA_ISSUE" ]; then
JIRA_LINK=" | <https://company.atlassian.net/browse/$JIRA_ISSUE|Jira: $JIRA_ISSUE>"
fi
curl -X POST $SLACK_WEBHOOK \
-H "Content-Type: application/json" \
-d "{
\"attachments\": [{
\"color\": \"$COLOR\",
\"title\": \"$EMOJI Deploy $STATUS$JIRA_LINK\",
\"fields\": [
{\"title\": \"Repository\", \"value\": \"${{ github.repository }}\", \"short\": true},
{\"title\": \"Commit\", \"value\": \"\`${{ github.sha }}\`\", \"short\": true}
],
\"actions\": [{
\"type\": \"button\",
\"text\": \"View Workflow\",
\"url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"
}]
}]
}"
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
JIRA_ISSUE: ${{ steps.jira.outputs.issue }}# Правильно: использовать secrets
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
# Неправильно: хардкод
env:
SONAR_TOKEN: sqp_abc123 # ❌ Никогда не делайте так - name: Jira Update with Error Handling
run: |
curl -X POST "$JIRA_API_URL" \
-H "Authorization: Bearer $JIRA_TOKEN" \
--max-time 30 \
--retry 3 \
--retry-delay 5 || echo "Jira update failed, continuing..." - name: Respect API Rate Limits
run: |
# Add delay between API calls
sleep 1
# Check rate limit headers
REMAINING=$(curl -sI "$JIRA_API_URL" | grep -i "X-RateLimit-Remaining" | cut -d' ' -f2)
if [ "$REMAINING" -lt 10 ]; then
echo "Rate limit low, waiting..."
sleep 60
fiВопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.