Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/SUPPORTED-PACKAGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,11 @@ aurtomator includes 14 version detection strategies. Choose by where your upstre
| Strategy | Source | Best for | Pre-release filter |
|---|---|---|---|
| `github-release` | GitHub Releases API | Most GitHub projects | Auto (excludes pre-releases) |
| `github-tag` | GitHub Tags API | Projects without formal releases | Manual (`tag_pattern`) |
| `github-tag` | GitHub Tags API | Projects without formal releases | Auto (`v1.2.3` tags only) |
| `github-nightly` | GitHub Releases API | Nightly/dev builds (4 patterns) | Nightly-specific |
| `github-asset` | GitHub Releases API | Assets re-uploaded on a fixed tag (model zoos) | N/A (fixed tag) |
| `gitlab-tag` | GitLab Tags API | GitLab.com, invent.kde.org, self-hosted | Manual (`tag_pattern`) |
| `gitea-tag` | Gitea/Forgejo Tags API | Codeberg, self-hosted Gitea | Manual (`tag_pattern`) |
| `gitlab-tag` | GitLab Tags API | GitLab.com, invent.kde.org, self-hosted | Auto (`v1.2.3` tags only) |
| `gitea-tag` | Gitea/Forgejo Tags API | Codeberg, self-hosted Gitea | Auto (`v1.2.3` tags only) |
| `git-latest` | Bare git clone | `-git` packages (commit tracking) | N/A |
| `pypi` | PyPI JSON API | Python packages | Auto (stable only) |
| `npm` | npm registry | Node.js packages | Auto (`latest` tag) |
Expand All @@ -206,7 +206,7 @@ aurtomator includes 14 version detection strategies. Choose by where your upstre

- **GitHub rate limit:** the GitHub strategies send `GITHUB_TOKEN` when it is set in the environment. The `check-updates` workflow sets it to the workflow token, which allows 1,000 requests per hour per repository. Without a token, the limit is 60 requests per hour.
- **No retry on API failure:** strategies make one attempt. A transient failure fails the package for that run.
- **Tag strategies include pre-releases:** `github-tag`, `gitlab-tag` and `gitea-tag` pick the highest tag with `sort -V`, which places `1.0.0-rc1` above `1.0.0`. Filter with `tag_pattern`.
- **Tag strategies take only `v1.2.3` and `1.2.3` by default:** without `tag_pattern`, and without `tag_version_regex` for `github-tag`, `github-tag`, `gitlab-tag` and `gitea-tag` leave out every other tag, `latest`, `1.0.0-rc1` and `1.0.0rc1` included. A `tag_pattern` replaces that filter. They pick the highest tag with `sort -V`, which places `1.0.0-rc1` above `1.0.0`, so a pattern that lets pre-release tags through picks them.
- **`gitea-tag` fetches at most 50 tags:** there is no pagination.
- **`gitlab-tag` has no authentication:** private GitLab projects cannot be queried.

Expand Down
15 changes: 7 additions & 8 deletions docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,10 @@ upstream:
| Field | Required | Description |
|------------------------------|----------|------------------------------------------|
| `upstream.project` | yes | `owner/repo` format |
| `upstream.tag_pattern` | no | Glob to filter tags (e.g. `v*`) |
| `upstream.tag_version_regex` | no | ERE with one capture group |
| `upstream.tag_pattern` | no | Glob to filter tags (e.g. `release-*`), replaces the default filter |
| `upstream.tag_version_regex` | no | ERE with one capture group, replaces the default filter |

**How the version is extracted:** fetches the first 100 tags and filters them by `tag_pattern`, where `*` matches anything and the match is anchored at the start. It strips a leading `v`, or applies `tag_version_regex`, and returns the highest tag by `sort -V`. `sort -V` places `1.0.0-rc1` above `1.0.0`, so filter pre-release tags with `tag_pattern`.
**How the version is extracted:** fetches the first 100 tags. Without `tag_pattern` and `tag_version_regex`, it keeps the tags of the form `v1.2.3` or `1.2.3`, so `latest`, `1.0.0-rc1` and `1.0.0rc1` are left out, and a project without such a tag fails the check with a message that names both fields. `tag_pattern` filters the tags instead, where `*` matches anything and the match is anchored at the start. The strategy strips a leading `v`, or applies `tag_version_regex`, and returns the highest tag by `sort -V`. `sort -V` places `1.0.0-rc1` above `1.0.0`, so a `tag_pattern` that lets pre-release tags through picks them.

