@@ -3,20 +3,20 @@ name: Create Release
33on :
44 push :
55 tags :
6- - ' v*' # Se dispara cuando se sube un tag que empieza con 'v'
6+ - ' v*' # Triggers when a tag starting with 'v' is pushed
77
88jobs :
99 release :
1010 name : Create GitHub Release
1111 runs-on : ubuntu-latest
1212 permissions :
13- contents : write # Necesario para crear releases
13+ contents : write # Required to create releases
1414
1515 steps :
1616 - name : Checkout code
1717 uses : actions/checkout@v6
1818 with :
19- fetch-depth : 0 # Necesario para obtener el historial completo y el mensaje del tag
19+ fetch-depth : 0 # Required to get full history and tag message
2020
2121 - name : Get tag name
2222 id : tag
@@ -26,12 +26,12 @@ jobs:
2626 id : tag_message
2727 run : |
2828 TAG_NAME=${GITHUB_REF#refs/tags/}
29- # Obtener el mensaje del tag anotado, o usar el nombre del tag si no hay mensaje
29+ # Get annotated tag message, or use tag name if no message
3030 TAG_MESSAGE=$(git tag -l --format='%(contents)' "$TAG_NAME" 2>/dev/null || echo "")
3131 if [ -z "$TAG_MESSAGE" ]; then
3232 TAG_MESSAGE="Release $TAG_NAME"
3333 fi
34- # Preservar saltos de línea usando heredoc (no se necesita escape )
34+ # Preserve newlines using heredoc (no escaping needed )
3535 {
3636 echo "MESSAGE<<EOF"
3737 echo "$TAG_MESSAGE"
@@ -42,23 +42,64 @@ jobs:
4242 id : version
4343 run : |
4444 TAG_NAME=${GITHUB_REF#refs/tags/}
45- # Remover el prefijo 'v' si existe
45+ # Remove 'v' prefix if exists
4646 VERSION=${TAG_NAME#v}
4747 echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
4848
49+ - name : Get previous tag
50+ id : previous_tag
51+ run : |
52+ TAG_NAME=${GITHUB_REF#refs/tags/}
53+ # Get the previous tag (excluding the current one)
54+ PREVIOUS_TAG=$(git tag -l 'v*' | sort -V | grep -B1 "^$TAG_NAME$" | grep -v "^$TAG_NAME$" | tail -1 || echo "")
55+ if [ -z "$PREVIOUS_TAG" ]; then
56+ # If no previous tag found, use the first commit
57+ PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD 2>/dev/null || echo "")
58+ fi
59+ echo "PREVIOUS_TAG=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
60+ echo "Previous tag: $PREVIOUS_TAG"
61+
62+ - name : Get commits between tags
63+ id : commits
64+ run : |
65+ TAG_NAME=${GITHUB_REF#refs/tags/}
66+ PREVIOUS_TAG="${{ steps.previous_tag.outputs.PREVIOUS_TAG }}"
67+
68+ if [ -n "$PREVIOUS_TAG" ]; then
69+ # Get commits between previous tag and current tag
70+ COMMITS=$(git log --pretty=format:"- %s (%h)" "$PREVIOUS_TAG..$TAG_NAME" 2>/dev/null || echo "")
71+ if [ -n "$COMMITS" ]; then
72+ echo "HAS_COMMITS=true" >> $GITHUB_OUTPUT
73+ {
74+ echo "COMMITS<<EOF"
75+ echo "$COMMITS"
76+ echo "EOF"
77+ } >> $GITHUB_OUTPUT
78+ echo "Found $(echo "$COMMITS" | wc -l) commits between $PREVIOUS_TAG and $TAG_NAME"
79+ else
80+ echo "HAS_COMMITS=false" >> $GITHUB_OUTPUT
81+ echo "No commits found between $PREVIOUS_TAG and $TAG_NAME"
82+ fi
83+ else
84+ echo "HAS_COMMITS=false" >> $GITHUB_OUTPUT
85+ echo "No previous tag found, skipping commits"
86+ fi
87+
4988 - name : Get changelog entry
5089 id : changelog
5190 run : |
5291 VERSION="${{ steps.version.outputs.VERSION }}"
5392 CHANGELOG_FILE="docs/CHANGELOG.md"
54-
93+
5594 if [ -f "$CHANGELOG_FILE" ]; then
56- # Buscar la sección del changelog para esta versión
57- # Busca líneas que empiecen con ## seguido de la versión (con o sin v)
58- CHANGELOG_ENTRY=$(awk "/^## \[?$VERSION\]?/ {flag=1; next} /^## \[?[0-9]/ {flag=0} flag" "$CHANGELOG_FILE" 2>/dev/null || echo "")
59-
95+ # Search changelog section for this version
96+ # Match patterns like: ## [0.0.4] or ## 0.0.4
97+ # Escape dots in version for regex
98+ VERSION_ESCAPED=$(echo "$VERSION" | sed 's/\./\\./g')
99+ CHANGELOG_ENTRY=$(awk "/^## \[$VERSION_ESCAPED\]/ {flag=1; next} /^## \[?[0-9]/ {flag=0} flag" "$CHANGELOG_FILE" 2>/dev/null || echo "")
100+
60101 if [ -n "$CHANGELOG_ENTRY" ]; then
61- # Preservar saltos de línea usando heredoc (no se necesita escape )
102+ # Preserve newlines using heredoc (no escaping needed )
62103 echo "HAS_CHANGELOG=true" >> $GITHUB_OUTPUT
63104 {
64105 echo "ENTRY<<EOF"
@@ -67,26 +108,165 @@ jobs:
67108 } >> $GITHUB_OUTPUT
68109 else
69110 echo "HAS_CHANGELOG=false" >> $GITHUB_OUTPUT
111+ echo "Warning: No changelog entry found for version $VERSION" >&2
70112 fi
71113 else
72114 echo "HAS_CHANGELOG=false" >> $GITHUB_OUTPUT
115+ echo "Warning: CHANGELOG file not found at $CHANGELOG_FILE" >&2
73116 fi
74117
118+ - name : Check if release exists and update if needed
119+ id : check-existing
120+ uses : actions/github-script@v7
121+ with :
122+ script : |
123+ const tagName = '${{ steps.tag.outputs.TAG_NAME }}';
124+
125+ try {
126+ const { data: releases } = await github.rest.repos.listReleases({
127+ owner: context.repo.owner,
128+ repo: context.repo.repo,
129+ per_page: 100
130+ });
131+
132+ const existingRelease = releases.find(r => r.tag_name === tagName);
133+
134+ if (existingRelease) {
135+ const hasChangelog = (existingRelease.body || '').includes('## Changelog');
136+ core.setOutput('exists', 'true');
137+ core.setOutput('release_id', existingRelease.id.toString());
138+ core.setOutput('has_changelog', hasChangelog ? 'true' : 'false');
139+ console.log('Release ' + tagName + ' already exists (ID: ' + existingRelease.id + '), has changelog: ' + hasChangelog);
140+ } else {
141+ core.setOutput('exists', 'false');
142+ console.log('Release ' + tagName + ' does not exist, will create new one');
143+ }
144+ } catch (error) {
145+ console.log('Error checking existing releases:', error.message);
146+ core.setOutput('exists', 'false');
147+ }
148+
149+ - name : Update existing release
150+ if : steps.check-existing.outputs.exists == 'true'
151+ uses : actions/github-script@v7
152+ env :
153+ TAG_NAME : ${{ steps.tag.outputs.TAG_NAME }}
154+ TAG_MESSAGE : ${{ steps.tag_message.outputs.MESSAGE }}
155+ CHANGELOG_ENTRY : ${{ steps.changelog.outputs.ENTRY }}
156+ COMMITS : ${{ steps.commits.outputs.COMMITS }}
157+ HAS_CHANGELOG : ${{ steps.changelog.outputs.HAS_CHANGELOG }}
158+ HAS_COMMITS : ${{ steps.commits.outputs.HAS_COMMITS }}
159+ RELEASE_ID : ${{ steps.check-existing.outputs.release_id }}
160+ with :
161+ script : |
162+ const releaseId = parseInt(process.env.RELEASE_ID);
163+ const tagName = process.env.TAG_NAME;
164+ const tagMessage = process.env.TAG_MESSAGE || '';
165+ const changelogEntry = process.env.CHANGELOG_ENTRY || '';
166+ const commits = process.env.COMMITS || '';
167+ const hasChangelog = process.env.HAS_CHANGELOG === 'true';
168+ const hasCommits = process.env.HAS_COMMITS === 'true';
169+ const isPrerelease = tagName.includes('-alpha') || tagName.includes('-beta') || tagName.includes('-rc');
170+
171+ // Build release body - include tag message, changelog, and commits
172+ let releaseBody = tagMessage;
173+
174+ if (hasChangelog && changelogEntry) {
175+ releaseBody += '\n\n## Changelog\n\n' + changelogEntry;
176+ console.log('📝 Changelog entry found (' + changelogEntry.split('\n').length + ' lines)');
177+ } else {
178+ console.log('⚠️ No changelog entry found for version ' + tagName);
179+ }
180+
181+ if (hasCommits && commits) {
182+ releaseBody += '\n\n## Commits\n\n' + commits;
183+ console.log('📝 Commits found (' + commits.split('\n').length + ' commits)');
184+ } else {
185+ console.log('ℹ️ No commits found between tags');
186+ }
187+
188+ try {
189+ await github.rest.repos.updateRelease({
190+ owner: context.repo.owner,
191+ repo: context.repo.repo,
192+ release_id: releaseId,
193+ body: releaseBody,
194+ prerelease: isPrerelease,
195+ make_latest: !isPrerelease // Mark as latest if not prerelease
196+ });
197+ if (hasChangelog && changelogEntry) {
198+ console.log('✅ Updated release ' + tagName + ' with complete changelog');
199+ } else {
200+ console.log('✅ Updated release ' + tagName + ' (no changelog available)');
201+ }
202+
203+ // Verify latest status
204+ if (!isPrerelease) {
205+ console.log('✅ Release ' + tagName + ' marked as latest (make_latest: true)');
206+ }
207+ } catch (error) {
208+ console.error('❌ Error updating release ' + tagName + ':', error.message);
209+ throw error;
210+ }
211+
75212 - name : Create Release
76- uses : softprops/action-gh-release@v2
213+ if : steps.check-existing.outputs.exists != 'true'
214+ uses : actions/github-script@v7
215+ env :
216+ TAG_NAME : ${{ steps.tag.outputs.TAG_NAME }}
217+ TAG_MESSAGE : ${{ steps.tag_message.outputs.MESSAGE }}
218+ CHANGELOG_ENTRY : ${{ steps.changelog.outputs.ENTRY }}
219+ COMMITS : ${{ steps.commits.outputs.COMMITS }}
220+ HAS_CHANGELOG : ${{ steps.changelog.outputs.HAS_CHANGELOG }}
221+ HAS_COMMITS : ${{ steps.commits.outputs.HAS_COMMITS }}
77222 with :
78- tag_name : ${{ steps.tag.outputs.TAG_NAME }}
79- name : Release ${{ steps.tag.outputs.TAG_NAME }}
80- body : |
81- ${{ steps.tag_message.outputs.MESSAGE }}
82- ${{ steps.changelog.outputs.HAS_CHANGELOG == 'true' && format('
83-
84- # # Changelog
85-
86- {0}', steps.changelog.outputs.ENTRY) || '' }}
87- draft : false
88- prerelease : ${{ contains(steps.tag.outputs.TAG_NAME, '-alpha') || contains(steps.tag.outputs.TAG_NAME, '-beta') || contains(steps.tag.outputs.TAG_NAME, '-rc') }}
89- generate_release_notes : true # GitHub generará notas automáticas basadas en los commits
223+ script : |
224+ const tagName = process.env.TAG_NAME;
225+ const tagMessage = process.env.TAG_MESSAGE || '';
226+ const changelogEntry = process.env.CHANGELOG_ENTRY || '';
227+ const commits = process.env.COMMITS || '';
228+ const hasChangelog = process.env.HAS_CHANGELOG === 'true';
229+ const hasCommits = process.env.HAS_COMMITS === 'true';
230+ const isPrerelease = tagName.includes('-alpha') || tagName.includes('-beta') || tagName.includes('-rc');
231+
232+ // Build release body - include tag message, changelog, and commits
233+ let releaseBody = tagMessage;
234+
235+ if (hasChangelog && changelogEntry) {
236+ releaseBody += '\n\n## Changelog\n\n' + changelogEntry;
237+ console.log('📝 Changelog entry found (' + changelogEntry.split('\n').length + ' lines), will include in release body');
238+ } else {
239+ console.log('⚠️ No changelog entry found for version ' + tagName);
240+ }
241+
242+ if (hasCommits && commits) {
243+ releaseBody += '\n\n## Commits\n\n' + commits;
244+ console.log('📝 Commits found (' + commits.split('\n').length + ' commits), will include in release body');
245+ } else {
246+ console.log('ℹ️ No commits found between tags');
247+ }
248+
249+ try {
250+ const { data: release } = await github.rest.repos.createRelease({
251+ owner: context.repo.owner,
252+ repo: context.repo.repo,
253+ tag_name: tagName,
254+ name: 'Release ' + tagName,
255+ body: releaseBody,
256+ draft: false,
257+ prerelease: isPrerelease,
258+ make_latest: !isPrerelease, // Explicitly mark as latest if not prerelease
259+ generate_release_notes: false // Don't generate auto notes, we have our own changelog
260+ });
261+ if (hasChangelog && changelogEntry) {
262+ console.log('✅ Created release ' + tagName + ' with complete changelog' + (!isPrerelease ? ' (marked as latest)' : ''));
263+ } else {
264+ console.log('✅ Created release ' + tagName + ' (no changelog available)' + (!isPrerelease ? ' (marked as latest)' : ''));
265+ }
266+ } catch (error) {
267+ console.error('❌ Error creating release ' + tagName + ':', error.message);
268+ throw error;
269+ }
90270
91271# Maintainer: Héctor Franco Aceituno (@HecFranco)
92272# Organization: nowo-tech (https://github.com/nowo-tech)
0 commit comments