diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9835d42..c1fb4b9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,77 +10,76 @@ jobs: runs-on: ubuntu-latest env: - PUBLIC_IMAGE: datastewardshipwizard/nanopub-submission-service - PRIVATE_IMAGE: ${{ secrets.PRIVATE_REGISTRY_URL }}/nanopub-submission-service - TAG_DEVELOP: develop - TAG_LATEST: latest + PUBLIC_IMAGE_PREFIX: 'datastewardshipwizard' + DOCKER_IMAGE_NAME: 'nanopub-submission-service' + DOCKER_META_CONTEXT: '.' + DOCKER_META_FILE: 'Dockerfile' + DOCKER_META_PLATFORMS: 'linux/amd64,linux/arm64' steps: - - uses: actions/checkout@v2 + - name: '[setup] Check out repository' + uses: actions/checkout@v4 + with: + fetch-depth: 0 -# (1) -> Build Docker image - - name: Update build info - run: | - ./scripts/build-info.sh + - name: '[setup] Set up QEMU' + uses: docker/setup-qemu-action@v3 - - name: Docker build - run: | - docker build -t $PRIVATE_IMAGE:$GITHUB_SHA . + - name: '[setup] Set up Docker Buildx' + id: buildx + uses: docker/setup-buildx-action@v3 -# (2) -> Docker image tagging - - name: Docker login - if: github.event_name == 'push' + - name: '[setup ]Update build info' run: | - docker login -u "$DOCKER_HUB_USERNAME" -p "$DOCKER_HUB_PASSWORD" - docker login -u "$PRIVATE_REGISTRY_USERNAME" -p "$PRIVATE_REGISTRY_PASSWORD" "$PRIVATE_REGISTRY_URL" - env: - DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }} - DOCKER_HUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }} - PRIVATE_REGISTRY_URL: ${{ secrets.PRIVATE_REGISTRY_URL }} - PRIVATE_REGISTRY_USERNAME: ${{ secrets.PRIVATE_REGISTRY_USERNAME }} - PRIVATE_REGISTRY_PASSWORD: ${{ secrets.PRIVATE_REGISTRY_PASSWORD }} + ./scripts/build-info.sh - - name: Docker push - commit SHA (private) - if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/') - run: | - docker push $PRIVATE_IMAGE:$GITHUB_SHA + - name: '[docker] Docker meta' + id: meta-test + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.PUBLIC_IMAGE_PREFIX }}/${{ env.DOCKER_IMAGE_NAME }} + tags: | + type=sha - - name: Docker tag and push - branch (private) - if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/') && !contains(github.ref, 'release') - run: | - GITHUB_BRANCH=`echo $GITHUB_REF | cut -d/ -f3- | sed 's#/#-#g'` - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $PRIVATE_IMAGE:$GITHUB_BRANCH - docker push $PRIVATE_IMAGE:$GITHUB_BRANCH + - name: '[docker] Docker build' + uses: docker/build-push-action@v4 + with: + context: ${{ env.DOCKER_META_CONTEXT }} + file: ${{ env.DOCKER_META_FILE }} + platforms: ${{ env.DOCKER_META_PLATFORMS }} + push: false + tags: ${{ steps.meta-test.outputs.tags }} + labels: ${{ steps.meta-test.outputs.labels }} - - name: Docker tag and push - develop (public) - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' - run: | - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $PUBLIC_IMAGE:$TAG_DEVELOP - docker push $PUBLIC_IMAGE:$TAG_DEVELOP + - name: '[docker-hub] Docker login' + if: github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_PASSWORD }} - - name: Docker tag and push - latest (public) - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - run: | - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $PUBLIC_IMAGE:$TAG_LATEST - docker push $PUBLIC_IMAGE:$TAG_LATEST + - name: '[docker-hub] Docker meta' + id: meta-public + if: github.event_name != 'pull_request' && github.actor != 'dependabot[bot]' + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.PUBLIC_IMAGE_PREFIX }}/${{ env.DOCKER_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }} - - name: Docker tag and push - version tag (public) - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - run: | - GITHUB_TAG=`echo $GITHUB_REF | cut -d/ -f3` - # Release vX.Y.Z - if [[ $GITHUB_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - IMAGE_TAG_MAJOR="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v(.*)\..*\..*/\1/g"` - IMAGE_TAG_MINOR="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v(.*)\..*/\1/g"` - IMAGE_TAG_PATCH="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v//g"` - echo "Publishing release: $IMAGE_TAG_PATCH"; - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_MAJOR && docker push $IMAGE_TAG_MAJOR; - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_MINOR && docker push $IMAGE_TAG_MINOR; - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_PATCH && docker push $IMAGE_TAG_PATCH; - fi - # Release candidate vX.Y.Z-rc.R - if [[ $GITHUB_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then - IMAGE_TAG_RC="$PUBLIC_IMAGE:"`echo $GITHUB_TAG | sed -E "s/v//g"` - echo "Publishing release candidate: $IMAGE_TAG_RC"; - docker image tag $PRIVATE_IMAGE:$GITHUB_SHA $IMAGE_TAG_RC && docker push $IMAGE_TAG_RC; - fi + - name: '[docker-hub] Docker build+push' + uses: docker/build-push-action@v4 + if: github.event_name != 'pull_request' && steps.meta-public.outputs.tags != '' + with: + context: ${{ env.DOCKER_META_CONTEXT }} + file: ${{ env.DOCKER_META_FILE }} + platforms: ${{ env.DOCKER_META_PLATFORMS }} + push: true + tags: ${{ steps.meta-public.outputs.tags }} + labels: ${{ steps.meta-public.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index 36c6917..713a1da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM python:3.9-slim-buster +FROM python:3.11-slim-bookworm RUN apt-get update && \ - apt install -y --no-install-recommends bash default-jre && \ + apt install -y --no-install-recommends bash default-jre build-essential gcc && \ rm -rf /var/lib/apt/lists/* WORKDIR /app diff --git a/nanopub_submitter/api.py b/nanopub_submitter/api.py index 97cb023..32f8ec0 100644 --- a/nanopub_submitter/api.py +++ b/nanopub_submitter/api.py @@ -7,7 +7,7 @@ from typing import Tuple from nanopub_submitter.config import cfg_parser, RequestConfig -from nanopub_submitter.consts import NICE_NAME, VERSION, BUILD_INFO,\ +from nanopub_submitter.consts import NICE_NAME, VERSION, BUILD_INFO, \ ENV_CONFIG, DEFAULT_CONFIG, DEFAULT_ENCODING from nanopub_submitter.logger import LOG, init_default_logging, init_config_logging from nanopub_submitter.mailer import Mailer @@ -101,11 +101,12 @@ async def submit_nanopub(request: fastapi.Request): # (4) Mail Mailer.get().notice(nanopub_uri=result.location) # (5) Return + headers = dict() + if result.location is not None: + headers['Location'] = result.location return fastapi.responses.Response( status_code=fastapi.status.HTTP_201_CREATED, - headers={ - 'Location': result.location, - }, + headers=headers, content=str(result), ) diff --git a/nanopub_submitter/triple_store.py b/nanopub_submitter/triple_store.py index ced3ea1..57e88e9 100644 --- a/nanopub_submitter/triple_store.py +++ b/nanopub_submitter/triple_store.py @@ -6,7 +6,7 @@ from typing import List from nanopub_submitter.config import SubmitterConfig -from nanopub_submitter.consts import COMMENT_INSTRUCTION_DELIMITER,\ +from nanopub_submitter.consts import COMMENT_INSTRUCTION_DELIMITER, \ COMMENT_POST_QUERY_PREFIX, COMMENT_PRE_QUERY_PREFIX @@ -22,6 +22,17 @@ def create_graph(class_name: str) -> rdflib.Graph: return GRAPH_CLASSES.get(class_name, rdflib.Graph)() +def _n3(node: rdflib.term.Node) -> str: + if isinstance(node, rdflib.URIRef): + return node.n3() + elif isinstance(node, rdflib.Literal): + return node.n3() + elif isinstance(node, rdflib.BNode): + return node.n3() + else: + raise ValueError(f'Unknown node type: {type(node)}') + + class QueryBuilder: def __init__(self): @@ -89,7 +100,7 @@ def basic_query_builder(cfg: SubmitterConfig, data: str, input_format: str) -> s g = create_graph(cfg.triple_store.graph_class) g.parse(data=data, format=input_format) triples = [ - f'{s.n3()} {p.n3()} {o.n3()} .' for s, p, o in g + f'{_n3(s)} {_n3(p)} {_n3(o)} .' for s, p, o in g ] if cfg.triple_store.graph_named is True and cfg.triple_store.graph_type: t = rdflib.URIRef(cfg.triple_store.graph_type) @@ -115,7 +126,7 @@ def multi_graph_query_builder(cfg: SubmitterConfig, data: str, input_format: str qb.insert_multigraph_start() for ctx in cg.contexts(): triples = [ - f'{s.n3()} {p.n3()} {o.n3()} .' + f'{_n3(s)} {_n3(p)} {_n3(o)} .' for s, p, o in cg.triples((None, None, None), context=ctx) ] qb.insert_multigraph(triples=triples, graph_node=ctx.identifier) diff --git a/requirements.txt b/requirements.txt index 4729b9f..d43684d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,35 +1,27 @@ -asgiref==3.4.1 -certifi==2021.5.30 -charset-normalizer==2.0.6 -click==8.0.1 -colorama==0.4.4 -fastapi==0.68.1 -flake8==3.9.2 -h11==0.12.0 -httptools==0.2.0 -idna==3.2 -isodate==0.6.0 -mccabe==0.6.1 -mypy==0.910 -mypy-extensions==0.4.3 -pycodestyle==2.7.0 -pydantic==1.8.2 -pyflakes==2.3.1 -pyparsing==2.4.7 -python-dotenv==0.19.0 -PyYAML==5.4.1 -rdflib==6.0.1 -requests==2.26.0 +annotated-types==0.6.0 +anyio==4.2.0 +certifi==2023.11.17 +charset-normalizer==3.3.2 +click==8.1.7 +fastapi==0.109.0 +h11==0.14.0 +httptools==0.6.1 +idna==3.6 +isodate==0.6.1 +pydantic==2.5.3 +pydantic_core==2.14.6 +pyparsing==3.1.1 +python-dotenv==1.0.0 +PyYAML==6.0.1 +rdflib==7.0.0 +requests==2.31.0 six==1.16.0 -SPARQLWrapper==1.8.5 -starlette==0.14.2 -toml==0.10.2 -types-aiofiles==0.1.9 -types-orjson==3.6.0 -types-PyYAML==5.4.10 -types-ujson==0.1.1 -typing-extensions==3.10.0.2 -urllib3==1.26.7 -uvicorn==0.15.0 -watchgod==0.7 -websockets==10.0 +sniffio==1.3.0 +SPARQLWrapper==2.0.0 +starlette==0.35.1 +typing_extensions==4.9.0 +urllib3==2.1.0 +uvicorn==0.26.0 +uvloop==0.19.0 +watchfiles==0.21.0 +websockets==12.0