Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ecbaf54
feat(release): sementic release
Ayush8923 Jun 12, 2026
88d0024
fix(template): update the pull request template
Ayush8923 Jun 13, 2026
25eecd7
fix(template): update the pull request template
Ayush8923 Jun 13, 2026
6fd2c75
fix(*): testing
Ayush8923 Jun 14, 2026
9490e20
Release: Semantic Release Testing (#208)
Ayush8923 Jun 14, 2026
b646680
Release: Pre-Release Testing (#210)
Ayush8923 Jun 14, 2026
ee842ad
fix(*): few updates for testing
Ayush8923 Jun 14, 2026
599d5e4
Merge branch 'feat/semantic-release' of https://github.com/ProjectTec…
Ayush8923 Jun 14, 2026
5462ccb
Release: Pre-Release Testing (#211)
Ayush8923 Jun 14, 2026
f5dfe29
fix(*): update the branch name
Ayush8923 Jun 14, 2026
3972c6b
Release: Pre-Release Testing (#212)
Ayush8923 Jun 14, 2026
4d18168
feat: test semantic release
Ayush8923 Jun 14, 2026
6502257
fix(*): added branch name for testing
Ayush8923 Jun 14, 2026
b46d077
feat: test semantic release
Ayush8923 Jun 14, 2026
393c372
fix(*): added branch name for testing
Ayush8923 Jun 14, 2026
0523eeb
feat: test semantic release
Ayush8923 Jun 14, 2026
d572b08
feat: test semantic release
Ayush8923 Jun 14, 2026
4df18be
feat: semantic release test
Ayush8923 Jun 14, 2026
c6a6405
feat: semantic release test
Ayush8923 Jun 14, 2026
d66cda1
feat: semantic release test
Ayush8923 Jun 14, 2026
15c5f08
feat: semantic release test
Ayush8923 Jun 14, 2026
f9f03a6
fix(*): pre-release setup correctly
Ayush8923 Jun 14, 2026
91d8db2
fix(release): remove the unwanted if condition
Ayush8923 Jun 14, 2026
f048b03
fix(*): update the pull request template
Ayush8923 Jun 14, 2026
d52e0d4
fix(release): added the concurrency
Ayush8923 Jun 15, 2026
e427032
Merge branch 'main' into feat/semantic-release
Ayush8923 Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Issue

Closes #PLEASE_TYPE_ISSUE_NUMBER

## Type

type: <-- Add the type of the PR -->

<!-- Options: feat | fix | chore | docs | refactor -->

## Summary

Explain the **motivation** for making this change. What existing problem does the pull request solve?

## Checklist

Before submitting a pull request, please ensure that you mark these task.

- [ ] Ran `npm run dev` and `npm run build` in the repository root and test.
- [ ] If you've fixed a bug or added code that is tested

## Notes

Please add here if any other information is required for the reviewer.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Deploy Kaapi to EC2 Production
on:
push:
tags:
- "v*" # Deploy only when tags like v1.0.0, v2.1.0, etc., are created
- "v[0-9]+.[0-9]+.[0-9]+" # Deploy only when tags like v1.0.0, v2.1.0, etc., are created

jobs:
deploy:
Expand Down
File renamed without changes.
249 changes: 249 additions & 0 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
name: Pre-release Tag

on:
pull_request:
types: [closed]
branches: [main]

concurrency:
group: pre-release
cancel-in-progress: false

jobs:
pre-release:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
name: Pre-Release
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref }}
fetch-depth: 0

- name: Get PR type from description
id: pr-type
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const body = context.payload.pull_request.body || '';
const typeMatch = body.match(/type:\s*(feat|fix|chore|docs|refactor)/i);

if (!typeMatch) {
core.setOutput('type', 'fix');
return;
}

const type = typeMatch[1].toLowerCase();
core.setOutput('type', type);

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install semantic-release
run: |
npm install -g \
semantic-release \
@semantic-release/commit-analyzer \
@semantic-release/release-notes-generator \
@semantic-release/github

- name: Create conventional commit
env:
COMMIT_TYPE: ${{ steps.pr-type.outputs.type }}
run: |
git config user.email "ci@github.com"
git config user.name "GitHub CI"
ORIGINAL_MSG=$(git log -1 --pretty=%s)
git commit --allow-empty -m "${COMMIT_TYPE}: ${ORIGINAL_MSG}"

- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release

- name: Rewrite notes (cumulative, grouped, since last stable)
if: success()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
const cmp = (a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2];

const releases = await github.paginate(github.rest.repos.listReleases, {
owner, repo, per_page: 100,
});
const latest = releases
.filter(r => r.prerelease && /-main\.\d+$/.test(r.tag_name))
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))[0];
if (!latest) {
core.info('No pre-release found to update.');
return;
}

const tags = await github.paginate(github.rest.repos.listTags, {
owner, repo, per_page: 100,
});
const stableRe = /^v(\d+)\.(\d+)\.(\d+)$/;
let stableTag = null;
let stableVer = [-1, -1, -1];
for (const t of tags) {
const m = stableRe.exec(t.name);
if (m) {
const v = [+m[1], +m[2], +m[3]];
if (cmp(v, stableVer) > 0) { stableVer = v; stableTag = t.name; }
}
}

