|
| 1 | +--- |
| 2 | +# ============================================================= |
| 3 | +# Check for New MegaLinter Version Workflow |
| 4 | +# |
| 5 | +# This workflow checks daily for new versions of the MegaLinter |
| 6 | +# custom-builder action and creates a release if a new version is found. |
| 7 | +# |
| 8 | +# Usage: |
| 9 | +# - Runs daily at 00:00 UTC via schedule |
| 10 | +# - Can be manually triggered via workflow_dispatch |
| 11 | +# - Compares MegaLinter tags with current repository tags |
| 12 | +# - Creates a new release if a new MegaLinter version is detected |
| 13 | +# |
| 14 | +# The workflow will: |
| 15 | +# 1. Fetch all tags from oxsecurity/megalinter repository |
| 16 | +# 2. Fetch all tags from the current repository |
| 17 | +# 3. Find new tags that exist in MegaLinter but not in current repo |
| 18 | +# 4. Create a release with the new version tag |
| 19 | +# 5. The release will trigger megalinter-custom-flavor-builder.yml |
| 20 | +# |
| 21 | +# Required permissions: |
| 22 | +# - contents: write (to create releases and tags) |
| 23 | +# |
| 24 | +# Required repository secrets: |
| 25 | +# - PAT_TOKEN: Personal Access Token with 'contents' and 'actions' permissions |
| 26 | +# This is required to trigger the builder workflow. Without it, the workflow |
| 27 | +# will fail and the release will be deleted (to be recreated once the token is set). |
| 28 | +# |
| 29 | +# SECURITY WARNING: Using a PAT comes with risk. Open-source projects have been |
| 30 | +# heavily targeted by supply-chain attacks in recent months, and a leaked or |
| 31 | +# compromised PAT can give attackers broad write access to your repository — |
| 32 | +# better safe than sorry! If you do not need fully automatic daily version |
| 33 | +# sync, you can skip the PAT entirely and trigger this workflow manually |
| 34 | +# (Actions tab → "Check for New MegaLinter Version" → "Run workflow") whenever |
| 35 | +# you want to bump to a new MegaLinter release. Only configure PAT_TOKEN if |
| 36 | +# the daily automation is worth the trade-off, and always scope it to this |
| 37 | +# single repository with the minimum required permissions. |
| 38 | +# |
| 39 | +# To create a Fine-grained PAT (still discouraged unless you need automation): |
| 40 | +# 1. Go to GitHub Settings > Developer settings > Personal access tokens > Fine-grained tokens |
| 41 | +# 2. Click "Generate new token" |
| 42 | +# 3. Give it a descriptive name (e.g., "MegaLinter Auto-Release") |
| 43 | +# 4. Set expiration (e.g., 90 days or 1 year) |
| 44 | +# 5. Under "Repository access", select "Only select repositories" |
| 45 | +# 6. Choose this repository (megalinter-custom-flavor-npm-groovy-lint) |
| 46 | +# 7. Under "Permissions" > "Repository permissions": |
| 47 | +# - Contents: Read and write |
| 48 | +# - Actions: Read and write |
| 49 | +# 8. Click "Generate token" and copy the token |
| 50 | +# 9. In your repository, go to Settings > Secrets and variables > Actions |
| 51 | +# 10. Click "New repository secret" |
| 52 | +# 11. Name: PAT_TOKEN, Value: paste your token |
| 53 | +# ============================================================= |
| 54 | +name: Check for New MegaLinter Version |
| 55 | + |
| 56 | +on: |
| 57 | + schedule: |
| 58 | + # Run daily at 00:00 UTC |
| 59 | + - cron: "0 0 * * *" |
| 60 | + workflow_dispatch: |
| 61 | + |
| 62 | +permissions: |
| 63 | + contents: write |
| 64 | + |
| 65 | +jobs: |
| 66 | + check-new-version: |
| 67 | + name: Check for New MegaLinter Version |
| 68 | + runs-on: ubuntu-latest |
| 69 | + |
| 70 | + steps: |
| 71 | + - name: Checkout Code |
| 72 | + uses: actions/checkout@v6 |
| 73 | + with: |
| 74 | + fetch-depth: 0 |
| 75 | + persist-credentials: false |
| 76 | + |
| 77 | + - name: Fetch MegaLinter Repository Tags |
| 78 | + id: fetch-megalinter-tags |
| 79 | + run: | |
| 80 | + echo "Fetching tags from oxsecurity/megalinter..." |
| 81 | +
|
| 82 | + # Fetch all tags from MegaLinter repository (filtering for version tags only) |
| 83 | + MEGALINTER_TAGS=$(git ls-remote --tags --refs https://github.com/oxsecurity/megalinter.git | \ |
| 84 | + grep -E 'refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$' | \ |
| 85 | + sed 's/.*refs\/tags\///' | \ |
| 86 | + sort -V | \ |
| 87 | + tail -n 20) |
| 88 | +
|
| 89 | + echo "Latest MegaLinter tags:" |
| 90 | + echo "$MEGALINTER_TAGS" |
| 91 | +
|
| 92 | + # Get the latest tag |
| 93 | + LATEST_MEGALINTER_TAG=$(echo "$MEGALINTER_TAGS" | tail -n 1) |
| 94 | + echo "latest_tag=$LATEST_MEGALINTER_TAG" >> $GITHUB_OUTPUT |
| 95 | +
|
| 96 | + # Save all tags to a file |
| 97 | + echo "$MEGALINTER_TAGS" > megalinter_tags.txt |
| 98 | +
|
| 99 | + - name: Fetch Current Repository Tags |
| 100 | + id: fetch-repo-tags |
| 101 | + run: | |
| 102 | + echo "Fetching tags from current repository..." |
| 103 | +
|
| 104 | + # Fetch all version tags from current repository |
| 105 | + REPO_TAGS=$(git tag -l 'v*' | sort -V) |
| 106 | +
|
| 107 | + echo "Current repository tags:" |
| 108 | + echo "$REPO_TAGS" |
| 109 | +
|
| 110 | + # Get the latest tag from current repository |
| 111 | + if [ -z "$REPO_TAGS" ]; then |
| 112 | + LATEST_REPO_TAG="" |
| 113 | + echo "No existing tags in repository" |
| 114 | + else |
| 115 | + LATEST_REPO_TAG=$(echo "$REPO_TAGS" | tail -n 1) |
| 116 | + echo "Latest repository tag: $LATEST_REPO_TAG" |
| 117 | + fi |
| 118 | +
|
| 119 | + echo "latest_repo_tag=$LATEST_REPO_TAG" >> $GITHUB_OUTPUT |
| 120 | +
|
| 121 | + - name: Find New Version |
| 122 | + id: find-new-version |
| 123 | + env: |
| 124 | + LATEST_MEGALINTER_TAG: ${{ steps.fetch-megalinter-tags.outputs.latest_tag }} |
| 125 | + LATEST_REPO_TAG: ${{ steps.fetch-repo-tags.outputs.latest_repo_tag }} |
| 126 | + run: | |
| 127 | + echo "Comparing versions..." |
| 128 | +
|
| 129 | + echo "Latest MegaLinter tag: $LATEST_MEGALINTER_TAG" |
| 130 | + echo "Latest repository tag: $LATEST_REPO_TAG" |
| 131 | +
|
| 132 | + # Function to compare semantic versions |
| 133 | + version_greater_than() { |
| 134 | + # Remove 'v' prefix for comparison |
| 135 | + ver1="${1#v}" |
| 136 | + ver2="${2#v}" |
| 137 | + |
| 138 | + # Use sort -V to compare versions |
| 139 | + if [ "$(printf '%s\n' "$ver1" "$ver2" | sort -V | tail -n1)" = "$ver1" ] && [ "$ver1" != "$ver2" ]; then |
| 140 | + return 0 # ver1 > ver2 |
| 141 | + else |
| 142 | + return 1 # ver1 <= ver2 |
| 143 | + fi |
| 144 | + } |
| 145 | +
|
| 146 | + # Check if we should create a new release |
| 147 | + if [ -z "$LATEST_REPO_TAG" ]; then |
| 148 | + echo "No existing tags in repository. Will create release for $LATEST_MEGALINTER_TAG" |
| 149 | + echo "new_version_found=true" >> $GITHUB_OUTPUT |
| 150 | + echo "new_version=$LATEST_MEGALINTER_TAG" >> $GITHUB_OUTPUT |
| 151 | + elif version_greater_than "$LATEST_MEGALINTER_TAG" "$LATEST_REPO_TAG"; then |
| 152 | + echo "✅ New version found! $LATEST_MEGALINTER_TAG > $LATEST_REPO_TAG" |
| 153 | + echo "new_version_found=true" >> $GITHUB_OUTPUT |
| 154 | + echo "new_version=$LATEST_MEGALINTER_TAG" >> $GITHUB_OUTPUT |
| 155 | + else |
| 156 | + echo "ℹ️ No new version. Repository is up to date." |
| 157 | + echo "new_version_found=false" >> $GITHUB_OUTPUT |
| 158 | + fi |
| 159 | +
|
| 160 | + - name: Create Release for New Version |
| 161 | + if: steps.find-new-version.outputs.new_version_found == 'true' |
| 162 | + env: |
| 163 | + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 164 | + NEW_VERSION: ${{ steps.find-new-version.outputs.new_version }} |
| 165 | + run: | |
| 166 | + echo "Creating release for version $NEW_VERSION..." |
| 167 | +
|
| 168 | + # Create a release using GitHub CLI (will also create the tag) |
| 169 | + gh release create "$NEW_VERSION" \ |
| 170 | + --title "MegaLinter Custom Flavor $NEW_VERSION" \ |
| 171 | + --notes "Automated release to sync with MegaLinter version $NEW_VERSION. |
| 172 | +
|
| 173 | + This release was automatically created to build a custom MegaLinter flavor based on the upstream MegaLinter release $NEW_VERSION. |
| 174 | +
|
| 175 | + For more information about changes in this version, see the [MegaLinter changelog](https://github.com/oxsecurity/megalinter/releases/tag/$NEW_VERSION)." \ |
| 176 | + --latest |
| 177 | +
|
| 178 | + - name: Trigger Custom Flavor Builder Workflow |
| 179 | + if: steps.find-new-version.outputs.new_version_found == 'true' |
| 180 | + env: |
| 181 | + # Use PAT_TOKEN if available, otherwise fall back to GITHUB_TOKEN |
| 182 | + # Note: GITHUB_TOKEN doesn't have permission to trigger workflows |
| 183 | + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} |
| 184 | + NEW_VERSION: ${{ steps.find-new-version.outputs.new_version }} |
| 185 | + run: | |
| 186 | + echo "Triggering megalinter-custom-flavor-builder workflow..." |
| 187 | +
|
| 188 | + # Trigger the workflow using GitHub CLI and capture output |
| 189 | + set +e # Don't exit immediately on error |
| 190 | + gh workflow run megalinter-custom-flavor-builder.yml \ |
| 191 | + --ref main \ |
| 192 | + --field megalinter-version="$NEW_VERSION" \ |
| 193 | + --field is-latest="true" \ |
| 194 | + 2>&1 | tee workflow_trigger.log |
| 195 | + EXIT_CODE=$? |
| 196 | + set -e # Re-enable exit on error |
| 197 | +
|
| 198 | + # Check for errors in the output |
| 199 | + if grep -q "could not create workflow dispatch event" workflow_trigger.log || \ |
| 200 | + grep -q "Resource not accessible by integration" workflow_trigger.log || \ |
| 201 | + grep -q "HTTP 403" workflow_trigger.log || \ |
| 202 | + [ $EXIT_CODE -ne 0 ]; then |
| 203 | + |
| 204 | + echo "::error::❌ Failed to trigger workflow!" |
| 205 | + echo "" |
| 206 | + cat workflow_trigger.log |
| 207 | + echo "" |
| 208 | + |
| 209 | + if grep -q "Resource not accessible by integration" workflow_trigger.log || grep -q "HTTP 403" workflow_trigger.log; then |
| 210 | + echo "::error::The workflow could not be triggered due to insufficient token permissions." |
| 211 | + echo "::error::" |
| 212 | + echo "::error::Deleting the release so it can be recreated once PAT_TOKEN is configured..." |
| 213 | + |
| 214 | + # Delete the release and tag that was just created |
| 215 | + gh release delete "$NEW_VERSION" --yes --cleanup-tag 2>&1 || echo "::warning::Could not delete release (it may not exist or already be deleted)" |
| 216 | + |
| 217 | + echo "::error::" |
| 218 | + echo "::error::⚠️ PAT_TOKEN is REQUIRED to create releases and trigger the builder workflow." |
| 219 | + echo "::error::" |
| 220 | + echo "::error::To fix this, create a Fine-grained Personal Access Token (recommended - more secure):" |
| 221 | + echo "::error::1. Go to: https://github.com/settings/personal-access-tokens/new" |
| 222 | + echo "::error::2. Token name: 'MegaLinter Auto-Release'" |
| 223 | + echo "::error::3. Expiration: Choose 90 days or 1 year" |
| 224 | + echo "::error::4. Repository access: Select 'Only select repositories'" |
| 225 | + echo "::error::5. Choose repository: ${GITHUB_REPOSITORY}" |
| 226 | + echo "::error::6. Repository permissions:" |
| 227 | + echo "::error:: - Contents: Read and write" |
| 228 | + echo "::error:: - Actions: Read and write" |
| 229 | + echo "::error::7. Click 'Generate token' and copy it" |
| 230 | + echo "::error::8. Go to: https://github.com/${GITHUB_REPOSITORY}/settings/secrets/actions" |
| 231 | + echo "::error::9. Click 'New repository secret'" |
| 232 | + echo "::error::10. Name: 'PAT_TOKEN', Value: paste your token" |
| 233 | + echo "::error::" |
| 234 | + echo "::error::Once configured, run this workflow again (manually or wait for the next scheduled run)." |
| 235 | + fi |
| 236 | + |
| 237 | + exit 1 |
| 238 | + fi |
| 239 | +
|
| 240 | + echo "✅ Builder workflow triggered successfully with version: $NEW_VERSION" |
| 241 | +
|
| 242 | + - name: Summary |
| 243 | + if: always() |
| 244 | + env: |
| 245 | + LATEST_MEGALINTER_TAG: ${{ steps.fetch-megalinter-tags.outputs.latest_tag }} |
| 246 | + LATEST_REPO_TAG: ${{ steps.fetch-repo-tags.outputs.latest_repo_tag }} |
| 247 | + NEW_VERSION_FOUND: ${{ steps.find-new-version.outputs.new_version_found }} |
| 248 | + NEW_VERSION: ${{ steps.find-new-version.outputs.new_version }} |
| 249 | + GH_REPO: ${GITHUB_REPOSITORY} |
| 250 | + run: | |
| 251 | + echo "## Check for New MegaLinter Version Summary" >> $GITHUB_STEP_SUMMARY |
| 252 | + echo "" >> $GITHUB_STEP_SUMMARY |
| 253 | +
|
| 254 | + if [ "${NEW_VERSION_FOUND}" == "true" ]; then |
| 255 | + echo "New version found: **${NEW_VERSION}**" >> $GITHUB_STEP_SUMMARY |
| 256 | + echo "" >> $GITHUB_STEP_SUMMARY |
| 257 | + echo "A new release has been created, which will trigger the custom flavor builder workflow." >> $GITHUB_STEP_SUMMARY |
| 258 | + else |
| 259 | + echo "No new versions found. Repository is up to date with MegaLinter." >> $GITHUB_STEP_SUMMARY |
| 260 | + fi |
| 261 | +
|
| 262 | + echo "" >> $GITHUB_STEP_SUMMARY |
| 263 | + echo "**Version Comparison:**" >> $GITHUB_STEP_SUMMARY |
| 264 | + echo "- Latest MegaLinter version: **$LATEST_MEGALINTER_TAG**" >> $GITHUB_STEP_SUMMARY |
| 265 | +
|
| 266 | + if [ -n "$LATEST_REPO_TAG" ]; then |
| 267 | + echo "- Latest repository version: **$LATEST_REPO_TAG**" >> $GITHUB_STEP_SUMMARY |
| 268 | + else |
| 269 | + echo "- Latest repository version: **No tags found**" >> $GITHUB_STEP_SUMMARY |
| 270 | + fi |
0 commit comments