name: Web Delete Release # Deletes a FTStore Admin (web/) release, images included. Only the `web-v*` # namespace: this refuses `v*` and `manager-v*` outright rather than trusting # the confirmation field, because a hand-typed tag is the one input that can # take out the release an installed app is currently resolving updates from. # # The two app namespaces are not left to pile up — `stable-release.yml` retires # their older releases itself, keeping the newest and trimming the AltStore # version history to match. That path knows which release it just published; # this one only knows what someone typed. on: workflow_dispatch: inputs: tag: description: "Release tag to delete (e.g. web-v0.1)" required: true type: string confirm: description: "Type the tag again to confirm deletion" required: true type: string jobs: delete: name: Delete Release & Tag runs-on: [self-hosted, linux, x64] permissions: contents: write packages: write steps: - name: Confirm deletion run: | if [ "${{ inputs.tag }}" != "${{ inputs.confirm }}" ]; then echo "::error::Confirmation does not match. You entered '${{ inputs.confirm }}' but expected '${{ inputs.tag }}'" exit 1 fi if [[ ! "${{ inputs.tag }}" =~ ^web-v ]]; then echo "::error::This workflow only deletes web-v* releases. '${{ inputs.tag }}' is not one — the app's v* releases are not deletable from here." exit 1 fi - name: Delete GitHub Release continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release delete "${{ inputs.tag }}" --repo "${{ github.repository }}" --yes --cleanup-tag && \ echo "Deleted release and tag: ${{ inputs.tag }}" || \ echo "No release found for ${{ inputs.tag }}, skipping" - name: Delete orphan tag continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git ls-remote --tags "https://github.com/${{ github.repository }}" "${{ inputs.tag }}" | grep -q . && \ gh api --method DELETE "repos/${{ github.repository }}/git/refs/tags/${{ inputs.tag }}" && \ echo "Deleted orphan tag: ${{ inputs.tag }}" || \ echo "No tag found for ${{ inputs.tag }}, skipping" - name: Delete Docker images continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="${{ inputs.tag }}" # web-release.yml pushes `vX.Y` — the git tag minus the namespace. # `X.Y` is what the pre-rename semver rule produced; still matched so # images from before the rename can be cleaned up too. VERSION="${TAG#web-}" BARE="${VERSION#v}" OWNER="${{ github.repository_owner }}" for IMAGE in ftstore-admin-app ftstore-admin-worker; do # Find the image version ID VERSION_ID=$(gh api \ "orgs/${OWNER}/packages/container/${IMAGE}/versions" \ --jq ".[] | select(.metadata.container.tags | index(\"${VERSION}\") or index(\"${BARE}\")) | .id" 2>/dev/null || true) if [ -n "$VERSION_ID" ]; then gh api --method DELETE \ "orgs/${OWNER}/packages/container/${IMAGE}/versions/${VERSION_ID}" 2>/dev/null || \ gh api --method DELETE \ "users/${OWNER}/packages/container/${IMAGE}/versions/${VERSION_ID}" 2>/dev/null || true echo "Deleted ${IMAGE}:${VERSION}" else echo "No image found for ${IMAGE}:${VERSION}" fi done