Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add action to automatically check for updates and create a PR #83

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .github/workflows/check-for-updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Check for tzdata updates

on:
schedule:
- cron: '0 9 * * *' # Runs daily at 9AM UTC
workflow_dispatch:

jobs:
check-pr-exists:
runs-on: ubuntu-latest
outputs:
pr_exists: ${{ steps.check_pr_exists.outputs.pr_exists }}
steps:
- name: Check if PR already exists
id: check_pr_exists
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_EXISTS=$(gh pr --repo $GITHUB_REPOSITORY \
list --search "Update tzdata to version" \
--json number --jq '.[] | .number')
if [ -n "$PR_EXISTS" ]; then
echo "A PR updating the tzdata version already exists: https://github.com/python/tzdata/pulls/${PR_EXISTS}"
echo "pr_exists=true" >> $GITHUB_OUTPUT
exit 0
else
echo "pr_exists=false" >> $GITHUB_OUTPUT
fi

check-for-updates:
runs-on: ubuntu-latest
needs: check-pr-exists
permissions:
pull-requests: write
contents: write
if: needs.check-pr-exists.outputs.pr_exists == 'false' # Run only if no PR exists
steps:
- name: Check out repository (shallow)
uses: actions/checkout@v3
with:
fetch-depth: 1 # Shallow clone to save time

- name: Set up Python 3.12
uses: actions/setup-python@v4
with:
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install tox
sudo apt-get install gh

- name: Run tox update
run: tox -e update

- name: Check for repository changes and commit
id: check_changes
run: |
git config --global user.email "[email protected]"
git config --global user.name "GitHub Action"

# Check for changes
if git diff --quiet; then
echo "No changes detected."
echo "CHANGES_DETECTED=false" >> $GITHUB_ENV
exit 0
fi

# Check for changes in the news.d directory
git add -A
news_files=$(git diff --cached --name-only --diff-filter=A | grep '^news.d/.*\.md' || true)

if [ -z "$news_files" ]; then
echo "No new file in news.d, failing the job."
exit 1
fi

if [ $(echo "$news_files" | wc -l) -ne 1 ]; then
echo "More than one new file added in news.d, failing the job."
exit 1
fi
echo "CHANGES_DETECTED=true" >> $GITHUB_ENV

# Extract TZDATA_VERSION from filename
TZDATA_VERSION=$(basename "$news_files" .md)

# Extract TZDATA_NEWS from file content
TZDATA_NEWS=$(cat "$news_files")

echo "TZDATA_VERSION=$TZDATA_VERSION" >> $GITHUB_ENV
echo "TZDATA_NEWS=$TZDATA_NEWS" >> $GITHUB_ENV

- name: Commit changes
id: commit_changes
if: env.CHANGES_DETECTED == 'true'
run: |
git checkout -b "updates/update_${TZDATA_VERSION}"
git commit -m "Update tzdata to version $TZDATA_VERSION" \
-m "$TZDATA_NEWS"
git push --force origin "updates/update_${TZDATA_VERSION}"

- name: Create pull request
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: env.CHANGES_DETECTED == 'true'
run: |
gh pr create --title "Update tzdata to version $TZDATA_VERSION" \
--body "$TZDATA_NEWS" \
--base master \
--head $(git rev-parse --abbrev-ref HEAD) \
--label "automatic-updates"
56 changes: 46 additions & 10 deletions update.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,15 @@ def create_package(version: str, zonenames: Sequence[str], zoneinfo_dir: pathlib
init_file.touch()


def get_current_package_version() -> str:
with open(PKG_BASE / "tzdata/__init__.py", "rt") as f:
for line in f:
if line.startswith("IANA_VERSION"):
return line.split("=", 1)[1].strip(' "\n')

raise ValueError("IANA version not found!")


def find_latest_version() -> str:
r = requests.get(IANA_LATEST_LOCATION)
fobj = io.BytesIO(r.content)
Expand Down Expand Up @@ -426,27 +435,54 @@ def update_news(news_entry: NewsEntry):
"--news-only/--no-news-only",
help="Flag to disable data updates and only update the news entry",
)
@click.option(
"--skip-existing/--no-skip-existing",
default=True,
help="Whether to skip the update if we're already at the current value.",
)
def main(
version: str | None,
news_only: bool,
skip_existing: bool,
source_dir: pathlib.Path | None,
):
logging.basicConfig(level=logging.INFO)

if source_dir is not None:
if version is None:
logging.error(
"--source-dir specified without --version: "
"If using --source-dir, --version must also be used."
if skip_existing:
existing_version: str | None = get_current_package_version()
else:
existing_version = None

if version is None or version != existing_version:
if source_dir is not None:
if version is None:
logging.error(
"--source-dir specified without --version: "
"If using --source-dir, --version must also be used."
)
sys.exit(-1)
download_locations: Sequence[pathlib.Path] | None = retrieve_local_tarballs(
version, source_dir
)
sys.exit(-1)
download_locations = retrieve_local_tarballs(version, source_dir)
else:
if version is None:
version = find_latest_version()

if version != existing_version or not skip_existing:
download_locations = download_tzdb_tarballs(version)
else:
download_locations = None
else:
if version is None:
version = find_latest_version()
download_locations = None

download_locations = download_tzdb_tarballs(version)
if skip_existing and version == existing_version:
logging.info(
f"Selected version {version} is identical "
f"to existing version {existing_version}; nothing to do!"
)
sys.exit(0)

assert download_locations is not None
tzdb_location = unpack_tzdb_tarballs(download_locations)

# Update the news entry
Expand Down
Loading