ci: cache GitHub builds and document image distribution
CI / Test and publish (push) Successful in 25s

This commit is contained in:
2026-09-10 19:53:47 +04:00
parent f096b25516
commit a1b49dfb8a
2 changed files with 104 additions and 30 deletions
+20 -6
View File
@@ -37,8 +37,17 @@ jobs:
- name: Check Docker
run: docker version
- uses: docker/setup-buildx-action@v4
- name: Check formatting, lint and build the release binary
run: docker build --target test --tag "$TEST_IMAGE" .
uses: docker/build-push-action@v7
with:
context: .
target: test
tags: ${{ env.TEST_IMAGE }}
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run E2E tests without network access
run: >-
@@ -47,11 +56,16 @@ jobs:
--memory 256m --cpus 2 --pids-limit 128 "$TEST_IMAGE"
- name: Build the runtime image from the tested binary
run: >-
docker build --target runtime
--label "org.opencontainers.image.revision=$REVISION"
--label "org.opencontainers.image.source=$SOURCE"
--tag "$IMAGE:sha-$REVISION" .
uses: docker/build-push-action@v7
with:
context: .
target: runtime
tags: ${{ env.IMAGE }}:sha-${{ env.REVISION }}
labels: |
org.opencontainers.image.revision=${{ env.REVISION }}
org.opencontainers.image.source=${{ env.SOURCE }}
load: true
cache-from: type=gha
- name: Smoke-test the runtime image
run: |
+84 -24
View File
@@ -4,41 +4,83 @@ A small, stateless Rust service that turns [Vacationplanner](https://vacationpla
vacations into an iCalendar subscription for Apple Calendar and other calendar apps.
It only calls `GET /v1/vacations/year/{year}`.
## Run
## Ready-made Docker images
The registry requires authentication. Log in with a `packager` token that has
`read:package` permission, then run the published `linux/amd64` image:
Both registries provide the same service for `linux/amd64`. Each platform tests,
builds and publishes its own image:
| Registry | Package | Image |
| --- | --- | --- |
| GitHub Container Registry | [GitHub package](https://github.com/valentineus/vacationplanner2ics/pkgs/container/vacationplanner2ics) | `ghcr.io/valentineus/vacationplanner2ics:latest` |
| Gitea Container Registry | [Gitea package](https://code.popov.link/packager/-/packages/container/vacationplanner2ics/latest) | `code.popov.link/packager/vacationplanner2ics:latest` |
Available tags:
- `latest`: the latest successful `master` build on that platform.
- `sha-<full-commit-sha>`: the tested commit.
- `v*`: the corresponding Git tag; does not overwrite `latest`.
Pull from GitHub:
```sh
docker pull ghcr.io/valentineus/vacationplanner2ics:latest
```
Gitea currently requires a `packager` token with `read:package` permission.
Enter the token at the password prompt:
```sh
docker login code.popov.link --username packager
docker run -d --name vacationplanner2ics --restart unless-stopped --read-only \
-p 127.0.0.1:8080:8080 code.popov.link/packager/vacationplanner2ics:latest
docker pull code.popov.link/packager/vacationplanner2ics:latest
```
Or build from source with Docker Compose (also works natively on ARM64):
## Self-hosted usage
Docker is the only runtime requirement. Start either image, for example:
```sh
docker compose up -d --build app
docker run -d --name vacationplanner2ics --restart unless-stopped --read-only \
-p 127.0.0.1:8080:8080 ghcr.io/valentineus/vacationplanner2ics:latest
```
The service listens on `127.0.0.1:8080`. `GET /healthz` returns `ok`.
For public access, put an HTTPS reverse proxy in front of it.
To use Gitea, replace the image name with the Gitea image listed above.
Verify that the service is ready:
```sh
curl --fail http://127.0.0.1:8080/healthz
```
The expected response is `ok`. The example exposes the service on
`127.0.0.1:8080`; for public access, put an HTTPS reverse proxy in front of it.
The image serves HTTP only. Its `scratch` filesystem contains a static musl binary
and CA certificates for outgoing HTTPS requests, with no shell or package manager.
It runs as an unprivileged user.
## Subscribe
## Calendar subscription and privacy
Add a calendar subscription using this URL, replacing the host and placeholder:
**Self-hosting is recommended. Do not blindly trust any hosted instance,
including the maintainer's.** Subscription URLs contain your Vacationplanner
API token. Service operators and hosting, CDN or proxy providers that terminate
HTTPS can read the full request URL, including its GET parameters. HTTPS protects
the connection; it does not hide the token from those operators.
The maintainer's instance is at [vacationplanner.popov.link](https://vacationplanner.popov.link/).
Query-string logging is disabled on the maintainer's server, but that is not a
guarantee of confidentiality across the infrastructure. Use it only if you accept
that trust requirement. Be careful with subscription URLs and prefer a server
you control.
Add a calendar subscription using this URL, replacing the token placeholder.
For self-hosting, also replace the host with your own:
```text
https://calendar.example.com/calendar.ics?token=YOUR_API_TOKEN
https://vacationplanner.popov.link/calendar.ics?token=YOUR_API_TOKEN
```
To select years:
```text
https://calendar.example.com/calendar.ics?token=YOUR_API_TOKEN&years=2026,2027,2028
https://vacationplanner.popov.link/calendar.ics?token=YOUR_API_TOKEN&years=2026,2027,2028
```
| Query parameter | Meaning |
@@ -97,28 +139,46 @@ access disabled. They cover calendar parsing with an independent iCalendar
library, year selection, input/upstream failures and subscription updates.
Tests use fake credentials only.
## Building from source
Clone the canonical repository and build with Docker Compose. This also works
natively on ARM64:
```sh
git clone https://code.popov.link/valentineus/vacationplanner2ics.git
cd vacationplanner2ics
docker compose up -d --build app
```
## CI and dependency updates
[Gitea Actions](https://code.popov.link/valentineus/vacationplanner2ics/actions)
runs one CI workflow for `master` pushes, pull requests, `v*` tags and manual runs: formatting,
and [GitHub Actions](https://github.com/valentineus/vacationplanner2ics/actions)
run independent CI workflows for `master` pushes, pull requests, `v*` tags and manual runs: formatting,
Clippy with warnings denied, release compilation, the four E2E tests without
network access, and an HTTP/shutdown check of the actual runtime image.
Only successful `master` builds and `v*` tags publish images to
`code.popov.link/packager/vacationplanner2ics`:
Only successful `master` builds and `v*` tags publish images. Gitea publishes
only to `code.popov.link`; GitHub publishes only to `ghcr.io`.
- `latest`: the latest successful `master` build.
- `sha-<full-commit-sha>`: the tested commit.
- `v*`: the corresponding Git tag; does not overwrite `latest`.
Both workflows build natively on AMD64, limit compilation to two jobs and package
the already-tested binary. Gitea reuses the runner's BuildKit/Cargo caches;
GitHub saves Docker build layers in its Actions cache.
CI uses the runner's native AMD64 architecture, reuses BuildKit/Cargo caches,
limits compilation to two jobs and packages the already-tested binary.
Publishing requires the Actions secret `REGISTRY_TOKEN`, belonging to `packager`
with `write:package` permission. No Vacationplanner token is used in CI.
Gitea publishing uses the Actions secret `REGISTRY_TOKEN`, belonging to `packager`
with `write:package` permission. GitHub uses its automatic `GITHUB_TOKEN` with
`packages: write`; no extra publishing secret is needed. No Vacationplanner
token is used in either CI.
Renovate runs daily or manually, using `RENOVATE_TOKEN` for Gitea and
Renovate runs on Gitea daily or manually, using `RENOVATE_TOKEN` for Gitea and
`RENOVATE_GITHUB_TOKEN` for dependency metadata. Non-major updates are grouped;
updates require review and are not merged automatically.
## License
[MIT](LICENSE)
---
Repository locations: [Gitea — canonical source](https://code.popov.link/valentineus/vacationplanner2ics) · [GitHub — secondary mirror](https://github.com/valentineus/vacationplanner2ics).
Changes are pushed to Gitea and automatically mirrored to GitHub.