As we no longer need to update each time yt-dlp updates, we going to use simver

This commit is contained in:
arabcoders 2025-06-18 22:08:11 +03:00
parent 8a3673bc07
commit f50708852a
9 changed files with 230 additions and 103 deletions

107
.github/scripts/generate_changelog.py vendored Normal file
View file

@ -0,0 +1,107 @@
#!/usr/bin/env python3
import argparse
import json
import sys
from datetime import UTC, datetime
try:
import git # type: ignore
except ImportError:
print("Please install GitPython: pip install GitPython", file=sys.stderr) # noqa: T201
sys.exit(1)
def get_sorted_tags(repo):
"""Returns all tags sorted by their commit date (newest first)."""
return sorted(repo.tags, key=lambda t: t.commit.committed_datetime, reverse=True)
def get_commits_between(repo, start, end):
"""Return commits between two refs (no merges)."""
return list(repo.iter_commits(f"{start}..{end}", no_merges=True))
def generate_changelog(repo_path, changelog_path):
repo = git.Repo(repo_path)
tags = get_sorted_tags(repo)
changelog = []
if not tags:
start = repo.commit(repo.git.rev_list("--max-parents=0", "HEAD"))
commits = get_commits_between(repo, start.hexsha, "HEAD")
changelog.append(
{
"tag": "Initial Release",
"date": datetime.now(UTC).isoformat(),
"commits": [
{
"sha": c.hexsha[:8],
"full_sha": c.hexsha,
"message": c.message.strip(),
"author": c.author.name,
"date": c.committed_datetime.astimezone(UTC).isoformat(),
}
for c in commits
],
}
)
else:
for i in range(len(tags) - 1):
newer, older = tags[i], tags[i + 1]
commits = get_commits_between(repo, older.commit.hexsha, newer.commit.hexsha)
if not commits:
continue
changelog.append(
{
"tag": newer.name,
"full_sha": newer.commit.hexsha,
"date": newer.commit.committed_datetime.astimezone(UTC).isoformat(),
"commits": [
{
"sha": c.hexsha[:8],
"full_sha": c.hexsha,
"message": c.message.strip(),
"author": c.author.name,
"date": c.committed_datetime.astimezone(UTC).isoformat(),
}
for c in commits
],
}
)
# Add HEAD -> latest tag if ahead
head = repo.head.commit
if head.hexsha != tags[0].commit.hexsha:
commits = get_commits_between(repo, tags[0].commit.hexsha, head.hexsha)
if commits:
changelog.insert(
0,
{
"tag": f"Unreleased ({head.hexsha[:8]})",
"full_sha": head.hexsha,
"date": head.committed_datetime.astimezone(UTC).isoformat(),
"commits": [
{
"sha": c.hexsha[:8],
"full_sha": c.hexsha,
"message": c.message.strip(),
"author": c.author.name,
"date": c.committed_datetime.astimezone(UTC).isoformat(),
}
for c in commits
],
},
)
with open(changelog_path, "w", encoding="utf-8") as f:
json.dump(changelog, f, indent=2)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate CHANGELOG.json from Git tags.")
parser.add_argument("--repo_path", "-p", default=".", help="Path to git repo")
parser.add_argument("--changelog_path", "-f", default="CHANGELOG.json", help="Output file path")
args = parser.parse_args()
generate_changelog(args.repo_path, args.changelog_path)

View file