**Authentication:** same as `github-release`.

Expand All @@ -153,7 +153,6 @@ name: my-package
strategy: github-tag
upstream:
project: owner/repo
tag_pattern: "v*"
```

### 3.3 github-nightly
Expand Down Expand Up @@ -268,9 +267,9 @@ upstream:
|------------------------|----------|-------------------------------------------------------|
| `upstream.host` | yes | GitLab hostname (e.g. `invent.kde.org`, `gitlab.com`) |
| `upstream.project` | yes | Project path (e.g. `network/my-kde-app`) |
| `upstream.tag_pattern` | no | Glob to filter tags |
| `upstream.tag_pattern` | no | Glob to filter tags, replaces the default filter |

**How the version is extracted:** URL-encodes the project path (`/` becomes `%2F`), fetches the first 100 tags, filters them by `tag_pattern`, strips a leading `v` and returns the highest by `sort -V`. There is no authentication, so private projects cannot be queried.
**How the version is extracted:** URL-encodes the project path (`/` becomes `%2F`) and fetches the first 100 tags. Without `tag_pattern`, it keeps the tags of the form `v1.2.3` or `1.2.3`, and a project without such a tag fails the check with a message that names `tag_pattern`. With it, it filters the tags by the pattern. It strips a leading `v` and returns the highest by `sort -V`. There is no authentication, so private projects cannot be queried.

**Example YAML:**

Expand All @@ -294,9 +293,9 @@ upstream:
|------------------------|----------|------------------------------------------|
| `upstream.host` | yes | Instance hostname (e.g. `codeberg.org`) |
| `upstream.project` | yes | `owner/repo` format |
| `upstream.tag_pattern` | no | Glob to filter tags |
| `upstream.tag_pattern` | no | Glob to filter tags, replaces the default filter |

**How the version is extracted:** fetches up to 50 tags without pagination, filters them by `tag_pattern`, strips a leading `v` and returns the highest by `sort -V`.
**How the version is extracted:** fetches up to 50 tags without pagination. Without `tag_pattern`, it keeps the tags of the form `v1.2.3` or `1.2.3`, and a project without such a tag fails the check with a message that names `tag_pattern`. With it, it filters the tags by the pattern. It strips a leading `v` and returns the highest by `sort -V`.

**Example YAML:**

Expand Down
16 changes: 9 additions & 7 deletions packages/example.yml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ upstream:
# tag_version_regex: '(.+)-stable' # optional, ERE with a capture group

# --- github-tag -------------------------------------------------------------
# Reads up to 100 tags from /repos/{project}/tags, pre-release tags included.
# Filter them with tag_pattern or tag_version_regex.
# Reads up to 100 tags from /repos/{project}/tags and keeps v1.2.3 and 1.2.3.
# tag_pattern or tag_version_regex replaces that filter.
#
# strategy: github-tag
# upstream:
# project: owner/repo # required
# tag_pattern: 'v*' # optional, glob filter
# tag_pattern: 'release-*' # optional, glob filter
# tag_version_regex: 'release-(.+)' # optional, ERE with a capture group

# --- github-nightly ---------------------------------------------------------
Expand Down Expand Up @@ -102,22 +102,24 @@ upstream:
# - voice-b.tar.bz2

# --- gitlab-tag -------------------------------------------------------------
# Any GitLab instance, public API only (no authentication).
# Any GitLab instance, public API only (no authentication). Keeps the tags
# v1.2.3 and 1.2.3; tag_pattern replaces that filter.
#
# strategy: gitlab-tag
# upstream:
# host: gitlab.com # required, hostname without protocol
# project: group/subgroup/project # required, full path
# tag_pattern: 'v*' # optional
# tag_pattern: 'release-*' # optional

# --- gitea-tag --------------------------------------------------------------
# Codeberg, self-hosted Gitea/Forgejo. Reads at most 50 tags.
# Codeberg, self-hosted Gitea/Forgejo. Reads at most 50 tags and keeps
# v1.2.3 and 1.2.3; tag_pattern replaces that filter.
#
# strategy: gitea-tag
# upstream:
# host: codeberg.org # required
# project: owner/repo # required
# tag_pattern: 'v*' # optional
# tag_pattern: 'release-*' # optional

# --- git-latest -------------------------------------------------------------
# For -git packages. Produces rCOUNT.HASH (e.g. r435.abc1234).
Expand Down
8 changes: 8 additions & 0 deletions scripts/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ validate_pkgver() {
fi
}

# The tags of the form v1.2.3 or 1.2.3, for a tag strategy without a filter
# of its own. sort -V puts latest, 2.1.0-rc1 and 2.1.0rc1 above 2.1.0, so
# they are left out.
# Args: $1=tags, one per line
release_tags() {
grep -E '^v?[0-9]+(\.[0-9]+)*$' <<<"$1" || true
}

# =============================================================================
# CHECKSUMS
# =============================================================================
Expand Down
8 changes: 7 additions & 1 deletion strategies/gitea-tag.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ if [[ -z "$tags" ]]; then
exit 1
fi

# Filter by pattern if set
# Filter by pattern if set, by the form of a release if not
if [[ -n "$tag_pattern" ]]; then
regex="^${tag_pattern//\*/.*}"
tags=$(echo "$tags" | grep -E "$regex" || true)
else
tags=$(release_tags "$tags")
if [[ -z "$tags" ]]; then
echo "No tag of the form v1.2.3 or 1.2.3; set upstream.tag_pattern" >&2
exit 1
fi
fi

# Strip v prefix, sort semver, take latest
Expand Down
8 changes: 7 additions & 1 deletion strategies/github-tag.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ if [[ -z "$tags" ]]; then
exit 1
fi

# Filter by pattern if set
# Filter by pattern if set, by the form of a release if neither field is
if [[ -n "$tag_pattern" ]]; then
regex="^${tag_pattern//\*/.*}"
tags=$(echo "$tags" | grep -E "$regex" || true)
elif [[ -z "$tag_version_regex" ]]; then
tags=$(release_tags "$tags")
if [[ -z "$tags" ]]; then
echo "No tag of the form v1.2.3 or 1.2.3; set upstream.tag_pattern or upstream.tag_version_regex" >&2
exit 1
fi
fi

# Extract version from tags
Expand Down
8 changes: 7 additions & 1 deletion strategies/gitlab-tag.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ if [[ -z "$tags" ]]; then
exit 1
fi

# Filter by pattern if set
# Filter by pattern if set, by the form of a release if not
if [[ -n "$tag_pattern" ]]; then
# Convert glob pattern (v*) to grep regex (^v)
regex="^${tag_pattern//\*/.*}"
tags=$(echo "$tags" | grep -E "$regex" || true)
else
tags=$(release_tags "$tags")
if [[ -z "$tags" ]]; then
echo "No tag of the form v1.2.3 or 1.2.3; set upstream.tag_pattern" >&2
exit 1
fi
fi

# Strip v prefix, sort semver, take latest
Expand Down
11 changes: 11 additions & 0 deletions tests/helpers/mock-curl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ case "$url" in
*api.github.com/repos/custom-prefix/repo/tags*)
echo '[{"name": "bun-v1.3.11"}, {"name": "bun-v1.2.0"}, {"name": "bun-v1.0.0"}]'
;;
# Tags of which some are not releases, and tags of which none is
*api.github.com/repos/mixed-owner/mixed-repo/tags* | \
*gitlab.com/api/v4/projects/mixed-group%2Fmixed-project/repository/tags* | \
*codeberg.org/api/v1/repos/mixed-owner/mixed-repo/tags*)
echo '[{"name": "latest"}, {"name": "v2.1.0-rc1"}, {"name": "v2.1.0rc2"}, {"name": "v2.0.0"}, {"name": "v1.9.0"}]'
;;
*api.github.com/repos/named-owner/named-repo/tags* | \
*gitlab.com/api/v4/projects/named-group%2Fnamed-project/repository/tags* | \
*codeberg.org/api/v1/repos/named-owner/named-repo/tags*)
echo '[{"name": "latest"}, {"name": "nightly"}]'
;;

# --- Normal success patterns (wildcards) ---

Expand Down
68 changes: 68 additions & 0 deletions tests/strategies.bats
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,34 @@ teardown() {
[ "$output" = "1.3.11" ]
}

@test "github-tag: takes only tags of the form v1.2.3 by default" {
create_test_package "test-gh-tag" "github-tag" "upstream:
project: mixed-owner/mixed-repo"

run "$TEST_TMPDIR/strategies/github-tag.sh" "test-gh-tag"
[ "$status" -eq 0 ]
[ "$output" = "2.0.0" ]
}

@test "github-tag: names both fields when no tag is a release" {
create_test_package "test-gh-tag" "github-tag" "upstream:
project: named-owner/named-repo"

run "$TEST_TMPDIR/strategies/github-tag.sh" "test-gh-tag"
[ "$status" -eq 1 ]
[[ "$output" == *"No tag of the form v1.2.3 or 1.2.3; set upstream.tag_pattern or upstream.tag_version_regex"* ]]
}

@test "github-tag: tag_pattern replaces the default filter" {
create_test_package "test-gh-tag" "github-tag" "upstream:
project: mixed-owner/mixed-repo
tag_pattern: 'latest'"

run "$TEST_TMPDIR/strategies/github-tag.sh" "test-gh-tag"
[ "$status" -eq 0 ]
[ "$output" = "latest" ]
}

# === gitlab-tag ===

@test "gitlab-tag: returns latest tag from GitLab" {
Expand All @@ -86,6 +114,26 @@ teardown() {
[ "$output" = "3.1.0" ]
}

@test "gitlab-tag: takes only tags of the form v1.2.3 by default" {
create_test_package "test-gl-tag" "gitlab-tag" "upstream:
host: gitlab.com
project: mixed-group/mixed-project"

run "$TEST_TMPDIR/strategies/gitlab-tag.sh" "test-gl-tag"
[ "$status" -eq 0 ]
[ "$output" = "2.0.0" ]
}

@test "gitlab-tag: names tag_pattern when no tag is a release" {
create_test_package "test-gl-tag" "gitlab-tag" "upstream:
host: gitlab.com
project: named-group/named-project"

run "$TEST_TMPDIR/strategies/gitlab-tag.sh" "test-gl-tag"
[ "$status" -eq 1 ]
[[ "$output" == *"No tag of the form v1.2.3 or 1.2.3; set upstream.tag_pattern"* ]]
}

# === gitea-tag ===

@test "gitea-tag: returns latest tag from Gitea/Codeberg" {
Expand All @@ -99,6 +147,26 @@ teardown() {
[ "$output" = "1.5.0" ]
}

@test "gitea-tag: takes only tags of the form v1.2.3 by default" {
create_test_package "test-gitea" "gitea-tag" "upstream:
host: codeberg.org
project: mixed-owner/mixed-repo"

run "$TEST_TMPDIR/strategies/gitea-tag.sh" "test-gitea"
[ "$status" -eq 0 ]
[ "$output" = "2.0.0" ]
}

@test "gitea-tag: names tag_pattern when no tag is a release" {
create_test_package "test-gitea" "gitea-tag" "upstream:
host: codeberg.org
project: named-owner/named-repo"

run "$TEST_TMPDIR/strategies/gitea-tag.sh" "test-gitea"
[ "$status" -eq 1 ]
[[ "$output" == *"No tag of the form v1.2.3 or 1.2.3; set upstream.tag_pattern"* ]]
}

# === pypi ===

@test "pypi: returns latest version from PyPI" {
Expand Down
Loading