Skip to content

Commit 2c2274d

Browse files
author
dotmavriq
committed
ci: automate release packaging
1 parent 0af1706 commit 2c2274d

2 files changed

Lines changed: 175 additions & 73 deletions

File tree

.github/workflows/release.yml

Lines changed: 164 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,196 @@
1-
name: Release Obsidian Plugin
1+
name: Build & Release Obsidian Plugin
22

33
on:
44
push:
55
branches:
66
- main
7-
tags:
8-
- "*"
7+
workflow_dispatch:
98

10-
env:
11-
PLUGIN_NAME: linian
9+
permissions:
10+
contents: write
11+
12+
concurrency:
13+
group: release-${{ github.ref }}
14+
cancel-in-progress: false
1215

1316
jobs:
14-
# Deploy to release branch on every push to main
15-
deploy-release-branch:
16-
if: github.ref == 'refs/heads/main'
17+
release:
1718
runs-on: ubuntu-latest
18-
19+
env:
20+
RELEASE_NOTES_PATH: ""
21+
RELEASE_FILES: ""
22+
RELEASE_FILES_DIR: ""
23+
ZIP_PATH: ""
24+
HAS_VERSIONS: "false"
25+
RELEASE_COMMIT: ""
1926
steps:
20-
- uses: actions/checkout@v4
27+
- name: Checkout repository
28+
uses: actions/checkout@v4
2129
with:
2230
fetch-depth: 0
2331

24-
- name: Use Node.js
32+
- name: Set up Node.js
2533
uses: actions/setup-node@v4
2634
with:
2735
node-version: "18"
36+
cache: npm
2837

29-
- name: Install dependencies
30-
run: npm ci
31-
32-
- name: Build plugin
33-
run: npm run build
34-
35-
- name: Create release folder structure
38+
- name: Read plugin metadata
39+
id: meta
40+
shell: bash
3641
run: |
37-
mkdir -p release-temp/${{ env.PLUGIN_NAME }}
38-
cp main.js manifest.json styles.css release-temp/${{ env.PLUGIN_NAME }}/
39-
if [ -f "versions.json" ]; then
40-
cp versions.json release-temp/${{ env.PLUGIN_NAME }}/
42+
VERSION=$(node -p "require('./manifest.json').version")
43+
ID=$(node -p "require('./manifest.json').id")
44+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
45+
echo "id=$ID" >> "$GITHUB_OUTPUT"
46+
47+
- name: Check if tag already exists
48+
id: check_tag
49+
shell: bash
50+
run: |
51+
git fetch --tags --force
52+
if git rev-parse "refs/tags/${{ steps.meta.outputs.version }}" >/dev/null 2>&1; then
53+
echo "exists=true" >> "$GITHUB_OUTPUT"
54+
else
55+
echo "exists=false" >> "$GITHUB_OUTPUT"
4156
fi
4257
43-
- name: Deploy to release branch
44-
uses: peaceiris/actions-gh-pages@v3
45-
with:
46-
github_token: ${{ secrets.GITHUB_TOKEN }}
47-
publish_branch: release
48-
publish_dir: ./release-temp
49-
force_orphan: true
50-
commit_message: "Deploy built plugin files for ${{ github.sha }}"
51-
52-
# Create GitHub Release on tag push
53-
create-github-release:
54-
if: startsWith(github.ref, 'refs/tags/')
55-
runs-on: ubuntu-latest
56-
57-
steps:
58-
- uses: actions/checkout@v4
59-
60-
- name: Use Node.js
61-
uses: actions/setup-node@v4
62-
with:
63-
node-version: "18"
58+
- name: Skip release (tag already exists)
59+
if: steps.check_tag.outputs.exists == 'true'
60+
run: echo "Tag ${{ steps.meta.outputs.version }} already exists — skipping release pipeline."
6461

6562
- name: Install dependencies
63+
if: steps.check_tag.outputs.exists == 'false'
6664
run: npm ci
6765

68-
- name: Build plugin
66+
- name: Build plugin bundle
67+
if: steps.check_tag.outputs.exists == 'false'
6968
run: npm run build
7069

