Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .changeset/refactor-ci-workflows-docker-security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
'deepsource-mcp-server': patch
---

Refactor CI/CD workflows and improve Docker security

### CI/CD Improvements

- **Unified Build Process**: Consolidated build into single job that creates reusable artifacts
- **Build Artifacts**: Generated once and reused throughout workflow for consistency
- **Build Manifest**: Added metadata tracking (SHA, timestamp, dependencies)
- **Tag Creation**: Tags now created before building artifacts for better traceability
- **Artifact Naming**: Include commit SHA for consistent naming across workflows

### Docker Support

- **Multi-Platform Builds**: Added support for linux/amd64 and linux/arm64
- **Docker Workflow**: New reusable workflow for container image builds
- **Configuration**: Docker releases controlled via `ENABLE_DOCKER_RELEASE` variable

### Security Enhancements

- **CodeQL Integration**: Added dedicated security scanning workflow
- **Consolidated Scanning**: Unified security checks in reusable workflow
- **Docker Security**: Container images run as non-root user (nodejs:1001)
- **Trivy Scanning**: Automated vulnerability detection in container images
- **Dependency Scanning**: Enhanced vulnerability reporting

### Developer Experience

- **Issue Templates**: Added bug report and feature request templates
- **PR Template**: Comprehensive pull request template with checklist
- **Workflow Documentation**: Enhanced comments for better maintainability
- **Better Validation**: Improved changeset validation in PR workflow

### Infrastructure

- **NPM Packaging**: Dedicated job for package preparation with attestations
- **SLSA Provenance**: Generate attestations for supply chain security
- **Improved Permissions**: Updated for container registry access
- **DeepSource Integration**: Maintained test coverage reporting

### Technical Details

- Removed redundant `reusable-setup.yml` (merged into other workflows)
- Better job dependency graph for parallel execution
- Consistent secret passing (DEEPSOURCE_DSN)
- Enhanced artifact retention strategies
40 changes: 40 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**

- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**

- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
19 changes: 19 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
33 changes: 33 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--- Please provide a general summary of your changes in the title above -->

## Pull request type

<!-- Please try to limit your pull request to one type, submit multiple pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: N/A

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by this PR. -->

-
-
-

## Other information

<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
202 changes: 202 additions & 0 deletions .github/scripts/determine-artifact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#!/bin/bash
# =============================================================================
# SCRIPT: Determine Build Artifact from GitHub Releases
# PURPOSE: Find and validate the correct artifact from a GitHub release
# USAGE: ./determine-artifact.sh --tag <tag> --repo <repo> --version <version> --prefix <prefix> --output <output_file>
# =============================================================================

set -euo pipefail

# Default values
TAG_NAME=""
REPO=""
VERSION=""
PREFIX=""
OUTPUT_FILE=""

# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--tag)
TAG_NAME="$2"
shift 2
;;
--repo)
REPO="$2"
shift 2
;;
--version)
VERSION="$2"
shift 2
;;
--prefix)
PREFIX="$2"
shift 2
;;
--output)
OUTPUT_FILE="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done

# Validate required parameters
if [ -z "$TAG_NAME" ] || [ -z "$REPO" ] || [ -z "$VERSION" ] || [ -z "$PREFIX" ] || [ -z "$OUTPUT_FILE" ]; then
echo "❌ Missing required parameters"
echo "Usage: $0 --tag <tag> --repo <repo> --version <version> --prefix <prefix> --output <output_file>"
exit 1
fi

echo "🔍 Determining artifact source for $PREFIX-$VERSION from release $TAG_NAME"

# Fetch tag information from GitHub API
TAG_API_URL="https://api.github.com/repos/$REPO/git/refs/tags/$TAG_NAME"
TAG_RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -w "\n%{http_code}" $TAG_API_URL)
TAG_BODY=$(echo "$TAG_RESPONSE" | head -n -1)
TAG_STATUS=$(echo "$TAG_RESPONSE" | tail -n 1)

if [ "$TAG_STATUS" != "200" ]; then
echo "❌ GitHub API request failed for $TAG_API_URL with status $TAG_STATUS"
echo "Response: $TAG_BODY"
exit 1
fi

# Extract the object SHA and type
TAG_OBJECT_SHA=$(echo "$TAG_BODY" | jq -r '.object.sha')
TAG_OBJECT_TYPE=$(echo "$TAG_BODY" | jq -r '.object.type')

echo "📌 Tag $TAG_NAME points to $TAG_OBJECT_TYPE: $TAG_OBJECT_SHA"

# Determine the commit SHA based on tag type
if [ "$TAG_OBJECT_TYPE" = "tag" ]; then
# Annotated tag - fetch the tag object to get the commit SHA
TAG_OBJECT_URL="https://api.github.com/repos/$REPO/git/tags/$TAG_OBJECT_SHA"
TAG_OBJECT_RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -w "\n%{http_code}" $TAG_OBJECT_URL)
TAG_OBJECT_BODY=$(echo "$TAG_OBJECT_RESPONSE" | head -n -1)
TAG_OBJECT_STATUS=$(echo "$TAG_OBJECT_RESPONSE" | tail -n 1)

if [ "$TAG_OBJECT_STATUS" != "200" ]; then
echo "❌ Failed to fetch annotated tag object with status $TAG_OBJECT_STATUS"
echo "Response: $TAG_OBJECT_BODY"
exit 1
fi

