|
| 1 | +name: "Update Sun Schedule" |
| 2 | + |
| 3 | +on: |
| 4 | + schedule: |
| 5 | + # Run once daily at 02:00 UTC (3 AM Oslo time in winter) |
| 6 | + - cron: "0 2 * * *" |
| 7 | + workflow_dispatch: # Manual trigger for testing |
| 8 | + |
| 9 | +jobs: |
| 10 | + update-schedule: |
| 11 | + runs-on: ubuntu-latest |
| 12 | + timeout-minutes: 5 |
| 13 | + |
| 14 | + steps: |
| 15 | + - name: Checkout repository |
| 16 | + uses: actions/checkout@v4 |
| 17 | + with: |
| 18 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 19 | + |
| 20 | + - name: Set up Python 3.11 |
| 21 | + uses: actions/setup-python@v4 |
| 22 | + with: |
| 23 | + python-version: '3.11' |
| 24 | + |
| 25 | + - name: Fetch sun times and update schedule |
| 26 | + run: | |
| 27 | + python3 << 'EOF' |
| 28 | + import json |
| 29 | + import requests |
| 30 | + from datetime import datetime, time |
| 31 | +
|
| 32 | + # Fetch sun times |
| 33 | + LAT = 59.95 |
| 34 | + LNG = 10.466667 |
| 35 | + url = f"https://api.sunrise-sunset.org/json?lat={LAT}&lng={LNG}&formatted=0" |
| 36 | + |
| 37 | + response = requests.get(url, timeout=15) |
| 38 | + response.raise_for_status() |
| 39 | + data = response.json() |
| 40 | + |
| 41 | + if data.get("status") != "OK": |
| 42 | + raise Exception(f"API returned status: {data.get('status')}") |
| 43 | + |
| 44 | + results = data["results"] |
| 45 | + |
| 46 | + # Parse times (ISO format from API when formatted=0) |
| 47 | + def parse_time(time_str): |
| 48 | + """Extract hour and minute from ISO datetime string""" |
| 49 | + dt = datetime.fromisoformat(time_str.replace('Z', '+00:00')) |
| 50 | + return dt.hour, dt.minute |
| 51 | + |
| 52 | + # Get astronomical twilight times (darkest) |
| 53 | + twilight_end_h, twilight_end_m = parse_time(results["astronomical_twilight_end"]) |
| 54 | + twilight_begin_h, twilight_begin_m = parse_time(results["astronomical_twilight_begin"]) |
| 55 | + |
| 56 | + # Generate cron schedule for dark hours |
| 57 | + # Run every 30 minutes during astronomical darkness |
| 58 | + # Cron format: minute hour * * * |
| 59 | + |
| 60 | + # If twilight_end is 17:44:59 (5:44 PM), start at 18:00 |
| 61 | + # If twilight_begin is 4:18:25 AM, end at 4:00 AM |
| 62 | + start_hour = twilight_end_h + 1 if twilight_end_m > 30 else twilight_end_h |
| 63 | + end_hour = twilight_begin_h |
| 64 | + |
| 65 | + # Handle case where darkness spans midnight |
| 66 | + if start_hour >= end_hour: |
| 67 | + # Example: 18:00-23:59 and 00:00-04:00 |
| 68 | + cron_evening = f"*/30 {start_hour}-23 * * *" |
| 69 | + cron_morning = f"*/30 0-{end_hour} * * *" if end_hour > 0 else None |
| 70 | + cron_schedule = [cron_evening] |
| 71 | + if cron_morning: |
| 72 | + cron_schedule.append(cron_morning) |
| 73 | + else: |
| 74 | + # Continuous dark period (rare in Lommedalen) |
| 75 | + cron_schedule = [f"*/30 {start_hour}-{end_hour} * * *"] |
| 76 | + |
| 77 | + # Save schedule info |
| 78 | + schedule_info = { |
| 79 | + "updated_at": datetime.utcnow().isoformat() + "Z", |
| 80 | + "location": {"lat": LAT, "lng": LNG}, |
| 81 | + "sun_times": results, |
| 82 | + "dark_hours": { |
| 83 | + "start_hour": start_hour, |
| 84 | + "end_hour": end_hour, |
| 85 | + "spans_midnight": start_hour >= end_hour |
| 86 | + }, |
| 87 | + "cron_schedules": cron_schedule |
| 88 | + } |
| 89 | + |
| 90 | + with open('sun_schedule.json', 'w') as f: |
| 91 | + json.dump(schedule_info, f, indent=2) |
| 92 | + |
| 93 | + print(f"✅ Updated sun schedule") |
| 94 | + print(f" Astronomical darkness: {start_hour}:00 - {end_hour}:00 UTC") |
| 95 | + print(f" Cron schedules: {', '.join(cron_schedule)}") |
| 96 | + EOF |
| 97 | +
|
| 98 | + - name: Update workflow schedule |
| 99 | + run: | |
| 100 | + python3 << 'EOF' |
| 101 | + import json |
| 102 | + import yaml |
| 103 | + |
| 104 | + # Load schedule info |
| 105 | + with open('sun_schedule.json', 'r') as f: |
| 106 | + schedule_info = json.load(f) |
| 107 | + |
| 108 | + # Load current workflow |
| 109 | + with open('.github/workflows/post.yml', 'r') as f: |
| 110 | + workflow = yaml.safe_load(f) |
| 111 | + |
| 112 | + # Update cron schedules |
| 113 | + cron_schedules = schedule_info['cron_schedules'] |
| 114 | + workflow['on']['schedule'] = [{'cron': cron} for cron in cron_schedules] |
| 115 | + |
| 116 | + # Save updated workflow |
| 117 | + with open('.github/workflows/post.yml', 'w') as f: |
| 118 | + yaml.dump(workflow, f, default_flow_style=False, sort_keys=False) |
| 119 | + |
| 120 | + print(f"✅ Updated post.yml with {len(cron_schedules)} cron schedule(s)") |
| 121 | + EOF |
| 122 | +
|
| 123 | + - name: Commit and push changes |
| 124 | + run: | |
| 125 | + git config user.name "github-actions[bot]" |
| 126 | + git config user.email "github-actions[bot]@users.noreply.github.com" |
| 127 | + git add sun_schedule.json .github/workflows/post.yml |
| 128 | + if git diff --staged --quiet; then |
| 129 | + echo "No changes to commit" |
| 130 | + else |
| 131 | + git commit -m "chore: update sun schedule and workflow timing [automated] |
| 132 | +
|
| 133 | +Dark hours: $(jq -r '.dark_hours.start_hour' sun_schedule.json):00 - $(jq -r '.dark_hours.end_hour' sun_schedule.json):00 UTC |
| 134 | +Updated: $(jq -r '.updated_at' sun_schedule.json)" |
| 135 | + git push |
| 136 | + fi |
0 commit comments