diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index c661ff6a..2f47c522 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -10,9 +10,9 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Use Node.js 18.17.1 - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: 18.17.1 - name: Install diff --git a/.github/workflows/publish-to-npm.yml b/.github/workflows/publish-to-npm.yml index f02db902..b3152ddb 100644 --- a/.github/workflows/publish-to-npm.yml +++ b/.github/workflows/publish-to-npm.yml @@ -4,22 +4,37 @@ on: push: branches: [ master ] +# Never run two publishes at once; a second merge waits for the first to finish +concurrency: + group: publish-to-npm + cancel-in-progress: false + +permissions: + contents: read + jobs: publish: runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.author.email, 'openactive-bot@users.noreply.github.com') }} + # Loop guard: the version bump commit below is pushed by the OpenActive GitHub App, and also carries [skip ci] + if: ${{ github.actor != 'openactive[bot]' }} steps: + - name: Generate GitHub App Token + uses: actions/create-github-app-token@v1 + id: generate-token + with: + app-id: ${{ secrets.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: - token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }} + token: ${{ steps.generate-token.outputs.token }} - name: Identify run: | - git config user.name openactive-bot - git config user.email openactive-bot@users.noreply.github.com + git config --global user.name "openactive[bot]" + git config --global user.email 207210293+openactive[bot]@users.noreply.github.com - name: Use Node.js 18.17.1 - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: 18.17.1 registry-url: https://registry.npmjs.org/ @@ -28,24 +43,50 @@ jobs: - name: Test run: npm test - name: Increment Version - run: npm version patch + run: npm version patch -m "%s [skip ci]" + # Push before publishing: if the push is rejected, nothing is published, so npm can never get ahead of GitHub + - name: Push version update + run: git push origin HEAD:master - name: Publish to npm run: npm publish env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - - name: Push version update - run: git push + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN2 }} + # npm may take several minutes to make a new version available; downstream repositories install @latest + # as soon as they are dispatched, so wait until the new version can be fetched before dispatching + - name: Wait for the new version on npm + run: | + version=$(node -p "require('./package.json').version") + for i in $(seq 1 60); do + if curl -sf -o /dev/null "https://registry.npmjs.org/@openactive%2fdata-models/$version?nocache=$(date +%s)"; then + echo "$version is available on npm" + exit 0 + fi + echo "Attempt $i: $version not yet available, waiting" + sleep 30 + done + echo "$version was not available on npm within 30 minutes" + exit 1 dispatch: needs: publish strategy: + fail-fast: false matrix: repo: ['OpenActive.NET', 'OpenActive.DatasetSite.NET', 'models-php', 'models-ruby', 'models-ts', 'data-model-validator-site', 'openactive.github.io', 'developer-documentation', 'openactive-test-suite'] runs-on: ubuntu-latest steps: + # Without owner and repositories, the token only covers this repository and cannot dispatch to others + - name: Generate GitHub App Token + uses: actions/create-github-app-token@v1 + id: generate-token + with: + app-id: ${{ secrets.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: openactive + repositories: ${{ matrix.repo }} - name: Trigger tooling update - uses: peter-evans/repository-dispatch@v1 + uses: peter-evans/repository-dispatch@v3 with: - token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }} + token: ${{ steps.generate-token.outputs.token }} repository: openactive/${{ matrix.repo }} event-type: data-models-update diff --git a/MODELS.md b/MODELS.md index 04196122..46bd89d7 100644 --- a/MODELS.md +++ b/MODELS.md @@ -314,7 +314,7 @@ The minimum value of a number, inclusive. ```json "repeatCount": { "fieldName": "repeatCount", - "sameAs": "https://pending.schema.org/repeatCount", + "sameAs": "https://schema.org/repeatCount", "requiredType": "https://schema.org/Integer", "example": 3, "description": [ diff --git a/README.md b/README.md index 30d47cff..a086c7f2 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,18 @@ The test run will also include a run of [eslint](https://eslint.org/). To run th $ npm run test-no-lint ``` +### Updating schema.org + +The build downloads the schema.org vocabulary from a tagged release (`schemaOrgVocabUrl` in `scripts/build.js`) rather than the latest version, so a new schema.org release cannot break the build until the pin is changed. Pin to the GitHub tag URL: `schema.org/version//` only serves the current release, so a pin to it would break as soon as the next release is published. + +To move to a new schema.org release: + +1. Change the tag and release number in `schemaOrgVocabUrl`. +2. Run `npm test`. +3. If schema.org has moved terms from pending to core, tests fail with `... is not an accurate pending.schema.org reference ...`. Change the matching `sameAs` and `derivedFrom` values under `versions/2.x/models` from `https://pending.schema.org/` to `https://schema.org/`, and update any examples in [MODELS.md](MODELS.md). Leave the `pending` namespace in `meta.json`, as it is part of the published JSON-LD context. + +For example, schema.org 30.1 moved 20 terms used by these models from pending to core: `EventSeries`, `Schedule`, `VirtualLocation`, `WebAPI`, `byDay`, `byMonth`, `byMonthDay`, `documentation`, `eventAttendanceMode`, `eventSchedule`, `exceptDate`, `gender`, `hasCredential`, `jobTitle`, `maximumVirtualAttendeeCapacity`, `provider`, `repeatCount`, `repeatFrequency`, `scheduleTimezone` and `termsOfService`. + ### Adding models Add new models to the `versions/models` directory. diff --git a/package.json b/package.json index dd5537e1..a356744e 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "test": "npm run test-no-lint", "test-no-lint": "jasmine", "test-debug": "node --inspect-brk -i ./node_modules/jasmine/bin/jasmine.js", - "postpublish": "git push", - "publish-patch": "npm test && git pull && git push && npm version patch && npm publish" + "publish-patch": "npm test && git pull && npm version patch && git push && npm publish" } } diff --git a/scripts/build.js b/scripts/build.js index d3cf4036..061995c8 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -13,7 +13,9 @@ const renderModel = require('../src/helpers/renderModel'); const uniqueVersions = [...new Set(Object.values(versions))]; const distPath = path.join(__dirname, '..', 'src', 'dist'); - const schemaOrgVocabUrl = 'https://schema.org/version/latest/schemaorg-current-https.jsonld'; + // Pinned to a tagged schema.org release so a new release cannot break CI unannounced. + // schema.org/version// only serves the current release, so use the GitHub tag instead. + const schemaOrgVocabUrl = 'https://raw.githubusercontent.com/schemaorg/schemaorg/v30.1/data/releases/30.1/schemaorg-current-https.jsonld'; console.log('Fetching schema.org vocab...'); const schema = JSON.parse(request('GET', schemaOrgVocabUrl, { @@ -130,6 +132,7 @@ const renderModel = require('../src/helpers/renderModel'); fs.writeFileSync( path.join(distPath, 'schemaOrgVocab.js'), `/* eslint-disable */ + // @ts-nocheck - the schema.org vocabulary is too large for tsc to infer (TS2590). // This is a generated file. Do not edit manually. module.exports = ${JSON.stringify(schema)};`, () => {}, diff --git a/versions/2.x/models/BookingService.json b/versions/2.x/models/BookingService.json index 05113ce9..ba7098fb 100644 --- a/versions/2.x/models/BookingService.json +++ b/versions/2.x/models/BookingService.json @@ -79,7 +79,7 @@ }, "termsOfService": { "fieldName": "termsOfService", - "sameAs": "https://pending.schema.org/termsOfService", + "sameAs": "https://schema.org/termsOfService", "model": "ArrayOf#Terms", "description": [ "The terms of service of the Booking System." @@ -95,7 +95,7 @@ }, "hasCredential": { "fieldName": "hasCredential", - "sameAs": "https://pending.schema.org/hasCredential", + "sameAs": "https://schema.org/hasCredential", "requiredType": "https://schema.org/URL", "description": [ "A human and machine-readable certification file that asserts the service's conformance to established standards. The value of this property should be a URL that resolves to a valid and up-to-date OpenActive Test Suite Conformance Certificate for this implementation. Note this property is in EARLY RELEASE AND IS SUBJECT TO CHANGE, as the [Dataset API Discovery specification](https://openactive.io/dataset-api-discovery/EditorsDraft/) evolves." diff --git a/versions/2.x/models/CourseInstance.json b/versions/2.x/models/CourseInstance.json index 9362c43c..4e70153d 100644 --- a/versions/2.x/models/CourseInstance.json +++ b/versions/2.x/models/CourseInstance.json @@ -243,7 +243,7 @@ }, "eventSchedule": { "fieldName": "eventSchedule", - "sameAs": "https://pending.schema.org/eventSchedule", + "sameAs": "https://schema.org/eventSchedule", "model": "ArrayOf#Schedule", "description": [ "A an array of oa:Schedule or oa:PartialSchedule, which represents a recurrence pattern." diff --git a/versions/2.x/models/Dataset.json b/versions/2.x/models/Dataset.json index a7a75e41..47393b62 100644 --- a/versions/2.x/models/Dataset.json +++ b/versions/2.x/models/Dataset.json @@ -169,7 +169,7 @@ }, "documentation": { "fieldName": "documentation", - "sameAs": "https://pending.schema.org/documentation", + "sameAs": "https://schema.org/documentation", "requiredType": "https://schema.org/URL", "example": "https://permalink.openactive.io/dataset-site/open-data-documentation", "description": [ diff --git a/versions/2.x/models/Event.json b/versions/2.x/models/Event.json index 34ae6a54..b7eb913c 100644 --- a/versions/2.x/models/Event.json +++ b/versions/2.x/models/Event.json @@ -508,7 +508,7 @@ }, "maximumVirtualAttendeeCapacity": { "fieldName": "maximumVirtualAttendeeCapacity", - "sameAs": "https://pending.schema.org/maximumVirtualAttendeeCapacity", + "sameAs": "https://schema.org/maximumVirtualAttendeeCapacity", "requiredType": "https://schema.org/Integer", "description": [ "Indicates the maximum number of connections to a shared virtual space." @@ -660,7 +660,7 @@ }, "eventAttendanceMode": { "fieldName": "eventAttendanceMode", - "sameAs": "https://pending.schema.org/eventAttendanceMode", + "sameAs": "https://schema.org/eventAttendanceMode", "requiredType": "https://schema.org/EventAttendanceModeEnumeration", "description": [ "The eventAttendanceMode of an event indicates whether it occurs online, offline, or a mix." diff --git a/versions/2.x/models/EventSeries.json b/versions/2.x/models/EventSeries.json index ad756e0a..b656de17 100644 --- a/versions/2.x/models/EventSeries.json +++ b/versions/2.x/models/EventSeries.json @@ -2,7 +2,7 @@ "type": "EventSeries", "hasId": true, "subClassOf": "#Event", - "derivedFrom": "https://pending.schema.org/EventSeries", + "derivedFrom": "https://schema.org/EventSeries", "sampleId": "https://api.example.com/event-series/12345", "requiredFields": [ "type", diff --git a/versions/2.x/models/FacilityUse.json b/versions/2.x/models/FacilityUse.json index 5002af4f..8b5e92a7 100644 --- a/versions/2.x/models/FacilityUse.json +++ b/versions/2.x/models/FacilityUse.json @@ -310,7 +310,7 @@ }, "provider": { "fieldName": "provider", - "sameAs": "https://pending.schema.org/provider", + "sameAs": "https://schema.org/provider", "model": "#Organization", "description": [ "The organisation responsible for providing the facility" diff --git a/versions/2.x/models/Organization.json b/versions/2.x/models/Organization.json index d27d8714..1fe559db 100755 --- a/versions/2.x/models/Organization.json +++ b/versions/2.x/models/Organization.json @@ -331,7 +331,7 @@ }, "termsOfService": { "fieldName": "termsOfService", - "sameAs": "https://pending.schema.org/termsOfService", + "sameAs": "https://schema.org/termsOfService", "model": "ArrayOf#Terms", "description": [ "The terms of service of the Seller." diff --git a/versions/2.x/models/Person.json b/versions/2.x/models/Person.json index 3dc3e12c..f9455c48 100755 --- a/versions/2.x/models/Person.json +++ b/versions/2.x/models/Person.json @@ -183,7 +183,7 @@ }, "gender": { "fieldName": "gender", - "sameAs": "https://pending.schema.org/gender", + "sameAs": "https://schema.org/gender", "requiredType": "https://schema.org/GenderType", "alternativeTypes": [ "https://schema.org/Text" @@ -224,7 +224,7 @@ }, "jobTitle": { "fieldName": "jobTitle", - "sameAs": "https://pending.schema.org/jobTitle", + "sameAs": "https://schema.org/jobTitle", "requiredType": "https://schema.org/Text", "example": "Team Captain", "description": [ @@ -259,7 +259,7 @@ }, "termsOfService": { "fieldName": "termsOfService", - "sameAs": "https://pending.schema.org/termsOfService", + "sameAs": "https://schema.org/termsOfService", "model": "ArrayOf#Terms", "description": [ "The terms of service of the Seller." diff --git a/versions/2.x/models/Schedule.json b/versions/2.x/models/Schedule.json index 89edb413..a044b0f0 100755 --- a/versions/2.x/models/Schedule.json +++ b/versions/2.x/models/Schedule.json @@ -1,6 +1,6 @@ { "type": "Schedule", - "derivedFrom": "https://pending.schema.org/Schedule", + "derivedFrom": "https://schema.org/Schedule", "derivedFromPending": true, "hasId": false, "requiredFields": [ @@ -59,7 +59,7 @@ }, "byDay": { "fieldName": "byDay", - "sameAs": "https://pending.schema.org/byDay", + "sameAs": "https://schema.org/byDay", "requiredType": "ArrayOf#https://schema.org/DayOfWeek", "alternativeTypes": ["ArrayOf#https://schema.org/Text"], "example": ["https://schema.org/Monday"], @@ -70,7 +70,7 @@ }, "byMonth": { "fieldName": "byMonth", - "sameAs": "https://pending.schema.org/byMonth", + "sameAs": "https://schema.org/byMonth", "requiredType": "ArrayOf#https://schema.org/Integer", "example": [2], "description": [ @@ -79,7 +79,7 @@ }, "byMonthDay": { "fieldName": "byMonthDay", - "sameAs": "https://pending.schema.org/byMonthDay", + "sameAs": "https://schema.org/byMonthDay", "requiredType": "ArrayOf#https://schema.org/Integer", "example": [28], "description": [ @@ -115,7 +115,7 @@ }, "exceptDate": { "fieldName": "exceptDate", - "sameAs": "https://pending.schema.org/exceptDate", + "sameAs": "https://schema.org/exceptDate", "requiredType": "ArrayOf#https://schema.org/Date", "alternativeTypes": ["ArrayOf#https://schema.org/DateTime"], "example": [ @@ -142,7 +142,7 @@ }, "repeatCount": { "fieldName": "repeatCount", - "sameAs": "https://pending.schema.org/repeatCount", + "sameAs": "https://schema.org/repeatCount", "requiredType": "https://schema.org/Integer", "example": 3, "description": [ @@ -152,7 +152,7 @@ }, "repeatFrequency": { "fieldName": "repeatFrequency", - "sameAs": "https://pending.schema.org/repeatFrequency", + "sameAs": "https://schema.org/repeatFrequency", "requiredType": "https://schema.org/Duration", "example": "P1D", "description": [ @@ -195,7 +195,7 @@ }, "scheduleTimezone": { "fieldName": "scheduleTimezone", - "sameAs": "https://pending.schema.org/scheduleTimezone", + "sameAs": "https://schema.org/scheduleTimezone", "requiredType": "https://schema.org/Text", "example": "Europe/London", "description": [ diff --git a/versions/2.x/models/SessionSeries.json b/versions/2.x/models/SessionSeries.json index 95e6e680..ed6bbd47 100644 --- a/versions/2.x/models/SessionSeries.json +++ b/versions/2.x/models/SessionSeries.json @@ -141,7 +141,7 @@ }, "eventSchedule": { "fieldName": "eventSchedule", - "sameAs": "https://pending.schema.org/eventSchedule", + "sameAs": "https://schema.org/eventSchedule", "model": "ArrayOf#Schedule", "description": [ "A an array of oa:Schedule or oa:PartialSchedule, which represents a recurrence pattern." diff --git a/versions/2.x/models/VirtualLocation.json b/versions/2.x/models/VirtualLocation.json index ba8563e2..3d49c59c 100755 --- a/versions/2.x/models/VirtualLocation.json +++ b/versions/2.x/models/VirtualLocation.json @@ -1,6 +1,6 @@ { "type": "VirtualLocation", - "derivedFrom": "https://pending.schema.org/VirtualLocation", + "derivedFrom": "https://schema.org/VirtualLocation", "hasId": false, "requiredFields":[ "type" diff --git a/versions/2.x/models/WebAPI.json b/versions/2.x/models/WebAPI.json index e020318f..f7eb1ee9 100644 --- a/versions/2.x/models/WebAPI.json +++ b/versions/2.x/models/WebAPI.json @@ -1,6 +1,6 @@ { "type": "WebAPI", - "derivedFrom": "https://pending.schema.org/WebAPI", + "derivedFrom": "https://schema.org/WebAPI", "hasId": false, "validationMode": { "RPDEFeed": "noRequired", @@ -91,7 +91,7 @@ }, "documentation": { "fieldName": "documentation", - "sameAs": "https://pending.schema.org/documentation", + "sameAs": "https://schema.org/documentation", "requiredType": "https://schema.org/URL", "example": "https://permalink.openactive.io/dataset-site/open-booking-api-documentation", "description": [ @@ -100,7 +100,7 @@ }, "termsOfService": { "fieldName": "termsOfService", - "sameAs": "https://pending.schema.org/termsOfService", + "sameAs": "https://schema.org/termsOfService", "requiredType": "https://schema.org/URL", "example": "https://example.bookingsystem.com/terms", "description": [