Skip to content

Commit 501a71f

Browse files
authored
Merge pull request #62 from devforth/next
Next
2 parents 99f958a + 91396a7 commit 501a71f

File tree

17 files changed

+8824
-1325
lines changed

17 files changed

+8824
-1325
lines changed

adminforth/build.log

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
> rm -rf dist && tsc && npm run prepareDist && npm link
4+
5+
6+
> [email protected] prepareDist
7+
> cp -rL spa dist/
8+
9+
10+
up to date, audited 3 packages in 7s
11+
12+
found 0 vulnerabilities
251 KB
Loading
41.2 KB
Loading
245 KB
Loading
165 KB
Loading

adminforth/documentation/blog/2025-01-09-how-adminforth-manages-version/index.md

Lines changed: 341 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,371 @@ In this post I will share our journey of how we did a transition from manual Cha
1111

1212
## Prehistory
1313

14-
Before 1.6.0 AdminForth was using manual ChangeLog. We were reviwing PRs, merged them all to `main` branch and there did manually npm release. We were constantly releasing to next pre-release version and used it internally on our projets for testing. Once we collected enough features and fixes, we were doing a release to `latest` version.
14+
Before 1.6.0 AdminForth was using manual ChangeLog. We were reviwing PRs, merged them all to `main` branch and there did manually npm release. We were constantly releasing to `next` pre-release version from `main` and used `next` it internally on our projets for testing. Once we collected enough features and fixes, we were doing a release to `latest` version, and at this time we did release documentation.
15+
ChangeLog included only main "latest" chain of versions, so while we releasing to `next` we added items under one version in ChangeLog. So user was not able to distinguish `1.5.0-next.0` from `1.5.0-next.1` in ChangeLog.
16+
17+
Anyway this was a pretty good approach, but it was hard to maintain ChangeLog: first you have to write commit message, then write same message in ChangeLog. And from time to time we were missing updating ChangeLog at all.
18+
19+
Also since release was manual from my PC there was a chance that I will forget to push tags to GitHub and release already happened. And chance that I will forget to push ChangeLog to GitHub.
20+
21+
Git tags were applied only after release and there again was a chance that I will forget to push them to GitHub before release.
22+
So only one reliabile way to check what is difference in source code between versions was to use https://npmdiff.dev/ and not rely on git and ChangeLog.
23+
24+
Another thing is documentation. If I added something to documentation and decided to release it, there was a chance that I will release items for `next` version which are not yet in `latest` version. But this was easily solvable by maintaining `next` and `latest` branches instead of doing `next` development in `main` branch. In the end we switched to this approach.
25+
26+
Another thing - structure of repository. Historically for faster development in dev demo, we added all plugins into one repository. This was good for development(and very bad when count of plugins increased), and very bad for release process. Every plugin had its own version and its own ChangeLog. So we had to enter every plugin folder, do release, and push ChangeLog to GitHub. This was very time consuming and error-prone. It started to be obvious that we need to split plugins to separate repositories what was done now.
1527

1628
## Transition
1729

18-
I will show a flow on empty fake repository
30+
I will show a flow on empty fake small project to not overcomplicate things.
31+
We will use minimal typescript package with `npm` and `semantic-release` to show how it works.
32+
33+
```
34+
echo "# test-sem-release" >> README.md
35+
git init
36+
git add README.md
37+
git commit -m "first commit"
38+
git branch -M main
39+
git remote add origin [email protected]:devforth/test-sem-release.git
40+
git push -u origin main
41+
```
42+
43+
Now lets init new `npm package`:
1944

2045
```
21-
git clone [email protected]:devforth/test-sem-release.git
2246
npm init -y
47+
npm install typescript --save-dev
48+
npx tsc --init
49+
```
50+
51+
Create a file `index.ts`:
52+
53+
```
54+
export const greet = (name: string): string => {
55+
return `Hello, ${name}!`;
56+
};
2357
```
2458

59+
2560
In `package.json` add:
2661

