diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..2f93975 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,75 @@ +name: release + +on: + push: + tags: + - '*-coa.*' # Asc-1.1.6-coa.2, 9.1.40-coa.3, etc. + - 'v*' # v0.3.0 for repos without an upstream version + +jobs: + release: + runs-on: linux-amd64 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # build_zip uses git archive HEAD; full history is fine + + - name: Build per-addon zip(s) + run: bash tools/build_zip.sh + + - name: Publish release (Gitea API direct; no action dependency) + env: + GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + API: ${{ github.server_url }}/api/v1 + # Gitea attachment ceiling is 200 MiB (see roles/gitea config). + # Skip anything larger so one oversized asset doesn't fail the job. + MAX_BYTES: 209715200 + run: | + set -euo pipefail + # Create the release (or reuse if it already exists for this tag). + RID=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$API/repos/$REPO/releases/tags/$TAG" 2>/dev/null \ + | jq -r '.id // empty') + if [ -z "$RID" ]; then + RID=$(curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + "$API/repos/$REPO/releases" \ + -d "$(jq -nc --arg t "$TAG" '{tag_name:$t,name:$t,draft:false,prerelease:false,hide_archive_links:true}')" \ + | jq -r '.id') + fi + echo "release id: $RID" + # Gitea honors hide_archive_links only on edit, not create — PATCH it + # so the auto-generated Source Code (zip/tar.gz) links stay hidden. + curl -sf -X PATCH -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" \ + "$API/repos/$REPO/releases/$RID" -d '{"hide_archive_links":true}' >/dev/null || true + # Upload every dist/*.zip. Per-asset failures don't fail the job — + # we want partial releases to still publish rather than block the + # whole pipeline on one big file. + failed=0 + uploaded=0 + for f in dist/*.zip; do + name=$(basename "$f") + size=$(stat -c '%s' "$f") + if [ "$size" -gt "$MAX_BYTES" ]; then + echo "::warning::skip $name (${size} B > ${MAX_BYTES} B Gitea limit; host on CDN instead)" + failed=$((failed+1)) + continue + fi + echo "uploading $name ($(numfmt --to=iec "$size"))" + if curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \ + -F "attachment=@$f" \ + "$API/repos/$REPO/releases/$RID/assets?name=$name" \ + | jq -r '" -> " + .browser_download_url'; then + uploaded=$((uploaded+1)) + else + echo "::warning::upload failed for $name" + failed=$((failed+1)) + fi + done + echo "release published: $uploaded uploaded, $failed skipped/failed" + # Only fail the job if NO assets uploaded — a release with zero + # attachments isn't useful to anyone. + [ "$uploaded" -gt 0 ] diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 2ac83fa..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,14 +0,0 @@ -# These are supported funding model platforms - -github: [NoM0Re] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -custom: https://streamelements.com/nom0ree/tip - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 0665220..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: "Bug Report" -description: Create a report to help us improve -labels: ['🐛 Bug'] -body: -- type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please [search for existing issues](https://github.com/Ascension-Addons/WeakAuras-Ascension/issues) to see if an open or closed one already exists for the bug you encountered. If a bug exists and it is closed as complete it may not yet be in a stable release. - options: - - label: I have searched the existing open and closed issues. - required: true - -- type: textarea - attributes: - label: Description - description: What did you expect to happen and what happened instead? - validations: - required: true - -- type: input - attributes: - label: WeakAuras Version - description: | - You can see the current version in the title bar of the options window, if the options do not open, check the `## Version:` field in the WeakAuras.toc file. - placeholder: "WeakAuras 5.0.0" - validations: - required: true - -- type: dropdown - id: flavor - attributes: - label: World of Warcraft Flavor - description: What version of World of Warcraft are are you running? - options: - - WotLK 3.3.5a - validations: - required: true - -- type: dropdown - id: language - attributes: - label: World of Warcraft Language - description: In which language do you play World of Warcraft? - options: - - enGB/enUS - - deDE - - frFR - - itIT - - esES - - esMX - - koKR - - ptBR - - ruRU - - zhCN - - zhTW - validations: - required: true - -- type: input - id: server - attributes: - label: Server - description: On which server/realm are you playing? - placeholder: Warmane-Icecrown - validations: - required: true - -- type: checkboxes - id: testing - attributes: - label: Tested with only WeakAuras - description: Sometimes, other addons can interfere with WeakAuras. We recommend testing with only WeakAuras enabled to see if the issue persists. - options: - - label: I got this issue with only WeakAuras enabled - required: true - -- type: textarea - attributes: - label: Lua Error - description: | - Do you have an error log of what happened? If you don't see any errors, make sure that error reporting is enabled (`/console scriptErrors 1`) or install [BugSack](https://www.curseforge.com/wow/addons/bugsack) & [BugGrabber](https://www.curseforge.com/wow/addons/bug-grabber), yes both are needed. - Note that if the error looks like `[string "--[[ Error in ' my awesome aura' ]` then the bug is in the aura that got mentioned, not in WeakAuras itself. - render: Text - validations: - required: false - -- type: textarea - attributes: - label: Reproduction Steps - description: Please list out the steps to reproduce your bug. Please verify that your reproduction steps are enough to reproduce the problem. - placeholder: | - 1. Go to '...' - 2. Click on '....' - 3. Scroll down to '....' - 4. See error - validations: - required: true - -- type: input - attributes: - label: Last Good Version - description: | - Was it working in a previous version? If yes, which was the last good one? - placeholder: "WeakAuras 5.0.0" - validations: - required: false - -- type: textarea - attributes: - label: Screenshots - description: If applicable, add screenshots to help explain your problem. - placeholder: Click here to attach your screenshots via the editor button in the top right. - validations: - required: false - -- type: textarea - attributes: - label: Export String - description: If you do not know which aura is causing issues for you, please attach a ZIP archive of your WeakAuras SavedVariables file, it's the `WeakAuras.lua` file in `World of Warcraft\_retail_\WTF\Account\YOUR_ACCOUNT\SavedVariables\`. In case you do, please export the string and paste it below. - placeholder: Paste your exported WeakAuras string here. - render: Text - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 6498af8..0000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Question - url: https://discord.gg/classlesswow - about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 5e0fb3a..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: "\U0001F3A8 Feature Request" -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. diff --git a/.github/workflows/autoclose.yaml b/.github/workflows/autoclose.yaml deleted file mode 100644 index 5dc7272..0000000 --- a/.github/workflows/autoclose.yaml +++ /dev/null @@ -1,115 +0,0 @@ -name: Autoclose after 2 days - -on: - schedule: - - cron: '0 0 * * *' - issues: - types: [labeled] - pull_request: - types: [labeled] - -permissions: - issues: write - pull-requests: write - -jobs: - autoclose: - runs-on: ubuntu-latest - steps: - - name: Close Issues/PRs labeled 'Auto Close' after 2 days, if label set by collaborator and no recent comments - uses: actions/github-script@v7 - with: - script: | - const { owner, repo } = context.repo; - const labelName = '⏳Auto Close'; - const maxAgeDays = 2; - const now = new Date(); - - const issues = await github.paginate(github.rest.issues.listForRepo, { - owner, - repo, - labels: labelName, - state: 'open', - per_page: 100, - }); - - for (const issue of issues) { - const issue_number = issue.number; - - const events = await github.paginate(github.rest.issues.listEventsForTimeline, { - owner, - repo, - issue_number, - per_page: 100, - }); - - const labelEvent = events.find(event => - event.event === 'labeled' && - event.label?.name === labelName - ); - - if (!labelEvent) continue; - - let labelDate = new Date(labelEvent.created_at); - - const comments = await github.paginate( - issue.pull_request - ? github.rest.pulls.listReviewComments - : github.rest.issues.listComments, - { - owner, - repo, - issue_number, - per_page: 100, - } - ); - - const recentCommentsAfterLabel = comments - .filter(c => new Date(c.created_at) > labelDate); - - let latestDate = labelDate; - if (recentCommentsAfterLabel.length > 0) { - latestDate = new Date(Math.max(...recentCommentsAfterLabel.map(c => new Date(c.created_at).getTime()))); - } - - const diffDays = (now - latestDate) / (1000 * 60 * 60 * 24); - - if (diffDays < maxAgeDays) continue; - - const actor = labelEvent.actor?.login; - if (!actor) continue; - - try { - await github.rest.repos.checkCollaborator({ - owner, - repo, - username: actor, - }); - } catch (error) { - if (error.status === 404) { - await github.rest.issues.removeLabel({ - owner, - repo, - issue_number, - name: labelName, - }); - continue; - } else { - throw error; - } - } - - await github.rest.issues.update({ - owner, - repo, - issue_number, - state: 'closed', - }); - - await github.rest.issues.removeLabel({ - owner, - repo, - issue_number, - name: labelName, - }); - } diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml deleted file mode 100644 index af43ec7..0000000 --- a/.github/workflows/lint-pr.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: lint - -on: - pull_request: - paths: - - '**.lua' - -jobs: - lint: - runs-on: ubuntu-latest - - env: - LUA_VERSION: 5.1.5 - LUAROCKS_VERSION: 3.11.1 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Cache Lua - uses: actions/cache@v3 - id: luacache - with: - path: .lua - key: ${{ runner.os }}-lua-${{ env.LUA_VERSION }} - restore-keys: | - ${{ runner.os }}-lua-${{ env.LUA_VERSION }} - - - name: Cache LuaRocks - uses: actions/cache@v3 - id: luarockscache - with: - path: .luarocks - key: ${{ runner.os }}-luarocks-${{ env.LUAROCKS_VERSION }} - restore-keys: | - ${{ runner.os }}-luarocks-${{ env.LUAROCKS_VERSION }} - - - name: Install Lua - if: steps.luacache.outputs.cache-hit != 'true' - run: | - sudo apt-get install libreadline-dev libncurses-dev - wget https://www.lua.org/ftp/lua-${{ env.LUA_VERSION }}.tar.gz -O - | tar -xzf - - cd lua-${{ env.LUA_VERSION }} - make linux - make -j INSTALL_TOP=$GITHUB_WORKSPACE/.lua install - rm -rf $GITHUB_WORKSPACE/lua-${{ env.LUA_VERSION }} - - - name: Install LuaRocks and Luacheck - if: steps.luarockscache.outputs.cache-hit != 'true' - run: | - wget https://luarocks.org/releases/luarocks-${{ env.LUAROCKS_VERSION }}.tar.gz -O - | tar -xzf - - cd luarocks-${{ env.LUAROCKS_VERSION }} - ./configure --with-lua-bin=$GITHUB_WORKSPACE/.lua/bin --prefix=$GITHUB_WORKSPACE/.luarocks - make build - make install - PATH=$PATH:$GITHUB_WORKSPACE/.luarocks/bin - luarocks install luacheck - luarocks install lanes - rm -rf $GITHUB_WORKSPACE/luarocks-${{ env.LUAROCKS_VERSION }} - - - name: Luacheck - run: .luarocks/bin/luacheck . -q diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index cb8c310..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: lint - -on: - push: - paths: - - '.github/workflows/**.yml' - - '**.lua' - pull_request: - paths: - - '**.lua' - -jobs: - lint: - runs-on: ubuntu-latest - - env: - LUA_VERSION: 5.1.5 - LUAROCKS_VERSION: 3.11.1 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Cache Lua - uses: actions/cache@v3 - id: luacache - with: - path: .lua - key: ${{ runner.os }}-lua-${{ env.LUA_VERSION }} - restore-keys: | - ${{ runner.os }}-lua-${{ env.LUA_VERSION }} - - - name: Cache LuaRocks - uses: actions/cache@v3 - id: luarockscache - with: - path: .luarocks - key: ${{ runner.os }}-luarocks-${{ env.LUAROCKS_VERSION }} - restore-keys: | - ${{ runner.os }}-luarocks-${{ env.LUAROCKS_VERSION }} - - - name: Install Lua - if: steps.luacache.outputs.cache-hit != 'true' - run: | - sudo apt-get install libreadline-dev libncurses-dev - wget https://www.lua.org/ftp/lua-${{ env.LUA_VERSION }}.tar.gz -O - | tar -xzf - - cd lua-${{ env.LUA_VERSION }} - make linux - make -j INSTALL_TOP=$GITHUB_WORKSPACE/.lua install - rm -rf $GITHUB_WORKSPACE/lua-${{ env.LUA_VERSION }} - - - name: Install LuaRocks and Luacheck - if: steps.luarockscache.outputs.cache-hit != 'true' - run: | - wget https://luarocks.org/releases/luarocks-${{ env.LUAROCKS_VERSION }}.tar.gz -O - | tar -xzf - - cd luarocks-${{ env.LUAROCKS_VERSION }} - ./configure --with-lua-bin=$GITHUB_WORKSPACE/.lua/bin --prefix=$GITHUB_WORKSPACE/.luarocks - make build - make install - PATH=$PATH:$GITHUB_WORKSPACE/.luarocks/bin - luarocks install luacheck - luarocks install lanes - rm -rf $GITHUB_WORKSPACE/luarocks-${{ env.LUAROCKS_VERSION }} - - - name: Luacheck - run: .luarocks/bin/luacheck . diff --git a/.gitignore b/.gitignore index 3552451..b11d5da 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .idea +.idea + +dist/ diff --git a/WeakAuras/Init.lua b/WeakAuras/Init.lua index e5948b5..791daaf 100644 --- a/WeakAuras/Init.lua +++ b/WeakAuras/Init.lua @@ -9,7 +9,7 @@ WeakAuras.halfWidth = WeakAuras.normalWidth / 2 WeakAuras.doubleWidth = WeakAuras.normalWidth * 2 local versionStringFromToc = GetAddOnMetadata("WeakAuras", "Version") -local versionString = "5.21.2 Beta" +local versionString = "5.22.0 Beta" -- Year, Month, Day, Hour, Minute, Seconds local buildTime = "2025".."11".."29".."04".."45".."00" local isTTSEnabled = C_VoiceChat and C_VoiceChat.SpeakText and true or false diff --git a/WeakAuras/Types.lua b/WeakAuras/Types.lua index 68573c7..db21db5 100644 --- a/WeakAuras/Types.lua +++ b/WeakAuras/Types.lua @@ -1187,6 +1187,16 @@ WeakAuras.class_types = {} for i, class in ipairs(CLASS_SORT_ORDER) do WeakAuras.class_types[class] = WrapTextInColorCode(LOCALIZED_CLASS_NAMES_MALE[class], WA_GetClassColor(class)) end +-- CoA: CLASS_SORT_ORDER only contains the 11 vanilla classes, missing the 21 +-- custom CoA classes. Fall back to LOCALIZED_CLASS_NAMES_MALE for anything not +-- yet registered. Same pattern used by the spec builder further down this file. +if LOCALIZED_CLASS_NAMES_MALE then + for class in pairs(LOCALIZED_CLASS_NAMES_MALE) do + if not WeakAuras.class_types[class] then + WeakAuras.class_types[class] = WrapTextInColorCode(LOCALIZED_CLASS_NAMES_MALE[class], WA_GetClassColor(class)) + end + end +end if WeakAuras.IsClassicPlus() then WeakAuras.class_types["DEATHKNIGHT"] = nil end @@ -3832,6 +3842,28 @@ do end table.sort(classOrder) end + else + -- CoA: CLASS_SORT_ORDER only contains the 11 vanilla classes. Append any + -- keys present in LOCALIZED_CLASS_NAMES_MALE that are not already in the + -- list (custom classes such as Witchdoctor, Templar, etc.). Build a fresh + -- local copy — do NOT mutate the global CLASS_SORT_ORDER. + if LOCALIZED_CLASS_NAMES_MALE then + local inOrder = {} + for _, class in ipairs(classOrder) do + inOrder[class] = true + end + classOrder = {unpack(classOrder)} + local extra = {} + for class in pairs(LOCALIZED_CLASS_NAMES_MALE) do + if not inOrder[class] then + extra[#extra + 1] = class + end + end + table.sort(extra) + for _, class in ipairs(extra) do + classOrder[#classOrder + 1] = class + end + end end if C_ClassInfo and C_ClassInfo.GetAllSpecs and C_ClassInfo.GetSpecInfo then @@ -3839,7 +3871,10 @@ do local specs = C_ClassInfo.GetAllSpecs(class) if specs then for _, spec in ipairs(specs) do - addSpec(class, C_ClassInfo.GetSpecInfo(class, spec)) + local ok, info = pcall(C_ClassInfo.GetSpecInfo, class, spec) + if ok and info then + addSpec(class, info) + end end end end diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index df00e53..0358bb2 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -1304,8 +1304,16 @@ loadedFrame:SetScript("OnEvent", function(self, event, ...) if dbIsValid then Private.Login(takeNewSnapshots) else - -- db isn't valid. Request permission to run repair tool before logging in - StaticPopup_Show("WEAKAURAS_CONFIRM_REPAIR", nil, nil, {reason = "downgrade"}) + -- db isn't valid. Request permission to run repair tool before logging in. + -- CoA: defer the StaticPopup_Show by one frame so it fires after the + -- PLAYER_LOGIN event-frame stack has unwound. On the CoA reworked + -- StaticPopup system, firing this synchronously during PLAYER_LOGIN + -- silently fails to show the popup, which means Private.Login() is + -- never invoked, no auras load, and the next logout writes an empty + -- WeakAurasSaved over the user's profile. + C_Timer.After(0, function() + StaticPopup_Show("WEAKAURAS_CONFIRM_REPAIR", nil, nil, {reason = "downgrade"}) + end) end elseif event == "PLAYER_LOGOUT" then for id in pairs(db.displays) do @@ -2264,14 +2272,25 @@ StaticPopupDialogs["WEAKAURAS_CONFIRM_REPAIR"] = { local AutomaticRepairText = L["WeakAuras has detected that it has been downgraded.\nYour saved auras may no longer work properly.\nWould you like to run the |cffff0000EXPERIMENTAL|r repair tool? This will overwrite any changes you have made since the last database upgrade.\nLast upgrade: %s\n\n|cffff0000You should BACKUP your WTF folder BEFORE pressing this button.|r"] local ManualRepairText = L["Are you sure you want to run the |cffff0000EXPERIMENTAL|r repair tool?\nThis will overwrite any changes you have made since the last database upgrade.\nLast upgrade: %s"] - if self.data.reason == "user" then + -- CoA: guard against malformed data; default reason to "unknown" so the + -- popup can't error out and block Private.Login() from ever running. + local reason = "unknown" + if self.data then + reason = self.data.reason or "unknown" + end + + if reason == "user" then self.text:SetText(ManualRepairText:format(LastUpgrade())) else self.text:SetText(AutomaticRepairText:format(LastUpgrade())) end end, OnCancel = function(self) - if self.data.reason ~= "user" then + local reason = "unknown" + if self.data then + reason = self.data.reason or "unknown" + end + if reason ~= "user" then Private.Login() end end, diff --git a/tools/build_zip.sh b/tools/build_zip.sh new file mode 100755 index 0000000..45d1400 --- /dev/null +++ b/tools/build_zip.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Build per-addon zip artefacts from HEAD via git-archive. +# +# - Discovers top-level addon folders (Foo/Foo.toc). +# - Re-creates dist/ each run. +# - Always archives HEAD, so the working tree state is irrelevant. +# - If more than one addon folder is present, also emits -all.zip +# with every addon folder side-by-side at the zip root. +# - When run inside Gitea Actions the working tree lives under a +# per-job dir like /var/lib/act_runner/work/.../hostexecutor, so the +# repo name comes from $GITHUB_REPOSITORY (set by the runner) and +# only falls back to the toplevel basename for local invocations. +set -euo pipefail + +root=$(git rev-parse --show-toplevel) +cd "$root" + +# Gitea Actions sets GITHUB_REPOSITORY=owner/repo. The basename of +# `git rev-parse --show-toplevel` inside the runner is the worker dir +# (e.g. `hostexecutor`), which would name the bundle wrong. +if [ -n "${GITHUB_REPOSITORY:-}" ]; then + repo_name="${GITHUB_REPOSITORY##*/}" +else + repo_name=$(basename "$root") +fi +dist="$root/dist" + +# Find Foo/Foo.toc pairs at depth 1; ignore libs nested deeper. +addons=() +while IFS= read -r toc; do + dir=$(dirname "$toc") + folder=$(basename "$dir") + base=$(basename "$toc" .toc) + # Accept Foo.toc and Foo_Wrath.toc style flavour variants; folder must match + # at least one toc basename prefix (Foo). + case "$base" in + "$folder"|"$folder"_*) addons+=("$folder") ;; + esac +done < <(command find . -mindepth 2 -maxdepth 2 -type f -name '*.toc' | sed 's|^\./||' | sort) + +# Dedupe (a folder with Foo.toc + Foo_Wrath.toc shows up twice). +if [ ${#addons[@]} -gt 0 ]; then + mapfile -t addons < <(printf '%s\n' "${addons[@]}" | awk '!seen[$0]++') +fi + +if [ ${#addons[@]} -eq 0 ]; then + echo "no addon folders found (looking for */Foo.toc with matching folder name)" >&2 + exit 1 +fi + +rm -rf "$dist" +mkdir -p "$dist" + +for folder in "${addons[@]}"; do + out="$dist/$folder.zip" + # No --prefix: the folder already sits at the repo root, so git-archive + # emits entries as /... which is exactly what + # Interface/AddOns/ expects after extraction. + git archive HEAD --format=zip -o "$out" -- "$folder" + echo "built dist/$folder.zip" +done + +# Combined bundle only makes sense when there are multiple addons. +if [ ${#addons[@]} -gt 1 ]; then + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + git archive HEAD --format=tar -- "${addons[@]}" | tar -x -C "$tmp" + out="$dist/$repo_name-all.zip" + ( cd "$tmp" && zip -qr "$out" "${addons[@]}" ) + echo "built dist/$repo_name-all.zip" +fi