diff --git a/.ai/coding-workspace.md b/.ai/coding-workspace.md new file mode 120000 index 0000000..af57a56 --- /dev/null +++ b/.ai/coding-workspace.md @@ -0,0 +1 @@ +/Users/tonyyan/Documents/GitHub/ai-common/coding-workspace.md \ No newline at end of file diff --git a/.ai/memory/MEMORY.md b/.ai/memory/MEMORY.md new file mode 100644 index 0000000..7df3116 --- /dev/null +++ b/.ai/memory/MEMORY.md @@ -0,0 +1,6 @@ +# Memory Index + +See `.ai/coding-workspace.md` for what belongs here vs `.ai/sessions/`. + +- [Ask before commit](feedback_ask_before_commit.md) — never `git commit` without asking first, even after verifying a fix works +- [Preferred address](user_preferred_address.md) — address the user as "🎓Tony" diff --git a/.ai/memory/feedback_ask_before_commit.md b/.ai/memory/feedback_ask_before_commit.md new file mode 100644 index 0000000..fe06152 --- /dev/null +++ b/.ai/memory/feedback_ask_before_commit.md @@ -0,0 +1,15 @@ +--- +name: feedback-ask-before-commit +description: "Always ask before running git commit, even when changes are verified and ready" +metadata: + node_type: memory + type: feedback + originSessionId: 83fa924c-d904-40df-ba15-ec9696cf8992 + modified: 2026-08-17T11:04:40.505Z +--- + +Never run `git commit` without asking Tony first, even after a fix has been implemented, tested, and verified. Staging, diffing, and reviewing changes (`git add`, `git status`, `git diff`) is fine without asking — only the commit itself requires explicit go-ahead each time. + +**Why:** Tony wants a final checkpoint before changes become permanent history, regardless of how confident the work is. This applies even in auto-mode / when otherwise encouraged to act without stopping for clarification. + +**How to apply:** After finishing and verifying a change, present the diff/summary and ask whether to commit, rather than committing and reporting after the fact. This is now codified in `.ai/coding-workspace.md` (symlinked from `ai-common/coding-workspace.md`), so it applies across all of Tony's repos, not just this one. diff --git a/.ai/memory/user_preferred_address.md b/.ai/memory/user_preferred_address.md new file mode 100644 index 0000000..9a5961d --- /dev/null +++ b/.ai/memory/user_preferred_address.md @@ -0,0 +1,11 @@ +--- +name: user-preferred-address +description: "Tony wants to be addressed as \"🎓Tony\" in conversation" +metadata: + node_type: memory + type: user + originSessionId: 83fa924c-d904-40df-ba15-ec9696cf8992 + modified: 2026-08-17T11:05:50.821Z +--- + +Address the user as "🎓Tony" (with the graduation-cap emoji prefix) when using their name in conversation, rather than plain "Tony". diff --git a/.github/workflows/dev-cicd.yml b/.github/workflows/dev-cicd.yml index 48916f1..773bc2e 100644 --- a/.github/workflows/dev-cicd.yml +++ b/.github/workflows/dev-cicd.yml @@ -3,14 +3,99 @@ name: CI CD on: push: - branches: "*" + branches: "*" tags: 'v*.*.*' +permissions: + contents: write + jobs: + get-version: + name: Detect version bump + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.compare.outputs.tag_name }} + should_release: ${{ steps.compare.outputs.should_release }} + steps: + - name: Checkout code + uses: actions/checkout@v7 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Set up Python + uses: actions/setup-python@v7 + with: + python-version: '3.12' + + - name: Install packaging + run: pip install packaging + + - name: Compare setup.py version against existing tags + id: compare + shell: python + run: | + import os + import re + import subprocess + from packaging.version import Version, InvalidVersion + + with open("setup.py") as f: + content = f.read() + match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content) + if not match: + raise SystemExit("Could not find version= in setup.py") + raw_version = match.group(1) + current = Version(raw_version) + tag_name = f"v{raw_version}" + + existing_tags = subprocess.run( + ["git", "tag", "-l"], capture_output=True, text=True, check=True + ).stdout.splitlines() + + latest_version = None + latest_tag = None + for t in existing_tags: + if not t.startswith("v"): + continue + try: + v = Version(t[1:]) + except InvalidVersion: + continue + if latest_version is None or v > latest_version: + latest_version = v + latest_tag = t + + print(f"setup.py version: {raw_version} ({tag_name})") + print(f"latest tagged version: {latest_tag or '(none)'}") + + if latest_version is not None and current < latest_version: + raise SystemExit( + f"::error::setup.py version {raw_version} is lower than latest " + f"tagged version {latest_tag}. Version must not decrease." + ) + + tag_exists = tag_name in existing_tags + on_main = os.environ["GITHUB_REF"] == "refs/heads/main" + should_release = on_main and not tag_exists + + if tag_exists: + print(f"Tag {tag_name} already exists, skipping release.") + elif not on_main: + print(f"Version {raw_version} not yet tagged, but not on main; skipping release.") + else: + print(f"Will tag and release {tag_name}.") + + gh_output = os.environ["GITHUB_OUTPUT"] + with open(gh_output, "a") as f: + f.write(f"tag_name={tag_name}\n") + f.write(f"should_release={'true' if should_release else 'false'}\n") + test: # name: Test on ${{ matrix.os }} - Python ${{ matrix.python-version }} name: Test on ${{ matrix.os }} py3.${{ matrix.python3-version }} runs-on: ${{ matrix.os }}-latest + timeout-minutes: 20 # continue-on-error: true # Allow continue to build even if tests fail continue-on-error: ${{ matrix.python3-version != '12' }} strategy: @@ -24,7 +109,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 # - name: Cache pip # uses: actions/cache@v4 @@ -64,7 +149,7 @@ jobs: echo "No dependencies to install on Windows." - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: 3.${{ matrix.python3-version }} cache: 'pip' @@ -91,31 +176,33 @@ jobs: else source venv/bin/activate fi - pytest -n 4 -v --cache-clear --cov=helab --junitxml=junit.xml -o junit_family=legacy + pytest -n 4 --verbose --cache-clear --cov=helab --junitxml=junit.xml -o junit_family=legacy --reruns 5 --reruns-delay 2 --timeout=120 --timeout-method=thread +# pytest -n 1 -v --cache-clear --cov=helab --junitxml=junit.xml -o junit_family=legacy # pytest -n 8 -v --cache-clear --cov=helab --junit-xml=pytest-report.xml - name: Upload Test Results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-results-${{ matrix.os }}-py3${{ matrix.python3-version }} path: junit.xml - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/test-results-action@v1 + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} + report_type: test_results - name: Run test with code coverage report if: matrix.python3-version == '12' && matrix.os == 'macos' run: | source venv/bin/activate - pytest --cov=helab --cov-report=xml --cov-report=term --ignore=tests/test_typing.py + pytest --cov=helab --cov-report=xml --cov-report=term --ignore=tests/test_typing.py --cache-clear --timeout=120 --timeout-method=thread - name: Upload Code Coverage Report if: matrix.python3-version == '12' && matrix.os == 'macos' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -138,7 +225,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 # - name: Cache pip # uses: actions/cache@v4 @@ -178,7 +265,7 @@ jobs: libopengl0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: 3.${{ matrix.python3-version }} cache: 'pip' @@ -208,7 +295,7 @@ jobs: pyinstaller HeLab.spec --clean -y - name: Archive build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: build-${{ matrix.os }}-py3${{ matrix.python3-version }} # name: build-${{ matrix.os }}-py${{ format('{0}', matrix.python-version) | replace('.','') }} @@ -223,23 +310,24 @@ jobs: release: name: Create Release on Tag - needs: [test, build] + needs: [get-version, test, build] + if: needs.get-version.outputs.should_release == 'true' runs-on: ubuntu-latest steps: - name: Download macOS Build Artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: build-macos-py312 path: dist/macos - name: Download Windows Build Artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: build-windows-py312 path: dist/windows - name: Download Ubuntu Build Artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: build-ubuntu-py312 path: dist/ubuntu @@ -260,9 +348,8 @@ jobs: zip -r -9 HeLab-ubuntu.zip HeLab - name: Prepare Release Body - # if: startsWith(github.ref, 'refs/tags/') run: | - RELEASE_BODY="## Release ${{ github.ref_name }} + RELEASE_BODY="## Release ${{ needs.get-version.outputs.tag_name }} **Date:** $(date +'%Y-%m-%d %H:%M') UTC @@ -275,23 +362,23 @@ jobs: - **macOS ARM64** build (Python 3.12) - **Windows Intel64** build (Python 3.12) - **Ubuntu Intel64** build (Python 3.12) - + **Note:** This is an automated release. Please verify builds before production use." - + echo "RELEASE_BODY<> $GITHUB_ENV echo "$RELEASE_BODY" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - + echo "Release body preview:" echo "$RELEASE_BODY" - - name: Create Release and Upload Assets on Tag - if: github.ref_type == 'tag' + - name: Create Release and Upload Assets on Version Bump uses: ncipollo/release-action@v1 with: - token: ${{ secrets.PAT_TOKEN }} - tag: ${{ github.ref_name }} - name: "Automated Release ${{ github.ref_name }}" + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ needs.get-version.outputs.tag_name }} + commit: ${{ github.sha }} + name: "Automated Release ${{ needs.get-version.outputs.tag_name }}" body: ${{ env.RELEASE_BODY }} draft: true prerelease: true @@ -300,18 +387,3 @@ jobs: dist/windows/HeLab-windows.zip dist/ubuntu/HeLab-ubuntu.zip - # - name: Create Release and Upload Assets on Tag 2 - # if: startsWith(github.ref, 'refs/tags/') - # uses: ncipollo/release-action@v1 - # with: - # token: ${{ secrets.PAT_TOKEN }} - # tag: ${{ github.ref_name }} - # name: "Automated Release ${{ github.ref_name }}" - # body: ${{ env.RELEASE_BODY }} - # draft: true - # prerelease: true - # artifacts: | - # dist/macos/HeLab-macos.zip - # dist/windows/HeLab-windows.zip - # dist/ubuntu/HeLab-ubuntu.zip - diff --git a/.gitignore b/.gitignore index 998a976..aa4cbc8 100644 --- a/.gitignore +++ b/.gitignore @@ -72,7 +72,7 @@ CMakeLists.txt.user* *.dll *.exe -.idea +# .idea .qtcreator env HELIUM.pyproject* @@ -86,3 +86,24 @@ Icon? helab.egg-info coverage.xml .coverage +helab_tempfiles +helab_caches +helab_temps +*.ipynb +coverage.xml +helab_main.prof +*.log +mprofile_*.dat +profile.html +profile.json +.pytest_cache +.idea/misc.xml +.idea/modules.xml +.coverage.* +.coverage* +junit.xml +_server alias/ +.ai/temp/ +venv +side_projects/rga_visualiser/RGAData +venv.nosync diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..37427d5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "legacy/tdc_autoconverter"] + path = legacy/tdc_autoconverter + url = https://github.com/HeBECANU/tdc_autoconverter + branch = master diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/HeLab.iml b/.idea/HeLab.iml new file mode 100644 index 0000000..b2dbe32 --- /dev/null +++ b/.idea/HeLab.iml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml new file mode 100644 index 0000000..4ea72a9 --- /dev/null +++ b/.idea/copilot.data.migration.agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml new file mode 100644 index 0000000..7ef04e2 --- /dev/null +++ b/.idea/copilot.data.migration.ask.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 0000000..1f2ea11 --- /dev/null +++ b/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml new file mode 100644 index 0000000..8648f94 --- /dev/null +++ b/.idea/copilot.data.migration.edit.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/tonyyan.xml b/.idea/dictionaries/tonyyan.xml new file mode 100644 index 0000000..21dfded --- /dev/null +++ b/.idea/dictionaries/tonyyan.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..dd4c951 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/mypy.xml b/.idea/mypy.xml new file mode 100644 index 0000000..2cc139e --- /dev/null +++ b/.idea/mypy.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/pyLspTools.xml b/.idea/pyLspTools.xml new file mode 100644 index 0000000..a6e6b32 --- /dev/null +++ b/.idea/pyLspTools.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/PyInstaller.xml b/.idea/runConfigurations/PyInstaller.xml index babae75..c3d3b11 100644 --- a/.idea/runConfigurations/PyInstaller.xml +++ b/.idea/runConfigurations/PyInstaller.xml @@ -8,7 +8,7 @@