2762
```
2863
{
29-
"name": "test-sem-release",
64+
"name": "@devforth/test-sem-release",
65+
//diff-add
66+
"publishConfig": {
67+
//diff-add
68+
"access": "public"
69+
//diff-add
70+
},
3071
"version": "1.0.0",
3172
"main": "index.js",
3273
"scripts": {
3374
"test": "echo \"Error: no test specified\" && exit 1"
75+
//diff-add
76+
"build": "tsc",
3477
},
3578
"author": "",
3679
"license": "ISC",
3780
"description": "",
3881
//diff-add
3982
"release": {
4083
//diff-add
41-
"branches": ["master", "next"]
84+
"branches": [main", "next"],
85+
//diff-add
86+
"plugins": [
87+
//diff-add
88+
"@semantic-release/commit-analyzer",
89+
//diff-add
90+
"@semantic-release/release-notes-generator",
91+
//diff-add
92+
"@semantic-release/npm",
93+
//diff-add
94+
"@semantic-release/github"
95+
//diff-add
96+
],
4297
//diff-add
4398
}
4499
}
100+
```
101+
102+
Make sure name in package.json has your organisation name like mine `@devforth/` and you have access to publish packages to npmjs.com.
103+
104+
105+
Also install `semantic-release`:
106+
107+
```
108+
npm i -D semantic-release
109+
```
110+
111+
112+
## Connecting to CI
113+
114+
We will use Woodpecker CI for this example. Woodpecker is a free and open-source CI/CD tool that you can install to your own server / VPS and will not need to pay only for server. No limits on pipelines, users, repositories, etc. If you want to try it, we have [Woodpecker installation guide](https://devforth.io/blog/step-by-step-guide-to-modern-secure-ci-setup/)
115+
116+
Create a file `.woodpecker.yml` in `deploy` directory:
117+
118+
```yaml title="deploy/.woodpecker.yml"
119+
clone:
120+
git:
121+
image: woodpeckerci/plugin-git
122+
settings:
123+
partial: false
124+
depth: 5
125+
126+
steps:
127+
release:
128+
image: node:22
129+
when:
130+
- event: push
131+
volumes:
132+
- /var/run/docker.sock:/var/run/docker.sock
133+
commands:
134+
- npm clean-install
135+
- npm run build
136+
- npm audit signatures
137+
- npx semantic-release
138+
secrets:
139+
- GITHUB_TOKEN
140+
- NPM_TOKEN
141+
```
142+
143+
Go to Woodpecker, authorize via GitHub, click "Add repository", find your repository and add it.
144+
145+
Disable "Project settings" -> "Allow Pull Requests" because we do not want to trigger builds on PRs.
146+
Enable "Project settings" -> "Trusted"
147+
Enable "Project Visibility" -> "Public"
148+
149+
![Woodpecker project settings](image-3.png)
150+
151+
152+
### Generating GitHub acces token
153+
154+
if your repo is in GitHub organisation, you need first enable access to personal access tokens for your organisation (if not yet done)
155+
156+
1. In the upper-right corner of GitHub, select your profile photo, then click Your organizations.
157+
2. Next to the organization, click Settings.
158+
3. In the left sidebar, under Personal access tokens, click Settings.
159+
4. Select "Allow access via fine-grained personal access tokens"
160+
5. We recommend setting "Require administrator approval"
161+
6. "Allow access via personal access tokens (classic)"
162+
163+
Now go to your profile, click on "Settings" -> "Developer settings" -> "Personal access tokens" -> "Generate new token"
164+
165+
For permissions, select Contents: Read and Write
166+
select Metadata: Read-only
167+
168+
169+
In Woodpecker Go to settings, Secrets, Add Secret `GITHUB_TOKEN` and paste your token:
170+
171+
![Woodpecker Secrets](image.png)
172+
173+
In "Available at following events" select "Push, also you can select "Manual" if you want to trigger builds manually.
174+
175+
176+
### Generating NPM token
177+
178+
Go to your npmjs.com account, click on "Profile Avatar" -> "Access Tokens" -> "Generate New Token" -> "New Granular Access Token".
179+
180+
Packages and scopes Permissions: Read and Write.
181+
182+
Add to Woodpecker as secret `NPM_TOKEN`
183+
184+
185+
## Testing
186+
187+
For now we did not yet push anything to GitHub and did not publish anything to npm.
188+
189+
Lets do it now.
190+
191+
192+
Just push your first commit as:
193+
194+
195+
```
196+
feat: initial commit
197+
```
198+
199+
This will trigger semantic-release to do first release v1.0.0.
200+
201+
Now change something is index.ts and push it as fix
202+
203+
```
204+
fix: fix greet function
205+
```
206+
207+
This will trigger semantic-release to do release v1.0.1.
208+
209+
210+
Now change something in index.ts and push it as feat
211+
212+
```
213+
feat: add new function
214+
```
215+
216+
This will trigger semantic-release to do release v1.1.0 because we added new feature, not just fixed something.
217+
218+
219+
### Next distribution channel
220+
221+
Now we will show how to release to `next` channel.
222+
223+
```
224+
git checkout -b next
225+
```
226+
227+
Change something and push it as fix
228+
229+
```
230+
fix: fix greet function in next
231+
```
232+
233+
Commit it and push:
234+
235+
```
236+
git push --set-upstream origin next
237+
```
238+
239+
This will trigger semantic-release to do release `v1.1.1-next.1`. Please not that it bumped patch version because we are in `next` channel.
240+
241+
Now lets add feature to next
242+
243+
```
244+
feat: add new feature in next
245+
```
246+
247+
It will trigger release `v1.2.0-next.1` because we added new feature and minor version was bumped. Please not that next number started from 1 again.
248+
249+
Noe lets merge `next` to `main` and push it:
250+
251+
```
252+
git checkout main
253+
git merge next
254+
git push
255+
```
256+
257+
This will trigger release `v1.2.0` because we merged `next` to `main` and it was a feature release.
258+
259+
260+
## Slack notifications about releases
261+
262+
So now we have automatic releases with release notes on GitHub.
263+
For our internal team we use Slack and we want to get notifications about releases there.
264+
265+
```
266+
npm i -D semantic-release-slack-bot
267+
```
268+
269+
Into "release" section of `package.json` add slack plugin:
270+
271+
```
272+
"plugins": [
273+
"@semantic-release/commit-analyzer",
274+
"@semantic-release/release-notes-generator",
275+
"@semantic-release/npm",
276+
"@semantic-release/github",
277+
//diff-add
278+
[
279+
//diff-add
280+
"semantic-release-slack-bot",
281+
//diff-add
282+
{
283+
//diff-add
284+
"notifyOnSuccess": true,
285+
//diff-add
286+
"notifyOnFail": true,
287+
//diff-add
288+
"slackIcon": ":package:",
289+
//diff-add
290+
"onSuccessTemplate": {
291+
//diff-add
292+
"text": "$npm_package_version has been released!"
293+
//diff-add
294+
},
295+
//diff-add
296+
"markdownReleaseNotes": true
297+
//diff-add
298+
}
299+
//diff-add
300+
]
301+
//diff-add
302+
],
303+
```
304+
305+
306+
Also create channel in Slack, click on channel name, "Integrations" -> "Add an App" -> "Incoming Webhooks" -> "Add to Slack" -> "Add Incoming Webhook to Workspace" -> "Add to Slack" -> "Copy Webhook URL"
307+
308+
Add it to Woodpecker as secret `SLACK_WEBHOOK` environment variable.
309+
310+
Also add this secterd to `.woodpecker.yml`:
311+
312+
```yaml title="deploy/.woodpecker.yml"
313+
secrets:
314+
- GITHUB_TOKEN
315+
- NPM_TOKEN
316+
//diff-add
317+
- SLACK_WEBHOOK
318+
```
319+
320+
321+
322+
This will send notifications to Slack channel about succesfull releases when `npm run build` is done without errors.
323+
324+
## Slack notifications about build errors and unhappen releases
325+
326+
327+
Create a file `failToSlack.sh` in `deploy` directory:
328+
329+
```sh title="deploy/failToSlack.sh"
330+
#!/bin/sh
331+
332+
export BUILD_LOG=$(cat ../../adminforth/build.log)
333+
334+
COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8)
335+
336+
MESSAGE="Broke \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\` with commit (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)."
337+
CODE_BLOCK="\`\`\`$BUILD_LOG\n\`\`\`"
338+
339+
curl -s -X POST -H "Content-Type: application/json" -d '{
340+
"username": "'"$CI_COMMIT_AUTHOR"'",
341+
"icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
342+
"attachments": [
343+
{
344+
"mrkdwn_in": ["text", "pretext"],
345+
"color": "#8A1C12",
346+
"text": "'"$CODE_BLOCK"'",
347+
"pretext": "'"$MESSAGE"'"
348+
}
349+
]
350+
}' "$DEVELOPERS_SLACK_WEBHOOK"
351+
```
352+
353+
354+
Add `step` to `.woodpecker.yml`:
355+
356+
```yaml title="deploy/.woodpecker.yml"
357+
steps:
358+
release:
359+
...
360+
//diff-add
361+
slack-on-failure:
362+
//diff-add
363+
image: curlimages/curl
364+
//diff-add
365+
when:
366+
//diff-add
367+
- status: failure
368+
//diff-add
369+
event: push
370+
//diff-add
371+
- event: push
372+
//diff-add
373+
commands:
374+
//diff-add
375+
- cd deploy && /bin/sh failToSlack.sh
376+
//diff-add
377+
secrets:
378+
//diff-add
379+
- DEVELOPERS_SLACK_WEBHOOK
380+
45381
```

0 commit comments

Comments
 (0)