chore: import upstream snapshot with attribution
Integration Tests / melodic (push) Has been cancelled
Integration Tests / noetic (push) Has been cancelled
Integration Tests / humble (push) Has been cancelled
Integration Tests / jazzy (push) Has been cancelled
Ruff Lint & Format / ruff (push) Has been cancelled
Sync main to develop / Check if sync is needed (push) Has been cancelled
Sync main to develop / Sync main to develop (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:36:23 +08:00
commit db1d565b64
209 changed files with 22046 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
name: Track Clone Metrics
on:
workflow_dispatch:
schedule:
- cron: '0 8 * * *' # Run every day at 8am
jobs:
clone-stats:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for proper branch operations
- name: Generate GitHub App token
id: generate_token
uses: tibdex/github-app-token@v2.1.0
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Switch to metrics branch
run: |
# Checkout or create metrics branch
if git show-ref --verify --quiet refs/remotes/origin/metrics; then
echo "📋 Checking out existing metrics branch..."
git checkout -b metrics origin/metrics || git checkout metrics
else
echo "🆕 Creating new metrics branch..."
git checkout -b metrics
fi
- name: Fetch clone data
env:
TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
mkdir -p .metrics
# Fetch clone metrics (contains both daily breakdown and 14-day totals)
curl -s -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
https://api.github.com/repos/${{ github.repository }}/traffic/clones \
> .metrics/clone_stats.json
echo "Clone metrics:"
cat .metrics/clone_stats.json
- name: Update daily metrics
run: |
# Process each day from the clones array
LAST_UPDATED=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
# Create daily CSV with header if it doesn't exist
if [ ! -f .metrics/daily_clone_metrics.csv ]; then
echo "date,total_clones,unique_cloners,last_updated" > .metrics/daily_clone_metrics.csv
fi
echo "📊 Processing daily metrics..."
jq -r '.clones[] | "\(.timestamp | split("T")[0]),\(.count),\(.uniques)"' .metrics/clone_stats.json | while IFS=',' read -r day_date count uniques; do
echo "Processing $day_date: $count clones, $uniques unique"
# Check if this date already exists in the CSV
if grep -q "^$day_date" .metrics/daily_clone_metrics.csv; then
echo "📝 Updating existing entry for $day_date..."
# Update existing entry
awk -v date="$day_date" -v count="$count" -v uniques="$uniques" -v last_updated="$LAST_UPDATED" '
BEGIN { FS=","; OFS="," }
/^[0-9]{4}-[0-9]{2}-[0-9]{2}/ && $1 == date {
print $1, count, uniques, last_updated;
updated=1;
next
}
{ print }
' .metrics/daily_clone_metrics.csv > .metrics/daily_clone_metrics_temp.csv
mv .metrics/daily_clone_metrics_temp.csv .metrics/daily_clone_metrics.csv
else
echo " Adding new daily entry for $day_date..."
echo "$day_date,$count,$uniques,$LAST_UPDATED" >> .metrics/daily_clone_metrics.csv
fi
done
echo "Daily metrics:"
tail -n 5 .metrics/daily_clone_metrics.csv
- name: Update 14-day rolling metrics
run: |
# Process 14-day metrics
COUNT_14D=$(jq '.count' .metrics/clone_stats.json)
UNIQUES_14D=$(jq '.uniques' .metrics/clone_stats.json)
DATE_ONLY=$(date -u +"%Y-%m-%d")
LAST_UPDATED=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
echo "📊 Processing 14-day metrics... for date: $DATE_ONLY"
echo "Processing values: $COUNT_14D clones, $UNIQUES_14D unique"
# Create 14-day CSV with header if it doesn't exist
if [ ! -f .metrics/rolling_14d_clone_metrics.csv ]; then
echo "date,total_clones_14d,unique_cloners_14d,last_updated" > .metrics/rolling_14d_clone_metrics.csv
echo "📄 Created new 14-day rolling CSV file"
fi
# Check if today's date already exists in the 14-day CSV
if grep -q "^$DATE_ONLY" .metrics/rolling_14d_clone_metrics.csv; then
echo "📝 Updating existing 14-day rolling entry for $DATE_ONLY..."
# Update existing entry
awk -v date="$DATE_ONLY" -v count="$COUNT_14D" -v uniques="$UNIQUES_14D" -v last_updated="$LAST_UPDATED" '
BEGIN { FS=","; OFS=","; updated=0 }
/^[0-9]{4}-[0-9]{2}-[0-9]{2}/ && $1 == date {
print $1, count, uniques, last_updated;
updated=1;
next
}
{ print }
END { if (!updated) print date, count, uniques, last_updated }
' .metrics/rolling_14d_clone_metrics.csv > .metrics/rolling_14d_clone_metrics_temp.csv
mv .metrics/rolling_14d_clone_metrics_temp.csv .metrics/rolling_14d_clone_metrics.csv
echo "✅ Updated existing entry"
else
echo " Adding new 14-day rolling entry for $DATE_ONLY..."
echo "$DATE_ONLY,$COUNT_14D,$UNIQUES_14D,$LAST_UPDATED" >> .metrics/rolling_14d_clone_metrics.csv
echo "✅ Added new entry"
fi
echo "14-day rolling metrics:"
tail -n 5 .metrics/rolling_14d_clone_metrics.csv
- name: Commit and push results
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
git config user.name "CloneMetricsBot[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Add both CSV files
git add .metrics/daily_clone_metrics.csv .metrics/rolling_14d_clone_metrics.csv
# Check if there are changes to commit
if git diff --staged --quiet; then
echo "️ No changes to commit - CSV data is up to date"
else
echo "📝 Committing changes..."
git commit -m "Automated update: repository clone metrics $(date)"
echo "🚀 Pushing to metrics branch..."
git push --force-with-lease origin metrics
fi
+109
View File
@@ -0,0 +1,109 @@
name: Track Download Metrics
on:
workflow_dispatch:
workflow_run:
workflows: ["Track View Metrics"] # exact name of PyPI workflow
types: [completed]
jobs:
download-stats:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for proper branch operations
- name: Generate GitHub App token
id: generate_token
uses: tibdex/github-app-token@v2.1.0
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Switch to metrics branch
run: |
# Checkout or create metrics branch
if git show-ref --verify --quiet refs/remotes/origin/metrics; then
echo "📋 Checking out existing metrics branch..."
git checkout -b metrics origin/metrics || git checkout metrics
else
echo "🆕 Creating new metrics branch..."
git checkout -b metrics
fi
- name: Fetch download data
env:
TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
mkdir -p .metrics
# Fetch download metrics from releases
curl -s -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases?per_page=100" \
> .metrics/releases.json
echo "Download metrics:"
jq '[.[] | {tag: .tag_name, date: .published_at, total_downloads: ([.assets[].download_count] | add), assets_count: (.assets | length)}]' .metrics/releases.json
- name: Update total download metrics
run: |
# Process each release from the releases array
LAST_UPDATED=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
# Create total download CSV with header if it doesn't exist
if [ ! -f .metrics/total_download_metrics.csv ]; then
echo "date,release_tag,total_downloads,last_updated" > .metrics/total_download_metrics.csv
fi
echo "📊 Processing total download metrics..."
jq -r '.[] | "\(.published_at[0:10]),\(.tag_name),\([.assets[].download_count] | add)"' .metrics/releases.json | while IFS=',' read -r release_date tag_name total_downloads; do
echo "Processing $release_date: $tag_name - $total_downloads downloads"
# Check if this release already exists in the CSV
if grep -q ",$tag_name," .metrics/total_download_metrics.csv; then
echo "📝 Updating existing entry for $tag_name..."
# Update existing entry
awk -v tag="$tag_name" -v downloads="$total_downloads" -v last_updated="$LAST_UPDATED" '
BEGIN { FS=","; OFS="," }
$2 == tag {
print $1, $2, downloads, last_updated;
updated=1;
next
}
{ print }
' .metrics/total_download_metrics.csv > .metrics/total_download_metrics_temp.csv
mv .metrics/total_download_metrics_temp.csv .metrics/total_download_metrics.csv
else
echo " Adding new download entry for $tag_name..."
echo "$release_date,$tag_name,$total_downloads,$LAST_UPDATED" >> .metrics/total_download_metrics.csv
fi
done
echo "Total download metrics:"
tail -n 5 .metrics/total_download_metrics.csv
- name: Commit and push results
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
git config user.name "DownloadMetricsBot[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Add CSV file
git add .metrics/total_download_metrics.csv
# Check if there are changes to commit
if git diff --staged --quiet; then
echo "️ No changes to commit - CSV data is up to date"
else
echo "📝 Committing changes..."
git commit -m "Automated update: repository download metrics $(date)"
echo "🚀 Pushing to metrics branch..."
git push --force-with-lease origin metrics
fi
+68
View File
@@ -0,0 +1,68 @@
name: Integration Tests
on:
pull_request:
push:
branches: [develop, main]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
integration:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ros-distro: [melodic, noetic, humble, jazzy]
include:
- ros-distro: melodic
dockerfile: Dockerfile.ros1-melodic
container-name: integration-ros-melodic
- ros-distro: noetic
dockerfile: Dockerfile.ros1-noetic
container-name: integration-ros-noetic
- ros-distro: humble
dockerfile: Dockerfile.ros2-humble
container-name: integration-ros2-humble
- ros-distro: jazzy
dockerfile: Dockerfile.ros2-jazzy
container-name: integration-ros2-jazzy
name: ${{ matrix.ros-distro }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync --extra dev
- name: Start ROS container
run: |
ROS_DOCKERFILE=${{ matrix.dockerfile }} \
ROS_CONTAINER_NAME=${{ matrix.container-name }} \
docker compose -f tests/integration/docker-compose.yml up --build -d --wait
timeout-minutes: 10
- name: Detect ROS version
run: uv run python tests/integration/test_quick_detect.py
- name: Run integration tests
run: |
uv run pytest tests/integration/ -v \
--ros-distro ${{ matrix.ros-distro }} --skip-compose
- name: Tear down
if: always()
run: |
ROS_DOCKERFILE=${{ matrix.dockerfile }} \
ROS_CONTAINER_NAME=${{ matrix.container-name }} \
docker compose -f tests/integration/docker-compose.yml down --volumes --remove-orphans
+154
View File
@@ -0,0 +1,154 @@
name: Merge branch into target
# Manual trigger only
on:
workflow_dispatch:
inputs:
source_branch:
description: "Branch to merge from (default: the branch this workflow is run on)"
type: string
required: false
default: ""
target_branch:
description: "Branch to merge into"
type: string
required: true
default: develop
delete_source_branch:
description: "Delete the source branch after a successful merge"
type: boolean
required: false
default: false
check_only:
description: "Verify the merge succeeds without pushing"
type: boolean
required: false
default: false
permissions:
contents: write
pull-requests: write
jobs:
merge-into-branch:
runs-on: ubuntu-latest
name: Merge '${{ inputs.source_branch || github.ref_name }}' into '${{ inputs.target_branch }}'
env:
SRC_BRANCH: ${{ inputs.source_branch || github.ref_name }}
TARGET_BRANCH: ${{ inputs.target_branch }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.target_branch }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up git user
run: |
git config user.name "RobotMCP Bot"
git config user.email "admin@robotmcp.ai"
# Make sure we have the source branch and a common ancestor to merge.
git fetch origin "$SRC_BRANCH"
if ! git merge-base "$TARGET_BRANCH" "origin/$SRC_BRANCH"; then
echo "No common ancestor found for merging!"
exit 1
fi
- name: Abort if conflict-resolution PR already open
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SAFE_SRC="${SRC_BRANCH//\//_}"
SAFE_TARGET="${TARGET_BRANCH//\//_}"
EXISTING_PR=$(gh pr list \
--base "$TARGET_BRANCH" \
--state open \
--json url,headRefName \
--jq ".[] | select(.headRefName | startswith(\"${SAFE_SRC}-\") and endswith(\"/merge-into-${SAFE_TARGET}\")) | .url" \
2>/dev/null | head -n1 || true)
if [ -n "$EXISTING_PR" ]; then
echo "::error::An open conflict-resolution PR already exists for '$SRC_BRANCH' -> '$TARGET_BRANCH': $EXISTING_PR"
echo "::error::Resolve and merge that PR before re-running this workflow."
exit 1
fi
- name: Attempt merge
id: merge
run: |
set +e
git merge \
--no-ff \
--no-edit \
--commit \
--message "Merged '$SRC_BRANCH' into '$TARGET_BRANCH'" \
"origin/$SRC_BRANCH"
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "Merge conflicts detected; aborting in-runner merge."
git merge --abort || true
echo "conflict=true" >> "$GITHUB_OUTPUT"
else
echo "conflict=false" >> "$GITHUB_OUTPUT"
fi
exit 0
- name: Push merged branch
if: ${{ steps.merge.outputs.conflict == 'false' && !inputs.check_only }}
run: git push origin "$TARGET_BRANCH"
- name: Delete source branch
if: ${{ inputs.delete_source_branch && steps.merge.outputs.conflict == 'false' && !inputs.check_only }}
run: git push origin --delete "$SRC_BRANCH"
- name: Create sync branch for conflict resolution
id: sync-branch
if: ${{ steps.merge.outputs.conflict == 'true' && !inputs.check_only }}
run: |
SAFE_SRC="${SRC_BRANCH//\//_}"
SAFE_TARGET="${TARGET_BRANCH//\//_}"
SHA_SHORT=$(git rev-parse --short "origin/$SRC_BRANCH")
SYNC_BRANCH="${SAFE_SRC}-${SHA_SHORT}/merge-into-${SAFE_TARGET}"
echo "Creating sync branch: $SYNC_BRANCH"
# If a stale sync branch exists from a previously closed-but-unmerged PR, drop it.
git push origin --delete "$SYNC_BRANCH" 2>/dev/null || true
git branch -f "$SYNC_BRANCH" "origin/$SRC_BRANCH"
git push origin "$SYNC_BRANCH"
echo "SYNC_BRANCH=$SYNC_BRANCH" >> "$GITHUB_OUTPUT"
- name: Open conflict-resolution PR
if: ${{ steps.merge.outputs.conflict == 'true' && !inputs.check_only }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SYNC_BRANCH: ${{ steps.sync-branch.outputs.SYNC_BRANCH }}
run: |
gh pr create \
--base "$TARGET_BRANCH" \
--head "$SYNC_BRANCH" \
--title "Conflict-resolution: merge '$SRC_BRANCH' into '$TARGET_BRANCH'" \
--draft \
--body "Automated merge of \`$SRC_BRANCH\` into \`$TARGET_BRANCH\` failed due to conflicts.
A temporary sync branch \`$SYNC_BRANCH\` was created off \`$SRC_BRANCH\` so conflicts can be resolved even when both branches are protected.
**Do not merge this PR through the GitHub UI.** Squash-merging a sync branch rewrites its commits, which inflates the commit count of later merges into \`$TARGET_BRANCH\`. Instead, a maintainer should:
1. Check out the sync branch locally:
\`\`\`
git fetch origin
git checkout $SYNC_BRANCH
\`\`\`
2. Merge '$TARGET_BRANCH' into the sync branch and resolve conflicts:
\`\`\`
git merge origin/$TARGET_BRANCH
# resolve conflicts, then commit
\`\`\`
3. Push the resolved sync branch:
\`\`\`
git push origin $SYNC_BRANCH
\`\`\`
4. Re-run the 'Merge branch into target' workflow from this branch (\`$SYNC_BRANCH\`) with target branch '$TARGET_BRANCH'.
5. After the workflow completes, this PR will close automatically."
- name: Fail on conflict
if: ${{ steps.merge.outputs.conflict == 'true' }}
run: |
echo "::error::Merge of '$SRC_BRANCH' into '$TARGET_BRANCH' has conflicts."
exit 1
+110
View File
@@ -0,0 +1,110 @@
name: Publish (PyPI + MCP Registry)
on:
workflow_dispatch:
push:
tags:
- 'v*'
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: true
jobs:
pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write # REQUIRED for PyPI Trusted Publishing (OIDC)
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Validate version matches tag
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
run: |
set -euo pipefail
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
PYPROJECT_VERSION=$(grep -oP '^version = "\K[^"]+' pyproject.toml)
SERVER_VERSION=$(grep -oP '"version":\s*"\K[^"]+' server.json | head -1)
SERVER_PKG_VERSION=$(grep -oP '"version":\s*"\K[^"]+' server.json | tail -1)
ERRORS=0
[ "$PYPROJECT_VERSION" != "$TAG_VERSION" ] && echo "pyproject.toml: $PYPROJECT_VERSION != $TAG_VERSION" && ERRORS=1
[ "$SERVER_VERSION" != "$TAG_VERSION" ] && echo "server.json: $SERVER_VERSION != $TAG_VERSION" && ERRORS=1
[ "$SERVER_PKG_VERSION" != "$TAG_VERSION" ] && echo "server.json package: $SERVER_PKG_VERSION != $TAG_VERSION" && ERRORS=1
if [ $ERRORS -eq 1 ]; then
echo "Please update all version fields to $TAG_VERSION before creating the tag."
exit 1
fi
echo "All versions match: $TAG_VERSION"
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
run: pipx install uv
- name: Sync dependencies
run: uv sync --extra dev
- name: Run ruff lint
run: uvx ruff check .
- name: Run ruff format check
run: uvx ruff format --check .
- name: Run tests
run: |
if [ -d tests ]; then
uv run pytest -q -m "not slow and not integration" || [ $? -eq 5 ] # exit 5 = no tests collected
else
echo "No tests/ directory; skipping."
fi
- name: Build package
run: uv build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
registry:
name: Publish to MCP Registry
runs-on: ubuntu-latest
timeout-minutes: 10
needs: pypi
permissions:
contents: read
id-token: write # REQUIRED for MCP Registry GitHub OIDC login
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Wait for PyPI metadata propagation
run: sleep 20
- name: Install MCP Publisher
run: |
set -euo pipefail
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
echo "Get the latest release version"
LATEST_VERSION=$(curl -s https://api.github.com/repos/modelcontextprotocol/registry/releases/latest | jq -r '.tag_name')
echo "Installing MCP Publisher version: $LATEST_VERSION"
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_${OS}_${ARCH}.tar.gz" \
| tar xz mcp-publisher
- name: Login to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Publish to MCP Registry
run: ./mcp-publisher publish
+37
View File
@@ -0,0 +1,37 @@
name: Publish MCP Registry
on:
workflow_dispatch:
concurrency:
group: publish-mcp-${{ github.ref }}
cancel-in-progress: true
jobs:
mcp-registry:
name: Publish to MCP Registry
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
id-token: write # REQUIRED for MCP Registry GitHub OIDC login
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install MCP Publisher
run: |
set -euo pipefail
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
echo "Get the latest release version"
LATEST_VERSION=$(curl -s https://api.github.com/repos/modelcontextprotocol/registry/releases/latest | jq -r '.tag_name')
echo "Installing MCP Publisher version: $LATEST_VERSION"
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_${OS}_${ARCH}.tar.gz" \
| tar xz mcp-publisher
- name: Login to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Publish to MCP Registry
run: ./mcp-publisher publish
+53
View File
@@ -0,0 +1,53 @@
name: Publish PyPI
on:
workflow_dispatch:
concurrency:
group: publish-pypi-${{ github.ref }}
cancel-in-progress: true
jobs:
pypi:
name: Publish PyPI
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
run: pipx install uv
- name: Sync dependencies
run: uv sync --extra dev
- name: Run ruff lint
run: uvx ruff check .
- name: Run ruff format check
run: uvx ruff format --check .
- name: Run tests
run: |
if [ -d tests ]; then
uv run pytest -q -m "not slow and not integration" || [ $? -eq 5 ] # exit 5 = no tests collected
else
echo "No tests/ directory; skipping."
fi
- name: Build package
run: uv build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
+27
View File
@@ -0,0 +1,27 @@
name: Ruff Lint & Format
on:
pull_request:
push:
branches: [develop, main]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Ruff
run: pip install ruff
- name: Run Ruff check (lint)
run: ruff check .
- name: Run Ruff format (verify formatting)
run: ruff format --check .
+115
View File
@@ -0,0 +1,115 @@
name: Sync main to develop
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
check:
runs-on: ubuntu-latest
name: Check if sync is needed
outputs:
skip: ${{ steps.check-sync.outputs.SKIP }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Check if sync is needed
id: check-sync
run: |
# Use merge-tree to check if merging main into develop would produce
# any content changes, without touching the working tree. This correctly
# handles cherry-picks where the SHAs differ but the content is identical.
if MERGE_TREE=$(git merge-tree --write-tree origin/develop origin/main 2>&1); then
DEVELOP_TREE=$(git rev-parse origin/develop^{tree})
echo "Merge result tree: $MERGE_TREE"
echo "Develop tree: $DEVELOP_TREE"
if [ "$MERGE_TREE" = "$DEVELOP_TREE" ]; then
echo "Merge would not change develop. Skipping sync."
echo "SKIP=true" >> "$GITHUB_OUTPUT"
else
echo "Merge would introduce changes. Sync needed."
echo "SKIP=false" >> "$GITHUB_OUTPUT"
fi
else
echo "Merge would have conflicts. Sync needed."
echo "SKIP=false" >> "$GITHUB_OUTPUT"
fi
sync:
needs: check
if: needs.check.outputs.skip != 'true'
runs-on: ubuntu-latest
name: Sync main to develop
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up git user
run: |
git config user.name "RobotMCP Bot"
git config user.email "admin@robotmcp.ai"
- name: Create syncing branch off of main
id: create-sync-branch
run: |
SYNC_BRANCH="main-$(git rev-parse --short HEAD)/sync"
echo "Creating syncing branch: $SYNC_BRANCH"
git switch --create "$SYNC_BRANCH"
git push origin "$SYNC_BRANCH"
echo "SYNC_BRANCH=$SYNC_BRANCH" >> "$GITHUB_OUTPUT"
- name: Create syncing PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SYNC_BRANCH: ${{ steps.create-sync-branch.outputs.SYNC_BRANCH }}
run: |
gh pr create \
--base develop \
--head "$SYNC_BRANCH" \
--title "chore: sync main to develop" \
--draft \
--body "Auto-syncing 'main' to 'develop'
If this PR does not merge automatically, there are conflicts between 'main' and 'develop'.
**Do not merge this PR through GitHub.** A maintainer should:
1. Pull this temporary syncing branch locally:
\`\`\`
git fetch origin
git checkout $SYNC_BRANCH
\`\`\`
2. Merge 'develop' into this branch and resolve conflicts:
\`\`\`
git merge origin/develop
# resolve any conflicts, then commit
\`\`\`
3. Push the resolved branch:
\`\`\`
git push origin $SYNC_BRANCH
\`\`\`
4. Run the 'Merge branch into target' workflow from this branch with target branch 'develop'
5. After the workflow completes, this PR will close automatically"
- name: Merge and push to develop
run: |
git switch develop
git merge --no-ff --no-edit --commit --message "chore: sync main to develop" "${{ steps.create-sync-branch.outputs.SYNC_BRANCH }}"
git push origin develop
- name: Cleanup sync branch
continue-on-error: true
env:
SYNC_BRANCH: ${{ steps.create-sync-branch.outputs.SYNC_BRANCH }}
run: git push origin :"$SYNC_BRANCH"
+151
View File
@@ -0,0 +1,151 @@
name: Track View Metrics
on:
workflow_dispatch:
workflow_run:
workflows: ["Track Clone Metrics"] # exact name of PyPI workflow
types: [completed]
jobs:
view-stats:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for proper branch operations
- name: Generate GitHub App token
id: generate_token
uses: tibdex/github-app-token@v2.1.0
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Switch to metrics branch
run: |
# Checkout or create metrics branch
if git show-ref --verify --quiet refs/remotes/origin/metrics; then
echo "📋 Checking out existing metrics branch..."
git checkout -b metrics origin/metrics || git checkout metrics
else
echo "🆕 Creating new metrics branch..."
git checkout -b metrics
fi
- name: Fetch view data
env:
TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
mkdir -p .metrics
# Fetch view metrics (contains both daily breakdown and 14-day totals)
curl -s -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $TOKEN" \
https://api.github.com/repos/${{ github.repository }}/traffic/views \
> .metrics/view_stats.json
echo "View metrics:"
cat .metrics/view_stats.json
- name: Update daily metrics
run: |
# Process each day from the views array
LAST_UPDATED=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
# Create daily CSV with header if it doesn't exist
if [ ! -f .metrics/daily_view_metrics.csv ]; then
echo "date,total_views,unique_visitors,last_updated" > .metrics/daily_view_metrics.csv
fi
echo "📊 Processing daily metrics..."
jq -r '.views[] | "\(.timestamp | split("T")[0]),\(.count),\(.uniques)"' .metrics/view_stats.json | while IFS=',' read -r day_date count uniques; do
echo "Processing $day_date: $count views, $uniques unique"
# Check if this date already exists in the CSV
if grep -q "^$day_date" .metrics/daily_view_metrics.csv; then
echo "📝 Updating existing entry for $day_date..."
# Update existing entry
awk -v date="$day_date" -v count="$count" -v uniques="$uniques" -v last_updated="$LAST_UPDATED" '
BEGIN { FS=","; OFS="," }
/^[0-9]{4}-[0-9]{2}-[0-9]{2}/ && $1 == date {
print $1, count, uniques, last_updated;
updated=1;
next
}
{ print }
' .metrics/daily_view_metrics.csv > .metrics/daily_view_metrics_temp.csv
mv .metrics/daily_view_metrics_temp.csv .metrics/daily_view_metrics.csv
else
echo " Adding new daily entry for $day_date..."
echo "$day_date,$count,$uniques,$LAST_UPDATED" >> .metrics/daily_view_metrics.csv
fi
done
echo "Daily metrics:"
tail -n 5 .metrics/daily_view_metrics.csv
- name: Update 14-day rolling metrics
run: |
# Process 14-day metrics
COUNT_14D=$(jq '.count' .metrics/view_stats.json)
UNIQUES_14D=$(jq '.uniques' .metrics/view_stats.json)
DATE_ONLY=$(date -u +"%Y-%m-%d")
LAST_UPDATED=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
echo "📊 Processing 14-day metrics... for date: $DATE_ONLY"
echo "Processing values: $COUNT_14D views, $UNIQUES_14D unique"
# Create 14-day CSV with header if it doesn't exist
if [ ! -f .metrics/rolling_14d_view_metrics.csv ]; then
echo "date,total_views_14d,unique_visitors_14d,last_updated" > .metrics/rolling_14d_view_metrics.csv
echo "📄 Created new 14-day rolling CSV file"
fi
# Check if today's date already exists in the 14-day CSV
if grep -q "^$DATE_ONLY" .metrics/rolling_14d_view_metrics.csv; then
echo "📝 Updating existing 14-day rolling entry for $DATE_ONLY..."
# Update existing entry
awk -v date="$DATE_ONLY" -v count="$COUNT_14D" -v uniques="$UNIQUES_14D" -v last_updated="$LAST_UPDATED" '
BEGIN { FS=","; OFS=","; updated=0 }
/^[0-9]{4}-[0-9]{2}-[0-9]{2}/ && $1 == date {
print $1, count, uniques, last_updated;
updated=1;
next
}
{ print }
END { if (!updated) print date, count, uniques, last_updated }
' .metrics/rolling_14d_view_metrics.csv > .metrics/rolling_14d_view_metrics_temp.csv
mv .metrics/rolling_14d_view_metrics_temp.csv .metrics/rolling_14d_view_metrics.csv
echo "✅ Updated existing entry"
else
echo " Adding new 14-day rolling entry for $DATE_ONLY..."
echo "$DATE_ONLY,$COUNT_14D,$UNIQUES_14D,$LAST_UPDATED" >> .metrics/rolling_14d_view_metrics.csv
echo "✅ Added new entry"
fi
echo "14-day rolling metrics:"
tail -n 5 .metrics/rolling_14d_view_metrics.csv
- name: Commit and push results
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
git config user.name "ViewMetricsBot[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Add both CSV files
git add .metrics/daily_view_metrics.csv .metrics/rolling_14d_view_metrics.csv
# Check if there are changes to commit
if git diff --staged --quiet; then
echo "️ No changes to commit - CSV data is up to date"
else
echo "📝 Committing changes..."
git commit -m "Automated update: repository view metrics $(date)"
echo "🚀 Pushing to metrics branch..."
git push --force-with-lease origin metrics
fi