Compare commits

..

1 Commits

Author SHA1 Message Date
oSumAtrIX
cadd38bf34 feat: Add downloaders route 2025-02-09 04:39:35 +01:00
9 changed files with 128 additions and 56 deletions

View File

@@ -17,9 +17,14 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
# Make sure the release step uses its own credentials:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Cache Gradle - name: Cache Gradle
uses: burrunan/gradle-cache-action@v3 uses: burrunan/gradle-cache-action@v1
- name: Build - name: Build
env: env:
@@ -51,12 +56,12 @@ jobs:
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Release - name: Release
uses: cycjimmy/semantic-release-action@v4
env: env:
DOCKER_REGISTRY_USER: ${{ github.actor }} DOCKER_REGISTRY_USER: ${{ github.actor }}
DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ACTOR: ${{ github.actor }} GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm exec semantic-release
- name: Set Portainer stack webhook URL based on branch - name: Set Portainer stack webhook URL based on branch
run: | run: |
@@ -73,10 +78,10 @@ jobs:
webhook_url: ${{ env.PORTAINER_WEBHOOK_URL }} webhook_url: ${{ env.PORTAINER_WEBHOOK_URL }}
- name: Purge outdated images - name: Purge outdated images
uses: snok/container-retention-policy@v3.0.0 uses: actions/delete-package-versions@v5
with: with:
account: ${{ github.repository_owner }} package-name: 'revanced-api'
package-type: 'container'
min-versions-to-keep: 5
delete-only-untagged-versions: 'true'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
image-names: revanced-api
keep-n-most-recent: 5
cut-off: 1w

View File