@ -22,16 +22,13 @@ on:
required: false
default: false
type: boolean
create_release:
description: "Create Release (requires build)"
required: false
default: false
type: boolean
push:
branches:
- dev
- master
tags:
- "v*"
paths-ignore:
- "**.md"
- ".github/**"
@ -96,11 +93,13 @@ jobs:
packages: write
contents: write
env:
PLATFORMS: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
PLATFORMS: ${{ startsWith(github.ref, 'refs/tags/v') && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v4
@ -120,13 +119,30 @@ jobs:
pnpm install --production --prefer-offline --frozen-lockfile
pnpm run generate
- name: Update Version File
uses: ArabCoders/write-version-to-file@master
with:
filename: "/app/library/version.py"
placeholder: "dev-master"
with_date: "true"
with_branch: "true"
- name: Install GitPython
run: pip install gitpython
- name: Generate CHANGELOG.json
run: |
python3 .github/scripts/generate_changelog.py -p . -f ./ui/exported/CHANGELOG.json
- name: Update app/library/version.py
run: |
VERSION="${GITHUB_REF##*/}"
SHA=$(git rev-parse HEAD)
DATE=$(date -u +"%Y%m%d")
echo "Current version: ${VERSION}, SHA: ${SHA}, Date: ${DATE}"
echo "APP_VERSION=${VERSION}" >> $"$GITHUB_ENV"
echo "APP_SHA=${SHA}" >> "$GITHUB_ENV"
echo "APP_DATE=${DATE}" >> "$GITHUB_ENV"
sed -i \
-e "s/^APP_VERSION = \".*\"/APP_VERSION = \"${VERSION}\"/" \
-e "s/^APP_COMMIT_SHA = \".*\"/APP_COMMIT_SHA = \"${SHA}\"/" \
-e "s/^APP_BUILD_DATE = \".*\"/APP_BUILD_DATE = \"${DATE}\"/" \
app/library/version.py
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@ -142,10 +158,9 @@ jobs:
${{ env.DOCKERHUB_SLUG }}
${{ env.GHCR_SLUG }}
tags: |
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=ref,event=branch
type=ref,event=tag
type=raw,value={{branch}}{{base_ref}}-{{date 'YYYYMMDD'}}-{{sha}}
flavor: |
latest=false
@ -173,13 +188,49 @@ jobs:
cache-from: type=gha, scope=${{ github.workflow }}
cache-to: type=gha, mode=max, scope=${{ github.workflow }}
- name: Version tag
uses: arabcoders/action-python-autotagger@master
- name: Overwrite GitHub release notes
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/github-script@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
repo_name: arabcoders/ytptube
path: app/library/version.py
regex: 'APP_VERSION\s=\s\"(.+)\"'
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const tag = context.ref.replace('refs/tags/', '');
const latestRelease = await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo,
});
const release = latestRelease.data.find(r => r.tag_name === tag);
if (!release) {
core.setFailed(`Release with tag ${tag} not found`);
return;
}
const { data: commits } = await github.rest.repos.compareCommits({
owner: context.repo.owner,
repo: context.repo.repo,
base: latestRelease.data[1]?.tag_name || '', // fallback to second latest tag
head: tag,
});
const changelog = commits.commits.map(
c => `- ${c.sha.substring(0, 7)} ${c.commit.message.split('\n')[0]} by ${c.commit.author.name}`
).join('\n');
if (!changelog) {
core.setFailed('No commits found for the changelog');
return;
}
console.log(`Changelog for ${tag}:\n${changelog}`);
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.id,
body: changelog
});
dockerhub-sync-readme:
runs-on: ubuntu-latest

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
# compiled output
/frontend/dist
/ui/dist
/ui/exported/CHANGELOG.json
# dependencies
**/node_modules/*

View file

@ -12,7 +12,7 @@ import coloredlogs
from dotenv import load_dotenv
from .Utils import FileLogFormatter, arg_converter
from .version import APP_VERSION
from .version import APP_BUILD_DATE, APP_COMMIT_SHA, APP_VERSION
class Config:
@ -113,12 +113,18 @@ class Config:
version: str = APP_VERSION
"The version of the application."
app_version: str = APP_VERSION
"The version of the application, same as `version`."
app_commit_sha: str = APP_COMMIT_SHA
"The commit SHA of the application."
app_build_date: str = APP_BUILD_DATE
"The build date of the application."
__instance = None
"The instance of the class."
new_version_available: bool = False
"A new version of the application is available."
started: int = 0
"The time the application was started."
@ -188,11 +194,13 @@ class Config:
"version",
"__instance",
"ytdl_options",
"new_version_available",
"started",
"ytdlp_cli",
"_ytdlp_cli_mutable",
"is_native",
"app_version",
"app_commit_sha",
"app_build_date",
)
"The variables that are immutable."
@ -246,6 +254,9 @@ class Config:
"is_native",
"app_env",
"tasks_handler_timer",
"app_version",
"app_commit_sha",
"app_build_date",
)
"The variables that are relevant to the frontend."
@ -441,9 +452,6 @@ class Config:
)
raise ValueError(msg)
if self.version == "dev-master":
self._version_via_git()
def _get_attributes(self) -> dict:
attrs: dict = {}
vClass: str = self.__class__
@ -511,59 +519,3 @@ class Config:
return YTDLP_VERSION
except ImportError:
return "0.0.0"
def _version_via_git(self):
"""
Updates the version of the application using git tags.
This is used to set the version to the latest git tag.
"""
git_path: Path = Path(__file__).parent / ".." / ".." / ".git"
if not git_path.exists():
logging.info(f"Git directory '{git_path}' does not exist. Cannot determine version.")
return
try:
import subprocess
branch_result = subprocess.run( # noqa: S603
["git", "rev-parse", "--abbrev-ref", "HEAD"], # noqa: S607
cwd=git_path.parent,
capture_output=True,
text=True,
check=False,
)
if 0 != branch_result.returncode:
logging.error(f"Git rev-parse failed: {branch_result.stderr.strip()}")
return
branch_name: str = branch_result.stdout.strip()
if not branch_name:
logging.warning("Git branch name is empty.")
return
commit_result = subprocess.run( # noqa: S603
["git", "log", "-1", "--format=%ct_%H"], # noqa: S607
cwd=git_path.parent,
capture_output=True,
text=True,
check=False,
)
if 0 != commit_result.returncode:
logging.error(f"Git log failed: {commit_result.stderr.strip()}")
return
commit_info: str = commit_result.stdout.strip()
if not commit_info:
logging.warning("Git commit info is empty.")
return
commit_date, commit_sha = commit_info.split("_", 1)
commit_date = time.strftime("%Y%m%d", time.localtime(int(commit_date)))
commit_sha = commit_sha[:8]
self.version = f"{branch_name}-{commit_date}-{commit_sha}"
logging.info(f"Application version set to '{self.version}' based on git data.")
except Exception as e:
logging.error(f"Error while getting git version: {e!s}")

View file

@ -5,3 +5,9 @@ This file is updated by the CI/CD pipeline, do not edit it manually.
APP_VERSION = "dev-master"
"Application version"
APP_COMMIT_SHA = "unknown"
"Commit SHA of the application"
APP_BUILD_DATE = "unknown"
"Build date of the application"

View file

@ -120,7 +120,7 @@ class Main:
def started(_):
LOG.info("=" * 40)
LOG.info(f"YTPTube v{self._config.version} - started on http://{host}:{port}{self._config.base_path}")
LOG.info(f"YTPTube {self._config.version} - started on http://{host}:{port}{self._config.base_path}")
LOG.info("=" * 40)
if cb:
cb()

View file

@ -107,6 +107,7 @@
<div>
<Settings v-if="show_settings" :isLoading="loadingImage" @reload_bg="() => loadImage(true)" />
<NuxtLoadingIndicator />
<NuxtPage />
</div>
@ -114,7 +115,9 @@
<div class="column is-8-mobile">
<div class="has-text-left" v-if="config.app?.version">
© {{ Year }} - <NuxtLink href="https://github.com/ArabCoders/ytptube" target="_blank">YTPTube</NuxtLink>
<span class="is-hidden-mobile">&nbsp;({{ config?.app?.version || 'unknown' }})</span>
<span class="is-hidden-mobile"
v-tooltip="`Build Date: ${config.app?.app_build_date}, commit: ${config.app?.app_commit_sha}`">
&nbsp;({{ config?.app?.version || 'unknown' }})</span>
- <NuxtLink target="_blank" href="https://github.com/yt-dlp/yt-dlp">yt-dlp</NuxtLink>
<span class="is-hidden-mobile">&nbsp;({{ config?.app?.ytdlp_version || 'unknown' }})</span>
- <NuxtLink :to="`/changelog?version=${config?.app?.version || 'unknown'}`">CHANGELOG</NuxtLink>

View file

@ -27,7 +27,7 @@
<div class="content p-0 m-0">
<h1 class="is-4">
<span class="icon"><i class="fas fa-code-branch" /></span>
{{ formatTag(log) }} <span class="tag has-text-success" v-if="isInstalled(log)">Installed</span>
{{ log.tag }} <span class="tag has-text-success" v-if="isInstalled(log)">Installed</span>
</h1>
<hr>
<ul>
@ -61,7 +61,7 @@ useHead({ title: 'CHANGELOG' })
const PROJECT = 'ytptube'
const REPO = `https://github.com/arabcoders/${PROJECT}`
const REPO_URL = `https://arabcoders.github.io/${PROJECT}/CHANGELOG-{branch}.json?version={version}`
const REPO_URL = `https://arabcoders.github.io/${PROJECT}/CHANGELOG.json?version={version}`
const logs = ref([])
const api_version = ref('')
@ -73,18 +73,6 @@ const branch = computed(() => {
return ['master', 'dev'].includes(branch) ? branch : 'master'
})
const formatTag = log => {
const parts = log.tag.split('-')
if (parts.length < 3) {
return log.tag
}
const branch = parts[0]
const date = parts[1]
const shortSha = log.full_sha.substring(0, hashLength.value)
return `${ucFirst(branch)}: ${moment(date, 'YYYYMMDD').format('YYYY-MM-DD')} - ${shortSha}`
}
watch(() => config.app.version, async value => {
if (!value) {
return
@ -104,8 +92,24 @@ const loadContent = async () => {
isLoading.value = true
try {
const changes = await fetch(REPO_URL.replace('{branch}', branch.value).replace('{version}', api_version.value))
logs.value = await changes.json()
try{
logs.value = await (await request(uri('/CHANGELOG.json'), { method: 'GET' })).json()
}catch(e){
console.error(e)
const changes = await fetch(REPO_URL.replace('{branch}', branch.value).replace('{version}', api_version.value))
logs.value = await changes.json()
}
await nextTick()
logs.value = logs.value.slice(0, 10).map(log => {
log.commits = log.commits.map(commit => {
commit.full_sha = commit.sha.padEnd(hashLength.value, '0')
return commit
})
log.full_sha = log.tag.padEnd(hashLength.value, '0')
return log
})
} catch (e) {
console.error(e)
toast.error('error', 'Error', `Failed to fetch changelog. ${e.message}`)

View file

@ -20,6 +20,9 @@ const CONFIG_KEYS = {
ytdlp_cli: '',
file_logging: false,
is_native: false,
app_version: '',
app_commit_sha: '',
app_build_date: '',
},
presets: [
{