diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1b66ddc..6fe23c7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,13 +8,13 @@ assignees: '' --- **Describe the bug** -A clear and concise description of what the bug is. +A clear and concise description of the bug. -**To Reproduce** +**Steps to reproduce** Steps to reproduce the behavior: 1. Go to '...' -2. Click on '....' -3. Scroll down to '....' +2. Run '...' +3. Provide '...' 4. See error **Expected behavior** @@ -23,9 +23,9 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** +**Environment (please complete the following information):** - OS: [e.g. Linux Fedora 43, Windows 11...] - - Version [e.g. v0.1.1] + - Version: [e.g. v0.1.1] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..b68f870 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Modelblocks project website + url: https://www.modelblocks.org/ + about: Consult our website for general information on our initiative. + - name: Documentation + url: https://modelblocks.readthedocs.io/ + about: Read our documentation for general questions and guidelines. + - name: Community chat + url: https://calliope-modelblocks.zulipchat.com + about: Reach out to our community on our official Zulip chat. diff --git a/.github/workflows/template-check-version.yml b/.github/workflows/template-check-version.yml index f99b70b..273b06a 100644 --- a/.github/workflows/template-check-version.yml +++ b/.github/workflows/template-check-version.yml @@ -1,54 +1,76 @@ -# Adapted from https://github.com/arup-group/actions-city-modelling-lab/blob/main/.github/workflows/template-check.yml -# MIT licensed +name: Deprecated reusable workflow for checking template updates -name: Reusable workflow for keeping up-to-date with template changes. on: workflow_call: defaults: run: - shell: bash -l {0} + shell: bash --noprofile --norc -euo pipefail {0} jobs: - copier-issue: + copier-check-update: runs-on: ubuntu-latest permissions: contents: read issues: write env: - UPDATE: false + ISSUE_TITLE: Template update workflow migration required steps: - uses: actions/checkout@v4 + - name: Warn about deprecated reusable workflow + run: | + echo "::warning title=Deprecated template check workflow::This reusable workflow and the data-module-template 'latest' tag update mechanism are deprecated. This compatibility workflow will be removed in the next template release. Update this project from the template to use the generated self-contained check-version workflow." + - name: Install uv uses: astral-sh/setup-uv@v5 - - name: Add dummy GitHub credentials + - name: Check for template updates + id: copier_check_update run: | - git config --global user.name Copier update - git config --global user.email check@dummy.bot.com + update_json="$(uvx --from 'copier>=9.15.2' copier check-update --output-format json)" + echo "$update_json" + { + echo "update_available=$(jq -r '.update_available // false' <<< "$update_json")" + echo "current_version=$(jq -r '.current_version // "unknown"' <<< "$update_json")" + echo "latest_version=$(jq -r '.latest_version // "unknown"' <<< "$update_json")" + } >> "$GITHUB_OUTPUT" - - name: Run copier update - run: uvx copier update --skip-answered --defaults + - name: Check for existing migration issue + id: existing_issue + run: | + issue_count="$(gh issue list \ + --repo "${{ github.repository }}" \ + --state open \ + --search "$ISSUE_TITLE in:title" \ + --json title \ + --jq "map(select(.title == \"$ISSUE_TITLE\")) | length")" + if [[ "$issue_count" -gt 0 ]]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Wait a bit for copier cleanups - run: sleep 10s + - name: Open migration issue + if: steps.existing_issue.outputs.exists != 'true' + run: | + gh issue create \ + --repo "${{ github.repository }}" \ + --title "$ISSUE_TITLE" \ + --body "This project is still using the deprecated reusable Modelblocks template update workflow through the data-module-template \`latest\` tag. - - name: Display differences - run: git status --porcelain + Template update available: ${{ steps.copier_check_update.outputs.update_available }} + Current template version: ${{ steps.copier_check_update.outputs.current_version }} + Latest template version: ${{ steps.copier_check_update.outputs.latest_version }} - - name: Set update flag if changes occurred - run: test -z "$(git status --porcelain)" || echo "UPDATE=true" >> $GITHUB_ENV + Please update this project from the template: - - name: Show environment variables - run: | - echo "$UPDATE" + \`\`\`shell + copier update --skip-answered --defaults + \`\`\` - - name: Open issue - if: env.UPDATE == true - run: | - gh issue --repo ${{ github.repository }} \ - create --title "Template update" \ - --body "A new version of the template has been published. Please update this project using \`copier update --skip-answered --defaults\` and review changes. If you need help, refer to the Modelblocks documentation or reach out to the project team." + The update will migrate this repository to a self-contained \`.github/workflows/check-version.yml\` workflow. This compatibility workflow will be removed in the next template release." env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/template-release.yml b/.github/workflows/template-release.yml index 07fb57b..faae424 100644 --- a/.github/workflows/template-release.yml +++ b/.github/workflows/template-release.yml @@ -3,15 +3,14 @@ name: Reusable release workflow for modules on: workflow_call: +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + jobs: - add-latest-tag: + latest-tag-deprecation-warning: runs-on: ubuntu-latest - permissions: - contents: write steps: - - uses: actions/checkout@v4 - - name: Run latest-tag - uses: EndBug/latest-tag@v1.6.2 - with: - # Name of the tag. - ref: latest + - name: Warn about removed latest tag management + run: | + echo "::warning title=Deprecated template release workflow::The Modelblocks data module template no longer manages a 'latest' tag for generated projects. This compatibility workflow will be removed in the next template release. Update this project from the template to remove the generated release workflow." diff --git a/CITATION.cff b/CITATION.cff index 8d13e52..ba7f05f 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -10,3 +10,6 @@ authors: - given-names: Ivan family-names: Ruiz Manuel orcid: https://orcid.org/0000-0003-2288-6423 + - given-names: Stefan + family-names: Pfenninger-Lee + orcid: https://orcid.org/0000-0002-8420-9498 diff --git a/copier.yaml b/copier.yaml index 443dccb..d404f8f 100644 --- a/copier.yaml +++ b/copier.yaml @@ -1,9 +1,9 @@ # === copier settings === -_min_copier_version: '9.4.1' +_min_copier_version: '9.15.2' _subdirectory: template _exclude: # copier defaults - - "copier.yaml" + - "copier.yml" - "copier.yaml" - "~*" - "*.py[co]" @@ -11,13 +11,17 @@ _exclude: - ".git" - ".DS_Store" - ".svn" - # Do not add the dummy example when updating - - "{% if _copier_operation == 'update' -%}workflow/envs/shell.yaml{% endif %}" - - "{% if _copier_operation == 'update' -%}workflow/internal/config.schema.yaml{% endif %}" - - "{% if _copier_operation == 'update' -%}workflow/internal/settings.yaml{% endif %}" - - "{% if _copier_operation == 'update' -%}workflow/rules/automatic.smk{% endif %}" - - "{% if _copier_operation == 'update' -%}workflow/rules/dummy.smk{% endif %}" - - "{% if _copier_operation == 'update' -%}workflow/scripts/dummy_script.py{% endif %}" + # Respect user-owned files and folders during updates. + - "{% if _copier_operation == 'update' -%}workflow{% endif %}" + - "{% if _copier_operation == 'update' -%}resources{% endif %}" + - "{% if _copier_operation == 'update' -%}figures{% endif %}" + - "{% if _copier_operation == 'update' -%}results{% endif %}" + - "{% if _copier_operation == 'update' -%}config/**{% endif %}" + - "{% if _copier_operation == 'update' -%}tests/**{% endif %}" + # Special cases within the above that should not be excluded. + - "{% if _copier_operation == 'update' -%}!config/README.md{% endif %}" + - "{% if _copier_operation == 'update' -%}!tests/integration_test.py{% endif %}" + - "{% if _copier_operation == 'update' -%}!workflow/profiles/default/config.yaml{% endif %}" _skip_if_exists: # Respect user changes, except for a few standard files. - config/** @@ -26,13 +30,10 @@ _skip_if_exists: - results/** - tests/** - workflow/** - - .readthedocs.yaml - - AUTHORS - - CITATION.cff - - INTERFACE.yaml - # Special cases: these files should not be user-modified, even though they are in user-modifiable directories + # Special cases within the above that should not be skipped. - "!config/README.md" - "!tests/integration_test.py" + - "!workflow/profiles/default/config.yaml" _templates_suffix: .jinja _answers_file: .copier-answers.yml diff --git a/pixi.lock b/pixi.lock index 39e348b..6bef3f6 100644 --- a/pixi.lock +++ b/pixi.lock @@ -47,7 +47,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.15.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.23.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda @@ -90,7 +90,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.15.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.23.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda @@ -161,7 +161,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.15.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.23.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda @@ -670,9 +670,9 @@ packages: license_family: BSD size: 27011 timestamp: 1733218222191 -- conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.1-pyhcf101f3_0.conda - sha256: 93d1b3ba43d15010c95938b491f11d53d058a8466aa11a0a7bb51ae8cf70d3e5 - md5: b5ddb42e9d800aacf070135d14b9423b +- conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.15.2-pyhcf101f3_0.conda + sha256: 9846978f01b09371e058bb795c73f246d351891871b17f6e5509e9249d947895 + md5: 7da0d461ad7410ff9fd5607da65cd8ed depends: - python >=3.10 - colorama >=0.4.6 @@ -693,8 +693,10 @@ packages: - typing_extensions >=4.0.0,<5.0.0 - python license: MIT - size: 56002 - timestamp: 1774962797854 + license_family: MIT + run_exports: {} + size: 58647 + timestamp: 1781335499983 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.3-pyhcf101f3_0.conda sha256: e2753997b8bd34205f42be01b8bab8037423dc30c02a1ec12de23e5b4c0b0a2e md5: 58638f77697c4f6726753eb8be34818b diff --git a/pixi.toml b/pixi.toml index fecd0e5..84b81b7 100644 --- a/pixi.toml +++ b/pixi.toml @@ -10,7 +10,7 @@ homepage = "https://www.modelblocks.org/" [dependencies] python = ">=3.12" -copier = ">=9.5.0" +copier = ">=9.15.2" mypy = ">=1.15.0" pytest = ">=8.3.5" ruff = ">=0.9.9" diff --git a/template/.github/ISSUE_TEMPLATE/bug_report.md b/template/.github/ISSUE_TEMPLATE/bug_report.md index 1b66ddc..6fe23c7 100644 --- a/template/.github/ISSUE_TEMPLATE/bug_report.md +++ b/template/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,13 +8,13 @@ assignees: '' --- **Describe the bug** -A clear and concise description of what the bug is. +A clear and concise description of the bug. -**To Reproduce** +**Steps to reproduce** Steps to reproduce the behavior: 1. Go to '...' -2. Click on '....' -3. Scroll down to '....' +2. Run '...' +3. Provide '...' 4. See error **Expected behavior** @@ -23,9 +23,9 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** +**Environment (please complete the following information):** - OS: [e.g. Linux Fedora 43, Windows 11...] - - Version [e.g. v0.1.1] + - Version: [e.g. v0.1.1] **Additional context** Add any other context about the problem here. diff --git a/template/.github/ISSUE_TEMPLATE/config.yaml b/template/.github/ISSUE_TEMPLATE/config.yaml deleted file mode 100644 index 28da94d..0000000 --- a/template/.github/ISSUE_TEMPLATE/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: General information - url: https://www.modelblocks.org/ - about: Please consult our website for general information on the Modelblocks methodology. diff --git a/template/.github/ISSUE_TEMPLATE/config.yml b/template/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..b68f870 --- /dev/null +++ b/template/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Modelblocks project website + url: https://www.modelblocks.org/ + about: Consult our website for general information on our initiative. + - name: Documentation + url: https://modelblocks.readthedocs.io/ + about: Read our documentation for general questions and guidelines. + - name: Community chat + url: https://calliope-modelblocks.zulipchat.com + about: Reach out to our community on our official Zulip chat. diff --git a/template/.github/workflows/check-version.yml b/template/.github/workflows/check-version.yml index 7bde778..04e53c3 100644 --- a/template/.github/workflows/check-version.yml +++ b/template/.github/workflows/check-version.yml @@ -1,13 +1,75 @@ -# Check for changes in the upstream template. If changes are found, an issue is created -name: Template check. +# Check for changes in the upstream template. If changes are found, an issue is created. +name: Template check + on: workflow_dispatch: schedule: - cron: '0 0 1 * *' # Runs at 00:00 UTC on the 1st day of every month +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + jobs: - copier-update: + copier-check-update: + runs-on: ubuntu-latest permissions: contents: read issues: write - uses: modelblocks-org/data-module-template/.github/workflows/template-check-version.yml@latest + env: + ISSUE_TITLE: Template update available + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Check for template updates + id: copier_check_update + run: | + update_json="$(uvx --from 'copier>=9.15.2' copier check-update --output-format json)" + echo "$update_json" + { + echo "update_available=$(jq -r '.update_available // false' <<< "$update_json")" + echo "current_version=$(jq -r '.current_version // "unknown"' <<< "$update_json")" + echo "latest_version=$(jq -r '.latest_version // "unknown"' <<< "$update_json")" + } >> "$GITHUB_OUTPUT" + + - name: Check for existing update issue + if: steps.copier_check_update.outputs.update_available == 'true' + id: existing_issue + run: | + issue_count="$(gh issue list \ + --repo "${{ github.repository }}" \ + --state open \ + --search "$ISSUE_TITLE in:title" \ + --json title \ + --jq "map(select(.title == \"$ISSUE_TITLE\")) | length")" + if [[ "$issue_count" -gt 0 ]]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Open update issue + if: steps.copier_check_update.outputs.update_available == 'true' && steps.existing_issue.outputs.exists != 'true' + run: | + gh issue create \ + --repo "${{ github.repository }}" \ + --title "$ISSUE_TITLE" \ + --body "A new version of the Modelblocks data module template is available. + + Current template version: ${{ steps.copier_check_update.outputs.current_version }} + Latest template version: ${{ steps.copier_check_update.outputs.latest_version }} + + Please update this project using: + + \`\`\`shell + copier update --skip-answered --defaults + \`\`\` + + Review the resulting changes before merging them." + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/template/.github/workflows/release.yml b/template/.github/workflows/release.yml deleted file mode 100644 index 7e25226..0000000 --- a/template/.github/workflows/release.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Release - -on: - release: - types: [released] - -jobs: - release-workflow: - permissions: - contents: write - uses: modelblocks-org/data-module-template/.github/workflows/template-release.yml@latest diff --git a/tests/template_test.py b/tests/template_test.py index 3f07ca7..a710ea6 100644 --- a/tests/template_test.py +++ b/tests/template_test.py @@ -2,6 +2,7 @@ import subprocess from copy import deepcopy +from pathlib import Path import pytest from copier import run_copy @@ -49,3 +50,21 @@ def test_linting(self, pixi_built): assert subprocess.run( "pixi run snakemake --lint", shell=True, check=True, cwd=pixi_built ) + + def test_github_issue_templates(self, template_path: Path, template_project: Path): + """The issue templating and configuration should match at both levels.""" + issue_template_dir = Path(".github/ISSUE_TEMPLATE") + repo_path = template_path / issue_template_dir + temp_path = template_project / issue_template_dir + + repo_files = sorted(path.relative_to(repo_path) for path in repo_path.iterdir()) + temp_files = sorted(path.relative_to(temp_path) for path in temp_path.iterdir()) + assert temp_files == repo_files + + for relative_path in repo_files: + repo_file = repo_path / relative_path + generated_file = temp_path / relative_path + + assert repo_file.is_file() + assert generated_file.is_file() + assert generated_file.read_text() == repo_file.read_text()