fix(settings): add debug logging functions for settings changes and s… #111
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # .github/workflows/tag-release.yml | |
| # Automatic release tagging on merge to main or dev | |
| # | |
| # When code is pushed to main or dev (typically via a merged PR), this workflow: | |
| # 1. Generates a date-based version tag (vYYYY.MM.DD for stable, vYYYY.MM.DD.HHMM for beta) | |
| # 2. Updates the PLG changelog with categorized commit notes | |
| # 3. Pushes the tag, then calls build.yml via workflow_call to build and release | |
| # | |
| # Guards: | |
| # - Skips bot commits ([skip ci], Release v*) to prevent infinite loops | |
| # - Handles same-day tag collisions with a/b/c suffixes (stable only) | |
| # - Beta releases require an open PR from dev → main | |
| name: Tag Release | |
| on: | |
| push: | |
| branches: [main, dev] | |
| paths: | |
| - 'source/compose.manager/**' | |
| - 'source/pkg_build.sh' | |
| - 'versions.env' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' | |
| PLUGIN_NAME: compose.manager | |
| jobs: | |
| gate: | |
| runs-on: ubuntu-latest | |
| if: >- | |
| !contains(github.event.head_commit.message, '[skip ci]') | |
| && !startsWith(github.event.head_commit.message, 'Release v') | |
| && github.event.head_commit.author.name != 'github-actions[bot]' | |
| outputs: | |
| should_release: ${{ steps.check.outputs.should_release }} | |
| pr_number: ${{ steps.check.outputs.pr_number }} | |
| steps: | |
| - name: Check for qualifying open PR (dev → main) | |
| id: check | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if [[ "$GITHUB_REF_NAME" == "dev" ]]; then | |
| PR_NUMBER=$(gh pr list --repo "${{ github.repository }}" \ | |
| --head dev --base main --state open \ | |
| --author mstrhakr --label release \ | |
| --json number --jq '.[0].number // empty') | |
| if [[ -z "$PR_NUMBER" ]]; then | |
| echo "::notice::No qualifying open PR from dev → main (must be by mstrhakr with 'release' label). Skipping beta release." | |
| echo "should_release=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "Open PR #${PR_NUMBER} found — proceeding with beta release." | |
| echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT | |
| fi | |
| echo "should_release=true" >> $GITHUB_OUTPUT | |
| tag: | |
| needs: gate | |
| runs-on: ubuntu-latest | |
| if: needs.gate.outputs.should_release == 'true' | |
| outputs: | |
| tag: ${{ steps.tag.outputs.tag }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Determine tag | |
| id: tag | |
| run: | | |
| BRANCH="${GITHUB_REF_NAME}" | |
| export TZ="America/Detroit" | |
| DATE_VERSION=$(date +%Y.%m.%d) | |
| if [[ "$BRANCH" == "dev" ]]; then | |
| # Beta: v2026.03.15.HHMM (America/Detroit) — always unique | |
| TIME_STAMP=$(date +%H%M) | |
| NEW_TAG="v${DATE_VERSION}.${TIME_STAMP}" | |
| echo "is_beta=true" >> $GITHUB_OUTPUT | |
| else | |
| # Stable: v2026.03.15, then v2026.03.15a, v2026.03.15b, ... | |
| BASE_TAG="v${DATE_VERSION}" | |
| # Find existing stable tags for today (keep only vYYYY.MM.DD[a-z] format) | |
| EXISTING=$(git tag -l "${BASE_TAG}*" | grep -P '^v\d{4}\.\d{2}\.\d{2}[a-z]?$' | sort || true) | |
| if [[ -z "$EXISTING" ]]; then | |
| NEW_TAG="$BASE_TAG" | |
| else | |
| LAST_TAG=$(echo "$EXISTING" | tail -1) | |
| if [[ "$LAST_TAG" == "$BASE_TAG" ]]; then | |
| NEW_TAG="${BASE_TAG}a" | |
| elif [[ "$LAST_TAG" =~ ^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([a-z])$ ]]; then | |
| LAST_SUFFIX="${BASH_REMATCH[1]}" | |
| # Increment suffix letter | |
| NEXT_SUFFIX=$(echo "$LAST_SUFFIX" | tr 'a-y' 'b-z') | |
| if [[ "$NEXT_SUFFIX" == "$LAST_SUFFIX" ]]; then | |
| echo "::error::Too many releases today (exceeded 'z' suffix)" | |
| exit 1 | |
| fi | |
| NEW_TAG="${BASE_TAG}${NEXT_SUFFIX}" | |
| else | |
| echo "::error::Unexpected tag format: $LAST_TAG" | |
| exit 1 | |
| fi | |
| fi | |
| echo "is_beta=false" >> $GITHUB_OUTPUT | |
| fi | |
| echo "tag=${NEW_TAG}" >> $GITHUB_OUTPUT | |
| echo "version=${NEW_TAG#v}" >> $GITHUB_OUTPUT | |
| echo "Generated tag: ${NEW_TAG}" | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| NEW_TAG="${{ steps.tag.outputs.tag }}" | |
| # Find the previous tag for this channel | |
| IS_BETA="${{ steps.tag.outputs.is_beta }}" | |
| if [[ "$IS_BETA" == "true" ]]; then | |
| LAST_TAG=$(git tag -l 'v*' | sort -V | tail -1) | |
| else | |
| LAST_TAG=$(git tag -l 'v*' | grep -P '^v\d{4}\.\d{2}\.\d{2}[a-z]?$' | sort -V | tail -1) | |
| fi | |
| if [[ -n "$LAST_TAG" ]]; then | |
| echo "Changes since $LAST_TAG" | |
| RANGE="${LAST_TAG}..HEAD" | |
| else | |
| echo "No previous tag found — using all commits" | |
| RANGE="HEAD" | |
| fi | |
| # Category mapping for conventional commits | |
| declare -A CATEGORIES=( | |
| [feat]="Features" | |
| [fix]="Bug Fixes" | |
| [perf]="Performance" | |
| [refactor]="Refactoring" | |
| [docs]="Documentation" | |
| [test]="Tests" | |
| [build]="Build" | |
| [ci]="CI/CD" | |
| [chore]="Chores" | |
| ) | |
| # Ordered category keys | |
| ORDER=(feat fix perf refactor docs test build ci chore) | |
| # Collect commits by category | |
| declare -A BUCKET | |
| UNCATEGORIZED="" | |
| while IFS= read -r subject; do | |
| # Skip noise | |
| [[ -z "$subject" ]] && continue | |
| [[ "$subject" =~ ^Merge\ ]] && continue | |
| [[ "$subject" =~ ^Release\ v ]] && continue | |
| [[ "$subject" =~ ^\[skip\ ci\] ]] && continue | |
| [[ "$subject" =~ ^v[0-9]{4}\. ]] && continue | |
| # Parse conventional commit: type(scope): message | |
| # Regex must be in a variable — bash [[ =~ ]] chokes on inline ) | |
| CONV_RE='^([a-z]+)(\([^)]+\))?:[[:space:]](.+)$' | |
| if [[ "$subject" =~ $CONV_RE ]]; then | |
| TYPE="${BASH_REMATCH[1]}" | |
| SCOPE="${BASH_REMATCH[2]}" | |
| MSG="${BASH_REMATCH[3]}" | |
| LABEL="${CATEGORIES[$TYPE]:-$TYPE}" | |
| if [[ -n "$SCOPE" ]]; then | |
| LINE="- ${LABEL} ${SCOPE}: ${MSG}" | |
| else | |
| LINE="- ${LABEL}: ${MSG}" | |
| fi | |
| BUCKET[$TYPE]+="${LINE}"$'\n' | |
| else | |
| UNCATEGORIZED+="- ${subject}"$'\n' | |
| fi | |
| done < <(git log "$RANGE" --pretty=format:"%s" --no-merges) | |
| # Build changelog text | |
| NOTES="###${VERSION}"$'\n' | |
| HAS_CONTENT=false | |
| for TYPE in "${ORDER[@]}"; do | |
| if [[ -n "${BUCKET[$TYPE]}" ]]; then | |
| NOTES+="${BUCKET[$TYPE]}" | |
| HAS_CONTENT=true | |
| fi | |
| done | |
| if [[ -n "$UNCATEGORIZED" ]]; then | |
| NOTES+="${UNCATEGORIZED}" | |
| HAS_CONTENT=true | |
| fi | |
| if [[ "$HAS_CONTENT" == false ]]; then | |
| NOTES+="- Minor updates and improvements"$'\n' | |
| fi | |
| # Add comparison and PR links | |
| if [[ "$IS_BETA" == "true" ]]; then | |
| PR_NUMBER="${{ needs.gate.outputs.pr_number }}" | |
| if [[ -n "$PR_NUMBER" ]]; then | |
| NOTES+="- [PR #${PR_NUMBER}](https://github.com/${{ github.repository }}/pull/${PR_NUMBER})"$'\n' | |
| fi | |
| LATEST_STABLE=$(git tag -l 'v*' | grep -P '^v\d{4}\.\d{2}\.\d{2}[a-z]?$' | sort -V | tail -1) | |
| if [[ -n "$LATEST_STABLE" ]]; then | |
| NOTES+="- [beta release diff](https://github.com/${{ github.repository }}/compare/${LATEST_STABLE}...${NEW_TAG})"$'\n' | |
| fi | |
| elif [[ -n "$LAST_TAG" ]]; then | |
| NOTES+="- [View all changes](https://github.com/${{ github.repository }}/compare/${LAST_TAG}...${NEW_TAG})"$'\n' | |
| fi | |
| # Save for next step | |
| echo "$NOTES" > /tmp/changelog.txt | |
| echo "Changelog:" | |
| cat /tmp/changelog.txt | |
| - name: Update PLG changelog | |
| run: | | |
| PLG="${{ env.PLUGIN_NAME }}.plg" | |
| CHANGELOG=$(cat /tmp/changelog.txt) | |
| # XML-escape the changelog | |
| ESCAPED=$(echo "$CHANGELOG" | sed 's/&/\&/g; s/</\</g; s/>/\>/g') | |
| # Replace everything between <CHANGES> and </CHANGES> | |
| python3 - "$PLG" "$ESCAPED" << 'PYEOF' | |
| import sys, re | |
| plg_path, new_notes = sys.argv[1], sys.argv[2] | |
| with open(plg_path, 'r') as f: | |
| content = f.read() | |
| pattern = r'(<CHANGES>\n).*?(\n</CHANGES>)' | |
| replacement = r'\g<1>' + new_notes.rstrip('\n') + r'\g<2>' | |
| content = re.sub(pattern, replacement, content, flags=re.DOTALL) | |
| with open(plg_path, 'w') as f: | |
| f.write(content) | |
| PYEOF | |
| echo "Updated PLG changelog" | |
| - name: Sync pluginURL and README for branch | |
| run: | | |
| BRANCH="${GITHUB_REF_NAME}" | |
| PLG_FILE="${{ env.PLUGIN_NAME }}.plg" | |
| README_FILE="source/compose.manager/README.md" | |
| # Ensure pluginURL entity references the current branch | |
| # Scope sed to the pluginURL line only — avoids corrupting packageURL | |
| CURRENT_URL_BRANCH=$(grep -oP '<!ENTITY pluginURL.*github;/\K[^/]+' "$PLG_FILE" || echo "unknown") | |
| if [[ "$CURRENT_URL_BRANCH" != "$BRANCH" ]]; then | |
| echo "Fixing pluginURL branch: $CURRENT_URL_BRANCH → $BRANCH" | |
| sed -i "/pluginURL/s|/&github;/[^/]*/|/\&github;/${BRANCH}/|" "$PLG_FILE" | |
| fi | |
| # Ensure README title matches branch disposition | |
| if [[ "$BRANCH" == "dev" ]]; then | |
| if ! grep -q '^\*\*Compose Manager Plus (Beta)\*\*$' "$README_FILE"; then | |
| sed -i '1s|^\*\*Compose Manager Plus.*\*\*$|**Compose Manager Plus (Beta)**|' "$README_FILE" | |
| fi | |
| else | |
| if grep -q '^\*\*Compose Manager Plus (Beta)\*\*$' "$README_FILE"; then | |
| sed -i '1s|^\*\*Compose Manager Plus (Beta)\*\*$|**Compose Manager Plus**|' "$README_FILE" | |
| fi | |
| fi | |
| - name: Commit changelog and create tag | |
| run: | | |
| NEW_TAG="${{ steps.tag.outputs.tag }}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add "${{ env.PLUGIN_NAME }}.plg" "source/compose.manager/README.md" | |
| git diff --cached --quiet && echo "No changelog changes" || \ | |
| git commit -m "chore: update changelog for ${NEW_TAG} [skip ci]" | |
| # Rebase on latest remote to avoid non-fast-forward rejection | |
| git pull --rebase origin "${GITHUB_REF_NAME}" | |
| git push origin "${GITHUB_REF_NAME}" | |
| # Create and push the tag | |
| git tag "$NEW_TAG" | |
| git push origin "$NEW_TAG" | |
| echo "::notice::Tagged ${NEW_TAG} — calling build.yml via workflow_call" | |
| build: | |
| needs: tag | |
| uses: ./.github/workflows/build.yml | |
| with: | |
| tag: ${{ needs.tag.outputs.tag }} | |
| permissions: | |
| contents: write |