Make and Update GitHub Releases (Weekly Cleanup + Rebuild) #204
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
| name: Make and Update GitHub Releases | |
| on: | |
| workflow_dispatch: {} | |
| release: | |
| types: [published] | |
| schedule: | |
| - cron: "0 0 * * *" # daily at 00:00 UTC | |
| concurrency: | |
| group: make-update-releases-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| cleanup-github-releases: | |
| name: 🧹 Cleanup Managed Releases & Tags | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Tags/releases managed by this workflow must start with one of these prefixes | |
| MANAGED_TAG_PREFIXES: >- | |
| AD-SSO-APIs-Integration | |
| All-Repository-Files | |
| BlueTeam-Tools | |
| Core-ScriptLibrary | |
| GPOs-Templates | |
| ITSM-Templates-SVR | |
| ITSM-Templates-WKS | |
| READMEs-Files-Package | |
| SysAdmin-Tools | |
| steps: | |
| - name: 🧰 Install Dependencies (gh + jq) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update | |
| sudo apt-get install -y jq gh | |
| - name: 🔐 Authenticate GitHub CLI | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| gh auth status | |
| - name: 🗑️ Delete Managed Releases and Tags (server-side) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| owner="${GITHUB_REPOSITORY%/*}" | |
| repo="${GITHUB_REPOSITORY#*/}" | |
| # Fetch release tag names once (limit 200; increase if you have more) | |
| mapfile -t all_release_tags < <(gh release list --limit 200 --json tagName -q '.[].tagName' || true) | |
| if [[ "${#all_release_tags[@]}" -eq 0 ]]; then | |
| echo "No releases found." | |
| exit 0 | |
| fi | |
| echo "Managed prefixes:" | |
| echo "${MANAGED_TAG_PREFIXES}" | tr ' ' '\n' | sed '/^\s*$/d' | |
| for prefix in ${MANAGED_TAG_PREFIXES}; do | |
| echo "Processing prefix: ${prefix}" | |
| for tag in "${all_release_tags[@]}"; do | |
| if [[ "$tag" == "${prefix}"* ]]; then | |
| echo "Deleting release: $tag" | |
| gh release delete "$tag" -y || true | |
| # Delete tag ref via API (avoids git checkout/push) | |
| # Endpoint expects: tags/<tag-name> (without refs/) | |
| echo "Deleting tag ref: tags/$tag" | |
| gh api -X DELETE "repos/${owner}/${repo}/git/refs/tags/${tag}" >/dev/null 2>&1 || true | |
| fi | |
| done | |
| done | |
| update-github-releases: | |
| name: 🚀 Build & Publish Managed Releases | |
| needs: cleanup-github-releases | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| release_name: | |
| - AD-SSO-APIs-Integration | |
| - All-Repository-Files | |
| - BlueTeam-Tools | |
| - Core-ScriptLibrary | |
| - GPOs-Templates | |
| - ITSM-Templates-SVR | |
| - ITSM-Templates-WKS | |
| - READMEs-Files-Package | |
| - SysAdmin-Tools | |
| steps: | |
| - name: 📦 Checkout Repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| submodules: true | |
| - name: 🧰 Install Dependencies (zip + jq) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update | |
| sudo apt-get install -y zip jq | |
| - name: 🏷️ Compute Version Tag | |
| id: tag | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| short_sha="${GITHUB_SHA::7}" | |
| date_tag="$(date -u +%Y%m%d)" | |
| echo "version_tag=${{ matrix.release_name }}-${date_tag}-${short_sha}" >> "$GITHUB_OUTPUT" | |
| - name: 📦 Build Release Artifact | |
| id: build | |
| shell: bash | |
| env: | |
| RELEASE_NAME: ${{ matrix.release_name }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts temp | |
| # Always start clean | |
| rm -rf temp/* | |
| mkdir -p temp | |
| # Helper: copy root metadata if present | |
| copy_root_meta() { | |
| [[ -f README.md ]] && cp README.md temp/ || true | |
| [[ -f LICENSE.txt ]] && cp LICENSE.txt temp/ || true | |
| [[ -f LICENSE ]] && cp LICENSE temp/ || true | |
| } | |
| case "$RELEASE_NAME" in | |
| BlueTeam-Tools|Core-ScriptLibrary|ITSM-Templates-SVR|ITSM-Templates-WKS|SysAdmin-Tools) | |
| if [[ ! -d "$RELEASE_NAME" ]]; then | |
| echo "::error::Directory not found: $RELEASE_NAME" | |
| exit 1 | |
| fi | |
| cp -r "$RELEASE_NAME" temp/ | |
| copy_root_meta | |
| ;; | |
| GPOs-Templates) | |
| if [[ ! -d "SysAdmin-Tools/GroupPolicyObjects-Templates" ]]; then | |
| echo "::error::Directory not found: SysAdmin-Tools/GroupPolicyObjects-Templates" | |
| exit 1 | |
| fi | |
| cp -r SysAdmin-Tools/GroupPolicyObjects-Templates/* temp/ | |
| # Optional helper tool (keep tolerant) | |
| [[ -f SysAdmin-Tools/ActiveDirectory-Management/Export-n-Import-GPOsTool.ps1 ]] && \ | |
| cp SysAdmin-Tools/ActiveDirectory-Management/Export-n-Import-GPOsTool.ps1 temp/ || true | |
| copy_root_meta | |
| ;; | |
| READMEs-Files-Package) | |
| # Collect all README.md files across repo (including root as main-README.md) | |
| [[ -f README.md ]] && cp README.md temp/main-README.md || true | |
| find . -type f -iname "README.md" ! -path "./README.md" | while read -r file; do | |
| dir="$(dirname "$file")" | |
| name="$(basename "$dir")" | |
| cp "$file" "temp/${name}-README.md" | |
| done | |
| ;; | |
| All-Repository-Files) | |
| for dir in BlueTeam-Tools Core-ScriptLibrary ITSM-Templates-SVR ITSM-Templates-WKS SysAdmin-Tools; do | |
| if [[ -d "$dir" ]]; then | |
| cp -r "$dir" temp/ | |
| else | |
| echo "::warning::Directory missing (skipped): $dir" | |
| fi | |
| done | |
| copy_root_meta | |
| ;; | |
| AD-SSO-APIs-Integration) | |
| if [[ ! -d "SysAdmin-Tools/ActiveDirectory-SSO-Integrations" ]]; then | |
| echo "::error::Directory not found: SysAdmin-Tools/ActiveDirectory-SSO-Integrations" | |
| exit 1 | |
| fi | |
| cp -r SysAdmin-Tools/ActiveDirectory-SSO-Integrations/* temp/ | |
| ;; | |
| *) | |
| echo "::error::Unknown RELEASE_NAME: $RELEASE_NAME" | |
| exit 1 | |
| ;; | |
| esac | |
| (cd temp && zip -r "../artifacts/${RELEASE_NAME}.zip" .) | |
| sha256sum "artifacts/${RELEASE_NAME}.zip" > "artifacts/${RELEASE_NAME}.sha256.txt" | |
| echo "zip_path=artifacts/${RELEASE_NAME}.zip" >> "$GITHUB_OUTPUT" | |
| echo "sha_path=artifacts/${RELEASE_NAME}.sha256.txt" >> "$GITHUB_OUTPUT" | |
| - name: 📝 Extract Changelog Section | |
| id: changelog | |
| shell: bash | |
| env: | |
| RELEASE_NAME: ${{ matrix.release_name }} | |
| run: | | |
| set -euo pipefail | |
| if [[ ! -f CHANGELOG.md ]]; then | |
| echo "body=No CHANGELOG.md found in repository root." >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| section="## ${RELEASE_NAME}" | |
| # Extract lines between "## RELEASE_NAME" and next "## " | |
| body="$(awk -v section="$section" ' | |
| $0 == section {found=1; next} | |
| /^## / && found {exit} | |
| found {print} | |
| ' CHANGELOG.md | sed -e 's/[[:space:]]\+$//' )" | |
| if [[ -z "${body// }" ]]; then | |
| body="No changelog available for ${RELEASE_NAME}." | |
| fi | |
| # Multi-line output | |
| echo "body<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$body" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| - name: 🧾 Upload Build Artifacts (Actions) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-${{ matrix.release_name }} | |
| path: | | |
| artifacts/${{ matrix.release_name }}.zip | |
| artifacts/${{ matrix.release_name }}.sha256.txt | |
| retention-days: 30 | |
| - name: 🚀 Create/Update GitHub Release (and upload assets) | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.tag.outputs.version_tag }} | |
| name: ${{ steps.tag.outputs.version_tag }} | |
| body: ${{ steps.changelog.outputs.body }} | |
| draft: false | |
| prerelease: false | |
| files: | | |
| artifacts/${{ matrix.release_name }}.zip | |
| artifacts/${{ matrix.release_name }}.sha256.txt | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |