diff --git a/.gitignore b/.gitignore index 34cadc6..33df40f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build-local/ bblocks-config-local.yml bblocks-config-local.yaml .volumes +*.bak diff --git a/README.md b/README.md index 36220bc..3d95f3a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +Note: see issue #11 — EarthCODE example URLs currently 404 and block the register build. # Open Science Building Blocks The repository documents a set of [OGC Building Blocks](https:blocks.ogc.org). for Open Science, which in turn leverage reusable ontologies, schemas, APIs etc. from published OGC standards. diff --git a/_sources/prov-processing-step/bblock.json b/_sources/prov-processing-step/bblock.json new file mode 100644 index 0000000..126005f --- /dev/null +++ b/_sources/prov-processing-step/bblock.json @@ -0,0 +1,13 @@ +{ + "name": "Geospatial Processing Step Provenance", + "abstract": "A profile of the PROV-O building block constrained to the description of a single geospatial processing step, with a required link to a registered process type.", + "status": "under-development", + "dateTimeAddition": "2026-08-05T00:00:00Z", + "itemClass": "schema", + "version": "0.1.0", + "dateOfLastChange": "2026-08-05", + "maturity": "development", + "scope": "unstable", + "tags": ["provenance", "prov-o", "processing", "ospd"], + "dependsOn": ["ogc.ogc-utils.prov"] +} diff --git a/_sources/prov-processing-step/description.md b/_sources/prov-processing-step/description.md new file mode 100644 index 0000000..ae15657 --- /dev/null +++ b/_sources/prov-processing-step/description.md @@ -0,0 +1,20 @@ +# Geospatial Processing Step Provenance + +This building block profiles `ogc-utils.prov` down to a single unit: one +geospatial processing step. + +PROV-O is deliberately general. Any valid PROV document is a valid description +of a processing step, which makes validation weak and cross-platform comparison +hard. + +OGC API - Processes Part 5 (draft 26-038) narrows this for job provenance: a +job entity, a process entity, an activity joining them, and input/output +artifacts carrying roles. This profile follows that shape. + +Part 5 does not define any link from a process to a registered process type. +This profile makes that link required, via the processType property. It is the +point at which the OSPD 2026 profile-plus-register pattern closes: the profile +gives the structure, the register gives the controlled term. + +Under development as part of OSPD 2026, deliverable D104 (Aganitha Space +Technologies, Workflow Profiler). diff --git a/_sources/prov-processing-step/examples.yaml b/_sources/prov-processing-step/examples.yaml new file mode 100644 index 0000000..42c870f --- /dev/null +++ b/_sources/prov-processing-step/examples.yaml @@ -0,0 +1,45 @@ +- title: Reprojection of a gridded precipitation dataset + description: >- + A single reprojection step, transforming a gridded precipitation dataset + from a geographic CRS to a projected CRS. Reprojection was the one + operation common to every workflow presented at the OSPD 2026 kick-off, + which makes it the natural first candidate for the process-type register. + snippets: + - language: json + code: | + { + "id": "urn:aganitha:step:reproject:20260805T101500Z", + "provType": "Activity", + "processType": "https://example.org/ospd/process-types/reprojection", + "label": "Reproject precipitation grid to national projected CRS", + "startedAtTime": "2026-08-05T10:15:00Z", + "endedAtTime": "2026-08-05T10:15:42Z", + "used": ["urn:aganitha:dataset:precip-daily-geographic"], + "generated": ["urn:aganitha:dataset:precip-daily-projected"], + "qualifiedUsage": [ + { + "entity": "urn:aganitha:dataset:precip-daily-geographic", + "hadRole": "https://example.org/ospd/roles/sourceGrid" + } + ], + "wasAssociatedWith": ["https://gdal.org/"], + "parameters": { + "sourceCrs": "EPSG:4326", + "targetCrs": "EPSG:7755", + "resamplingMethod": "bilinear" + } + } + +- title: Minimal conforming step + description: >- + The smallest document that satisfies this profile. + snippets: + - language: json + code: | + { + "id": "urn:example:step:0001", + "provType": "Activity", + "processType": "https://example.org/ospd/process-types/reprojection", + "used": ["urn:example:input:0001"], + "generated": ["urn:example:output:0001"] + } diff --git a/_sources/prov-processing-step/schema.yaml b/_sources/prov-processing-step/schema.yaml new file mode 100644 index 0000000..320aba2 --- /dev/null +++ b/_sources/prov-processing-step/schema.yaml @@ -0,0 +1,82 @@ +$schema: https://json-schema.org/draft/2020-12/schema +title: Geospatial Processing Step Provenance +description: >- + A single geospatial processing step, expressed as a constrained profile of + PROV-O. Inputs and outputs are entity references; the role each played is + expressed through prov:qualifiedUsage rather than inline, so the profile + stays compatible with the PROV-O object model. + +allOf: + - $ref: bblocks://ogc.ogc-utils.prov + - type: object + required: + - id + - processType + - used + - generated + properties: + + id: + description: Stable identifier for this processing step activity. + type: string + + processType: + description: >- + Identifier of the registered process type this step is an instance + of. This is the link that PROV-O and OGC API - Processes Part 5 do + not themselves provide. + type: string + + label: + type: string + + startedAtTime: + type: string + format: date-time + + endedAtTime: + type: string + format: date-time + + used: + description: Entities consumed by this step. + type: array + minItems: 1 + items: + type: string + + generated: + description: Entities produced by this step. + type: array + minItems: 1 + items: + type: string + + qualifiedUsage: + description: >- + Which declared input slot of the process type each consumed entity + occupied. Makes two runs of the same process type comparable. + type: array + items: + type: object + required: + - entity + - hadRole + properties: + entity: + type: string + hadRole: + type: string + + wasAssociatedWith: + type: array + items: + type: string + + parameters: + description: >- + Parameter values that determined the behaviour of this step. Keys + SHOULD be drawn from the parameter terms declared by the registered + process type. + type: object + additionalProperties: true diff --git a/bblocks-config.yaml b/bblocks-config.yaml index 7b6b302..7bd03e5 100644 --- a/bblocks-config.yaml +++ b/bblocks-config.yaml @@ -18,6 +18,7 @@ imports: - https://ogcincubator.github.io/bblocks-ogcapi-processes - https://ogcincubator.github.io/prov-cwl/ - https://ogcincubator.github.io/bblocks-wf4ever/ + - https://ogcincubator.github.io/bblock-prov-schema/ - https://ogcincubator.github.io/bblocks-stac - https://ogcincubator.github.io/cross-domain-model diff --git a/build-devel.sh b/build-devel.sh new file mode 100755 index 0000000..0fff6f7 --- /dev/null +++ b/build-devel.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# Process building blocks using the development (rolling) image build +BBP_IMAGE_TAG=develop exec "$(dirname "$0")/build.sh" "$@" \ No newline at end of file diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index 849f66f..29d0cc8 --- a/build.sh +++ b/build.sh @@ -9,7 +9,7 @@ if [ -f '.volumes' ]; then fi done < .volumes) fi -docker run --pull=always --rm --workdir /workspace -v "$(pwd):/workspace" ${VOLUMES} \ +docker run -it --pull=always --rm --workdir /workspace -v "$(pwd):/workspace" ${VOLUMES} \ ghcr.io/opengeospatial/bblocks-postprocess \ --clean true --base-url http://localhost:9090/register/ diff --git a/create-clean-pr.sh b/create-clean-pr.sh new file mode 100755 index 0000000..ac3592b --- /dev/null +++ b/create-clean-pr.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +# Creates a "clean" branch of a Building Blocks register fork, excluding all +# changes in the build/ directory (and fork-only config overrides), so that +# it can be used to open a Pull Request against the upstream register without +# generated-artifact merge conflicts. +# +# See https://opengeospatial.github.io/bblocks-docs/build/contribution for details. + +usage() { + echo "Usage: $0 [-u upstreamRemote] [-f forkRemote] [-b upstreamBranch] [-d buildDirectory] [-t tempBranch] [-P]" + echo + echo "Options:" + echo " -u Upstream remote name (default: 'fork-parent')" + echo " -f Fork remote name (default: 'origin')" + echo " -b Upstream branch (default: 'main' or 'master')" + echo " -d Build directory (default: 'build')" + echo " -t Name for the temporary clean branch (default: randomly generated)" + echo " -P Do not push to remote" + echo " -h Show this help message" + exit 1 +} + +UPSTREAM_REMOTE=fork-parent +BUILD_DIR=build +FORK_REMOTE=origin +UPSTREAM_BRANCH= +PUSH_TO_REMOTE=1 +TMP_BRANCH= + +while getopts "u:f:b:d:t:Ph" opt; do + case $opt in + u) UPSTREAM_REMOTE="$OPTARG" ;; + f) FORK_REMOTE="$OPTARG" ;; + b) UPSTREAM_BRANCH="$OPTARG" ;; + d) BUILD_DIR="$OPTARG" ;; + P) PUSH_TO_REMOTE=;; + t) TMP_BRANCH="$OPTARG";; + h) usage ;; + \?) usage ;; + esac +done + +trap_add() { + local cmd="$1" + # shellcheck disable=SC2064 + trap "${cmd};$(trap -p EXIT | cut -d"'" -f2)" EXIT +} + +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "This script must be run inside a git directory." + echo "If you are using Docker, check you have mounted the right volumes." + exit 1 +fi + +if ! ( git diff --quiet && git diff --cached --quiet ); then + echo "There are pending changes. Please commit or stash them before continuing." + exit 1 +fi + +# Detect git-filter-repo +if [[ -x "./git-filter-repo" ]]; then + FILTER_REPO_CMD="./git-filter-repo" +elif command -v git-filter-repo >/dev/null 2>&1; then + FILTER_REPO_CMD="git-filter-repo" +elif git filter-repo --help >/dev/null 2>&1; then + FILTER_REPO_CMD="git filter-repo" +else + # Download the latest released version to a temp location + LATEST_TAG=$(curl -sSfL https://api.github.com/repos/newren/git-filter-repo/releases/latest \ + | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') + if [[ -z "$LATEST_TAG" ]]; then + echo "Could not determine the latest git-filter-repo release." >&2 + exit 1 + fi + TMP_DIR=$(mktemp -d) + FILTER_REPO_CMD="$TMP_DIR/git-filter-repo" + if ! curl -sSfL -o "$FILTER_REPO_CMD" \ + "https://raw.githubusercontent.com/newren/git-filter-repo/${LATEST_TAG}/git-filter-repo"; then + echo "Failed to download git-filter-repo ${LATEST_TAG}." >&2 + rm -rf "$TMP_DIR" + exit 1 + fi + chmod +x "$FILTER_REPO_CMD" + trap_add "rm -rf \"$TMP_DIR\"" +fi +echo "Using git-filter-repo command: $FILTER_REPO_CMD" + +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +trap_add "git checkout \"$CURRENT_BRANCH\" >/dev/null 2>/dev/null" + +UPSTREAM_URL=$(git remote get-url "$UPSTREAM_REMOTE" 2>/dev/null) +if [[ $? -ne 0 || -z "$UPSTREAM_URL" ]]; then + echo "Upstream remote '$UPSTREAM_REMOTE' does not exist. Please add it first by running:" + echo " git remote add ${UPSTREAM_REMOTE} git@github.com:username/repo.git" + exit 1 +fi +echo "Using upstream remote: ${UPSTREAM_REMOTE} / ${UPSTREAM_URL}" + +FORK_URL=$(git remote get-url "$FORK_REMOTE" 2>/dev/null) +if [[ $? -ne 0 || -z "$FORK_URL" ]]; then + echo "Fork (local) remote '$FORK_REMOTE' does not exist." + exit 1 +fi +echo "Using fork remote: ${FORK_REMOTE} / ${FORK_URL}" + +set -euo pipefail +git fetch "${UPSTREAM_REMOTE}" + +if [[ -z "$TMP_BRANCH" ]]; then + TMP_BRANCH="clean-pr-$(date +%s).$RANDOM" +fi +git checkout -b "$TMP_BRANCH" + +if [[ -z "${UPSTREAM_BRANCH}" ]]; then + if git rev-parse --verify --quiet "${UPSTREAM_REMOTE}"/main >/dev/null; then + UPSTREAM_BRANCH=main + else + UPSTREAM_BRANCH=master + fi +fi +echo "Using upstream branch: ${UPSTREAM_BRANCH}" + +$FILTER_REPO_CMD --path "${BUILD_DIR}" --path .fork --path bblocks-config-override.yml --path bblocks-config-override.yaml --invert-paths --force \ + --refs "${UPSTREAM_REMOTE}/${UPSTREAM_BRANCH}..HEAD" + +if [[ -n "$PUSH_TO_REMOTE" ]]; then + git push "${FORK_REMOTE}" "${TMP_BRANCH}" + echo "Branch created and changed pushed" + + # Strip a trailing .git suffix, then split "owner/repo" on the last slash/colon. + UPSTREAM_PATH="${UPSTREAM_URL#git@github.com:}" + UPSTREAM_PATH="${UPSTREAM_PATH#https://github.com/}" + UPSTREAM_PATH="${UPSTREAM_PATH%.git}" + + FORK_PATH="${FORK_URL#git@github.com:}" + FORK_PATH="${FORK_PATH#https://github.com/}" + FORK_PATH="${FORK_PATH%.git}" + + PR_URL="https://github.com/${UPSTREAM_PATH}/compare/${UPSTREAM_BRANCH}...${FORK_PATH%%/*}:${FORK_PATH#*/}:${TMP_BRANCH}?expand=1" + echo "" + echo "========" + echo "You can use the following URL to create the Pull Request:" + echo " ${PR_URL}" + echo "========" + echo "" +else + echo "Branch '${TMP_BRANCH}' created - push disabled" +fi diff --git a/view.sh b/view.sh old mode 100644 new mode 100755