@@ -1,17 +1,3 @@
## [1.6.1](https://github.com/ReVanced/revanced-api/compare/v1.6.0...v1.6.1) (2025-07-11)
### Bug Fixes
* Return latest regardless of prerelease parameter for release APIs ([53bd820](https://github.com/ReVanced/revanced-api/commit/53bd82071db165741f8f08e7ec438b45abde4425))
## [1.6.1-dev.1](https://github.com/ReVanced/revanced-api/compare/v1.6.0...v1.6.1-dev.1) (2025-07-10)
### Bug Fixes
* Return latest regardless of prerelease parameter for release APIs ([53bd820](https://github.com/ReVanced/revanced-api/commit/53bd82071db165741f8f08e7ec438b45abde4425))
# [1.6.0](https://github.com/ReVanced/revanced-api/compare/v1.5.0...v1.6.0) (2025-02-04) # [1.6.0](https://github.com/ReVanced/revanced-api/compare/v1.5.0...v1.6.0) (2025-02-04)

View File

@@ -79,6 +79,7 @@ Some of the features ReVanced API include:
- 📢 **Announcements**: Post and get announcements - 📢 **Announcements**: Post and get announcements
- **About**: Get more information such as a description, ways to donate to, - **About**: Get more information such as a description, ways to donate to,
and links of the hoster of ReVanced API and links of the hoster of ReVanced API
- 💊 **Manager**: Get the latest updates of ReVanced Manager and its downloaders
- 🧩 **Patches**: Get the latest updates of ReVanced Patches, directly from ReVanced API - 🧩 **Patches**: Get the latest updates of ReVanced Patches, directly from ReVanced API
- 👥 **Contributors**: List all contributors involved in the project - 👥 **Contributors**: List all contributors involved in the project

View File

@@ -20,6 +20,8 @@ public-key-id = 3897925568445097277
[manager] [manager]
repository = "revanced-manager" repository = "revanced-manager"
asset-regex = "apk$" asset-regex = "apk$"
downloaders-repository = "revanced-manager-downloaders"
downloaders-asset-regex = "apk$"
[contributors-repositories] [contributors-repositories]
revanced-patcher = "ReVanced Patcher" revanced-patcher = "ReVanced Patcher"

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 1.6.1 version = 1.6.0

View File

@@ -23,8 +23,8 @@ import kotlin.io.path.createDirectories
* The repository storing the configuration for the API. * The repository storing the configuration for the API.
* *
* @property organization The API backends organization name where the repositories are. * @property organization The API backends organization name where the repositories are.
* @property patches The source of the patches. * @property patches The configuration for patches.
* @property manager The source of the manager. * @property manager The configuration for the manager.
* @property contributorsRepositoryNames The friendly name of repos mapped to the repository names to get contributors from. * @property contributorsRepositoryNames The friendly name of repos mapped to the repository names to get contributors from.
* @property backendServiceName The name of the backend service to use for the repositories, contributors, etc. * @property backendServiceName The name of the backend service to use for the repositories, contributors, etc.
* @property apiVersion The version to use for the API. * @property apiVersion The version to use for the API.
@@ -38,8 +38,8 @@ import kotlin.io.path.createDirectories
@Serializable @Serializable
internal class ConfigurationRepository( internal class ConfigurationRepository(
val organization: String, val organization: String,
val patches: SignedAssetConfiguration, val patches: PatchesConfiguration,
val manager: AssetConfiguration, val manager: ManagerConfiguration,
@SerialName("contributors-repositories") @SerialName("contributors-repositories")
val contributorsRepositoryNames: Map<String, String>, val contributorsRepositoryNames: Map<String, String>,
@SerialName("backend-service-name") @SerialName("backend-service-name")
@@ -65,22 +65,20 @@ internal class ConfigurationRepository(
} }
/** /**
* Am asset configuration whose asset is signed. * A configuration for [PatchesService].
* *
* [PatchesService] for example uses [BackendRepository] to get assets from its releases. * @property repository The patches repository.
* A release contains multiple assets. * @property assetRegex The regex matching the patches asset name
* * in releases from the patches repository.
* This configuration is used in [ConfigurationRepository] * @property signatureAssetRegex The regex matching the patches signature asset name
* to determine which release assets from repositories to get and to verify them. * in releases from the patches repository.
* * @property publicKeyFile The public key file to verify the signature of the patches asset
* @property repository The repository in which releases are made to get an asset. * in releases from the patches repository.
* @property assetRegex The regex matching the asset name. * @property publicKeyId The ID of the public key to verify the signature of the patches asset
* @property signatureAssetRegex The regex matching the signature asset name to verify the asset. * in releases from the patches repository.
* @property publicKeyFile The public key file to verify the signature of the asset.
* @property publicKeyId The ID of the public key to verify the signature of the asset.
*/ */
@Serializable @Serializable
internal class SignedAssetConfiguration( internal class PatchesConfiguration(
val repository: String, val repository: String,
@Serializable(with = RegexSerializer::class) @Serializable(with = RegexSerializer::class)
@SerialName("asset-regex") @SerialName("asset-regex")
@@ -96,23 +94,26 @@ internal class ConfigurationRepository(
) )
/** /**
* Am asset configuration. * A configuration for [ManagerService].
*
* [ManagerService] for example uses [BackendRepository] to get assets from its releases. * @property repository The manager repository.
* A release contains multiple assets. * @property assetRegex The regex matching the manager asset name
* * in releases from the manager repository.
* This configuration is used in [ConfigurationRepository] * @property downloadersRepository The manager downloaders repository.
* to determine which release assets from repositories to get and to verify them. * @property downloadersAssetRegex The regex matching the manager downloaders asset name
* * in releases from the manager downloaders repository.
* @property repository The repository in which releases are made to get an asset.
* @property assetRegex The regex matching the asset name.
*/ */
@Serializable @Serializable
internal class AssetConfiguration( internal class ManagerConfiguration(
val repository: String, val repository: String,
@Serializable(with = RegexSerializer::class) @Serializable(with = RegexSerializer::class)
@SerialName("asset-regex") @SerialName("asset-regex")
val assetRegex: Regex, val assetRegex: Regex,
@SerialName("downloaders-repository")
val downloadersRepository: String,
@Serializable(with = RegexSerializer::class)
@SerialName("downloaders-asset-regex")
val downloadersAssetRegex: Regex,
) )
} }

View File

@@ -27,7 +27,7 @@ class GitHubBackendRepository : BackendRepository("https://api.github.com", "htt
prerelease: Boolean, prerelease: Boolean,
): BackendRelease { ): BackendRelease {
val release: GitHubRelease = if (prerelease) { val release: GitHubRelease = if (prerelease) {
client.get(Releases(owner, repository)).body<List<GitHubRelease>>().first() client.get(Releases(owner, repository)).body<List<GitHubRelease>>().first { it.prerelease }
} else { } else {
client.get(Releases.Latest(owner, repository)).body() client.get(Releases.Latest(owner, repository)).body()
} }
@@ -99,10 +99,10 @@ class GitHubBackendRepository : BackendRepository("https://api.github.com", "htt
url = user.htmlUrl, url = user.htmlUrl,
bio = user.bio, bio = user.bio,
gpgKeys = gpgKeys =
BackendMember.GpgKeys( BackendMember.GpgKeys(
ids = gpgKeys.map { it.keyId }, ids = gpgKeys.map { it.keyId },
url = "https://github.com/${user.login}.gpg", url = "https://github.com/${user.login}.gpg",
), ),
) )
} }
} }
@@ -203,7 +203,7 @@ class Organization {
class Contributors(val owner: String, val repo: String, @SerialName("per_page") val perPage: Int = 100) class Contributors(val owner: String, val repo: String, @SerialName("per_page") val perPage: Int = 100)
@Resource("/repos/{owner}/{repo}/releases") @Resource("/repos/{owner}/{repo}/releases")
class Releases(val owner: String, val repo: String, @SerialName("per_page") val perPage: Int = 1) { class Releases(val owner: String, val repo: String) {
@Resource("/repos/{owner}/{repo}/releases/latest") @Resource("/repos/{owner}/{repo}/releases/latest")
class Latest(val owner: String, val repo: String) class Latest(val owner: String, val repo: String)
} }

View File

@@ -35,6 +35,26 @@ internal fun Route.managerRoute() = route("manager") {
call.respond(managerService.latestVersion(prerelease)) call.respond(managerService.latestVersion(prerelease))
} }
} }
route("downloaders") {
installManagerDownloadersRouteDocumentation()
get {
val prerelease = call.parameters["prerelease"]?.toBoolean() ?: false
call.respond(managerService.latestDownloadersRelease(prerelease))
}
route("version") {
installManagerDownloadersVersionRouteDocumentation()
get {
val prerelease = call.parameters["prerelease"]?.toBoolean() ?: false
call.respond(managerService.latestDownloadersVersion(prerelease))
}
}
}
} }
} }
@@ -77,3 +97,35 @@ private fun Route.installManagerVersionRouteDocumentation() = installNotarizedRo
} }
} }
} }
private fun Route.installManagerDownloadersRouteDocumentation() = installNotarizedRoute {
tags = setOf("Manager")
get = GetInfo.builder {
description("Get the current manager downloaders release")
summary("Get current manager downloaders release")
parameters(prereleaseParameter)
response {
description("The latest manager downloaders release")
mediaTypes("application/json")
responseCode(HttpStatusCode.OK)
responseType<ApiRelease>()
}
}
}
private fun Route.installManagerDownloadersVersionRouteDocumentation() = installNotarizedRoute {
tags = setOf("Manager")
get = GetInfo.builder {
description("Get the current manager downloaders release version")
summary("Get current manager downloaders release version")
parameters(prereleaseParameter)
response {
description("The current manager downloaders release version")
mediaTypes("application/json")
responseCode(HttpStatusCode.OK)
responseType<ApiReleaseVersion>()
}
}
}

View File

@@ -34,4 +34,29 @@ internal class ManagerService(
return ApiReleaseVersion(managerRelease.tag) return ApiReleaseVersion(managerRelease.tag)
} }
suspend fun latestDownloadersRelease(prerelease: Boolean): ApiRelease {
val downloaderPluginsRelease = backendRepository.release(
configurationRepository.organization,
configurationRepository.manager.downloadersRepository,
prerelease,
)
return ApiRelease(
downloaderPluginsRelease.tag,
downloaderPluginsRelease.createdAt,
downloaderPluginsRelease.releaseNote,
downloaderPluginsRelease.assets.first(configurationRepository.manager.downloadersAssetRegex).downloadUrl,
)
}
suspend fun latestDownloadersVersion(prerelease: Boolean): ApiReleaseVersion {
val downloaderPluginsRelease = backendRepository.release(
configurationRepository.organization,
configurationRepository.manager.downloadersRepository,
prerelease,
)
return ApiReleaseVersion(downloaderPluginsRelease.tag)
}
} }