7 Commits

10 changed files with 228 additions and 235 deletions

View File

@@ -9,19 +9,53 @@
"publish": false "publish": false
}, },
"hooks": { "hooks": {
"before:init": "node ./scripts/read-build-number.js",
"after:release": "node ./scripts/create-gitea-release.js ${version}" "after:release": "node ./scripts/create-gitea-release.js ${version}"
}, },
"github": false, "github": false,
"plugins": { "plugins": {
"@release-it/conventional-changelog": { "@release-it/conventional-changelog": {
"preset": "conventionalcommits", "preset": {
"name": "conventionalcommits",
"types": [
{ "type": "feat", "section": "🌟 Enhancements" },
{ "type": "fix", "section": "🐛 Bug fixes" },
{ "type": "chore", "section": "📝 Chore" },
{ "type": "docs", "section": "📚 Documentation" },
{ "type": "style", "section": "📚 Style" },
{ "type": "refactor", "section": "🛠️ Code Refactor" },
{
"type": "perf",
"section": "🚀 Performance"
},
{ "type": "test", "section": "📝 Testing Code" },
{
"type": "ci",
"section": "📈 Project changes"
},
{
"type": "build",
"hidden": false,
"section": "📈 Project Builds"
}
],
"commitUrlFormat": "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}",
"compareUrlFormat": "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}",
"issueUrlFormat": "{{host}}/{{owner}}/{{repository}}/issues/{{id}}",
"userUrlFormat": "{{host}}/{{user}}"
},
"infile": "CHANGELOG.md", "infile": "CHANGELOG.md",
"config": ".versionrc.json" "header": "# Changelog\n\nAll notable changes to LST will be documented in this file.\n",
"releaseCommitMessageFormat": "chore(release): {{currentTag}}"
} }
}, },
"gitea": { "@release-it/gitea": {
"host": "https://${GITEA_URL}", "host": "https://${GITEA_URL}",
"tokenRef": "GITEA_TOKEN" "tokenRef": "GITEA_TOKEN",
"releaseName": "v${version} (build ${BUILD_NUMBER})",
"releaseNotes": "node ./scripts/get-changelog-entry.js ${version}"
}, },
"files": ["package.json", "CHANGELOG.md"] "files": ["package.json", "CHANGELOG.md"]

View File

@@ -1,17 +0,0 @@
{
"types": [
{ "type": "feat", "section": "🌟 Enhancements" },
{ "type": "fix", "section": "🐛 Bug fixes" },
{ "type": "chore", "hidden": false, "section": "📝 Chore" },
{ "type": "docs", "section": "📚 Documentation" },
{ "type": "style", "hidden": true },
{ "type": "refactor", "section": "🛠️ Code Refactor" },
{ "type": "perf", "hidden": false, "section": "🚀 Performance" },
{ "type": "test", "section": "📝 Testing Code" },
{ "type": "ci", "hidden": false, "section": "📈 Project changes" },
{ "type": "build", "hidden": true, "section": "📈 Project Builds" }
],
"commitUrlFormat": "https://git.tuffraid.net/cowch/logistics_support_tool/commits/{{hash}}",
"compareUrlFormat": "https://git.tuffraid.net/cowch/logistics_support_tool/compare/{{previousTag}}...{{currentTag}}",
"header": "# All changes to lst are shown below.\nReleases are combined zip of backend and frontend."
}

View File

