|
1 | 1 | name: Release |
2 | 2 |
|
| 3 | +permissions: |
| 4 | + contents: read |
| 5 | + |
3 | 6 | on: |
4 | 7 | push: |
5 | | - # Trigger on tag pushes. Adjust pattern as needed (e.g. 'v*.*.*') |
6 | | - tags: |
7 | | - - '**' |
| 8 | + branches: |
| 9 | + - main |
8 | 10 | workflow_dispatch: |
9 | 11 |
|
10 | 12 | jobs: |
|
25 | 27 | cgo: 0 |
26 | 28 | - goos: linux |
27 | 29 | goarch: arm |
28 | | - goarm: '7' |
| 30 | + goarm: "7" |
29 | 31 | cgo: 0 |
30 | 32 | - goos: linux |
31 | 33 | goarch: mipsle |
|
76 | 78 | - name: Set up Go |
77 | 79 | uses: actions/setup-go@v6 |
78 | 80 | with: |
79 | | - go-version: '1.25.5' |
| 81 | + go-version: "1.25.5" |
80 | 82 |
|
81 | 83 | # Install Android NDK only for android rows |
82 | 84 | - name: Setup Android NDK |
@@ -149,23 +151,187 @@ jobs: |
149 | 151 | name: Create GitHub Release |
150 | 152 | needs: build |
151 | 153 | runs-on: ubuntu-latest |
| 154 | + outputs: |
| 155 | + should_release: ${{ steps.meta.outputs.should_release }} |
| 156 | + tag_name: ${{ steps.meta.outputs.tag_name }} |
152 | 157 | permissions: |
153 | 158 | contents: write |
154 | 159 | steps: |
| 160 | + - name: Check out code |
| 161 | + uses: actions/checkout@v6 |
| 162 | + with: |
| 163 | + fetch-depth: 0 |
| 164 | + fetch-tags: true |
| 165 | + |
155 | 166 | - name: Download build artifacts |
156 | 167 | uses: actions/download-artifact@v7 |
157 | 168 | with: |
158 | 169 | path: dist |
159 | 170 |
|
| 171 | + - name: Prepare release metadata |
| 172 | + id: meta |
| 173 | + run: | |
| 174 | + LATEST_TAG="$(git tag -l | grep -E '^(v)?[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || true)" |
| 175 | + PREFIX="" |
| 176 | + BASE_VERSION="$LATEST_TAG" |
| 177 | +
|
| 178 | + if [ -z "$LATEST_TAG" ]; then |
| 179 | + BASE_VERSION="0.0.0" |
| 180 | + elif [ "${LATEST_TAG#v}" != "$LATEST_TAG" ]; then |
| 181 | + PREFIX="v" |
| 182 | + BASE_VERSION="${LATEST_TAG#v}" |
| 183 | + fi |
| 184 | +
|
| 185 | + IFS=. read -r MAJOR MINOR PATCH <<< "$BASE_VERSION" |
| 186 | +
|
| 187 | + if [ -n "$LATEST_TAG" ]; then |
| 188 | + RANGE="$LATEST_TAG..HEAD" |
| 189 | + else |
| 190 | + RANGE="HEAD" |
| 191 | + fi |
| 192 | +
|
| 193 | + COMMITS="$(git log --no-merges --format='%s' "$RANGE")" |
| 194 | + BUMP="" |
| 195 | +
|
| 196 | + if printf '%s\n' "$COMMITS" | grep -Eiq '^break([(:! ]|$)'; then |
| 197 | + BUMP="major" |
| 198 | + MAJOR=$((MAJOR + 1)) |
| 199 | + MINOR=0 |
| 200 | + PATCH=0 |
| 201 | + elif printf '%s\n' "$COMMITS" | grep -Eiq '^feat([(:! ]|$)'; then |
| 202 | + BUMP="minor" |
| 203 | + MINOR=$((MINOR + 1)) |
| 204 | + PATCH=0 |
| 205 | + elif printf '%s\n' "$COMMITS" | grep -Eiq '^fix([(:! ]|$)'; then |
| 206 | + BUMP="patch" |
| 207 | + PATCH=$((PATCH + 1)) |
| 208 | + fi |
| 209 | +
|
| 210 | + NEXT_VERSION="${MAJOR}.${MINOR}.${PATCH}" |
| 211 | + NEXT_TAG="${PREFIX}${NEXT_VERSION}" |
| 212 | +
|
| 213 | + { |
| 214 | + echo "previous_tag=$LATEST_TAG" |
| 215 | + echo "range=$RANGE" |
| 216 | + echo "bump=$BUMP" |
| 217 | + echo "should_release=$([ -n "$BUMP" ] && echo true || echo false)" |
| 218 | + echo "tag_name=$NEXT_TAG" |
| 219 | + echo "release_name=$NEXT_TAG" |
| 220 | + echo "body_path=release-notes.md" |
| 221 | + } >> "$GITHUB_OUTPUT" |
| 222 | +
|
| 223 | + - name: Generate release notes from commit messages |
| 224 | + id: notes |
| 225 | + if: steps.meta.outputs.should_release == 'true' |
| 226 | + env: |
| 227 | + PREVIOUS_TAG: ${{ steps.meta.outputs.previous_tag }} |
| 228 | + NEXT_TAG: ${{ steps.meta.outputs.tag_name }} |
| 229 | + BUMP: ${{ steps.meta.outputs.bump }} |
| 230 | + RANGE: ${{ steps.meta.outputs.range }} |
| 231 | + run: | |
| 232 | + strip_prefix() { |
| 233 | + sed -E 's/^(feat|fix|break)([(!:]|[[:space:]])+[[:space:]]*//I' |
| 234 | + } |
| 235 | +
|
| 236 | + FEATURES="$(git log --no-merges --format='%s (%h)' "$RANGE" | grep -Ei '^feat([(:! ]|$)' | strip_prefix | LC_ALL=C sort -f || true)" |
| 237 | + FIXES="$(git log --no-merges --format='%s (%h)' "$RANGE" | grep -Ei '^fix([(:! ]|$)' | strip_prefix | LC_ALL=C sort -f || true)" |
| 238 | + BREAKING="$(git log --no-merges --format='%s (%h)' "$RANGE" | grep -Ei '^break([(:! ]|$)' | strip_prefix | LC_ALL=C sort -f || true)" |
| 239 | +
|
| 240 | + { |
| 241 | + echo "body_path=release-notes.md" |
| 242 | + } >> "$GITHUB_OUTPUT" |
| 243 | +
|
| 244 | + { |
| 245 | + if [ -n "$BREAKING" ]; then |
| 246 | + echo "### Несовместимые изменения" |
| 247 | + echo |
| 248 | + printf '%s\n' "$BREAKING" | sed 's/^/- /' |
| 249 | + echo |
| 250 | + fi |
| 251 | +
|
| 252 | + if [ -n "$FEATURES" ]; then |
| 253 | + echo "### Новые функции" |
| 254 | + echo |
| 255 | + printf '%s\n' "$FEATURES" | sed 's/^/- /' |
| 256 | + echo |
| 257 | + fi |
| 258 | +
|
| 259 | + if [ -n "$FIXES" ]; then |
| 260 | + echo "### Исправление багов" |
| 261 | + echo |
| 262 | + printf '%s\n' "$FIXES" | sed 's/^/- /' |
| 263 | + echo |
| 264 | + fi |
| 265 | + } > release-notes.md |
| 266 | +
|
160 | 267 | - name: Create Release |
| 268 | + if: steps.meta.outputs.should_release == 'true' |
161 | 269 | uses: softprops/action-gh-release@v2 |
162 | 270 | with: |
163 | 271 | # upload every file in dist/ including nested files |
164 | 272 | files: | |
165 | 273 | dist/** |
166 | | - # make the release name/tag the pushed tag |
167 | | - tag_name: ${{ github.ref_name }} |
168 | | - name: ${{ github.ref_name }} |
| 274 | + tag_name: ${{ steps.meta.outputs.tag_name }} |
| 275 | + target_commitish: ${{ github.sha }} |
| 276 | + name: ${{ steps.meta.outputs.release_name }} |
| 277 | + body_path: ${{ steps.meta.outputs.body_path }} |
169 | 278 | overwrite_files: true |
170 | 279 | env: |
171 | 280 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 281 | + |
| 282 | + - name: Skip release when no semantic commit is found |
| 283 | + if: steps.meta.outputs.should_release != 'true' |
| 284 | + run: | |
| 285 | + echo "No new semantic release created." |
| 286 | + echo "Add a commit starting with break, feat, or fix after the latest tag." |
| 287 | +
|
| 288 | + docker: |
| 289 | + name: Publish Docker image |
| 290 | + needs: release |
| 291 | + if: needs.release.outputs.should_release == 'true' |
| 292 | + runs-on: ubuntu-latest |
| 293 | + permissions: |
| 294 | + contents: read |
| 295 | + packages: write |
| 296 | + steps: |
| 297 | + - name: Check out code |
| 298 | + uses: actions/checkout@v6 |
| 299 | + |
| 300 | + - name: Set up QEMU |
| 301 | + uses: docker/setup-qemu-action@v3 |
| 302 | + |
| 303 | + - name: Set up Docker Buildx |
| 304 | + uses: docker/setup-buildx-action@v3 |
| 305 | + |
| 306 | + - name: Prepare image name |
| 307 | + id: image |
| 308 | + env: |
| 309 | + REPOSITORY: ${{ github.repository }} |
| 310 | + run: | |
| 311 | + echo "name=ghcr.io/$(echo "$REPOSITORY" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" |
| 312 | +
|
| 313 | + - name: Log in to GitHub Container Registry |
| 314 | + uses: docker/login-action@v3 |
| 315 | + with: |
| 316 | + registry: ghcr.io |
| 317 | + username: ${{ github.actor }} |
| 318 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 319 | + |
| 320 | + - name: Extract Docker metadata |
| 321 | + id: meta |
| 322 | + uses: docker/metadata-action@v5 |
| 323 | + with: |
| 324 | + images: ${{ steps.image.outputs.name }} |
| 325 | + tags: | |
| 326 | + type=raw,value=latest |
| 327 | + type=raw,value=${{ needs.release.outputs.tag_name }} |
| 328 | +
|
| 329 | + - name: Build and push Docker image |
| 330 | + uses: docker/build-push-action@v6 |
| 331 | + with: |
| 332 | + context: . |
| 333 | + file: ./Dockerfile |
| 334 | + platforms: linux/amd64,linux/arm64 |
| 335 | + push: true |
| 336 | + tags: ${{ steps.meta.outputs.tags }} |
| 337 | + labels: ${{ steps.meta.outputs.labels }} |
0 commit comments