Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
64 changes: 64 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Release

on:
push:
branches:
- main
- protected-release-branch

jobs:
release:
runs-on: ubuntu-latest

environment: production

permissions:
contents: write
pull-requests: write

steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22.14.0
cache: "yarn"

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Install @octokit/auth-app
run: yarn add @octokit/auth-app

- name: Get GitHub App Token
id: app_token
run: |
echo "${{ secrets.GH_APP_PRIVATE_KEY }}" > private-key.pem
TOKEN=$(node <<EOF
const { createAppAuth } = require("@octokit/auth-app");
const { readFileSync } = require("fs");
(async () => {
const auth = createAppAuth({
appId: "${{ secrets.GH_APP_ID }}",
privateKey: readFileSync("private-key.pem", "utf8"),
installationId: "${{ secrets.GH_INSTALLATION_ID }}"
});
const installationAuthentication = await auth({ type: "installation" });
console.log(installationAuthentication.token);
})();
EOF
)
echo "::add-mask::$TOKEN"
echo "GITHUB_TOKEN=$TOKEN" >> $GITHUB_ENV

- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
REPO_URL: "${{ github.server_url }}/${{ github.repository }}"
run: npx semantic-release
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
## [1.2.0](https://github.com/upstars-global/semver-example/compare/v1.1.1...v1.2.0) (2025-03-17)

### 🚀 Features

