Skip to content

Commit a42ac30

Browse files
committed
feat: Update scripts with new metadata injection setup
1 parent 30023ef commit a42ac30

13 files changed

+2560
-296
lines changed

.github/workflows/update-metadata.yml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,116 @@ jobs:
109109
else
110110
echo "No metadata changes detected"
111111
fi
112+
name: Update Metadata
113+
114+
on:
115+
push:
116+
workflow_dispatch:
117+
118+
# Prevent infinite loops when workflow commits changes
119+
concurrency:
120+
group: ${{ github.workflow }}-${{ github.ref }}
121+
cancel-in-progress: true
122+
123+
jobs:
124+
update-metadata:
125+
runs-on: ubuntu-latest
126+
# Need write permission to push changes
127+
permissions:
128+
contents: write
129+
steps:
130+
- name: Checkout repository
131+
uses: actions/checkout@v4
132+
with:
133+
ref: ${{ github.ref }}
134+
fetch-depth: 0
135+
136+
- name: Extract version from tag (if triggered by tag)
137+
id: extract_version
138+
run: |
139+
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
140+
TAG_NAME=${GITHUB_REF#refs/tags/}
141+
# Remove 'v' prefix if present (v1.0.0 -> 1.0.0)
142+
VERSION=${TAG_NAME#v}
143+
echo "version=$VERSION" >> $GITHUB_OUTPUT
144+
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
145+
echo "is_tag=true" >> $GITHUB_OUTPUT
146+
echo "Triggered by version tag: $TAG_NAME (version: $VERSION)"
147+
else
148+
echo "is_tag=false" >> $GITHUB_OUTPUT
149+
echo "Triggered by regular push to: $GITHUB_REF"
150+
fi
151+
152+
- name: Set up Python
153+
uses: actions/setup-python@v5
154+
with:
155+
python-version-file: '.python-version'
156+
cache: pip
157+
158+
- name: Install dependencies
159+
run: |
160+
python -m pip install --upgrade pip
161+
pip install pyyaml rdflib
162+
163+
- name: Update version metadata (if triggered by tag)
164+
if: steps.extract_version.outputs.is_tag == 'true'
165+
env:
166+
TAG_VERSION: ${{ steps.extract_version.outputs.version }}
167+
run: |
168+
python -m quadriga.metadata.update_version_from_tag
169+
170+
- name: Update metadata files
171+
env:
172+
PYTHONHASHSEED: 0
173+
run: python -m quadriga.metadata.run_all
174+
175+
- name: Check if files changed
176+
id: check_changes
177+
run: |
178+
if git diff --quiet metadata.yml && git diff --quiet CITATION.bib && git diff --quiet CITATION.cff && git diff --quiet .zenodo.json && git diff --quiet metadata.jsonld && git diff --quiet metadata.rdf; then
179+
echo "changes_detected=false" >> $GITHUB_OUTPUT
180+
else
181+
echo "changes_detected=true" >> $GITHUB_OUTPUT
182+
fi
183+
184+
- name: Commit changes (regular push)
185+
if: steps.check_changes.outputs.changes_detected == 'true' && steps.extract_version.outputs.is_tag == 'false'
186+
run: |
187+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
188+
git config --local user.name "github-actions[bot]"
189+
git add metadata.yml CITATION.bib CITATION.cff .zenodo.json metadata.jsonld metadata.rdf
190+
git commit -m "[Automated] Update metadata files"
191+
git push
192+
193+
- name: Commit changes and move tag (tag-triggered)
194+
if: steps.check_changes.outputs.changes_detected == 'true' && steps.extract_version.outputs.is_tag == 'true'
195+
run: |
196+
# Configure git
197+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
198+
git config --local user.name "github-actions[bot]"
199+
200+
# Commit the metadata changes
201+
git add metadata.yml CITATION.bib CITATION.cff .zenodo.json metadata.jsonld metadata.rdf
202+
git commit -m "[Automated] Update metadata for version ${{ steps.extract_version.outputs.version }}"
203+
204+
# Delete the old tag (locally and remotely)
205+
git tag -d ${{ steps.extract_version.outputs.tag_name }}
206+
git push origin :refs/tags/${{ steps.extract_version.outputs.tag_name }}
207+
208+
# Create new tag at the current commit (with updated metadata)
209+
git tag ${{ steps.extract_version.outputs.tag_name }}
210+
211+
# Push the changes and the new tag
212+
git push origin HEAD:main
213+
git push origin ${{ steps.extract_version.outputs.tag_name }}
214+
215+
echo "Tag ${{ steps.extract_version.outputs.tag_name }} moved to commit with updated metadata"
216+
217+
- name: No changes needed
218+
if: steps.check_changes.outputs.changes_detected == 'false'
219+
run: |
220+
if [[ "${{ steps.extract_version.outputs.is_tag }}" == "true" ]]; then
221+
echo "Metadata already matches the tag version - no changes needed"
222+
else
223+
echo "No metadata changes detected"
224+
fi

dev-requirements.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
pyyaml
1+
pyyaml
2+
mypy
3+
ruff
4+
types-PyYAML
5+
rdflib

quadriga/metadata/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@
77

88
__all__ = [
99
"create_bibtex",
10-
"update_citation_cff",
1110
"extract_from_book_config",
11+
"update_citation_cff",
1212
"update_version_from_tag",
1313
"utils",
1414
]
1515

1616
# Import the modules to make their functions available
17-
from . import create_bibtex
18-
from . import update_citation_cff
19-
from . import extract_from_book_config
20-
from . import update_version_from_tag
21-
from . import utils
17+
from . import (
18+
create_bibtex,
19+
extract_from_book_config,
20+
update_citation_cff,
21+
update_version_from_tag,
22+
utils,
23+
)

quadriga/metadata/create_bibtex.py

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
from __future__ import annotations
2+
13
import logging
24
import sys
3-
from pathlib import Path
45

56
from .utils import (
67
extract_keywords,
@@ -11,6 +12,7 @@
1112
)
1213

1314
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
15+
logger = logging.getLogger(__name__)
1416

1517
# Map CFF types to BibTeX entry types
1618
CFF_TO_BIBTEX_TYPES = {
@@ -70,14 +72,15 @@
7072
}
7173

7274

73-
def create_bibtex_from_cff():
75+
def create_bibtex_from_cff() -> bool | None:
7476
"""
75-
Creates a CITATION.bib file from CITATION.cff.
77+
Create a CITATION.bib file from CITATION.cff.
7678
77-
It reads citation data, prioritizing the 'preferred-citation' block if available,
79+
Reads citation data, prioritizing the 'preferred-citation' block if available,
7880
formats authors, generates a citation key, and constructs a BibTeX entry.
7981
80-
Returns:
82+
Returns
83+
-------
8184
bool: True if successful, False otherwise.
8285
"""
8386
try:
@@ -86,28 +89,31 @@ def create_bibtex_from_cff():
8689
repo_root = get_file_path("") # Get repo root
8790
citation_cff_path = get_file_path("CITATION.cff", repo_root)
8891
citation_bib_path = get_file_path("CITATION.bib", repo_root)
89-
except Exception as e:
90-
logging.error(f"Failed to resolve file paths: {str(e)}")
92+
except Exception:
93+
logger.exception("Failed to resolve file paths")
9194
return False
9295

9396
# Check if citation_cff_path exists
94-
if not Path(citation_cff_path).exists():
95-
logging.error(f"CITATION.cff file not found at {citation_cff_path}")
97+
if not citation_cff_path.exists():
98+
logger.error("CITATION.cff file not found at %s", citation_cff_path)
9699
return False
97100

98101
# Read CITATION.cff using utility function
99102
citation_data = load_yaml_file(citation_cff_path)
100103

101-
if not citation_data:
102-
logging.error(f"Could not load {citation_cff_path}. Exiting.")
104+
if not citation_data or not isinstance(citation_data, dict):
105+
logger.error("Could not load CITATION.cff or invalid format. Exiting.")
103106
return False
104107

105108
# Extract data from preferred-citation or root
106109
if "preferred-citation" in citation_data:
107-
logging.info("Using 'preferred-citation' section from CITATION.cff")
110+
logger.info("Using 'preferred-citation' section from CITATION.cff")
108111
pref = citation_data.get("preferred-citation")
112+
if not isinstance(pref, dict):
113+
logger.error("preferred-citation is not a dictionary")
114+
return False
109115
else:
110-
logging.info("No 'preferred-citation' section found, using root data")
116+
logger.info("No 'preferred-citation' section found, using root data")
111117
pref = citation_data
112118

113119
# Validate required fields
@@ -116,19 +122,19 @@ def create_bibtex_from_cff():
116122
year = str(pref.get("year", "")) # Ensure year is a string for generate_citation_key
117123

118124
if not authors:
119-
logging.warning("No authors found in CITATION.cff")
125+
logger.warning("No authors found in CITATION.cff")
120126

121127
if title == "Untitled":
122-
logging.warning("No title found in CITATION.cff, using 'Untitled'")
128+
logger.warning("No title found in CITATION.cff, using 'Untitled'")
123129

124130
if not year:
125-
logging.warning("No year found in CITATION.cff")
131+
logger.warning("No year found in CITATION.cff")
126132

127133
# Use utility function to format authors
128134
try:
129135
author_str = format_authors_for_bibtex(authors)
130-
except Exception as e:
131-
logging.error(f"Error formatting authors: {str(e)}")
136+
except Exception:
137+
logger.exception("Error formatting authors")
132138
author_str = ""
133139

134140
# Choose entry type based on type field
@@ -141,19 +147,19 @@ def create_bibtex_from_cff():
141147
if entry_type == "thesis":
142148
# Check for thesis type information
143149
thesis_type = pref.get("thesis-type", "").lower()
144-
if thesis_type == "master" or thesis_type == "masters" or thesis_type == "master's":
150+
if thesis_type in {"master", "masters", "master's"}:
145151
entry_type = "mastersthesis"
146152
else:
147153
# Default to phdthesis if type is not specified or is something else
148154
entry_type = "phdthesis"
149155

150-
logging.info(f"Converting CFF type '{cff_type}' to BibTeX entry type: {entry_type}")
156+
logger.info("Converting CFF type '%s' to BibTeX entry type: %s", cff_type, entry_type)
151157

152158
# Use utility function to generate citation key
153159
try:
154160
citation_key = generate_citation_key(authors, title, year)
155-
except Exception as e:
156-
logging.error(f"Error generating citation key: {str(e)}")
161+
except Exception:
162+
logger.exception("Error generating citation key")
157163
citation_key = "Unknown_Citation_Key"
158164

159165
# Compile BibTeX entry
@@ -254,8 +260,8 @@ def create_bibtex_from_cff():
254260
try:
255261
editor_str = format_authors_for_bibtex(pref["collection-editors"])
256262
bibtex_lines.append(f" editor = {{{editor_str}}},")
257-
except Exception as e:
258-
logging.warning(f"Error formatting collection editors: {str(e)}")
263+
except (KeyError, TypeError, AttributeError) as e:
264+
logger.warning("Error formatting collection editors: %s", e)
259265

260266
# Special handling for software, code, data entries
261267
if cff_type.lower().startswith("software") or cff_type.lower() in [
@@ -299,39 +305,40 @@ def create_bibtex_from_cff():
299305
bibtex_lines.append(f" {field:<9} = {{{field_value}}},")
300306

301307
# Handle list fields like languages
302-
if "languages" in pref and pref["languages"]:
308+
if pref.get("languages"):
303309
try:
304310
languages_str = ", ".join(pref["languages"])
305311
bibtex_lines.append(f" language = {{{languages_str}}},")
306-
except Exception as e:
307-
logging.warning(f"Error processing languages field: {str(e)}")
312+
except (TypeError, AttributeError) as e:
313+
logger.warning("Error processing languages field: %s", e)
308314

309315
# Handle keywords field
310-
if "keywords" in pref and pref["keywords"]:
316+
if pref.get("keywords"):
311317
try:
312318
keywords_list = extract_keywords(pref["keywords"])
313319
if keywords_list:
314320
keywords_str = ", ".join(keywords_list)
315321
bibtex_lines.append(f" keywords = {{{keywords_str}}},")
316-
except Exception as e:
317-
logging.warning(f"Error processing keywords field: {str(e)}")
322+
except (TypeError, AttributeError) as e:
323+
logger.warning("Error processing keywords field: %s", e)
318324

319325
# Close the entry
320326
bibtex_lines.append("}")
321327
bibtex = "\n".join(bibtex_lines)
322328

323329
# Write to CITATION.bib
324330
try:
325-
with open(citation_bib_path, "w", encoding="utf-8") as f:
331+
with citation_bib_path.open("w", encoding="utf-8") as f:
326332
f.write(bibtex)
327-
logging.info(f"BibTeX citation successfully created at {citation_bib_path}")
328-
return True
329-
except IOError as e:
330-
logging.error(f"Error writing to {citation_bib_path}: {e}")
333+
except OSError:
334+
logger.exception("Error writing to {citation_bib_path}")
331335
return False
336+
else:
337+
logger.info("BibTeX citation successfully created at %s", citation_bib_path)
338+
return True
332339

333-
except Exception as e:
334-
logging.exception(f"Unexpected error in create_bibtex_from_cff: {str(e)}")
340+
except Exception:
341+
logger.exception("Unexpected error in create_bibtex_from_cff")
335342
return False
336343

337344

0 commit comments

Comments
 (0)