|
| 1 | +--- |
| 2 | +name: Update CodeGuard Rules |
| 3 | + |
| 4 | +on: |
| 5 | + schedule: |
| 6 | + - cron: "0 9 1 * *" # Monthly on the 1st at 9:00 UTC |
| 7 | + workflow_dispatch: |
| 8 | + inputs: |
| 9 | + force: |
| 10 | + description: "Force update even if version matches" |
| 11 | + required: false |
| 12 | + default: false |
| 13 | + type: boolean |
| 14 | + |
| 15 | +permissions: |
| 16 | + contents: write |
| 17 | + pull-requests: write |
| 18 | + |
| 19 | +jobs: |
| 20 | + update-rules: |
| 21 | + runs-on: ubuntu-latest |
| 22 | + steps: |
| 23 | + - name: Checkout repository |
| 24 | + uses: actions/checkout@v6 |
| 25 | + with: |
| 26 | + ref: ${{ github.event.repository.default_branch }} |
| 27 | + |
| 28 | + - name: Update CodeGuard rules |
| 29 | + env: |
| 30 | + GH_TOKEN: ${{ github.token }} |
| 31 | + FORCE: ${{ inputs.force }} |
| 32 | + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} |
| 33 | + run: | |
| 34 | + set -euo pipefail |
| 35 | +
|
| 36 | + # Format configurations: directory -> zip asset name |
| 37 | + declare -A FORMATS=( |
| 38 | + [".cursor/rules"]="ide-rules-cursor.zip" |
| 39 | + [".windsurf/rules"]="ide-rules-windsurf.zip" |
| 40 | + [".github/instructions"]="ide-rules-copilot.zip" |
| 41 | + [".agent/rules"]="ide-rules-antigravity.zip" |
| 42 | + ) |
| 43 | +
|
| 44 | + # File patterns for each format |
| 45 | + declare -A PATTERNS=( |
| 46 | + [".cursor/rules"]="codeguard-*.mdc" |
| 47 | + [".windsurf/rules"]="codeguard-*.md" |
| 48 | + [".github/instructions"]="codeguard-*.instructions.md" |
| 49 | + [".agent/rules"]="codeguard-*.md" |
| 50 | + ) |
| 51 | +
|
| 52 | + # Detect which formats exist |
| 53 | + DETECTED_DIRS="" |
| 54 | + for dir in "${!FORMATS[@]}"; do |
| 55 | + pattern="${PATTERNS[$dir]}" |
| 56 | + if [ -d "$dir" ] && ls "$dir"/$pattern 1>/dev/null 2>&1; then |
| 57 | + DETECTED_DIRS="$DETECTED_DIRS $dir" |
| 58 | + echo "Found: $dir" |
| 59 | + fi |
| 60 | + done |
| 61 | + DETECTED_DIRS=$(echo "$DETECTED_DIRS" | xargs) |
| 62 | +
|
| 63 | + if [ -z "$DETECTED_DIRS" ]; then |
| 64 | + echo "No CodeGuard rules directories found. Nothing to update." |
| 65 | + exit 0 |
| 66 | + fi |
| 67 | +
|
| 68 | + # Get current version from any rule file |
| 69 | + CURRENT_VERSION=$(grep -rh "^version:" $DETECTED_DIRS 2>/dev/null | head -1 | awk '{print $2}') || true |
| 70 | + CURRENT_VERSION="${CURRENT_VERSION:-0.0.0}" |
| 71 | + echo "Current version: $CURRENT_VERSION" |
| 72 | +
|
| 73 | + # Fetch latest release info |
| 74 | + RELEASE=$(curl -sfS "https://api.github.com/repos/cosai-oasis/project-codeguard/releases/latest") || { |
| 75 | + echo "::error::Failed to fetch latest release from GitHub API" |
| 76 | + exit 1 |
| 77 | + } |
| 78 | + LATEST_VERSION=$(echo "$RELEASE" | jq -r '.tag_name' | sed 's/^v//') |
| 79 | + RELEASE_URL=$(echo "$RELEASE" | jq -r '.html_url') |
| 80 | +
|
| 81 | + if [ -z "$LATEST_VERSION" ] || [ "$LATEST_VERSION" = "null" ]; then |
| 82 | + echo "::error::Invalid release data from GitHub API" |
| 83 | + exit 1 |
| 84 | + fi |
| 85 | + echo "Latest version: $LATEST_VERSION" |
| 86 | +
|
| 87 | + # Check if update needed |
| 88 | + if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ] && [ "$FORCE" != "true" ]; then |
| 89 | + echo "Already up to date (v$CURRENT_VERSION)" |
| 90 | + exit 0 |
| 91 | + fi |
| 92 | + echo "Updating: v$CURRENT_VERSION -> v$LATEST_VERSION" |
| 93 | +
|
| 94 | + # Download and extract each format's zip asset |
| 95 | + UPDATED="" |
| 96 | + for dir in $DETECTED_DIRS; do |
| 97 | + asset="${FORMATS[$dir]}" |
| 98 | + pattern="${PATTERNS[$dir]}" |
| 99 | + |
| 100 | + # Get download URL for this asset |
| 101 | + ASSET_URL=$(echo "$RELEASE" | jq -r ".assets[] | select(.name == \"$asset\") | .browser_download_url") |
| 102 | + |
| 103 | + if [ -z "$ASSET_URL" ] || [ "$ASSET_URL" = "null" ]; then |
| 104 | + echo "Warning: Asset $asset not found in release" |
| 105 | + continue |
| 106 | + fi |
| 107 | +
|
| 108 | + echo "Downloading $asset..." |
| 109 | + mkdir -p /tmp/codeguard-update |
| 110 | + curl -sfSL "$ASSET_URL" -o "/tmp/codeguard-update/$asset" || { |
| 111 | + echo "Warning: Failed to download $asset" |
| 112 | + continue |
| 113 | + } |
| 114 | +
|
| 115 | + # Extract and update |
| 116 | + rm -f "$dir"/$pattern 2>/dev/null || true |
| 117 | + unzip -q -o "/tmp/codeguard-update/$asset" -d /tmp/codeguard-update/ |
| 118 | +
|
| 119 | + # Copy rules from extracted zip |
| 120 | + SOURCE_DIR="/tmp/codeguard-update/$dir" |
| 121 | + if [ -d "$SOURCE_DIR" ] && cp "$SOURCE_DIR"/$pattern "$dir/" 2>/dev/null; then |
| 122 | + UPDATED="$UPDATED $dir" |
| 123 | + echo "Updated: $dir" |
| 124 | + else |
| 125 | + echo "Warning: No files found in $SOURCE_DIR" |
| 126 | + fi |
| 127 | +
|
| 128 | + rm -rf /tmp/codeguard-update/* |
| 129 | + done |
| 130 | + UPDATED=$(echo "$UPDATED" | xargs) |
| 131 | +
|
| 132 | + if [ -z "$UPDATED" ]; then |
| 133 | + echo "No formats were updated" |
| 134 | + exit 0 |
| 135 | + fi |
| 136 | +
|
| 137 | + if git diff --quiet; then |
| 138 | + echo "No file changes detected" |
| 139 | + exit 0 |
| 140 | + fi |
| 141 | +
|
| 142 | + # Create branch and commit |
| 143 | + BRANCH="codeguard-rules-update" |
| 144 | + git config user.name "github-actions[bot]" |
| 145 | + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" |
| 146 | + git checkout -B "$BRANCH" |
| 147 | + git add . |
| 148 | + git commit -m "chore: Update CodeGuard rules to v$LATEST_VERSION" |
| 149 | + git push -f origin "$BRANCH" |
| 150 | +
|
| 151 | + # Create or update PR |
| 152 | + UPDATED_BULLETS=$(echo "$UPDATED" | tr ' ' '\n' | sed 's/^/- /') |
| 153 | + PR_BODY=$(printf '%s\n\n%s\n%s\n\n%s\n\n%s\n%s' \ |
| 154 | + "Updates CodeGuard security rules to **v$LATEST_VERSION**." \ |
| 155 | + "**Updated:**" \ |
| 156 | + "${UPDATED_BULLETS}" \ |
| 157 | + "**Release notes:** ${RELEASE_URL}" \ |
| 158 | + "---" \ |
| 159 | + "*Auto-generated by [update-codeguard-rules](.github/workflows/update-codeguard-rules.yml)*") |
| 160 | +
|
| 161 | + if gh pr list --head "$BRANCH" --state open | grep -q .; then |
| 162 | + gh pr edit "$BRANCH" --title "chore: Update CodeGuard rules to v$LATEST_VERSION" --body "$PR_BODY" |
| 163 | + else |
| 164 | + gh pr create --base "${DEFAULT_BRANCH:-main}" --head "$BRANCH" \ |
| 165 | + --title "chore: Update CodeGuard rules to v$LATEST_VERSION" --body "$PR_BODY" |
| 166 | + fi |
| 167 | +
|
| 168 | + echo "Done! PR created/updated for v$LATEST_VERSION" |
0 commit comments