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
66 changes: 66 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: '🐛 Bug report'
description: Report an issue with crpy.
labels: [bug]

body:
- type: checkboxes
id: checks
attributes:
label: Checks
options:
- label: I have checked that this issue has not already been reported.
required: true
- label: I have confirmed this bug exists on the latest version of crpy.
required: true

- type: textarea
id: example
attributes:
label: Reproducible example
description: >
Please follow [this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) on how to
provide a minimal, copy-pastable example. Include the (wrong) output if applicable.
value: |
```python

```
validations:
required: true

- type: textarea
id: logs
attributes:
label: Log output
description: >
Include the stack trace, if available, of the problem being reported.
render: shell

- type: textarea
id: problem
attributes:
label: Issue description
description: >
Provide any additional information you think might be relevant.
validations:
required: true

- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: >
Describe or show a code example of the expected behavior.
validations:
required: true

- type: textarea
id: version
attributes:
label: Installed versions
description: >
Describe which version (or if running to git version, which commit) of the Python library.
value: >
- crpy version:
- Python version:
validations:
required: true
14 changes: 14 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: '✨ Feature request'
description: Suggest a new feature or enhancement for crpy.
labels: [enhancement]

body:
- type: textarea
id: description
attributes:
label: Description
description: >
Describe the feature or enhancement and explain why it should be implemented.
Include a code example if applicable.
validations:
required: true
1 change: 1 addition & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
# see https://github.com/orgs/community/discussions/25029#discussioncomment-3246275
release:
types: [published]
workflow_dispatch:
jobs:
pypi-publish:
name: Upload release to PyPI
Expand Down
17 changes: 10 additions & 7 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
name: Run tests
name: Tests

on:
pull_request:
push:
branches: [ main ]

jobs:
build:

test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v3
Expand All @@ -22,14 +21,18 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
- name: Lint with ruff
run: |
# stop the build if there are Python syntax errors or undefined names
ruff --select=E9,F63,F7,F82 --target-version=py37 .
ruff check --select=E9,F63,F7,F82 --target-version=py39 .
# default set of ruff rules with GitHub Annotations
ruff --target-version=py37 .
ruff check --target-version=py39 .
- name: Test with pytest
run: |
pytest
# - name: Upload coverage reports to Codecov
# uses: codecov/codecov-action@v4
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
18 changes: 18 additions & 0 deletions .github/workflows/try-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test package for import errors and dependency installation
on:
push:
branches: [ main ]
jobs:
try-build:
name: Tries to build the package and import it
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install locally
run: python3 -m pip install .
- name: Test library import
run: cd / && python3 -c "from crpy import RegistryInfo"
24 changes: 5 additions & 19 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
repos:
- repo: 'https://github.com/charliermarsh/ruff-pre-commit'
rev: v0.0.285
rev: v0.12.4
hooks:
- id: ruff
- id: ruff-check
args:
- '--line-length=120'
- '--fix'
- '--exit-non-zero-on-fix'
- id: ruff-format
- repo: 'https://github.com/pre-commit/pre-commit-hooks'
rev: v4.4.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: 'https://github.com/pycqa/isort'
rev: 5.12.0
hooks:
- id: isort
name: isort (python)
args:
- '--profile'
- black
- '--filter-files'
- repo: 'https://github.com/psf/black'
rev: 23.7.0
hooks:
- id: black
args:
- '--line-length=120'
3 changes: 0 additions & 3 deletions .ruff.toml

This file was deleted.

7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3.13-slim-bookworm

