19 Commits

Author SHA1 Message Date
485d5b6f22 Revert "choreL: update test tooling (#72)" (#74)
All checks were successful
CD / Create tag (push) Successful in 5s
CD / test (push) Successful in 1m43s
CD / Build and push (amd64) (push) Successful in 37s
CD / Build and push (arm64) (push) Successful in 2m11s
CD / Create manifest (push) Successful in 7s
This reverts commit 2bfb4419d3.

Reviewed-on: #74
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2026-01-10 21:25:12 +01:00
2bfb4419d3 choreL: update test tooling (#72)
Some checks failed
CD / Create tag (push) Successful in 5s
CD / test (push) Failing after 3m2s
CD / Build and push (amd64) (push) Has been skipped
CD / Build and push (arm64) (push) Has been skipped
CD / Create manifest (push) Has been skipped
Reviewed-on: #72
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2026-01-10 21:18:09 +01:00
ac73cf14eb refactor: to use ionosDnsClient (#71)
All checks were successful
CD / Create tag (push) Successful in 14s
CD / test (push) Successful in 32s
CD / Build and push (amd64) (push) Successful in 43s
CD / Build and push (arm64) (push) Successful in 2m23s
CD / Create manifest (push) Successful in 34s
Reviewed-on: #71
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2026-01-10 21:00:43 +01:00
15431166e0 chore(deps): update https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment action to v0.1.22 (#68)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment](https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment) | action | patch | `0.1.20` → `0.1.22` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the Dependency Dashboard for more information.

---

### Release Notes

<details>
<summary>t.behrendt/conventional-semantic-git-tag-increment (https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment)</summary>

### [`v0.1.22`](https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment/compare/0.1.21...0.1.22)

[Compare Source](https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment/compare/0.1.21...0.1.22)

### [`v0.1.21`](https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment/compare/0.1.20...0.1.21)

[Compare Source](https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment/compare/0.1.20...0.1.21)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi42OS4yIiwidXBkYXRlZEluVmVyIjoiNDIuNjkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYWN0aW9uIiwiZGVwcyJdfQ==-->

Reviewed-on: #68
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2026-01-08 19:25:18 +01:00
caf13306d4 chore(deps): update module github.com/go-co-op/gocron/v2 to v2.19.0 (#58)
All checks were successful
CD / Create tag (push) Successful in 14s
CD / test (push) Successful in 42s
CD / Build and push (amd64) (push) Successful in 37s
CD / Build and push (arm64) (push) Successful in 2m2s
CD / Create manifest (push) Successful in 9s
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/go-co-op/gocron/v2](https://github.com/go-co-op/gocron) | `v2.18.0` -> `v2.19.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-co-op%2fgocron%2fv2/v2.19.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-co-op%2fgocron%2fv2/v2.18.0/v2.19.0?slim=true) |

---

### Release Notes

<details>
<summary>go-co-op/gocron (github.com/go-co-op/gocron/v2)</summary>

### [`v2.19.0`](https://github.com/go-co-op/gocron/releases/tag/v2.19.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.18.2...v2.19.0)

#### What's Changed

- feat: add scheduler lifecycle monitoring <> [#&#8203;785](https://github.com/go-co-op/gocron/issues/785) by [@&#8203;iyashjayesh](https://github.com/iyashjayesh) in [#&#8203;889](https://github.com/go-co-op/gocron/pull/889)
- feat: expose default cron implementation by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;906](https://github.com/go-co-op/gocron/pull/906)
- build(deps): bump golangci/golangci-lint-action from 9.1.0 to 9.2.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;903](https://github.com/go-co-op/gocron/pull/903)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.18.2...v2.19.0>

### [`v2.18.2`](https://github.com/go-co-op/gocron/releases/tag/v2.18.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.18.1...v2.18.2)

#### What's Changed

- build(deps): bump golangci/golangci-lint-action from 9.0.0 to 9.1.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;899](https://github.com/go-co-op/gocron/pull/899)
- build(deps): bump actions/checkout from 5 to 6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;898](https://github.com/go-co-op/gocron/pull/898)
- fix: calling start multiple times should no-op by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;901](https://github.com/go-co-op/gocron/pull/901)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.18.1...v2.18.2>

### [`v2.18.1`](https://github.com/go-co-op/gocron/releases/tag/v2.18.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.18.0...v2.18.1)

#### What's Changed

- build(deps): bump golangci/golangci-lint-action from 8.0.0 to 9.0.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;894](https://github.com/go-co-op/gocron/pull/894)
- fix: WithStartDateTimePast now correctly calculates from past time by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;897](https://github.com/go-co-op/gocron/pull/897)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.18.0...v2.18.1>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4xMC41IiwidXBkYXRlZEluVmVyIjoiNDIuNTIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: https://gitea.t000-n.de/t.behrendt/realDynDNS/pulls/58
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-21 10:45:46 +01:00
9291687b35 chore(deps): update docker/setup-buildx-action digest to 8d2750c (#66)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) ([changelog](e468171a9d..8d2750c68a)) | action | digest | `e468171` -> `8d2750c` |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Ny4wIiwidXBkYXRlZEluVmVyIjoiNDIuNDcuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYWN0aW9uIiwiZGVwcyJdfQ==-->

Reviewed-on: #66
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-19 19:35:10 +01:00
735a106826 chore(deps): update actions/cache action to v5 (#63)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://github.com/actions/cache) | action | major | `v4` -> `v5` |

---

### Release Notes

<details>
<summary>actions/cache (actions/cache)</summary>

### [`v5`](https://github.com/actions/cache/compare/v4...v5)

[Compare Source](https://github.com/actions/cache/compare/v4...v5)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zNy4xIiwidXBkYXRlZEluVmVyIjoiNDIuMzcuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYWN0aW9uIiwiZGVwcyJdfQ==-->

Reviewed-on: #63
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-19 19:34:23 +01:00
d0bce1b77c chore(config): migrate Renovate config (#64)
The Renovate config in this repository needs migrating. Typically this is because one or more configuration options you are using have been renamed.

  You don't need to merge this PR right away, because Renovate will continue to migrate these fields internally each time it runs. But later some of these fields may be fully deprecated and the migrations removed. So it's a good idea to merge this migration PR soon.

🔕 **Ignore**: Close this PR and you won't be reminded about config migration again, but one day your current config may no longer be valid.

 Got questions? Does something look wrong to you? Please don't hesitate to [request help here](https://github.com/renovatebot/renovate/discussions).

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).

Reviewed-on: #64
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-14 09:16:22 +01:00
b5bc615cbb chore(deps): pin dependencies (#62)
All checks were successful
CD / Create tag (push) Successful in 8s
CD / test (push) Successful in 1m4s
CD / Build and push (amd64) (push) Successful in 37s
CD / Build and push (arm64) (push) Successful in 2m13s
CD / Create manifest (push) Successful in 22s
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://github.com/actions/cache) | action | pinDigest |  -> `0057852` |
| [actions/checkout](https://github.com/actions/checkout) | action | pinDigest |  -> `8e8c483` |
| [actions/setup-go](https://github.com/actions/setup-go) | action | pinDigest |  -> `4dc6199` |
| [docker/build-push-action](https://github.com/docker/build-push-action) | action | pinDigest |  -> `2634353` |
| [docker/login-action](https://github.com/docker/login-action) | action | pinDigest |  -> `5e57cd1` |
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | action | pinDigest |  -> `e468171` |
| gcr.io/distroless/static-debian12 | final | pinDigest |  -> `4b2a093` |
| golang | stage | pinDigest |  -> `2611181` |
| [https://gitea.com/actions/go-hashfiles](https://gitea.com/actions/go-hashfiles) | action | pinDigest |  -> `264ae76` |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zNy4xIiwidXBkYXRlZEluVmVyIjoiNDIuMzcuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiYWN0aW9uIiwiZGVwcyJdfQ==-->

Reviewed-on: #62
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-14 09:08:59 +01:00
0193eac6e5 ci(renovate): switch to shared configs (#61)
Reviewed-on: #61
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2025-12-12 21:51:07 +01:00
dc76c5fb26 chore(deps): update golang docker tag to v1.25 (#48)
All checks were successful
CD / Create tag (push) Successful in 9s
CD / test (push) Successful in 2m35s
CD / Build and push (amd64) (push) Successful in 41s
CD / Build and push (arm64) (push) Successful in 2m16s
CD / Create manifest (push) Successful in 47s
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| golang | stage | minor | `1.24-alpine` -> `1.25-alpine` |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS44MS41IiwidXBkYXRlZEluVmVyIjoiNDIuMTAuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Reviewed-on: #48
Reviewed-by: branch-buddy <branch-buddy@t00n.de>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-12 21:42:46 +01:00
eb182ac7ce ci: add semver release (#60)
Reviewed-on: #60
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2025-12-12 21:37:27 +01:00
ae6981cb02 ci: switch to native change detection (#59)
Reviewed-on: #59
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2025-12-12 21:27:55 +01:00
6511147a41 chore(deps): update actions/checkout action to v6 (#57)
All checks were successful
CD / Check changes (push) Successful in 12s
CD / test (push) Successful in 43s
CD / Build and push (arm64) (push) Has been skipped
CD / Build and push (amd64) (push) Has been skipped
CD / Create manifest (push) Has been skipped
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://github.com/actions/checkout) | action | major | `v5` -> `v6` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

### [`v6`](https://github.com/actions/checkout/compare/v5...v6)

[Compare Source](https://github.com/actions/checkout/compare/v5...v6)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4xMC41IiwidXBkYXRlZEluVmVyIjoiNDIuMTAuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: #57
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-12-04 16:20:53 +01:00
2f05e48962 fix(deps): update module github.com/go-co-op/gocron to v2 (#39)
All checks were successful
CD / Check changes (push) Successful in 7s
CD / test (push) Successful in 1m51s
CD / Build and push (amd64) (push) Successful in 40s
CD / Build and push (arm64) (push) Successful in 2m36s
CD / Create manifest (push) Successful in 9s
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [github.com/go-co-op/gocron](https://github.com/go-co-op/gocron) | `v1.37.0` -> `v2.18.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-co-op%2fgocron/v2.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-co-op%2fgocron/v1.37.0/v2.18.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>go-co-op/gocron (github.com/go-co-op/gocron)</summary>

### [`v2.18.0`](https://github.com/go-co-op/gocron/releases/tag/v2.18.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.17.0...v2.18.0)

#### What's Changed

- feat: Add option to calculate intervals from job completion time for interval-based scheduling (fixes [#&#8203;565](https://github.com/go-co-op/gocron/issues/565)) by [@&#8203;iyashjayesh](https://github.com/iyashjayesh) in [#&#8203;884](https://github.com/go-co-op/gocron/pull/884)

#### Fixes

- fix: limit validation for WithLimitedRuns by [@&#8203;OsipovMax](https://github.com/OsipovMax) in [#&#8203;893](https://github.com/go-co-op/gocron/pull/893)

#### Chores

- doc: add v1 to v2 migration guide by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;890](https://github.com/go-co-op/gocron/pull/890)
- tests: add more daylight savings time tests by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;891](https://github.com/go-co-op/gocron/pull/891)
- docs: add Articles & Blog Posts section to README  by [@&#8203;iyashjayesh](https://github.com/iyashjayesh) in [#&#8203;886](https://github.com/go-co-op/gocron/pull/886)
- Bump github/codeql-action from 3 to 4 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;883](https://github.com/go-co-op/gocron/pull/883)

#### New Contributors

- [@&#8203;OsipovMax](https://github.com/OsipovMax) made their first contribution in [#&#8203;893](https://github.com/go-co-op/gocron/pull/893)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.17.0...v2.18.0>

### [`v2.17.0`](https://github.com/go-co-op/gocron/releases/tag/v2.17.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.6...v2.17.0)

#### What's Changed

- feat: add WithStartDateTimePast WithStartAt option by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;882](https://github.com/go-co-op/gocron/pull/882)

#### Performance Improvements

- Reusing the results of reflections to improve performance by [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;873](https://github.com/go-co-op/gocron/pull/873)

#### Fixes

- fix: handle negative time.durations with error by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;878](https://github.com/go-co-op/gocron/pull/878)
- fix: multiple calls to shutdown should be no-op by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;880](https://github.com/go-co-op/gocron/pull/880)

#### Chores

- chore: go 1.23 is end of life - now go 1.24 by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;879](https://github.com/go-co-op/gocron/pull/879)
- Update README.md by [@&#8203;iyashjayesh](https://github.com/iyashjayesh) in [#&#8203;875](https://github.com/go-co-op/gocron/pull/875)

#### New Contributors

- [@&#8203;iyashjayesh](https://github.com/iyashjayesh) made their first contribution in [#&#8203;875](https://github.com/go-co-op/gocron/pull/875)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.6...v2.17.0>

### [`v2.16.6`](https://github.com/go-co-op/gocron/releases/tag/v2.16.6)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.5...v2.16.6)

#### What's Changed

- a return is missing to stop execution if the job is not found by [@&#8203;sarff](https://github.com/sarff) in [#&#8203;872](https://github.com/go-co-op/gocron/pull/872)

#### Chores

- Bump actions/setup-go from 5 to 6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;871](https://github.com/go-co-op/gocron/pull/871)

#### New Contributors

- [@&#8203;sarff](https://github.com/sarff) made their first contribution in [#&#8203;872](https://github.com/go-co-op/gocron/pull/872)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.5...v2.16.6>

### [`v2.16.5`](https://github.com/go-co-op/gocron/releases/tag/v2.16.5)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.4...v2.16.5)

#### What's Changed

- Use `errors.New` for non-formatted strings by [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;870](https://github.com/go-co-op/gocron/pull/870)
- Add go1.25 tests by [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;869](https://github.com/go-co-op/gocron/pull/869)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.4...v2.16.5>

### [`v2.16.4`](https://github.com/go-co-op/gocron/releases/tag/v2.16.4)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.3...v2.16.4)

#### What's Changed

- Bump actions/checkout from 4 to 5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;860](https://github.com/go-co-op/gocron/pull/860)
- Bump golang.org/x/crypto from 0.40.0 to 0.41.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;859](https://github.com/go-co-op/gocron/pull/859)
- Add comprehensive GitHub Copilot instructions for gocron development by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;866](https://github.com/go-co-op/gocron/pull/866)
- Fix memory consumption issue by changing jobOutRequest channels to use pointers and reducing buffer size by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;864](https://github.com/go-co-op/gocron/pull/864)
- Bump testify by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;868](https://github.com/go-co-op/gocron/pull/868)

#### New Contributors

- [@&#8203;Copilot](https://github.com/Copilot) made their first contribution in [#&#8203;866](https://github.com/go-co-op/gocron/pull/866)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.3...v2.16.4>

### [`v2.16.3`](https://github.com/go-co-op/gocron/releases/tag/v2.16.3)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.2...v2.16.3)

#### What's Changed

- fix: cancel job contexts in create/update errors by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;858](https://github.com/go-co-op/gocron/pull/858)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.2...v2.16.3>

### [`v2.16.2`](https://github.com/go-co-op/gocron/releases/tag/v2.16.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.1...v2.16.2)

#### What's Changed

- docs: adapt README to the dark theme by [@&#8203;alexandear](https://github.com/alexandear) in [#&#8203;844](https://github.com/go-co-op/gocron/pull/844)
- go 1.23 & golangci-lint v2 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;843](https://github.com/go-co-op/gocron/pull/843)
  - [Go 1.22 and below are end of life](https://endoflife.date/go)
- Bump golangci/golangci-lint-action from 7.0.0 to 8.0.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;847](https://github.com/go-co-op/gocron/pull/847)
- chore: document the limitations with the locker design by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;848](https://github.com/go-co-op/gocron/pull/848)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.1...v2.16.2>

### [`v2.16.1`](https://github.com/go-co-op/gocron/releases/tag/v2.16.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.16.0...v2.16.1)

#### What's Changed

- Fix [#&#8203;835](https://github.com/go-co-op/gocron/issues/835) and [#&#8203;837](https://github.com/go-co-op/gocron/issues/837) by [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;836](https://github.com/go-co-op/gocron/pull/836)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.16.0...v2.16.1>

### [`v2.16.0`](https://github.com/go-co-op/gocron/releases/tag/v2.16.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.15.0...v2.16.0)

#### What's Changed

- feat:custom-cron interface for own custom cron implimentation by [@&#8203;Dojeto](https://github.com/Dojeto) in [#&#8203;834](https://github.com/go-co-op/gocron/pull/834)

#### Bug fixes

- fixes related to the bug where a job unexpectedly runs twice by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;810](https://github.com/go-co-op/gocron/pull/810)
- fix scheduler restart by [@&#8203;27149chen](https://github.com/27149chen) in [#&#8203;825](https://github.com/go-co-op/gocron/pull/825)
- removes nextRuns in the past when job skipped by locker [#&#8203;828](https://github.com/go-co-op/gocron/issues/828) by [@&#8203;manuelarte](https://github.com/manuelarte) in [#&#8203;829](https://github.com/go-co-op/gocron/pull/829)

#### Chores

- go to 1.21, upgrade deps by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;822](https://github.com/go-co-op/gocron/pull/822)
- replace "golang.org/x/exp" with standard libraries by [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;823](https://github.com/go-co-op/gocron/pull/823)
- Bump golangci/golangci-lint-action from 6.2.0 to 6.3.2 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;827](https://github.com/go-co-op/gocron/pull/827)
- fix err assertion in TestScheduler\_RemoveJob by [@&#8203;alexandear](https://github.com/alexandear) in [#&#8203;830](https://github.com/go-co-op/gocron/pull/830)
- Bump golangci/golangci-lint-action from 6.3.2 to 6.5.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;831](https://github.com/go-co-op/gocron/pull/831)
- re-enable goleak detection in ci by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;832](https://github.com/go-co-op/gocron/pull/832)
- chore: add go1.24 to ci by [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;833](https://github.com/go-co-op/gocron/pull/833)

#### New Contributors

- [@&#8203;alexandear](https://github.com/alexandear) made their first contribution in [#&#8203;830](https://github.com/go-co-op/gocron/pull/830)
- [@&#8203;Dojeto](https://github.com/Dojeto) made their first contribution in [#&#8203;834](https://github.com/go-co-op/gocron/pull/834)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.15.0...v2.16.0>

### [`v2.15.0`](https://github.com/go-co-op/gocron/releases/tag/v2.15.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.14.2...v2.15.0)

#### What's New

- New method WithContext supports providing a parent context by [@&#8203;27149chen](https://github.com/27149chen) in [#&#8203;819](https://github.com/go-co-op/gocron/pull/819) & [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;820](https://github.com/go-co-op/gocron/pull/820)

```go
// WithContext sets the parent context for the job.
// If you set the first argument of your Task func to be a context.Context,
// gocron will pass in the provided context to the job and will cancel the
// context on shutdown. If you cancel the context the job will no longer be
// scheduled as well. This allows you to both control the job via a context
// and listen for and handle cancellation within your job.
```

- Job task function now supports passing a ctx if the first argument in your function is a `context.Context` by [@&#8203;27149chen](https://github.com/27149chen) in [#&#8203;819](https://github.com/go-co-op/gocron/pull/819) & [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;820](https://github.com/go-co-op/gocron/pull/820)

```go
// NewTask provides the job's task function and parameters.
// If you set the first argument of your Task func to be a context.Context,
// gocron will pass in a context (either the default Job context, or one
// provided via WithContext) to the job and will cancel the context on shutdown.
// This allows you to listen for and handle cancellation within your job.
```

#### Chores

- Bump golangci/golangci-lint-action from 6.1.1 to 6.2.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;817](https://github.com/go-co-op/gocron/pull/817)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.14.2...v2.15.0>

### [`v2.14.2`](https://github.com/go-co-op/gocron/releases/tag/v2.14.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.14.1...v2.14.2)

#### What's Changed

- feat: allow disabling global distributed locker per job by [@&#8203;seinshah](https://github.com/seinshah) in [#&#8203;811](https://github.com/go-co-op/gocron/pull/811)
- feat(event listener): introduce BeforeJobRunsSkipIfBeforeFuncErrors as a new Eventlistener by [@&#8203;FalcoSuessgott](https://github.com/FalcoSuessgott) in [#&#8203;813](https://github.com/go-co-op/gocron/pull/813)

#### New Contributors

- [@&#8203;seinshah](https://github.com/seinshah) made their first contribution in [#&#8203;811](https://github.com/go-co-op/gocron/pull/811)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.14.1...v2.14.2>

### [`v2.14.1`](https://github.com/go-co-op/gocron/releases/tag/v2.14.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.14.0...v2.14.1)

#### What's Changed

- BUG FIX: creating a new slice in several job options because appending modifies original by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;809](https://github.com/go-co-op/gocron/pull/809)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.14.0...v2.14.1>

### [`v2.14.0`](https://github.com/go-co-op/gocron/releases/tag/v2.14.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.13.0...v2.14.0)

#### What's Changed

- parse time.Time from AtTime by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;806](https://github.com/go-co-op/gocron/pull/806)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.13.0...v2.14.0>

### [`v2.13.0`](https://github.com/go-co-op/gocron/releases/tag/v2.13.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.12.4...v2.13.0)

#### What's Changed

- Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;801](https://github.com/go-co-op/gocron/pull/801)
- stop timeout timers when no longer needed by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;803](https://github.com/go-co-op/gocron/pull/803)
- feat(monitor): introduce MonitorStatus by [@&#8203;FalcoSuessgott](https://github.com/FalcoSuessgott) in [#&#8203;780](https://github.com/go-co-op/gocron/pull/780)

#### New Contributors

- [@&#8203;FalcoSuessgott](https://github.com/FalcoSuessgott) made their first contribution in [#&#8203;780](https://github.com/go-co-op/gocron/pull/780)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.12.4...v2.13.0>

### [`v2.12.4`](https://github.com/go-co-op/gocron/releases/tag/v2.12.4)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.12.3...v2.12.4)

#### What's Changed

- Bump golangci/golangci-lint-action from 6.1.0 to 6.1.1 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;781](https://github.com/go-co-op/gocron/pull/781)
- fix overly greedy panic handler by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;800](https://github.com/go-co-op/gocron/pull/800)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.12.3...v2.12.4>

### [`v2.12.3`](https://github.com/go-co-op/gocron/releases/tag/v2.12.3)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.12.2...v2.12.3)

#### What's Changed

- update mocks with latest job/scheduler changes by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;794](https://github.com/go-co-op/gocron/pull/794)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.12.2...v2.12.3>

### [`v2.12.2`](https://github.com/go-co-op/gocron/releases/tag/v2.12.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.12.1...v2.12.2)

#### What's Changed

- dailyjob should not allow interval zero by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;791](https://github.com/go-co-op/gocron/pull/791)
- weekly and monthly jobs should not allow zero interval by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;792](https://github.com/go-co-op/gocron/pull/792)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.12.1...v2.12.2>

### [`v2.12.1`](https://github.com/go-co-op/gocron/releases/tag/v2.12.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.12.0...v2.12.1)

#### What's Changed

- Fix CPU spike / max-out in One-time job when 2 or more equal times are provided by [@&#8203;rbroggi](https://github.com/rbroggi) in [#&#8203;779](https://github.com/go-co-op/gocron/pull/779)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.12.0...v2.12.1>

### [`v2.12.0`](https://github.com/go-co-op/gocron/releases/tag/v2.12.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.11.0...v2.12.0)

#### What's Changed

- add Rescheduled metric for executor. by [@&#8203;Higan](https://github.com/Higan) in [#&#8203;763](https://github.com/go-co-op/gocron/pull/763)
- handle crontab and return error with invalid day in a month by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;766](https://github.com/go-co-op/gocron/pull/766)
- Bump golangci/golangci-lint-action from 6.0.1 to 6.1.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;764](https://github.com/go-co-op/gocron/pull/764)
- fix: cleanup timers by [@&#8203;hayotbisonai](https://github.com/hayotbisonai) in [#&#8203;776](https://github.com/go-co-op/gocron/pull/776)

#### New Contributors

- [@&#8203;hayotbisonai](https://github.com/hayotbisonai) made their first contribution in [#&#8203;776](https://github.com/go-co-op/gocron/pull/776)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.11.0...v2.12.0>

### [`v2.11.0`](https://github.com/go-co-op/gocron/releases/tag/v2.11.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.10.1...v2.11.0)

#### Features

- `WithStopAt` added to `JobOption`'s to allow giving a time for jobs to stop running by [@&#8203;Higan](https://github.com/Higan) in [#&#8203;760](https://github.com/go-co-op/gocron/pull/760)

#### Fixes

- Fix typo in security policy by [@&#8203;deining](https://github.com/deining) in [#&#8203;759](https://github.com/go-co-op/gocron/pull/759)

#### Internal

- internal refactoring by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;761](https://github.com/go-co-op/gocron/pull/761)

#### New Contributors

- [@&#8203;deining](https://github.com/deining) made their first contribution in [#&#8203;759](https://github.com/go-co-op/gocron/pull/759)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.10.1...v2.11.0>

### [`v2.10.1`](https://github.com/go-co-op/gocron/releases/tag/v2.10.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.10.0...v2.10.1)

#### What's Changed

- fix validation of variadic parameters when the type is interfaceby [@&#8203;apocelipes](https://github.com/apocelipes) in [#&#8203;757](https://github.com/go-co-op/gocron/pull/757)

#### New Contributors

- [@&#8203;apocelipes](https://github.com/apocelipes) made their first contribution in [#&#8203;757](https://github.com/go-co-op/gocron/pull/757)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.10.0...v2.10.1>

### [`v2.10.0`](https://github.com/go-co-op/gocron/releases/tag/v2.10.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.9.0...v2.10.0)

#### What's Changed

- issue-677: support task creation with variadic args by [@&#8203;Higan](https://github.com/Higan) in [#&#8203;755](https://github.com/go-co-op/gocron/pull/755)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.9.0...v2.10.0>

### [`v2.9.0`](https://github.com/go-co-op/gocron/releases/tag/v2.9.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.8.0...v2.9.0)

#### What's Changed

- issue-751: speed up rescheduling when time scheduling from is time.Zero by [@&#8203;samuelattwood](https://github.com/samuelattwood) in [#&#8203;752](https://github.com/go-co-op/gocron/pull/752)
- feat: add WithIdentifier() as new job option by [@&#8203;pcfreak30](https://github.com/pcfreak30) in [#&#8203;754](https://github.com/go-co-op/gocron/pull/754)

#### New Contributors

- [@&#8203;samuelattwood](https://github.com/samuelattwood) made their first contribution in [#&#8203;752](https://github.com/go-co-op/gocron/pull/752)
- [@&#8203;pcfreak30](https://github.com/pcfreak30) made their first contribution in [#&#8203;754](https://github.com/go-co-op/gocron/pull/754)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.8.0...v2.9.0>

### [`v2.8.0`](https://github.com/go-co-op/gocron/releases/tag/v2.8.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.7.1...v2.8.0)

#### What's Changed

- issue-747: Provide more details of ErrPanicRecovered by [@&#8203;Higan](https://github.com/Higan) in [#&#8203;749](https://github.com/go-co-op/gocron/pull/749)

#### New Contributors

- [@&#8203;Higan](https://github.com/Higan) made their first contribution in [#&#8203;749](https://github.com/go-co-op/gocron/pull/749)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.7.1...v2.8.0>

### [`v2.7.1`](https://github.com/go-co-op/gocron/releases/tag/v2.7.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.7.0...v2.7.1)

#### What's Changed

- issue-740: ascending time function by [@&#8203;rbroggi](https://github.com/rbroggi) in [#&#8203;744](https://github.com/go-co-op/gocron/pull/744)
- fix jobs not starting on scheduler restart when using WithLimitConcurrentJobs by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;745](https://github.com/go-co-op/gocron/pull/745)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.7.0...v2.7.1>

### [`v2.7.0`](https://github.com/go-co-op/gocron/releases/tag/v2.7.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.6.0...v2.7.0)

#### Added

- issue-740: expand oneTimeJob to support multiple times by [@&#8203;rbroggi](https://github.com/rbroggi) in [#&#8203;741](https://github.com/go-co-op/gocron/pull/741)
  - [go doc](https://pkg.go.dev/github.com/go-co-op/gocron/v2#OneTimeJobStartDateTimes)

#### Fixed

- issue-742: bug in `NextRun` by [@&#8203;rbroggi](https://github.com/rbroggi) in [#&#8203;743](https://github.com/go-co-op/gocron/pull/743)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.6.0...v2.7.0>

### [`v2.6.0`](https://github.com/go-co-op/gocron/releases/tag/v2.6.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.5.0...v2.6.0)

#### Added

- after lock error listener by [@&#8203;manuelarte](https://github.com/manuelarte) in [#&#8203;734](https://github.com/go-co-op/gocron/pull/734)
  - [go doc](https://pkg.go.dev/github.com/go-co-op/gocron/v2#AfterLockError)
- Add `AfterJobRunsWithPanic` by [@&#8203;trungdlp-wolffun](https://github.com/trungdlp-wolffun) in [#&#8203;733](https://github.com/go-co-op/gocron/pull/733)
  - [go doc](https://pkg.go.dev/github.com/go-co-op/gocron/v2#AfterJobRunsWithPanic)

#### Fixed

- issue-738: make withSeconds optional in cron-expression by [@&#8203;rbroggi](https://github.com/rbroggi) in [#&#8203;739](https://github.com/go-co-op/gocron/pull/739)
- issue-736: moving validation of one-time to by [@&#8203;rbroggi](https://github.com/rbroggi) in [#&#8203;737](https://github.com/go-co-op/gocron/pull/737)

#### Misc

- Bump golangci/golangci-lint-action from 5.3.0 to 6.0.1 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;730](https://github.com/go-co-op/gocron/pull/730)
- remove circleci config by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;735](https://github.com/go-co-op/gocron/pull/735)

#### New Contributors

- [@&#8203;manuelarte](https://github.com/manuelarte) made their first contribution in [#&#8203;734](https://github.com/go-co-op/gocron/pull/734)
- [@&#8203;trungdlp-wolffun](https://github.com/trungdlp-wolffun) made their first contribution in [#&#8203;733](https://github.com/go-co-op/gocron/pull/733)
- [@&#8203;rbroggi](https://github.com/rbroggi) made their first contribution in [#&#8203;739](https://github.com/go-co-op/gocron/pull/739)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.5.0...v2.6.0>

### [`v2.5.0`](https://github.com/go-co-op/gocron/releases/tag/v2.5.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.4.1...v2.5.0)

#### What's Changed

- adding Job.NextRuns to provide n next run times by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;729](https://github.com/go-co-op/gocron/pull/729)
- Bump golangci/golangci-lint-action from 4.0.0 to 5.3.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;728](https://github.com/go-co-op/gocron/pull/728)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.4.1...v2.5.0>

### [`v2.4.1`](https://github.com/go-co-op/gocron/releases/tag/v2.4.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.4.0...v2.4.1)

#### What's Changed

- fix memory leak with singleton mode where job is sending duplicate reschedule requests by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;723](https://github.com/go-co-op/gocron/pull/723)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.4.0...v2.4.1>

### [`v2.4.0`](https://github.com/go-co-op/gocron/releases/tag/v2.4.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.3.0...v2.4.0)

#### What's Changed

- Added JobsWaitingInQueue in Scheduler by [@&#8203;giri-vsr](https://github.com/giri-vsr) in [#&#8203;721](https://github.com/go-co-op/gocron/pull/721)
- don't trash the incoming slice, match what was done in NewAtTime by [@&#8203;cloudkucooland](https://github.com/cloudkucooland) in [#&#8203;724](https://github.com/go-co-op/gocron/pull/724)

#### New Contributors

- [@&#8203;cloudkucooland](https://github.com/cloudkucooland) made their first contribution in [#&#8203;724](https://github.com/go-co-op/gocron/pull/724)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.3.0...v2.4.0>

### [`v2.3.0`](https://github.com/go-co-op/gocron/releases/tag/v2.3.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.10...v2.3.0)

#### What's Changed

- Add Go 1.22 to test matrix by [@&#8203;evgenymarkov](https://github.com/evgenymarkov) in [#&#8203;714](https://github.com/go-co-op/gocron/pull/714)
- Monitor: IncrementJob in case of skipped job run by [@&#8203;giri-vsr](https://github.com/giri-vsr) in [#&#8203;715](https://github.com/go-co-op/gocron/pull/715)
- fix mocks import path by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;720](https://github.com/go-co-op/gocron/pull/720)

#### New Contributors

- [@&#8203;evgenymarkov](https://github.com/evgenymarkov) made their first contribution in [#&#8203;714](https://github.com/go-co-op/gocron/pull/714)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.10...v2.2.11>

### [`v2.2.10`](https://github.com/go-co-op/gocron/releases/tag/v2.2.10)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.9...v2.2.10)

#### What's Changed

- fix nextRun with singleton mode reporting incorrect time by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;705](https://github.com/go-co-op/gocron/pull/705)
- Added Distributed Locker to JobOptions by [@&#8203;giri-vsr](https://github.com/giri-vsr) in [#&#8203;711](https://github.com/go-co-op/gocron/pull/711)

#### New Contributors

- [@&#8203;giri-vsr](https://github.com/giri-vsr) made their first contribution in [#&#8203;711](https://github.com/go-co-op/gocron/pull/711)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.9...v2.2.10>

### [`v2.2.9`](https://github.com/go-co-op/gocron/releases/tag/v2.2.9)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.8...v2.2.9)

#### What's Changed

- fix case where OneTimeJob with concurrent limit and limited runs fails to run by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;703](https://github.com/go-co-op/gocron/pull/703)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.8...v2.2.9>

### [`v2.2.8`](https://github.com/go-co-op/gocron/releases/tag/v2.2.8)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.7...v2.2.8)

#### What's Changed

- return an error if duration is zero by [@&#8203;moyu-x](https://github.com/moyu-x) in [#&#8203;701](https://github.com/go-co-op/gocron/pull/701)
- properly report lastRun for limit type jobs and RunNow by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;700](https://github.com/go-co-op/gocron/pull/700)

#### New Contributors

- [@&#8203;moyu-x](https://github.com/moyu-x) made their first contribution in [#&#8203;701](https://github.com/go-co-op/gocron/pull/701)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.7...v2.2.8>

### [`v2.2.7`](https://github.com/go-co-op/gocron/releases/tag/v2.2.7)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.6...v2.2.7)

#### What's Changed

- Allow more time for requestJobCtx by [@&#8203;drewgonzales360](https://github.com/drewgonzales360) in [#&#8203;699](https://github.com/go-co-op/gocron/pull/699)
- fix case where job removed causes panic when rescheduling by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;698](https://github.com/go-co-op/gocron/pull/698)

#### New Contributors

- [@&#8203;drewgonzales360](https://github.com/drewgonzales360) made their first contribution in [#&#8203;699](https://github.com/go-co-op/gocron/pull/699)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.6...v2.2.7>

### [`v2.2.6`](https://github.com/go-co-op/gocron/releases/tag/v2.2.6)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.5...v2.2.6)

#### What's Changed

- Bump github.com/stretchr/testify from 1.8.4 to 1.9.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;684](https://github.com/go-co-op/gocron/pull/684)
- elector & locker were failing to send out when not leader by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;688](https://github.com/go-co-op/gocron/pull/688)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.5...v2.2.6>

### [`v2.2.5`](https://github.com/go-co-op/gocron/releases/tag/v2.2.5)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.4...v2.2.5)

#### What's Changed

- remove codecov by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;672](https://github.com/go-co-op/gocron/pull/672)
- Bump golangci/golangci-lint-action from 3.7.0 to 4.0.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;675](https://github.com/go-co-op/gocron/pull/675)
- fix cases where default on send out is resulting in job not going out by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;686](https://github.com/go-co-op/gocron/pull/686)
  - This fixes two bugs related to limit mode and singleton mode having jobs stop running

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.4...v2.2.5>

### [`v2.2.4`](https://github.com/go-co-op/gocron/releases/tag/v2.2.4)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.3...v2.2.4)

#### What's Changed

- correct AfterJobRuns doc by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;670](https://github.com/go-co-op/gocron/pull/670)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.3...v2.2.4>

### [`v2.2.3`](https://github.com/go-co-op/gocron/releases/tag/v2.2.3)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.2...v2.2.3)

#### What's Changed

- fix RunNow() when calling from a job returned by Jobs() by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;668](https://github.com/go-co-op/gocron/pull/668)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.2...v2.2.3>

### [`v2.2.2`](https://github.com/go-co-op/gocron/releases/tag/v2.2.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.1...v2.2.2)

#### What's Changed

- Bump github.com/google/uuid from 1.5.0 to 1.6.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;664](https://github.com/go-co-op/gocron/pull/664)
- fix unsafe map usage in singletonMode by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) & [@&#8203;a3sroot](https://github.com/a3sroot) in [#&#8203;665](https://github.com/go-co-op/gocron/pull/665)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.1...v2.2.2>

### [`v2.2.1`](https://github.com/go-co-op/gocron/releases/tag/v2.2.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.2.0...v2.2.1)

#### What's Changed

- fix monthly jobs when counting days from the end by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;662](https://github.com/go-co-op/gocron/pull/662)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.2.0...v2.2.1>

### [`v2.2.0`](https://github.com/go-co-op/gocron/releases/tag/v2.2.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.1.2...v2.2.0)

#### What's Changed

- wait for new job to be fully created before returning by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;658](https://github.com/go-co-op/gocron/pull/658)
- BETA FEATURE: Add job monitor interface to allow for collecting job metrics by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;659](https://github.com/go-co-op/gocron/pull/659)
  - This is the first release of the monitor feature - it may be changed as initial implementations are created and feedback comes in

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.1.2...v2.2.0>

### [`v2.1.2`](https://github.com/go-co-op/gocron/releases/tag/v2.1.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.1.1...v2.1.2)

#### Fixes

- fix to handle when next ends up in the past by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;650](https://github.com/go-co-op/gocron/pull/650)
- make the order of the returned jobs slice deterministic by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;652](https://github.com/go-co-op/gocron/pull/652)

#### Documentation

- refactor: fix indent by [@&#8203;leedrum](https://github.com/leedrum) in [#&#8203;649](https://github.com/go-co-op/gocron/pull/649)

#### New Contributors

- [@&#8203;leedrum](https://github.com/leedrum) made their first contribution in [#&#8203;649](https://github.com/go-co-op/gocron/pull/649)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.1.1...v2.1.2>

### [`v2.1.1`](https://github.com/go-co-op/gocron/releases/tag/v2.1.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.1.0...v2.1.1)

#### What's Changed

- [bump golang.org/x/exp](7ee4c50f57)
- [fixup Job and Scheduler interface docs](a51820e30f)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.1.0...v2.1.1>

### [`v2.1.0`](https://github.com/go-co-op/gocron/releases/tag/v2.1.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.0.3...v2.1.0)

#### What's Changed

- add new features, OneTimeJob and Job.RunNow() by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;646](https://github.com/go-co-op/gocron/pull/646)

#### Version bumps

- Bump github/codeql-action from 2 to 3 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;644](https://github.com/go-co-op/gocron/pull/644)
- Bump github.com/google/uuid from 1.4.0 to 1.5.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;645](https://github.com/go-co-op/gocron/pull/645)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.0.3...v2.1.0>

### [`v2.0.3`](https://github.com/go-co-op/gocron/releases/tag/v2.0.3)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.0.2...v2.0.3)

#### Fixes

- fix weekly and monthly to handle midnight by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;643](https://github.com/go-co-op/gocron/pull/643) [#&#8203;642](https://github.com/go-co-op/gocron/issues/642)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.0.2...v2.0.3>

### [`v2.0.2`](https://github.com/go-co-op/gocron/releases/tag/v2.0.2)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.0.1...v2.0.2)

#### Fixes

- fix: check function param length and type by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;638](https://github.com/go-co-op/gocron/pull/638) raised in [#&#8203;637](https://github.com/go-co-op/gocron/issues/637)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.0.1...v2.0.2>

### [`v2.0.1`](https://github.com/go-co-op/gocron/releases/tag/v2.0.1)

[Compare Source](https://github.com/go-co-op/gocron/compare/v2.0.0...v2.0.1)

#### Fixes

- daily job next logic failed to consider 1 midnight attime by [@&#8203;JohnRoesler](https://github.com/JohnRoesler) in [#&#8203;635](https://github.com/go-co-op/gocron/pull/635)

#### Bumps

- Bump actions/checkout from 3 to 4 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;631](https://github.com/go-co-op/gocron/pull/631)
- Bump actions/setup-go from 4 to 5 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;630](https://github.com/go-co-op/gocron/pull/630)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.0.0...v2.0.1>

### [`v2.0.0`](https://github.com/go-co-op/gocron/releases/tag/v2.0.0)

[Compare Source](https://github.com/go-co-op/gocron/compare/v1.37.0...v2.0.0)

#### v2.0.0

It's here! 🎉  Take a look at the readme and godoc to see how the new version works!

Please give feedback! (Reach out on slack if you're interested in contributing so we can coordinate work 😄 ) And open issues if you find any bugs or have features you'd like to see supported!

#### New Contributors

- [@&#8203;AlphaNecron](https://github.com/AlphaNecron) made their first contribution in [#&#8203;613](https://github.com/go-co-op/gocron/pull/613)

**Full Changelog**: <https://github.com/go-co-op/gocron/compare/v2.0.0-alpha-1...v2.0.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yNjMuMSIsInVwZGF0ZWRJblZlciI6IjQyLjEuMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: https://gitea.t000-n.de/t.behrendt/realDynDNS/pulls/39
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-11-12 21:42:49 +01:00
a79ce64e82 fix(externalIpProvider/plain): when reading response body (#54)
All checks were successful
CD / Check changes (push) Successful in 6s
CD / test (push) Successful in 2m48s
CD / Build and push (amd64) (push) Successful in 40s
CD / Build and push (arm64) (push) Successful in 2m40s
CD / Create manifest (push) Successful in 8s
Fixing an error where the service crashes reading the response body, when no content-length header was provided in the response.

Reviewed-on: #54
Reviewed-by: branch-buddy <branch-buddy@t00n.de>
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2025-11-07 17:14:14 +01:00
660f2eac0d chore(deps): update actions/setup-go action to v6 (#52)
All checks were successful
CD / Check changes (push) Successful in 12s
CD / test (push) Successful in 1m50s
CD / Build and push (arm64) (push) Has been skipped
CD / Build and push (amd64) (push) Has been skipped
CD / Create manifest (push) Has been skipped
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-go](https://github.com/actions/setup-go) | action | major | `v5` -> `v6` |

---

### Release Notes

<details>
<summary>actions/setup-go (actions/setup-go)</summary>

### [`v6`](https://github.com/actions/setup-go/compare/v5...v6)

[Compare Source](https://github.com/actions/setup-go/compare/v5...v6)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45NS4wIiwidXBkYXRlZEluVmVyIjoiNDEuOTUuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: #52
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-09-04 07:28:46 +02:00
0a722ff1b2 chore(deps): update actions/checkout action to v5 (#44)
All checks were successful
CD / Check changes (push) Successful in 7s
CD / test (push) Successful in 2m0s
CD / Build and push (arm64) (push) Has been skipped
CD / Build and push (amd64) (push) Has been skipped
CD / Create manifest (push) Has been skipped
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://github.com/actions/checkout) | action | major | `v4` -> `v5` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

### [`v5`](https://github.com/actions/checkout/compare/v4...v5)

[Compare Source](https://github.com/actions/checkout/compare/v4...v5)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: #44
Reviewed-by: t.behrendt <t.behrendt@noreply.localhost>
Co-authored-by: Renovate Bot <renovate@t00n.de>
Co-committed-by: Renovate Bot <renovate@t00n.de>
2025-08-31 17:50:38 +02:00
5875af55bb ci: chore try out new renovate config (#50)
All checks were successful
CD / Check changes (push) Successful in 10s
CD / test (push) Successful in 2m53s
CD / Build and push (arm64) (push) Has been skipped
CD / Build and push (amd64) (push) Has been skipped
CD / Create manifest (push) Has been skipped
Reviewed-on: #50
Co-authored-by: Timo Behrendt <t.behrendt@t00n.de>
Co-committed-by: Timo Behrendt <t.behrendt@t00n.de>
2025-08-25 18:31:33 +02:00
12 changed files with 1672 additions and 678 deletions

View File

@@ -4,45 +4,32 @@ on:
push:
branches:
- main
paths:
- "go.mod"
- "go.sum"
- "**/*.go"
- "config.example.yaml"
- "Dockerfile"
- "Makefile"
workflow_dispatch:
env:
DOCKER_REGISTRY: gitea.t000-n.de
jobs:
check-changes:
name: Check changes
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get changes
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
code:
- 'go.mod'
- 'go.sum'
- '**/*.go'
- 'config.example.yaml'
- 'Dockerfile'
- 'Makefile'
test:
name: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup go
uses: actions/setup-go@v5
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version-file: go.mod
check-latest: true
- name: Create cache key
uses: https://gitea.com/actions/go-hashfiles@v0.0.1
uses: https://gitea.com/actions/go-hashfiles@264ae76b7e50173ce71ed7da4b48e5e517f3f9ec # v0.0.1
id: hash-go
with:
patterns: |
@@ -50,7 +37,7 @@ jobs:
go.sum
- name: cache go
id: cache-go
uses: actions/cache@v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
with:
path: |
/go_path
@@ -70,18 +57,16 @@ jobs:
arch: [amd64, arm64]
needs:
- test
- check-changes
if: ${{ needs.check-changes.outputs.code == 'true' }}
runs-on:
- ubuntu-latest
- linux_${{ matrix.arch }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Login to Registry
uses: docker/login-action@v3
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
@@ -92,7 +77,7 @@ jobs:
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}' | tr '[:upper:]' '[:lower:]') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
with:
context: .
file: ./Dockerfile
@@ -103,14 +88,36 @@ jobs:
tags: |
${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}-${{ matrix.arch }}
create_tag:
name: Create tag
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.tag.outputs.new-tag }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0
- uses: https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment@9841c96e72d4fc1ecec620e91e213895341cce86 # 0.1.22
id: tag
with:
token: ${{ secrets.GITEA_TOKEN }}
prerelease: ${{ github.event_name == 'workflow_dispatch' }}
- run: |
git tag ${{ steps.tag.outputs.new-tag }}
git push origin ${{ steps.tag.outputs.new-tag }}
- name: Set output
run: |
echo "tag=${{ steps.tag.outputs.new-tag }}" >> $GITHUB_OUTPUT
create_manifest:
name: Create manifest
needs:
- build_and_push
- create_tag
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Get Metadata
id: meta
@@ -119,7 +126,7 @@ jobs:
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Login to Registry
uses: docker/login-action@v3
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
@@ -127,8 +134,8 @@ jobs:
- name: Create manifest
run: |
docker manifest create ${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:latest \
docker manifest create ${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:${{ needs.create_tag.outputs.tag }} \
${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}-amd64 \
${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}-arm64
docker manifest push ${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:latest
docker manifest push ${{ env.DOCKER_REGISTRY }}/t.behrendt/${{ steps.meta.outputs.REPO_NAME }}:${{ needs.create_tag.outputs.tag }}

View File

@@ -13,14 +13,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup go
uses: actions/setup-go@v5
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version-file: go.mod
check-latest: true
- name: Create cache key
uses: https://gitea.com/actions/go-hashfiles@v0.0.1
uses: https://gitea.com/actions/go-hashfiles@264ae76b7e50173ce71ed7da4b48e5e517f3f9ec # v0.0.1
id: hash-go
with:
patterns: |
@@ -28,7 +28,7 @@ jobs:
go.sum
- name: cache go
id: cache-go
uses: actions/cache@v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
with:
path: |
/go_path

View File

@@ -1,4 +1,4 @@
FROM golang:1.24-alpine as build
FROM golang:1.25-alpine@sha256:26111811bc967321e7b6f852e914d14bede324cd1accb7f81811929a6a57fea9 as build
ARG GOARCH=amd64
@@ -9,6 +9,6 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${GOARCH} \
go build -trimpath -ldflags="-s -w" -o main .
FROM gcr.io/distroless/static-debian12
FROM gcr.io/distroless/static-debian12@sha256:4b2a093ef4649bccd586625090a3c668b254cfe180dee54f4c94f3e9bd7e381e
COPY --from=build /app/main /
CMD ["/main"]

View File

@@ -8,6 +8,10 @@ dns_provider:
config:
api_key: <your-api-key>
base_url: https://api.hosting.ionos.com/dns
# Optional: default TTL to use when patching records with missing TTL (default: 300)
# default_ttl: 300
# Optional: default priority to use when patching records with missing priority (default: 0)
# default_prio: 0
notification_provider:
type: gotify
config:

7
go.mod
View File

@@ -1,15 +1,18 @@
module realdnydns
go 1.24.0
go 1.25.0
require (
gitea.t000-n.de/t.behrendt/ionosDnsClient v1.0.2
github.com/go-co-op/gocron v1.37.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/oapi-codegen/runtime v1.1.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
)

14
go.sum
View File

@@ -1,3 +1,9 @@
gitea.t000-n.de/t.behrendt/ionosDnsClient v1.0.2 h1:EWz4kLLv7lSZx/F8K0/WxN5xeVNh7z4qbeDurZYBcNk=
gitea.t000-n.de/t.behrendt/ionosDnsClient v1.0.2/go.mod h1:HZfdMF7X9LK/3FP1jJVzMWbM5BcgcZg/Qwx2WClFipg=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -7,6 +13,7 @@ github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISk
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -15,6 +22,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -23,6 +32,7 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -30,8 +40,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=

View File

@@ -1,224 +0,0 @@
package ionosAPI
import (
"bytes"
"encoding/json"
"errors"
"io"
"net"
"net/http"
"net/url"
"realdnydns/model/common"
)
/**
* Docs: https://developer.hosting.ionos.com/docs/dns
*/
type IonosAPI interface {
GetARecord(tld string, subdomain string) (*common.ARecord, error)
SetARecord(tld string, subdomain string, ip net.IP, ttl int, prio int, disabled bool) (*common.ARecord, error)
GetZoneId(tld string) (string, error)
GetRecordId(zoneId string, tld string, subdomain string, recordType string) (string, error)
HttpCall(method string, url string, body io.Reader, queryParams map[string]string) (*http.Response, error)
}
type IonosAPIImpl struct {
APIKey string
BaseURL string
HTTPClient *http.Client
}
type ZonesResponse []struct {
Name string `json:"name"`
Id string `json:"id"`
Type string `json:"type"`
}
type ZoneResponse struct {
Id string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Records []RecordResponse `json:"records"`
}
type RecordResponse struct {
Id string `json:"id"`
Name string `json:"name"`
RootName string `json:"rootName"`
Type string `json:"type"`
Content string `json:"content"`
ChangeDate string `json:"changeDate"`
TTL int `json:"ttl"`
Prio int `json:"prio"`
Disabled bool `json:"disabled"`
}
type ChangeRecord struct {
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
TTL int `json:"ttl"`
Prio int `json:"prio"`
Disabled bool `json:"disabled"`
}
type ChangeRecordRequest struct {
Content string `json:"content"`
TTL int `json:"ttl"`
Prio int `json:"prio"`
Disabled bool `json:"disabled"`
}
func New(APIKey string, BaseURL string) IonosAPI {
return &IonosAPIImpl{
APIKey: APIKey,
BaseURL: BaseURL,
HTTPClient: &http.Client{},
}
}
func (i *IonosAPIImpl) HttpCall(method string, path string, body io.Reader, queryParams map[string]string) (*http.Response, error) {
requestUrl, _ := url.Parse(i.BaseURL + path)
query := requestUrl.Query()
for key, value := range queryParams {
query.Add(key, value)
}
requestUrl.RawQuery = query.Encode()
req, err := http.NewRequest(method, requestUrl.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("X-API-Key", i.APIKey)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
return i.HTTPClient.Do(req)
}
func (i *IonosAPIImpl) GetZoneId(tld string) (string, error) {
res, err := i.HttpCall("GET", "/v1/zones", nil, nil)
if err != nil {
return "", err
}
responseBody := make([]byte, res.ContentLength)
res.Body.Read(responseBody)
zones := []ZoneResponse{}
json.Unmarshal(responseBody, &zones)
for _, z := range zones {
if z.Name == tld {
return z.Id, nil
}
}
return "", errors.New("zone not found")
}
func (i *IonosAPIImpl) GetRecordId(zoneId string, tld string, subdomain string, recordType string) (string, error) {
var domain string
if subdomain == "@" || subdomain == "" {
domain = tld
} else {
domain = subdomain + "." + tld
}
res, err := i.HttpCall("GET", "/v1/zones/"+zoneId, nil, map[string]string{"recordName": domain, "recordType": recordType})
if err != nil {
return "", err
}
responseBody := make([]byte, res.ContentLength)
res.Body.Read(responseBody)
zone := ZoneResponse{}
json.Unmarshal(responseBody, &zone)
for _, record := range zone.Records {
if record.Type == recordType && record.Name == domain {
return record.Id, nil
}
}
return "", errors.New("record not found")
}
func (i *IonosAPIImpl) SetARecord(tld string, subdomain string, ip net.IP, ttl int, prio int, disabled bool) (*common.ARecord, error) {
zoneId, err := i.GetZoneId(tld)
if err != nil {
return nil, err
}
recordId, err := i.GetRecordId(zoneId, tld, subdomain, "A")
if err != nil {
return nil, err
}
changeRecordRequest, err := json.Marshal(ChangeRecordRequest{
Content: ip.String(),
TTL: ttl,
Prio: prio,
Disabled: false,
})
if err != nil {
return nil, err
}
res, err := i.HttpCall("PUT", "/v1/zones/"+zoneId+"/records/"+recordId, bytes.NewReader(changeRecordRequest), nil)
if err != nil {
return nil, err
}
responseBody := make([]byte, res.ContentLength)
res.Body.Read(responseBody)
if res.StatusCode != 200 {
return nil, errors.New("error updating record")
}
changeRecord := ChangeRecord{}
json.Unmarshal(responseBody, &changeRecord)
return &common.ARecord{
Domain: changeRecord.Name,
IP: changeRecord.Content,
TTL: changeRecord.TTL,
Prio: changeRecord.Prio,
Disabled: changeRecord.Disabled,
}, nil
}
func (ionos *IonosAPIImpl) GetARecord(tld string, subdomain string) (*common.ARecord, error) {
zoneId, err := ionos.GetZoneId(tld)
if err != nil {
return nil, err
}
recordId, err := ionos.GetRecordId(zoneId, tld, subdomain, "A")
if err != nil {
return nil, err
}
res, err := ionos.HttpCall("GET", "/v1/zones/"+zoneId+"/records/"+recordId, nil, nil)
if err != nil {
return nil, err
}
responseBody := make([]byte, res.ContentLength)
res.Body.Read(responseBody)
record := RecordResponse{}
json.Unmarshal(responseBody, &record)
return &common.ARecord{
Domain: record.Name,
IP: record.Content,
TTL: record.TTL,
Prio: record.Prio,
Disabled: record.Disabled,
}, nil
}

View File

@@ -1,285 +0,0 @@
package ionosAPI
import (
"encoding/json"
"net"
"net/http"
"net/http/httptest"
"testing"
)
func utilMockServerImpl() func(w http.ResponseWriter, r *http.Request) {
zonesResponse := []ZoneResponse{
{
Name: "example.com",
Id: "1234567890",
Type: "NATIVE",
},
{
Name: "notTheExample.org",
Id: "0987654321",
Type: "SLAVE",
},
}
zonesResponseJson, _ := json.Marshal(zonesResponse)
recordResponseSub := RecordResponse{
Id: "abcdefghij",
Name: "example.com",
RootName: "example.com",
Type: "A",
Content: "127.0.0.1",
ChangeDate: "2019-12-09T13:04:25.772Z",
TTL: 300,
Prio: 0,
Disabled: false,
}
recordResponseSubJson, _ := json.Marshal(recordResponseSub)
recordResponseTLD := RecordResponse{
Id: "jihgfedcba",
Name: "sub.example.com",
RootName: "example.com",
Type: "A",
Content: "127.0.0.2",
ChangeDate: "2019-12-09T13:04:25.772Z",
TTL: 300,
Prio: 0,
Disabled: false,
}
recordResponseTLDJson, _ := json.Marshal(recordResponseTLD)
zoneResponse := ZoneResponse{
Id: "1234567890",
Name: "example.com",
Type: "NATIVE",
Records: []RecordResponse{
recordResponseSub,
recordResponseTLD,
},
}
zoneResponseJson, _ := json.Marshal(zoneResponse)
changeRecord := ChangeRecord{
Name: "sub.example.com",
Type: "A",
Content: "127.0.0.1",
TTL: 300,
Prio: 0,
Disabled: false,
}
changeRecordJson, _ := json.Marshal(changeRecord)
return func(w http.ResponseWriter, r *http.Request) {
var response []byte
if r.Method == "GET" {
if r.RequestURI == "/v1/zones" {
response = zonesResponseJson
} else if r.RequestURI == "/v1/zones/1234567890?recordName=example.com&recordType=A" ||
r.RequestURI == "/v1/zones/1234567890?recordName=sub.example.com&recordType=A" {
response = zoneResponseJson
} else if r.RequestURI == "/v1/zones/1234567890/records/abcdefghij" {
response = recordResponseSubJson
} else if r.RequestURI == "/v1/zones/1234567890/records/jihgfedcba" {
response = recordResponseTLDJson
}
} else if r.Method == "PUT" {
response = changeRecordJson
} else {
response = []byte("404")
}
w.Write(response)
}
}
func TestHttpCall(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(utilMockServerImpl()))
defer mockServer.Close()
ionosAPI := New("dummyKey", mockServer.URL)
t.Run("returns response for GET request", testHttpCallGet(ionosAPI))
t.Run("returns response for PUT request", testHttpCallPut(ionosAPI))
t.Run("returns error for non existing endpoint", testHttpCallNonExistingEndpoint())
}
func testHttpCallGet(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
res, err := api.HttpCall("GET", "/v1/zones", nil, nil)
if err != nil {
t.Fatalf("HttpCall() returned unexpected error: %v", err)
}
if res.StatusCode != 200 {
t.Fatalf("HttpCall() returned unexpected status code: %v instead of 200", res.StatusCode)
}
}
}
func testHttpCallPut(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
res, err := api.HttpCall("PUT", "/v1/zones/1234567890/records/abcdefghij", nil, nil)
if err != nil {
t.Fatalf("HttpCall() returned unexpected error: %v", err)
}
if res.StatusCode != 200 {
t.Fatalf("HttpCall() returned unexpected status code: %v instead of 200", res.StatusCode)
}
}
}
func testHttpCallNonExistingEndpoint() func(t *testing.T) {
return func(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
}))
defer mockServer.Close()
api := New("dummyKey", mockServer.URL)
res, err := api.HttpCall("GET", "/v1/nonExistingEndpoint", nil, nil)
if err != nil {
t.Fatalf("HttpCall() returned unexpected error: %v", err)
}
if res.StatusCode != 404 {
t.Fatalf("HttpCall() returned unexpected status code: %v instead of 404", res.StatusCode)
}
}
}
func TestGetZoneId(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(utilMockServerImpl()))
ionosAPI := New("dummyKey", mockServer.URL)
t.Run("returns zoneId for tracked domain", testGetZoneIdSuccess(ionosAPI))
t.Run("returns error for non tracked domain", testGetZoneIdNoMatch(ionosAPI))
}
func testGetZoneIdSuccess(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
zoneId, err := api.GetZoneId("example.com")
if err != nil {
t.Fatalf("GetZoneId() returned unexpected error: %v", err)
}
if zoneId != "1234567890" {
t.Fatalf("GetZoneId() returned unexpected zoneId: %v instead of 1234567890", zoneId)
}
}
}
func testGetZoneIdNoMatch(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
zoneId, err := api.GetZoneId("nonTrackedDomain.com")
if err == nil || zoneId != "" {
t.Fatalf("GetZoneId() did not return an error for a non tracked domain")
}
}
}
func TestGetRecordId(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(utilMockServerImpl()))
defer mockServer.Close()
ionosAPI := New("dummyKey", mockServer.URL)
t.Run("returns zoneId for top level domain", testGetRecordIdForTopLevelDomain(ionosAPI))
t.Run("returns zoneId for subdomain", testGetRecordIdForSubdomain(ionosAPI))
t.Run("returns error for untracked subdomain", testGetRecordIdWithUntrackedSubdomain(ionosAPI))
}
func testGetRecordIdForTopLevelDomain(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
recordId, err := api.GetRecordId("1234567890", "example.com", "", "A")
if err != nil {
t.Fatalf("GetZoneId() returned unexpected error: %v", err)
}
if recordId != "abcdefghij" {
t.Fatalf("GetZoneId() returned unexpected zoneId: %v instead of abcdefghij", recordId)
}
}
}
func testGetRecordIdForSubdomain(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
recordId, err := api.GetRecordId("1234567890", "example.com", "sub", "A")
if err != nil {
t.Fatalf("GetZoneId() returned unexpected error: %v", err)
}
if recordId != "jihgfedcba" {
t.Fatalf("GetZoneId() returned unexpected zoneId: %v instead of jihgfedcba", recordId)
}
}
}
func testGetRecordIdWithUntrackedSubdomain(api IonosAPI) func(t *testing.T) {
return func(t *testing.T) {
recordId, err := api.GetRecordId("1234567890", "example.com", "untrackedSub", "A")
if err == nil && recordId == "" {
t.Fatalf("GetZoneId() did not return an error for a non tracked domain")
}
}
}
func TestGetARecord(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(utilMockServerImpl()))
defer mockServer.Close()
ionosAPI := New("dummyKey", mockServer.URL)
t.Run("returns A record for top level domain", testGetARecordFor(ionosAPI, "example.com", ""))
t.Run("returns A record for subdomain", testGetARecordFor(ionosAPI, "example.com", "sub"))
}
func testGetARecordFor(api IonosAPI, tld string, subdomain string) func(t *testing.T) {
return func(t *testing.T) {
var domain string
if subdomain == "" {
domain = tld
} else {
domain = subdomain + "." + tld
}
record, err := api.GetARecord(tld, subdomain)
if err != nil {
t.Fatalf("GetARecord() returned unexpected error: %v", err)
}
if record.Domain != domain {
t.Fatalf("GetARecord() returned unexpected record for: %v instead of %v", record.Domain, domain)
}
}
}
func TestSetARecord(t *testing.T) {
mockServer := httptest.NewServer(http.HandlerFunc(utilMockServerImpl()))
defer mockServer.Close()
ionosAPI := New("dummyKey", mockServer.URL)
changedARecord, err := ionosAPI.SetARecord("example.com", "sub", net.ParseIP("127.0.0.1"), 300, 0, false)
if err != nil {
t.Fatalf("SetARecord() returned unexpected error: %v", err)
}
if changedARecord.Domain != "sub.example.com" {
t.Fatalf("SetARecord() returned unexpected record for: %v instead of sub.example.com", changedARecord.Domain)
}
}

View File

@@ -1,20 +1,30 @@
package ionosDnsProvider
import (
"context"
"errors"
"net"
"net/http"
"realdnydns/model/common"
"realdnydns/pkg/dnsProvider"
ionosAPI "realdnydns/pkg/dnsProvider/ionos/api"
"sync"
ionosDnsClient "gitea.t000-n.de/t.behrendt/ionosDnsClient"
)
type IONOS struct {
API ionosAPI.IonosAPI
client *ionosDnsClient.ClientWithResponses
zoneIdMap map[string]string
defaultTtl int
defaultPrio int
zoneIdMapMutex sync.Mutex
}
type IONOSConfig struct {
APIKey string `yaml:"api_key"`
BaseURL string `yaml:"base_url"`
APIKey string `yaml:"api_key"`
BaseURL string `yaml:"base_url"`
DefaultTTL *int `yaml:"default_ttl,omitempty"`
DefaultPrio *int `yaml:"default_prio,omitempty"`
}
func NewIonos(config *IONOSConfig) (dnsProvider.DNSProvider, error) {
@@ -26,15 +36,231 @@ func NewIonos(config *IONOSConfig) (dnsProvider.DNSProvider, error) {
return nil, errors.New("base_url is required")
}
options := []ionosDnsClient.ClientOption{
ionosDnsClient.WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
req.Header.Set("X-API-Key", config.APIKey)
return nil
}),
}
client, err := ionosDnsClient.NewClientWithResponses(config.BaseURL, options...)
if err != nil {
return nil, err
}
// Set default values for TTL and Prio if not provided
defaultTtl := 300
if config.DefaultTTL != nil {
defaultTtl = *config.DefaultTTL
}
defaultPrio := 0
if config.DefaultPrio != nil {
defaultPrio = *config.DefaultPrio
}
return &IONOS{
ionosAPI.New(config.APIKey, config.BaseURL),
client: client,
zoneIdMap: make(map[string]string),
defaultTtl: defaultTtl,
defaultPrio: defaultPrio,
}, nil
}
func (ionos *IONOS) UpdateRecord(tld string, subdomain string, ip net.IP, ttl int, prio int, disabled bool) (*common.ARecord, error) {
return ionos.API.SetARecord(tld, subdomain, ip, ttl, prio, disabled)
func (i *IONOS) UpdateRecord(tld string, subdomain string, ip net.IP, ttl int, prio int, disabled bool) (*common.ARecord, error) {
zoneId, err := i.getZoneIdForTld(tld)
if err != nil {
return nil, err
}
domain := assembleTldSubdomainToDomain(tld, subdomain)
recordId, err := i.getRecordIdForDomain(zoneId, domain)
if err != nil {
return nil, err
}
ipString := ip.String()
res, err := i.client.UpdateRecordWithResponse(context.Background(), zoneId, recordId, ionosDnsClient.RecordUpdate{
Content: &ipString,
Ttl: &ttl,
Prio: &prio,
Disabled: &disabled,
})
if err != nil || res.StatusCode() != 200 {
return nil, errors.New("failed to update record")
}
if res.JSON200 == nil {
return nil, errors.New("record response is empty")
}
record := *res.JSON200
if record.Name == nil || record.Content == nil {
return nil, errors.New("record is empty")
}
// Handle optional fields with defaults
recordTtl := 0
if record.Ttl != nil {
recordTtl = *record.Ttl
}
recordPrio := 0
if record.Prio != nil {
recordPrio = *record.Prio
}
recordDisabled := false
if record.Disabled != nil {
recordDisabled = *record.Disabled
}
return &common.ARecord{
Domain: *record.Name,
IP: *record.Content,
TTL: recordTtl,
Prio: recordPrio,
Disabled: recordDisabled,
}, nil
}
func (ionos *IONOS) GetRecord(tld string, subdomain string) (*common.ARecord, error) {
return ionos.API.GetARecord(tld, subdomain)
func (i *IONOS) getZoneIdForTld(tld string) (string, error) {
i.zoneIdMapMutex.Lock()
defer i.zoneIdMapMutex.Unlock()
if zoneId, ok := i.zoneIdMap[tld]; ok {
return zoneId, nil
}
res, err := i.client.GetZonesWithResponse(context.Background())
if err != nil {
return "", err
} else if res.StatusCode() != 200 {
return "", errors.New("failed to get zones")
}
if res.JSON200 == nil {
return "", errors.New("zones response is empty")
}
zones := *res.JSON200
for _, zone := range zones {
if *zone.Name == tld {
if zone.Id == nil || *zone.Id == "" {
return "", errors.New("zone id is empty")
}
i.zoneIdMap[tld] = *zone.Id
return *zone.Id, nil
}
}
return "", errors.New("no zone found")
}
var recordTypeA = "A"
func assembleTldSubdomainToDomain(tld string, subdomain string) string {
if subdomain == "@" || subdomain == "" {
return tld
}
return subdomain + "." + tld
}
func (i *IONOS) getRecordIdForDomain(zoneId string, domain string) (string, error) {
res, err := i.client.GetZoneWithResponse(context.Background(), zoneId, &ionosDnsClient.GetZoneParams{
RecordName: &domain,
RecordType: &recordTypeA,
})
if err != nil || res.StatusCode() != 200 {
return "", errors.New("failed to get zone")
}
if res.JSON200 == nil {
return "", errors.New("zone response is empty")
}
zone := *res.JSON200
for _, record := range *zone.Records {
if *record.Name == domain && *record.Type == ionosDnsClient.RecordTypes("A") {
return *record.Id, nil
}
}
return "", errors.New("no record found")
}
func (i *IONOS) GetRecord(tld string, subdomain string) (*common.ARecord, error) {
zoneId, err := i.getZoneIdForTld(tld)
if err != nil {
return nil, err
}
domain := assembleTldSubdomainToDomain(tld, subdomain)
recordId, err := i.getRecordIdForDomain(zoneId, domain)
if err != nil {
return nil, err
}
res, err := i.client.GetRecordWithResponse(context.Background(), zoneId, recordId)
if err != nil || res.StatusCode() != 200 || res.JSON200 == nil {
return nil, errors.New("failed to get record")
}
record := *res.JSON200
if record.Name == nil || *record.Name == "" || *record.Name != domain {
return nil, errors.New("record name does not match or is empty")
}
if record.Content == nil || *record.Content == "" {
return nil, errors.New("record content is empty")
}
needsUpdate := false
ttl := i.defaultTtl
if record.Ttl != nil {
ttl = *record.Ttl
} else {
needsUpdate = true
}
prio := i.defaultPrio
if record.Prio != nil {
prio = *record.Prio
} else {
needsUpdate = true
}
disabled := false
if record.Disabled != nil {
disabled = *record.Disabled
} else {
needsUpdate = true
}
// realDynDns requires every field to be set, so we need to update the record if any of the fields are missing
if needsUpdate {
ip := net.ParseIP(*record.Content)
if ip == nil {
return nil, errors.New("invalid IP address in record content")
}
updatedRecord, err := i.UpdateRecord(tld, subdomain, ip, ttl, prio, disabled)
if err != nil {
return nil, err
}
return updatedRecord, nil
}
return &common.ARecord{
Domain: *record.Name,
IP: *record.Content,
TTL: ttl,
Prio: prio,
Disabled: disabled,
}, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ package externalIpProvider
import (
"errors"
"io"
"net"
"net/http"
"net/url"
@@ -40,12 +41,15 @@ func (p *ExternalIpProviderImplPlain) GetExternalIp() (net.IP, error) {
}
if res.StatusCode != 200 {
res.Body.Close()
return nil, errors.New("unexpected status code")
}
responseBody := make([]byte, res.ContentLength)
res.Body.Read(responseBody)
defer res.Body.Close()
responseBody, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return nil, err
}
parsedIp := net.ParseIP(string(responseBody))
if parsedIp == nil {

View File

@@ -1,12 +1,22 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>t.behrendt/renovate-configs:common",
"local>t.behrendt/renovate-configs:action"
],
"packageRules": [
{
"matchPackageNames": [
"golang",
"gomod",
"go"
],
"groupName": "go version"
"groupName": "go version",
"matchUpdateTypes": [
"major",
"minor",
"patch"
]
}
]
}
}