@@ -1,5 +1,24 @@
# Changelog # Changelog
All notable changes to LST will be documented in this file.
## [0.0.1-alpha.3](https://git.tuffraid.net/cowch/logistics_support_tool/compare/v0.0.1-alpha.2...v0.0.1-alpha.3) (2025-07-16)
### 🌟 Enhancements
* **scripts:** 2 new scripts for the build process ([afe795e](https://git.tuffraid.net/cowch/logistics_support_tool/commit/afe795edd588b823e5459a6762f787152a49a8cb))
### 🛠️ Code Refactor
* **build:** changes to show more details when we run the build stuff ([5ed4a6c](https://git.tuffraid.net/cowch/logistics_support_tool/commit/5ed4a6c081e29da6600949ed2b811551e6a947f7))
* **createzip:** change the name from release to createZip ([6467355](https://git.tuffraid.net/cowch/logistics_support_tool/commit/646735565f28153c0750e7788b349b384bd5c5de))
* **package.json:** changes to the scripts ([7a4840e](https://git.tuffraid.net/cowch/logistics_support_tool/commit/7a4840ef95c86a96e3b8a2e8af124a55af2b01b4))
* **release-it:** changes to the build process and changelog ([c3473ca](https://git.tuffraid.net/cowch/logistics_support_tool/commit/c3473ca60e7e92f68cdff4e4a35bfcff2e7ae37b))
* **releases:** simplified the functions ([a0856d1](https://git.tuffraid.net/cowch/logistics_support_tool/commit/a0856d171a404c3adf52591b3c9da4fae53322d3))
# Changelog
## [0.0.1-alpha.2](https://git.tuffraid.net/cowch/logistics_support_tool/compare/v0.0.1-alpha.1...v0.0.1-alpha.2) (2025-07-16) ## [0.0.1-alpha.2](https://git.tuffraid.net/cowch/logistics_support_tool/compare/v0.0.1-alpha.1...v0.0.1-alpha.2) (2025-07-16)
## [0.0.3-alpha.22](https://git.tuffraid.net/cowch/logistics_support_tool/compare/v0.0.3-alpha.21...v0.0.3-alpha.22) (2025-07-12) ## [0.0.3-alpha.22](https://git.tuffraid.net/cowch/logistics_support_tool/compare/v0.0.3-alpha.21...v0.0.3-alpha.22) (2025-07-12)

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "logistics_support_tool", "name": "logistics_support_tool",
"version": "0.0.1-alpha.2", "version": "0.0.1-alpha.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "logistics_support_tool", "name": "logistics_support_tool",
"version": "0.0.1-alpha.2", "version": "0.0.1-alpha.3",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"dotenv": "^17.2.0", "dotenv": "^17.2.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "logistics_support_tool", "name": "logistics_support_tool",
"version": "0.0.1-alpha.2", "version": "0.0.1-alpha.3",
"description": "This is the new logisitcs support tool", "description": "This is the new logisitcs support tool",
"private": true, "private": true,
"main": "index.js", "main": "index.js",
@@ -11,9 +11,8 @@
"docker:front": "docker build -t logistics_support_tool:frontend-latest -f frontend/Dockerfile ./frontend", "docker:front": "docker build -t logistics_support_tool:frontend-latest -f frontend/Dockerfile ./frontend",
"docker:back": "docker build -t logistics_support_tool:backend-latest -f backend/Dockerfile ./backend", "docker:back": "docker build -t logistics_support_tool:backend-latest -f backend/Dockerfile ./backend",
"docker": "powershell -File ./scripts/dockerBuild.ps1", "docker": "powershell -File ./scripts/dockerBuild.ps1",
"release:createZip": "powershell -File ./scripts/release.ps1", "createZip": "powershell -File ./scripts/createZip.ps1",
"release:gitea": "node ./scripts/create-gitea-release.js", "release": "release-it --verbose --non-interactive --preRelease=alpha",
"release:publish": "release-it --verbose --non-interactive --preRelease=alpha",
"commit": "cz" "commit": "cz"
}, },
"repository": { "repository": {

View File

@@ -117,17 +117,37 @@ function Update-BuildNumber {
dotnet publish -c Release -o ./publish dotnet publish -c Release -o ./publish
Pop-Location Pop-Location
try {
Update-BuildNumber Update-BuildNumber
Write-Host "Zipping up the release" Write-Host "Zipping up the release"
npm run release:createZip npm run createZip
if ($LASTEXITCODE -ne 0) {
throw "Failed to create release zip"
}
$choice = Read-Host "Are we going to create a release? y/N" $choice = Read-Host "Are we going to create a release? y/N"
if ($choice -eq "y" -or $choice -eq "Y") { if ($choice -eq "y" -or $choice -eq "Y") {
npm run release:gitea $version Write-Host "Creating release..."
npm run release:publish
# This will:
# 1. Update version in package.json
# 2. Generate changelog
# 3. Create git tag
# 4. Push to remote
# 5. Create Gitea release via our script
npm run release
if ($LASTEXITCODE -ne 0) {
throw "Failed to create Gitea release"
}
} }
} catch {
Write-Warning "Release process failed: $_"
exit 1
}
break break

View File

@@ -1,252 +1,124 @@
const version = process.argv[2];
if (!version) {
console.error("Version not passed to create-gitea-release.js");
process.exit(1);
}
import fs from "fs-extra"; import fs from "fs-extra";
import path from "path"; import path from "path";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
import fetch from "node-fetch"; import fetch from "node-fetch";
import dotenv from "dotenv"; import dotenv from "dotenv";
dotenv.config({ path: "./.env" }); dotenv.config({ path: "./.env" });
// Resolve the directory of the current script
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
// Absolute path to BUILD_NUMBER const version = process.argv[2];
const buildNumberPath = path.resolve(__dirname, "../BUILD_NUMBER"); if (!version) {
console.error("Version not passed to create-gitea-release.js");
// Load build number from BUILD_NUMBER file process.exit(1);
let buildNumber = "0";
try {
const rawBuild = fs.readFileSync(buildNumberPath, "utf8");
console.log("Raw build", rawBuild);
buildNumber = rawBuild.trim();
} catch (e) {
console.log(e);
console.warn("BUILD_NUMBER file not found, defaulting to 0");
} }
const fullVersion = `${version}.${buildNumber}`;
const { GITEA_URL, GITEA_USERNAME, GITEA_REPO, GITEA_TOKEN } = process.env; const { GITEA_URL, GITEA_USERNAME, GITEA_REPO, GITEA_TOKEN } = process.env;
if (!GITEA_URL || !GITEA_USERNAME || !GITEA_REPO || !GITEA_TOKEN) { if (!GITEA_URL || !GITEA_USERNAME || !GITEA_REPO || !GITEA_TOKEN) {
console.error("Missing required environment variables"); console.error("Missing required environment variables");
process.exit(1); process.exit(1);
} }
// Step 1: Generate or update CHANGELOG.md const getChangelogContent = async () => {
// console.log("Generating CHANGELOG.md...");
// const result = spawnSync(
// "npx",
// [
// "conventional-changelog",
// "-p",
// "conventionalcommits",
// "-i",
// "CHANGELOG.md",
// "-s",
// "-r",
// "0",
// ],
// { stdio: "inherit", shell: true }
// );
// if (result.status !== 0) {
// console.error("Failed to generate changelog");
// process.exit(1);
// }
// Corrected function to get the latest changelog entry from CHANGELOG.md
const getLatestChangelog = async () => {
try { try {
const changelogPath = path.resolve(__dirname, "../CHANGELOG.md"); const changelogPath = path.resolve(__dirname, "../CHANGELOG.md");
console.log(`Attempting to read changelog from: ${changelogPath}`); // Debugging line const content = await fs.readFile(changelogPath, "utf8");
const changelogContent = await fs.readFile(changelogPath, "utf8");
console.log(
"Changelog content read successfully (first 200 chars):",
changelogContent.substring(0, 200)
); // Debugging line
const lines = changelogContent.trim().split(/\r?\n/); // Extract the section for the current version
const versionHeading = `## [${version}]`;
const sections = content.split(/(?=^## \[)/m); // Split at version headings
let latestReleaseNotes = []; if (sections.length < 2) {
let inLatestRelease = false; console.warn("Couldn't find version section in changelog");
let foundFirstHeading = false; return "No changelog content available.";
// Regex to match a conventional changelog heading format: "## [version](url) (date)"
// The key is to correctly parse the URL part in parentheses.
const releaseHeadingRegex = /^## \[.*?\]\(.*?\)\s\(.*\)$/;
for (const line of lines) {
// Trim each line to handle potential leading/trailing spaces when matching
const trimmedLine = line.trim();
if (trimmedLine.match(releaseHeadingRegex)) {
if (!foundFirstHeading) {
// This is the first (latest) release heading we encounter
inLatestRelease = true;
foundFirstHeading = true;
// We skip the heading line itself from the notes body
continue;
} else {
// This is a subsequent release heading, meaning we've passed the latest release's content
break;
}
}
// If we are currently inside the latest release block, add the line
if (inLatestRelease) {
latestReleaseNotes.push(trimmedLine); // Use trimmedLine here
}
} }
// Clean up the collected notes by filtering out empty lines if they are not meaningful content // The first section is the latest version
// and joining them back, then trimming. const latestSection = sections[1];
const cleanedNotes = latestReleaseNotes return latestSection.trim();
.filter((line) => line !== "") // Remove truly empty lines
.join("\n")
.trim();
if (cleanedNotes) {
console.log(
"Successfully extracted latest changelog notes:\n",
cleanedNotes
);
return cleanedNotes;
} else {
console.warn(
"Could not find any content for the latest changelog entry in CHANGELOG.md. This might mean the file is empty, or the regex for headings is incorrect, or there's no content after the heading."
);
return "No changelog notes available.";
}
} catch (err) { } catch (err) {
console.error("Error reading or parsing CHANGELOG.md:", err); console.error("Error reading changelog:", err);
throw err; return "No changelog content available.";
} }
}; };
const releaseNotes = await getLatestChangelog(); const createOrUpdateRelease = async (releaseNotes) => {
// Step 3: Create or update Gitea release
const createOrUpdateRelease = async () => {
const tagName = `v${version}`; const tagName = `v${version}`;
const apiBase = `https://${GITEA_URL}/api/v1/repos/${GITEA_USERNAME}/${GITEA_REPO}`; const apiBase = `https://${GITEA_URL}/api/v1/repos/${GITEA_USERNAME}/${GITEA_REPO}`;
const existing = await fetch(`${apiBase}/releases/tags/${tagName}`, { try {
headers: { Authorization: `token ${GITEA_TOKEN}` }, const existing = await fetch(`${apiBase}/releases/tags/${tagName}`, {
}); headers: { Authorization: `token ${GITEA_TOKEN}` },
});
let release; if (existing.ok) {
// Update existing release
const existingRelease = await existing.json();
console.log(`Updating existing release ${tagName}`);
if (existing.ok) { const response = await fetch(
const existingRelease = await existing.json(); `${apiBase}/releases/${existingRelease.id}`,
console.log(`Release ${tagName} already exists. Updating it.`); {
method: "PATCH",
headers: {
Authorization: `token ${GITEA_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: tagName,
body: releaseNotes,
draft: false,
prerelease: false, // Change to true if you want prereleases
}),
}
);
const updateResponse = await fetch( if (!response.ok)
`${apiBase}/releases/${existingRelease.id}`, throw new Error(`Failed to update release: ${response.status}`);
{ const release = await response.json();
method: "PATCH", console.log("Release updated:", release.html_url);
return release;
} else if (existing.status === 404) {
// Create new release
console.log(`Creating new release ${tagName}`);
const response = await fetch(`${apiBase}/releases`, {
method: "POST",
headers: { headers: {
Authorization: `token ${GITEA_TOKEN}`, Authorization: `token ${GITEA_TOKEN}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
name: tagName, tag_name: tagName,
name: `Release ${version}`,
body: releaseNotes, body: releaseNotes,
draft: false, draft: false,
prerelease: true, prerelease: false, // Change to true if you want prereleases
}), }),
} });
);
if (!updateResponse.ok) { if (!response.ok)
const errorText = await updateResponse.text(); throw new Error(`Failed to create release: ${response.status}`);
throw new Error( const release = await response.json();
`Failed to update release: ${updateResponse.status} - ${errorText}` console.log("Release created:", release.html_url);
); return release;
} else {
throw new Error(`Failed to check release: ${existing.status}`);
} }
} catch (error) {
release = await updateResponse.json(); console.error("Error in createOrUpdateRelease:", error);
console.log("Release updated:", release.html_url || release.url); throw error;
} else if (existing.status === 404) {
const createResponse = await fetch(`${apiBase}/releases`, {
method: "POST",
headers: {
Authorization: `token ${GITEA_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
tag_name: tagName,
name: `Release ${fullVersion}`,
body: releaseNotes,
draft: false,
prerelease: true,
}),
});
if (!createResponse.ok) {
const errorText = await createResponse.text();
throw new Error(
`Failed to create release: ${createResponse.status} - ${errorText}`
);
}
release = await createResponse.json();
console.log("Release created:", release.html_url || release.url);
} else {
const errorText = await existing.text();
throw new Error(
`Failed to check release: ${existing.status} - ${errorText}`
);
} }
return release;
}; };
const uploadAsset = async (release) => {
const apiUrl = `https://${GITEA_URL}/api/v1/repos/${GITEA_USERNAME}/${GITEA_REPO}/releases/assets?tag=${release.tag_name}`;
const filePath = `releases/release-${fullVersion}.zip`;
if (!(await fs.pathExists(filePath))) {
console.warn(`Zip file not found: ${filePath}. Skipping asset upload.`);
return;
}
const FormData = (await import("form-data")).default;
const form = new FormData();
form.append("name", `release-${fullVersion}.zip`);
form.append("attachment", fs.createReadStream(filePath));
const response = await fetch(apiUrl, {
method: "POST",
headers: {
Authorization: `token ${GITEA_TOKEN}`,
...form.getHeaders(),
},
body: form,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Failed to upload asset: ${response.status} - ${errorText}`
);
}
const asset = await response.json();
console.log("Asset uploaded:", asset.browser_download_url || asset.url);
};
// Run everything
(async () => { (async () => {
try { try {
const release = await createOrUpdateRelease(); const releaseNotes = await getChangelogContent();
// await uploadAsset(release); // fix this later and just update the readme. await createOrUpdateRelease(releaseNotes);
} catch (err) { } catch (error) {
console.error(err); console.error("Release failed:", error);
process.exit(1); process.exit(1);
} }
})(); })();

View File

@@ -0,0 +1,42 @@
import fs from "fs-extra";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const version = process.argv[2];
if (!version) {
console.error("Version argument is required");
process.exit(1);
}
async function getChangelogEntry() {
try {
const changelogPath = path.resolve(__dirname, "../../CHANGELOG.md");
const content = await fs.readFile(changelogPath, "utf8");
// Find the section for this version
const versionHeader = `## [${version}]`;
const sections = content.split(versionHeader);
if (sections.length < 2) {
console.warn(`No changelog entry found for version ${version}`);
return `Release ${version}`;
}
// Extract the content for this version
const versionContent = sections[1].split("\n## ")[0].trim();
// Add the version header back
return `${versionHeader}\n${versionContent}`;
} catch (error) {
console.error("Error reading changelog:", error);
return `Release ${version}`;
}
}
// Output the changelog entry (release-it will capture this stdout)
getChangelogEntry()
.then(console.log)
.catch(() => process.exit(1));

View File

@@ -0,0 +1,24 @@
/**
* This is for release-it to be able to post the new build number in the releases
*/
import fs from "fs-extra";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function readBuildNumber() {
try {
const buildNumberPath = path.resolve(__dirname, "../BUILD_NUMBER");
const buildNumber = (await fs.readFile(buildNumberPath, "utf8")).trim();
process.env.BUILD_NUMBER = buildNumber;
console.log(`Build number: ${buildNumber}`);
} catch (error) {
console.error("Error reading BUILD_NUMBER:", error);
process.exit(1);
}
}
readBuildNumber();