COMMIT_SHA=$(echo "$TAG_OBJECT_BODY" | jq -r '.object.sha')
echo "📌 Annotated tag references commit: $COMMIT_SHA"
elif [ "$TAG_OBJECT_TYPE" = "commit" ]; then
# Lightweight tag - directly references a commit
COMMIT_SHA=$TAG_OBJECT_SHA
echo "📌 Lightweight tag directly references commit: $COMMIT_SHA"
else
echo "❌ Unexpected tag object type: $TAG_OBJECT_TYPE"
exit 1
fi

# The tag points to the version commit, but artifacts were built with the previous commit
# Get the parent commit (the one that triggered the build)
echo "🔍 Getting parent commit of $COMMIT_SHA"
PARENT_COMMIT_URL="https://api.github.com/repos/$REPO/commits/$COMMIT_SHA"
PARENT_RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -w "\n%{http_code}" $PARENT_COMMIT_URL)
PARENT_BODY=$(echo "$PARENT_RESPONSE" | head -n -1)
PARENT_STATUS=$(echo "$PARENT_RESPONSE" | tail -n 1)

if [ "$PARENT_STATUS" != "200" ]; then
echo "❌ Failed to fetch commit information with status $PARENT_STATUS"
echo "Response: $PARENT_BODY"
exit 1
fi

# Get the parent SHA (the commit that triggered the build)
PARENT_SHA=$(echo "$PARENT_BODY" | jq -r '.parents[0].sha')
echo "📌 Parent commit (build trigger): $PARENT_SHA"

# Find the workflow run that created the release artifacts
# Retry with exponential backoff to handle race conditions
RUNS_API_URL="https://api.github.com/repos/$REPO/actions/runs?head_sha=$PARENT_SHA&status=success&event=push"
echo "🔍 Searching for successful workflow runs for parent commit $PARENT_SHA"

MAX_RETRIES=5
RETRY_COUNT=0
MAIN_RUN=""

while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ -z "$MAIN_RUN" ]; do
if [ $RETRY_COUNT -gt 0 ]; then
WAIT_TIME=$((5 * RETRY_COUNT))
echo "⏳ Waiting ${WAIT_TIME}s before retry $RETRY_COUNT/$MAX_RETRIES..."
sleep $WAIT_TIME
fi

RUNS_RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -w "\n%{http_code}" $RUNS_API_URL)
RUNS_BODY=$(echo "$RUNS_RESPONSE" | head -n -1)
RUNS_STATUS=$(echo "$RUNS_RESPONSE" | tail -n 1)

if [ "$RUNS_STATUS" != "200" ]; then
echo "❌ Failed to fetch workflow runs with status $RUNS_STATUS"
echo "Response: $RUNS_BODY"
exit 1
fi

# Find the Main workflow run
MAIN_RUN=$(echo "$RUNS_BODY" | jq -r '.workflow_runs[] | select(.name == "Main") | {id: .id, created_at: .created_at}')

if [ -z "$MAIN_RUN" ]; then
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "⚠️ Main workflow not found yet (attempt $RETRY_COUNT/$MAX_RETRIES)"
fi
fi
done

if [ -z "$MAIN_RUN" ]; then
echo "❌ No successful Main workflow run found for parent commit $PARENT_SHA after $MAX_RETRIES attempts"
echo "Available runs:"
echo "$RUNS_BODY" | jq -r '.workflow_runs[] | "\(.name): \(.id) (\(.status))"'
exit 1
fi

RUN_ID=$(echo "$MAIN_RUN" | jq -r '.id')
echo "✅ Found Main workflow run: $RUN_ID"

# Get artifacts from the workflow run
ARTIFACTS_API_URL="https://api.github.com/repos/$REPO/actions/runs/$RUN_ID/artifacts"
echo "🔍 Fetching artifacts from run $RUN_ID"

ARTIFACTS_RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -w "\n%{http_code}" $ARTIFACTS_API_URL)
ARTIFACTS_BODY=$(echo "$ARTIFACTS_RESPONSE" | head -n -1)
ARTIFACTS_STATUS=$(echo "$ARTIFACTS_RESPONSE" | tail -n 1)

if [ "$ARTIFACTS_STATUS" != "200" ]; then
echo "❌ Failed to fetch artifacts with status $ARTIFACTS_STATUS"
echo "Response: $ARTIFACTS_BODY"
exit 1
fi

# Find the artifact with the specified prefix (using full parent SHA to match artifact naming)
ARTIFACT_NAME="$PREFIX-$VERSION-${PARENT_SHA}"
ARTIFACT=$(echo "$ARTIFACTS_BODY" | jq -r --arg name "$ARTIFACT_NAME" '.artifacts[] | select(.name == $name)')

if [ -z "$ARTIFACT" ]; then
echo "❌ Artifact $ARTIFACT_NAME not found in workflow run $RUN_ID"
echo "Available artifacts:"
echo "$ARTIFACTS_BODY" | jq -r '.artifacts[].name'
exit 1
fi

ARTIFACT_ID=$(echo "$ARTIFACT" | jq -r '.id')
ARTIFACT_SIZE=$(echo "$ARTIFACT" | jq -r '.size_in_bytes')

echo "✅ Found artifact: $ARTIFACT_NAME (ID: $ARTIFACT_ID, Size: $ARTIFACT_SIZE bytes)"

# Output the results for GitHub Actions
{
echo "artifact_name=$ARTIFACT_NAME"
echo "artifact_id=$ARTIFACT_ID"
echo "run_id=$RUN_ID"
echo "commit_sha=$PARENT_SHA" # Use parent SHA since that's what built the artifacts
} >> "$OUTPUT_FILE"

echo "✅ Artifact information written to $OUTPUT_FILE"
Loading