WORKDIR /app
COPY . .
RUN pip install . && pip cache purge && rm -rf /app/*

ENTRYPOINT ["crpy"]
66 changes: 60 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,21 @@ The script creates a cache directory (~/.crpy/) to store layers already download
It was based on a simpler version called [sdenel/docker-pull-push](https://github.com/sdenel/docker-pull-push), but has
since received so many changes that it does not resemble the original code anymore.

# Basic usage
# Installation

You can install it from the official pip repository:

```bash
pip install crpy
```

If you want to live on the edge and have the latest development features, install it directly from the repo:

```bash
pip install git+https://github.com/bvanelli/crpy.git
```

# Basic CLI usage

TODO: Fill in once the "final" version of the API is stable. For a preview of the options, here is the help command:

Expand Down Expand Up @@ -46,18 +60,58 @@ optional arguments:
For reporting issues visit https://github.com/bvanelli/crpy
```

# Installation
One of the original intended usages was to run it CI to cache dependencies docker image (i.e. for Gitlab). In this
case, we can check if the image already exists on the remote repository:

You can install it from the official pip repository:
```bash
$ crpy manifest alpine:1.2.3
Authenticated at index.docker.io/library/alpine:latest
{'errors': [{'code': 'MANIFEST_UNKNOWN', 'message': 'manifest unknown', 'detail': 'unknown tag=1.2.3'}]}
```

You are also able to download images and save them to disk:

```bash
pip install crpy
$ crpy pull alpine:latest alpine.tar.gz
latest: Pulling from index.docker.io/library/alpine
Authenticated at index.docker.io/library/alpine:latest
Using cache for layer 9824c27679d3
9824c27679d3: Pull complete
Downloaded image from index.docker.io/library/alpine:latest
```

If you want to live on the edge and have the latest development features, install it directly from the repo:
On can then push this image to another repository:

```bash
pip install git+https://github.com/bvanelli/crpy.git
$ crpy push alpine.tar.gz bvanelli/test:latest
crpy push alpine.tar.gz bvanelli/test:latest
The push refers to repository
Authenticated at index.docker.io/bvanelli/test:latest
Authenticated at index.docker.io/bvanelli/test:latest
9824c27679d3: Pushed
Pushed latest: digest: sha256:3f372403810ab0506dda12549f1035804192ef02fb36040c036845f90bd6bfe2
```

Let's now list the tags available at this repository:

```bash
$ crpy tags bvanelli/test
Authenticated at index.docker.io/bvanelli/test:latest
1.0.0
latest
```

And delete one of the tags. I show this example because both tags were the same, and deleting one will delete them both,
so use this command with caution:

```bash
$ crpy delete bvanelli/test:1.0.0
crpy delete bvanelli/test:1.0.0
Authenticated at index.docker.io/bvanelli/test:1.0.0
Authenticated at index.docker.io/bvanelli/test:1.0.0
b''
$ crpy tags bvanelli/test
Authenticated at index.docker.io/bvanelli/test:latest
```

# Why creating this package?
Expand Down
5 changes: 5 additions & 0 deletions codecov.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
status:
project:
default:
threshold: 5%
14 changes: 14 additions & 0 deletions crpy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from crpy.registry import RegistryInfo
from crpy.image import Blob, Image
from crpy.common import HTTPConnectionError, UnauthorizedError, BaseCrpyError
from crpy.version import __version__

__all__ = [
"RegistryInfo",
"Blob",
"Image",
"HTTPConnectionError",
"UnauthorizedError",
"BaseCrpyError",
"__version__",
]
9 changes: 9 additions & 0 deletions crpy/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ async def _auth(args):
print(table)


async def _version(_args) -> None:
from crpy import __version__

print(__version__)


def main(*args):
parser = argparse.ArgumentParser(
prog="crpy",
Expand Down Expand Up @@ -260,6 +266,9 @@ def main(*args):
default="index.docker.io",
)
delete.set_defaults(func=_delete)
# version
version = subparsers.add_parser("version", help="Displays the application version.")
version.set_defaults(func=_version)

arguments = parser.parse_args(args if args else None)

Expand Down
6 changes: 5 additions & 1 deletion crpy/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def layers(self, layers: List[INPUT_TYPES]):
def to_disk(self, filename: pathlib.Path, tags: List[str] = None):
with tempfile.TemporaryDirectory() as temp_dir:
web_manifest = self.manifest.as_dict()
config_filename = f'{web_manifest["config"]["digest"].split(":")[1]}.json'
config_filename = f"{web_manifest['config']['digest'].split(':')[1]}.json"
with open(f"{temp_dir}/{config_filename}", "wb") as outfile:
outfile.write(self.config.as_bytes())

Expand All @@ -101,6 +101,10 @@ def to_disk(self, filename: pathlib.Path, tags: List[str] = None):
os.makedirs(f"{temp_dir}/{layer_folder}", exist_ok=True)
with open(f"{temp_dir}/{path}", "wb") as f:
f.write(layer_bytes)
# add version for backwards compatibility
# https://github.com/moby/moby/blob/daa4618da826fb1de4fc2478d88196edbba49b2f/image/spec/v1.md
with open(f"{temp_dir}/{layer_folder}/VERSION", "w") as f:
f.write("1.0")
layer_path_l.append(path)

manifest = [{"Config": config_filename, "RepoTags": tags or [], "Layers": layer_path_l}]
Expand Down
Loading
Loading