music-assistant-server

10.3 KBYML
auto-release.yml
10.3 KB260 lines • yaml
1name: Auto Release
2
3# Automatically creates releases with proper version increments
4# - Nightly: runs at 02:00 UTC daily if there are 2+ commits (format: 1.2.3.dev20251025HH)
5# - Beta: manual trigger (format: 1.2.0b1, 1.2.0b2, etc.)
6# - Stable: manual trigger (format: 1.2.3, 1.2.4, etc.)
7
8on:
9  schedule:
10    # Run at 03:00 UTC every day for nightly releases
11    - cron: "0 3 * * *"
12  workflow_dispatch:
13    inputs:
14      channel:
15        description: "Release channel"
16        required: true
17        type: choice
18        options:
19          - nightly
20          - beta
21          - stable
22        default: nightly
23      important_notes:
24        description: "Important notes (breaking changes, critical info, etc.)"
25        required: false
26
27permissions:
28  actions: write
29  contents: write
30
31jobs:
32  check-and-release:
33    runs-on: ubuntu-latest
34    outputs:
35      version: ${{ steps.next_version.outputs.version }}
36      should_release: ${{ steps.check_commits.outputs.has_commits }}
37      channel: ${{ steps.set_channel.outputs.channel }}
38    steps:
39      - name: Set release channel
40        id: set_channel
41        run: |
42          # Use input channel for manual runs, default to nightly for scheduled runs
43          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
44            CHANNEL="${{ inputs.channel }}"
45          else
46            CHANNEL="nightly"
47          fi
48          echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
49          echo "Release channel: $CHANNEL"
50
51      - name: Checkout repository
52        uses: actions/checkout@v6
53        with:
54          fetch-depth: 0 # Fetch all history for proper comparison
55
56      - name: Check for new commits
57        id: check_commits
58        run: |
59          CHANNEL="${{ steps.set_channel.outputs.channel }}"
60
61          # Define search patterns for each channel
62          case "$CHANNEL" in
63            nightly)
64              SEARCH_PATTERN="dev"
65              ;;
66            beta)
67              SEARCH_PATTERN="b"
68              ;;
69            stable)
70              # For stable, we want versions that don't contain dev or beta suffixes
71              SEARCH_PATTERN="stable"
72              ;;
73          esac
74
75          # Get the latest release for the channel
76          if [ "$CHANNEL" = "stable" ]; then
77            # For stable, get releases that don't contain dev or beta suffixes
78            LATEST_RELEASE=$(gh release list --exclude-drafts --limit 100 --json createdAt,tagName,isPrerelease --jq '.[] | select(.tagName | contains("dev") | not) | select(.tagName | test("b[0-9]") | not)' 2>/dev/null | jq -s '.[0]' || echo "")
79          else
80            # For nightly and beta, filter by pattern
81            LATEST_RELEASE=$(gh release list --exclude-drafts --limit 100 --json createdAt,tagName,isPrerelease --jq ".[] | select(.tagName | contains(\"$SEARCH_PATTERN\"))" 2>/dev/null | jq -s '.[0]' || echo "")
82          fi
83
84          if [ -z "$LATEST_RELEASE" ] || [ "$LATEST_RELEASE" == "null" ]; then
85            echo "No previous $CHANNEL releases found"
86            echo "has_commits=true" >> $GITHUB_OUTPUT
87            echo "last_tag=" >> $GITHUB_OUTPUT
88          else
89            RELEASE_DATE=$(echo "$LATEST_RELEASE" | jq -r '.createdAt')
90            LAST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tagName')
91            echo "Latest $CHANNEL release: $LAST_TAG at $RELEASE_DATE"
92            echo "last_tag=$LAST_TAG" >> $GITHUB_OUTPUT
93
94            # Check if there are commits since the latest release
95            COMMITS_SINCE=$(git log --since="$RELEASE_DATE" --oneline | wc -l)
96            echo "Commits since last $CHANNEL release: $COMMITS_SINCE"
97
98            # Require at least 2 commits for auto-release (nightly only)
99            # For manual beta/stable releases, always proceed
100            if [ "$CHANNEL" = "nightly" ]; then
101              if [ "$COMMITS_SINCE" -ge 2 ]; then
102                echo "has_commits=true" >> $GITHUB_OUTPUT
103              else
104                echo "has_commits=false" >> $GITHUB_OUTPUT
105                echo "Only $COMMITS_SINCE commit(s) found. Need at least 2 commits for auto-release."
106              fi
107            else
108              # Manual releases (beta/stable) always proceed
109              echo "has_commits=true" >> $GITHUB_OUTPUT
110            fi
111          fi
112        env:
113          GH_TOKEN: ${{ github.token }}
114
115      - name: Get last stable release (for beta and nightly versioning)
116        id: last_stable
117        if: ${{ steps.set_channel.outputs.channel == 'beta' || steps.set_channel.outputs.channel == 'nightly' }}
118        run: |
119          # Get the latest stable release (no dev or beta suffixes)
120          LATEST_STABLE=$(gh release list --exclude-drafts --limit 100 --json createdAt,tagName,isPrerelease --jq '.[] | select(.tagName | contains("dev") | not) | select(.tagName | test("b[0-9]") | not)' 2>/dev/null | jq -s '.[0]' || echo "")
121
122          if [ -z "$LATEST_STABLE" ] || [ "$LATEST_STABLE" == "null" ]; then
123            echo "No previous stable releases found"
124            echo "stable_tag=" >> $GITHUB_OUTPUT
125          else
126            STABLE_TAG=$(echo "$LATEST_STABLE" | jq -r '.tagName')
127            echo "Latest stable release: $STABLE_TAG"
128            echo "stable_tag=$STABLE_TAG" >> $GITHUB_OUTPUT
129          fi
130        env:
131          GH_TOKEN: ${{ github.token }}
132
133      - name: Calculate next version
134        id: next_version
135        if: steps.check_commits.outputs.has_commits == 'true'
136        run: |
137          LAST_TAG="${{ steps.check_commits.outputs.last_tag }}"
138          CHANNEL="${{ steps.set_channel.outputs.channel }}"
139
140          case "$CHANNEL" in
141            nightly)
142              # Nightly: format 1.2.3.devYYYYMMDDHH
143              # Always one minor version ahead of the last stable release
144              TODAY=$(date -u +%Y%m%d)
145              HOUR=$(date -u +%H)
146              LAST_STABLE_TAG="${{ steps.last_stable.outputs.stable_tag }}"
147
148              # Determine the base version (should be one minor version ahead of stable)
149              if [ -n "$LAST_STABLE_TAG" ]; then
150                STABLE_VERSION=$(echo "$LAST_STABLE_TAG" | sed 's/^v//')
151
152                if [[ "$STABLE_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
153                  MAJOR="${BASH_REMATCH[1]}"
154                  MINOR="${BASH_REMATCH[2]}"
155                  NEXT_MINOR=$((MINOR + 1))
156                  BASE_VERSION="${MAJOR}.${NEXT_MINOR}.0"
157                else
158                  BASE_VERSION="0.1.0"
159                fi
160              else
161                # No stable release found, start with default
162                BASE_VERSION="0.1.0"
163              fi
164
165              NEW_VERSION="${BASE_VERSION}.dev${TODAY}${HOUR}"
166              echo "Nightly version based on stable ${LAST_STABLE_TAG}: ${NEW_VERSION}"
167              ;;
168
169            beta)
170              # Beta: format 1.2.0b1, 1.2.0b2, etc.
171              # Always base the version on the last STABLE release, not dev versions
172              LAST_BETA_TAG="${{ steps.check_commits.outputs.last_tag }}"
173              LAST_STABLE_TAG="${{ steps.last_stable.outputs.stable_tag }}"
174
175              # Check if there's an existing beta version
176              if [ -n "$LAST_BETA_TAG" ]; then
177                BETA_VERSION=$(echo "$LAST_BETA_TAG" | sed 's/^v//')
178
179                # Check if it's already a beta version (e.g., 2.7.0b1)
180                if [[ "$BETA_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)b([0-9]+)$ ]]; then
181                  BASE_VERSION="${BASH_REMATCH[1]}"
182                  BETA_NUM="${BASH_REMATCH[2]}"
183                  NEXT_BETA=$((BETA_NUM + 1))
184                  NEW_VERSION="${BASE_VERSION}b${NEXT_BETA}"
185                  echo "Incrementing existing beta: ${LAST_BETA_TAG} -> ${NEW_VERSION}"
186                else
187                  # Should not happen, but fallback
188                  NEW_VERSION="0.1.0b1"
189                fi
190              elif [ -n "$LAST_STABLE_TAG" ]; then
191                # No beta exists, increment minor from last stable and start at b1
192                STABLE_VERSION=$(echo "$LAST_STABLE_TAG" | sed 's/^v//')
193
194                if [[ "$STABLE_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
195                  MAJOR="${BASH_REMATCH[1]}"
196                  MINOR="${BASH_REMATCH[2]}"
197                  NEXT_MINOR=$((MINOR + 1))
198                  NEW_VERSION="${MAJOR}.${NEXT_MINOR}.0b1"
199                  echo "Creating first beta based on stable ${LAST_STABLE_TAG}: ${NEW_VERSION}"
200                else
201                  NEW_VERSION="0.1.0b1"
202                fi
203              else
204                # No stable or beta found, start fresh
205                NEW_VERSION="0.1.0b1"
206              fi
207              ;;
208
209            stable)
210              # Stable: format 1.2.3, increment patch version
211              if [ -z "$LAST_TAG" ]; then
212                NEW_VERSION="0.1.0"
213              else
214                VERSION=$(echo "$LAST_TAG" | sed 's/^v//')
215
216                # Extract major.minor.patch and increment patch
217                if [[ "$VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
218                  MAJOR="${BASH_REMATCH[1]}"
219                  MINOR="${BASH_REMATCH[2]}"
220                  PATCH="${BASH_REMATCH[3]}"
221                  NEXT_PATCH=$((PATCH + 1))
222                  NEW_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}"
223                else
224                  NEW_VERSION="0.1.0"
225                fi
226              fi
227              ;;
228          esac
229
230          echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
231          echo "New $CHANNEL version: $NEW_VERSION"
232
233      - name: Log release decision
234        run: |
235          CHANNEL="${{ steps.set_channel.outputs.channel }}"
236          if [ "${{ steps.check_commits.outputs.has_commits }}" == "true" ]; then
237            echo "✅ Will create $CHANNEL release ${{ steps.next_version.outputs.version }}"
238          else
239            echo "⏭️ Skipping release - not enough commits"
240          fi
241
242  trigger-release:
243    name: Trigger Release Workflow
244    needs: check-and-release
245    if: needs.check-and-release.outputs.should_release == 'true'
246    permissions:
247      actions: write
248      contents: write
249      pull-requests: read
250      packages: write
251      id-token: write # Required for PyPI publishing
252    uses: ./.github/workflows/release.yml
253    with:
254      version: ${{ needs.check-and-release.outputs.version }}
255      channel: ${{ needs.check-and-release.outputs.channel }}
256      important_notes: ${{ inputs.important_notes }}
257    secrets:
258      PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
259      PRIVILEGED_GITHUB_TOKEN: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }}
260