Skip to content

Commit 511498d

Browse files
committed
feat: implement build and publish workflow for VS Code and Open VSX with enhanced version control and manual trigger options
1 parent 13f20c5 commit 511498d

1 file changed

Lines changed: 392 additions & 0 deletions

File tree

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
name: Build and Publish Extension
2+
on:
3+
push:
4+
branches:
5+
- main
6+
paths-ignore:
7+
- '.github/workflows/**'
8+
workflow_dispatch:
9+
inputs:
10+
publish_vscode:
11+
description: 'Publish to VS Code Marketplace'
12+
required: false
13+
default: true
14+
type: boolean
15+
publish_openvsx:
16+
description: 'Publish to Open VSX Registry'
17+
required: false
18+
default: true
19+
type: boolean
20+
force_publish:
21+
description: 'Force publish (ignore version change check)'
22+
required: false
23+
default: false
24+
type: boolean
25+
26+
jobs:
27+
# 1️⃣ BUILD ONCE - Single source of truth
28+
build:
29+
runs-on: ubuntu-latest
30+
outputs:
31+
version: ${{ steps.package.outputs.version }}
32+
should_publish: ${{ steps.verify.outputs.should_publish }}
33+
vsix_name: ${{ steps.build.outputs.vsix_name }}
34+
35+
steps:
36+
- name: Checkout code
37+
uses: actions/checkout@v4
38+
with:
39+
fetch-depth: 0
40+
41+
- name: Setup Node.js
42+
uses: actions/setup-node@v4
43+
with:
44+
node-version: '20'
45+
46+
- name: Install dependencies
47+
run: npm ci
48+
49+
- name: Compile TypeScript
50+
run: npm run compile
51+
52+
- name: Lint code
53+
run: npm run lint
54+
55+
- name: Verify changes
56+
id: verify
57+
run: |
58+
# Handle different trigger scenarios
59+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
60+
# For manual triggers, compare with previous commit
61+
echo "files=$(git diff --name-only HEAD~1 HEAD | tr '\n' ' ')" >> $GITHUB_OUTPUT
62+
VERSION_CHANGED=$(git diff HEAD~1 HEAD package.json | grep -E '^\+\s*"version"' || echo "")
63+
elif [ -z "${{ github.event.before }}" ] || [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
64+
# For first commit or when before is null, get all files in the commit
65+
echo "files=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git ls-files | tr '\n' ' ')" >> $GITHUB_OUTPUT
66+
VERSION_CHANGED="version_changed_first_commit"
67+
else
68+
# Normal push event
69+
echo "files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | tr '\n' ' ')" >> $GITHUB_OUTPUT
70+
VERSION_CHANGED=$(git diff ${{ github.event.before }} ${{ github.event.after }} package.json | grep -E '^\+\s*"version"' || echo "")
71+
fi
72+
73+
# Get list of changed files
74+
CHANGED_FILES=$(echo "${{ github.event_name }}" | grep -q "workflow_dispatch" && git diff --name-only HEAD~1 HEAD | tr '\n' ' ' || git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | tr '\n' ' ')
75+
76+
# Debug output
77+
echo "🔍 Event: ${{ github.event_name }}"
78+
echo "📁 Changed files: $CHANGED_FILES"
79+
echo "📦 Version changed: ${VERSION_CHANGED:-'no'}"
80+
81+
# Check if only documentation files were changed
82+
DOCS_ONLY=true
83+
for file in $CHANGED_FILES; do
84+
if [[ ! $file =~ \.(md|txt|doc|docx)$ ]] && [[ ! $file =~ ^docs/ ]]; then
85+
DOCS_ONLY=false
86+
break
87+
fi
88+
done
89+
90+
# Initialize should_publish as false
91+
echo "should_publish=false" >> $GITHUB_OUTPUT
92+
93+
# Decision logic
94+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
95+
if [ "${{ github.event.inputs.force_publish }}" = "true" ]; then
96+
echo "🚀 Manual workflow dispatch with force publish - proceeding with publish"
97+
echo "should_publish=true" >> $GITHUB_OUTPUT
98+
elif [[ -n "$VERSION_CHANGED" ]]; then
99+
echo "🚀 Manual workflow dispatch with version change - proceeding with publish"
100+
echo "should_publish=true" >> $GITHUB_OUTPUT
101+
else
102+
echo "⚠️ Manual workflow dispatch without version change - use force_publish to override"
103+
echo "should_publish=false" >> $GITHUB_OUTPUT
104+
fi
105+
elif [[ "$DOCS_ONLY" == "true" ]]; then
106+
echo "ℹ️ Only documentation files were changed - skipping publish"
107+
echo "should_publish=false" >> $GITHUB_OUTPUT
108+
elif [[ -z "$VERSION_CHANGED" ]]; then
109+
echo "❌ Version in package.json was not updated - skipping publish"
110+
echo "should_publish=false" >> $GITHUB_OUTPUT
111+
else
112+
echo "✅ Version changed and non-documentation files modified - proceeding with publish"
113+
echo "should_publish=true" >> $GITHUB_OUTPUT
114+
fi
115+
116+
- name: Get package info
117+
id: package
118+
run: |
119+
VERSION=$(node -p "require('./package.json').version")
120+
echo "version=$VERSION" >> $GITHUB_OUTPUT
121+
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
122+
echo "📦 Package version: $VERSION"
123+
124+
- name: Build VSIX package
125+
id: build
126+
if: steps.verify.outputs.should_publish == 'true'
127+
run: |
128+
npm install -g @vscode/vsce@latest
129+
rm -f *.vsix
130+
vsce package
131+
132+
VSIX_NAME="simple-coding-time-tracker-${{ steps.package.outputs.version }}.vsix"
133+
echo "vsix_name=$VSIX_NAME" >> $GITHUB_OUTPUT
134+
135+
# Validate VSIX
136+
if [ ! -f "$VSIX_NAME" ]; then
137+
echo "❌ VSIX build failed"
138+
exit 1
139+
fi
140+
141+
# Verify the package can be listed
142+
vsce ls "$VSIX_NAME"
143+
144+
SIZE=$(stat -c%s "$VSIX_NAME" 2>/dev/null || stat -f%z "$VSIX_NAME")
145+
SIZE_MB=$((SIZE / 1024 / 1024))
146+
147+
if [ $SIZE_MB -gt 10 ]; then
148+
echo "⚠️ Warning: Package size is ${SIZE_MB}MB (consider optimizing)"
149+
else
150+
echo "✅ Package size: ${SIZE_MB}MB"
151+
fi
152+
153+
echo "✅ VSIX built successfully - $VSIX_NAME"
154+
155+
- name: Upload VSIX artifact
156+
if: steps.verify.outputs.should_publish == 'true'
157+
uses: actions/upload-artifact@v4
158+
with:
159+
name: extension-vsix-${{ steps.package.outputs.version }}
160+
path: ${{ steps.build.outputs.vsix_name }}
161+
retention-days: 90
162+
163+
# 2️⃣ DEPLOY TO VS CODE MARKETPLACE
164+
publish-vscode:
165+
needs: build
166+
if: |
167+
needs.build.outputs.should_publish == 'true' && (
168+
github.event_name != 'workflow_dispatch' ||
169+
github.event.inputs.publish_vscode == 'true'
170+
)
171+
runs-on: ubuntu-latest
172+
environment: VSC EXT
173+
permissions:
174+
contents: write
175+
actions: read
176+
177+
steps:
178+
- name: Log VS Code Marketplace Publishing
179+
run: |
180+
echo "🏪 Publishing to VS Code Marketplace"
181+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
182+
echo "📋 Manual trigger - VS Code Marketplace: ${{ github.event.inputs.publish_vscode }}"
183+
else
184+
echo "📋 Automatic trigger - VS Code Marketplace: enabled"
185+
fi
186+
- name: Checkout code
187+
uses: actions/checkout@v4
188+
189+
- name: Download VSIX artifact
190+
uses: actions/download-artifact@v4
191+
with:
192+
name: extension-vsix-${{ needs.build.outputs.version }}
193+
194+
- name: Install vsce CLI
195+
run: npm install -g @vscode/vsce@latest
196+
197+
- name: Check if tag exists
198+
id: check_tag
199+
run: |
200+
if git rev-parse "v${{ needs.build.outputs.version }}" >/dev/null 2>&1; then
201+
echo "Tag v${{ needs.build.outputs.version }} already exists"
202+
echo "tag_exists=true" >> $GITHUB_OUTPUT
203+
else
204+
echo "tag_exists=false" >> $GITHUB_OUTPUT
205+
fi
206+
207+
- name: Publish to VS Code Marketplace
208+
id: marketplace_publish
209+
uses: nick-fields/retry@v3
210+
with:
211+
timeout_minutes: 5
212+
max_attempts: 3
213+
retry_wait_seconds: 30
214+
command: vsce publish -p $VSC_PAT --packagePath ${{ needs.build.outputs.vsix_name }}
215+
env:
216+
VSC_PAT: ${{ secrets.VSC_PAT }}
217+
218+
- name: Generate Release Notes
219+
id: release_notes
220+
run: |
221+
# Get the previous tag
222+
PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
223+
224+
if [ -n "$PREV_TAG" ]; then
225+
# Generate changelog from commits since last tag
226+
CHANGELOG=$(git log ${PREV_TAG}..HEAD --pretty=format:"- %s" --no-merges)
227+
228+
# Create release body with actual changes
229+
cat << EOF > release_body.md
230+
## 🚀 What's New in v${{ needs.build.outputs.version }}
231+
232+
${CHANGELOG}
233+
234+
## 📦 Installation
235+
- **VS Code Marketplace**: [Install from VS Code](https://marketplace.visualstudio.com/items?itemName=noorashuvo.simple-coding-time-tracker)
236+
- **Open VSX Registry**: [Install for VS Codium](https://open-vsx.org/extension/noorashuvo/simple-coding-time-tracker)
237+
238+
## 🔗 Links
239+
- [📋 Changelog](https://github.com/twentyTwo/vsc-ext-coding-time-tracker/blob/main/README.md#changelog)
240+
- [📚 Documentation Wiki](https://github.com/twentyTwo/vsc-ext-coding-time-tracker/wiki)
241+
- [🐛 Report Issues](https://github.com/twentyTwo/vsc-ext-coding-time-tracker/issues)
242+
243+
---
244+
245+
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...v${{ needs.build.outputs.version }}
246+
EOF
247+
else
248+
cat << EOF > release_body.md
249+
## 🚀 What's New in v${{ needs.build.outputs.version }}
250+
251+
Initial release of Simple Coding Time Tracker
252+
253+
## 📦 Installation
254+
- **VS Code Marketplace**: [Install from VS Code](https://marketplace.visualstudio.com/items?itemName=noorashuvo.simple-coding-time-tracker)
255+
- **Open VSX Registry**: [Install for VS Codium](https://open-vsx.org/extension/noorashuvo/simple-coding-time-tracker)
256+
257+
## 🔗 Links
258+
- [📚 Documentation Wiki](https://github.com/twentyTwo/vsc-ext-coding-time-tracker/wiki)
259+
- [🐛 Report Issues](https://github.com/twentyTwo/vsc-ext-coding-time-tracker/issues)
260+
EOF
261+
fi
262+
263+
- name: Create GitHub Release
264+
id: create_release
265+
if: steps.check_tag.outputs.tag_exists == 'false' && steps.marketplace_publish.outcome == 'success'
266+
uses: softprops/action-gh-release@v2
267+
with:
268+
tag_name: v${{ needs.build.outputs.version }}
269+
name: Release v${{ needs.build.outputs.version }}
270+
body_path: release_body.md
271+
files: ${{ needs.build.outputs.vsix_name }}
272+
draft: false
273+
prerelease: false
274+
generate_release_notes: false
275+
env:
276+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
277+
278+
# 3️⃣ DEPLOY TO OPEN VSX (Parallel)
279+
publish-openvsx:
280+
needs: build
281+
if: |
282+
needs.build.outputs.should_publish == 'true' && (
283+
github.event_name != 'workflow_dispatch' ||
284+
github.event.inputs.publish_openvsx == 'true'
285+
)
286+
runs-on: ubuntu-latest
287+
environment: Open VSX
288+
permissions:
289+
contents: read
290+
actions: read
291+
292+
steps:
293+
- name: Log Open VSX Publishing
294+
run: |
295+
echo "🌐 Publishing to Open VSX Registry"
296+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
297+
echo "📋 Manual trigger - Open VSX: ${{ github.event.inputs.publish_openvsx }}"
298+
else
299+
echo "📋 Automatic trigger - Open VSX: enabled"
300+
fi
301+
- name: Download VSIX artifact
302+
uses: actions/download-artifact@v4
303+
with:
304+
name: extension-vsix-${{ needs.build.outputs.version }}
305+
306+
- name: Install ovsx CLI
307+
run: npm install -g ovsx@latest
308+
309+
- name: Publish to Open VSX Registry
310+
id: openvsx_publish
311+
uses: nick-fields/retry@v3
312+
with:
313+
timeout_minutes: 5
314+
max_attempts: 3
315+
retry_wait_seconds: 30
316+
command: ovsx publish ${{ needs.build.outputs.vsix_name }} --pat $OPENVSX_PAT
317+
env:
318+
OPENVSX_PAT: ${{ secrets.OPENVSX_PAT }}
319+
320+
- name: Verify Publication
321+
if: steps.openvsx_publish.outcome == 'success'
322+
run: |
323+
echo "🎉 Successfully published to Open VSX Registry!"
324+
echo "📦 Extension: simple-coding-time-tracker v${{ needs.build.outputs.version }}"
325+
echo "🌐 Open VSX: https://open-vsx.org/extension/noorashuvo/simple-coding-time-tracker"
326+
327+
# 4️⃣ REPORT FINAL STATUS
328+
report-status:
329+
needs: [build, publish-vscode, publish-openvsx]
330+
if: always() && needs.build.outputs.should_publish == 'true'
331+
runs-on: ubuntu-latest
332+
333+
steps:
334+
- name: Report Final Status
335+
run: |
336+
echo "## 📊 Publication Status Report"
337+
echo "**Version**: v${{ needs.build.outputs.version }}"
338+
339+
# Show what was requested
340+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
341+
echo "**Trigger**: Manual (workflow_dispatch)"
342+
echo "**VS Code Marketplace**: ${{ github.event.inputs.publish_vscode }}"
343+
echo "**Open VSX Registry**: ${{ github.event.inputs.publish_openvsx }}"
344+
else
345+
echo "**Trigger**: Automatic (push to main)"
346+
echo "**VS Code Marketplace**: enabled"
347+
echo "**Open VSX Registry**: enabled"
348+
fi
349+
echo ""
350+
351+
# VS Code Marketplace status
352+
if [ "${{ needs.publish-vscode.result }}" = "success" ]; then
353+
echo "✅ **VS Code Marketplace**: Successfully published"
354+
elif [ "${{ needs.publish-vscode.result }}" = "skipped" ]; then
355+
echo "⏭️ **VS Code Marketplace**: Skipped (disabled in manual trigger)"
356+
else
357+
echo "❌ **VS Code Marketplace**: Failed to publish"
358+
fi
359+
360+
# Open VSX status
361+
if [ "${{ needs.publish-openvsx.result }}" = "success" ]; then
362+
echo "✅ **Open VSX Registry**: Successfully published"
363+
elif [ "${{ needs.publish-openvsx.result }}" = "skipped" ]; then
364+
echo "⏭️ **Open VSX Registry**: Skipped (disabled in manual trigger)"
365+
else
366+
echo "❌ **Open VSX Registry**: Failed to publish"
367+
fi
368+
369+
# Overall status
370+
VSCODE_SUCCESS=${{ needs.publish-vscode.result == 'success' }}
371+
VSCODE_SKIPPED=${{ needs.publish-vscode.result == 'skipped' }}
372+
OPENVSX_SUCCESS=${{ needs.publish-openvsx.result == 'success' }}
373+
OPENVSX_SKIPPED=${{ needs.publish-openvsx.result == 'skipped' }}
374+
375+
if [ "$VSCODE_SUCCESS" = "true" ] && [ "$OPENVSX_SUCCESS" = "true" ]; then
376+
echo ""
377+
echo "🎉 **Overall Status**: All requested publications successful!"
378+
echo "::notice title=Publication Complete::Version v${{ needs.build.outputs.version }} published to both marketplaces"
379+
elif [ "$VSCODE_SUCCESS" = "true" ] || [ "$OPENVSX_SUCCESS" = "true" ]; then
380+
echo ""
381+
echo "✅ **Overall Status**: Partial success - some publications completed"
382+
echo "::notice title=Partial Publication::Version v${{ needs.build.outputs.version }} published to some marketplaces"
383+
elif [ "$VSCODE_SKIPPED" = "true" ] && [ "$OPENVSX_SKIPPED" = "true" ]; then
384+
echo ""
385+
echo "⏭️ **Overall Status**: All publications skipped (both disabled)"
386+
echo "::warning title=No Publications::All marketplaces were disabled in manual trigger"
387+
else
388+
echo ""
389+
echo "❌ **Overall Status**: Publication failures occurred"
390+
echo "::error title=Publication Failed::Some publications failed - check logs above"
391+
exit 1
392+
fi

0 commit comments

Comments
 (0)