Compare commits
22 Commits
2a9d0baa6a
...
renovate/c
| Author | SHA1 | Date | |
|---|---|---|---|
| fe2923b621 | |||
| 5978ced62a | |||
| 7a69524e91 | |||
| ffc326a940 | |||
| aea3418854 | |||
| 0dc0e5e135 | |||
| 44902487b0 | |||
| 2147d48f6b | |||
| 450b83c3ef | |||
| 364ee6e066 | |||
| eef8a9cbcb | |||
| 862d8cf5f8 | |||
| 104a6822bc | |||
| 82517a10a7 | |||
| 4d671a283f | |||
| 7b32044803 | |||
| 46e94a5d84 | |||
| 531ea9f1c2 | |||
| 2c735de7e7 | |||
| 4bcca0b832 | |||
| f6034a32fc | |||
| be652dfadd |
2
.github/workflows/sync.yaml
vendored
2
.github/workflows/sync.yaml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
sync-images:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [liteyukios-latest]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
9
dockerfiles/cloudreve-aio.Dockerfile
Normal file
9
dockerfiles/cloudreve-aio.Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM cloudreve/cloudreve:4.0.0-beta.6
|
||||
|
||||
# Install ffmpeg LibreOffice and VIPS
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ffmpeg \
|
||||
libreoffice \
|
||||
vips-tools \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
@@ -24,4 +24,7 @@ images:
|
||||
- "20"
|
||||
- "20-slim"
|
||||
- "18"
|
||||
- "18-slim"
|
||||
- "18-slim"
|
||||
|
||||
- dockerfile: "./dockerfiles/cloudreve-aio.Dockerfile"
|
||||
target: "reg.liteyuki.icu/reedit/cloudreve-aio:latest"
|
||||
@@ -9,3 +9,9 @@ dependencies = [
|
||||
"pydantic>=2.11.3",
|
||||
"pyyaml>=6.0.2",
|
||||
]
|
||||
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "liteyuki"
|
||||
url = "https://git.liteyuki.icu/api/packages/snowykami/pypi/simple"
|
||||
explicit = true
|
||||
|
||||
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||
}
|
||||
81
sync.py
81
sync.py
@@ -1,19 +1,20 @@
|
||||
import asyncio
|
||||
from types import CoroutineType
|
||||
from typing import Any, Awaitable, Callable, Coroutine
|
||||
import aiofiles
|
||||
import yaml
|
||||
|
||||
from typing import Any, Callable, Coroutine
|
||||
from functools import partial
|
||||
from pydantic import BaseModel
|
||||
|
||||
class Config(BaseModel):
|
||||
images: list["Images"] = []
|
||||
|
||||
class Images(BaseModel):
|
||||
source: str
|
||||
dockerfile: str | None = None
|
||||
source: str | None = None
|
||||
target: str
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
async def run_command(command: str) -> int | None:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
command,
|
||||
@@ -28,26 +29,52 @@ async def run_command(command: str) -> int | None:
|
||||
print(f"[STDERR]\n{stderr.decode()}")
|
||||
|
||||
return process.returncode
|
||||
|
||||
|
||||
async def docker_pull(image: str) -> int | None:
|
||||
print(f"Pulling image {image}...")
|
||||
return await run_command(f"docker pull {image}")
|
||||
|
||||
|
||||
async def docker_tag(source: str, target: str) -> int | None:
|
||||
print(f"Tagging image {source} as {target}...")
|
||||
return await run_command(f"docker tag {source} {target}")
|
||||
|
||||
|
||||
async def docker_push(image: str) -> int | None:
|
||||
print(f"Pushing image {image}...")
|
||||
return await run_command(f"docker push {image}")
|
||||
|
||||
semaphore = asyncio.Semaphore(5)
|
||||
async def docker_build(dockerfile: str, target: str) -> int | None:
|
||||
print(f"Building image from {dockerfile}...")
|
||||
return await run_command(f"docker build -t {target} -f {dockerfile} .")
|
||||
|
||||
async def limited_task[T: Any](task: Callable[[], Coroutine[None, None, T]]) -> T:
|
||||
async def docker_sync_task(source: str, target: str) -> int | None:
|
||||
print(f"Pulling image {source}...")
|
||||
if r := await docker_pull(source):
|
||||
if r != 0:
|
||||
return r
|
||||
if r := await docker_tag(source, target):
|
||||
if r != 0:
|
||||
return r
|
||||
if r := await docker_push(target):
|
||||
if r != 0:
|
||||
return r
|
||||
return 0
|
||||
|
||||
async def docker_build_task(dockerfile: str, target: str) -> int | None:
|
||||
print(f"Building image from {dockerfile}...")
|
||||
if r := await docker_build(dockerfile, target):
|
||||
if r != 0:
|
||||
return r
|
||||
if r := await docker_push(target):
|
||||
if r != 0:
|
||||
return r
|
||||
return 0
|
||||
|
||||
semaphore = asyncio.Semaphore(20)
|
||||
|
||||
async def limited_task[T: Any](semaphore: asyncio.Semaphore, task: Callable[[], Coroutine[None, None, T]]) -> T:
|
||||
async with semaphore:
|
||||
return await task()
|
||||
|
||||
|
||||
async def main():
|
||||
async with aiofiles.open('images.yaml', 'r') as file:
|
||||
config = await file.read()
|
||||
@@ -60,28 +87,26 @@ async def main():
|
||||
|
||||
tasks = []
|
||||
for image in config.images:
|
||||
if len(image.tags) > 0:
|
||||
if image.dockerfile:
|
||||
# 若有dockerfile提供,則使用docker build
|
||||
if len(image.tags) > 0:
|
||||
# 若有tags提供,則使用docker build和docker tag
|
||||
for tag in image.tags:
|
||||
tasks.append(limited_task(semaphore, partial(docker_build_task, image.dockerfile, f"{image.target}:{tag}")))
|
||||
else:
|
||||
tasks.append(limited_task(semaphore, partial(docker_build_task, image.dockerfile, image.target)))
|
||||
elif len(image.tags) > 0:
|
||||
# 若有tags提供,則使用docker pull和docker tag
|
||||
for tag in image.tags:
|
||||
async def task() -> int | None:
|
||||
return (await docker_pull(f"{image.source}:{tag}")) \
|
||||
or (await docker_pull(f"{image.source}:{tag}")) \
|
||||
or (await docker_push(f"{image.target}:{tag}"))
|
||||
tasks.append(limited_task(task))
|
||||
tasks.append(limited_task(semaphore, partial(docker_sync_task, f"{image.source}:{tag}", f"{image.target}:{tag}")))
|
||||
else:
|
||||
async def task() -> int | None:
|
||||
return (await docker_pull(image.source)) \
|
||||
or (await docker_pull(image.source)) \
|
||||
or (await docker_push(image.target))
|
||||
tasks.append(limited_task(task))
|
||||
|
||||
tasks.append(limited_task(semaphore, partial(docker_sync_task, image.source, image.target)))
|
||||
|
||||
results = await asyncio.gather(*tasks)
|
||||
print(tasks)
|
||||
failed_tasks = 0
|
||||
for result in results:
|
||||
if result is not None and result != 0:
|
||||
failed_tasks += 1
|
||||
|
||||
failed_tasks = sum(1 for result in results if result is not None and result != 0)
|
||||
print(f"{len(results)} tasks completed. {len(results) - failed_tasks} succeed, {failed_tasks} failed.")
|
||||
if failed_tasks > 0:
|
||||
raise Exception(f"{failed_tasks} tasks failed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
10
uv.lock
generated
10
uv.lock
generated
@@ -27,6 +27,7 @@ dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "test-pypi" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -34,6 +35,7 @@ requires-dist = [
|
||||
{ name = "aiofiles", specifier = ">=24.1.0" },
|
||||
{ name = "pydantic", specifier = ">=2.11.3" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||
{ name = "test-pypi", specifier = ">=0.1.1", index = "https://git.liteyuki.icu/api/packages/snowykami/pypi/simple" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -119,6 +121,14 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-pypi"
|
||||
version = "0.1.1"
|
||||
source = { registry = "https://git.liteyuki.icu/api/packages/snowykami/pypi/simple" }
|
||||
wheels = [
|
||||
{ url = "https://git.liteyuki.icu/api/packages/snowykami/pypi/files/test-pypi/0.1.1/test_pypi-0.1.1-py3-none-any.whl", hash = "sha256:b34310401079188a6b286cbf95c60c24c5ae950c7682e07cda7259d73c8b3b67" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.13.2"
|
||||
|
||||
Reference in New Issue
Block a user