let sinceMs = 0;
if (stableTag) {
const ref = await github.rest.git.getRef({ owner, repo, ref: `tags/${stableTag}` });
let sha = ref.data.object.sha;
if (ref.data.object.type === 'tag') {
const t = await github.rest.git.getTag({ owner, repo, tag_sha: sha });
sha = t.data.object.sha;
}
const c = await github.rest.repos.getCommit({ owner, repo, ref: sha });
sinceMs = new Date(c.data.commit.committer.date).getTime();
}

const merged = [];
for (let page = 1; page <= 20; page++) {
const { data } = await github.rest.pulls.list({
owner, repo, state: 'closed', base: 'main',
sort: 'updated', direction: 'desc', per_page: 100, page,
});
if (!data.length) break;
for (const pr of data) {
if (pr.merged_at && new Date(pr.merged_at).getTime() > sinceMs) merged.push(pr);
}
if (new Date(data[data.length - 1].updated_at).getTime() < sinceMs) break;
}

if (!merged.length) {
core.info('No merged PRs since last stable; leaving notes as-is.');
return;
}

const cats = [
{ title: '🚀 Features', types: ['feat'] },
{ title: '🐛 Fixes', types: ['fix'] },
{ title: '🧹 Chores', types: ['chore'] },
{ title: '📚 Documentation', types: ['docs'] },
{ title: '♻️ Refactors', types: ['refactor'] },
{ title: 'Other Changes', types: ['*'] },
].map(c => ({ ...c, items: [] }));

const typeOf = (pr) => {
const m = (pr.body || '').match(/type:\s*(feat|fix|chore|docs|refactor)/i);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a suggestion: instead of writing the category title (like feat, fix, etc.) in the PR body, we could derive it from the branch name. I think that would keep the PR body cleaner and make the process a bit more streamlined.

@Ayush8923 Ayush8923 Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this suggestion but I considered this, but I'd prefer to keep the type explicit in the PR rather than derive it from the branch name. Since this is open source, we can't rely on contributors following a feat/, fix/ branch convention (GitHub web edits produce patch-1, and our own team often names branches module-wise like auth/... etc.). Branch names also can't carry mixed types, and they're hard to correct after push. To address the "less manual / cleaner" goal, I'd rather add a PR-description lint (conventional-commit check) so the explicit type is validated automatically.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make Sense!

return m ? m[1].toLowerCase() : 'fix';
};
const seen = new Set();
for (const pr of merged) {
if (seen.has(pr.number)) continue;
seen.add(pr.number);
const type = typeOf(pr);
const bucket = cats.find(c => c.types.includes(type)) || cats.find(c => c.types.includes('*'));
bucket.items.push(pr);
}

let body = `## What's Changed\n`;
for (const c of cats) {
if (!c.items.length) continue;
body += `\n### ${c.title}\n`;
for (const pr of c.items) {
body += `* ${pr.title} by @${pr.user.login} in #${pr.number}\n`;
}
}
if (stableTag) {
body += `\n**Full Changelog**: https://github.com/${owner}/${repo}/compare/${stableTag}...${latest.tag_name}\n`;
}

await github.rest.repos.updateRelease({
owner, repo, release_id: latest.id, body,
});
core.info(`Rewrote ${latest.tag_name} notes: ${merged.length} PR(s) since ${stableTag || 'repo start'}.`);

- name: Remove previous pre-releases
if: success()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;

const releases = await github.paginate(
github.rest.repos.listReleases,
{ owner, repo, per_page: 100 }
);

const preReleases = releases
.filter(r => r.prerelease && /-main\.\d+$/.test(r.tag_name))
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

const stale = preReleases.slice(1);

for (const r of stale) {
core.info(`Deleting stale pre-release ${r.tag_name}`);
await github.rest.repos.deleteRelease({ owner, repo, release_id: r.id });
try {
await github.rest.git.deleteRef({
owner,
repo,
ref: `tags/${r.tag_name}`,
});
} catch (e) {
core.warning(`Could not delete tag ${r.tag_name}: ${e.message}`);
}
}

- name: Update PR labels
if: success()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = context.payload.pull_request.number;

const labelsToCreate = [
{ name: 'released', color: '0075ca' },
{ name: 'released on @main', color: '0052cc' }
];

for (const label of labelsToCreate) {
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name
});
} catch {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color
});
}
}

await github.rest.issues.removeAllLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber
});

await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: ['released', 'released on @main']
});
25 changes: 25 additions & 0 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"branches": [
"release",
{
"name": "main",
"prerelease": "main"
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"releaseRules": [
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "chore", "release": "patch" },
{ "type": "docs", "release": "patch" },
{ "type": "refactor", "release": "patch" }
]
}
],
"@semantic-release/release-notes-generator",
"@semantic-release/github"
]
}
2 changes: 1 addition & 1 deletion app/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
AnalyticsModality,
} from "@/app/lib/types/analytics";

export const APP_NAME = "Kaapi Konsole";
export const APP_NAME = "Kaapi Konsole Staging V1";

export const STORAGE_KEYS = {
API_KEYS: "kaapi_api_keys",
Expand Down
Loading