71-
- name: Create release assets
70+
- name: Assemble release folder (BRAT structure)
71+
if: steps.check_tag.outputs.exists == 'false'
72+
shell: bash
7273
run: |
73-
# Create zip with files directly in root (correct Obsidian plugin structure)
74-
zip ${{ env.PLUGIN_NAME }}-${{ github.ref_name }}.zip main.js manifest.json styles.css versions.json
74+
set -euo pipefail
75+
mkdir -p dist/${{ steps.meta.outputs.id }}
76+
cp main.js dist/${{ steps.meta.outputs.id }}/
77+
cp manifest.json dist/${{ steps.meta.outputs.id }}/
78+
cp styles.css dist/${{ steps.meta.outputs.id }}/
79+
if [ -f "versions.json" ]; then
80+
cp versions.json dist/${{ steps.meta.outputs.id }}/
81+
fi
82+
cd dist
83+
zip -r "${{ steps.meta.outputs.id }}-${{ steps.meta.outputs.version }}.zip" "${{ steps.meta.outputs.id }}"
84+
echo "ZIP_PATH=$PWD/${{ steps.meta.outputs.id }}-${{ steps.meta.outputs.version }}.zip" >> $GITHUB_ENV
85+
cd ..
86+
87+
- name: Extract release notes from changelog
88+
if: steps.check_tag.outputs.exists == 'false'
89+
env:
90+
VERSION: ${{ steps.meta.outputs.version }}
91+
shell: bash
92+
run: |
93+
set -euo pipefail
94+
node <<'EOF' > dist/release-notes.md
95+
const fs = require('fs');
96+
const version = process.env.VERSION;
97+
const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
98+
const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
99+
const pattern = new RegExp(`## \\[${escape(version)}\\][\\s\\S]*?(?=^## \\[|$)`, 'm');
100+
const match = changelog.match(pattern);
101+
if (!match) {
102+
console.error(`Could not find changelog entry for version ${version}`);
103+
process.exit(1);
104+
}
105+
process.stdout.write(match[0].trim() + '\n');
106+
EOF
107+
echo "RELEASE_NOTES_PATH=$(pwd)/dist/release-notes.md" >> $GITHUB_ENV
108+
109+
- name: Stage artifacts for downstream steps
110+
if: steps.check_tag.outputs.exists == 'false'
111+
shell: bash
112+
run: |
113+
set -euo pipefail
114+
mkdir -p "$RUNNER_TEMP/plugin-files"
115+
cp dist/${{ steps.meta.outputs.id }}/main.js "$RUNNER_TEMP/plugin-files/"
116+
cp dist/${{ steps.meta.outputs.id }}/manifest.json "$RUNNER_TEMP/plugin-files/"
117+
cp dist/${{ steps.meta.outputs.id }}/styles.css "$RUNNER_TEMP/plugin-files/"
118+
if [ -f dist/${{ steps.meta.outputs.id }}/versions.json ]; then
119+
cp dist/${{ steps.meta.outputs.id }}/versions.json "$RUNNER_TEMP/plugin-files/"
120+
echo "HAS_VERSIONS=true" >> $GITHUB_ENV
121+
else
122+
echo "HAS_VERSIONS=false" >> $GITHUB_ENV
123+
fi
124+
cp "$ZIP_PATH" "$RUNNER_TEMP/"
125+
echo "ZIP_PATH=$RUNNER_TEMP/${{ steps.meta.outputs.id }}-${{ steps.meta.outputs.version }}.zip" >> $GITHUB_ENV
126+
echo "RELEASE_FILES_DIR=$RUNNER_TEMP/plugin-files" >> $GITHUB_ENV
127+
echo "RELEASE_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
128+
129+
- name: Update release branch
130+
if: steps.check_tag.outputs.exists == 'false'
131+
shell: bash
132+
run: |
133+
set -euo pipefail
134+
git config user.name "github-actions[bot]"
135+
git config user.email "github-actions[bot]@users.noreply.github.com"
136+
ORIG_REF=$(git rev-parse --abbrev-ref HEAD)
137+
git fetch origin release || true
138+
if git show-ref --verify --quiet refs/remotes/origin/release; then
139+
git checkout -B release origin/release
140+
else
141+
git checkout -B release
142+
fi
143+
rm -rf *
144+
cp "$RELEASE_FILES_DIR"/main.js .
145+
cp "$RELEASE_FILES_DIR"/manifest.json .
146+
cp "$RELEASE_FILES_DIR"/styles.css .
147+
if [ "$HAS_VERSIONS" = "true" ]; then
148+
cp "$RELEASE_FILES_DIR"/versions.json .
149+
else
150+
rm -f versions.json || true
151+
fi
152+
git add main.js manifest.json styles.css
153+
if [ "$HAS_VERSIONS" = "true" ]; then
154+
git add versions.json
155+
fi
156+
if git diff --staged --quiet; then
157+
echo "No release branch changes to commit"
158+
else
159+
git commit -m "Deploy built plugin files for v${{ steps.meta.outputs.version }}\n\nGenerated from commit: $RELEASE_COMMIT"
160+
git push origin release --force
161+
fi
162+
git checkout "$ORIG_REF"
163+
git reset --hard "$RELEASE_COMMIT"
75164
76-
- name: Create Release
165+
- name: Prepare asset list
166+
if: steps.check_tag.outputs.exists == 'false'
167+
shell: bash
168+
run: |
169+
set -euo pipefail
170+
FILE_LIST="$ZIP_PATH"$'\n'$RELEASE_FILES_DIR/main.js$'\n'$RELEASE_FILES_DIR/manifest.json$'\n'$RELEASE_FILES_DIR/styles.css
171+
if [ "$HAS_VERSIONS" = "true" ]; then
172+
FILE_LIST+=$'\n'$RELEASE_FILES_DIR/versions.json
173+
fi
174+
{
175+
echo "RELEASE_FILES<<'EOF'"
176+
echo "$FILE_LIST"
177+
echo EOF
178+
} >> $GITHUB_ENV
179+
180+
- name: Create GitHub release
181+
if: steps.check_tag.outputs.exists == 'false'
77182
uses: softprops/action-gh-release@v1
78183
with:
79-
tag_name: ${{ github.ref_name }}
80-
name: "Linian v${{ github.ref_name }}"
81-
body: |
82-
## Linian Obsidian Plugin v${{ github.ref_name }}
83-
84-
### Installation
85-
86-
**Option 1: Download Plugin Folder (Recommended)**
87-
1. Download `${{ env.PLUGIN_NAME }}-${{ github.ref_name }}.zip`
88-
2. Extract the zip file
89-
3. Copy the `${{ env.PLUGIN_NAME }}` folder to your vault's `.obsidian/plugins/` directory
90-
4. Enable the plugin in Obsidian Settings → Community Plugins
91-
92-
**Option 2: Manual Installation**
93-
1. Download `main.js`, `manifest.json`, and `styles.css`
94-
2. Create a folder named `${{ env.PLUGIN_NAME }}` in your vault's `.obsidian/plugins/` directory
95-
3. Place the downloaded files in that folder
96-
4. Enable the plugin in Obsidian Settings → Community Plugins
97-
184+
tag_name: ${{ steps.meta.outputs.version }}
185+
name: Linian v${{ steps.meta.outputs.version }}
186+
body_path: ${{ env.RELEASE_NOTES_PATH }}
187+
files: ${{ env.RELEASE_FILES }}
98188
draft: false
99189
prerelease: false
100-
files: |
101-
${{ env.PLUGIN_NAME }}-${{ github.ref_name }}.zip
102-
main.js
103-
manifest.json
104-
styles.css
105-
versions.json
190+
191+
- name: Upload release bundle artifact
192+
if: steps.check_tag.outputs.exists == 'false'
193+
uses: actions/upload-artifact@v4
194+
with:
195+
name: linian-${{ steps.meta.outputs.version }}
196+
path: ${{ env.RELEASE_FILES }}

DEPLOYMENT.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,17 @@ When ready to publish:
178178
4. Package files for distribution
179179
5. Submit to Obsidian Community Plugins (if desired)
180180

181+
### Automated Release Pipeline
182+
183+
- Pushing a version bump to the `main` branch automatically runs the **Build & Release Obsidian Plugin** workflow.
184+
- The workflow:
185+
1. Builds the plugin from source
186+
2. Packages a BRAT-compliant zip (`linian-{version}.zip`) containing a `linian/` folder with all runtime files
187+
3. Updates the `release` branch with the fresh runtime files
188+
4. Creates a GitHub Release tagged with the version from `manifest.json`
189+
5. Attaches both the zip archive and the individual files to the release
190+
- To trigger a new release, bump the version in `manifest.json` (and `versions.json` when needed) and push to `main`; the workflow skips automatically if a release tag already exists for that version.
191+
181192
## Support
182193

183194
If you encounter issues:

0 commit comments

Comments
 (0)