Compare commits
12 Commits
7b6f9ef224
...
main
Author | SHA1 | Date | |
---|---|---|---|
6c2df38dd4 | |||
3d52b6e13f
|
|||
ef8dda20d8 | |||
8adaa916ff
|
|||
af0477f8e7
|
|||
5d8d995a04 | |||
36f6e7314a
|
|||
bc8ae46b4a | |||
dde2363ad7
|
|||
32e3b51a77
|
|||
da387f2ee6
|
|||
08679c2680
|
5
.gitea/default_merge_message/MERGE_TEMPLATE.md
Normal file
5
.gitea/default_merge_message/MERGE_TEMPLATE.md
Normal file
@@ -0,0 +1,5 @@
|
||||
chore(pr): ${PullRequestTitle} ${PullRequestReference}
|
||||
|
||||
${PullRequestDescription}
|
||||
|
||||
Merged from ${HeadBranch} into ${BaseBranch}
|
45
.gitea/scripts/cleanup_versions.sh
Executable file
45
.gitea/scripts/cleanup_versions.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# cleanup_dev_versions.sh - Delete old PyPI dev versions from Gitea package registry
|
||||
|
||||
# Required environment variables
|
||||
USERNAME="${TWINE_USERNAME}"
|
||||
TOKEN="${TWINE_PASSWORD}"
|
||||
REPO="${GITHUB_REPOSITORY}" # e.g., maxp/repocat
|
||||
API_BASE="${GITHUB_API_URL%/}" # Strip trailing slash if present
|
||||
|
||||
OWNER="${REPO%%/*}"
|
||||
PACKAGE_NAME="${REPO##*/}"
|
||||
API_URL="${API_BASE}/packages/${OWNER}/pypi/${PACKAGE_NAME}"
|
||||
|
||||
# Fetch the list of versions
|
||||
response=$(curl -s -u "$USERNAME:$TOKEN" "$API_URL")
|
||||
|
||||
# Extract all .dev versions, sort by creation time
|
||||
mapfile -t versions_to_delete < <(echo "$response" | jq -r '
|
||||
map(select(.version | test("\\.dev"))) |
|
||||
sort_by(.created_at) |
|
||||
.[0:-1][] |
|
||||
.version')
|
||||
|
||||
# Determine latest version to keep
|
||||
latest_version=$(echo "$response" | jq -r '
|
||||
map(select(.version | test("\\.dev"))) |
|
||||
sort_by(.created_at) |
|
||||
last.version')
|
||||
|
||||
if [[ -z "$latest_version" || ${#versions_to_delete[@]} -eq 0 ]]; then
|
||||
echo "No old .dev versions to delete."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Keeping latest .dev version: $latest_version"
|
||||
|
||||
# Delete old .dev versions
|
||||
for version in "${versions_to_delete[@]}"; do
|
||||
echo "Deleting old .dev version: $version"
|
||||
curl -s -X DELETE -u "$USERNAME:$TOKEN" "$API_URL/$version"
|
||||
done
|
||||
|
||||
echo "Cleanup complete."
|
21
.gitea/scripts/get-release-id.sh
Executable file
21
.gitea/scripts/get-release-id.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Eingaben
|
||||
TAG="$1"
|
||||
TOKEN="${ACTIONS_RUNTIME_TOKEN:-<fallback_token>}"
|
||||
REPO="${GITHUB_REPOSITORY:-owner/example}"
|
||||
API="${GITHUB_API_URL:-https://gitea.example.tld/api/v1}"
|
||||
|
||||
OWNER=$(echo "$REPO" | cut -d/ -f1)
|
||||
NAME=$(echo "$REPO" | cut -d/ -f2)
|
||||
|
||||
RESPONSE=$(curl -sf \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
"$API/repos/$OWNER/$NAME/releases/tags/$TAG")
|
||||
|
||||
RELEASE_ID=$(echo "$RESPONSE" | jq -r '.id')
|
||||
echo "Release-ID für $TAG ist: $RELEASE_ID"
|
||||
|
||||
# Für GitHub Actions als Umgebungsvariable
|
||||
echo "GT_RELEASE_ID=$RELEASE_ID" >> "$GITHUB_ENV"
|
14
.gitea/scripts/set_poetry_version.sh
Executable file
14
.gitea/scripts/set_poetry_version.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
BASE_VERSION=$(cat VERSION)
|
||||
NIGHTLY_SUFFIX=""
|
||||
|
||||
if [[ "$1" == "nightly" ]]; then
|
||||
# Beispiel: 20240511.1358 → 11. Mai, 13:58 Uhr
|
||||
NIGHTLY_SUFFIX=".dev$(date +%Y%m%d%H%M)"
|
||||
fi
|
||||
|
||||
FULL_VERSION="${BASE_VERSION}${NIGHTLY_SUFFIX}"
|
||||
|
||||
echo "Using version: $FULL_VERSION"
|
||||
poetry version "$FULL_VERSION"
|
21
.gitea/scripts/sync_version_from_poetry.sh
Executable file
21
.gitea/scripts/sync_version_from_poetry.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Stelle sicher, dass wir im Projektverzeichnis sind
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
PYPROJECT="pyproject.toml"
|
||||
VERSION_FILE="VERSION"
|
||||
|
||||
# Extrahiere die Version mit grep + sed (keine externen Abhängigkeiten nötig)
|
||||
VERSION=$(grep -E '^version\s*=' "$PYPROJECT" | head -n1 | sed -E 's/.*=\s*"([^"]+)".*/\1/')
|
||||
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
echo "❌ Version konnte nicht aus $PYPROJECT gelesen werden."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "%s" "$VERSION" > "$VERSION_FILE"
|
||||
echo "✅ Version synchronisiert: $VERSION → $VERSION_FILE"
|
40
.gitea/scripts/upload-asset.sh
Executable file
40
.gitea/scripts/upload-asset.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Eingabeparameter
|
||||
FILE_PATH="$1" # z. B. ./dist/build.zip
|
||||
CUSTOM_NAME="${2:-}" # optional: anderer Name unter dem das Asset gespeichert werden soll
|
||||
RELEASE_ID="${GT_RELEASE_ID:-}" # aus Umgebung
|
||||
|
||||
# Validierung
|
||||
if [[ -z "$RELEASE_ID" ]]; then
|
||||
echo "❌ RELEASE_ID ist nicht gesetzt. Abbruch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$FILE_PATH" ]]; then
|
||||
echo "❌ Datei '$FILE_PATH' existiert nicht. Abbruch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Default-Konfiguration
|
||||
TOKEN="${ACTIONS_RUNTIME_TOKEN:-<fallback_token>}"
|
||||
REPO="${GITHUB_REPOSITORY:-owner/example}"
|
||||
API="${GITHUB_API_URL:-https://gitea.example.tld/api/v1}"
|
||||
|
||||
# Owner/Repo auflösen
|
||||
OWNER=$(echo "$REPO" | cut -d/ -f1)
|
||||
NAME=$(echo "$REPO" | cut -d/ -f2)
|
||||
|
||||
# Dateiname setzen
|
||||
FILENAME="${CUSTOM_NAME:-$(basename "$FILE_PATH")}"
|
||||
|
||||
echo "🔼 Uploading '$FILE_PATH' as '$FILENAME' to release ID $RELEASE_ID"
|
||||
|
||||
# Upload
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-F "attachment=@$FILE_PATH" \
|
||||
"$API/repos/$OWNER/$NAME/releases/$RELEASE_ID/assets?name=$FILENAME"
|
||||
|
||||
echo "✅ Upload abgeschlossen: $FILENAME"
|
@@ -1,30 +1,33 @@
|
||||
# .gitea/workflows/publish.yml
|
||||
|
||||
name: Build and Publish
|
||||
name: Build and Publish nightly package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "pyproject.toml" # Nur bei Änderungen an dieser Datei
|
||||
workflow_dispatch: # Manuelles Anstoßen zulassen
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'CHANGELOG.md'
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: ⚙️ Prepare Environment Variables
|
||||
run: |
|
||||
echo "AGENT_TOOLSDIRECTORY=/home/runner/toolcache" >> $GITHUB_ENV
|
||||
mkdir -p /home/runner/toolcache
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🐍 Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.12"
|
||||
|
||||
- name: 🔄 Restore cache
|
||||
uses: https://git.0xmax42.io/actions/cache@v1
|
||||
with:
|
||||
key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
|
||||
paths: |
|
||||
~/.cache/pypoetry
|
||||
~/.cache/pip
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
@@ -35,6 +38,9 @@ jobs:
|
||||
run: |
|
||||
poetry install
|
||||
|
||||
- name: Set version from VERSION file (with nightly suffix)
|
||||
run: ./.gitea/scripts/set_poetry_version.sh nightly
|
||||
|
||||
- name: Build Package
|
||||
working-directory: .
|
||||
run: |
|
||||
@@ -47,3 +53,10 @@ jobs:
|
||||
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
|
||||
run: |
|
||||
poetry run twine upload --repository-url ${{ secrets.TWINE_URL }} dist/*
|
||||
|
||||
- name: Cleanup old dev versions
|
||||
run: |
|
||||
.gitea/scripts/cleanup_versions.sh '\.dev'
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
|
64
.gitea/workflows/build-and-deploy-release.yml
Normal file
64
.gitea/workflows/build-and-deploy-release.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Build and Publish nightly package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: 🐍 Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: 🔄 Restore cache
|
||||
uses: https://git.0xmax42.io/actions/cache@v1
|
||||
with:
|
||||
key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
|
||||
paths: |
|
||||
~/.cache/pypoetry
|
||||
~/.cache/pip
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
pip install poetry
|
||||
|
||||
- name: Install Project Dependencies
|
||||
working-directory: .
|
||||
run: |
|
||||
poetry install
|
||||
|
||||
- name: Build Package
|
||||
working-directory: .
|
||||
run: |
|
||||
poetry build
|
||||
|
||||
- name: Get built wheel filename
|
||||
id: get_whl
|
||||
run: |
|
||||
echo "whl_file=$(basename dist/*.whl)" >> $GITHUB_OUTPUT
|
||||
echo "sdist_file=$(basename dist/*.tar.gz)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish to Gitea Package Registry
|
||||
working-directory: .
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
|
||||
run: |
|
||||
poetry run twine upload --repository-url ${{ secrets.TWINE_URL }} dist/*
|
||||
|
||||
- name: Get Release ID from tag
|
||||
run: .gitea/scripts/get-release-id.sh "${{ github.event.release.tag_name }}"
|
||||
|
||||
- name: Upload assets
|
||||
run: |
|
||||
.gitea/scripts/upload-asset.sh ./dist/${{ steps.get_whl.outputs.whl_file }}
|
||||
.gitea/scripts/upload-asset.sh ./dist/${{ steps.get_whl.outputs.sdist_file }}
|
18
.gitea/workflows/release.yml
Normal file
18
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Auto Changelog & (Release)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Release
|
||||
uses: https://git.0xmax42.io/actions/auto-changelog-release-action@v0
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_PUBLISH_TOKEN }}
|
44
CHANGELOG.md
Normal file
44
CHANGELOG.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [unreleased]
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- *(readme)* Add pip installation instructions and refine license section - ([3d52b6e](https://git.0xmax42.io/maxp/hdlbuild/commit/3d52b6e13fd14ff1df61cb6e27d4f31445c82270))
|
||||
|
||||
## [0.7.0](https://git.0xmax42.io/maxp/hdlbuild/compare/v0.6.1..v0.7.0) - 2025-07-17
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(version)* Bump version to 0.7.0 - ([8adaa91](https://git.0xmax42.io/maxp/hdlbuild/commit/8adaa916ff4e736e9da707c232d6f57b788e57e8))
|
||||
- *(cli)* Add template generation commands - ([af0477f](https://git.0xmax42.io/maxp/hdlbuild/commit/af0477f8e74a9471c3e7d36877069592e41f651c))
|
||||
|
||||
## [0.6.1] - 2025-07-16
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(ci)* Add workflows for nightly builds and releases - ([da387f2](https://git.0xmax42.io/maxp/hdlbuild/commit/da387f2ee602390d616c79bf4057ccf941e21462))
|
||||
|
||||
### 🚜 Refactor
|
||||
|
||||
- Use typer for CLI argument parsing - ([6ca389d](https://git.0xmax42.io/maxp/hdlbuild/commit/6ca389d5cbbeff53faab9d61376a8c77ed097b6c))
|
||||
- Improves project configuration - ([175bf48](https://git.0xmax42.io/maxp/hdlbuild/commit/175bf4882a8f172ee536d726b31136690572be36))
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- *(readme)* Expand README with detailed usage and setup - ([fa7e738](https://git.0xmax42.io/maxp/hdlbuild/commit/fa7e738b7eade5a627218741a6fb4bd1617f7801))
|
||||
|
||||
### 🎨 Styling
|
||||
|
||||
- *(pyproject)* Simplify include array formatting - ([08679c2](https://git.0xmax42.io/maxp/hdlbuild/commit/08679c2680b49119e0414688a80e8dc2659236b4))
|
||||
- Updates VS Code editor color scheme - ([1d7bc19](https://git.0xmax42.io/maxp/hdlbuild/commit/1d7bc1996522ab54970348b5118ad319849a6a1f))
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(config)* Add git-cliff configuration file - ([dde2363](https://git.0xmax42.io/maxp/hdlbuild/commit/dde2363ad7dd2fd2d48c6154e3b88c1c4a6867fd))
|
||||
- Updates project version to 0.6.0 - ([7b6f9ef](https://git.0xmax42.io/maxp/hdlbuild/commit/7b6f9ef2240864b103903e79c895a76db59c14fa))
|
||||
- Remove build-deb.yml workflow file - ([0d26c42](https://git.0xmax42.io/maxp/hdlbuild/commit/0d26c42f8ae419d509aa47d3f7a23bfdd08cf79b))
|
||||
|
||||
|
21
README.md
21
README.md
@@ -41,6 +41,15 @@ HDLBuild is a flexible build management tool for FPGA projects. It simplifies th
|
||||
|
||||
---
|
||||
|
||||
## Installation per pip
|
||||
|
||||
To install HDLBuild via pip, run:
|
||||
```bash
|
||||
pip install --index-url https://git.0xmax42.io/api/packages/maxp/pypi/simple/ --extra-index-url https://pypi.org/ hdlbuild
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### CLI Commands
|
||||
@@ -155,14 +164,4 @@ The project includes GitHub workflows for building and deploying the package:
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please follow these steps:
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a new branch for your feature or bugfix.
|
||||
3. Submit a pull request.
|
||||
This project is licensed under the [MIT License](LICENSE). See the LICENSE file for details.
|
104
cliff.toml
Normal file
104
cliff.toml
Normal file
@@ -0,0 +1,104 @@
|
||||
# CLIFF_VERSION=2.8.0
|
||||
# git-cliff ~ default configuration file
|
||||
# https://git-cliff.org/docs/configuration
|
||||
#
|
||||
# Lines starting with "#" are comments.
|
||||
# Configuration options are organized into tables and keys.
|
||||
# See documentation for more information on available options.
|
||||
[remote.gitea]
|
||||
owner = "maxp"
|
||||
repo = "hdlbuild"
|
||||
|
||||
[changelog]
|
||||
# postprocessors
|
||||
postprocessors = [
|
||||
{ pattern = '<GITEA_URL>', replace = "https://git.0xmax42.io" }, # replace gitea url
|
||||
]
|
||||
|
||||
# template for the changelog header
|
||||
header = """
|
||||
# Changelog\n
|
||||
All notable changes to this project will be documented in this file.\n
|
||||
"""
|
||||
# template for the changelog body
|
||||
# https://keats.github.io/tera/docs/#introduction
|
||||
body = """
|
||||
{%- macro remote_url() -%}
|
||||
<GITEA_URL>/{{ remote.gitea.owner }}/{{ remote.gitea.repo }}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% if version %}\
|
||||
{% if previous.version %}\
|
||||
## [{{ version | trim_start_matches(pat="v") }}]\
|
||||
({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
{% else %}\
|
||||
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
{% endif %}\
|
||||
{% else %}\
|
||||
## [unreleased]
|
||||
{% endif %}\
|
||||
{% for group, commits in commits | group_by(attribute="group") %}
|
||||
### {{ group | striptags | trim | upper_first }}
|
||||
{% for commit in commits %}
|
||||
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
|
||||
{% if commit.breaking %}[**breaking**] {% endif %}\
|
||||
{{ commit.message | upper_first }} - \
|
||||
([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% endfor %}
|
||||
{% endfor %}\n
|
||||
"""
|
||||
# template for the changelog footer
|
||||
footer = """
|
||||
|
||||
"""
|
||||
# remove the leading and trailing s
|
||||
trim = true
|
||||
|
||||
# render body even when there are no releases to process
|
||||
# render_always = true
|
||||
# output file path
|
||||
# output = "test.md"
|
||||
|
||||
[git]
|
||||
# parse the commits based on https://www.conventionalcommits.org
|
||||
conventional_commits = true
|
||||
# filter out the commits that are not conventional
|
||||
filter_unconventional = true
|
||||
# process each line of a commit as an individual commit
|
||||
split_commits = false
|
||||
# regex for preprocessing the commit messages
|
||||
commit_preprocessors = [
|
||||
# Replace issue numbers
|
||||
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
|
||||
# Check spelling of the commit with https://github.com/crate-ci/typos
|
||||
# If the spelling is incorrect, it will be automatically fixed.
|
||||
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
|
||||
]
|
||||
# regex for parsing and grouping commits
|
||||
commit_parsers = [
|
||||
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
|
||||
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
|
||||
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
|
||||
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
|
||||
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
|
||||
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
|
||||
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
|
||||
{ message = "^chore\\(changelog\\)", skip = true },
|
||||
{ message = "^chore\\(version\\)", skip = true },
|
||||
{ message = "^chore\\(release\\): prepare for", skip = true },
|
||||
{ message = "^chore\\(deps.*\\)", skip = true },
|
||||
{ message = "^chore\\(pr\\)", skip = true },
|
||||
{ message = "^chore\\(pull\\)", skip = true },
|
||||
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
|
||||
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
|
||||
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
|
||||
{ message = ".*", group = "<!-- 10 -->💼 Other" },
|
||||
]
|
||||
# Regex to select git tags that represent releases.
|
||||
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
|
||||
# filter out the commits that are not matched by commit parsers
|
||||
filter_commits = false
|
||||
# sort the tags topologically
|
||||
topo_order = false
|
||||
# sort the commits inside sections by oldest/newest order
|
||||
sort_commits = "newest"
|
@@ -1,14 +1,12 @@
|
||||
[tool.poetry]
|
||||
name = "hdlbuild"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
description = "Flexible FPGA Build System"
|
||||
authors = ["0xMax42 <Mail@0xMax42.io>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
packages = [{ include = "hdlbuild", from = "src" }]
|
||||
include = [
|
||||
"src/hdlbuild/templates/*"
|
||||
]
|
||||
include = ["src/hdlbuild/templates/*"]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
hdlbuild = "hdlbuild.cli:main"
|
||||
@@ -20,6 +18,7 @@ pydantic = "^2.11.3"
|
||||
rich = "^14.0.0"
|
||||
gitpython = "^3.1.44"
|
||||
typer = "^0.16.0"
|
||||
jinja2 = "^3.1.6"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
twine = "^6.1.0"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import typer
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
|
||||
from hdlbuild.commands.gen import cli as gen_cli
|
||||
from hdlbuild.commands.build import cli as build_cli
|
||||
from hdlbuild.commands.clean import cli as clean_cli
|
||||
from hdlbuild.commands.dep import cli as dep_cli
|
||||
@@ -18,12 +19,12 @@ app = typer.Typer(
|
||||
help=f"hdlbuild v{get_version()} – Build‑Management for FPGA projects"
|
||||
)
|
||||
|
||||
# Unter‑Kommandos registrieren (entspricht add_subparsers)
|
||||
app.add_typer(build_cli, name="build", help="Build the project")
|
||||
app.add_typer(clean_cli, name="clean", help="Clean build artifacts")
|
||||
app.add_typer(dep_cli, name="dep", help="Resolve dependencies")
|
||||
app.add_typer(test_cli, name="test", help="Run simulations/testbenches")
|
||||
app.add_typer(init_cli, name="init", help="Initialize project")
|
||||
app.add_typer(gen_cli, name="gen", help="Generate HDL files from templates")
|
||||
|
||||
def main():
|
||||
app()
|
||||
|
53
src/hdlbuild/commands/gen.py
Normal file
53
src/hdlbuild/commands/gen.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typer
|
||||
|
||||
from hdlbuild.generate.template_generator import TemplateGenerator
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
from hdlbuild.utils.project_loader import load_project_config
|
||||
|
||||
cli = typer.Typer(rich_help_panel="🧬 Template Commands")
|
||||
|
||||
@cli.command("list")
|
||||
def list_templates() -> None:
|
||||
"""
|
||||
List all available template names from *project.yml*.
|
||||
|
||||
```bash
|
||||
hdlbuild gen list
|
||||
```
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
project = load_project_config()
|
||||
TemplateGenerator.list_templates(project, console)
|
||||
|
||||
|
||||
@cli.callback(invoke_without_command=True)
|
||||
def gen(
|
||||
ctx: typer.Context,
|
||||
name: str = typer.Option(
|
||||
None,
|
||||
"--name",
|
||||
"-n",
|
||||
help="Name of the template to generate (from project.yml)",
|
||||
show_default=False,
|
||||
),
|
||||
dry_run: bool = typer.Option(
|
||||
False,
|
||||
"--dry-run",
|
||||
help="Only show the output without writing file",
|
||||
),
|
||||
) -> None:
|
||||
"""
|
||||
Render HDL files from Jinja2 templates.
|
||||
|
||||
* `hdlbuild gen` → render all templates
|
||||
* `hdlbuild gen <name>` → render a specific template
|
||||
* `hdlbuild gen <name> --dry-run` → only show output without saving
|
||||
"""
|
||||
console = ConsoleUtils("hdlbuild")
|
||||
project = load_project_config()
|
||||
|
||||
# Only executed when no subcommand (e.g., "list") is active.
|
||||
if ctx.invoked_subcommand is None:
|
||||
TemplateGenerator.generate(project, name, dry_run, console)
|
120
src/hdlbuild/generate/template_generator.py
Normal file
120
src/hdlbuild/generate/template_generator.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
hdlbuild.generate.template_generator
|
||||
====================================
|
||||
|
||||
Enthält die Klasse :class:`TemplateGenerator`, die das Auflisten und Rendern
|
||||
von in *project.yml* definierten Jinja2-Templates kapselt.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from hdlbuild.models.templates import TemplateInstance
|
||||
from hdlbuild.utils.console_utils import ConsoleUtils
|
||||
|
||||
|
||||
class TemplateGenerator:
|
||||
"""
|
||||
Hilfsklasse zum Auflisten und Rendern der im Projekt konfigurierten
|
||||
Jinja2-Templates.
|
||||
"""
|
||||
|
||||
# --------------------------------------------------------------------- #
|
||||
# Öffentliche API
|
||||
# --------------------------------------------------------------------- #
|
||||
|
||||
@staticmethod
|
||||
def list_templates(project, console: ConsoleUtils) -> None:
|
||||
"""
|
||||
Alle in *project.yml* definierten Templates auflisten.
|
||||
"""
|
||||
if not project.templates:
|
||||
console.print("[yellow]No templates defined in project.yml")
|
||||
return
|
||||
|
||||
console.print("[bold underline]Available Templates:")
|
||||
for name in project.templates.root.keys():
|
||||
console.print(f"• {name}")
|
||||
|
||||
@classmethod
|
||||
def generate(
|
||||
cls,
|
||||
project,
|
||||
name: Optional[str],
|
||||
dry_run: bool,
|
||||
console: ConsoleUtils,
|
||||
) -> None:
|
||||
"""
|
||||
Templates erzeugen.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
project
|
||||
Geladenes Projekt-Model.
|
||||
name
|
||||
Name eines einzelnen Templates oder *None*, um alle Templates
|
||||
zu erzeugen.
|
||||
dry_run
|
||||
Wenn *True*, wird das gerenderte Ergebnis nur ausgegeben,
|
||||
jedoch nicht auf die Festplatte geschrieben.
|
||||
console
|
||||
Farbige Konsolen-Ausgaben.
|
||||
"""
|
||||
if not project.templates:
|
||||
console.print("[red]No templates defined in project.yml")
|
||||
return
|
||||
|
||||
templates = project.templates.root
|
||||
|
||||
if name:
|
||||
# Ein bestimmtes Template
|
||||
if name not in templates:
|
||||
console.print(f"[red]Template '{name}' not found.")
|
||||
return
|
||||
cls._render_template(name, templates[name], dry_run, console)
|
||||
else:
|
||||
# Alle Templates durchlaufen
|
||||
for tname, template in templates.items():
|
||||
cls._render_template(tname, template, dry_run, console)
|
||||
|
||||
# --------------------------------------------------------------------- #
|
||||
# Interne Helfer
|
||||
# --------------------------------------------------------------------- #
|
||||
|
||||
@staticmethod
|
||||
def _render_template(
|
||||
name: str,
|
||||
template: TemplateInstance,
|
||||
dry_run: bool,
|
||||
console: ConsoleUtils,
|
||||
) -> None:
|
||||
"""
|
||||
Einzelnes Template rendern und wahlweise speichern.
|
||||
"""
|
||||
template_path = template.template
|
||||
output_path = template.output
|
||||
variables = template.variables
|
||||
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(os.path.dirname(template_path)),
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
)
|
||||
j2 = env.get_template(os.path.basename(template_path))
|
||||
result = j2.render(**variables)
|
||||
|
||||
if dry_run:
|
||||
console.print(f"[green]--- Template: {name} (dry-run) ---")
|
||||
console.print(result)
|
||||
console.print(f"[green]--- End of {name} ---")
|
||||
return
|
||||
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
with open(output_path, "w") as f:
|
||||
f.write(result)
|
||||
|
||||
console.print(f"[cyan]✔ Rendered template '{name}' → {output_path}")
|
@@ -1,6 +1,8 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional
|
||||
|
||||
from hdlbuild.models.templates import ProjectTemplates
|
||||
|
||||
class SourceFile(BaseModel):
|
||||
path: str
|
||||
library: str = "work" # Default auf 'work'
|
||||
@@ -43,6 +45,7 @@ class ProjectConfig(BaseModel):
|
||||
sources: Sources
|
||||
testbenches: Optional[Testbenches] = None
|
||||
constraints: Optional[str] = None
|
||||
templates: Optional[ProjectTemplates] = None
|
||||
build: Optional[BuildOptions] = None
|
||||
dependencies: Optional[List[Dependency]] = Field(default_factory=list)
|
||||
tool_options: Optional[ToolOptions] = ToolOptions()
|
||||
|
14
src/hdlbuild/models/templates.py
Normal file
14
src/hdlbuild/models/templates.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from pydantic import BaseModel, Field, RootModel
|
||||
from typing import Dict, Any
|
||||
|
||||
class TemplateInstance(BaseModel):
|
||||
template: str # Pfad zur Jinja2-Vorlage
|
||||
output: str # Zielpfad
|
||||
variables: Dict[str, Any] = Field(default_factory=dict) # Variablen für Rendering
|
||||
|
||||
class ProjectTemplates(RootModel):
|
||||
"""
|
||||
Pydantic-RootModel, das die Mapping-Struktur *name → TemplateInstance*
|
||||
kapselt. In Pydantic v2 ersetzt `RootModel` die frühere `__root__`-Syntax.
|
||||
"""
|
||||
root: Dict[str, TemplateInstance] # key = Name wie „alu“, „control_unit“
|
Reference in New Issue
Block a user