* add semver to protected branch ([#30](https://github.com/upstars-global/semver-example/issues/30))
([3a5be2f](https://github.com/upstars-global/semver-example/commit/3a5be2f16c3f463accb463184b3f6faa2263a620))


* add semver to protected branch ([#31](https://github.com/upstars-global/semver-example/issues/31))
([82200f2](https://github.com/upstars-global/semver-example/commit/82200f25a434d8084fcc0fe9027394635fcf981a))


* add semver to protected branch ([#32](https://github.com/upstars-global/semver-example/issues/32))
([ddc96ef](https://github.com/upstars-global/semver-example/commit/ddc96ef15d72f7cca7f80abb6db665a4d9e319a3))



### 🧪 Testing

* Add 'protected-release-branch' to release branches
([eff41ec](https://github.com/upstars-global/semver-example/commit/eff41ec4236f83ffda9588c12a6be261cba7c2ca))

, closes [#16](https://github.com/upstars-global/semver-example/issues/)

* Add 'protected-release-branch' to release branches

Updated the release configuration to include 'protected-release-branch' alongside 'main'. This enables semantic-release to manage releases on the additional branch.

* test commit to protected-release-branch

## [1.1.1](https://github.com/upstars-global/semver-example/compare/v1.1.0...v1.1.1) (2025-02-21)

### 🐛 Bug Fixes

* - test deleted changelog
([016f7a8](https://github.com/upstars-global/semver-example/commit/016f7a89f50fab6e39e3481f9ed1de9b08f7111f))

## [1.1.0](https://github.com/upstars-global/semver-example/compare/v1.0.0...v1.1.0) (2025-02-21)

### 🔨 Refactoring

* - test changelog
([dfe2197](https://github.com/upstars-global/semver-example/commit/dfe21973506e0a4fee483228257f9e7085306c35))

## 1.0.0 (2025-02-21)

### ⚠ BREAKING CHANGES

* add semVer config

### 🚀 Features

* add semVer config
([a9e438f](https://github.com/upstars-global/semver-example/commit/a9e438fa33471c330fc4bb23e819af876026281d))



**BREAKING CHANGE**: add semVer config
49 changes: 49 additions & 0 deletions changelog-template-commit.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{{!--
Copy of https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-conventionalcommits/templates/commit.hbs

The following have been replaced:

- `commitUrlFormat` with `{{@root.host}}/{{@root.owner}}/{{@root.repository}}/commit/{{hash}})`
- `issueUrlFormat` with `{{@root.host}}/{{@root.owner}}/{{@root.repository}}/issues/{{this.id}}`

As they won't be replaced when overriding the commitPartial
--}}
*{{#if scope}} **{{scope}}:**
{{~/if}} {{#if subject}}
{{~subject}}
{{~else}}
{{~header}}
{{~/if}}

{{!-- commit link --}}
{{~#if hash}} {{#if @root.linkReferences~}}
([{{shortHash}}]({{@root.host}}/{{@root.owner}}/{{@root.repository}}/commit/{{hash}}))
{{~else}}
{{~shortHash}}
{{~/if}}{{~/if}}

{{!-- commit references --}}
{{~#if references~}}
, closes
{{~#each references}} {{#if @root.linkReferences~}}
[
{{~#if this.owner}}
{{~this.owner}}/
{{~/if}}
{{~this.repository}}{{this.prefix}}{{this.issue}}]({{@root.host}}/{{@root.owner}}/{{@root.repository}}/issues/{{this.id}})
{{~else}}
{{~#if this.owner}}
{{~this.owner}}/
{{~/if}}
{{~this.repository}}{{this.prefix}}{{this.issue}}
{{~/if}}{{/each}}
{{~/if}}
{{!-- End of copy --}}

{{!-- Start of custom additions --}}
{{#each bodyLines}}

{{this}}
{{/each}}{{#each notes}}
**BREAKING CHANGE**: {{text}}
{{/each}}
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "unity-core-modules",
"version": "1.2.0",
"description": "",
"type": "module",
"devDependencies": {
"@octokit/auth-app": "^7.1.5",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^7.0.3",
"@semantic-release/git": "^10.0.1",
"conventional-changelog-conventionalcommits": "^8.0.0",
"semantic-release": "^24.2.3"
}
}
68 changes: 68 additions & 0 deletions release.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { readFileSync } from 'node:fs'
const commitPartial = readFileSync('./changelog-template-commit.hbs', { encoding: 'utf-8' })


function finalizeContext (context) {
for (const commitGroup of context.commitGroups) {
for (const commit of commitGroup.commits) {
commit.bodyLines = commit.body?.split('\n').filter((line) => line !== '') ?? []
}
}

return context
}

export default {
branches: ["main", "protected-release-branch"],
preset: "conventionalcommits",
plugins: [
["@semantic-release/commit-analyzer", {
releaseRules: [
{ type: "fix", release: "patch" },
{ type: "perf", release: "patch" },
{ type: "feat", release: "minor" },
{ type: "minor", release: "minor" },
{ type: "refactor", release: "minor" },
{ type: "style", release: "minor" },
{ type: "docs", release: "minor" },
{ type: "test", release: "minor" },
{ type: "chore", release: "minor" },
{ breaking: true, release: "major" },
{ type: "BREAKING CHANGE", release: "major" },
]
}],
["@semantic-release/release-notes-generator", {
preset: "conventionalcommits",
parserOpts: {
noteKeywords: ["BREAKING CHANGE", "BREAKING CHANGES"]
},
writerOpts: {
commitPartial,
finalizeContext,
},
presetConfig: {
types: [
{ type: "fix", section: "🐛 Bug Fixes", hidden: false },
{ type: "feat", section: "🚀 Features", hidden: false },
{ type: "chore", section: "🔧 Maintenance", hidden: false },
{ type: "docs", section: "📖 Documentation", hidden: false },
{ type: "style", section: "💅 Code Style", hidden: false },
{ type: "refactor", section: "🔨 Refactoring", hidden: false },
{ type: "perf", section: "⚡ Performance", hidden: false },
{ type: "test", section: "🧪 Testing", hidden: false },
{ type: "breaking", section: "⚠ Breaking Changes", hidden: false },
{ type: "other", section: "📌 Other Changes", hidden: false }
]
}
}],
"@semantic-release/changelog",
["@semantic-release/exec", {
prepareCmd: "node -e \"let pkg=require('./package.json'); pkg.version='${nextRelease.version}'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\"",
// successCmd: "node send-slack-notification.js \"${nextRelease.version}\" \"${process.env.REPO_URL}\"",
}],
["@semantic-release/git", {
assets: ["package.json", "CHANGELOG.md"],
message: "chore(release): ${nextRelease.version} [skip ci]"
}]
]
};
82 changes: 82 additions & 0 deletions send-slack-notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const https = require('https');
const { execSync } = require('child_process');

const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
const version = process.argv[2]; // Release version
const repoUrl = process.argv[3]; // Repository URL
const branch = process.env.GITHUB_REF?.replace("refs/heads/", "") || "main"; // Branch name
const author = process.env.GITHUB_ACTOR || "Unknown"; // Release author
const timestamp = new Date().toLocaleString("en-US", { timeZone: "UTC" }); // Release timestamp in UTC

if (!SLACK_WEBHOOK_URL) {
console.error("❌ Error: SLACK_WEBHOOK_URL is not set.");
process.exit(1);
}

// Get the first 10 lines from CHANGELOG.md (latest updates)
let changelog = "";
try {
changelog = execSync('head -n 10 CHANGELOG.md').toString().trim();
} catch (err) {
console.error("❌ Error reading CHANGELOG.md:", err.message);
}

// Generate URLs
const branchUrl = `${repoUrl}/tree/${branch}`;
const authorUrl = `https://github.com/${author}`;
const diffUrl = `${repoUrl}/compare/v${version.replace(/\.\d+$/, ".0")}...v${version}`;
const changelogUrl = `${repoUrl}/blob/main/CHANGELOG.md`;

const message = {
text: `🚀 *New Release:* *v${version}* \n🔗 <${repoUrl}/releases/tag/v${version}|View Release>`,
attachments: [
{
color: "#36a64f",
fields: [
{ title: "Branch", value: `<${branchUrl}|${branch}>`, short: true },
{ title: "Author", value: `<${authorUrl}|${author}>`, short: true },
{ title: "Release Time (UTC)", value: timestamp, short: true },
{ title: "Changelog", value: `<${changelogUrl}|View full CHANGELOG>`, short: false },
{ title: "Recent Changes", value: changelog ? `\`\`\`\n${changelog}\n\`\`\`` : "_No changes available_", short: false },
{ title: "GitHub Diff", value: `<${diffUrl}|View Changes>`, short: false }
]
}
]
};

const requestData = JSON.stringify(message);

const requestOptions = new URL(SLACK_WEBHOOK_URL);
const req = https.request(
{
hostname: requestOptions.hostname,
path: requestOptions.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(requestData),
},
},
(res) => {
let responseData = "";
res.on("data", (chunk) => {
responseData += chunk;
});

res.on("end", () => {
console.log(`🔹 Slack Response: ${res.statusCode} ${responseData}`);
if (res.statusCode === 200) {
console.log("✅ Notification successfully sent to Slack.");
} else {
console.error(`❌ Error sending notification: ${res.statusCode}`);
}
});
}
);

req.on('error', (err) => {
console.error(`❌ Request error: ${err.message}`);
});

req.write(requestData);
req.end();
Loading