diff --git a/.changeset/config.json b/.changeset/config.json index ab848d1..0158a2c 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -4,8 +4,12 @@ "commit": false, "fixed": [], "linked": [], - "access": "restricted", + "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": [], + "privatePackages": { + "tag": true, + "version": true + } } diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 96860ef..e473cdf 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,6 +11,9 @@ jobs: docker: runs-on: ubuntu-latest + env: + DOCKER_IMAGE: ghcr.io/zazuko/cube-link + steps: - name: Checkout uses: actions/checkout@v4 @@ -32,11 +35,16 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Docker meta - uses: zazuko/action-docker-meta@main id: docker_meta + uses: docker/metadata-action@v5 with: - images: ghcr.io/zazuko/cube-link - include-pipeline-id: true + images: "${{ env.DOCKER_IMAGE }}" + tags: | + type=ref,event=branch + type=semver,prefix=v,pattern={{version}} + type=semver,prefix=v,pattern={{major}}.{{minor}} + type=semver,prefix=v,pattern={{major}} + type=sha - name: Build and push Docker images id: docker_build diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d46537a..63a0ff8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -12,8 +12,20 @@ jobs: name: Release runs-on: ubuntu-latest steps: + # This allow GitHub Actions to trigger the jobs for tags if needed + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2 + with: + app_id: ${{ secrets.GH_APP_ID }} + private_key: ${{ secrets.GH_PRIVATE_KEY }} + - name: Checkout Repo uses: actions/checkout@v4 + with: + # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits + fetch-depth: 0 + token: ${{ steps.generate_token.outputs.token }} - name: Setup Node.js uses: actions/setup-node@v3 @@ -23,7 +35,11 @@ jobs: - name: Install Dependencies run: npm ci - - name: Create Release Pull Request + - name: Create Release Pull Request or Create a GitHub Release + tag uses: changesets/action@v1 + with: + # This expects you to have a script called release which does a build for your packages and calls changeset publish + publish: yarn release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..85da5e7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# cube-link + +## 0.1.3 + +### Patch Changes + +- 86a40d7: In the CI, make sure to pull the Git repository with the token, to make sure it is able to trigger tags GitHub Actions workflows +- 7777e94: Generate Docker tags and labels using docker/metadata-action + +## 0.1.2 + +### Patch Changes + +- 6b78238: Bumping version only to trigger CI + +## 0.1.1 + +### Patch Changes + +- 09e2f3d: Forward profiles directly from GitHub + +## 0.1.0 + +### Minor Changes + +- 6d65dca: Adjust shape base to match how they are deployed (re #94) + +### Patch Changes + +- a38baff: Fixes the problem that validation script did not correctly target cube observations diff --git a/README.md b/README.md index ca461a2..521f663 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,17 @@ In this repository we develop the *Cube Schema* model. DRAFT: https://zazuko.github.io/cube-link/ +## Validation shapes + +The [validation](validation) directory contains various SHACL Shapes which can be used to ensure the correctness of datasets, cubes, hierarchies, and other. + +They can be retrieved from the web using an URI in the form of `https://cube.link/{VERSION}/shape/{CONSTRAINT}`, where the `{CONSTRAINT}` variable is replaced with any of the shape documents (without `.ttl`) and the `{VERSION}` variable is replaced with any [tag name](https://github.com/zazuko/cube-link/tags) or the word `latest`. +It is recommended to always use a specific version to avoid breaking changes. + +For example, to get version 0.0.4 of `standalone-cube-constraint.ttl`, fetch https://cube.link/v0.0.4/shape/standalone-cube-constraint . + +Otherwise, to get the latest version, fetch https://cube.link/latest/shape/standalone-cube-constraint instead. + ## How to Contribute Please open [Issues](https://github.com/zazuko/cube-link/issues) on this repository or provide PRs for contributions. diff --git a/documentation/core.md b/documentation/core.md index 335c7e0..e0d2471 100644 --- a/documentation/core.md +++ b/documentation/core.md @@ -174,13 +174,13 @@ To provide a generic solution that works for all numbers and IRIs, _Cube Schema_ a cube:Observation; ex:literalDimension ""^^cube:Undefined. -## Snippet for for the according shape: +## Snippet for the according shape: [ sh:path ex:literalDimension; sh:nodeKind sh:Literal; sh:or([ - sh:datatype xsd:string + sh:datatype xsd:string ; sh:minLength 1 ], [ sh:datatype cube:Undefined ]) @@ -191,6 +191,8 @@ To provide a generic solution that works for all numbers and IRIs, _Cube Schema_ If it is necessary to state why the value is `cube:Undefined`, annotations should be used. +Additional constraints (like `sh:minLength` in the example) may be placed within the _real_ data type so that they do not apply to undefined values. + ## Metadata diff --git a/package-lock.json b/package-lock.json index 38d17e3..90275e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7364,20 +7364,23 @@ "integrity": "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==" }, "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "peer": true, "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" } }, "node_modules/browserify-sign/node_modules/readable-stream": { diff --git a/package.json b/package.json index 81e219c..de08dd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "cube-link", - "version": "0.0.5", + "version": "0.1.3", + "private": true, "description": "Cube Schema", "main": "validate.js", "scripts": { @@ -8,7 +9,7 @@ "trifid": "trifid --verbose --config=trifid/config.json", "trifid:local": "trifid --verbose --config=trifid/config.local.json", "test": "standard", - "validation-test": "mocha" + "release": "changeset tag" }, "bin": { "cube-link": "bin/cube-link.js" diff --git a/trifid/redirect.js b/trifid/redirect.js index 2384a54..ab5568b 100644 --- a/trifid/redirect.js +++ b/trifid/redirect.js @@ -1,4 +1,5 @@ // @ts-check +const { Readable } = require('stream') const { fetchBuilder, MemoryCache } = require('node-fetch-cache') /** @@ -54,7 +55,26 @@ function factory () { } } if (shapePath) { - return res.redirect(`https://raw.githubusercontent.com/zazuko/cube-link/${versionPath}/validation/${shapePath}.ttl`) + try { + const rawGithub = await fetch(`https://raw.githubusercontent.com/zazuko/cube-link/${versionPath}/validation/${shapePath}.ttl`) + if (rawGithub.ok) { + res.set('Content-Type', 'text/turtle') + } else { + res.status(500) + } + // if the shape does not exist, we return a 404 + if (rawGithub.status === 404) { + return res.sendStatus(404) + } + /** @type {any | null} */ + const body = rawGithub.body + if (!rawGithub.body) { + throw new Error('No body') + } + return Readable.fromWeb(body).pipe(res) + } catch (e) { + return res.status(502).send(`Error fetching shape: ${e.message}`) + } } } diff --git a/validate.js b/validate.js index 00b3140..9def825 100644 --- a/validate.js +++ b/validate.js @@ -17,7 +17,7 @@ The cube and shape are connected on the fly with triples in the following patter _:b1 a sh:NodeShape; sh:targetNode ?cube; sh:property [ - sh:node ?observationShape; # from ?cube cube:observationShape ?observationShape + sh:node ?observationShape; # from ?cube cube:observationConstraint ?observationShape sh:path (cube:observationSet cube:observation); ]. @@ -32,7 +32,7 @@ async function validateCube (cube, shape, factory = rdf) { clownface({ dataset: shape, term: rdf.blankNode() }) .addOut(ns.sh.targetNode, cubeRoot) .addOut(ns.sh.property, null, property => { - property.addOut(ns.sh.node, cubeRoot.out(ns.cube.observationShape)) + property.addOut(ns.sh.node, cubeRoot.out(ns.cube.observationConstraint)) property.addList(ns.sh.path, [ns.cube.observationSet, ns.cube.observation]) }) diff --git a/validation/basic-cube-constraint-ml.ttl b/validation/basic-cube-constraint-ml.ttl index 40098ab..518869e 100644 --- a/validation/basic-cube-constraint-ml.ttl +++ b/validation/basic-cube-constraint-ml.ttl @@ -1,4 +1,4 @@ -@base . +@base . @prefix dash: . @prefix rdf: . @prefix rdfs: . @@ -46,7 +46,7 @@ sh:path sh:property ; sh:minCount 3 ; sh:message "cube:Constraint needs at least a certain amount of sh:properties" - ] , + ] , [ # we assume at least 3 dimensions, otherwise we would have an empty list of dimensions # one for cube:observedBy, one for rdf:type and at least one cube dimension @@ -69,4 +69,4 @@ sh:in (rdf:type cube:observedBy); ] ) - ] . \ No newline at end of file + ] . diff --git a/validation/basic-cube-constraint.ttl b/validation/basic-cube-constraint.ttl index 2ef3ef5..b2ef0eb 100644 --- a/validation/basic-cube-constraint.ttl +++ b/validation/basic-cube-constraint.ttl @@ -1,4 +1,4 @@ -@base . +@base . @prefix dash: . @prefix rdf: . @prefix rdfs: . @@ -46,4 +46,4 @@ sh:path sh:property ; sh:minCount 3 ; sh:message "cube:Constraint needs at least a certain amount of sh:properties" - ] . \ No newline at end of file + ] . diff --git a/validation/datacatalog-constraint.ttl b/validation/datacatalog-constraint.ttl index 0254da6..ba1cc89 100644 --- a/validation/datacatalog-constraint.ttl +++ b/validation/datacatalog-constraint.ttl @@ -1,4 +1,4 @@ -@base . +@base . @prefix dash: . @prefix rdf: . @prefix rdfs: . @@ -114,33 +114,33 @@ PREFIX dcterms: sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:message "schema:Dataset needs a schema:hasPart" - ] ; + ] ; sh:property [ sh:path void:sparqlEndpoint ; sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:message "schema:Dataset needs a void:sparqlEndpoint" - ] ; + ] ; sh:property [ sh:path void:rootResource ; sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:message "schema:Dataset needs a void:rootResource" - ] ; + ] ; sh:property [ sh:path void:exampleResource ; sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:message "schema:Dataset needs a void:exampleResource" - ] ; + ] ; sh:property [ sh:path dcat:distribution ; sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:node ; sh:message "schema:Dataset needs a valid dcat:Distribution" - ] ; - + ] ; + . @@ -171,4 +171,4 @@ PREFIX dcterms: sh:nodeKind sh:IRI ; sh:message "dcat:Distribution needs a valid dcat:downloadURL" ] ; - . \ No newline at end of file + . diff --git a/validation/standalone-constraint-constraint.ttl b/validation/standalone-constraint-constraint.ttl index 0766dde..877ff46 100644 --- a/validation/standalone-constraint-constraint.ttl +++ b/validation/standalone-constraint-constraint.ttl @@ -1,4 +1,4 @@ -@base . +@base . @prefix dash: . @prefix rdf: . @prefix rdfs: . @@ -88,7 +88,7 @@ ] ); ]; - + sh:property [ sh:message "needs a sh:datatype, sh:nodeKind or sh:datatype within sh:or (...)" ; sh:or( @@ -214,4 +214,4 @@ sh:node ; sh:minCount 1; sh:message "inHierarchy requires a conform nextInHierarchy" - ] . \ No newline at end of file + ] . diff --git a/validation/standalone-cube-constraint.ttl b/validation/standalone-cube-constraint.ttl index df6e93c..d2a3bb7 100644 --- a/validation/standalone-cube-constraint.ttl +++ b/validation/standalone-cube-constraint.ttl @@ -1,4 +1,4 @@ -@base . +@base . @prefix dash: . @prefix rdf: . @prefix rdfs: . @@ -9,7 +9,7 @@ # # This is the bare minimal SHACL shape for validating a cube. -# All cubes should pass this validation. +# All cubes should pass this validation. # It does not validate the constraints # @@ -82,7 +82,7 @@ # optional, but recommended sh:path cube:observationConstraint ; sh:message "cube:Cube must point to a valid constraint" - ] + ] . @@ -104,4 +104,4 @@ sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:message "cube:Observation requires cube:observedBy" - ] . \ No newline at end of file + ] .