Skip to content

Commit 639e530

Browse files
YoniMelkigithub-actions[bot]
andauthored
AX-2020 - Add tag-driven release workflow (#1)
Add .github/workflows/release.yml, triggered by pushing a vX.Y.Z tag. It verifies the tag is on main, matches VERSION and that VERSION agrees with gradle.properties, builds the plugin zip with ./gradlew buildPlugin, and attaches it to a GitHub release. It deletes the tag if any step fails, so a broken tag never lingers as a release candidate. Add the same VERSION/gradle.properties agreement check to validate.yml so pull requests catch version drift before merge. https://jfrog-int.atlassian.net/browse/AX-2020 Co-authored-by: github-actions[bot] <devops+github-actions@qwak.ai>
1 parent 1d80f74 commit 639e530

2 files changed

Lines changed: 141 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright (c) JFrog Ltd. 2026
2+
# Licensed under the Apache License, Version 2.0
3+
# https://www.apache.org/licenses/LICENSE-2.0
4+
#
5+
# Release flow: a human bumps VERSION and gradle.properties together, merges to main,
6+
# then tags that main commit:
7+
# git checkout main && git pull && git tag vX.Y.Z && git push origin vX.Y.Z
8+
# This workflow builds the plugin zip and attaches it to a GitHub Release.
9+
name: Release
10+
11+
on:
12+
push:
13+
tags: ["v*"]
14+
15+
permissions:
16+
contents: write
17+
18+
jobs:
19+
release:
20+
runs-on: ubuntu-latest
21+
steps:
22+
# Full history, so the tag checks below can see existing tags and ancestry.
23+
- uses: actions/checkout@v5
24+
with:
25+
fetch-depth: 0
26+
27+
# Refuse to release a tag that points at a commit not on main (e.g. a feature branch).
28+
- name: Ensure the tag is on main
29+
run: |
30+
git fetch --no-tags origin main
31+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
32+
echo "::error::Tag ${GITHUB_REF_NAME} points at a commit that is not on main. Release only after merging to main."
33+
exit 1
34+
fi
35+
36+
# The tag is pushed by a human before this workflow ever runs, so by the time any check here
37+
# could fail, the tag already exists. Catch the two ways that goes wrong: a malformed tag name
38+
# (the trigger glob `v*` accepts `v1`, `v1.2.3-rc1`, `vfoo`, not just `vX.Y.Z`), and a tag that
39+
# is not actually newer than the last release (a typo'd or duplicate version number).
40+
# `sort -V` orders numerically (0.9.0 < 0.10.0), not lexicographically.
41+
- name: Validate the tag itself (format, and newer than the last release)
42+
run: |
43+
set -euo pipefail
44+
TAG="${GITHUB_REF_NAME}"
45+
if ! echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
46+
echo "::error::Tag '$TAG' is not a valid vX.Y.Z semver tag — refusing to release"
47+
exit 1
48+
fi
49+
git fetch --tags origin
50+
# grep -v exits 1 if it filters out every line - the first release, where the only tag is
51+
# the one being pushed. Absorb it so the "no previous release" check below still runs.
52+
LATEST=$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' | { grep -v "^${TAG}\$" || true; } | sort -V | tail -1)
53+
if [ -n "$LATEST" ]; then
54+
LOWEST=$(printf '%s\n%s\n' "$TAG" "$LATEST" | sort -V | head -1)
55+
if [ "$LOWEST" = "$TAG" ]; then
56+
echo "::error::Tag $TAG is not newer than the latest release ($LATEST) — check for a typo"
57+
exit 1
58+
fi
59+
fi
60+
61+
# VERSION at the repo root is the single source of truth for the version; the tag has to
62+
# agree with it, and gradle.properties has to agree with VERSION.
63+
- name: Ensure the tag matches VERSION, and VERSION matches gradle.properties
64+
run: |
65+
set -euo pipefail
66+
FILE_VERSION=$(tr -d '[:space:]' < VERSION)
67+
if [ "${GITHUB_REF_NAME#v}" != "$FILE_VERSION" ]; then
68+
echo "::error::Tag ${GITHUB_REF_NAME} does not match VERSION ($FILE_VERSION) — bump VERSION and re-tag"
69+
exit 1
70+
fi
71+
GRADLE_VERSION=$(grep -E '^version[[:space:]]*=' gradle.properties | sed -E 's/^version[[:space:]]*=[[:space:]]*//')
72+
if [ "$FILE_VERSION" != "$GRADLE_VERSION" ]; then
73+
echo "::error::VERSION ($FILE_VERSION) does not match gradle.properties version ($GRADLE_VERSION) — sync them and re-tag"
74+
exit 1
75+
fi
76+
77+
- name: Set up Node.js
78+
uses: actions/setup-node@v5
79+
with:
80+
node-version: "24"
81+
82+
# Gate the release on the same layout check PRs run, so a tag can never ship a broken layout.
83+
- name: Validate plugin layout
84+
run: node scripts/validate-jetbrains-plugin.mjs
85+
86+
- name: Set up JDK
87+
uses: actions/setup-java@v4
88+
with:
89+
distribution: temurin
90+
java-version: "17"
91+
92+
- name: Build plugin
93+
run: ./gradlew buildPlugin --no-daemon
94+
95+
- name: Verify plugin
96+
run: ./gradlew verifyPlugin --no-daemon
97+
98+
# buildPlugin names the zip after the plugin and its version, so find it rather than
99+
# hardcoding the name.
100+
- name: Locate the built plugin zip
101+
id: artifact
102+
run: |
103+
set -euo pipefail
104+
ZIP=$(find build/distributions -maxdepth 1 -name '*.zip' | head -1)
105+
if [ -z "$ZIP" ]; then
106+
echo "::error::No plugin zip found under build/distributions"
107+
exit 1
108+
fi
109+
echo "zip=$ZIP" >> "$GITHUB_OUTPUT"
110+
111+
- name: Create GitHub release
112+
env:
113+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114+
run: |
115+
gh release create "${{ github.ref_name }}" \
116+
"${{ steps.artifact.outputs.zip }}" \
117+
--title "${{ github.ref_name }}" \
118+
--generate-notes
119+
120+
# The tag was already pushed (and publicly visible) before this job even started, so a
121+
# failure above doesn't stop it from existing — it just stops a GitHub Release from being
122+
# created for it. Delete the tag on any failure so a broken/untested tag doesn't linger as
123+
# a plausible-looking release; re-tag after fixing the underlying issue. `if: failure()`
124+
# runs regardless of which step above failed.
125+
- name: Delete the tag if the release failed
126+
if: failure()
127+
run: |
128+
echo "::error::Release failed — deleting tag ${GITHUB_REF_NAME} so it can't be mistaken for a real release. Fix the issue and re-tag."
129+
git push origin ":refs/tags/${GITHUB_REF_NAME}"

.github/workflows/validate.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ jobs:
1515
steps:
1616
- uses: actions/checkout@v5
1717

18+
# VERSION at the repo root is the release source of truth (see .github/workflows/release.yml).
19+
# Catch drift here so a PR cannot merge with the two files disagreeing.
20+
- name: Check VERSION matches gradle.properties
21+
run: |
22+
set -euo pipefail
23+
FILE_VERSION=$(tr -d '[:space:]' < VERSION)
24+
GRADLE_VERSION=$(grep -E '^version[[:space:]]*=' gradle.properties | sed -E 's/^version[[:space:]]*=[[:space:]]*//')
25+
if [ "$FILE_VERSION" != "$GRADLE_VERSION" ]; then
26+
echo "::error::VERSION ($FILE_VERSION) does not match gradle.properties version ($GRADLE_VERSION) — sync them"
27+
exit 1
28+
fi
29+
1830
- name: Set up Node.js
1931
uses: actions/setup-node@v5
2032
with:

0 commit comments

Comments
 (0)